def __init__(self, eventEngine, gatewayName='IB'): """Constructor""" super(IbGateway, self).__init__(eventEngine, gatewayName) self.host = EMPTY_STRING # 连接地址 self.port = EMPTY_INT # 连接端口 self.clientId = EMPTY_INT # 用户编号 self.accountCode = EMPTY_STRING # 账户编号 self.tickerId = 0 # 订阅行情时的代码编号 self.tickDict = {} # tick快照字典,key为tickerId,value为VtTickData对象 self.tickProductDict = {} # tick对应的产品类型字典,key为tickerId,value为产品类型 self.orderId = 0 # 订单编号 self.orderDict = {} # 报单字典,key为orderId,value为VtOrderData对象 self.accountDict = {} # 账户字典 self.contractDict = {} # 合约字典 self.subscribeReqDict = {} # 用来保存订阅请求的字典 self.connected = False # 连接状态 self.api = IbWrapper(self) # API接口 self.fileName = self.gatewayName + '_connect.json' self.filePath = load_json_path(self.fileName, __file__)
def __init__(self, eventEngine, gatewayName='LHANG'): """Constructor""" super(LhangGateway, self).__init__(eventEngine, gatewayName) self.api = LhangApi(self) self.fileName = self.gatewayName + '_connect.json' self.filePath = load_json_path(self.fileName, __file__)
def __init__(self, eventEngine, gatewayName='OANDA'): """Constructor""" super(OandaGateway, self).__init__(eventEngine, gatewayName) self.api = Api(self) self.qryEnabled = False # 是否要启动循环查询 self.fileName = self.gatewayName + '_connect.json' self.filePath = load_json_path(self.fileName, __file__)
def __init__(self, eventEngine, gatewayName='HUOBI'): """Constructor""" super(HuobiGateway, self).__init__(eventEngine, gatewayName) self.market = 'cny' self.tradeApi = HuobiTradeApi(self) self.dataApi = HuobiDataApi(self) self.fileName = self.gatewayName + '_connect.json' self.filePath = load_json_path(self.fileName, __file__)
def __init__(self, eventEngine, gatewayName='OKCOIN'): """Constructor""" super(OkcoinGateway, self).__init__(eventEngine, gatewayName) self.api = Api(self) self.leverage = 0 self.connected = False self.fileName = self.gatewayName + '_connect.json' self.filePath = load_json_path(self.fileName, __file__)
def __init__(self, eventEngine, gatewayName='KSGOLD'): """Constructor""" super(KsgoldGateway, self).__init__(eventEngine, gatewayName) self.tdApi = KsgoldTdApi(self) # 黄金接口只有交易API self.tdConnected = False # 交易API连接状态 self.qryEnabled = False # 是否要启动循环查询 self.orderInited = False # 委托初始化查询 self.tradeInited = False # 成交初始化查询 self.fileName = self.gatewayName + '_connect.json' self.filePath = load_json_path(self.fileName, __file__)
def __init__(self, eventEngine, gatewayName='FEMAS'): """Constructor""" super(FemasGateway, self).__init__(eventEngine, gatewayName) self.mdApi = FemasMdApi(self) # 行情API self.tdApi = FemasTdApi(self) # 交易API self.mdConnected = False # 行情API连接状态,登录完成后为True self.tdConnected = False # 交易API连接状态 self.qryEnabled = False # 是否要启动循环查询 self.fileName = self.gatewayName + '_connect.json' self.filePath = load_json_path(self.fileName, __file__)
def __init__(self, eventEngine, gatewayName='LTS'): """Constructor""" super(LtsGateway, self).__init__(eventEngine, gatewayName) self.mdApi = LtsMdApi(self) self.tdApi = LtsTdApi(self) self.qryApi = LtsQryApi(self) self.mdConnected = False self.tdConnected = False self.qryConnected = False self.qryEnabled = False # 是否要启动循环查询 self.fileName = self.gatewayName + '_connect.json' self.filePath = load_json_path(self.fileName, __file__)
class RmEngine(object): """风控引擎""" settingFileName = 'RM_setting.json' settingFilePath = load_json_path(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 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 CtaEngine(object): """CTA策略引擎""" settingFileName = 'CTA_setting.json' settingfilePath = load_json_path(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) # 发单 # 保存vtOrderID和策略的映射关系 self.orderStrategyDict[vtOrderID] = strategy 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 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) # 订阅合约 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 initStrategy(self, name): """初始化策略""" if name in self.strategyDict: strategy = self.strategyDict[name] if not strategy.inited: strategy.inited = True self.callStrategyFunc(strategy, strategy.onInit) 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) self.loadPosition() #---------------------------------------------------------------------- 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 putStrategyEvent(self, name): """触发策略状态变化事件(通常用于通知GUI更新)""" event = Event(EVENT_CTA_STRATEGY + name) self.eventEngine.put(event) #---------------------------------------------------------------------- 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 savePosition(self, strategy): """保存策略的持仓情况到数据库""" flt = {'name': strategy.name, 'vtSymbol': strategy.vtSymbol} d = {'name': strategy.name, 'vtSymbol': strategy.vtSymbol, 'pos': strategy.pos} self.mainEngine.dbUpdate(POSITION_DB_NAME, strategy.className, d, flt, True) content = '策略%s持仓保存成功,当前持仓%s' % (strategy.name, strategy.pos) self.writeCtaLog(content) #---------------------------------------------------------------------- def loadPosition(self): """从数据库载入策略的持仓情况""" for strategy in self.strategyDict.values(): flt = {'name': strategy.name, 'vtSymbol': strategy.vtSymbol} posData = self.mainEngine.dbQuery( POSITION_DB_NAME, strategy.className, flt) for d in posData: strategy.pos = d['pos'] #---------------------------------------------------------------------- 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)
class StDataEngine(object): """价差数据计算引擎""" settingFileName = 'ST_setting.json' settingFilePath = load_json_path(settingFileName, __file__) #---------------------------------------------------------------------- def __init__(self, mainEngine, eventEngine): """Constructor""" self.mainEngine = mainEngine self.eventEngine = eventEngine # 腿、价差相关字典 self.legDict = {} # vtSymbol:StLeg self.spreadDict = {} # 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 BaseException: 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() # 推送价差持仓更新 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 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()
class DrEngine(object): """数据记录引擎""" settingFileName = 'DR_setting.json' settingFilePath = load_json_path(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() # 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) # 保存到配置字典中 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] = BarManager(self.onBar) # 主力合约记录配置 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.%f') self.onTick(tick) bm = self.bmDict.get(vtSymbol, None) if bm: bm.updateTick(tick) #---------------------------------------------------------------------- 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( data_recorder_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) if vtSymbol in self.activeSymbolDict: activeSymbol = self.activeSymbolDict[vtSymbol] self.insertData(MINUTE_DB_NAME, activeSymbol, bar) self.writeDrLog( data_recorder_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) #---------------------------------------------------------------------- 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) flt = {'datetime': d['datetime']} self.mainEngine.dbUpdate(dbName, collectionName, d, flt, True) 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)