VnTrader 實現CTA策略初始化完成後,自動啟動該策略

張國平發表於2021-11-19

其實用VnTrader都知道,CTA策略啟動分兩步,第一步初始化,第二部啟動。初始化要做的比較多,比如回放歷史資料,載入儲存交易資料等,如果載入歷史資料較多,或者從資料來源實時下載,是要一會。


如果no_ui, 或者定時執行;能不呢讓CTA策略初始化完成後,自動啟動呢。看起來很簡單。

cta_engine.init_strategy(strategy.strategy_name)
cta_engine.start_strategy(strategy.strategy_name)

直接這樣執行,後果都是提示: 策略{strategy.strategy_name}啟動失敗,請先初始化

為什麼呢,因為雖然大多數情況python是單執行緒執行,但是這裡init_strategy 使用了一個python 3的新特性執行緒池。

cta_engine原始碼如下

self.init_executor = ThreadPoolExecutor(max_workers=1)
def init_strategy(self, strategy_name: str):
    """
    Init a strategy.
    """
    self.init_executor.submit(self._init_strategy, strategy_name)

這裡不用具體去學習這個執行緒池,只要知道相對於vnTrade主執行緒,多建立一個獨立執行緒專門處理策略初始化;不會因此阻塞主執行緒的執行。所以當初始化程式碼執行後,雖然初始化沒有完成,系統也直接跳到下一步啟動程式碼了,因此報錯。

最好補充下,執行緒池有相對直接新建Thread有不少優點,主執行緒可以實時查詢某一個執行緒(或者任務的)的狀態,以及返回值。該執行緒完成的時候,主執行緒能夠立即知道。


好了,知道上面這個執行緒池的情況;那麼如何實現自動啟動了;首先看看vnTrader自帶的 no_ui示例程式碼。

cta_engine.init_all_strategies()    
sleep(60)   # Leave enough time to complete strategy initialization    
main_engine.write_log("CTA策略全部初始化")    
cta_engine.start_all_strategies()    
main_engine.write_log("CTA策略全部啟動")

好吧,這段程式碼有點笨拙,阻塞主執行緒60秒等待,雖然可以實現目的。但是如果CTA策略需要從資料來源實時下載資料,或者CTA策略有上百個時候;還沒有等全部初始話完成,就啟動了。


改進下,去查詢CTA策略初始化狀態,等策略狀態初始化完成後再啟動。

下面程式碼是每隔5秒去遍歷所有CTA策略,如果有一個策略未初始化,就跳出,等所有策略都初始化,進入下一步,啟動全部CTA策略。

cta_engine.init_all_strategies()
all_strategise_inited = False
while not all_strategise_inited:
   sleep(5)
   for strategy in cta_engine.strategies.values():
      if strategy.inited == False:
         all_strategise_inited = False
         break
      all_strategise_inited = True
main_engine.write_log("CTA策略全部初始化=====")
cta_engine.start_all_strategies()
main_engine.write_log("CTA策略全部啟動=====")


因為初始化執行緒池是單線池一個worker,初始化是一個接一個完成。 還有一個寫法,針對每個策略等待;如果策略已經初始化完成,就執行,在觀察下一個策略。

cta_engine.init_all_strategies()
strategies_list = cta_engine.strategies.values()
for strategy in strategies_list:
    while not strategy.inited:
         sleep(2)
       cta_engine.start_strategy(strategy.strategy_name)


但是兩個寫法都有個一個問題,一個是sleep來阻塞主流程不是很好程式碼寫法;更重要是因為如果其中有一個CTA初始化失敗報錯,那麼主執行緒就不會進入下一步,卡在while迴圈了。


有沒有可能不用主動去查詢CTA策略,而是等CTA策略自動完成初始化來回報提醒了。當然可以,這裡有就幾個實現方法,最方便就是使用VNPY自帶的Event事件機制。真實很方便的黑技術。

當CTA策略狀態改變時候會傳送事件EVENT_CTA_STRATEGY,此時訂閱這個事件,當接收到CTA狀態時是初始化完成但是未啟動時候,設定策略啟動。程式碼如下

cta_engine.init_engine()
main_engine.write_log("CTA策略引擎初始化完成")
for_init_system = True 
def once_strategy_inited(event):
   strategy = event.data
   vars = strategy["variables"]
   if  for_init_system and vars["inited"] and (not vars["trading"]):
      main_engine.write_log(f"CTA策略{strategy['strategy_name']}初始化完成")
      cta_engine.start_strategy(strategy["strategy_name"])
      main_engine.write_log(f"CTA策略{strategy['strategy_name']}啟動完成")
      
event_engine.register(EVENT_CTA_STRATEGY,once_strategy_inited)
main_engine.write_log("註冊CTA策略事件監聽")
cta_engine.init_all_strategies()

注意地方就是這裡使用方法中直接寫入方法的程式碼寫法,這樣就不用去傳輸main_engine和cta_engine了;還有就是需要有個for_init_system變數,在全部初始化後該變數改為False,這樣就不會出現使用中停止不了策略的情況。


最後補充下,在實際使用中,因為我使用時子執行緒介面化啟動vnTrader,就是vnTrader也是在子執行緒狀態執行,如果伺服器只是雙核CPU的情況,在初始化時候會出現PyQt5的介面更新被初始化阻塞情況,只有等全面策略自動初始化完成後,才會重新整理。個人感覺應該是雙核,一個被父執行緒,一個被初始化子執行緒都佔用了,造成vnTrader介面卡死情況,但是手動全部啟動時候又不會,有點奇怪。具體還是再研究把。

來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/22259926/viewspace-2843182/,如需轉載,請註明出處,否則將追究法律責任。

相關文章