先看一下原版的 on_tick 逻辑以及问题点:
def on_tick(self, tick: TickData):
"""
Callback of new tick data update.
"""
if (
self.last_tick_time
and self.last_tick_time.minute != tick.datetime.minute
):
bars = {}
for vt_symbol, bg in self.bgs.items():
bars[vt_symbol] = bg.generate()
self.on_bars(bars)
bg: BarGenerator = self.bgs[tick.vt_symbol]
bg.update_tick(tick)
self.last_tick_time = tick.datetime
问题1:在订阅多个品种的时候,无法保证多品种的 tick 推送到 on_tick 里面的先后顺序,进而会出现 前一个 tick时间戳是第59秒, 后一个 tick时间戳变为了第0秒,然后再下一个时间戳又变为了第59秒的问题。每当tick的时间戳反复跨越第0秒时,会导致 bg.generate() 在很短的时间内,被多次被调用,每次被调用 bg.generate() 后,bg,bar 就会被清空为 None值了,进而导致第二次推到 on_bars 里面的 bars 大部分为None,因为在很短的时间内,几乎没有任何新的tick数据 调用了 bg.update_tick(tick)。
问题2:self.last_tick_time = tick.datetime, 这个写法在多合约订阅的时候,问题如上,这个写法不能保证,tick.datetime 的时间戳就一定比 self.last_tick_time 更晚。
问题3: 某些不活跃的合约,特别是期权合约,会存在一分钟之内没有新的tick过来的情况,如果直接调用 bg.generate() 的话,同样得到的Bar数据会为None
改进版的写法:
def on_tick(self, tick: TickData):
"""
Callback of new tick data update.
"""
if (
self.last_tick_time and tick.datetime > self.last_tick_time
and self.last_tick_time.minute != tick.datetime.minute
):
bars = {}
for vt_symbol, bg in self.bgs.items():
if bg.bar is not None:
bars[vt_symbol] = bg.generate()
bg.last_bar = bars[vt_symbol]
elif bg.last_bar is not None:
bars[vt_symbol] = bg.last_bar
else:
bars[vt_symbol] = None
self.on_bars(bars)
bg: BarGenerator = self.bgs[tick.vt_symbol]
bg.update_tick(tick)
if self.last_tick_time is None:
self.last_tick_time = tick.datetime
elif self.last_tick_time and self.last_tick_time < tick.datetime:
self.last_tick_time = tick.datetime
改进点1: 保证了 缓存中的 last_tick_time 时间戳,一定晚于 新来的 tick的时间戳,并且只有当 tick的时间戳 晚于 last_tick_time 时间戳时,才会合成 bars
改进点2: 每个品种的 bar里面,都缓存了 last_bar的属性,用于某些不活跃的品种 一分钟内没有新tick过来而无法合成 bars的情况。这个情况下,直接用上一个非None 的last_bar 推到 on_bars()里面
改完之后,on_bars里面已经很少会出现 bar 为 None的情况了。 如果还有什么考虑不周的地方,欢迎大家来指正共同改进~~