Interpretation vn.py source (IV ---- main engine initialization function code analysis)

 

        vnpy there is a thing called the main engine, also said three inside, personally feel that this should be something of a running frame, the engine should not be called, but it does not matter, just the name thing. This one it is mainly analyze the main engine code.

class MainEngine(object):
    """主引擎"""

    #----------------------------------------------------------------------
    def __init__(self, eventEngine):
        """Constructor"""
        
    #----------------------------------------------------------------------
    def addGateway(self, gatewayModule):
               
    #----------------------------------------------------------------------
    def addApp(self, appModule):
       
    #----------------------------------------------------------------------
    def getGateway(self, gatewayName):
                
    #----------------------------------------------------------------------
    def connect(self, gatewayName):
                 
    #----------------------------------------------------------------------
    def subscribe(self, subscribeReq, gatewayName):
          
    #----------------------------------------------------------------------
    def sendOrder(self, orderReq, gatewayName):
                
    #----------------------------------------------------------------------
    def cancelOrder(self, cancelOrderReq, gatewayName):
        """对特定接口撤单"""
          
    #----------------------------------------------------------------------
    def qryAccount(self, gatewayName):
        """查询特定接口的账户"""
                    
    #----------------------------------------------------------------------
    def qryPosition(self, gatewayName):
        """查询特定接口的持仓"""
        
    #----------------------------------------------------------------------
    def exit(self):
        """退出程序前调用,保证正常退出"""        
            
    #----------------------------------------------------------------------
    def writeLog(self, content):
        """快速发出日志事件"""
                  
    #----------------------------------------------------------------------
    def dbConnect(self):
        """连接MongoDB数据库"""
            
    #----------------------------------------------------------------------
    def dbInsert(self, dbName, collectionName, d):
        """向MongoDB中插入数据,d是具体数据"""
           
    #----------------------------------------------------------------------
    def dbQuery(self, dbName, collectionName, d, sortKey='', sortDirection=ASCENDING):
        """从MongoDB中读取数据,d是查询要求,返回的是数据库查询的指针"""
        
    #----------------------------------------------------------------------
    def dbUpdate(self, dbName, collectionName, d, flt, upsert=False):
        """向MongoDB中更新数据,d是具体数据,flt是过滤条件,upsert代表若无是否要插入"""
    
    #----------------------------------------------------------------------
    def dbDelete(self, dbName, collectionName, flt):
        """从数据库中删除数据,flt是过滤条件"""
            
    #----------------------------------------------------------------------
    def dbLogging(self, event):
        """向MongoDB中插入日志"""
    
    #----------------------------------------------------------------------
    def getTick(self, vtSymbol):
        """查询行情"""  
    
    #----------------------------------------------------------------------
    def getContract(self, vtSymbol):
        """查询合约"""
    
    #----------------------------------------------------------------------
    def getAllContracts(self):
        """查询所有合约(返回列表)"""
    
    #----------------------------------------------------------------------
    def getOrder(self, vtOrderID):
        """查询委托"""
    
    #----------------------------------------------------------------------
    def getPositionDetail(self, vtSymbol):
        """查询持仓细节"""
    
    #----------------------------------------------------------------------
    def getAllWorkingOrders(self):
        """查询所有的活跃的委托(返回列表)"""
    
    #----------------------------------------------------------------------
    def getAllOrders(self):
        """查询所有委托"""
    
    #----------------------------------------------------------------------
    def getAllTrades(self):
        """查询所有成交"""
    
    #----------------------------------------------------------------------
    def getAllAccounts(self):
        """查询所有账户"""
    
    #----------------------------------------------------------------------
    def getAllPositions(self):
        """查询所有持仓"""
    
    #----------------------------------------------------------------------
    def getAllPositionDetails(self):
        """查询本地持仓缓存细节"""
    
    #----------------------------------------------------------------------
    def getAllGatewayDetails(self):
        """查询引擎中所有底层接口的信息"""
    
    #----------------------------------------------------------------------
    def getAllAppDetails(self):
        """查询引擎中所有上层应用的信息"""
       
    
    #----------------------------------------------------------------------
    def getApp(self, appName):
        """获取APP引擎对象"""
    
    #----------------------------------------------------------------------
    def initLogEngine(self):
        """初始化日志引擎"""
    
    #----------------------------------------------------------------------
    def registerLogEvent(self, eventType):
        """注册日志事件监听"""
    
    #----------------------------------------------------------------------
    def convertOrderReq(self, req):
        """转换委托请求"""
        return self.dataEngine.convertOrderReq(req)

    #----------------------------------------------------------------------
    def getLog(self):
        """查询日志"""
        return self.dataEngine.getLog()
    
    #----------------------------------------------------------------------
    def getError(self):
        """查询错误"""
        return self.dataEngine.getError()

        This MainEngine class is still very long, a lot of deleted here in the middle of the definition, at the time explained later eleven put up, we first have a general.

        Still the same, assuming we own the log to write such a port, trading strategies, ctp real-time quotes, and database interaction together, then we will be how to write? We first look at the initialization function of it.

