def processContractsEvent(self, event): contract_data = event.dict_['data'] nl = [] # 交易的合约代码也应可以用配置文件配置 tradingContractData = [] # 打开设置文件 with open(getJsonPath(self.settingFileName, __file__)) as f: drsettings = json.load(f) if 'active' in drsettings: actives = drsettings['active'] if actives != {}: for i in actives: tradingContractData.append(i[:-4]) for ocn in contract_data: contract = ocn.decode() if contract[:1] in tradingContractData or contract[:2] in tradingContractData: nl.append([contract, "CTP"]) from operator import itemgetter, attrgetter filename = 'openInterest.json' # 打开持仓量文件 with open(getJsonPath(filename, __file__)) as f: openinterests = json.load(f) if 'oi' in openinterests: ois = openinterests['oi'] for item in tradingContractData: oiarray = [] # 获取持仓量列表 for oi in ois: if oi[:1] == item or oi[:2] == item: oiarray.append([oi, ois[oi]]) # 排序持仓量列表 if oiarray != []: sortedioarray = sorted(oiarray, key=itemgetter(1), reverse=True) if sortedioarray[0][1] / sortedioarray[1][1] > 1.1 and sortedioarray[0][0] > sortedioarray[1][0]: if actives[item + '.HOT'] != sortedioarray[0][0]: actives[item + '.HOT'] = sortedioarray[0][0] json_data = {'working': True, 'tick': {}, 'bar': nl, 'active': actives} d1 = json.dumps(json_data, sort_keys=True, indent=4) f = open(os.path.join(os.getcwd(), self.settingFileName), 'w') f.write(d1) f.close() self.loadSetting()
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 list(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 = copy(tick) # 收到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.saveSyncData(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: 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) #---------------------------------------------------------------------- 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 initStrategy(self, name): """初始化策略""" if name in self.strategyDict: strategy = self.strategyDict[name] if not strategy.inited: strategy.inited = True self.callStrategyFunc(strategy, strategy.onInit) self.loadSyncData(strategy) # 初始化完成后加载同步数据 self.subscribeMarketData(strategy) # 加载同步数据后再订阅行情 else: self.writeCtaLog(u'请勿重复初始化策略实例:%s' %name) else: self.writeCtaLog(u'策略实例不存在:%s' %name) #--------------------------------------------------------------------- def startStrategy(self, name): """启动策略""" if name in self.strategyDict: strategy = self.strategyDict[name] if strategy.inited and not strategy.trading: strategy.trading = True self.callStrategyFunc(strategy, strategy.onStart) else: self.writeCtaLog(u'策略实例不存在:%s' %name) #---------------------------------------------------------------------- def stopStrategy(self, name): """停止策略""" if name in self.strategyDict: strategy = self.strategyDict[name] if strategy.trading: strategy.trading = False self.callStrategyFunc(strategy, strategy.onStop) # 对该策略发出的所有限价单进行撤单 for vtOrderID, s in self.orderStrategyDict.items(): if s is strategy: self.cancelOrder(vtOrderID) # 对该策略发出的所有本地停止单撤单 for stopOrderID, so in self.workingStopOrderDict.items(): if so.strategy is strategy: self.cancelStopOrder(stopOrderID) else: self.writeCtaLog(u'策略实例不存在:%s' %name) #---------------------------------------------------------------------- def initAll(self): """全部初始化""" for name in self.strategyDict.keys(): self.initStrategy(name) #---------------------------------------------------------------------- def startAll(self): """全部启动""" for name in self.strategyDict.keys(): self.startStrategy(name) #---------------------------------------------------------------------- def stopAll(self): """全部停止""" for name in self.strategyDict.keys(): self.stopStrategy(name) #---------------------------------------------------------------------- def saveSetting(self): """保存策略配置""" with open(self.settingfilePath, 'w') as f: l = [] for strategy in self.strategyDict.values(): setting = {} for param in strategy.paramList: setting[param] = strategy.__getattribute__(param) l.append(setting) jsonL = json.dumps(l, indent=4) f.write(jsonL) #---------------------------------------------------------------------- def loadSetting(self): """读取策略配置""" with open(self.settingfilePath) as f: l = json.load(f) for setting in l: self.loadStrategy(setting) #---------------------------------------------------------------------- def getStrategyVar(self, name): """获取策略当前的变量字典""" if name in self.strategyDict: strategy = self.strategyDict[name] varDict = OrderedDict() for key in strategy.varList: varDict[key] = strategy.__getattribute__(key) return varDict else: self.writeCtaLog(u'策略实例不存在:' + name) return None #---------------------------------------------------------------------- def getStrategyParam(self, name): """获取策略的参数字典""" if name in self.strategyDict: strategy = self.strategyDict[name] paramDict = OrderedDict() for key in strategy.paramList: paramDict[key] = strategy.__getattribute__(key) return paramDict else: self.writeCtaLog(u'策略实例不存在:' + name) return None #---------------------------------------------------------------------- def getStrategyNames(self): """查询所有策略名称""" return self.strategyDict.keys() #---------------------------------------------------------------------- def putStrategyEvent(self, name): """触发策略状态变化事件(通常用于通知GUI更新)""" strategy = self.strategyDict[name] d = {k:strategy.__getattribute__(k) for k in strategy.varList} event = Event(EVENT_CTA_STRATEGY+name) event.dict_['data'] = d self.eventEngine.put(event) d2 = {k:str(v) for k,v in d.items()} d2['name'] = name event2 = Event(EVENT_CTA_STRATEGY) event2.dict_['data'] = d2 self.eventEngine.put(event2) #---------------------------------------------------------------------- def callStrategyFunc(self, strategy, func, params=None): """调用策略的函数,若触发异常则捕捉""" try: if params: func(params) else: func() except Exception: # 停止策略,修改状态为未初始化 strategy.trading = False strategy.inited = False # 发出日志 content = '\n'.join([u'策略%s触发异常已停止' %strategy.name, traceback.format_exc()]) self.writeCtaLog(content) #---------------------------------------------------------------------- def saveSyncData(self, strategy): """保存策略的持仓情况到数据库""" flt = {'name': strategy.name, 'vtSymbol': strategy.vtSymbol} d = copy(flt) for key in strategy.syncList: d[key] = strategy.__getattribute__(key) self.mainEngine.dbUpdate(POSITION_DB_NAME, strategy.className, d, flt, True) content = u'策略%s同步数据保存成功,当前持仓%s' %(strategy.name, strategy.pos) self.writeCtaLog(content) #---------------------------------------------------------------------- def loadSyncData(self, strategy): """从数据库载入策略的持仓情况""" flt = {'name': strategy.name, 'vtSymbol': strategy.vtSymbol} syncData = self.mainEngine.dbQuery(POSITION_DB_NAME, strategy.className, flt) if not syncData: return d = syncData[0] for key in strategy.syncList: if key in d: strategy.__setattr__(key, d[key]) #---------------------------------------------------------------------- def roundToPriceTick(self, priceTick, price): """取整价格到合约最小价格变动""" if not priceTick: return price newPrice = round(price/priceTick, 0) * priceTick return newPrice #---------------------------------------------------------------------- def stop(self): """停止""" pass #---------------------------------------------------------------------- def cancelAll(self, name): """全部撤单""" s = self.strategyOrderDict[name] # 遍历列表,全部撤单 # 这里不能直接遍历集合s,因为撤单时会修改s中的内容,导致出错 for orderID in list(s): if STOPORDERPREFIX in orderID: self.cancelStopOrder(orderID) else: self.cancelOrder(orderID) #---------------------------------------------------------------------- def getPriceTick(self, strategy): """获取最小价格变动""" contract = self.mainEngine.getContract(strategy.vtSymbol) if contract: return contract.priceTick return 0
class RsEngine(object): """RPC服务引擎""" settingFileName = 'RS_setting.json' settingFilePath = getJsonPath(settingFileName, __file__) name = u'RPC服务' #---------------------------------------------------------------------- def __init__(self, mainEngine, eventEngine): """Constructor""" self.mainEngine = mainEngine self.eventEngine = eventEngine self.server = None # RPC服务对象 self.repAddress = EMPTY_STRING # REP地址 self.pubAddress = EMPTY_STRING # PUB地址 self.functionDict = {} # 调用过的函数对象缓存字典 self.loadSetting() self.registerEvent() #---------------------------------------------------------------------- def loadSetting(self): """读取配置""" with open(self.settingFilePath) as f: d = json.load(f) self.repAddress = d['repAddress'] self.pubAddress = d['pubAddress'] self.server = RpcServer(self.repAddress, self.pubAddress) self.server.usePickle() self.server.register(self.call) self.server.start() #---------------------------------------------------------------------- def registerEvent(self): """注册事件监听""" self.eventEngine.registerGeneralHandler(self.processEvent) #---------------------------------------------------------------------- def call(self, d): """调用函数""" nameList = d['nameList'] # 对象属性列表 nameTuple = tuple(nameList) # 转化为元组 args = d['args'] # 调用参数 kwargs = d['kwargs'] # 如果已经有缓存,则直接调用 if nameTuple in self.functionDict: function = self.functionDict[nameTuple] result = function(*args, **kwargs) return result # 逐层寻找函数对象 else: # 根对象为主引擎 obj = self.mainEngine # 逐层寻找对象属性 for name in nameTuple: obj = obj.__getattribute__(name) # 缓存结果 self.functionDict[nameTuple] = obj # 调用最终对象 result = obj(*args, **kwargs) return result #---------------------------------------------------------------------- def processEvent(self, event): """处理事件推送""" self.server.publish(''.encode(encoding='utf-8'), event) #---------------------------------------------------------------------- def stop(self): """停止""" self.server.stop()
class DrEngine(object): """数据记录引擎""" settingFileName = 'DR_setting.json' settingFilePath = getJsonPath(settingFileName, __file__) #---------------------------------------------------------------------- def __init__(self, mainEngine, eventEngine): """Constructor""" self.mainEngine = mainEngine self.eventEngine = eventEngine # 当前日期 self.today = todayDate() # 主力合约代码映射字典,key为具体的合约代码(如IF1604),value为主力合约代码(如IF0000) self.activeSymbolDict = {} # Tick对象字典 self.tickSymbolSet = set() self.barSymbolSet = set() # K线合成器字典 self.bmDict = {} # 配置字典 self.settingDict = OrderedDict() # 负责执行数据库插入的单独线程相关 self.active = False # 工作状态 self.queue = Queue() # 队列 self.thread = Thread(target=self.run) # 线程 # 载入设置,订阅行情 #self.loadSetting() # 启动数据插入线程 self.start() # 注册事件监听 self.registerEvent() #---------------------------------------------------------------------- def loadSetting(self): """加载配置""" with open(self.settingFilePath) as f: drSetting = json.load(f) # 如果working设为False则不启动行情记录功能 working = drSetting['working'] if not working: return # Tick记录配置 if 'tick' in drSetting: l = drSetting['tick'] for setting in l: symbol = setting[0] gateway = setting[1] 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, gateway) #tick = VtTickData() # 该tick实例可以用于缓存部分数据(目前未使用) #self.tickDict[vtSymbol] = tick self.tickSymbolSet.add(vtSymbol) # 保存到配置字典中 if vtSymbol not in self.settingDict: d = { 'symbol': symbol, 'gateway': gateway, 'tick': True } self.settingDict[vtSymbol] = d else: d = self.settingDict[vtSymbol] d['tick'] = True # 分钟线记录配置 if 'bar' in drSetting: l = drSetting['bar'] for setting in l: symbol = setting[0] gateway = setting[1] 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, gateway) self.barSymbolSet.add(vtSymbol) # 保存到配置字典中 if vtSymbol not in self.settingDict: d = { 'symbol': symbol, 'gateway': gateway, 'bar': True } self.settingDict[vtSymbol] = d else: d = self.settingDict[vtSymbol] d['bar'] = True # 创建BarManager对象 #self.bmDict[vtSymbol] = BarGenerator(self.onBar) self.bmDict[vtSymbol] = RecorderBarManager(self.onBar, self.onXBar) # 主力合约记录配置 if 'active' in drSetting: d = drSetting['active'] self.activeSymbolDict = {vtSymbol:activeSymbol for activeSymbol, vtSymbol in d.items()} #---------------------------------------------------------------------- def getSetting(self): """获取配置""" return self.settingDict, self.activeSymbolDict #---------------------------------------------------------------------- def procecssTickEvent(self, event): """处理行情事件""" tick = event.dict_['data'] vtSymbol = tick.vtSymbol # 生成datetime对象 if not tick.datetime: tick.datetime = datetime.strptime(' '.join([tick.date, tick.time]), '%Y%m%d %H:%M:%S') self.onTick(tick) bm = self.bmDict.get(vtSymbol, None) if bm: bm.updateTick(tick) def isDirtyData(self,tick): """ 判断脏数据 """ #期货判断交易时间 dt = datetime.now().time() # 如果在交易事件内,则为有效数据,无需清洗 if ((MORNING_START <= dt < MORNING_REST) or (MORNING_RESTART <= dt < MORNING_END) or (AFTERNOON_START <= dt < AFTERNOON_END) or (dt >= NIGHT_START) or (dt < NIGHT_END)): return False #中间启停去掉脏数据 if not tick.datetime: tick.datetime = datetime.strptime(' '.join([tick.date, tick.time]), '%Y%m%d %H:%M:%S') #判断脏数据 time_now = datetime.now() time_delt = (tick.datetime - time_now).total_seconds() if time_delt < 180 and time_delt > -180: #大于3分钟 脏数据 return False return True #---------------------------------------------------------------------- def onTick(self, tick): """Tick更新""" vtSymbol = tick.vtSymbol if vtSymbol in self.tickSymbolSet: self.insertData(TICK_DB_NAME, vtSymbol, tick) if vtSymbol in self.activeSymbolDict: activeSymbol = self.activeSymbolDict[vtSymbol] self.insertData(TICK_DB_NAME, activeSymbol, tick) self.writeDrLog(text.TICK_LOGGING_MESSAGE.format(symbol=tick.vtSymbol, time=tick.time, last=tick.lastPrice, bid=tick.bidPrice1, ask=tick.askPrice1)) #---------------------------------------------------------------------- def onBar(self, bar): """分钟线更新""" vtSymbol = bar.vtSymbol self.insertData(MINUTE_DB_NAME, vtSymbol, bar) bm = self.bmDict.get(vtSymbol, None) if bm: bm.updateBar(bar) if vtSymbol in self.activeSymbolDict: activeSymbol = self.activeSymbolDict[vtSymbol] self.insertData(MINUTE_DB_NAME, activeSymbol, bar) self.writeDrLog(text.BAR_LOGGING_MESSAGE.format(symbol=bar.vtSymbol, time=bar.time, open=bar.open, high=bar.high, low=bar.low, close=bar.close)) # ---------------------------------------------------------------------- def onXBar(self, xmin, bar): """X分钟线更新""" vtSymbol = bar.vtSymbol self.insertData(MINUTE_TO_DB_NAME[xmin], vtSymbol, bar) if vtSymbol in self.activeSymbolDict: activeSymbol = self.activeSymbolDict[vtSymbol] self.insertData(MINUTE_TO_DB_NAME[xmin], activeSymbol, bar) self.writeDrLog(text.BAR_LOGGING_MESSAGE.format(symbol=bar.vtSymbol, time=bar.time, open=bar.open, high=bar.high, low=bar.low, close=bar.close)) #---------------------------------------------------------------------- def registerEvent(self): """注册事件监听""" self.eventEngine.register(EVENT_TICK, self.procecssTickEvent) self.eventEngine.register(EVENT_RECORDER_DAY, self.handleRecorderDay) self.eventEngine.register(EVENT_ALLCONTRACTS, self.processContractsEvent) #-------------------------------------------------------------------------- def processContractsEvent(self, event): contract_data = event.dict_['data'] nl = [] # 交易的合约代码也应可以用配置文件配置 tradingContractData = [] # 打开设置文件 with open(getJsonPath(self.settingFileName, __file__)) as f: drsettings = json.load(f) if 'active' in drsettings: actives = drsettings['active'] if actives != {}: for i in actives: tradingContractData.append(i[:-4]) for ocn in contract_data: contract = ocn.decode() if contract[:1] in tradingContractData or contract[:2] in tradingContractData: nl.append([contract, "CTP"]) from operator import itemgetter, attrgetter filename = 'openInterest.json' # 打开持仓量文件 with open(getJsonPath(filename, __file__)) as f: openinterests = json.load(f) if 'oi' in openinterests: ois = openinterests['oi'] for item in tradingContractData: oiarray = [] # 获取持仓量列表 for oi in ois: if oi[:1] == item or oi[:2] == item: oiarray.append([oi, ois[oi]]) # 排序持仓量列表 if oiarray != []: sortedioarray = sorted(oiarray, key=itemgetter(1), reverse=True) if sortedioarray[0][1] / sortedioarray[1][1] > 1.1 and sortedioarray[0][0] > sortedioarray[1][0]: if actives[item + '.HOT'] != sortedioarray[0][0]: actives[item + '.HOT'] = sortedioarray[0][0] json_data = {'working': True, 'tick': {}, 'bar': nl, 'active': actives} d1 = json.dumps(json_data, sort_keys=True, indent=4) f = open(os.path.join(os.getcwd(), self.settingFileName), 'w') f.write(d1) f.close() self.loadSetting() # #------------------------------------------------------------------------ # def saveContractMain(self): # # from operator import itemgetter, attrgetter # filename = 'openInterest.json' # activefilename = 'active.json' # # 交易的合约代码也应可以用配置文件配置 # tradingContractData = [] # # 打开设置文件 # with open(getJsonPath(activefilename, __file__)) as f: # activejson = json.load(f) # if 'active' in activejson: # actives = activejson['active'] # if actives != {}: # for i in actives: # tradingContractData.append(i) # activejson['active'] = {} # # 打开持仓量文件 # with open(getJsonPath(filename, __file__)) as f: # openinterests = json.load(f) # if 'oi' in openinterests: # ois = openinterests['oi'] # for item in tradingContractData: # oiarray = [] # # 获取持仓量列表 # for oi in ois: # if oi[:1] == item or oi[:2] == item: # oiarray.append([oi, ois[oi]]) # # 排序持仓量列表 # if oiarray != []: # sortedioarray = sorted(oiarray, key=itemgetter(1), reverse=True) # if sortedioarray[0][1] / sortedioarray[1][1] > 1.1: # if actives[item] != sortedioarray[0][0]: # actives[item] = sortedioarray[0][0] # # 把主力合约的代码添加到设置文件中 # activejson['active'] = actives # d1 = json.dumps(activejson, sort_keys=True, indent=4) # f = open(os.path.join(os.getcwd(), activefilename), 'w') # f.write(d1) # f.close() #-------------------------------------------------------------------------- ##处理日线数据 def handleRecorderDay(self, event): """从数据库中读取Bar数据,startDate是datetime对象""" oi = {} for contact_ in self.barSymbolSet: time_now = datetime.now() if datetime.today().weekday() == 0: #周一接上周五的夜盘 time_yes = time_now + timedelta(-3) else: time_yes = time_now + timedelta(-1) startDate = datetime(time_yes.year, time_yes.month,time_yes.day,21) #前一天 9点 d = {'datetime':{'$gte':startDate}} barData = self.mainEngine.dbQuery(MINUTE_15_DB_NAME, contact_, d, 'datetime') day_bar =None for bar in barData: # 尚未创建对象 if not day_bar: day_bar = VtBarData() day_bar.vtSymbol = bar['vtSymbol'] day_bar.symbol = bar['symbol'] day_bar.exchange = bar['exchange'] day_bar.open = bar['open'] day_bar.high = bar['high'] day_bar.low = bar['low'] # 累加老K线 else: day_bar.high = max(day_bar.high, bar['high']) day_bar.low = min(day_bar.low, bar['low']) # 通用部分 day_bar.close = bar['close'] day_bar.datetime = bar['datetime'] day_bar.openInterest = bar['openInterest'] day_bar.volume += int(bar['volume']) if day_bar: day_bar.datetime = datetime(time_now.year, time_now.month,time_now.day) day_bar.date = day_bar.datetime.strftime('%Y%m%d') day_bar.time = day_bar.datetime.strftime('%H:%M:%S') self.mainEngine.dbInsert(DAILY_DB_NAME, contact_, day_bar.__dict__) if contact_ in self.activeSymbolDict: activeSymbol = self.activeSymbolDict[contact_] self.mainEngine.dbInsert(DAILY_DB_NAME, activeSymbol, day_bar.__dict__) # 写入持仓量数据 oi[day_bar.symbol] = day_bar.openInterest # 保存持仓量数据 filename = 'openInterest.json' json_data = {'oi': oi} d1 = json.dumps(json_data, sort_keys=True, indent=4) f = open(os.path.join(os.getcwd(), filename), 'w') f.write(d1) f.close() #---------------------------------------------------------------------- def insertData(self, dbName, collectionName, data): """插入数据到数据库(这里的data可以是VtTickData或者VtBarData)""" self.queue.put((dbName, collectionName, data.__dict__)) #---------------------------------------------------------------------- def run(self): """运行插入线程""" while self.active: try: dbName, collectionName, d = self.queue.get(block=True, timeout=1) # 这里采用MongoDB的update模式更新数据,在记录tick数据时会由于查询 # 过于频繁,导致CPU占用和硬盘读写过高后系统卡死,因此不建议使用 #flt = {'datetime': d['datetime']} #self.mainEngine.dbUpdate(dbName, collectionName, d, flt, True) # 使用insert模式更新数据,可能存在时间戳重复的情况,需要用户自行清洗 try: self.mainEngine.dbInsert(dbName, collectionName, d) except DuplicateKeyError: self.writeDrLog(u'键值重复插入失败,报错信息:%s' %traceback.format_exc()) except Empty: pass #---------------------------------------------------------------------- def start(self): """启动""" self.active = True self.thread.start() #---------------------------------------------------------------------- def stop(self): """退出""" if self.active: self.active = False self.thread.join() #---------------------------------------------------------------------- def writeDrLog(self, content): """快速发出日志事件""" log = VtLogData() log.logContent = content event = Event(type_=EVENT_DATARECORDER_LOG) event.dict_['data'] = log self.eventEngine.put(event)
class RmEngine(object): """风控引擎""" settingFileName = 'RM_setting.json' settingFilePath = getJsonPath(settingFileName, __file__) name = u'风控模块' #---------------------------------------------------------------------- def __init__(self, mainEngine, eventEngine): """Constructor""" self.mainEngine = mainEngine self.eventEngine = eventEngine # 绑定自身到主引擎的风控引擎引用上 mainEngine.rmEngine = self # 是否启动风控 self.active = False # 流控相关 self.orderFlowCount = EMPTY_INT # 单位时间内委托计数 self.orderFlowLimit = EMPTY_INT # 委托限制 self.orderFlowClear = EMPTY_INT # 计数清空时间(秒) self.orderFlowTimer = EMPTY_INT # 计数清空时间计时 # 单笔委托相关 self.orderSizeLimit = EMPTY_INT # 单笔委托最大限制 # 成交统计相关 self.tradeCount = EMPTY_INT # 当日成交合约数量统计 self.tradeLimit = EMPTY_INT # 当日成交合约数量限制 # 单品种撤单统计 self.orderCancelLimit = EMPTY_INT # 撤单总次数限制 self.orderCancelDict = {} # 单一合约对应撤单次数的字典 # 活动合约相关 self.workingOrderLimit = EMPTY_INT # 活动合约最大限制 # 保证金相关 self.marginRatioDict = {} # 保证金占账户净值比例字典 self.marginRatioLimit = EMPTY_FLOAT # 最大比例限制 self.loadSetting() self.registerEvent() #---------------------------------------------------------------------- def loadSetting(self): """读取配置""" with open(self.settingFilePath) as f: d = json.load(f) # 设置风控参数 self.active = d['active'] self.orderFlowLimit = d['orderFlowLimit'] self.orderFlowClear = d['orderFlowClear'] self.orderSizeLimit = d['orderSizeLimit'] self.tradeLimit = d['tradeLimit'] self.workingOrderLimit = d['workingOrderLimit'] self.orderCancelLimit = d['orderCancelLimit'] self.marginRatioLimit = d['marginRatioLimit'] #---------------------------------------------------------------------- def saveSetting(self): """保存风控参数""" with open(self.settingFilePath, 'w') as f: # 保存风控参数 d = {} d['active'] = self.active d['orderFlowLimit'] = self.orderFlowLimit d['orderFlowClear'] = self.orderFlowClear d['orderSizeLimit'] = self.orderSizeLimit d['tradeLimit'] = self.tradeLimit d['workingOrderLimit'] = self.workingOrderLimit d['orderCancelLimit'] = self.orderCancelLimit d['marginRatioLimit'] = self.marginRatioLimit # 写入json jsonD = json.dumps(d, indent=4) f.write(jsonD) #---------------------------------------------------------------------- def registerEvent(self): """注册事件监听""" self.eventEngine.register(EVENT_TRADE, self.updateTrade) self.eventEngine.register(EVENT_TIMER, self.updateTimer) self.eventEngine.register(EVENT_ORDER, self.updateOrder) self.eventEngine.register(EVENT_ACCOUNT, self.updateAccount) #---------------------------------------------------------------------- def updateOrder(self, event): """更新成交数据""" # 只需要统计撤单成功的委托 order = event.dict_['data'] if order.status != STATUS_CANCELLED: return if order.symbol not in self.orderCancelDict: self.orderCancelDict[order.symbol] = 1 else: self.orderCancelDict[order.symbol] += 1 #---------------------------------------------------------------------- def updateTrade(self, event): """更新成交数据""" trade = event.dict_['data'] self.tradeCount += trade.volume #---------------------------------------------------------------------- def updateTimer(self, event): """更新定时器""" self.orderFlowTimer += 1 # 如果计时超过了流控清空的时间间隔,则执行清空 if self.orderFlowTimer >= self.orderFlowClear: self.orderFlowCount = 0 self.orderFlowTimer = 0 #---------------------------------------------------------------------- def updateAccount(self, event): """账户资金更新""" account = event.dict_['data'] # 计算保证金占比 ratio = 0 if account.balance: ratio = account.margin / account.balance # 更新到字典中 self.marginRatioDict[account.gatewayName] = ratio #---------------------------------------------------------------------- def writeRiskLog(self, content): """快速发出日志事件""" # 发出报警提示音 if platform.uname() == 'Windows': import winsound winsound.PlaySound("SystemHand", winsound.SND_ASYNC) # 发出日志事件 log = VtLogData() log.logContent = content log.gatewayName = self.name event = Event(type_=EVENT_LOG) event.dict_['data'] = log self.eventEngine.put(event) #---------------------------------------------------------------------- def checkRisk(self, orderReq, gatewayName): """检查风险""" # 如果没有启动风控检查,则直接返回成功 if not self.active: return True # 检查委托数量 if orderReq.volume <= 0: self.writeRiskLog(u'委托数量必须大于0') return False if orderReq.volume > self.orderSizeLimit: self.writeRiskLog(u'单笔委托数量%s,超过限制%s' % (orderReq.volume, self.orderSizeLimit)) return False # 检查成交合约量 if self.tradeCount >= self.tradeLimit: self.writeRiskLog(u'今日总成交合约数量%s,超过限制%s' % (self.tradeCount, self.tradeLimit)) return False # 检查流控 if self.orderFlowCount >= self.orderFlowLimit: self.writeRiskLog(u'委托流数量%s,超过限制每%s秒%s' % (self.orderFlowCount, self.orderFlowClear, self.orderFlowLimit)) return False # 检查总活动合约 workingOrderCount = len(self.mainEngine.getAllWorkingOrders()) if workingOrderCount >= self.workingOrderLimit: self.writeRiskLog(u'当前活动委托数量%s,超过限制%s' % (workingOrderCount, self.workingOrderLimit)) return False # 检查撤单次数 if orderReq.symbol in self.orderCancelDict and self.orderCancelDict[ orderReq.symbol] >= self.orderCancelLimit: self.writeRiskLog( u'当日%s撤单次数%s,超过限制%s' % (orderReq.symbol, self.orderCancelDict[orderReq.symbol], self.orderCancelLimit)) return False # 检查保证金比例(只针对开仓委托) if orderReq.offset == OFFSET_OPEN and gatewayName in self.marginRatioDict and self.marginRatioDict[ gatewayName] >= self.marginRatioLimit: self.writeRiskLog(u'%s接口保证金占比%s,超过限制%s' % (gatewayName, self.marginRatioDict[gatewayName], self.marginRatioLimit)) return False # 对于通过风控的委托,增加流控计数 self.orderFlowCount += 1 return True #---------------------------------------------------------------------- def clearOrderFlowCount(self): """清空流控计数""" self.orderFlowCount = 0 self.writeRiskLog(u'清空流控计数') #---------------------------------------------------------------------- def clearTradeCount(self): """清空成交数量计数""" self.tradeCount = 0 self.writeRiskLog(u'清空总成交计数') #---------------------------------------------------------------------- def setOrderFlowLimit(self, n): """设置流控限制""" self.orderFlowLimit = n #---------------------------------------------------------------------- def setOrderFlowClear(self, n): """设置流控清空时间""" self.orderFlowClear = n #---------------------------------------------------------------------- def setOrderSizeLimit(self, n): """设置委托最大限制""" self.orderSizeLimit = n #---------------------------------------------------------------------- def setTradeLimit(self, n): """设置成交限制""" self.tradeLimit = n #---------------------------------------------------------------------- def setWorkingOrderLimit(self, n): """设置活动合约限制""" self.workingOrderLimit = n #---------------------------------------------------------------------- def setOrderCancelLimit(self, n): """设置单合约撤单次数上限""" self.orderCancelLimit = n #---------------------------------------------------------------------- def setMarginRatioLimit(self, n): """设置保证金比例限制""" self.marginRatioLimit = n / 100 # n为百分数,需要除以100 #---------------------------------------------------------------------- def switchEngineStatus(self): """开关风控引擎""" self.active = not self.active if self.active: self.writeRiskLog(u'风险管理功能启动') else: self.writeRiskLog(u'风险管理功能停止') #---------------------------------------------------------------------- def stop(self): """停止""" self.saveSetting()
class StDataEngine(object): """价差数据计算引擎""" settingFileName = 'ST_setting.json' settingFilePath = getJsonPath(settingFileName, __file__) #---------------------------------------------------------------------- def __init__(self, mainEngine, eventEngine): """Constructor""" self.mainEngine = mainEngine self.eventEngine = eventEngine # 腿、价差相关字典 self.legDict = {} # vtSymbol:StLeg self.spreadDict = OrderedDict() # name:StSpread self.vtSymbolSpreadDict = {} # vtSymbol:StSpread self.registerEvent() #---------------------------------------------------------------------- def loadSetting(self): """加载配置""" try: with open(self.settingFilePath) as f: l = json.load(f) for setting in l: result, msg = self.createSpread(setting) self.writeLog(msg) self.writeLog(u'价差配置加载完成') except: content = u'价差配置加载出错,原因:' + traceback.format_exc() self.writeLog(content) #---------------------------------------------------------------------- def saveSetting(self): """保存配置""" with open(self.settingFilePath) as f: pass #---------------------------------------------------------------------- def createSpread(self, setting): """创建价差""" result = False msg = '' # 检查价差重名 if setting['name'] in self.spreadDict: msg = u'%s价差存在重名' % setting['name'] return result, msg # 检查腿是否已使用 l = [] l.append(setting['activeLeg']['vtSymbol']) for d in setting['passiveLegs']: l.append(d['vtSymbol']) for vtSymbol in l: if vtSymbol in self.vtSymbolSpreadDict: existingSpread = self.vtSymbolSpreadDict[vtSymbol] msg = u'%s合约已经存在于%s价差中' % (vtSymbol, existingSpread.name) return result, msg # 创建价差 spread = StSpread() spread.name = setting['name'] self.spreadDict[spread.name] = spread # 创建主动腿 activeSetting = setting['activeLeg'] activeLeg = StLeg() activeLeg.vtSymbol = str(activeSetting['vtSymbol']) activeLeg.ratio = float(activeSetting['ratio']) activeLeg.multiplier = float(activeSetting['multiplier']) activeLeg.payup = int(activeSetting['payup']) spread.addActiveLeg(activeLeg) self.legDict[activeLeg.vtSymbol] = activeLeg self.vtSymbolSpreadDict[activeLeg.vtSymbol] = spread self.subscribeMarketData(activeLeg.vtSymbol) # 创建被动腿 passiveSettingList = setting['passiveLegs'] passiveLegList = [] for d in passiveSettingList: passiveLeg = StLeg() passiveLeg.vtSymbol = str(d['vtSymbol']) passiveLeg.ratio = float(d['ratio']) passiveLeg.multiplier = float(d['multiplier']) passiveLeg.payup = int(d['payup']) spread.addPassiveLeg(passiveLeg) self.legDict[passiveLeg.vtSymbol] = passiveLeg self.vtSymbolSpreadDict[passiveLeg.vtSymbol] = spread self.subscribeMarketData(passiveLeg.vtSymbol) # 初始化价差 spread.initSpread() self.putSpreadTickEvent(spread) self.putSpreadPosEvent(spread) # 返回结果 result = True msg = u'%s价差创建成功' % spread.name return result, msg #---------------------------------------------------------------------- def processTickEvent(self, event): """处理行情推送""" # 检查行情是否需要处理 tick = event.dict_['data'] if tick.vtSymbol not in self.legDict: return # 更新腿价格 leg = self.legDict[tick.vtSymbol] leg.bidPrice = tick.bidPrice1 leg.askPrice = tick.askPrice1 leg.bidVolume = tick.bidVolume1 leg.askVolume = tick.askVolume1 # 更新价差价格 spread = self.vtSymbolSpreadDict[tick.vtSymbol] spread.calculatePrice() # 发出事件 self.putSpreadTickEvent(spread) #---------------------------------------------------------------------- def putSpreadTickEvent(self, spread): """发出价差行情更新事件""" event1 = Event(EVENT_SPREADTRADING_TICK + spread.name) event1.dict_['data'] = spread self.eventEngine.put(event1) event2 = Event(EVENT_SPREADTRADING_TICK) event2.dict_['data'] = spread self.eventEngine.put(event2) #---------------------------------------------------------------------- def processTradeEvent(self, event): """处理成交推送""" # 检查成交是否需要处理 trade = event.dict_['data'] if trade.vtSymbol not in self.legDict: return # 更新腿持仓 leg = self.legDict[trade.vtSymbol] direction = trade.direction offset = trade.offset if direction == DIRECTION_LONG: if offset == OFFSET_OPEN: leg.longPos += trade.volume else: leg.shortPos -= trade.volume else: if offset == OFFSET_OPEN: leg.shortPos += trade.volume else: leg.longPos -= trade.volume leg.netPos = leg.longPos - leg.shortPos # 更新价差持仓 spread = self.vtSymbolSpreadDict[trade.vtSymbol] spread.calculatePos() # 推送价差持仓更新 self.putSpreadPosEvent(spread) #---------------------------------------------------------------------- def processPosEvent(self, event): """处理持仓推送""" # 检查持仓是否需要处理 pos = event.dict_['data'] if pos.vtSymbol not in self.legDict: return # 更新腿持仓 leg = self.legDict[pos.vtSymbol] direction = pos.direction if direction == DIRECTION_LONG: leg.longPos = pos.position else: leg.shortPos = pos.position leg.netPos = leg.longPos - leg.shortPos # 更新价差持仓 spread = self.vtSymbolSpreadDict[pos.vtSymbol] spread.calculatePos() # 推送价差持仓更新 self.putSpreadPosEvent(spread) #---------------------------------------------------------------------- def putSpreadPosEvent(self, spread): """发出价差持仓事件""" event1 = Event(EVENT_SPREADTRADING_POS + spread.name) event1.dict_['data'] = spread self.eventEngine.put(event1) event2 = Event(EVENT_SPREADTRADING_POS) event2.dict_['data'] = spread self.eventEngine.put(event2) #---------------------------------------------------------------------- def registerEvent(self): """""" self.eventEngine.register(EVENT_TICK, self.processTickEvent) self.eventEngine.register(EVENT_TRADE, self.processTradeEvent) self.eventEngine.register(EVENT_POSITION, self.processPosEvent) #---------------------------------------------------------------------- def subscribeMarketData(self, vtSymbol): """订阅行情""" contract = self.mainEngine.getContract(vtSymbol) if not contract: self.writeLog(u'订阅行情失败,找不到该合约%s' % vtSymbol) return req = VtSubscribeReq() req.symbol = contract.symbol req.exchange = contract.exchange self.mainEngine.subscribe(req, contract.gatewayName) #---------------------------------------------------------------------- def writeLog(self, content): """发出日志""" log = VtLogData() log.logContent = content event = Event(EVENT_SPREADTRADING_LOG) event.dict_['data'] = log self.eventEngine.put(event) #---------------------------------------------------------------------- def getAllSpreads(self): """获取所有的价差""" return self.spreadDict.values()