#-*-:conding:utf-8-*- from multiprocessing import Process, Queue from threading import Thread import time, os, sys from capi.com_types import * from api import base_api from .strategy_model import StrategyModel from .engine_model import DataModel from capi.event import Event import traceback import importlib import queue import datetime from datetime import datetime import copy from collections import OrderedDict from tkinter import Tk class StartegyManager(object): '''策略进程管理器,负责进程创建、销毁、暂停等''' def __init__(self, logger, st2egQueue): self.logger = logger # 策略进程到引擎的队列 self._st2egQueue = st2egQueue # 进程字典,{'id', Strategy} self._strategyDict = {} self._strategyInfo = {} self._strategyAttribute = {} self._isEquantExitStage = False self._isEquantExitCom = False @staticmethod def run(strategy): strategy.run() def getStrategyState(self, strategyId): assert strategyId in self._strategyInfo, 'error ' return self._strategyInfo[strategyId]["StrategyState"] def handleStrategyException(self, event): strategyId = event.getStrategyId() if strategyId not in self._strategyInfo: return self._strategyInfo[strategyId]["StrategyState"] = ST_STATUS_EXCEPTION self.destroyProcessByStrategyId(event.getStrategyId()) self._strategyInfo[strategyId]["Process"] = None def create(self, strategyId, eg2stQueue, eg2uiQueue, st2egQueue, event): qdict = {'eg2st': eg2stQueue, 'st2eg': st2egQueue, 'st2ui':eg2uiQueue} strategy = Strategy(self.logger, strategyId, qdict, event) self._strategyDict[strategyId] = strategy process = Process(target=self.run, args=(strategy,)) process.daemon = True process.start() args = { "Config": event.getData()["Args"], # "UIConfig": copy.deepcopy(event.getData()["Args"]), "Path": event.getData()["Path"], "StrategyName": None, "StrategyId": strategyId, } self.insertNewStrategy(strategyId, process, args) # 新建策略、恢复策略调用 def insertNewStrategy(self, strategyId, process, args): self._strategyInfo[strategyId] = { "StrategyId": strategyId, "Process": process, "StrategyState": ST_STATUS_NONE, } self._strategyAttribute[strategyId] = args def insertResumedStrategy(self, strategyId, args): self._strategyInfo[strategyId] = { "StrategyId": strategyId, "Process": None, "StrategyState": ST_STATUS_QUIT, } # 策略恢复的时候用 self._strategyAttribute[strategyId] = args def quitStrategy(self, event): strategyId = event.getStrategyId() assert strategyId in self._strategyInfo and self._strategyInfo[strategyId]["Process"].is_alive(), " error " strategyInfo = self._strategyInfo[strategyId] strategyInfo["StrategyState"] = ST_STATUS_QUIT self.destroyProcess(self._strategyInfo[strategyId]["Process"], strategyId) strategyInfo["Process"] = None def removeQuitedStrategy(self, event): assert event.getStrategyId() in self._strategyInfo, "error" self._strategyInfo.pop(event.getStrategyId()) self._strategyAttribute.pop(event.getStrategyId()) def removeRunningStrategy(self, event): strategyId = event.getStrategyId() if strategyId not in self._strategyInfo: self.logger.error(f"策略{strategyId}在engine已经被删除了") return # assert strategyId in self._strategyInfo, "error" self.destroyProcess(self._strategyInfo[strategyId]['Process'], strategyId) self._strategyInfo.pop(strategyId) self._strategyAttribute.pop(event.getStrategyId()) def removeExceptionStrategy(self, event): self.removeRunningStrategy(event) def restartStrategy(self, engineLoadFunc, event): assert event.getStrategyId() in self._strategyInfo, "error" strategyInfo = self._strategyAttribute[event.getStrategyId()] loadStrategyEvent = Event({ 'EventSrc': EEQU_EVSRC_UI, 'EventCode': EV_UI2EG_LOADSTRATEGY, 'SessionId': None, 'StrategyId': 0, 'UserNo': '', 'Data': { 'Path': strategyInfo["Path"], 'Args': strategyInfo["Config"], "NoInitialize": True, } }) engineLoadFunc(loadStrategyEvent, strategyId=event.getStrategyId()) def singleStrategyExitComEquantExit(self, event): self._isEquantExitStage = True strategyId = event.getStrategyId() assert strategyId in self._strategyInfo, " error " strategyInfo = self._strategyInfo[event.getStrategyId()] strategyInfo["StrategyState"] = ST_STATUS_QUIT # def isAllStrategyQuit(self): result = True for k, v in self._strategyInfo.items(): if v["StrategyState"] != ST_STATUS_QUIT and v["StrategyState"] != ST_STATUS_EXCEPTION: result = False break # print("now is equant exit complete ", result) return result def stopStrategy(self, strategyId): pass def resumeStrategy(self, strategyId): pass def destroyProcessByStrategyId(self, strategyId): assert strategyId in self._strategyInfo, " error " process = self._strategyInfo[strategyId]["Process"] self.destroyProcess(process, strategyId) self._strategyInfo[strategyId]["Process"] = None def destroyProcess(self, process, strategyId): if not process or not process.is_alive: self.logger.info(f"策略{strategyId}所在进程已经退出,将忽略") return try: process.terminate() process.join(timeout=1) self.logger.debug("strategy %d exit success" % strategyId) except Exception as e: # traceback.print_exc() self.logger.debug("strategy %d exit fail" % strategyId) def syncStrategyConfig(self, event): strategyId = event.getStrategyId() self._strategyAttribute[strategyId] = event.getData() def getStrategyConfig(self): result = {} for strategyId, value in self._strategyInfo.items(): if value["StrategyState"] == ST_STATUS_EXCEPTION: continue v = self._strategyAttribute[strategyId] result[strategyId] = { "Config":v["Config"], "Path":v["Path"], "StrategyName":v["StrategyName"], "StrategyId":strategyId, #"UIConfig":v["UIConfig"] } result = OrderedDict(sorted(result.items(), key=lambda obj: str(obj[0]))) return result def getStrategyAttribute(self, strategyId): return self._strategyAttribute[strategyId] class StrategyContext: def __init__(self): self._strategyStatus = None self._triggerType = None self._conTractNo = None self._kLineType = None self._kLineSlice = None self._tradeDate = None self._dateTimeStamp = None self._triggerData = None self._parameter = {} def strategyStatus(self): return self._strategyStatus def triggerType(self): return self._triggerType def contractNo(self): return self._conTractNo def kLineType(self): return self._kLineType def kLineSlice(self): return self._kLineSlice def tradeDate(self): if self._tradeDate is not None: return str(self._tradeDate) else: return None def dateTimeStamp(self): if self._dateTimeStamp is not None: return str(self._dateTimeStamp) else: return None def triggerData(self): return self._triggerData def setCurTriggerSourceInfo(self, args): self._strategyStatus = copy.deepcopy(args["Status"]) self._triggerType = copy.deepcopy(args["TriggerType"]) self._conTractNo = copy.deepcopy(args["ContractNo"]) self._kLineType = copy.deepcopy(args["KLineType"]) self._kLineSlice = copy.deepcopy(args["KLineSlice"]) self._tradeDate = copy.deepcopy(args["TradeDate"]) self._dateTimeStamp = copy.deepcopy(args["DateTimeStamp"]) self._triggerData = copy.deepcopy(args["TriggerData"]) @property def params(self): return self._parameter @params.setter def params(self, parameter): self._parameter = parameter class TradeRecord(object): def __init__(self, eSessionId, orderData={}): self._eSessionId = eSessionId # eSessionId self._barInfo = None # 触发的Bar信息 # SessionId self._sessionId = orderData['SessionId'] if 'SessionId' in orderData else None #用户编号 self._userNo = orderData['UserNo'] if 'UserNo' in orderData else None # 合约编号 self._contNo = orderData['Cont'] if 'Cont' in orderData else None # 定单号 self._orderId = orderData['OrderId'] if 'OrderId' in orderData else None # 委托单号 self._orderNo = orderData['OrderNo'] if 'OrderNo' in orderData else None # 方向 self._direct = orderData['Direct'] if 'Direct' in orderData else None # 开平 self._offset = orderData['Offset'] if 'Offset' in orderData else None # 订单状态 self._orderState = orderData['OrderState'] if 'OrderState' in orderData else None # 委托成交价 self._matchPrice = orderData['MatchPrice'] if 'MatchPrice' in orderData else None # 委托成交量 self._matchQty = orderData['MatchQty'] if 'MatchQty' in orderData else None # 委托价 self._orderPrice = orderData['OrderPrice'] if 'OrderPrice' in orderData else None # 委托订单量 self._orderQty = orderData['OrderQty'] if 'OrderQty' in orderData else None # 下单时间 self._insertTime = orderData['InsertTime'] if 'InsertTime' in orderData else None # 订单更新时间 self._updateTime = orderData['UpdateTime'] if 'UpdateTime' in orderData else None def updateOrderInfo(self, eSessionId, orderData): if eSessionId != self._eSessionId: return if 'UserNo' in orderData: self._userNo = orderData['UserNo'] if 'SessionId' in orderData: self._sessionId = orderData['SessionId'] if 'Cont' in orderData: self._contNo = orderData['Cont'] if 'OrderId' in orderData: self._orderId = orderData['OrderId'] if 'OrderNo' in orderData: self._orderNo = orderData['OrderNo'] if 'Direct' in orderData: self._direct = orderData['Direct'] if 'Offset' in orderData: self._offset = orderData['Offset'] if 'OrderState' in orderData: self._orderState = orderData['OrderState'] if 'MatchPrice' in orderData: self._matchPrice = orderData['MatchPrice'] if 'MatchQty' in orderData: self._matchQty = orderData['MatchQty'] if 'OrderPrice' in orderData: self._orderPrice = orderData['OrderPrice'] if 'OrderQty' in orderData: self._orderQty = orderData['OrderQty'] if 'InsertTime' in orderData: self._insertTime = orderData['InsertTime'] if 'UpdateTime' in orderData: self._updateTime = orderData['UpdateTime'] def getBarInfo(self): return self._barInfo class Strategy: def __init__(self, logger, id, args, event): self._strategyId = id self.logger = logger self._dataModel = None data = event.getData() self._filePath = data['Path'] self._argsDict = data['Args'] # 是否运行initialize函数 self._noInitialize = "NoInitialize" in data and data["NoInitialize"] self._uiConfig = copy.deepcopy(data['Args']) self._eg2stQueue = args['eg2st'] self._st2egQueue = args['st2eg'] self._isSt2EgQueueEffective = True self._st2uiQueue = args['st2ui'] moduleDir, moduleName = os.path.split(self._filePath) self._strategyName = ''.join(moduleName.split('.')[:-1]) # 策略所在进程状态, Ready、Running、Exit、Pause self._strategyState = StrategyStatusReady # self._runStatus = ST_STATUS_NONE self._curTriggerSourceInfo = None self._firstTriggerQueueEmpty = True # self._strategyId+"-"+self._eSessionId 组成本地生成的eSessionId self._eSessionId = 1 # 该策略的所有下单信息 self._eSessionIdList = [] # 存储本地生成的eSessionId,为了保存下单顺序信息 self._localOrder = {} # {本地生成的eSessionId : TradeRecode对象} self._moneyLastTime = 0 self._virtualPosTime = 0 # self._userModelDict = {} # ////////////////////////////对外接口//////////////////// def _initialize(self): self._strategyState = StrategyStatusRunning # 用户模板函数路径加入系统路径,并扩展baseapi的作用域 workPath = os.path.abspath('.') userPath = workPath + r"\strategy\扩展函数" if userPath not in sys.path: sys.path.insert(0, userPath) moduleDir, moduleName = os.path.split(self._filePath) moduleName = os.path.splitext(moduleName)[0] if moduleDir not in sys.path: sys.path.insert(0, moduleDir) # 3. 创建数据模块 self._dataModel = StrategyModel(self) self._cfgModel = self._dataModel.getConfigModel() self._hisModel = self._dataModel.getHisQuoteModel() self._qteModel = self._dataModel.getQuoteModel() self._trdModel = self._dataModel.getTradeModel() self._userModelDict = {} # 4. 初始化系统函数 self._baseApi = base_api.baseApi.updateData(self, self._dataModel) # 扩展用户模板函数作用域 if os.path.exists(userPath): userDir = os.listdir(userPath) for file in userDir: modelFile = os.path.splitext(file)[0] model = importlib.import_module(modelFile) model.__dict__.update(base_api.__dict__) self._userModelDict[modelFile] = model # 1. 加载用户策略 import builtins builtins.g_params = {} userModule = importlib.import_module(moduleName) userModule.__dict__.update(base_api.__dict__) # 2. 创建策略上下文 self._context = StrategyContext() self._cfgModel.setParams(self._argsDict["Params"]) builtins.g_params = {k:v[0] for k,v in self._argsDict["Params"].items()} # 5. 初始化用户策略参数 if not self._noInitialize: userModule.initialize(self._context) # self._cfgModel.setParams(self._argsDict["Params"]) # # builtins.g_params = {k:v[0] for k,v in self._argsDict["Params"].items()} # self._argsDict["Params"] = self._context.params # self._dataModel.getConfigModel().setParams(self._context.params) # else: # self._context.params = self._argsDict["Params"] self._userModule = userModule # 5.1 同步配置 self._sendConfig2Engine() # 6. 初始化model self._dataModel.initialize() # 7. 注册处理函数 self._regEgCallback() # 8. 启动策略运行线程 self._triggerQueue = queue.Queue() self._startStrategyThread() # 9. 启动策略心跳线程 self._startStrategyTimer() def _regEgCallback(self): self._egCallbackDict = { #//////////////////////引擎发送的数据///////////////////////// EV_EG2ST_EXCHANGE_RSP : self._onExchange , EV_EG2ST_COMMODITY_RSP : self._onCommodity , EV_EG2ST_CONTRACT_RSP : self._onContract , EV_EG2ST_UNDERLAYMAPPING_RSP : self._onUnderlayMap , EV_EG2ST_SUBQUOTE_RSP : self._onQuoteRsp , EV_EG2ST_SNAPSHOT_NOTICE : self._onQuoteNotice , EV_EG2ST_DEPTH_NOTICE : self._onDepthNotice , EV_EG2ST_HISQUOTE_RSP : self._onHisQuoteRsp , EV_EG2ST_HISQUOTE_NOTICE : self._onHisQuoteNotice , EV_EG2ST_LOGINNO_RSP : self._onLoginInfo , EV_EG2ST_USERNO_RSP : self._onUserInfo , EV_EG2ST_MONEY_RSP : self._onMoneyInfo , EV_EG2ST_ORDER_RSP : self._onOrderInfo , EV_EG2ST_MATCH_RSP : self._onMatchInfo , EV_EG2ST_POSITION_RSP : self._onPositionInfo , #//////////////////////API直接推送的数据///////////////////// EEQU_SRVEVENT_TRADE_LOGINQRY : self._onTradeLoginQry , EEQU_SRVEVENT_TRADE_LOGINNOTICE : self._onTradeLoginNotice , EEQU_SRVEVENT_TRADE_USERQRY : self._onTradeUserQry , EEQU_SRVEVENT_TRADE_MATCHQRY : self._onTradeMatchQry , EEQU_SRVEVENT_TRADE_MATCH : self._onTradeMatch , EEQU_SRVEVENT_TRADE_POSITQRY : self._onTradePositionQry , EEQU_SRVEVENT_TRADE_POSITION : self._onTradePosition , EEQU_SRVEVENT_TRADE_FUNDQRY : self._onTradeMoney , EEQU_SRVEVENT_TRADE_ORDERQRY : self._onTradeOrderQry , EEQU_SRVEVENT_TRADE_ORDER : self._onTradeOrder , EEQU_SRVEVENT_TRADE_EXCSTATEQRY : self._onExchangeStateNotice , EEQU_SRVEVENT_TRADE_EXCSTATE : self._onExchangeStateNotice , #//////////////////////UI推送的数据///////////////////////// EV_UI2EG_REPORT : self._onReport , EV_UI2EG_LOADSTRATEGY : self._onLoadStrategyResponse , EV_UI2EG_STRATEGY_QUIT : self._onStrategyQuit , EV_UI2EG_EQUANT_EXIT : self._onEquantExit , EV_UI2EG_STRATEGY_FIGURE : self._switchStrategy , EV_UI2EG_STRATEGY_REMOVE : self._onStrategyRemove , EV_UI2EG_SYNCPOS_CONF : self._onSyncPosConf , } def _actualRun(self): try: # 1. 内部初始化 self._initialize() # 1.1 请求交易所、品种、合约等 self._reqExchange() self._reqCommodity() # self._reqContract() # 1.2 请求主力/近月/指数合约映射合约信息 self._reqUnderlayMap() # 2. 订阅即时行情 self._subQuote() # 3. 请求历史行情 self._reqHisQuote() # 4. 查询交易数据 self._reqLoginInfo() # 5. 数据处理 self._mainLoop() except Exception as e: self._strategyState = StrategyStatusExit self._isSt2EgQueueEffective = False errorText = traceback.format_exc() # traceback.print_exc() self._exit(-1, errorText) def run(self): self._actualThread = Thread(target=self._actualRun) self._actualThread.start() self.top = Tk() self.top.withdraw() self.top.mainloop() # ////////////////////////////内部接口//////////////////// def _isExit(self): return self._strategyState == StrategyStatusExit def _isPause(self): return self._strategyState == StrategyStatusPause # 从engine进程接受事件并处理 def _mainLoop(self): while True: if self._isExit(): time.sleep(0.2) self._clearQueue(self._eg2stQueue) continue event = self._eg2stQueue.get() code = event.getEventCode() if code not in self._egCallbackDict: self.logger.warn("_egCallbackDict code(%d) not register!"%code) continue self._egCallbackDict[code](event) def _clearQueue(self, someQueue): try: while True: someQueue.get_nowait() except queue.Empty: pass def _runStrategy(self): try: # 等待回测阶段 self._runStatus = ST_STATUS_HISTORY self._send2UiEgStatus(self._runStatus) # runReport中会有等待 self._dataModel.runReport(self._context, self._userModule.handle_data) if not self._dataModel.getConfigModel().isActualRun(): self.logger.warn(f"未选择实盘运行,如果需要请在设置界面勾选'实盘运行',或者在策略代码中调用SetActual()") while not self._isExit(): try: event = self._triggerQueue.get(timeout=0.1) # 发单方式,实时发单、k线稳定后发单。 self._dataModel.runRealTime(self._context, self._userModule.handle_data, event) except queue.Empty: if self._firstTriggerQueueEmpty: self._clearHisPos() self._atHisOver() self._runStatus = ST_STATUS_CONTINUES self._send2UiEgStatus(self._runStatus) self._firstTriggerQueueEmpty = False except Exception as e: self._strategyState = StrategyStatusExit self._isSt2EgQueueEffective = False errorText = traceback.format_exc() # traceback.print_exc() self._exit(-1, errorText) def _clearHisPos(self): '''清空历史持仓''' if self._runStatus == ST_STATUS_CONTINUES: return if not self._cfgModel.isActualRun(): return if not self._cfgModel.getAutoSyncPos(): return calc = self._dataModel.getCalcCenter() # 获取该策略所有合约的虚拟持仓 posDict = calc.getUsersPosition() #self.logger.debug("PosDict1:%s" %posDict) trd = self._dataModel.getTradeModel() users = trd.getAllAccountId() for id in posDict: conts = posDict[id] for ct in conts: buyPos = conts[ct]['TotalBuy'] buyPrice = conts[ct]['BuyPrice'] if buyPos > 0: self._dataModel.setSell(id, ct, buyPos, buyPrice) sellPos = conts[ct]['TotalSell'] sellPrice = conts[ct]['SellPrice'] if sellPos > 0: self._dataModel.setBuyToCover(id, ct, sellPos, sellPrice) posDict = calc.getUsersPosition() #self.logger.debug("PosDict2:%s" %posDict) def _startStrategyThread(self): '''历史数据准备完成后,运行策略''' self._stThread = Thread(target=self._runStrategy) self._stThread.start() def _triggerTime(self): '''检查定时触发''' if not self._dataModel.getConfigModel().hasTimerTrigger() or not self.isRealTimeStatus(): return nowTime = datetime.now() for i,timeSecond in enumerate(self._dataModel.getConfigTimer()): specifiedTime = datetime.strptime(timeSecond, "%H%M%S") if 0<=(nowTime-specifiedTime).seconds<1 and not self._isTimeTriggered[i]: self._isTimeTriggered[i] = True key = self._dataModel.getConfigModel().getKLineShowInfoSimple() dateTimeStamp, tradeDate, lv1Data = self.getTriggerTimeAndData(key[0]) event = Event({ "EventCode" : ST_TRIGGER_TIMER, "ContractNo": None, "KLineType" : None, "KLineSlice": None, "Data":{ "TradeDate": tradeDate, "DateTimeStamp": dateTimeStamp, "Data":timeSecond } }) self._triggerQueue.put(event) def _triggerCycle(self): '''检查周期性触发''' if not self._dataModel.getConfigModel().hasCycleTrigger(): return if not self.isRealTimeStatus(): return nowTime = datetime.now() cycle = self._dataModel.getConfigCycle() if (nowTime - self._nowTime).total_seconds()*1000>cycle: self._nowTime = nowTime key = self._dataModel.getConfigModel().getKLineShowInfoSimple() dateTimeStamp, tradeDate, lv1Data = self.getTriggerTimeAndData(key[0]) event = Event({ "EventCode": ST_TRIGGER_CYCLE, "ContractNo": None, "KLineType" : None, "KLineSlice": None, "Data":{ "TradeDate": tradeDate, "DateTimeStamp": dateTimeStamp, "Data": None, } }) self._triggerQueue.put(event) def _triggerMoney(self): nowTime = datetime.now() if self._moneyLastTime == 0 or (nowTime - self._moneyLastTime).total_seconds() > 1: self._moneyLastTime = nowTime data = self._dataModel.getMonResult() if len(data) == 0: return event = Event({ "StrategyId" : self._strategyId, "EventCode": EV_EG2ST_MONITOR_INFO, "Data": self._dataModel.getMonResult() }) self.sendEvent2UI(event) def _reportStrategyPosition(self): nowTime = datetime.now() if self._virtualPosTime == 0 or (nowTime - self._virtualPosTime).total_seconds() >= 1: self._virtualPosTime = nowTime calc = self._dataModel.getCalcCenter() # 获取该策略所有合约的虚拟持仓 posDict = calc.getUsersPosition() if len(posDict) == 0: return event = Event({ "StrategyId" : self._strategyId, "EventCode" : EV_ST2EG_POSITION_NOTICE, "Data" : posDict }) self.sendEvent2Engine(event) def _runTimer(self): timeList = self._dataModel.getConfigTimer() if timeList is None: timeList = [] self._isTimeTriggered = [False for i in timeList] self._nowTime = datetime.now() '''秒级定时器''' while not self._isExit() and not self._isPause(): # 定时触发 self._triggerTime() # 周期性触发 self._triggerCycle() # 通知资金变化 self._triggerMoney() # 发送持仓变化 self._reportStrategyPosition() # 休眠100ms time.sleep(0.1) def _startStrategyTimer(self): self._stTimer = Thread(target=self._runTimer) self._stTimer.start() def _send2UiEgStatus(self, status): '''通知界面和引擎策略运行状态''' event = Event({ "StrategyId" : self._strategyId, "EventCode" : EV_EG2UI_STRATEGY_STATUS, "Data" : { 'Status' : status } }) self.sendEvent2UI(event) self.sendEvent2Engine(event) # ////////////////////////////内部数据请求接口//////////////////// def _reqData(self, code, data=''): event = Event({ 'EventCode': code, 'StrategyId': self._strategyId, 'Data': data, }) self.sendEvent2Engine(event) def _reqExchange(self): self._reqData(EV_ST2EG_EXCHANGE_REQ) def _reqCommodity(self): self._reqData(EV_ST2EG_COMMODITY_REQ) def _reqContract(self): self._reqData(EV_ST2EG_CONTRACT_REQ) def _reqUnderlayMap(self): self._reqData(EV_ST2EG_UNDERLAYMAPPING_REQ) # 订阅即时tick、 k线 def _subQuote(self): '''需要根据配置列表''' contList = [] self._contractTuple = self._cfgModel.getContract() for cno in self._contractTuple: contList.append(cno) self._reqData(EV_ST2EG_SUB_QUOTE, contList) # 请求历史tick、k线数据 def _reqHisQuote(self): #暂时先不修改 self._hisModel.reqAndSubKLine() # 查询登录账号 def _reqLoginInfo(self): self._reqData(EV_ST2EG_LOGINNO_REQ) # 查询资金账号 def _reqUserInfo(self): self._reqData(EV_ST2EG_USERNO_REQ) def _reqMoney(self): self._reqData(EV_ST2EG_MONEY_REQ) self.logger.info("request money 0") # 查询委托数据 def _reqOrder(self): self._reqData(EV_ST2EG_ORDER_REQ) # 查询成交数据 def _reqMatch(self): self._reqData(EV_ST2EG_MATCH_REQ) # 查询持仓数据 def _reqPosition(self): self._reqData(EV_ST2EG_POSITION_REQ) # ////////////////////////////内部数据应答接口//////////////////// def _onExchange(self, event): '''交易所信息应答''' self._qteModel.onExchange(event) def _onCommodity(self, event): '''品种查询应答''' self._qteModel.onCommodity(event) #self.logger.debug("1111111:%s" %self._dataModel.getContractUnit('ZCE|S|OI|001|005')) self._dataModel.initializeCalc() def _onContract(self, event): self._qteModel.onContract(event) def _onUnderlayMap(self, event): self._qteModel.onUnderlayMap(event) def _onExchangeStateNotice(self, event): '''交易所状态''' self._qteModel.onExchangeStatus(event) def _onQuoteRsp(self, event): '''行情应答,来着策略引擎''' self._qteModel.onQuoteRsp(event) def _onQuoteNotice(self, event): self._qteModel.onQuoteNotice(event) self._snapShotTrigger(event) def _onDepthNotice(self, event): self._qteModel.onDepthNotice(event) def _onHisQuoteRsp(self, event): '''历史数据请求应答''' self._hisModel.onHisQuoteRsp(event) def _onHisQuoteNotice(self, event): self._hisModel.onHisQuoteNotice(event) # 报告事件, 发到engine进程中,engine进程 再发到ui进程。 def _onReport(self, event): data = self._dataModel.getCalcCenter().testResult() responseEvent = Event({ "EventCode":EV_EG2UI_REPORT_RESPONSE, "StrategyId":self._strategyId, "Data":{ "Result":data, } }) self.sendEvent2UI(responseEvent) def _onLoadStrategyResponse(self, event): '''向界面返回策略加载应答''' cfg = self._dataModel.getConfigData() key = self._dataModel.getConfigModel().getKLineShowInfoSimple() revent = Event({ "EventCode" : EV_EG2UI_LOADSTRATEGY_RESPONSE, "StrategyId": self._strategyId, "Data":{ "StrategyId" : self._strategyId, "StrategyName" : self._strategyName, "StrategyState": self._runStatus, "ContractNo" : key[0], "KLineType" : key[1], "KLinceSlice" : key[2], "IsActualRun" : self._dataModel.getConfigModel().isActualRun(), "InitialFund" : self._dataModel.getConfigModel().getInitCapital(), "Config" : cfg, "Params" : self._dataModel.getConfigModel().getParams(), "Path" : self._filePath, } }) self.sendEvent2UI(revent) def _onLoginInfo(self, event): self._onTradeLoginQry(event) #查询资金账户 self._reqUserInfo() def _onUserInfo(self, event): self._onTradeUserQry(event) #查询所有用户资金信息 self._reqMoney() def _onMoneyInfo(self, event): self._onTradeMoney(event) #查询所有委托信息 self._reqOrder() def _onOrderInfo(self, event): #self.logger.debug("_onOrderInfo:%s"%event.getData()) self._onTradeOrderQry(event) #委托信息可能会有很多笔,最后一笔查询下一个数据 if event.isChainEnd(): self._reqMatch() def _onMatchInfo(self, event): #self.logger.debug("_onMatchInfo:%s"%event.getData()) self._onTradeMatch(event) #委托信息可能会有很多笔,最后一笔查询下一个数据 if event.isChainEnd(): self._reqPosition() def _onPositionInfo(self, event): #self.logger.debug("_onPositionInfo:%s"%event.getData()) self._onTradePosition(event) #委托信息可能会有很多笔,最后一笔查询下一个数据 if event.isChainEnd(): #TODO:设置为数据完成 pass def _onTradeLoginQry(self, apiEvent): self._trdModel.updateLoginInfo(apiEvent) def _onTradeLoginNotice(self, apiEvent): #ret = self._trdModel.updateLoginInfoEg(apiEvent) dataList = apiEvent.getData() loginInfo = self._trdModel.getLoginInfo() #不用重新查询交易数据,引擎会推送 for data in dataList: #新登录账号 loginNo = data['LoginNo'] if loginNo not in loginInfo: self._trdModel.addLoginInfo(data) #登出,清理登录账号和资金账号 elif data['IsReady'] == EEQU_NOTREADY: self._trdModel.delLoginInfo(data) self._trdModel.delUserInfo(loginNo) #交易日切换,清理所有资金账号及本地委托数据 elif self._trdModel.chkTradeDate(data): userDict = self._trdModel.getLoginUser(loginNo) self.delLocalOrder(userDict) self._trdModel.delUserInfo(loginNo) else: self.logger.warn("Unknown login status: %s"%data) #self._trdModel.updateLoginInfo(apiEvent) def _onTradeUserQry(self, apiEvent): self._trdModel.updateUserInfo(apiEvent) self._trdModel.updateLoginInfo(apiEvent) def _onTradeMatchQry(self, apiEvent): self._trdModel.updateMatchData(apiEvent) def _onTradePositionQry(self, apiEvent): self._trdModel.updatePosData(apiEvent) def _onTradeOrderQry(self, apiEvent): self._trdModel.updateOrderData(apiEvent) def _onTradeOrder(self, apiEvent): self._trdModel.updateOrderData(apiEvent) # 更新本地订单信息 dataList = apiEvent.getData() eSessionId = apiEvent.getESessionId() for data in dataList: self.updateLocalOrder(eSessionId, data) if self.isRealTimeStatus(): self._tradeTriggerOrder(apiEvent) def _onTradeMatch(self, apiEvent): ''' 交易成交信息发生变化时,更新交易模型信息 :param apiEvent: 引擎返回事件 :return: None ''' self._trdModel.updateMatchData(apiEvent) # self._tradeTriggerMatch(apiEvent) # 去掉成交触发 def _onTradePosition(self, apiEvent): ''' 交易持仓信息发生变化时,更新交易模型信息 :param apiEvent: 引擎返回事件 :return: None ''' self._trdModel.updatePosData(apiEvent) def _onTradeMoney(self, apiEvent): ''' 交易资金信息发生变化时,更新交易模型信息 :param apiEvent: 引擎返回事件 :return: None ''' self._trdModel.updateMoney(apiEvent) def getStrategyId(self): return self._strategyId def getESessionId(self): return self._eSessionId def setESessionId(self, eSessionId): if eSessionId <= 0: return 0 self._eSessionId = eSessionId def getLocalOrder(self): return self._localOrder def getESessionIdList(self): return self._eSessionIdList def updateLocalOrder(self, eSesnId, data): # 更新本地订单信息 #self.logger.debug("AAAAA:%s,%s"%(eSesnId, data)) if eSesnId in self._localOrder: tradeRecode = self._localOrder[eSesnId] tradeRecode.updateOrderInfo(eSesnId, data) else: self._localOrder[eSesnId] = TradeRecord(eSesnId, data) self._eSessionIdList.append(eSesnId) def delLocalOrder(self, userDict): popSessionList = [] for k, v in self._localOrder.items(): if v in userDict: popSessionList.append(k) for eid in popSessionList: self._localOrder.pop(eid) def updateBarInfoInLocalOrder(self, eSessionId, barInfo): if not barInfo: return if eSessionId not in self._localOrder: return tradeRecode = self._localOrder[eSessionId] tradeRecode._barInfo = barInfo def getOrderBuyOrSell(self, eSessionId): if eSessionId not in self._localOrder: return 'N' tradeRecord = self._localOrder[eSessionId] return tradeRecord._direct def getOrderEntryOrExit(self, eSessionId): if eSessionId not in self._localOrder: return 'N' tradeRecord = self._localOrder[eSessionId] return tradeRecord._offset def getOrderFilledLot(self, eSessionId): if eSessionId not in self._localOrder: return 0 tradeRecord = self._localOrder[eSessionId] return tradeRecord._matchQty def getOrderFilledPrice(self, eSessionId): if eSessionId not in self._localOrder: return 0 tradeRecord = self._localOrder[eSessionId] return tradeRecord._matchPrice def getOrderFilledList(self, eSessionId): if eSessionId not in self._localOrder: return {} tradeRecord = self._localOrder[eSessionId] return tradeRecord._matchPrice def getOrderLot(self, eSessionId): if eSessionId not in self._localOrder: return 0 tradeRecord = self._localOrder[eSessionId] return tradeRecord._orderQty def getOrderPrice(self, eSessionId): if eSessionId not in self._localOrder: return 0 tradeRecord = self._localOrder[eSessionId] return tradeRecord._orderPrice def getOrderStatus(self, eSessionId): if eSessionId not in self._localOrder: return 'N' tradeRecord = self._localOrder[eSessionId] return tradeRecord._orderState def getOrderTime(self, eSessionId): if eSessionId not in self._localOrder: return 0 tradeRecord = self._localOrder[eSessionId] insertTime = tradeRecord._insertTime if not insertTime: return 0 struct_time = time.strptime(insertTime, "%Y-%m-%d %H:%M:%S") timeStamp = time.strftime("%Y%m%d.%H%M%S", struct_time) return float(timeStamp) def getOrderUpdateTime(self, eSessionId): if eSessionId not in self._localOrder: return 0 tradeRecord = self._localOrder[eSessionId] updateTime = tradeRecord._updateTime if not updateTime: return 0 struct_time = time.strptime(updateTime, "%Y-%m-%d %H:%M:%S") timeStamp = time.strftime("%Y%m%d.%H%M%S", struct_time) return float(timeStamp) def deleteOrder(self, eSessionId): orderId = '' if isinstance(eSessionId, str) and '-' in eSessionId: orderId = self.getOrderId(eSessionId) if not orderId: return False else: orderId = eSessionId return self.deleteOrderByOrderId(orderId) def deleteOrderByOrderId(self, orderId): aOrder = { "OrderId": orderId, } aOrderEvent = Event({ "EventCode": EV_ST2EG_ACTUAL_CANCEL_ORDER, "StrategyId": self.getStrategyId(), "Data": aOrder }) self.sendEvent2Engine(aOrderEvent) return True def getContNo(self, eSessionId): if eSessionId not in self._localOrder: return '' tradeRecord = self._localOrder[eSessionId] return tradeRecord._contNo def getContNoByOrderId(self, orderId): for k, v in self._localOrder.items(): if v._orderId == orderId: return v._contNo return '' def getOrderNo(self, eSessionId): if eSessionId not in self._localOrder: return 0 tradeRecord = self._localOrder[eSessionId] return tradeRecord._orderNo def getOrderId(self, eSessionId): if eSessionId not in self._localOrder: return 0 tradeRecord = self._localOrder[eSessionId] return tradeRecord._orderId def getStrategyName(self): return self._strategyName def isRealTimeStatus(self): return self._runStatus == ST_STATUS_CONTINUES def isHisStatus(self): return self._runStatus == ST_STATUS_HISTORY def getStatus(self): return self._runStatus def sendEvent2Engine(self, event): if self._isSt2EgQueueEffective: while True: try: self._st2egQueue.put_nowait(event) break except queue.Full: time.sleep(0.1) #self.logger.error(f"策略{self._strategyId}向引擎传递事件{event.getEventCode()}时卡住") def sendEvent2EngineForce(self, event): while True: try: self._st2egQueue.put_nowait(event) break except queue.Full: time.sleep(0.1) #self.logger.error(f"策略{self._strategyId}强制向引擎传递事件{event.getEventCode()}时阻塞") def sendEvent2UI(self, event): while True: try: self._st2uiQueue.put_nowait(event) break except queue.Full: time.sleep(0.1) #self.logger.error(f"策略{self._strategyId}制向UI传递事件{event.getEventCode()}时阻塞") def sendTriggerQueue(self, event): self._triggerQueue.put(event) def _exit(self, errorCode, errorText): event = Event({ "EventCode": EV_EG2UI_CHECK_RESULT, "StrategyId": self._strategyId, "Data": { "ErrorCode": errorCode, "ErrorText": errorText, } }) self.sendEvent2EngineForce(event) self._onStrategyQuit(None, ST_STATUS_EXCEPTION) # 停止策略 def _onStrategyQuit(self, event=None, status=ST_STATUS_QUIT): try: #回调退出函数 if hasattr(self._userModule, 'exit_callback'): self._userModule.exit_callback(self._context) except Exception as e: self.logger.error('onStrategyStop callback error: %s' % str(e)) self._isSt2EgQueueEffective = False self._strategyState = StrategyStatusExit config = None if self._dataModel is None else self._dataModel.getConfigData() result = None if self._dataModel and self._dataModel.getCalcCenter(): result = self._dataModel.getCalcCenter().testResult() quitEvent = Event({ "EventCode": EV_EG2UI_STRATEGY_STATUS, "StrategyId": self._strategyId, "Data":{ "Status":status, "Config":config, "Pid":os.getpid(), "Path":self._filePath, "StrategyName": self._strategyName, "Result":result } }) self.sendEvent2UI(quitEvent) self.sendEvent2EngineForce(quitEvent) self.logger.info(f"策略已经将停止完成信号发送到UI和engine,策略{self._strategyId}") # 保证该进程is_alive, 使得队列可用 while True: time.sleep(2) def _onEquantExit(self, event): try: #回调退出函数 if hasattr(self._userModule, 'exit_callback'): self._userModule.exit_callback(self._context) except Exception as e: self.logger.error('onEquantExit callback error: %s' % str(e)) self._isSt2EgQueueEffective = False self._strategyState = StrategyStatusExit config = None if self._dataModel is None else self._dataModel.getConfigData() responseEvent = Event({ "EventCode": EV_EG2UI_STRATEGY_STATUS, "StrategyId": self._strategyId, "Data": { "Status": event.getEventCode(), "Config": config, "Pid": os.getpid(), "Path": self._filePath, "StrategyName":self._strategyName, } }) self.sendEvent2EngineForce(responseEvent) def _atHisOver(self): try: # 历史回测结束回调 if hasattr(self._userModule, 'hisover_callback'): self._userModule.hisover_callback(self._context) except Exception as e: self.logger.error('atHisOver callback error: %s' % str(e)) def _switchStrategy(self, event): self._dataModel.getHisQuoteModel()._switchKLine() def _onSyncPosConf(self, event): conf = event.getData() self._cfgModel.setAutoSyncPos(conf) def _onStrategyRemove(self, event): self._isSt2EgQueueEffective = False self._strategyState = StrategyStatusExit config = None if self._dataModel is None else self._dataModel.getConfigData() responseEvent = Event({ "EventCode": EV_EG2UI_STRATEGY_STATUS, "StrategyId": self._strategyId, "Data": { "Status": ST_STATUS_REMOVE, "Config": config, "Pid": os.getpid(), "Path": self._filePath, "StrategyName": self._strategyName, } }) self.sendEvent2UI(responseEvent) self.sendEvent2EngineForce(responseEvent) self.logger.info(f"策略已经将删除完成信号发送到UI和engine,策略{self._strategyId}, {EV_EG2UI_STRATEGY_STATUS}") # 保证该进程is_alive, 使得队列可用 while True: time.sleep(2) def _snapShotTrigger(self, event): # 未选择即时行情触发 # if not self._dataModel.getConfigModel().hasSnapShotTrigger() or not self.isRealTimeStatus(): # return # # # 该合约不触发 # if event.getContractNo() not in self._dataModel.getConfigModel().getTriggerContract(): # return if not self.isRealTimeStatus(): return # 对应字段没有变化不触发 data = event.getData() if len(data)==0 or (not set(data[0]["FieldData"].keys())&set([4, 11, 17, 18, 19, 20])): # 4:最新价 11:成交量 17:最优买价 18:买量 19:最优卖价 20:卖量 return dateTimeStamp, tradeDate, lv1Data = self.getTriggerTimeAndData(event.getContractNo()) event = Event({ "EventCode" : ST_TRIGGER_SANPSHOT_FILL, "ContractNo": event.getContractNo(), "KLineType" : None, "KLineSlice": None, "Data":{ "Data": lv1Data, "DateTimeStamp": dateTimeStamp, "TradeDate": tradeDate, "IsLastPriceChanged": 4 in data[0]["FieldData"], # 最新价是否改变 } }) self.sendTriggerQueue(event) def setCurTriggerSourceInfo(self, args): self._curTriggerSourceInfo = args def getCurTriggerSourceInfo(self): return self._curTriggerSourceInfo # def _tradeTriggerOrder(self, apiEvent): if not self._dataModel.getConfigModel().hasTradeTrigger() or len(apiEvent.getData()) == 0: return if apiEvent.getEventCode() == EEQU_SRVEVENT_TRADE_ORDER and str(apiEvent.getStrategyId()) == str(self._strategyId): contractNo = apiEvent.getData()[0]["Cont"] dateTimeStamp, tradeDate, lv1Data = self.getTriggerTimeAndData(contractNo) tradeTriggerEvent = Event({ "EventCode":ST_TRIGGER_TRADE_ORDER, "ContractNo": contractNo, "KLineType" : None, "KLineSlice": None, "Data":{ "Data": apiEvent.getData()[0], "DateTimeStamp": dateTimeStamp, "TradeDate": tradeDate, } }) # 交易触发 self.sendTriggerQueue(tradeTriggerEvent) def _tradeTriggerMatch(self, apiEvent): if not self._dataModel.getConfigModel().hasTradeTrigger() or len(apiEvent.getData()) == 0: return if apiEvent.getEventCode() == EEQU_SRVEVENT_TRADE_MATCH and str(apiEvent.getStrategyId()) == str(self._strategyId): contractNo = apiEvent.getData()[0]["Cont"] dateTimeStamp, tradeDate, lv1Data = self.getTriggerTimeAndData(contractNo) tradeTriggerEvent = Event({ "EventCode":ST_TRIGGER_TRADE_MATCH, "ContractNo": contractNo, "KLineType" : None, "KLineSlice": None, "Data":{ "Data": apiEvent.getData()[0], "DateTimeStamp": dateTimeStamp, "TradeDate": tradeDate, } }) # 交易触发 self.sendTriggerQueue(tradeTriggerEvent) def getTriggerTimeAndData(self, contractNo): lv1DataAndUpdateTime = self._dataModel.getQuoteModel().getLv1DataAndUpdateTime(contractNo) if lv1DataAndUpdateTime is not None: dateTimeStamp = lv1DataAndUpdateTime["UpdateTime"] tradeDate = dateTimeStamp // 1000000000 lv1Data = lv1DataAndUpdateTime["Lv1Data"] else: dateTimeStamp = None tradeDate = None lv1Data = None return dateTimeStamp, tradeDate, lv1Data def _sendConfig2Engine(self): event = Event({ "EventCode": ST_ST2EG_SYNC_CONFIG, "StrategyId": self._strategyId, "ContractNo": None, "KLineType": None, "KLineSlice": None, "Data": { "UIConfig": self._uiConfig, "Config": self._dataModel.getConfigModel().getConfig(), "Path": self._filePath, "StrategyName": self._strategyName, "StrategyId": self._strategyId, } }) self.sendEvent2Engine(event)