1. initialization function

        Initialization function, first of all, we are talking about the good before the initialization event engine to hang up, then put in charge of the data of the data engine to hang. We found that there was something a DataEngine here, we look at how this is defined.

        # 记录今日日期
        self.todayDate = datetime.now().strftime('%Y%m%d')
        
        # 绑定事件引擎
        self.eventEngine = eventEngine
        self.eventEngine.start()
        
        # 创建数据引擎
        self.dataEngine = DataEngine(self.eventEngine)

        This is all the code DataEngine, we are further subdivided.

class DataEngine(object):
    """数据引擎"""
    contractFileName = 'ContractData.vt'
    contractFilePath = getTempPath(contractFileName)
    
    FINISHED_STATUS = [STATUS_ALLTRADED, STATUS_REJECTED, STATUS_CANCELLED]

    #----------------------------------------------------------------------
    def __init__(self, eventEngine):
        """Constructor"""
        self.eventEngine = eventEngine
        
        # 保存数据的字典和列表
        self.tickDict = {}
        self.contractDict = {}
        self.orderDict = {}
        self.workingOrderDict = {}  # 可撤销委托
        self.tradeDict = {}
        self.accountDict = {}
        self.positionDict= {}
        self.logList = []
        self.errorList = []
        
        # 持仓细节相关
        self.detailDict = {}                                # vtSymbol:PositionDetail
        self.tdPenaltyList = globalSetting['tdPenalty']     # 平今手续费惩罚的产品代码列表
        
        # 读取保存在硬盘的合约数据
        self.loadContracts()
        
        # 注册事件监听
        self.registerEvent()
    
    #----------------------------------------------------------------------
    def registerEvent(self):
        """注册事件监听"""
        self.eventEngine.register(EVENT_TICK, self.processTickEvent)
        self.eventEngine.register(EVENT_CONTRACT, self.processContractEvent)
        self.eventEngine.register(EVENT_ORDER, self.processOrderEvent)
        self.eventEngine.register(EVENT_TRADE, self.processTradeEvent)
        self.eventEngine.register(EVENT_POSITION, self.processPositionEvent)
        self.eventEngine.register(EVENT_ACCOUNT, self.processAccountEvent)
        self.eventEngine.register(EVENT_LOG, self.processLogEvent)
        self.eventEngine.register(EVENT_ERROR, self.processErrorEvent)
        
    #----------------------------------------------------------------------
    def processTickEvent(self, event):
        """处理成交事件"""
        tick = event.dict_['data']
        self.tickDict[tick.vtSymbol] = tick    
    
    #----------------------------------------------------------------------
    def processContractEvent(self, event):
        """处理合约事件"""
        contract = event.dict_['data']
        self.contractDict[contract.vtSymbol] = contract
        self.contractDict[contract.symbol] = contract       # 使用常规代码(不包括交易所)可能导致重复
    
    #----------------------------------------------------------------------
    def processOrderEvent(self, event):
        """处理委托事件"""
        order = event.dict_['data']        
        self.orderDict[order.vtOrderID] = order
        
        # 如果订单的状态是全部成交或者撤销,则需要从workingOrderDict中移除
        if order.status in self.FINISHED_STATUS:
            if order.vtOrderID in self.workingOrderDict:
                del self.workingOrderDict[order.vtOrderID]
        # 否则则更新字典中的数据        
        else:
            self.workingOrderDict[order.vtOrderID] = order
            
        # 更新到持仓细节中
        detail = self.getPositionDetail(order.vtSymbol)
        detail.updateOrder(order)            
            
    #----------------------------------------------------------------------
    def processTradeEvent(self, event):
        """处理成交事件"""
        trade = event.dict_['data']
        
        self.tradeDict[trade.vtTradeID] = trade
    
        # 更新到持仓细节中
        detail = self.getPositionDetail(trade.vtSymbol)
        detail.updateTrade(trade)        

    #----------------------------------------------------------------------
    def processPositionEvent(self, event):
        """处理持仓事件"""
        pos = event.dict_['data']
        
        self.positionDict[pos.vtPositionName] = pos
    
        # 更新到持仓细节中
        detail = self.getPositionDetail(pos.vtSymbol)
        detail.updatePosition(pos)                
        
    #----------------------------------------------------------------------
    def processAccountEvent(self, event):
        """处理账户事件"""
        account = event.dict_['data']
        self.accountDict[account.vtAccountID] = account
    
    #----------------------------------------------------------------------
    def processLogEvent(self, event):
        """处理日志事件"""
        log = event.dict_['data']
        self.logList.append(log)
    
    #----------------------------------------------------------------------
    def processErrorEvent(self, event):
        """处理错误事件"""
        error = event.dict_['data']
        self.errorList.append(error)
        
    #----------------------------------------------------------------------
    def getTick(self, vtSymbol):
        """查询行情对象"""
        try:
            return self.tickDict[vtSymbol]
        except KeyError:
            return None        
    
    #----------------------------------------------------------------------
    def getContract(self, vtSymbol):
        """查询合约对象"""
        try:
            return self.contractDict[vtSymbol]
        except KeyError:
            return None
        
    #----------------------------------------------------------------------
    def getAllContracts(self):
        """查询所有合约对象(返回列表)"""
        return self.contractDict.values()
    
    #----------------------------------------------------------------------
    def saveContracts(self):
        """保存所有合约对象到硬盘"""
        f = shelve.open(self.contractFilePath)
        f['data'] = self.contractDict
        f.close()
    
    #----------------------------------------------------------------------
    def loadContracts(self):
        """从硬盘读取合约对象"""
        f = shelve.open(self.contractFilePath)
        if 'data' in f:
            d = f['data']
            for key, value in d.items():
                self.contractDict[key] = value
        f.close()
        
    #----------------------------------------------------------------------
    def getOrder(self, vtOrderID):
        """查询委托"""
        try:
            return self.orderDict[vtOrderID]
        except KeyError:
            return None
    
    #----------------------------------------------------------------------
    def getAllWorkingOrders(self):
        """查询所有活动委托(返回列表)"""
        return self.workingOrderDict.values()
    
    #----------------------------------------------------------------------
    def getAllOrders(self):
        """获取所有委托"""
        return self.orderDict.values()
    
    #----------------------------------------------------------------------
    def getAllTrades(self):
        """获取所有成交"""
        return self.tradeDict.values()
    
    #----------------------------------------------------------------------
    def getAllPositions(self):
        """获取所有持仓"""
        return self.positionDict.values()
    
    #----------------------------------------------------------------------
    def getAllAccounts(self):
        """获取所有资金"""
        return self.accountDict.values()
    
    #----------------------------------------------------------------------
    def getPositionDetail(self, vtSymbol):
        """查询持仓细节"""
        if vtSymbol in self.detailDict:
            detail = self.detailDict[vtSymbol]
        else:
            contract = self.getContract(vtSymbol)
            detail = PositionDetail(vtSymbol, contract)
            self.detailDict[vtSymbol] = detail
            
            # 设置持仓细节的委托转换模式
            contract = self.getContract(vtSymbol)
            
            if contract:
                detail.exchange = contract.exchange
                
                # 上期所合约
                if contract.exchange == EXCHANGE_SHFE:
                    detail.mode = detail.MODE_SHFE
                
                # 检查是否有平今惩罚
                for productID in self.tdPenaltyList:
                    if str(productID) in contract.symbol:
                        detail.mode = detail.MODE_TDPENALTY
                
        return detail
    
    #----------------------------------------------------------------------
    def getAllPositionDetails(self):
        """查询所有本地持仓缓存细节"""
        return self.detailDict.values()
    
    #----------------------------------------------------------------------
    def updateOrderReq(self, req, vtOrderID):
        """委托请求更新"""
        vtSymbol = req.vtSymbol
            
        detail = self.getPositionDetail(vtSymbol)
        detail.updateOrderReq(req, vtOrderID)
    
    #----------------------------------------------------------------------
    def convertOrderReq(self, req):
        """根据规则转换委托请求"""
        detail = self.detailDict.get(req.vtSymbol, None)
        if not detail:
            return [req]
        else:
            return detail.convertOrderReq(req)

    #----------------------------------------------------------------------
    def getLog(self):
        """获取日志"""
        return self.logList
    
    #----------------------------------------------------------------------
    def getError(self):
        """获取错误"""
        return self.errorList

