VNPY建立集中式的k線生成引擎,各個策略採用註冊訂閱,接收k線推送

張國平發表於2021-12-03


建立一個集中的k線生成引擎,可以統一的生成不同品種和長度的k線;各個策略採用註冊訂閱的模式,接收k線更新和推送。


使用背景,在使用vntrade cta策略交易模組時候,其每個策略都有各自例項化的K線生成器BarGenerator,來根據傳入的tick或者歷史回放的bar,來合成需要的k線bar,在on_minute_bar方法中進行條件發單或者平倉。對於中低頻的分鐘級策略,一個策略大部分時候,甚至90%時間主要的事情就是接收tick,然後合成1分鐘bar,然後合成15分鐘或者其他自定義長度bar。


對於一個品種只有一兩個策略倒是沒有什麼問題,如果一個品種對應的策略比較多,比如針對螺紋鋼的策略有5-6個,那麼每個策略自己來花時間做同樣的K線合成實在有浪費算力,尤其是python本身效率不高情況。從專案角度來看,批次的功能重複的事情,可以透過集中 k線生成器來實現。集中合成bar,使用方法回撥傳給策略對應的方法on_bar或者on_minute_bar。


本來想建立一個新的針對Bar的Event事件,使用Event事件引擎來傳送合成好的bar到訂閱的策略裡面;Event是標誌位只有簡單名稱str一條,而每個bar,至少有vt_symbol, window 和interval三個標誌位來區分推送給。寫著寫著脫離。


主要有兩個類,BarFactoryEngine,就是一個前臺引擎,提供針對策略K線呼叫方法註冊,接收tick,和歷史回放的bar等。

其中註冊訂閱方法,策略註冊需要的品種vt_symbol, K線時長,on_minute_bar方法。

BarFactoryEngine帶有symbol_barProdutors {品種: bar_productor例項} 字典儲存對應不同品種和對應bar_productor例項。


BarProductor,就是負責和vntrader自帶BarGenerator互動;被隱藏在引擎後面。有兩個字典。interval_bg是{K線長度:對應 BarGenerator} 字典,儲存真正的生成器;interval_functions_dict是{間隔 - [註冊方法]}字典儲存不同間隔和需要通知方法佇列。


程式碼在後面,使用的時候,可以在cta_engine 初始化時候,建立 BarFactoryEngine;策略使用很簡單,就是在替換掉原來self.bg,換成回撥方法組成即可,這裡一分鐘方法on_bar也有顯示的註冊。

cta_engine.barEngine.register_bar_function(vt_symbol, 1, self.on_bar)
cta_engine.barEngine.register_bar_function(vt_symbol,10,self.on_window_bar)

另外,因為 BarGenerator生成的bar,並不帶有K線資訊,不得不加上在 BarGenerator建立bar時候加上下面兩個屬性關了

self.window_bar.interval =self.interval
self.window_bar.window = self.window
# Check if window bar completed

這裡也支援歷史推送,倒是間接的解決重複歷史推送回放的問題,只要一次推送,所有這個品種的策略都會更新。 在cta_engine 的load_bar方法中,直接呼叫,會自動推送到所有涉及的策略,當然,對於原來的策略初始化邏輯也要修改。

for bar in bars:
    # callback(bar)
    self.barEngine.update_bar(bar)


最後,這個只是一個很初級的程式碼,沒有考慮到多執行緒,資料同步等情況。如果真的使用,還有大面積更新,純屬拋磚引玉。

比如繼承 BarGenerator,實現一個barGenerator生成多個不同長度K線功能等等。


具體程式碼

from vnpy.event import Event, EventEngine
from vnpy.trader.engine import BaseEngine, MainEngine
from vnpy.trader.constant import Interval
from vnpy.trader.object import (
    TickData,
    BarData
)
from vnpy.trader.event import EVENT_TICK
from vnpy.trader.utility import  BarGenerator
from typing import Callable
APP_NAME = "BarFactory"
class BarProductor:
    """
    BarProductor有個base_bg bargenertor預設提供1分鐘bar,其他分鐘長度bargenertor使用這個base_bg的一分鐘bar合成
    有{間隔 - generator}字典儲存不同間隔和對應的bargenerator
    有{間隔 - [註冊方法]} 字典儲存不同間隔和需要通知方法佇列。
    """
    def __init__(self,vt_symbol):
        self.vt_symbol = vt_symbol
        self.base_bg = BarGenerator(self.on_bar,1,self._process)
        self.interval_bg = {}
        self.interval_functions_dict = {}
        self.interval_bg[(1,"1m")] = self.base_bg
        self.interval_functions_dict[(1,"1m")] = []
    def register_function(self, interval:tuple, function) -> None:
        function_list = self.get_function_list(interval)
        if function not in function_list:
            function_list.append(function)
    def get_function_list(self,interval):
        function_list = self.interval_functions_dict.get(interval,None)
        if not function_list:
            self.get_bg(interval)
            self.interval_functions_dict[interval] = []
        return self.interval_functions_dict[interval]
    def get_bg(self,interval:tuple):
        bg = self.interval_bg.get(interval, None)
        if not bg:
            self.interval_bg[interval] = BarGenerator(self.on_bar, interval[0], self._process, interval[1])
        return self.interval_bg[interval]
    def on_tick(self, tick: TickData):
        """
        Callback of new tick data update.
        """
        self.base_bg.update_tick(tick)
    def on_bar(self, bar: BarData):
        """
        Callback of new bar data update.
        """
        [bg.update_bar(bar) for bg in self.interval_bg.values()]
    def _process(self,bar: BarData):
        function_list = self.get_function_list((bar.window,bar.interval))
        [function(bar) for function in function_list]
class BarFactoryEngine(BaseEngine):
    """
    Bar批次生成器,策略註冊需要的bar和對應on_bar或on_minute_bar方法;BarFactoryEngine去生成bar,並推送給對應方法。
    BarFactoryEngine帶有{品種-bar_productor例項} 字典儲存對應不同品種和對應bar_productor例項,
    """
    def __init__(self, main_engine: MainEngine, event_engine: EventEngine):
        """"""
        super().__init__(main_engine, event_engine, APP_NAME)
        self.symbol_barProdutors = {}
    def register_bar_function(
            self,
            vt_symbol,
            window: int = 0,
            on_window_bar: Callable = None,
            interval: Interval = Interval.MINUTE
    ):
        """"""
        bar_productor = self.get_symbol_barProdutors(vt_symbol)
        bar_productor.register_function((window,interval),on_window_bar)
    def get_symbol_barProdutors(self, vt_symbol: str):
        bp = self.symbol_barProdutors.get(vt_symbol, None)
        if not bp:
            bp = BarProductor(vt_symbol)
            self.symbol_barProdutors[vt_symbol] = bp
        return bp
    def register_event(self):
        """"""
        self.event_engine.register(EVENT_TICK, self.process_tick_event)
    def process_tick_event(self, event: Event):
        """"""
        tick = event.data
        for tick.vt_symbol in self.symbol_barProdutors:
            self.symbol_barProdutors[tick.vt_symbol].on_tick(tick)
    def update_bar(self, bar: BarData):
        """"""
        for bar.vt_symbol in self.symbol_barProdutors:
            self.symbol_barProdutors[bar.vt_symbol].on_bar(bar)


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

相關文章