VNPY 交易所返回委託和交易狀態到策略的原始碼分析
-
主要分析兩個在類策略模型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)。
來自 < >
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中。
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/22259926/viewspace-2168561/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- VNPY,從傳送交易指令到交易所的原始碼分析原始碼
- VNPY 基於SAR和肯特納的交易策略
- VNPY 自帶跨時間週期交易策略MultiTimeframeStrategy 分析
- 數字交易所原始碼大全原始碼
- NFT交易所繫統執行規則分析 | NFT交易所繫統開發原始碼示例原始碼
- BlueStore原始碼分析之事物狀態機原始碼
- 數字貨幣交易所APP開發原始碼案例分析APP原始碼
- (demo)交易所App開發功能|交易所繫統開發原始碼APP原始碼
- VNPY 單品種期貨的網格交易策略的實現
- C# 委託(delegate)、泛型委託和Lambda表示式C#泛型
- dotnet 委託的實現解析(2)開放委託和封閉委託 (Open Delegates vs. Closed Delegates)
- 在·VNPY中按照CTA策略例項記錄交易條目和損益
- 交易所開發(穩定版)/交易所繫統開發(python開發)/交易所開發(原始碼版)Python原始碼
- 交易所開發(海外版)丨交易所繫統開發(Python)丨 交易所繫統原始碼功能Python原始碼
- vnpy,BollChannel布林線軌道策略分析
- VNPY中 Tick級別準高頻交易簡單策略
- status 返回當前請求的http狀態碼HTTP
- VNPY 一種基於統計的交易策略簡易實現
- 交易所開發(海外版)/交易所繫統開發(案例詳細)/交易所繫統原始碼及demo原始碼
- swap交易所繫統(原始碼)丨swap交易所繫統開發(去中心化交易所開發詳細)原始碼中心化
- 交易所開發(海外版)丨交易所繫統開發(多語言)丨交易所成熟原始碼版原始碼
- 交易所繫統丨交易所繫統開發(上線版)丨交易所開發詳細原始碼部署原始碼
- 事件模型和事件委託事件模型
- c# 委託和事件C#事件
- 委託、匿名方法到lambda表示式
- 虛擬幣交易所繫統開發流程及原始碼分析介紹原始碼
- # 委託
- 委託
- 巢狀滾動設計和原始碼分析巢狀原始碼
- JavaScript設計模式之策略模式【組合委託】JavaScript設計模式
- 圖解HTTP《四》:返回結果的HTTP狀態碼圖解HTTP
- web頁面中http返回的狀態碼解釋WebHTTP
- 直播app原始碼,狀態列和導航欄設定成透明狀態APP原始碼
- 委託與事件-委託詳解(一)事件
- JavaScript 中的閉包和事件委託JavaScript事件
- JS事件流和事件委託JS事件
- 交易所開發(海外版)丨交易所繫統開發(Demo)交易所專案系統開發(原始碼定製)原始碼
- 深度解析:交易所繫統開發(海外版)交易所繫統開發(多語言)交易所開發(原始碼部署)原始碼