vn.py官网
开源量化社区
Member
加入于:
帖子: 30
声望: 4

1, 策略初始化的时候(假设此刻时间戳为T1) 会调用strategy.on_init方法, on_init方法调用load_bar(10)方法读取从(T1-10天)到T1内的K线数据并将其更新到ArrayManager中.

2, 而子线程中process_tick_event里面注册的方法是在策略已经完成初始化之后, 回调strategy.on_tick方法, 这个方法也会合成K线数据并更新到ArrayManager中, 而策略的初始化与否只取决于load_bar(10)是否完成

3, 也就是说load_bar(10)完成后, 子线程才会将收到的tick数据推送合成K线更新到ArrayManager中, 如果在load_bar(10)还未完成的时候, 交易所就推送了新的足以构成新的K线的tick数据, 那么这部分数据就不会更新到ArrayManager中,导致后续Arraymanager中的指标运算不准确

4, 个人觉得这是vnpy设计的不大健壮的一点

Member
加入于:
帖子: 30
声望: 4

根据目前的逻辑, 我唯一能想到的避免此种情况发生的办法就是在开盘前几分钟初始化并启动策略

Member
avatar
加入于:
帖子: 3027
声望: 174
  1. 不是的,你可以自己去print,on_bar一开始传数据am就在初始化了,而且大多数策略都是写了if not self.am.inited: return,这样am不初始化完是不能发出交易信号的;
  2. load_bar只是对self.Trading做了限制,完成后self.Trading才会变为True,才发的出单
Member
加入于:
帖子: 30
声望: 4

xiaohe wrote:

  1. 不是的,你可以自己去print,on_bar一开始传数据am就在初始化了,而且大多数策略都是写了if not self.am.inited: return,这样am不初始化完是不能发出交易信号的;
  2. load_bar只是对self.Trading做了限制,完成后self.Trading才会变为True,才发的出单
    1, am实例最初始由load_bar创建的, 是由rq接口的k线数据对其进行初始化的, 调用rq的那一刻起读取10天的数据
    2, if not self.am.inited: return,的作用是防止load_bar载入的10天数据不足以让Arraymanager完成初始化, 即am的数据不足100根, 如果这种情况的话是没有问题的, 因为不足100根k线就会return掉, 而am会继续接收推送过来的tick数据合成k线, 直至达到100个k线, am.inited状态变成TURE, 开始交易
    3, am的初始化状态有可能在10天K线未完全读取完就变成TURE, 不过剩下的K线数据还是会不停的更新am
    4, tick推送更新am需要等待load_bar的完成
    5, 所以问题就出现了啊, 这个很容易被忽略的
Member
加入于:
帖子: 30
声望: 4
def process_tick_event(self, event: Event):
    """"""
    tick = event.data

    strategies = self.symbol_strategy_map[tick.vt_symbol]
    if not strategies:
        return

    self.check_stop_order(tick)

    for strategy in strategies:
        if strategy.inited:
            self.call_strategy_func(strategy, strategy.on_tick, tick)

@xiaohe
你看下这里的逻辑, tick数据更新k线继而更新am的前提是strategy.inited=TRUE, 而strategy.inited的前提是load_bar()的完成和行情订阅, 你看下面的代码
self.call_strategy_func(strategy, strategy.on_init) # 这里的初始化指的是当策略启动的时候要预先做的一些事情,比如载入10天的K线数据

    # Restore strategy data(variables)
    data = self.strategy_data.get(strategy_name, None)
    if data:
        for name in strategy.variables:
            value = data.get(name, None)
            if value:
                setattr(strategy, name, value)

    # Subscribe market data
    contract = self.main_engine.get_contract(strategy.vt_symbol)
    if contract:
        req = SubscribeRequest(
            symbol=contract.symbol, exchange=contract.exchange)
        self.main_engine.subscribe(req, contract.gateway_name)
    else:
        self.write_log(f"行情订阅失败,找不到合约{strategy.vt_symbol}", strategy)

    # Put event to update init completed status.
    strategy.inited = True

看下面这行:
self.call_strategy_func(strategy, strategy.on_init),这里的strategy.on_init 其实就是load_bar(10), 所以strategy.inited状态的改变也是要等load_bar(10)完成了之后才会变为TRUE

Member
加入于:
帖子: 30
声望: 4

xiaohe wrote:

  1. 不是的,你可以自己去print,on_bar一开始传数据am就在初始化了,而且大多数策略都是写了if not self.am.inited: return,这样am不初始化完是不能发出交易信号的;
  2. load_bar只是对self.Trading做了限制,完成后self.Trading才会变为True,才发的出单

而且tick行情推送和load_bar()是不能两个线程同时更新am的, 因为根据am中数据的更新逻辑, 每次传入的单根K线只能顺序的更新数组的最后一个值

Member
avatar
加入于:
帖子: 3027
声望: 174
  1. 回测的trading==True在backtesting.py的run_backtesting函数里。而实盘的是engine.py里,是你在图形界面鼠标点击启动策略激活的。
  2. 实盘没有历史数据也可以运行,只是on_bar里写了if not self.am.inited: return 才没有发出交易信号。
Member
加入于:
帖子: 30
声望: 4

xiaohe wrote:

  1. 回测的trading==True在backtesting.py的run_backtesting函数里。而实盘的是engine.py里,是你在图形界面鼠标点击启动策略激活的。
  2. 实盘没有历史数据也可以运行,只是on_bar里写了if not self.am.inited: return 才没有发出交易信号。

@xiaohe 嗯,我现在用的是在github上的无ui交易,如果在图形界面交易的话,其实也需要历史数据以初始化100根K线,根据on_init逻辑, 他还是会执行三个步骤以尝试载入历史数据,如果行情接口不支持历史数据,则会尝试从rq接口读取数据,如果rq接口没有购买的话,则又会从数据库读取数据,如果数据库也没有读取到数据,则会跳过,vnpy会接着利用on_tick,on_bar回调函数收取数据以初始化arraymanager,直至arraymanager的numpy数组里填充满了100根K线,完成am的初始化,开始运算策略逻辑, 当然发出报单还是得看strategy.inited状态

我之所以会担心数据衔接问题, 是因为我需要重写arraymanager类,个人验证了一下, vnpy里的macd指标的计算有潜在问题, 倒不是因为这里macd用的是sma来算的, 而是因为talib从运算ema的时候底层的运算方式会有问题,这样会产生偏离,我改写arraymanager类之后可以做到几乎与通达信上的指标的值完全相同,误差在0.0001. 目前我需要解决的是载入历史数据和新数据产生组装时间上的同步问题

© 2015-2019 上海韦纳软件科技有限公司
备案服务号:沪ICP备18006526号-3

沪公网安备 31011502017034号