小編給大家分享一下VNPY中如何實現(xiàn)從發(fā)送交易指令到交易所的源代碼,相信大部分人都還不怎么了解,因此分享這篇文章給大家參考一下,希望大家閱讀完這篇文章后大有收獲,下面讓我們一起去了解一下吧!
10年專注成都網(wǎng)站制作,企業(yè)網(wǎng)站設計,個人網(wǎng)站制作服務,為大家分享網(wǎng)站制作知識、方案,網(wǎng)站設計流程、步驟,成功服務上千家企業(yè)。為您提供網(wǎng)站建設,網(wǎng)站制作,網(wǎng)頁設計及定制高端網(wǎng)站建設服務,專注于企業(yè)網(wǎng)站設計,高端網(wǎng)頁制作,對發(fā)電機維修等多個領域,擁有豐富的網(wǎng)站營銷經(jīng)驗。
在策略中,一般不直接調(diào)用sendOrder(), 而且用四個二次封裝函數(shù)函數(shù),這些都是在class CtaTemplate中定義的,這里面主要區(qū)別就是sendorder()函數(shù)的中ordertype制定不一樣,用來區(qū)分是買開賣開等交易類型。
返回一個vtOrderIDList, 這個list里面包含vtOrderID,這個是個內(nèi)部給號,可以用做追蹤同一個order的狀態(tài)。
def buy(self, price, volume, stop=False): """買開""" return self.sendOrder(CTAORDER_BUY, price, volume, stop) #---------------------------------------------------------------------- def sell(self, price, volume, stop=False): """賣平""" return self.sendOrder(CTAORDER_SELL, price, volume, stop) #---------------------------------------------------------------------- def short(self, price, volume, stop=False): """賣開""" return self.sendOrder(CTAORDER_SHORT, price, volume, stop) #---------------------------------------------------------------------- def cover(self, price, volume, stop=False): """買平""" return self.sendOrder(CTAORDER_COVER, price, volume, stop)
2. 接下來我們看看那sendOrder()源碼,還在class CtaTemplate中定義;如果stop為True是本地停止單,這個停止單并沒有發(fā)送給交易所,而是存儲在內(nèi)部,使用ctaEngine.sendStopOrder()函數(shù); 否則這直接發(fā)送到交易所,使用ctaEngine.sendStopOrder函數(shù)。
這里會返回一個vtOrderIDList, 這個list里面包含vtOrderID,然后在被上面返回。這里補充一下,對于StopOrder真正觸發(fā)的交易通常是漲停價或者跌停價發(fā)出的市價單(Market price),參數(shù)price只是觸發(fā)條件;而普通sendOrder是真正按照參數(shù)price的限價單(Limit price)
def sendOrder(self, orderType, price, volume, stop=False): """發(fā)送委托""" if self.trading: # 如果stop為True,則意味著發(fā)本地停止單 if stop: vtOrderIDList = self.ctaEngine.sendStopOrder(self.vtSymbol, orderType, price, volume, self) else: vtOrderIDList = self.ctaEngine.sendOrder(self.vtSymbol, orderType, price, volume, self) return vtOrderIDList else: # 交易停止時發(fā)單返回空字符串 return []
3. 這里我們首先看看ctaEngine.sendStopOrder()函數(shù),在class CtaEngine中定義的,首先實例初始化時候定義了兩個字典,用來存放stoporder,區(qū)別一個是停止單撤銷后刪除,一個不會刪除;還定義了一個字典,策略對應的所有orderID。
def __init__(self, mainEngine, eventEngine): ……… # 本地停止單字典 # key為stopOrderID,value為stopOrder對象 self.stopOrderDict = {} # 停止單撤銷后不會從本字典中刪除 self.workingStopOrderDict = {} # 停止單撤銷后會從本字典中刪除 # 保存策略名稱和委托號列表的字典 # key為name,value為保存orderID(限價+本地停止)的集合 self.strategyOrderDict = {} ………
然后在函數(shù)sendStopOrder中,首先記錄給本地停止單一個專門編號,就是前綴加上順序編號,其中STOPORDERPREFIX 是 'CtaStopOrder.',那么第一條本地編碼就是'CtaStopOrder.1'。 后面是這個單據(jù)信息;這里可以發(fā)現(xiàn)orderType其實是一個direction和offset的組合,交易方向direction有Long、short兩個情況,交易對offset有open和close兩個情況。組合就是上面買開,賣平等等。然后把這個stoporder放入字典,等待符合價格情況到達觸發(fā)真正的發(fā)單。這里返回本地編碼作為vtOrderIDList。
def sendStopOrder(self, vtSymbol, orderType, price, volume, strategy): """發(fā)停止單(本地實現(xiàn))""" 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) # 推送停止單狀態(tài) strategy.onStopOrder(so) return [stopOrderID]
4. 下面是processStopOrder()函數(shù),也在class CtaEngine中定義的,主要是當行情符合時候如何發(fā)送真正交易指令,因為stopOrderID不是tick交易重點,這里簡單講講,具體請看源碼。
當接收到tick時候,會查看tick.vtSymbol,是不是存在workingStopOrderDict的so.vtSymbol有一樣的,如果有,再看tick.lastPrice價格是否可以滿足觸發(fā)閾值,如果滿足,根據(jù)原來so的交易Direction,Long按照漲停價,Short按照跌停價發(fā)出委托。然后從workingStopOrderDic和strategyOrderDict移除該so,并更新so狀態(tài),并觸發(fā)事件onStopOrder(so).
這里發(fā)現(xiàn),so只是只是按照漲停價發(fā)單給交易所,并沒有確保成績,而且市價委托的實際交易vtOrderID也沒有返回;從tick交易角度,再收到tick后再發(fā)送交易,本事也是有了延遲一tick。所以一般tick級別交易不建議使用stoporder。
def processStopOrder(self, tick): """收到行情后處理本地停止單(檢查是否要立即發(fā)出)""" vtSymbol = tick.vtSymbol # 首先檢查是否有策略交易該合約 if vtSymbol in self.tickStrategyDict: # 遍歷等待中的停止單,檢查是否會被觸發(fā) for so in self.workingStopOrderDict.values(): if so.vtSymbol == vtSymbol: longTriggered = so.direction==DIRECTION_LONG and tick.lastPrice>=so.price # 多頭停止單被觸發(fā) shortTriggered = so.direction==DIRECTION_SHORT and tick.lastPrice<=so.price # 空頭停止單被觸發(fā) if longTriggered or shortTriggered: # 買入和賣出分別以漲停跌停價發(fā)單(模擬市價單) if so.direction==DIRECTION_LONG: price = tick.upperLimit else: price = tick.lowerLimit # 發(fā)出市價委托 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) # 更新停止單狀態(tài),并通知策略 so.status = STOPORDER_TRIGGERED so.strategy.onStopOrder(so)
5. 前面說了這么多,終于到了正主sendOrder(),也在class CtaEngine中定義的。代碼較長,下面做了寫縮減。
1)通過mainEngine.getContract獲得這個品種的合約的信息,包括這個合約的名稱,接口名gateway(國內(nèi)期貨就是ctp),交易所,最小價格變動等信息;
2)創(chuàng)建一個class
VtOrderReq的對象req,在vtObject.py中,這個py包括很多事務類的定義;然后賦值,包括contract獲得信息,交易手數(shù),和price,和priceType,這里只有限價單。
3)根據(jù)orderType賦值direction和offset,之前sendStopOrder中已經(jīng)說了,就不重復。
4)然后跳到mainEngine.convertOrderReq(req),這里代碼比較跳,分析下來,如果之前沒有持倉,或者是直接返回[req];如果有持倉就調(diào)用PositionDetail.convertOrderReq(req),這個時候如果是平倉操作,就分析持倉量,和平今和平昨等不同操作返回拆分的出來[reqTd,reqYd],這里不展開。
5)
如果上一部沒有返回[req],則委托有問題,直接返回控制。如果有[req],因為存在多個req情況,就遍歷每個req,使用mainEngine.sendOrder發(fā)單,并保存返回的vtOrderID到orderStrategyDict[],strategyOrderDict[]兩個字典;然后把vtOrderIDList返回。
def sendOrder(self, vtSymbol, orderType, price, volume, strategy): """發(fā)單""" contract = self.mainEngine.getContract(vtSymbol) req = VtOrderReq() req.symbol = contract.symbol …… # 設計為CTA引擎發(fā)出的委托只允許使用限價單 req.priceType = PRICETYPE_LIMITPRICE # CTA委托類型映射 if orderType == CTAORDER_BUY: req.direction = DIRECTION_LONG req.offset = OFFSET_OPEN …… # 委托轉換 reqList = self.mainEngine.convertOrderReq(req) vtOrderIDList = [] if not reqList: return vtOrderIDList for convertedReq in reqList: vtOrderID = self.mainEngine.sendOrder(convertedReq, contract.gatewayName) # 發(fā)單 self.orderStrategyDict[vtOrderID] = strategy # 保存vtOrderID和策略的映射關系 self.strategyOrderDict[strategy.name].add(vtOrderID) # 添加到策略委托號集合中 vtOrderIDList.append(vtOrderID) self.writeCtaLog(u'策略%s發(fā)送委托,%s,%s,%s@%s' %(strategy.name, vtSymbol, req.direction, volume, price)) return vtOrderIDList
6. 在mainEngine.sendOrder中,這里不列舉代碼了,首先進行風控,如果到閾值就不發(fā)單,然后看gateway是否存在,如果存在,就調(diào)用gateway.sendOrder(orderReq)方法;下面用ctpgateway說明。class CtpGateway(VtGateway)是VtGateway是繼承,把主要發(fā)單,返回上面都實現(xiàn),同時對于不同的接口,比如外匯,數(shù)字貨幣,只要用一套接口標準就可以,典型繼承使用。
CtpGateway.sendOrder實際是調(diào)用class CtpTdApi(TdApi)的,這個就是一套ctp交易交口,代碼很簡單,最后是調(diào)用封裝好C++的ctp接口reqOrderInsert()。最關鍵返回的vtOrderID是接口名+順序數(shù)。
def sendOrder(self, orderReq): """發(fā)單""" self.reqID += 1 self.orderRef += 1 req = {} req['InstrumentID'] = orderReq.symbol req['LimitPrice'] = orderReq.price req['VolumeTotalOriginal'] = orderReq.volume # 下面如果由于傳入的類型本接口不支持,則會返回空字符串 req['OrderPriceType'] = priceTypeMap.get(orderReq.priceType, '') ....... # 判斷FAK和FOK if orderReq.priceType == PRICETYPE_FAK: req['OrderPriceType'] = defineDict["THOST_FTDC_OPT_LimitPrice"] req['TimeCondition'] = defineDict['THOST_FTDC_TC_IOC'] req['VolumeCondition'] = defineDict['THOST_FTDC_VC_AV'] if orderReq.priceType == PRICETYPE_FOK: req['OrderPriceType'] = defineDict["THOST_FTDC_OPT_LimitPrice"] req['TimeCondition'] = defineDict['THOST_FTDC_TC_IOC'] req['VolumeCondition'] = defineDict['THOST_FTDC_VC_CV'] self.reqOrderInsert(req, self.reqID) # 返回訂單號(字符串),便于某些算法進行動態(tài)管理 vtOrderID = '.'.join([self.gatewayName, str(self.orderRef)]) return vtOrderID
整個流程下來,不考慮stoporder,是ctaTemplate -> CtaEngine ->mainEngine ->ctpgateway ->CtpTdApi, 傳到C++封裝的接口。返回的就是vtOrderID; 因為存在平昨,平今還有鎖倉,反手等拆分情況,返回的可能是一組。
以上是“VNPY中如何實現(xiàn)從發(fā)送交易指令到交易所的源代碼”這篇文章的所有內(nèi)容,感謝各位的閱讀!相信大家都有了一定的了解,希望分享的內(nèi)容對大家有所幫助,如果還想學習更多知識,歡迎關注創(chuàng)新互聯(lián)行業(yè)資訊頻道!