642 lines
23 KiB
Python
642 lines
23 KiB
Python
# encoding: UTF-8
|
||
|
||
from datetime import datetime
|
||
|
||
from pymongo import Connection
|
||
from pymongo.errors import *
|
||
|
||
from eventEngine import *
|
||
|
||
|
||
# 常量定义
|
||
OFFSET_OPEN = '0' # 开仓
|
||
OFFSET_CLOSE = '1' # 平仓
|
||
|
||
DIRECTION_BUY = '0' # 买入
|
||
DIRECTION_SELL = '1' # 卖出
|
||
|
||
PRICETYPE_LIMIT = '2' # 限价
|
||
|
||
|
||
|
||
########################################################################
|
||
class Tick:
|
||
"""Tick数据对象"""
|
||
|
||
#----------------------------------------------------------------------
|
||
def __init__(self, symbol):
|
||
"""Constructor"""
|
||
self.symbol = symbol # 合约代码
|
||
|
||
self.openPrice = 0 # OHLC
|
||
self.highPrice = 0
|
||
self.lowPrice = 0
|
||
self.lastPrice = 0
|
||
|
||
self.volume = 0 # 成交量
|
||
self.openInterest = 0 # 持仓量
|
||
|
||
self.upperLimit = 0 # 涨停价
|
||
self.lowerLimit = 0 # 跌停价
|
||
|
||
self.time = '' # 更新时间和毫秒
|
||
self.ms= 0
|
||
|
||
self.bidPrice1 = 0 # 深度行情
|
||
self.bidPrice2 = 0
|
||
self.bidPrice3 = 0
|
||
self.bidPrice4 = 0
|
||
self.bidPrice5 = 0
|
||
|
||
self.askPrice1 = 0
|
||
self.askPrice2 = 0
|
||
self.askPrice3 = 0
|
||
self.askPrice4 = 0
|
||
self.askPrice5 = 0
|
||
|
||
self.bidVolume1 = 0
|
||
self.bidVolume2 = 0
|
||
self.bidVolume3 = 0
|
||
self.bidVolume4 = 0
|
||
self.bidVolume5 = 0
|
||
|
||
self.askVolume1 = 0
|
||
self.askVolume2 = 0
|
||
self.askVolume3 = 0
|
||
self.askVolume4 = 0
|
||
self.askVolume5 = 0
|
||
|
||
|
||
########################################################################
|
||
class Trade:
|
||
"""成交数据对象"""
|
||
|
||
#----------------------------------------------------------------------
|
||
def __init__(self, symbol):
|
||
"""Constructor"""
|
||
self.symbol = symbol # 合约代码
|
||
|
||
self.orderRef = '' # 报单号
|
||
self.tradeID = '' # 成交编号
|
||
|
||
self.direction = None # 方向
|
||
self.offset = None # 开平
|
||
self.price = 0 # 成交价
|
||
self.volume = 0 # 成交量
|
||
|
||
|
||
########################################################################
|
||
class Order:
|
||
"""报单数据对象"""
|
||
|
||
#----------------------------------------------------------------------
|
||
def __init__(self, symbol):
|
||
"""Constructor"""
|
||
self.symbol = symbol # 合约代码
|
||
|
||
self.orderRef = '' # 报单编号
|
||
|
||
self.direction = None # 方向
|
||
self.offset = None # 开平
|
||
self.price = 0 # 委托价
|
||
self.volumeOriginal = 0 # 报单量
|
||
self.volumeTraded = 0 # 已成交数量
|
||
|
||
self.insertTime = '' # 报单时间
|
||
self.cancelTime = '' # 撤单时间
|
||
|
||
self.frontID = 0 # 前置机编号
|
||
self.sessionID = 0 # 会话编号
|
||
|
||
self.status = '' # 报单状态代码
|
||
|
||
|
||
########################################################################
|
||
class StopOrder:
|
||
"""
|
||
停止单对象
|
||
用于实现价格突破某一水平后自动追入
|
||
即通常的条件单和止损单
|
||
"""
|
||
|
||
#----------------------------------------------------------------------
|
||
def __init__(self, symbol, direction, offset, price, volume, strategy):
|
||
"""Constructor"""
|
||
self.symbol = symbol
|
||
self.direction = direction
|
||
self.offset = offset
|
||
self.price = price
|
||
self.volume = volume
|
||
self.strategy = strategy
|
||
|
||
|
||
########################################################################
|
||
class StrategyEngine(object):
|
||
"""策略引擎"""
|
||
|
||
#----------------------------------------------------------------------
|
||
def __init__(self, eventEngine, mainEngine):
|
||
"""Constructor"""
|
||
self.__eventEngine = eventEngine
|
||
self.mainEngine = mainEngine
|
||
|
||
# 获取代表今日的datetime
|
||
t = datetime.today()
|
||
self.today = t.replace(hour=0, minute=0, second=0, microsecond=0)
|
||
|
||
# 保存所有报单数据的字典
|
||
self.__dictOrder = {}
|
||
|
||
# 保存策略对象的字典
|
||
# key为策略名称
|
||
# value为策略对象
|
||
self.dictStrategy = {}
|
||
|
||
# 保存合约代码和策略对象映射关系的字典
|
||
# key为合约代码
|
||
# value为交易该合约的策略列表
|
||
self.__dictSymbolStrategy = {}
|
||
|
||
# 保存报单编号和策略对象映射关系的字典
|
||
# key为报单编号
|
||
# value为策略对象
|
||
self.__dictOrderRefStrategy = {}
|
||
|
||
# 保存合约代码和相关停止单的字典
|
||
# key为合约代码
|
||
# value为该合约相关的停止单列表
|
||
self.__dictStopOrder = {}
|
||
|
||
# MongoDB数据库相关
|
||
self.__mongoConnected = False
|
||
self.__mongoConnection = None
|
||
self.__mongoTickDB = None
|
||
|
||
# 调用函数
|
||
self.__connectMongo()
|
||
self.__registerEvent()
|
||
|
||
#----------------------------------------------------------------------
|
||
def createStrategy(self, strategyName, strategySymbol, strategyClass, strategySetting):
|
||
"""创建策略"""
|
||
strategy = strategyClass(strategyName, strategySymbol, self)
|
||
self.dictStrategy[strategyName] = strategy
|
||
strategy.loadSetting(strategySetting)
|
||
|
||
# 订阅合约行情,注意这里因为是CTP,所以ExchangeID可以忽略
|
||
self.mainEngine.subscribe(strategySymbol, None)
|
||
|
||
# 注册策略监听
|
||
self.registerStrategy(strategySymbol, strategy)
|
||
|
||
#----------------------------------------------------------------------
|
||
def __connectMongo(self):
|
||
"""连接MongoDB数据库"""
|
||
try:
|
||
self.__mongoConnection = Connection()
|
||
self.__mongoConnected = True
|
||
self.__mongoTickDB = self.__mongoConnection['TickDB']
|
||
self.writeLog(u'策略引擎连接MongoDB成功')
|
||
except ConnectionFailure:
|
||
self.writeLog(u'策略引擎连接MongoDB失败')
|
||
|
||
#----------------------------------------------------------------------
|
||
def __recordTick(self, data):
|
||
"""将Tick数据插入到MongoDB中"""
|
||
if self.__mongoConnected:
|
||
symbol = data['InstrumentID']
|
||
data['date'] = self.today
|
||
self.__mongoTickDB[symbol].insert(data)
|
||
|
||
#----------------------------------------------------------------------
|
||
def loadTick(self, symbol, dt):
|
||
"""从MongoDB中读取Tick数据"""
|
||
if self.__mongoConnected:
|
||
collection = self.__mongoTickDB[symbol]
|
||
cx = collection.find({'date':{'$gte':dt}})
|
||
return cx
|
||
else:
|
||
return None
|
||
|
||
#----------------------------------------------------------------------
|
||
def __updateMarketData(self, event):
|
||
"""行情更新"""
|
||
data = event.dict_['data']
|
||
symbol = data['InstrumentID']
|
||
|
||
# 检查是否存在交易该合约的策略
|
||
if symbol in self.__dictSymbolStrategy:
|
||
# 创建TICK数据对象并更新数据
|
||
tick = Tick(symbol)
|
||
|
||
tick.openPrice = data['OpenPrice']
|
||
tick.highPrice = data['HighestPrice']
|
||
tick.lowPrice = data['LowestPrice']
|
||
tick.lastPrice = data['LastPrice']
|
||
|
||
tick.volume = data['Volume']
|
||
tick.openInterest = data['OpenInterest']
|
||
|
||
tick.upperLimit = data['UpperLimitPrice']
|
||
tick.lowerLimit = data['LowerLimitPrice']
|
||
|
||
tick.time = data['UpdateTime']
|
||
tick.ms = data['UpdateMillisec']
|
||
|
||
tick.bidPrice1 = data['BidPrice1']
|
||
tick.bidPrice2 = data['BidPrice2']
|
||
tick.bidPrice3 = data['BidPrice3']
|
||
tick.bidPrice4 = data['BidPrice4']
|
||
tick.bidPrice5 = data['BidPrice5']
|
||
|
||
tick.askPrice1 = data['AskPrice1']
|
||
tick.askPrice2 = data['AskPrice2']
|
||
tick.askPrice3 = data['AskPrice3']
|
||
tick.askPrice4 = data['AskPrice4']
|
||
tick.askPrice5 = data['AskPrice5']
|
||
|
||
tick.bidVolume1 = data['BidVolume1']
|
||
tick.bidVolume2 = data['BidVolume2']
|
||
tick.bidVolume3 = data['BidVolume3']
|
||
tick.bidVolume4 = data['BidVolume4']
|
||
tick.bidVolume5 = data['BidVolume5']
|
||
|
||
tick.askVolume1 = data['AskVolume1']
|
||
tick.askVolume2 = data['AskVolume2']
|
||
tick.askVolume3 = data['AskVolume3']
|
||
tick.askVolume4 = data['AskVolume4']
|
||
tick.askVolume5 = data['AskVolume5']
|
||
|
||
# 首先检查停止单是否需要发出
|
||
self.__processStopOrder(tick)
|
||
|
||
# 将该TICK数据推送给每个策略
|
||
for strategy in self.__dictSymbolStrategy[symbol]:
|
||
strategy.onTick(tick)
|
||
|
||
# 将数据插入MongoDB数据库,实盘建议另开程序记录TICK数据
|
||
self.__recordTick(data)
|
||
|
||
#----------------------------------------------------------------------
|
||
def __processStopOrder(self, tick):
|
||
"""处理停止单"""
|
||
symbol = tick.symbol
|
||
lastPrice = tick.lastPrice
|
||
upperLimit = tick.upperLimit
|
||
lowerLimit = tick.lowerLimit
|
||
|
||
# 如果当前有该合约上的止损单
|
||
if symbol in self.__dictStopOrder:
|
||
# 获取止损单列表
|
||
listSO = self.__dictStopOrder[symbol] # SO:stop order
|
||
|
||
# 准备一个空的已发止损单列表
|
||
listSent = []
|
||
|
||
for so in listSO:
|
||
# 如果是买入停止单,且最新成交价大于停止触发价
|
||
if so.direction == DIRECTION_BUY and lastPrice >= so.price:
|
||
# 以当日涨停价发出限价单买入
|
||
ref = self.sendOrder(symbol, DIRECTION_BUY, so.offset,
|
||
upperLimit, so.volume, strategy)
|
||
|
||
# 触发策略的止损单发出更新
|
||
so.strategy.onStopOrder(ref)
|
||
|
||
# 将该止损单对象保存到已发送列表中
|
||
listSent.append(so)
|
||
|
||
# 如果是卖出停止单,且最新成交价小于停止触发价
|
||
elif so.direction == DIRECTION_SELL and lastPrice <= so.price:
|
||
ref = self.sendOrder(symbol, DIRECTION_SELL, so.offset,
|
||
lowerLimit, so.volume, strategy)
|
||
|
||
so.strategy.onStopOrder(ref)
|
||
|
||
listSent.append(so)
|
||
|
||
# 从停止单列表中移除已经发单的停止单对象
|
||
if listSent:
|
||
for so in listSent:
|
||
listSO.remove(so)
|
||
|
||
# 检查停止单列表是否为空,若为空,则从停止单字典中移除该合约代码
|
||
if not listSO:
|
||
del self.__dictStopOrder[symbol]
|
||
|
||
#----------------------------------------------------------------------
|
||
def __updateOrder(self, event):
|
||
"""报单更新"""
|
||
data = event.dict_['data']
|
||
orderRef = data['OrderRef']
|
||
|
||
# 检查是否存在监听该报单的策略
|
||
if orderRef in self.__dictOrderRefStrategy:
|
||
|
||
# 创建Order数据对象
|
||
order = Order(data['InstrumentID'])
|
||
|
||
order.orderRef = data['OrderRef']
|
||
order.direction = data['Direction']
|
||
order.offset = data['CombOffsetFlag']
|
||
|
||
order.price = data['LimitPrice']
|
||
order.volumeOriginal = data['VolumeTotalOriginal']
|
||
order.volumeTraded = data['VolumeTraded']
|
||
order.insertTime = data['InsertTime']
|
||
order.cancelTime = data['CancelTime']
|
||
order.frontID = data['FrontID']
|
||
order.sessionID = data['SessionID']
|
||
|
||
order.status = data['OrderStatus']
|
||
|
||
# 推送给策略
|
||
strategy = self.__dictOrderRefStrategy[orderRef]
|
||
strategy.onOrder(order)
|
||
|
||
# 记录该Order的数据
|
||
self.__dictOrder[orderRef] = data
|
||
|
||
#----------------------------------------------------------------------
|
||
def __updateTrade(self, event):
|
||
"""成交更新"""
|
||
print 'updateTrade'
|
||
data = event.dict_['data']
|
||
orderRef = data['OrderRef']
|
||
print 'trade:', orderRef
|
||
|
||
if orderRef in self.__dictOrderRefStrategy:
|
||
|
||
# 创建Trade数据对象
|
||
trade = Trade(data['InstrumentID'])
|
||
|
||
trade.orderRef = orderRef
|
||
trade.tradeID = data['TradeID']
|
||
trade.direction = data['Direction']
|
||
trade.offset = data['OffsetFlag']
|
||
|
||
trade.price = data['Price']
|
||
trade.volume = data['Volume']
|
||
|
||
# 推送给策略
|
||
strategy = self.__dictOrderRefStrategy[orderRef]
|
||
strategy.onTrade(trade)
|
||
|
||
#----------------------------------------------------------------------
|
||
def sendOrder(self, symbol, direction, offset, price, volume, strategy):
|
||
"""
|
||
发单(仅允许限价单)
|
||
symbol:合约代码
|
||
direction:方向,DIRECTION_BUY/DIRECTION_SELL
|
||
offset:开平,OFFSET_OPEN/OFFSET_CLOSE
|
||
price:下单价格
|
||
volume:下单手数
|
||
strategy:策略对象
|
||
"""
|
||
contract = self.mainEngine.selectInstrument(symbol)
|
||
|
||
if contract:
|
||
ref = self.mainEngine.sendOrder(symbol,
|
||
contract['ExchangeID'],
|
||
price,
|
||
PRICETYPE_LIMIT,
|
||
volume,
|
||
direction,
|
||
offset)
|
||
|
||
self.__dictOrderRefStrategy[ref] = strategy
|
||
print 'ref:', ref
|
||
print 'strategy:', strategy.name
|
||
|
||
return ref
|
||
|
||
#----------------------------------------------------------------------
|
||
def cancelOrder(self, orderRef):
|
||
"""
|
||
撤单
|
||
"""
|
||
order = self.__dictOrder[orderRef]
|
||
symbol = order['InstrumentID']
|
||
contract = self.mainEngine.selectInstrument(symbol)
|
||
|
||
if contract:
|
||
self.mainEngine.cancelOrder(symbol,
|
||
contract['ExchangeID'],
|
||
orderRef,
|
||
order['FrontID'],
|
||
order['SessionID'])
|
||
|
||
#----------------------------------------------------------------------
|
||
def __registerEvent(self):
|
||
"""注册事件监听"""
|
||
self.__eventEngine.register(EVENT_MARKETDATA, self.__updateMarketData)
|
||
self.__eventEngine.register(EVENT_ORDER, self.__updateOrder)
|
||
self.__eventEngine.register(EVENT_TRADE ,self.__updateTrade)
|
||
|
||
#----------------------------------------------------------------------
|
||
def writeLog(self, log):
|
||
"""写日志"""
|
||
event = Event(type_=EVENT_LOG)
|
||
event.dict_['log'] = log
|
||
self.__eventEngine.put(event)
|
||
|
||
#----------------------------------------------------------------------
|
||
def registerStrategy(self, symbol, strategy):
|
||
"""注册策略对合约TICK数据的监听"""
|
||
# 尝试获取监听该合约代码的策略的列表,若无则创建
|
||
try:
|
||
listStrategy = self.__dictSymbolStrategy[symbol]
|
||
except KeyError:
|
||
listStrategy = []
|
||
self.__dictSymbolStrategy[symbol] = listStrategy
|
||
|
||
# 防止重复注册
|
||
if strategy not in listStrategy:
|
||
listStrategy.append(strategy)
|
||
|
||
#----------------------------------------------------------------------
|
||
def placeStopOrder(self, symbol, direction, offset, price, volume, strategy):
|
||
"""
|
||
下停止单(运行于本地引擎中)
|
||
注意这里的price是停止单的触发价
|
||
"""
|
||
# 创建止损单对象
|
||
so = StopOrder(symbol, direction, offset, price, volume, strategy)
|
||
|
||
# 获取该合约相关的止损单列表
|
||
try:
|
||
listSO = self.__dictStopOrder[symbol]
|
||
except KeyError:
|
||
listSO = []
|
||
self.__dictStopOrder[symbol] = listSO
|
||
|
||
# 将该止损单插入列表中
|
||
listSO.append(so)
|
||
|
||
return so
|
||
|
||
#----------------------------------------------------------------------
|
||
def cancelStopOrder(self, so):
|
||
"""撤销停止单"""
|
||
symbol = so.symbol
|
||
|
||
try:
|
||
listSO = self.__dictStopOrder[symbol]
|
||
|
||
if so in listSO:
|
||
listSO.remove(so)
|
||
|
||
if not listSO:
|
||
del self.__dictStopOrder[symbol]
|
||
except KeyError:
|
||
pass
|
||
|
||
#----------------------------------------------------------------------
|
||
def startAll(self):
|
||
"""启动所有策略"""
|
||
for strategy in self.dictStrategy.values():
|
||
strategy.start()
|
||
|
||
#----------------------------------------------------------------------
|
||
def stopAll(self):
|
||
"""停止所有策略"""
|
||
for strategy in self.dictStrategy.values():
|
||
strategy.stop()
|
||
|
||
|
||
########################################################################
|
||
class StrategyTemplate(object):
|
||
"""策略模板"""
|
||
|
||
#----------------------------------------------------------------------
|
||
def __init__(self, name, symbol, engine):
|
||
"""Constructor"""
|
||
self.name = name # 策略名称(注意唯一性)
|
||
self.symbol = symbol # 策略交易的合约
|
||
self.engine = engine # 策略引擎对象
|
||
|
||
self.trading = False # 策略是否启动交易
|
||
|
||
#----------------------------------------------------------------------
|
||
def onTick(self, tick):
|
||
"""行情更新"""
|
||
raise NotImplementedError
|
||
|
||
#----------------------------------------------------------------------
|
||
def onTrade(self, trade):
|
||
"""交易更新"""
|
||
raise NotImplementedError
|
||
|
||
#----------------------------------------------------------------------
|
||
def onOrder(self, order):
|
||
"""报单更新"""
|
||
raise NotImplementedError
|
||
|
||
#----------------------------------------------------------------------
|
||
def onStopOrder(self, orderRef):
|
||
"""停止单更新"""
|
||
raise NotImplementedError
|
||
|
||
#----------------------------------------------------------------------
|
||
def onBar(self, o, h, l, c, volume, time):
|
||
"""K线数据更新"""
|
||
raise NotImplementedError
|
||
|
||
#----------------------------------------------------------------------
|
||
def start(self):
|
||
"""
|
||
启动交易
|
||
这里是最简单的改变self.trading
|
||
有需要可以重新实现更复杂的操作
|
||
"""
|
||
self.trading = True
|
||
self.engine.writeLog(self.name + u'开始运行')
|
||
|
||
#----------------------------------------------------------------------
|
||
def stop(self):
|
||
"""
|
||
停止交易
|
||
同上
|
||
"""
|
||
self.trading = False
|
||
self.engine.writeLog(self.name + u'停止运行')
|
||
|
||
#----------------------------------------------------------------------
|
||
def loadSetting(self, setting):
|
||
"""
|
||
载入设置
|
||
setting通常是一个包含了参数设置的字典
|
||
"""
|
||
raise NotImplementedError
|
||
|
||
#----------------------------------------------------------------------
|
||
def buy(self, price, volume, stopOrder=False):
|
||
"""买入开仓"""
|
||
if self.trading:
|
||
if stopOrder:
|
||
so = self.engine.placeStopOrder(self.symbol, DIRECTION_BUY,
|
||
OFFSET_OPEN, price, volume, self)
|
||
return so
|
||
else:
|
||
ref = self.engine.sendOrder(self.symbol, DIRECTION_BUY,
|
||
OFFSET_OPEN, price, volume, self)
|
||
return ref
|
||
else:
|
||
return None
|
||
|
||
#----------------------------------------------------------------------
|
||
def cover(self, price, volume, StopOrder=False):
|
||
"""买入平仓"""
|
||
if self.trading:
|
||
if stopOrder:
|
||
so = self.engine.placeStopOrder(self.symbol, DIRECTION_BUY,
|
||
OFFSET_CLOSE, price, volume, self)
|
||
return so
|
||
else:
|
||
ref = self.engine.sendOrder(self.symbol, DIRECTION_BUY,
|
||
OFFSET_CLOSE, price, volume, self)
|
||
return ref
|
||
else:
|
||
return None
|
||
|
||
#----------------------------------------------------------------------
|
||
def sell(self, price, volume, stopOrder=False):
|
||
"""卖出平仓"""
|
||
if self.trading:
|
||
if stopOrder:
|
||
so = self.engine.placeStopOrder(self.symbol, DIRECTION_SELL,
|
||
OFFSET_CLOSE, price, volume, self)
|
||
return so
|
||
else:
|
||
ref = self.engine.sendOrder(self.symbol, DIRECTION_SELL,
|
||
OFFSET_CLOSE, price, volume, self)
|
||
return ref
|
||
else:
|
||
return None
|
||
|
||
#----------------------------------------------------------------------
|
||
def short(self, price, volume, stopOrder=False):
|
||
"""卖出开仓"""
|
||
if self.trading:
|
||
if stopOrder:
|
||
so = self.engine.placeStopOrder(self.symbol, DIRECTION_SELL,
|
||
OFFSET_OPEN, price, volume, self)
|
||
return so
|
||
else:
|
||
ref = self.engine.sendOrder(self.symbol, DIRECTION_SELL,
|
||
OFFSET_OPEN, price, volume, self)
|
||
return ref
|
||
else:
|
||
return None
|
||
|
||
#----------------------------------------------------------------------
|
||
def cancelOrder(self, orderRef):
|
||
"""撤单"""
|
||
self.engine.cancelOrder(orderRef)
|
||
|
||
#----------------------------------------------------------------------
|
||
def cancelStopOrder(self, so):
|
||
"""撤销停止单"""
|
||
self.engine.cancelStopOrder(so)
|
||
|
||
|