在no_ui中使用多程式實現多賬戶並行執行,並分配各自獨立的工作環境和策略
看到群裡有人問vnpy下多賬戶同時執行,並且給每個賬戶分配不同工作內容,就是執行不同策略。
考慮了一下,實現方式很多,在vnTrader 例項程式碼中 no_ui 啟動vnTrader就是用使用多程式模組multiprocessing,在子程式中執行。因為python GIL全域性鎖的存在,每個子程式都有獨立的例項化環境,其實就是一個獨立執行vnTrader。那麼多開幾個子程式,分配給對應的賬戶引數,就可以實現 vnpy下多賬戶同時執行。
這裡我用json檔案儲存賬戶配置資訊,每個賬戶配置資訊,
其中包括
- 賬戶名,後面用賬戶名命名子程式,這樣可以在log檔案中區加入子程式資訊,區分是那個賬戶的日誌資訊,方便跟蹤。
- 介面名,預設是CTP,也可以支援其他介面。
- 工作路徑,就是放.vnTrader資料夾和策略的路徑;給每個賬戶分配不同配置資訊和策略引數等。這裡要注意,必須在路徑下建立 .vnTrader資料夾,否則 vnpy的 utility 中_get_trader_dir方法就會使用home路徑作為工作路徑。
- 登入資訊,就是登入密碼一類東西。
具體程式碼這裡有幾個涉及技術點,先說說,具體的在程式碼看註釋就可以了。
第一個,同時執行account 不應該超過cpu核心數,因為vntrader 執行可以看成是一個持續死迴圈,在main_engine.close之前不會退出cpu佔用; 如果超過核心數,等待的執行事務將一直等待,所以不可以超過。
第二個,其實一開始我是用multiprocessing.Pool,再用pool.map_async(run_child, account_detail_list) 來實現批次建立程式,但是這樣沒法給每個程式命名,而且 map_async不帶阻塞,沒法跟蹤每個子程式返回。最後還是使用
multiprocessing.Process(target=run_child, name=account_detail["account_name"], args=(account_detail,))。
最後這裡不用pool.map,或者jion();這樣阻塞主程式的操作,剛剛說的因為vnTrader子執行緒是一個持續死迴圈,一旦阻塞,下次主執行緒只用等到完全停止完畢才能開始。
第三個,工作環境切換,這裡使用os.chdir更改工作路徑到配置檔案中的指定工作路徑;但是因為在引用vnpy 模組時候,就已經有全域性資料使用靜態方法去獲取工作路徑了,所以程式碼中把vnpy模組的import放在 程式方法中,更改工作路徑之後。
第四個,在啟動程式方法中,我又定義了兩個巢狀方法,用於給threading.Timer子執行緒來定時啟動監控策略初始化是否完成,和交易時間是否結束。原來程式碼都是用time.sleep 結合 while 來停止當前執行緒的,但是之前介面化執行vnTrader時候,sleep會停止pyQt前端介面,阻塞主程式碼執行,當然策略初始化和事件傳輸引擎都是獨立執行緒執行,不會影響。 所以為了為了保證交易安全,改成執行緒執行。
這裡簡單介紹下python執行緒,雖然由於GIL鎖,一個python環境只能用到一個核,但是在IO密集情況,比如網路爬蟲或者請求等待時候,使用多執行緒可以提高效率;直接sleep主執行緒,可以把資源給其他子執行緒。
執行效果如下:
兩個賬戶TEST_1_ACCOUNT ; TEST_2_ACCOUNT;各自獨立工作路徑;程式名也輸出介面,而log檔案也按照賬戶分開輸出。
引數檔案如下,名稱Mutiple_Accounts_Config.json 放在no_ui資料夾下就可以
[ { "account_name": "TEST_2_ACCOUNT", "gateway": "CTP", "workspace": "C:/TEST_ACCOUNT_2_WORKSPACE/", "logon_detail": { "使用者名稱": "", "密碼": "", "經紀商程式碼": "", "交易伺服器": "", "行情伺服器": "", "產品名稱": "", "授權編碼": "" } }, { "account_name": "TEST_1_ACCOUNT", "gateway": "CTP", "workspace": "C:/TEST_ACCOUNT_1_WORKSPACE/", "logon_detail": { "使用者名稱": "", "密碼": "", "經紀商程式碼": "", "交易伺服器": "", "行情伺服器": "", "產品名稱": "", "授權編碼": "" } } ]
run程式碼如下,因為改動較多,建議直接覆蓋原來的run.py:
import multiprocessing import json from threading import Timer import sys import os from time import sleep from datetime import datetime, time from logging import INFO # Chinese futures market trading period (day/night) DAY_START = time(8, 45) DAY_END = time(15, 0) NIGHT_START = time(20, 45) NIGHT_END = time(1, 30) def check_trading_period(): """""" current_time = datetime.now().time() trading = False if ( (current_time >= DAY_START and current_time <= DAY_END) or (current_time >= NIGHT_START) or (current_time <= NIGHT_END) ): trading = True return trading def run_child(account_detail): """ Running in the child process. """ account_name = account_detail["account_name"] ctp_setting = account_detail["logon_detail"] # 更改工作路徑 os.chdir(account_detail["workspace"]) # 把對vntrader 包的引用放在工作路徑更改後,不然工作路徑更改無法生效, from vnpy.event import EventEngine from vnpy.trader.setting import SETTINGS from vnpy.trader.engine import MainEngine from vnpy.gateway.ctp import CtpGateway from vnpy.app.cta_strategy import CtaStrategyApp from vnpy.app.cta_strategy.base import EVENT_CTA_LOG SETTINGS["log.active"] = True SETTINGS["log.level"] = INFO SETTINGS["log.console"] = True from vnpy.trader.utility import TRADER_DIR, TEMP_DIR # 結束引用 SETTINGS["log.file"] = True event_engine = EventEngine() main_engine = MainEngine(event_engine) main_engine.add_gateway(CtpGateway) cta_engine = main_engine.add_app(CtaStrategyApp) main_engine.write_log(f" 主引擎建立成功") main_engine.write_log(f"工作路徑: {TRADER_DIR, TEMP_DIR}") log_engine = main_engine.get_engine("log") # 更新log 格式。 event_engine.register(EVENT_CTA_LOG, log_engine.process_log_event) main_engine.write_log(f"註冊日誌事件監聽") main_engine.connect(ctp_setting, account_detail["gateway"]) main_engine.write_log(f"連線CTP介面") sleep(10) cta_engine.init_engine() main_engine.write_log(f"CTA策略初始化完成") def recheck_thread(): #每個10秒檢查所有策略是否初始化完成,使用Timer執行緒每10秒檢查, all_strategise_inited = False for strategy in cta_engine.strategies.values(): if strategy.inited == False: all_strategise_inited = False break all_strategise_inited = True if all_strategise_inited: main_engine.write_log(f"CTA策略全部初始化=====") cta_engine.start_all_strategies() main_engine.write_log(f"CTA策略全部啟動=====") else: newTask = Timer(10, recheck_thread) newTask.start() cta_engine.init_all_strategies() newTask = Timer(5, recheck_thread) newTask.start() def recheck_trading_period(): # 每隔10秒檢查是否交易時段,否則退出,使用Timer執行緒每10秒檢查, trading = check_trading_period() if not trading: main_engine.write_log(f"關閉子程式") main_engine.close() sys.exit(0) else: closeTask = Timer(10, recheck_trading_period) closeTask.start() closeTask = Timer(5, recheck_trading_period) closeTask.start() def run_parent(): """ Running in the parent process. """ print("啟動CTA策略守護父程式") with open('Mutiple_Accounts_Config.json', mode="r", encoding="UTF-8") as f: account_detail_list = json.load(f) child_process_list = [] while True: trading = check_trading_period() # Start child process in trading period if trading and child_process_list == []: print("啟動子程式") # 同時執行account 不應該超過cpu核心數,因為vntrader 可以看成是一個持續迴圈,在main_engine.close之前不會退出cpu佔用; # 等待的執行事務將一直等待 for account_detail in account_detail_list: new_process = multiprocessing.Process(target=run_child, name=account_detail["account_name"], args=(account_detail,)) new_process.start() child_process_list.append(new_process) # # 使用程式池更加方便,但是無法給程式命名去檢視log 是那個account的,所以不建議 # pool = multiprocessing.Pool(multiprocessing.cpu_count()) # child_process_list = pool.map_async(run_child, account_detail_list) print("子程式啟動成功") # 非記錄時間則退出子程式 if not trading and child_process_list: for process in child_process_list: if not process.is_alive(): child_process_list.remove(process) if child_process_list == []: print("子程式關閉成功") sleep(10) if __name__ == "__main__": run_parent()
還有一個小修改,為了輸出各自子程式名,在LogEngine中,trader/engine.py 中,進入這個processName:
self.formatter = logging.Formatter( "%(asctime)s %(levelname)s: %(processName)s %(message)s" )
不過如果賬戶太多,還是有點亂,那時候可以用其他工具來執行;另外策略程式碼也可以按照工作路徑區分,這裡沒有展示。
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/22259926/viewspace-2847093/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- Java多執行緒並行處理任務的實現Java執行緒並行
- 在Keycloak中實現多租戶並在ASP.NET Core下進行驗證ASP.NET
- 搭建go環境並執行Go
- C#並行,多執行緒程式設計並行集合和PLINQ的例項講解並行執行緒程式設計
- 26、多執行緒與並行執行緒並行
- Python並行程式設計(七):多程式的基本使用和與多執行緒的差異Python並行行程程式設計執行緒
- Java指令重排序在多執行緒環境下的應對策略Java排序執行緒
- 多專案並行時人員怎麼分配並行
- 從偽並行的 Python 多執行緒說起並行Python執行緒
- 多執行緒並行執行,然後彙總結果執行緒並行
- PyTorch中的多程序並行處理PyTorch並行
- Python的多程式和多執行緒Python執行緒
- 使用Java實現多執行緒程式設計Java執行緒程式設計
- Android中的多程式、多執行緒Android執行緒
- Python並行程式設計(二):多執行緒鎖機制利用Lock與RLock實現執行緒同步Python並行行程程式設計執行緒
- 如何使用 appium+pytest 進行多機並行執行不同 case 檔案APP並行
- Pytorch:單卡多程式並行訓練PyTorch並行
- [20190219]xargs -P實現並行執行.txt並行
- WPF打包獨立執行的程式
- 多執行緒實現多工二執行緒
- 多執行緒實現多工一執行緒
- Docker安裝java環境並部署jar包執行DockerJavaJAR
- 在 Golang 中使用 Go 關鍵字和 Channel 實現並行Golang並行
- systemverilog中for/foreach並行執行並行
- 多執行緒和多執行緒同步執行緒
- 實現dnmp中多站點多版本php並存PHP
- 多執行緒,多程式執行緒
- Java多執行緒的實現Java執行緒
- Pytorch使用資料並行,單機多卡PyTorch並行
- 執行緒和程式基礎以及多執行緒的基本使用(iOS)執行緒iOS
- 第19節 從庫MTS多執行緒並行回放(一)執行緒並行
- 第20節 從庫MTS多執行緒並行回放(二)執行緒並行
- 二十:從庫MTS多執行緒並行回放(二)(筆記)執行緒並行筆記
- 十九:從庫MTS多執行緒並行回放(一)(筆記)執行緒並行筆記
- C#多執行緒開發-任務並行庫04C#執行緒並行
- 【python隨筆】之【多程式並行統計多個cvs檔案行數】Python並行
- python 多程式和多執行緒學習Python執行緒
- 【多執行緒高併發程式設計】二 實現多執行緒的幾種方式執行緒程式設計