您好,登錄后才能下訂單哦!
主要分析兩個在類策略模型ctaTemplate的中的函數,onTrade和onOrder,其實兩個很相似,被別的其他實例調用,推入更新的Trade和Order實例,并執行函數內的代碼。對于Tick級別的交易,還是還是會經常用到這兩個。下面是在ctaTemplate中的定義。
def onOrder(self, order): """收到委托變化推送(必須由用戶繼承實現)""" # 對于無需做細粒度委托控制的策略,可以忽略onOrder pass # ---------------------------------------------------------------------- def onTrade(self, trade): """收到成交推送(必須由用戶繼承實現)""" # 對于無需做細粒度委托控制的策略,可以忽略onOrder pass
2. 先去看看 order 和 trade 是什么樣的類,兩個都在 vtObject.py 里面。理論上來說,在 tick 級別中高頻策略,當 order 和 trade 發生變化后,使用 onOrder/onTrade 傳遞更新給策略;函數 onOrder/onTrade 里面一般定義一些對應不同狀態進行的對應操作。
1) VtTradeData包含是成交的數據,其中最關鍵就是vtOrderID,可以和之前發送交易返回的vtOrderID做對應,用來對應的交易訂單。其他諸如direction/offset/price/volume都是很重要;可以用來更新postion數據。
2) 類VtOrderData和之前VtQrderReq很像,但是不一樣,這個是記錄委托信息狀態,req是交易請求,其中最關鍵的就是status,訂單狀態;這里有四個狀態(ALLTRADED全部成交,PARTTRADED部分成交, NOTTRADED未成交,和CANCLLED拒單),這些屬性在ctpGateway.py定義的。
class VtTradeData(VtBaseData): """成交數據類""" #---------------------------------------------------------------------- def __init__(self): """Constructor""" super(VtTradeData, self).__init__() # 代碼編號相關 self.symbol = EMPTY_STRING # 合約代碼 self.exchange = EMPTY_STRING # 交易所代碼 self.vtSymbol = EMPTY_STRING # 合約在vt系統中的唯一代碼,通常是 合約代碼.交易所代碼 self.tradeID = EMPTY_STRING # 成交編號 self.vtTradeID = EMPTY_STRING # 成交在vt系統中的唯一編號,通常是 Gateway名.成交編號 self.orderID = EMPTY_STRING # 訂單編號 self.vtOrderID = EMPTY_STRING # 訂單在vt系統中的唯一編號,通常是 Gateway名.訂單編號 # 成交相關 self.direction = EMPTY_UNICODE # 成交方向 self.offset = EMPTY_UNICODE # 成交開平倉 self.price = EMPTY_FLOAT # 成交價格 self.volume = EMPTY_INT # 成交數量 self.tradeTime = EMPTY_STRING # 成交時間 ######################################################################## class VtOrderData(VtBaseData): """訂單數據類""" #---------------------------------------------------------------------- def __init__(self): """Constructor""" super(VtOrderData, self).__init__() # 代碼編號相關 self.symbol = EMPTY_STRING # 合約代碼 self.exchange = EMPTY_STRING # 交易所代碼 self.vtSymbol = EMPTY_STRING # 合約在vt系統中的唯一代碼,通常是 合約代碼.交易所代碼 self.orderID = EMPTY_STRING # 訂單編號 self.vtOrderID = EMPTY_STRING # 訂單在vt系統中的唯一編號,通常是 Gateway名.訂單編號 # 報單相關 self.direction = EMPTY_UNICODE # 報單方向 self.offset = EMPTY_UNICODE # 報單開平倉 self.price = EMPTY_FLOAT # 報單價格 self.totalVolume = EMPTY_INT # 報單總數量 self.tradedVolume = EMPTY_INT # 報單成交數量 self.status = EMPTY_UNICODE # 報單狀態 self.orderTime = EMPTY_STRING # 發單時間 self.cancelTime = EMPTY_STRING # 撤單時間 # CTP/LTS相關 self.frontID = EMPTY_INT # 前置機編號 self.sessionID = EMPTY_INT # 連接編號
3. 之前提到數次通過 onOrder/onTrade 傳遞最新 Order/Trade 狀態,這個負責處理的是一個系列過程,上層推手就是類 ctaEngine ,下面主要說下函數 processOrderEvent ,處理委托推送。其中傳入的 event 是一個事件對象,由一個 type_ 說明類型,和一個字典 dict_ 存儲具體的事件數據組成。可以理解為是上面 vtObject 的一個包裝盒, event Engine只要根據標簽 type_ ,就可以把具體數據傳給對應的下層處理者。這個關于 event 具體的后面再分析.
這個函數,首先讀取了event字典中包好的order,因為存在手動發起交易情況 , 如果這個vtOrder是之前通過策略發出的,則調用callStrategyFunc來把這個order回傳到對應strategy . onOrder方法,如果是手動發出指令就算了。同時也分析狀態,如果在委托完成狀態,也更新strategyOrderDict字典,移除這個
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)
4. 在往上追溯就到eventEngine,首先在 ctaEngine 初始化時候,會分配eventEngine實例,再通過下面代碼注冊處理事件,當某類事件收到時候,調用對應的方法,比如事件類型EVENT_ORDER, 對應的方法是self.processOrderEvent。
class ctaEngine def registerEvent(self): """注冊事件監聽""" self.eventEngine.register(EVENT_TICK, self.processTickEvent) self.eventEngine.register(EVENT_ORDER, self.processOrderEvent) self.eventEngine.register(EVENT_TRADE, self.processTradeEvent) class eventEngine def register(self, type_, handler): """注冊事件處理函數監聽""" # 嘗試獲取該事件類型對應的處理函數列表,若無defaultDict會自動創建新的list handlerList = self.__handlers[type_] # 若要注冊的處理器不在該事件的處理器列表中,則注冊該事件 if handler not in handlerList: handlerList.append(handler)
在 eventEngine 中的 register函數就是處理的方法通過 __handlers字典來對應,__handlers是defaultdict(list),是一種特殊的字典,最大特點就是如果同一個 key 值插入不同 value ,他不會像就普通 dict 用新的替代,而且變成 {key:[value1,value2 , ……]} 這樣存儲。這樣就可以讓同一個 type ,可以有對應多個接收 handler 。
這里有兩個event Engine, 按照官方說法,
EventEngine類使用了PyQt中的QTimer來實現定時器功能,由PyQt應用主線程中的Qt事件循環來定時觸發(無需新開單獨的線程),適合在帶有圖形界面的應用程序中使用(如examples/VnTrader);
EventEngine2類則是使用了一個單獨的線程來實現定時器功能,適合在無圖形界面的應用程序中使用(如examples/CtaTrading)。
來自 < https://github.com/vnpy/vnpy/wiki/%E4%BA%8B%E4%BB%B6%E5%BC%95%E6%93%8E >
5. 上面說了 eventEngine的組成 Event ,然后還有一個后面處理函數def __process(self, event)。 在一個內部隊列__queue中不停抓起 event ,通過檢索字典 __handlers來分配到對應的函數處理。那么誰放入新的event呢,就是一個調用put(event)函數向事件隊列插入事件。這個時候發現一個特殊的 EVENT_TIMER ,看了半天,感覺可以理解為是一個節奏控制器,每一秒去做一次 process ;那么對于高頻來說,可能換成 500 毫秒更合適。
下面是VNPY定義的EVENT事件。
# 系統相關 EVENT_TIMER = 'eTimer' # 計時器事件,每隔1秒發送一次 EVENT_LOG = 'eLog' # 日志事件,全局通用 # Gateway相關 EVENT_TICK = 'eTick.' # TICK行情事件,可后接具體的vtSymbol EVENT_TRADE = 'eTrade.' # 成交回報事件 EVENT_ORDER = 'eOrder.' # 報單回報事件 EVENT_POSITION = 'ePosition.' # 持倉回報事件 EVENT_ACCOUNT = 'eAccount.' # 賬戶回報事件 EVENT_CONTRACT = 'eContract.' # 合約基礎信息回報事件 EVENT_ERROR = 'eError.' # 錯誤回報事件
6. 現在想著是誰在不停的給這個內部隊列放入 order/trick 狀態的 event 呢 , 而在 ctp G ate 這個類中,在其父類 vtGate 中有 on Or de r 方法,很規范的打包 order 到 evet ,然后放到隊列里面。還有分析后發現在 Mainengine 對整個 eventEngine 進行管理,并通過 addGateway 通過中把在事件引擎和交易接口管理。
def onOrder(self, order): """訂單變化推送""" # 通用事件 event1 = Event(type_=EVENT_ORDER) event1.dict_['data'] = order self.eventEngine.put(event1) # 特定訂單編號的事件 event2 = Event(type_=EVENT_ORDER+order.vtOrderID) event2.dict_['data'] = order self.eventEngine.put(event2)
7.
在至上是
class CtpTdApi(TdApi)
這個類的,讀取
data
中的
order
相關數據,創建
order
,推送到上面的這個
onOrder
里面
;
在往上就有點頭大了,這個
data
信息應該是從編譯底層返回的。
def onRtnOrder(self, data): """報單回報""" # 更新最大報單編號 newref = data['OrderRef'] self.orderRef = max(self.orderRef, int(newref)) # 創建報單數據對象 order = VtOrderData() order.gatewayName = self.gatewayName # 保存代碼和報單號 order.symbol = data['InstrumentID'] order.exchange = exchangeMapReverse[data['ExchangeID']] order.vtSymbol = order.symbol #'.'.join([order.symbol, order.exchange]) order.orderID = data['OrderRef'] # CTP的報單號一致性維護需要基于frontID, sessionID, orderID三個字段 # 但在本接口設計中,已經考慮了CTP的OrderRef的自增性,避免重復 # 唯一可能出現OrderRef重復的情況是多處登錄并在非常接近的時間內(幾乎同時發單) # 考慮到VtTrader的應用場景,認為以上情況不會構成問題 order.vtOrderID = '.'.join([self.gatewayName, order.orderID]) order.direction = directionMapReverse.get(data['Direction'], DIRECTION_UNKNOWN) order.offset = offsetMapReverse.get(data['CombOffsetFlag'], OFFSET_UNKNOWN) order.status = statusMapReverse.get(data['OrderStatus'], STATUS_UNKNOWN) # 價格、報單量等數值 order.price = data['LimitPrice'] order.totalVolume = data['VolumeTotalOriginal'] order.tradedVolume = data['VolumeTraded'] order.orderTime = data['InsertTime'] order.cancelTime = data['CancelTime'] order.frontID = data['FrontID'] order.sessionID = data['SessionID'] # 推送 self.gateway.onOrder(order)
總體來看,eventEngine這個是一個總的驅動,在內部queue這個傳送帶,分發做了字典里面類型標記的Event實例給對應的處理對象;ctpGateway這個通過put把新的event放入queue中。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。