VNPY源碼學習(1) ---------- 兩個主引擎
在vnpy中,有兩個主要的引擎:事件引擎和主引擎:
事件引擎:EventEngine
vnpy/event/engine.py
事件引擎根據其類型將事件對象分發給註冊的處理程序。 它還會按每個間隔秒生成定時器事件,這個功能可用於計時。計時器事件每interval秒被生成。實例化:
event_engine = EventEngine()
或
event_engine = EventEngine(interval)
interval表示計時器間隔,默認爲1秒。
EventEngine中
self._thread = Thread(target=self._run) # 處理事件的線程
self._timer = Thread(target=self._run_timer) # 計時器線程
-
_run()函數:從隊列中獲取事件並處理(_process()函數)事件。
- 只要引擎被啓動,引擎將不斷從隊列中取出事件進行process
- _process():首先將事件分發給那些註冊監聽了此類型的處理程序。 然後分發事件給監聽了所有類型通用處理程序。
-
_run_timer()函數:暫停interval秒後生成一個計時器事件
-
put():將一個事件對象放到事件隊列中
-
register:給特定的事件類型註冊處理器。每一個函數只能爲某一事件類型註冊一次。
-
register_general:註冊通用處理程序
下面通過幾個例子看看事件引擎的操作。
1. 計時器
首先看看計時器是怎麼操作的。在實例化了事件引擎以後,啓動事件引擎,並註冊兩個計時器事件:
event_engine = EventEngine()
event_engine.start()
event_engine.register(EVENT_TIMER, test)
event_engine.register(EVENT_TIMER, test2)
test和test2爲事件的處理器,爲一個方法,方法必須接受事件參數,這裏打印出時間:
def test(event: Event):
print("in test:" + str(datetime.now()))
def test2(event: Event):
print("in test2:" + str(datetime.now()))
運行後得到結果:
解釋:
event_engine.register(EVENT_TIMER, test)
註冊一個計時器事件,並將該計時器事件的處理方法設置爲test()
register()
裏面,將事件名稱和事件處理器保存起來
def register(self, type: str, handler: HandlerType):
handler_list = self._handlers[type]
if handler not in handler_list:
handler_list.append(handler)
註冊了兩個計時器事件之後,self._handlers
爲:
可見,事件是以字典的形式保存的,字典的key爲事件名稱,value爲一個列表,裏面的值爲事件的處理方法。
而在事件引擎啓動時,計時器線程就啓動了,計時器每暫停一秒,就put一個計時器事件
def _run_timer(self):
while self._active:
sleep(self._interval)
event = Event(EVENT_TIMER)
self.put(event)
put就是將事件添加到隊列對象中
def put(self, event: Event):
self._queue.put(event)
在計時器線程啓動的同時,處理器線程就啓動了,下面是啓動事件引擎的方法:
def start(self):
self._active = True
self._thread.start()
self._timer.start()
因此處理器線程調用_run()方法,方法不斷從隊列中取事件,並用self._process(event)處理它:
def _run(self):
while self._active:
try:
event = self._queue.get(block=True, timeout=1)
self._process(event)
except Empty:
pass
2. 處理事件
使用for循環將該事件的處理方法全部依次調用,參數爲事件本身
def _process(self, event: Event):
if event.type in self._handlers:
[handler(event) for handler in self._handlers[event.type]]
if self._general_handlers:
[handler(event) for handler in self._general_handlers]
3. 測試
如何自己定義一個事件
首先定義一個處理器,事件引擎調用方法後參數爲事件本身,因此打印出事件的data屬性:
def my_func(event: Event):
print(event.data)
實例化一個事件:
my_event = Event(
type="self_event",
data="test data ..."
)
然後註冊這個事件
event_engine.register("self_event", my_func)
並將該事件放入隊列中
event_engine.put(my_event)
執行後得到結果爲:
主引擎:MainEngine
vnpy/trader/engine.py 主引擎是VN Trader的核心,
實例化:
main_engine = MainEngine(event_engine)
1. 實例化主引擎
event_engine表示事件引擎對象,默認爲None,如果沒有傳入事件引擎對象,系統在主引擎中實例化一個對象:
1.1 加載事件引擎
if event_engine:
self.event_engine = event_engine
else:
self.event_engine = EventEngine()
1.2 啓動事件引擎
然後event_engine.start() 啓動事件引擎,start()方法中
- 啓動事件引擎(self._active = True)以處理事件生和成計時器事件。
- 啓動處理事件的線程
- 啓動計時器線程
os.chdir(TRADER_DIR) # 更改工作目錄,這一步沒必要
1.3 初始化內置引擎
self.init_engines()
初始化引擎:
def init_engines(self):
self.add_engine(LogEngine)
self.add_engine(OmsEngine)
self.add_engine(EmailEngine)
初始化引擎時將這三種引擎添加到主引擎的engines字典中,key爲engine的名字,value爲引擎的實例對象:
def add_engine(self, engine_class: Any):
engine = engine_class(self, self.event_engine) # 實例化指定引擎
self.engines[engine.engine_name] = engine
return engine
vnpy中內置了三個引擎,分別爲:
- LogEngine 日誌引擎
- OmsEngine 指定管理系統 (Order Manage System)
- EmailEngine
在後面會詳細介紹該三種引擎。
2. 其他方法
def add_engine(self, engine_class: Any) # 添加引擎
def add_gateway(self, gateway_class: BaseGateway) # 添加網關
def add_app(self, app_class: BaseApp) # 添加應用
def write_log(self, msg: str, source: str = "", level=20) # 輸出日誌,在LogEngine中介紹
def get_gateway(self, gateway_name: str) # 根據網關名稱獲取網關實例
def get_engine(self, engine_name: str) # 根據引擎名稱獲取引擎實例
def get_all_gateway_names(self) # 獲取所有的網關名(只獲取名稱,不獲取實例)
def get_all_apps(self) # 獲取所有的應用名稱
def get_all_exchanges(self) # 獲取所有的交易所 交易所在添加網關的時候將指定網關支持的交易所添加到交易所列表中
def connect(self, setting: dict, gateway_name: str)
def subscribe(self, req: SubscribeRequest, gateway_name: str)
def send_order(self, req: OrderRequest, gateway_name: str)
def cancel_order(self, req: CancelRequest, gateway_name: str)
def send_orders(self, reqs: Sequence[OrderRequest], gateway_name: str)
def cancel_orders(self, reqs: Sequence[CancelRequest], gateway_name: str)
def query_history(self, req: HistoryRequest, gateway_name: str)
上面的幾個方法都是在指定網關內完成的,具體使用的時候具體講解
def close(self):
"""
在程序退出前確保每一個網關和應用都被關閉
"""
self.event_engine.stop()
for engine in self.engines.values():
engine.close()
for gateway in self.gateways.values():
gateway.close()