2.DataEngine initialization function

        We see, first of all before the event engine passed over, initialize a variable number that will be used to store data.

 def __init__(self, eventEngine):
        """Constructor"""
        self.eventEngine = eventEngine
        
        # 保存数据的字典和列表
        self.tickDict = {}
        self.contractDict = {}
        self.orderDict = {}
        self.workingOrderDict = {}  # 可撤销委托
        self.tradeDict = {}
        self.accountDict = {}
        self.positionDict= {}
        self.logList = []
        self.errorList = []
        
        # 持仓细节相关
        self.detailDict = {}                                # vtSymbol:PositionDetail
        self.tdPenaltyList = globalSetting['tdPenalty']     # 平今手续费惩罚的产品代码列表
        
        # 读取保存在硬盘的合约数据
        self.loadContracts()
        
        # 注册事件监听
        self.registerEvent()

        Then read a list of futures flat this large fee. GlobalSetting presence inside, and this variable was actually read out from the file vtGlobal in

settingFileName = "VT_setting.json"
globalSetting = loadJsonSetting(settingFileName)

        VT_setting.json has this line:

"tdPenalty": ["IF", "IH", "IC"]

        So is the stock index futures open interest charges are days of punishment, if later let go of punishment, it can be removed.

        Then read the contract documents,

    def loadContracts(self):
        """从硬盘读取合约对象"""
        f = shelve.open(self.contractFilePath)
        if 'data' in f:
            d = f['data']
            for key, value in d.items():
                self.contractDict[key] = value
        f.close()

