def subscribeMarketData(self, strategy): """订阅行情""" for gateway in self.mainEngine.gatewayDict.keys(): req = VtSubscribeReq() req.symbol = strategy.vtSymbol self.mainEngine.subscribe(req, gateway) '''
def subscribeMarketData(self, strategy): """订阅行情""" # 订阅合约 contract = self.mainEngine.getContract(strategy.vtSymbol) if contract: req = VtSubscribeReq() req.symbol = contract.symbol req.exchange = contract.exchange # 对于IB接口订阅行情时所需的货币和产品类型,从策略属性中获取 req.currency = strategy.currency req.productClass = strategy.productClass self.mainEngine.subscribe(req, contract.gatewayName) else: self.writeCtaLog(u'%s的交易合约%s无法找到' %(strategy.name, strategy.vtSymbol))
def subscribeMarketData(self, vtSymbol): """订阅行情""" # 订阅合约 contract = self.mainEngine.getContract(vtSymbol) if contract: req = VtSubscribeReq() req.symbol = contract.symbol req.exchange = contract.exchange # 对于IB接口订阅行情时所需的货币和产品类型,从策略属性中获取 #req.currency = strategy.currency #req.productClass = strategy.productClass self.mainEngine.subscribe(req, contract.gatewayName) else: self.writeCtaLog(u'交易合约%s无法找到' % (vtSymbol))
def subscribe(self, strategy, symbolList): name = strategy.name for vtSymbol in symbolList: # 保存Tick映射关系 if vtSymbol in self.tickStrategyDict: l = self.tickStrategyDict[vtSymbol] else: l = [] self.tickStrategyDict[vtSymbol] = l l.append(strategy) # 订阅合约 contract = self.mainEngine.getContract(vtSymbol) if contract: req = VtSubscribeReq() req.symbol = contract.symbol req.exchange = contract.exchange # 对于IB接口订阅行情时所需的货币和产品类型,从策略属性中获取 req.currency = strategy.currency req.productClass = strategy.productClass self.mainEngine.subscribe(req, contract.gatewayName) else: self.writeArbLog(u'%s的交易合约%s无法找到' % (name, vtSymbol)) self.putStrategyEvent(name)
def initStrategyBySymbol(self, symbol, setting): # 创建策略实例 # 防止策略重名 if symbol in self.strategyDict: self.writeCtaLog(u'策略实例重名:%s' % name) return setting["vtSymbol"] = symbol strategy = ZzsdStrategy(self, setting) self.strategyDict[symbol] = strategy strategy.setFixSize(self.FIX_SIZE_AUDO) strategy.initDayBar(self.loadAllBar(DAY_DB_NAME, symbol)) strategy.initDayCurrentBar( self.getCurrentBar(self.loadCurrentDayMinBar(symbol))) if setting["strategyCycle"] == "60min": strategy.initHourBar(self.loadAllBar(MINUTE_60_DB_NAME, symbol)) strategy.initHourCurrentBar( self.getCurrentBar(self.loadMinuteBar(symbol, 60))) if setting["strategyCycle"] == "30min": strategy.initHourBar(self.loadAllBar(MINUTE_30_DB_NAME, symbol)) strategy.initHourCurrentBar( self.getCurrentBar(self.loadMinuteBar(symbol, 30))) if setting["strategyCycle"] == "5min": strategy.initHourBar(self.loadAllBar(MINUTE_5_DB_NAME, symbol)) strategy.initHourCurrentBar( self.getCurrentBar(self.loadMinuteBar(symbol, 5))) # 创建委托号列表 self.strategyOrderDict[symbol] = set() # 保存Tick映射关系 if strategy.vtSymbol in self.tickStrategyDict: l = self.tickStrategyDict[strategy.vtSymbol] else: l = [] self.tickStrategyDict[strategy.vtSymbol] = l l.append(strategy) # 订阅合约 contract = self.mainEngine.getContract(strategy.vtSymbol) if contract: req = VtSubscribeReq() req.symbol = contract.symbol req.exchange = contract.exchange # 对于IB接口订阅行情时所需的货币和产品类型,从策略属性中获取 req.currency = strategy.currency req.productClass = strategy.productClass self.mainEngine.subscribe(req, contract.gatewayName) else: self.writeCtaLog(u'%s的交易合约%s无法找到' % (name, strategy.vtSymbol))
def subscribeMarketData(self, strategy): """订阅行情""" for vtSymbol in strategy.symbolList: contract = self.mainEngine.getContract(vtSymbol) if contract: req = VtSubscribeReq() req.symbol = contract.symbol req.vtSymbol = contract.vtSymbol req.exchange = contract.exchange # 对于IB接口订阅行情时所需的货币和产品类型,从策略属性中获取 req.currency = strategy.currency req.productClass = strategy.productClass self.mainEngine.subscribe(req, contract.gatewayName) else: self.writeCtaLog(u'策略%s的交易合约%s无法找到' %(strategy.name, vtSymbol))
def loadStrategy(self, setting): """载入策略""" try: name = setting['name'] className = setting['className'] except Exception: msg = traceback.format_exc() self.writeCtaLog(u'载入策略出错:%s' %msg) return # 获取策略类 strategyClass = STRATEGY_CLASS.get(className, None) if not strategyClass: self.writeCtaLog(u'找不到策略类:%s' %className) return # 防止策略重名 if name in self.strategyDict: self.writeCtaLog(u'策略实例重名:%s' %name) else: # 创建策略实例 strategy = strategyClass(self, setting) self.strategyDict[name] = strategy # 创建委托号列表 self.strategyOrderDict[name] = set() # 保存Tick映射关系 if strategy.vtSymbol in self.tickStrategyDict: l = self.tickStrategyDict[strategy.vtSymbol] else: l = [] self.tickStrategyDict[strategy.vtSymbol] = l l.append(strategy) # 订阅合约 contract = self.mainEngine.getContract(strategy.vtSymbol) if contract: req = VtSubscribeReq() req.symbol = contract.symbol req.exchange = contract.exchange # 对于IB接口订阅行情时所需的货币和产品类型,从策略属性中获取 req.currency = strategy.currency req.productClass = strategy.productClass self.mainEngine.subscribe(req, contract.gatewayName) else: self.writeCtaLog(u'%s的交易合约%s无法找到' %(name, strategy.vtSymbol))
def subscribeMarketData(self, strategy): """订阅行情""" # 订阅合约 contract = self._mainRoutine.getContract(strategy.vtSymbol) if contract: req = VtSubscribeReq() req.symbol = contract.symbol req.exchange = contract.exchange # 对于IB接口订阅行情时所需的货币和产品类型,从策略属性中获取 req.currency = strategy.currency req.productClass = strategy.productClass self._mainRoutine.subscribe(req, contract.gatewayName) else: self.log(u'%s的交易合约%s无法找到' % (strategy.name, strategy.vtSymbol))
def updateContract(self, name, strategy): """更新策略""" for vtSymbol in self.tickStrategyDict: if strategy in self.tickStrategyDict[vtSymbol]: self.tickStrategyDict[vtSymbol].remove(strategy) # 保存Tick映射关系 if strategy.vtSymbol in self.tickStrategyDict: l = self.tickStrategyDict[strategy.vtSymbol] else: l = [] # 订阅合约 contract = self.mainEngine.getContract(strategy.vtSymbol) if contract: req = VtSubscribeReq() req.symbol = contract.symbol req.exchange = contract.exchange self.mainEngine.subscribe(req, contract.gatewayName) else: self.writeArbLog(u'%s的交易合约%s无法找到' % (name, strategy.vtSymbol)) self.tickStrategyDict[strategy.vtSymbol] = l l.append(strategy) if strategy.vtSymbol1 and strategy.vtSymbol1 in self.tickStrategyDict: l = self.tickStrategyDict[strategy.vtSymbol1] else: l = [] contract = self.mainEngine.getContract(strategy.vtSymbol1) if contract: req = VtSubscribeReq() req.symbol = contract.symbol req.exchange = contract.exchange self.mainEngine.subscribe(req, contract.gatewayName) else: self.writeArbLog(u'%s的交易合约%s无法找到' % (name, strategy.vtSymbol)) self.tickStrategyDict[strategy.vtSymbol1] = l l.append(strategy)
def subscribe(self, strategy, symbol): """订阅合约,不成功时,加入到待订阅列表""" contract = self.mainEngine.getContract(symbol) if contract: # 4.构造订阅请求包 req = VtSubscribeReq() req.symbol = contract.symbol req.exchange = contract.exchange # 对于IB接口订阅行情时所需的货币和产品类型,从策略属性中获取 req.currency = strategy.currency req.productClass = strategy.productClass # 5.调用主引擎的订阅接口 self.mainEngine.subscribe(req, contract.gatewayName) else: print u'Warning, can not find {0} in contracts'.format(symbol) self.writeCtaLog(u'交易合约{}无法找到,添加到待订阅列表'.format(symbol)) self.pendingSubcribeSymbols[symbol] = strategy
def loadStrategy(self, setting): """载入策略""" try: name = setting['name'] className = setting['className'] except Exception as e: self.writeCtaLog(u'载入策略出错:%s' %e) return # 获取策略类 strategyClass = STRATEGY_CLASS.get(className, None) if not strategyClass: self.writeCtaLog(u'找不到策略类:%s' %className) return # 防止策略重名 if name in self.strategyDict: self.writeCtaLog(u'策略实例重名:%s' %name) else: # 创建策略实例 strategy = strategyClass(self, setting) self.strategyDict[name] = strategy # 创建委托号列表 self.strategyOrderDict[name] = set() # 保存Tick映射关系 if strategy.vtSymbol in self.tickStrategyDict: l = self.tickStrategyDict[strategy.vtSymbol] else: l = [] self.tickStrategyDict[strategy.vtSymbol] = l l.append(strategy) # 订阅合约 # print(strategy.vtSymbol) contract = self.mainEngine.getContract(strategy.vtSymbol) # print '222222222222222222222222222222 ctaEngine.py_line471 contract:', contract if contract: # print(3333333333333333333333333333, 'ctaEngine.py_line473') req = VtSubscribeReq() req.symbol = contract.symbol req.exchange = contract.exchange # 对于IB接口订阅行情时所需的货币和产品类型,从策略属性中获取 req.currency = strategy.currency req.productClass = strategy.productClass # print(req.productClass) #这个的编码待修改 self.mainEngine.subscribe(req, contract.gatewayName) else: # # 下面是zls自己添加的,是否保存待确定 # req = VtSubscribeReq() # req.symbol = 'USD.CNH' # req.exchange = 'IDEALPRO' # # 对于IB接口订阅行情时所需的货币和产品类型,从策略属性中获取 # req.currency = '' # req.productClass = '' # self.mainEngine.subscribe(req, 'IB') self.writeCtaLog(u'%s的交易合约%s无法找到' %(name, strategy.vtSymbol))
def initStrategyBySymbol(self, symbol, setting): # 创建策略实例 # 防止策略重名 if (symbol + setting['className']) in self.strategyDict: self.writeCtaLog(u'策略实例重名:%s' % name) return setting["vtSymbol"] = symbol className = setting['className'] # 获取策略类 strategyClass = STRATEGY_CLASS.get(className, None) if not strategyClass: self.writeCtaLog(u'找不到策略类:%s' % className) return strategy = strategyClass(self, setting) self.strategyDict[symbol + className] = strategy strategy.setFixSize(self.FIX_SIZE_AUDO) # 创建委托号列表 self.strategyOrderDict[symbol + strategy.className] = set() # 保存Tick映射关系 if strategy.vtSymbol in self.tickStrategyDict: l = self.tickStrategyDict[strategy.vtSymbol] else: l = [] self.tickStrategyDict[strategy.vtSymbol] = l l.append(strategy) ##读数据库 traderOrd = TraderOrder() traderOrd.symbol = symbol traderOrd.strategyName = strategy.className traderOrd.offset = OFFSET_OPEN t = self.mainEngine.mysqlClient.dbSelect(SQL_TABLENAME_TRADER, traderOrd, "one") if t: traderOrd.orderID = t["orderID"] traderOrd.orderUuid = t["orderUuid"] traderOrd.direction = t["direction"] traderOrd.orderVolume = t["orderVolume"] traderOrd.orderPrice = t["orderPrice"] traderOrd.tradeVolume = t["tradeVolume"] traderOrd.tradePrice = t["tradePrice"] if traderOrd.offset == OFFSET_OPEN: self.lastOpenTrade[traderOrd.symbol] = traderOrd self.traderDict[traderOrd.orderID] = traderOrd # 订阅合约 contract = self.mainEngine.getContract(strategy.vtSymbol) if contract: strategy.setPriceTick(contract.size, contract.priceTick) req = VtSubscribeReq() req.symbol = contract.symbol req.exchange = contract.exchange # 对于IB接口订阅行情时所需的货币和产品类型,从策略属性中获取 req.currency = strategy.currency req.productClass = strategy.productClass self.mainEngine.subscribe(req, contract.gatewayName) self.writeCtaLog("subscribe===%s" % (strategy.vtSymbol)) else: self.writeCtaLog(u'%s的交易合约%s无法找到' % (name, strategy.vtSymbol))
def loadSetting(self): """载入设置""" with open(self.settingFileName) as f: drSetting = json.load(f) # 如果working设为False则不启动行情记录功能 working = drSetting['working'] if not working: return if 'tick' in drSetting: l = drSetting['tick'] for setting in l: symbol = setting[0] vtSymbol = symbol req = VtSubscribeReq() req.symbol = setting[0] # 针对LTS和IB接口,订阅行情需要交易所代码 if len(setting) >= 3: req.exchange = setting[2] vtSymbol = '.'.join([symbol, req.exchange]) # 针对IB接口,订阅行情需要货币和产品类型 if len(setting) >= 5: req.currency = setting[3] req.productClass = setting[4] self.mainEngine.subscribe(req, setting[1]) drTick = DrTickData() # 该tick实例可以用于缓存部分数据(目前未使用) self.tickDict[vtSymbol] = drTick if 'bar' in drSetting: l = drSetting['bar'] for setting in l: symbol = setting[0] vtSymbol = symbol req = VtSubscribeReq() req.symbol = symbol if len(setting) >= 3: req.exchange = setting[2] vtSymbol = '.'.join([symbol, req.exchange]) if len(setting) >= 5: req.currency = setting[3] req.productClass = setting[4] self.mainEngine.subscribe(req, setting[1]) bar = DrBarData() self.barDict[vtSymbol] = bar if 'active' in drSetting: d = drSetting['active'] # 注意这里的vtSymbol对于IB和LTS接口,应该后缀.交易所 for activeSymbol, vtSymbol in d.items(): self.activeSymbolDict[vtSymbol] = activeSymbol # 启动数据插入线程 self.start() # 注册事件监听 self.registerEvent()
def loadStrategy(self, setting): """载入策略""" try: name = setting['name'] className = setting['className'] except Exception: msg = traceback.format_exc() self.writeMmkLog(u'载入策略出错:%s' % msg) return # 获取策略类 strategyClass = STRATEGY_CLASS.get(className, None) if not strategyClass: self.writeMmkLog(u'找不到策略类:%s' % className) return # 防止策略重名 if name in self.strategyDict: self.writeMmkLog(u'策略实例重名:%s' % name) else: # 创建策略实例 strategy = strategyClass(self, setting) self.strategyDict[name] = strategy # 创建委托号列表 self.strategyOrderDict[name] = set() # 保存Tick映射关系 # if strategy.vtSymbol in self.tickStrategyDict: # l = self.tickStrategyDict[strategy.vtSymbol] # else: # l = [] # self.tickStrategyDict[strategy.vtSymbol] = l # l.append(strategy) for vtsym in strategy.vtSymbol: # 初始化posStrategyNameDict for sym in [vtsym.split('.')[0][:3], vtsym.split('.')[0][3:]]: if sym not in self.posStrategyNameDict: self.posStrategyNameDict[sym] = [strategy.name] elif sym in self.posStrategyNameDict: if self.posStrategyNameDict[sym][0] != strategy.name: self.posStrategyNameDict[sym].append(strategy.name) if vtsym in self.tickStrategyDict: l = self.tickStrategyDict[vtsym] else: l = [] self.tickStrategyDict[vtsym] = l l.append(strategy) # 订阅合约 contract = self.mainEngine.getContract(vtsym) if contract: req = VtSubscribeReq() req.symbol = contract.symbol req.exchange = contract.exchange # 对于IB接口订阅行情时所需的货币和产品类型,从策略属性中获取 req.currency = strategy.currency req.productClass = strategy.productClass self.mainEngine.subscribe(req, contract.gatewayName) else: self.writeMmkLog(u'%s的交易合约%s无法找到' % (name, vtsym))
def loadSetting(self): """载入设置""" with open(self.settingFilePath) as f: drSetting = json.load(f) # 如果working设为False则不启动行情记录功能 working = drSetting['working'] if not working: return if 'tick' in drSetting: l = drSetting['tick'] for setting in l: symbol = setting[0] vtSymbol = symbol req = VtSubscribeReq() req.symbol = setting[0] # 针对LTS和IB接口,订阅行情需要交易所代码 if len(setting)>=3: req.exchange = setting[2] vtSymbol = '.'.join([symbol, req.exchange]) # 针对IB接口,订阅行情需要货币和产品类型 if len(setting)>=5: req.currency = setting[3] req.productClass = setting[4] self.mainEngine.subscribe(req, setting[1]) drTick = DrTickData() # 该tick实例可以用于缓存部分数据(目前未使用) self.tickDict[vtSymbol] = drTick if 'bar' in drSetting: l = drSetting['bar'] for setting in l: symbol = setting[0] vtSymbol = symbol req = VtSubscribeReq() req.symbol = symbol if len(setting)>=3: req.exchange = setting[2] vtSymbol = '.'.join([symbol, req.exchange]) if len(setting)>=5: req.currency = setting[3] req.productClass = setting[4] self.mainEngine.subscribe(req, setting[1]) bar = DrBarData() self.barDict[vtSymbol] = bar if 'active' in drSetting: d = drSetting['active'] # 注意这里的vtSymbol对于IB和LTS接口,应该后缀.交易所 for activeSymbol, vtSymbol in d.items(): self.activeSymbolDict[vtSymbol] = activeSymbol # 启动数据插入线程 self.start() # 注册事件监听 self.registerEvent()
class CtaEngine(object): """CTA策略引擎""" settingFileName = 'CTA_setting.json' settingfilePath = getJsonPath(settingFileName, __file__) STATUS_FINISHED = set( [STATUS_REJECTED, STATUS_CANCELLED, STATUS_ALLTRADED]) #---------------------------------------------------------------------- def __init__(self, mainEngine, eventEngine): """Constructor""" self.mainEngine = mainEngine self.eventEngine = eventEngine # 当前日期 self.today = todayDate() # 保存策略实例的字典 # key为策略名称,value为策略实例,注意策略名称不允许重复 self.strategyDict = {} # 保存vtSymbol和策略实例映射的字典(用于推送tick数据) # 由于可能多个strategy交易同一个vtSymbol,因此key为vtSymbol # value为包含所有相关strategy对象的list self.tickStrategyDict = {} # 保存vtOrderID和strategy对象映射的字典(用于推送order和trade数据) # key为vtOrderID,value为strategy对象 self.orderStrategyDict = {} # 本地停止单编号计数 self.stopOrderCount = 0 # stopOrderID = STOPORDERPREFIX + str(stopOrderCount) # 本地停止单字典 # key为stopOrderID,value为stopOrder对象 self.stopOrderDict = {} # 停止单撤销后不会从本字典中删除 self.workingStopOrderDict = {} # 停止单撤销后会从本字典中删除 # 保存策略名称和委托号列表的字典 # key为name,value为保存orderID(限价+本地停止)的集合 self.strategyOrderDict = {} # 成交号集合,用来过滤已经收到过的成交推送 self.tradeSet = set() # 引擎类型为实盘 self.engineType = ENGINETYPE_TRADING # 注册日式事件类型 self.mainEngine.registerLogEvent(EVENT_CTA_LOG) # 注册事件监听 self.registerEvent() #---------------------------------------------------------------------- def sendOrder(self, vtSymbol, orderType, price, volume, strategy): """发单""" contract = self.mainEngine.getContract(vtSymbol) req = VtOrderReq() req.symbol = contract.symbol req.exchange = contract.exchange req.vtSymbol = contract.vtSymbol req.price = self.roundToPriceTick(contract.priceTick, price) req.volume = volume req.productClass = strategy.productClass req.currency = strategy.currency # 设计为CTA引擎发出的委托只允许使用限价单 req.priceType = PRICETYPE_LIMITPRICE # CTA委托类型映射 if orderType == CTAORDER_BUY: req.direction = DIRECTION_LONG req.offset = OFFSET_OPEN elif orderType == CTAORDER_SELL: req.direction = DIRECTION_SHORT req.offset = OFFSET_CLOSE elif orderType == CTAORDER_SHORT: req.direction = DIRECTION_SHORT req.offset = OFFSET_OPEN elif orderType == CTAORDER_COVER: req.direction = DIRECTION_LONG req.offset = OFFSET_CLOSE # 委托转换 reqList = self.mainEngine.convertOrderReq(req) vtOrderIDList = [] if not reqList: return vtOrderIDList for convertedReq in reqList: vtOrderID = self.mainEngine.sendOrder(convertedReq, contract.gatewayName) # 发单 self.orderStrategyDict[vtOrderID] = strategy # 保存vtOrderID和策略的映射关系 self.strategyOrderDict[strategy.name].add(vtOrderID) # 添加到策略委托号集合中 vtOrderIDList.append(vtOrderID) self.writeCtaLog( u'策略%s发送委托,%s,%s,%s@%s' % (strategy.name, vtSymbol, req.direction, volume, price)) return vtOrderIDList #---------------------------------------------------------------------- def cancelOrder(self, vtOrderID): """撤单""" # 查询报单对象 order = self.mainEngine.getOrder(vtOrderID) # 如果查询成功 if order: # 检查是否报单还有效,只有有效时才发出撤单指令 orderFinished = (order.status == STATUS_ALLTRADED or order.status == STATUS_CANCELLED) if not orderFinished: req = VtCancelOrderReq() req.symbol = order.symbol req.exchange = order.exchange req.frontID = order.frontID req.sessionID = order.sessionID req.orderID = order.orderID self.mainEngine.cancelOrder(req, order.gatewayName) #---------------------------------------------------------------------- def sendStopOrder(self, vtSymbol, orderType, price, volume, strategy): """发停止单(本地实现)""" self.stopOrderCount += 1 stopOrderID = STOPORDERPREFIX + str(self.stopOrderCount) so = StopOrder() so.vtSymbol = vtSymbol so.orderType = orderType so.price = price so.volume = volume so.strategy = strategy so.stopOrderID = stopOrderID so.status = STOPORDER_WAITING if orderType == CTAORDER_BUY: so.direction = DIRECTION_LONG so.offset = OFFSET_OPEN elif orderType == CTAORDER_SELL: so.direction = DIRECTION_SHORT so.offset = OFFSET_CLOSE elif orderType == CTAORDER_SHORT: so.direction = DIRECTION_SHORT so.offset = OFFSET_OPEN elif orderType == CTAORDER_COVER: so.direction = DIRECTION_LONG so.offset = OFFSET_CLOSE # 保存stopOrder对象到字典中 self.stopOrderDict[stopOrderID] = so self.workingStopOrderDict[stopOrderID] = so # 保存stopOrderID到策略委托号集合中 self.strategyOrderDict[strategy.name].add(stopOrderID) # 推送停止单状态 strategy.onStopOrder(so) return [stopOrderID] #---------------------------------------------------------------------- def cancelStopOrder(self, stopOrderID): """撤销停止单""" # 检查停止单是否存在 if stopOrderID in self.workingStopOrderDict: so = self.workingStopOrderDict[stopOrderID] strategy = so.strategy # 更改停止单状态为已撤销 so.status = STOPORDER_CANCELLED # 从活动停止单字典中移除 del self.workingStopOrderDict[stopOrderID] # 从策略委托号集合中移除 s = self.strategyOrderDict[strategy.name] if stopOrderID in s: s.remove(stopOrderID) # 通知策略 strategy.onStopOrder(so) #---------------------------------------------------------------------- def processStopOrder(self, tick): """收到行情后处理本地停止单(检查是否要立即发出)""" vtSymbol = tick.vtSymbol # 首先检查是否有策略交易该合约 if vtSymbol in self.tickStrategyDict: # 遍历等待中的停止单,检查是否会被触发 for so in self.workingStopOrderDict.values(): if so.vtSymbol == vtSymbol: longTriggered = so.direction == DIRECTION_LONG and tick.lastPrice >= so.price # 多头停止单被触发 shortTriggered = so.direction == DIRECTION_SHORT and tick.lastPrice <= so.price # 空头停止单被触发 if longTriggered or shortTriggered: # 买入和卖出分别以涨停跌停价发单(模拟市价单) if so.direction == DIRECTION_LONG: price = tick.upperLimit else: price = tick.lowerLimit # 发出市价委托 self.sendOrder(so.vtSymbol, so.orderType, price, so.volume, so.strategy) # 从活动停止单字典中移除该停止单 del self.workingStopOrderDict[so.stopOrderID] # 从策略委托号集合中移除 s = self.strategyOrderDict[so.strategy.name] if so.stopOrderID in s: s.remove(so.stopOrderID) # 更新停止单状态,并通知策略 so.status = STOPORDER_TRIGGERED so.strategy.onStopOrder(so) #---------------------------------------------------------------------- def processTickEvent(self, event): """处理行情推送""" tick = event.dict_['data'] # 收到tick行情后,先处理本地停止单(检查是否要立即发出) self.processStopOrder(tick) # 推送tick到对应的策略实例进行处理 if tick.vtSymbol in self.tickStrategyDict: # tick时间可能出现异常数据,使用try...except实现捕捉和过滤 try: # 添加datetime字段 if not tick.datetime: tick.datetime = datetime.strptime( ' '.join([tick.date, tick.time]), '%Y%m%d %H:%M:%S.%f') except ValueError: self.writeCtaLog(traceback.format_exc()) return # 逐个推送到策略实例中 l = self.tickStrategyDict[tick.vtSymbol] for strategy in l: self.callStrategyFunc(strategy, strategy.onTick, tick) #---------------------------------------------------------------------- def processOrderEvent(self, event): """处理委托推送""" order = event.dict_['data'] vtOrderID = order.vtOrderID if vtOrderID in self.orderStrategyDict: strategy = self.orderStrategyDict[vtOrderID] # 如果委托已经完成(拒单、撤销、全成),则从活动委托集合中移除 if order.status in self.STATUS_FINISHED: s = self.strategyOrderDict[strategy.name] if vtOrderID in s: s.remove(vtOrderID) self.callStrategyFunc(strategy, strategy.onOrder, order) #---------------------------------------------------------------------- def processTradeEvent(self, event): """处理成交推送""" trade = event.dict_['data'] # 过滤已经收到过的成交回报 if trade.vtTradeID in self.tradeSet: return self.tradeSet.add(trade.vtTradeID) # 将成交推送到策略对象中 if trade.vtOrderID in self.orderStrategyDict: strategy = self.orderStrategyDict[trade.vtOrderID] # 计算策略持仓 if trade.direction == DIRECTION_LONG: strategy.pos += trade.volume else: strategy.pos -= trade.volume self.callStrategyFunc(strategy, strategy.onTrade, trade) # 保存策略持仓到数据库 self.savePosition(strategy) #---------------------------------------------------------------------- def registerEvent(self): """注册事件监听""" self.eventEngine.register(EVENT_TICK, self.processTickEvent) self.eventEngine.register(EVENT_ORDER, self.processOrderEvent) self.eventEngine.register(EVENT_TRADE, self.processTradeEvent) #---------------------------------------------------------------------- def insertData(self, dbName, collectionName, data): """插入数据到数据库(这里的data可以是VtTickData或者VtBarData)""" self.mainEngine.dbInsert(dbName, collectionName, data.__dict__) #---------------------------------------------------------------------- def loadBar(self, dbName, collectionName, days): """从数据库中读取Bar数据,startDate是datetime对象""" startDate = self.today - timedelta(days) d = {'datetime': {'$gte': startDate}} barData = self.mainEngine.dbQuery(dbName, collectionName, d, 'datetime') l = [] for d in barData: bar = VtBarData() bar.__dict__ = d l.append(bar) return l #---------------------------------------------------------------------- def loadTick(self, dbName, collectionName, days): """从数据库中读取Tick数据,startDate是datetime对象""" startDate = self.today - timedelta(days) d = {'datetime': {'$gte': startDate}} tickData = self.mainEngine.dbQuery(dbName, collectionName, d, 'datetime') l = [] for d in tickData: tick = VtTickData() tick.__dict__ = d l.append(tick) return l #---------------------------------------------------------------------- def writeCtaLog(self, content): """快速发出CTA模块日志事件""" log = VtLogData() log.logContent = content log.gatewayName = 'CTA_STRATEGY' event = Event(type_=EVENT_CTA_LOG) event.dict_['data'] = log self.eventEngine.put(event) #---------------------------------------------------------------------- def loadStrategy(self, setting): """载入策略""" try: name = setting['name'] className = setting['className'] except Exception, e: self.writeCtaLog(u'载入策略出错:%s' % e) return # 获取策略类 strategyClass = STRATEGY_CLASS.get(className, None) if not strategyClass: self.writeCtaLog(u'找不到策略类:%s' % className) return # 防止策略重名 if name in self.strategyDict: self.writeCtaLog(u'策略实例重名:%s' % name) else: # 创建策略实例 strategy = strategyClass(self, setting) self.strategyDict[name] = strategy # 创建委托号列表 self.strategyOrderDict[name] = set() # 保存Tick映射关系 if strategy.vtSymbol in self.tickStrategyDict: l = self.tickStrategyDict[strategy.vtSymbol] else: l = [] self.tickStrategyDict[strategy.vtSymbol] = l l.append(strategy) # 订阅合约 contract = self.mainEngine.getContract(strategy.vtSymbol) if contract: req = VtSubscribeReq() req.symbol = contract.symbol req.exchange = contract.exchange # 对于IB接口订阅行情时所需的货币和产品类型,从策略属性中获取 req.currency = strategy.currency req.productClass = strategy.productClass self.mainEngine.subscribe(req, contract.gatewayName) else: self.writeCtaLog(u'%s的交易合约%s无法找到' % (name, strategy.vtSymbol))
class CtaEngine(object): """CTA策略引擎""" settingFileName = 'CTA_setting.json' path = os.path.abspath(os.path.dirname(__file__)) settingFileName = os.path.join(path, settingFileName) #---------------------------------------------------------------------- def __init__(self, mainEngine, eventEngine): """Constructor""" self.mainEngine = mainEngine self.eventEngine = eventEngine # 当前日期 self.today = todayDate() # 保存策略实例的字典 # key为策略名称,value为策略实例,注意策略名称不允许重复 self.strategyDict = {} # 保存vtSymbol和策略实例映射的字典(用于推送tick数据) # 由于可能多个strategy交易同一个vtSymbol,因此key为vtSymbol # value为包含所有相关strategy对象的list self.tickStrategyDict = {} # 保存vtOrderID和strategy对象映射的字典(用于推送order和trade数据) # key为vtOrderID,value为strategy对象 self.orderStrategyDict = {} # 本地停止单编号计数 self.stopOrderCount = 0 # stopOrderID = STOPORDERPREFIX + str(stopOrderCount) # 本地停止单字典 # key为stopOrderID,value为stopOrder对象 self.stopOrderDict = {} # 停止单撤销后不会从本字典中删除 self.workingStopOrderDict = {} # 停止单撤销后会从本字典中删除 # 持仓缓存字典 # key为vtSymbol,value为PositionBuffer对象 self.posBufferDict = {} # 成交号集合,用来过滤已经收到过的成交推送 self.tradeSet = set() # 引擎类型为实盘 self.engineType = ENGINETYPE_TRADING # 注册事件监听 self.registerEvent() #---------------------------------------------------------------------- def sendOrder(self, vtSymbol, orderType, price, volume, strategy): """发单""" contract = self.mainEngine.getContract(vtSymbol) req = VtOrderReq() req.symbol = contract.symbol req.exchange = contract.exchange req.price = self.roundToPriceTick(contract.priceTick, price) req.volume = volume req.productClass = strategy.productClass req.currency = strategy.currency # 设计为CTA引擎发出的委托只允许使用限价单 req.priceType = PRICETYPE_LIMITPRICE # CTA委托类型映射 if orderType == CTAORDER_BUY: req.direction = DIRECTION_LONG req.offset = OFFSET_OPEN elif orderType == CTAORDER_SELL: req.direction = DIRECTION_SHORT # 只有上期所才要考虑平今平昨 if contract.exchange != EXCHANGE_SHFE: req.offset = OFFSET_CLOSE else: # 获取持仓缓存数据 posBuffer = self.posBufferDict.get(vtSymbol, None) # 如果获取持仓缓存失败,则默认平昨 if not posBuffer: req.offset = OFFSET_CLOSE # 否则如果有多头今仓,则使用平今 elif posBuffer.longToday: req.offset= OFFSET_CLOSETODAY # 其他情况使用平昨 else: req.offset = OFFSET_CLOSE elif orderType == CTAORDER_SHORT: req.direction = DIRECTION_SHORT req.offset = OFFSET_OPEN elif orderType == CTAORDER_COVER: req.direction = DIRECTION_LONG # 只有上期所才要考虑平今平昨 if contract.exchange != EXCHANGE_SHFE: req.offset = OFFSET_CLOSE else: # 获取持仓缓存数据 posBuffer = self.posBufferDict.get(vtSymbol, None) # 如果获取持仓缓存失败,则默认平昨 if not posBuffer: req.offset = OFFSET_CLOSE # 否则如果有空头今仓,则使用平今 elif posBuffer.shortToday: req.offset= OFFSET_CLOSETODAY # 其他情况使用平昨 else: req.offset = OFFSET_CLOSE vtOrderID = self.mainEngine.sendOrder(req, contract.gatewayName) # 发单 self.orderStrategyDict[vtOrderID] = strategy # 保存vtOrderID和策略的映射关系 self.writeCtaLog(u'策略%s发送委托,%s,%s,%s@%s' %(strategy.name, vtSymbol, req.direction, volume, price)) return vtOrderID #---------------------------------------------------------------------- def cancelOrder(self, vtOrderID): """撤单""" # 查询报单对象 order = self.mainEngine.getOrder(vtOrderID) # 如果查询成功 if order: # 检查是否报单还有效,只有有效时才发出撤单指令 orderFinished = (order.status==STATUS_ALLTRADED or order.status==STATUS_CANCELLED) if not orderFinished: req = VtCancelOrderReq() req.symbol = order.symbol req.exchange = order.exchange req.frontID = order.frontID req.sessionID = order.sessionID req.orderID = order.orderID self.mainEngine.cancelOrder(req, order.gatewayName) #---------------------------------------------------------------------- def sendStopOrder(self, vtSymbol, orderType, price, volume, strategy): """发停止单(本地实现)""" self.stopOrderCount += 1 stopOrderID = STOPORDERPREFIX + str(self.stopOrderCount) so = StopOrder() so.vtSymbol = vtSymbol so.orderType = orderType so.price = price so.volume = volume so.strategy = strategy so.stopOrderID = stopOrderID so.status = STOPORDER_WAITING if orderType == CTAORDER_BUY: so.direction = DIRECTION_LONG so.offset = OFFSET_OPEN elif orderType == CTAORDER_SELL: so.direction = DIRECTION_SHORT so.offset = OFFSET_CLOSE elif orderType == CTAORDER_SHORT: so.direction = DIRECTION_SHORT so.offset = OFFSET_OPEN elif orderType == CTAORDER_COVER: so.direction = DIRECTION_LONG so.offset = OFFSET_CLOSE # 保存stopOrder对象到字典中 self.stopOrderDict[stopOrderID] = so self.workingStopOrderDict[stopOrderID] = so return stopOrderID #---------------------------------------------------------------------- def cancelStopOrder(self, stopOrderID): """撤销停止单""" # 检查停止单是否存在 if stopOrderID in self.workingStopOrderDict: so = self.workingStopOrderDict[stopOrderID] so.status = STOPORDER_CANCELLED del self.workingStopOrderDict[stopOrderID] #---------------------------------------------------------------------- def processStopOrder(self, tick): """收到行情后处理本地停止单(检查是否要立即发出)""" vtSymbol = tick.vtSymbol # 首先检查是否有策略交易该合约 if vtSymbol in self.tickStrategyDict: # 遍历等待中的停止单,检查是否会被触发 for so in self.workingStopOrderDict.values(): if so.vtSymbol == vtSymbol: longTriggered = so.direction==DIRECTION_LONG and tick.lastPrice>=so.price # 多头停止单被触发 shortTriggered = so.direction==DIRECTION_SHORT and tick.lastPrice<=so.price # 空头停止单被触发 if longTriggered or shortTriggered: # 买入和卖出分别以涨停跌停价发单(模拟市价单) if so.direction==DIRECTION_LONG: price = tick.upperLimit else: price = tick.lowerLimit so.status = STOPORDER_TRIGGERED self.sendOrder(so.vtSymbol, so.orderType, price, so.volume, so.strategy) del self.workingStopOrderDict[so.stopOrderID] #---------------------------------------------------------------------- def processTickEvent(self, event): """处理行情推送""" tick = event.dict_['data'] # 收到tick行情后,先处理本地停止单(检查是否要立即发出) self.processStopOrder(tick) # 推送tick到对应的策略实例进行处理 if tick.vtSymbol in self.tickStrategyDict: # 添加datetime字段 if not tick.datetime: tick.datetime = datetime.strptime(' '.join([tick.date, tick.time]), '%Y%m%d %H:%M:%S.%f') # 逐个推送到策略实例中 l = self.tickStrategyDict[tick.vtSymbol] for strategy in l: self.callStrategyFunc(strategy, strategy.onTick, tick) #---------------------------------------------------------------------- def processOrderEvent(self, event): """处理委托推送""" order = event.dict_['data'] if order.vtOrderID in self.orderStrategyDict: strategy = self.orderStrategyDict[order.vtOrderID] self.callStrategyFunc(strategy, strategy.onOrder, order) #---------------------------------------------------------------------- def processTradeEvent(self, event): """处理成交推送""" trade = event.dict_['data'] # 过滤已经收到过的成交回报 if trade.vtTradeID in self.tradeSet: return self.tradeSet.add(trade.vtTradeID) # 将成交推送到策略对象中 if trade.vtOrderID in self.orderStrategyDict: strategy = self.orderStrategyDict[trade.vtOrderID] # 计算策略持仓 if trade.direction == DIRECTION_LONG: strategy.pos += trade.volume else: strategy.pos -= trade.volume self.callStrategyFunc(strategy, strategy.onTrade, trade) # 更新持仓缓存数据 if trade.vtSymbol in self.tickStrategyDict: posBuffer = self.posBufferDict.get(trade.vtSymbol, None) if not posBuffer: posBuffer = PositionBuffer() posBuffer.vtSymbol = trade.vtSymbol self.posBufferDict[trade.vtSymbol] = posBuffer posBuffer.updateTradeData(trade) #---------------------------------------------------------------------- def processPositionEvent(self, event): """处理持仓推送""" pos = event.dict_['data'] # 更新持仓缓存数据 if pos.vtSymbol in self.tickStrategyDict: posBuffer = self.posBufferDict.get(pos.vtSymbol, None) if not posBuffer: posBuffer = PositionBuffer() posBuffer.vtSymbol = pos.vtSymbol self.posBufferDict[pos.vtSymbol] = posBuffer posBuffer.updatePositionData(pos) #---------------------------------------------------------------------- def registerEvent(self): """注册事件监听""" self.eventEngine.register(EVENT_TICK, self.processTickEvent) self.eventEngine.register(EVENT_ORDER, self.processOrderEvent) self.eventEngine.register(EVENT_TRADE, self.processTradeEvent) self.eventEngine.register(EVENT_POSITION, self.processPositionEvent) #---------------------------------------------------------------------- def insertData(self, dbName, collectionName, data): """插入数据到数据库(这里的data可以是VtTickData或者VtBarData)""" self.mainEngine.dbInsert(dbName, collectionName, data.__dict__) #---------------------------------------------------------------------- def loadBar(self, dbName, collectionName, days): """从数据库中读取Bar数据,startDate是datetime对象""" startDate = self.today - timedelta(days) d = {'datetime':{'$gte':startDate}} barData = self.mainEngine.dbQuery(dbName, collectionName, d) l = [] for d in barData: bar = VtBarData() bar.__dict__ = d l.append(bar) return l #---------------------------------------------------------------------- def loadTick(self, dbName, collectionName, days): """从数据库中读取Tick数据,startDate是datetime对象""" startDate = self.today - timedelta(days) d = {'datetime':{'$gte':startDate}} tickData = self.mainEngine.dbQuery(dbName, collectionName, d) l = [] for d in tickData: tick = VtTickData() tick.__dict__ = d l.append(tick) return l #---------------------------------------------------------------------- def writeCtaLog(self, content): """快速发出CTA模块日志事件""" log = VtLogData() log.logContent = content event = Event(type_=EVENT_CTA_LOG) event.dict_['data'] = log self.eventEngine.put(event) #---------------------------------------------------------------------- def loadStrategy(self, setting): """载入策略""" try: name = setting['name'] className = setting['className'] except Exception, e: self.writeCtaLog(u'载入策略出错:%s' %e) return # 获取策略类 strategyClass = STRATEGY_CLASS.get(className, None) if not strategyClass: self.writeCtaLog(u'找不到策略类:%s' %className) return # 防止策略重名 if name in self.strategyDict: self.writeCtaLog(u'策略实例重名:%s' %name) else: # 创建策略实例 strategy = strategyClass(self, setting) self.strategyDict[name] = strategy # 保存Tick映射关系 if strategy.vtSymbol in self.tickStrategyDict: l = self.tickStrategyDict[strategy.vtSymbol] else: l = [] self.tickStrategyDict[strategy.vtSymbol] = l l.append(strategy) # 订阅合约 contract = self.mainEngine.getContract(strategy.vtSymbol) if contract: req = VtSubscribeReq() req.symbol = contract.symbol req.exchange = contract.exchange # 对于IB接口订阅行情时所需的货币和产品类型,从策略属性中获取 req.currency = strategy.currency req.productClass = strategy.productClass self.mainEngine.subscribe(req, contract.gatewayName) else: self.writeCtaLog(u'%s的交易合约%s无法找到' %(name, strategy.vtSymbol))
def loadSetting(self): """载入设置""" with open(self.settingFilePath) as f: drSetting = json.load(f) # 如果working设为False则不启动行情记录功能 working = drSetting['working'] if not working: return if 'tick' in drSetting: l = drSetting['tick'] for setting in l: symbol = setting[0] vtSymbol = symbol req = VtSubscribeReq() req.symbol = setting[0] # 针对LTS和IB接口,订阅行情需要交易所代码 if len(setting) >= 3: req.exchange = setting[2] vtSymbol = '.'.join([symbol, req.exchange]) # 针对IB接口,订阅行情需要货币和产品类型 if len(setting) >= 5: req.currency = setting[3] req.productClass = setting[4] self.mainEngine.subscribe(req, setting[1]) drTick = DrTickData() # 该tick实例可以用于缓存部分数据(目前未使用) self.tickDict[vtSymbol] = drTick if 'bar' in drSetting: l = drSetting['bar'] for setting in l: symbol = setting[0] vtSymbol = symbol req = VtSubscribeReq() req.symbol = symbol if len(setting) >= 3: req.exchange = setting[2] vtSymbol = '.'.join([symbol, req.exchange]) if len(setting) >= 5: req.currency = setting[3] req.productClass = setting[4] self.mainEngine.subscribe(req, setting[1]) bar = DrBarData() self.barDict[vtSymbol] = bar if 'renko' in drSetting: l = drSetting.get('renko') req_set = set() for setting in l: # 获取合约,合约短号,renko的高度(多少个跳) vtSymbol = setting.get('vtSymbol', None) if vtSymbol is None: continue short_symbol = getShortSymbol(vtSymbol).upper() height = setting.get('height', 5) minDiff = setting.get('minDiff', 1) # 获取vtSymbol的多个renkobar列表,添加新的CtaRenkoBar renko_list = self.renkoDict.get(vtSymbol, []) bar_setting = { 'name': '{}_{}'.format(vtSymbol, height), 'shortSymbol': short_symbol, 'vtSymbol': vtSymbol, 'minDiff': minDiff, 'height': minDiff * height } renko_bar = CtaRenkoBar(strategy=None, onBarFunc=self.onRenkoBar, setting=bar_setting) renko_list.append(renko_bar) self.renkoDict.update({vtSymbol: renko_list}) req = VtSubscribeReq() req.symbol = vtSymbol req_set.add((req, setting.get('gateway', None))) # 更新合约的历史数据 self.add_gap_ticks() # 订阅行情 for req, gw in req_set: self.mainEngine.subscribe(req, gw) if 'active' in drSetting: d = drSetting['active'] # 注意这里的vtSymbol对于IB和LTS接口,应该后缀.交易所 for activeSymbol, vtSymbol in d.items(): self.activeSymbolDict[vtSymbol] = activeSymbol # 启动数据插入线程 self.start() # 注册事件监听 self.registerEvent()