among them

contractFileName = 'ContractData.vt'
contractFilePath = getTempPath(contractFileName)

        In fact, this is from Pontus read a file, the file is actually obtained after ctp connection, access to information on the day's trading code has the code. Where the value of the key and the code key is, for example rb1901, while the corresponding value is a contract vnpy custom class, the class definition as follows:

class VtContractData(VtBaseData):
    """合约详细信息类"""

    #----------------------------------------------------------------------
    def __init__(self):
        """Constructor"""
        super(VtContractData, self).__init__()
        
        self.symbol = EMPTY_STRING              # 代码
        self.exchange = EMPTY_STRING            # 交易所代码
        self.vtSymbol = EMPTY_STRING            # 合约在vt系统中的唯一代码,通常是 合约代码.交易所代码
        self.name = EMPTY_UNICODE               # 合约中文名
        
        self.productClass = EMPTY_UNICODE       # 合约类型
        self.size = EMPTY_INT                   # 合约大小
        self.priceTick = EMPTY_FLOAT            # 合约最小价格TICK
        
        # 期权相关
        self.strikePrice = EMPTY_FLOAT          # 期权行权价
        self.underlyingSymbol = EMPTY_STRING    # 标的物合约代码
        self.optionType = EMPTY_UNICODE         # 期权类型
        self.expiryDate = EMPTY_STRING          # 到期日

        It is very detailed.

        Followed by the need to monitor the event to hang up

    def registerEvent(self):
        """注册事件监听"""
        self.eventEngine.register(EVENT_TICK, self.processTickEvent)
        self.eventEngine.register(EVENT_CONTRACT, self.processContractEvent)
        self.eventEngine.register(EVENT_ORDER, self.processOrderEvent)
        self.eventEngine.register(EVENT_TRADE, self.processTradeEvent)
        self.eventEngine.register(EVENT_POSITION, self.processPositionEvent)
        self.eventEngine.register(EVENT_ACCOUNT, self.processAccountEvent)
        self.eventEngine.register(EVENT_LOG, self.processLogEvent)
        self.eventEngine.register(EVENT_ERROR, self.processErrorEvent)

        This function which, once registered eight events, while the other method of the class basically is used for these events in response to the callback method.

        such as:

    def processTickEvent(self, event):
        """处理成交事件"""
        tick = event.dict_['data']
        self.tickDict[tick.vtSymbol] = tick   

        The role of event handlers is that somewhere triggered TICK event, our event engine will call this method, the data event to event object out method oh tickDict them. As for when the TICK event, then that thing will be discussed later.

        So DataEngine can be discussed here, hit the back of the corresponding event when we come back here to see an event handling callback function.

        After MainEngine initialized DataEngine, I began to look at something else initialization.

        # MongoDB数据库相关
        self.dbClient = None    # MongoDB客户端对象
        
        # 接口实例
        self.gatewayDict = OrderedDict()
        self.gatewayDetailList = []
        
        # 应用模块实例
        self. appDict = OrderedDict()
        self.appDetailList = []
        
        # 风控引擎实例(特殊独立对象)
        self.rmEngine = None

        Here, MainEngine initialize the MongoDB database objects, data interface instance object, that is behind us CTP, as well as application modules, which is an example of our strategy of stored dictionary, as well as risk control engine, which we would not be discussed, because let us not use the air control module.

        Then initialize the log.

        # 日志引擎实例
        self.logEngine = None
        self.initLogEngine()

        Here, we found MainEngine class again inside a logEngine, is not it amazing? We look initLogEngine and logEngine class will know, this kind of function is very simple, and not something else linkage, so outside logEngine go outside, go inside the class inside, do not make anyone with whom.

    def initLogEngine(self):
        """初始化日志引擎"""
        if not globalSetting["logActive"]:
            return
        
        # 创建引擎
        self.logEngine = LogEngine()
        
        # 设置日志级别
        levelDict = {
            "debug": LogEngine.LEVEL_DEBUG,
            "info": LogEngine.LEVEL_INFO,
            "warn": LogEngine.LEVEL_WARN,
            "error": LogEngine.LEVEL_ERROR,
            "critical": LogEngine.LEVEL_CRITICAL,
        }
        level = levelDict.get(globalSetting["logLevel"], LogEngine.LEVEL_CRITICAL)
        self.logEngine.setLogLevel(level)
        
        # 设置输出
        if globalSetting['logConsole']:
            self.logEngine.addConsoleHandler()
            
        if globalSetting['logFile']:
            self.logEngine.addFileHandler()
            
        # 注册事件监听
        self.registerLogEvent(EVENT_LOG)

        First, the setup file is provided to read the log, a log and then initializes the class:
        self.logEngine = LogEngine ()

        Then we look at how logEngine exactly is a kind of class. The code is very long, you can see for yourself at the source code. Here only part of the explanation impress, the latter article as well.

    def __init__(self):
        """Constructor"""
        self.logger = logging.getLogger()        
        self.formatter = logging.Formatter('%(asctime)s  %(levelname)s: %(message)s')
        self.level = self.LEVEL_CRITICAL
        
        self.consoleHandler = None
        self.fileHandler = None
        
        # 添加NullHandler防止无handler的错误输出
        nullHandler = logging.NullHandler()
        self.logger.addHandler(nullHandler)    
        
        # 日志级别函数映射
        self.levelFunctionDict = {
            self.LEVEL_DEBUG: self.debug,
            self.LEVEL_INFO: self.info,
            self.LEVEL_WARN: self.warn,
            self.LEVEL_ERROR: self.error,
            self.LEVEL_CRITICAL: self.critical,
        }

        In fact, the code is very simple, is used to package python native logging, and some information output Bale packaging. Key to look at different levels of logging implementation functions, such as debug level:

    def debug(self, msg):
        """开发时用"""
        self.logger.debug(msg)
        
    #----------------------------------------------------------------------
    def info(self, msg):
        """正常输出"""
        self.logger.info(msg)

        There is also a more important method of the class is to log the event of a callback function,

    def processLogEvent(self, event):
        """处理日志事件"""
        log = event.dict_['data']
        function = self.levelFunctionDict[log.logLevel]     # 获取日志级别对应的处理函数
        msg = '\t'.join([log.gatewayName, log.logContent])
        function(msg)

        In fact, the logic is very simple, it's time to log events to be output by taking the log level event, debug level to use debug, info on the level with the info.

        Let's look at the default settings file settings to log in:

"logActive": true,
"logLevel": "debug",
"logConsole": true,
"logFile": true,

        So, we see MainEngine of initLogEngine approach, we are set in the class comes out of this, we look at what we output the log file in the temp folder below.

For example, there is something like this:

2018-11-29 21:52:01,707  INFO: 启动CTA策略运行子进程
2018-11-29 21:52:01,707  INFO: 事件引擎创建成功
2018-11-29 21:52:01,733  INFO: 主引擎创建成功
2018-11-29 21:52:01,734  INFO: 注册日志事件监听
2018-11-29 21:52:01,756  INFO: 连接CTP接口
2018-11-29 21:52:01,760  INFO: MAIN_ENGINE MongoDB连接成功
2018-11-29 21:52:01,773  INFO: CTP 交易服务器连接成功
2018-11-29 21:52:01,782  INFO: CTP 行情服务器连接成功
2018-11-29 21:52:01,851  INFO: CTP 行情服务器登录完成
2018-11-29 21:52:01,852  INFO: CTP 交易服务器登录完成
2018-11-29 21:52:02,025  INFO: CTP 结算信息确认完成
2018-11-29 21:52:02,688  INFO: CTP 交易合约信息获取完成
2018-11-29 21:52:11,782  INFO: CTA策略载入成功
2018-11-29 21:52:11,788  INFO: CTA策略初始化成功
2018-11-29 21:52:11,788  INFO: CTA策略启动成功
2018-11-29 21:52:11,809  INFO: CTA_STRATEGY    找不到策略类:EmaDemoStrategy
2018-11-29 21:52:11,809  INFO: CTA_STRATEGY    atr rsi:atr rsi策略初始化
2018-11-29 21:52:11,809  INFO: CTA_STRATEGY    atr rsi的交易合约IC1706无法找到
2018-11-29 21:52:11,811  INFO: CTA_STRATEGY    king keltner:king keltner策略初始化
2018-11-29 21:52:11,811  INFO: CTA_STRATEGY    king keltner的交易合约IH1706无法找到
2018-11-29 21:52:11,812  INFO: CTA_STRATEGY    atr rsi:atr rsi策略启动
2018-11-29 21:52:11,812  INFO: CTA_STRATEGY    king keltner:king keltner策略启动

        In initLogEngine function, as well as the last one, it is to register an event log function

 # 注册事件监听
self.registerLogEvent(EVENT_LOG)

        Then we take a look at what this function yes.

    def registerLogEvent(self, eventType):
        """注册日志事件监听"""
        if self.logEngine:
            self.eventEngine.register(eventType, self.logEngine.processLogEvent)

        Nothing special, that is to say before the handler and EVENT_LOG linked, although I think there's really a bit tedious to write vnpy logical bit around.

        Here, we considered the main engine, which is MainEngine initialization function finished. Next is a very important thing, is to join the CTP, to stay inside in terms of a lower bar. Join CTP and strategies considered the core of the real trading code it.

 

Published 205 original articles · won praise 236 · views 980 000 +

Guess you like

Origin blog.csdn.net/qtlyx/article/details/84845093