大核桃 wrote:
更新部分代码:
if new_minute:
self.bar = BarData(
symbol=tick.symbol,
exchange=tick.exchange,
interval=Interval.MINUTE,
datetime=tick.datetime,
gateway_name=tick.gateway_name,
open_price=tick.last_price,
high_price=tick.last_price,
low_price=tick.last_price,
close_price=tick.last_price,
open_interest=tick.open_interest
)
'''
修正新的bar线当第一个tick出现当日最高价、最低价时,原有算法取不到该值的问题,同步更新初始成交量
'''
if (self.last_tick and tick.high_price > self.last_tick.high_price) or (self.last_tick == None):
self.bar.high_price = max(self.bar.high_price, tick.high_price)
if (self.last_tick and tick.low_price < self.last_tick.low_price) or (self.last_tick == None):
self.bar.low_price = min(self.bar.low_price, tick.low_price)
if (self.last_tick == None):
self.bar.volume = max(tick.volume, 0)
self.bar.turnover = max(tick.volume, 0)
新版vnpy4.5.1的BarGenerator的update_tick还是没有解决当self.last_tick == None的情形,需要按照这个PR进行修复。
一、验证vnpy4.5.1没有修复的代码:
from vnpy.trader.constant import Exchange
from vnpy.trader.database import get_database
from vnpy.trader.utility import BarGenerator #4.5.1版本
from vnpy.trader.object import TickData, BarData
from datetime import datetime
# 全局变量,用来接住合成出来的bar,分别是2025年8月4日9:30和9:31分的分钟Bar,品种为IF2509.CFFEX
target_bar_930: BarData = None
target_bar_931: BarData = None
def on_bar(bar: BarData):
"""BarGenerator回调,收到合成完成的K线"""
global target_bar_930, target_bar_931
bar_naive = bar.datetime.replace(tzinfo=None)
# 同时捕获 09:30 和 09:31 两根分钟Bar
if (bar.symbol == "IF2509"
and bar.exchange.value == "CFFEX"
and bar_naive in (datetime(2025, 8, 4, 9, 30), datetime(2025, 8, 4, 9, 31))
):
# 区分两根bar分别保存
if bar_naive == datetime(2025, 8, 4, 9, 30):
target_bar_930 = bar
elif bar_naive == datetime(2025, 8, 4, 9, 31):
target_bar_931 = bar
print(f"✅捕获目标分钟Bar:{bar.datetime}")
print(f"low_price: {bar.low_price}")
print(f"high_price: {bar.high_price}")
print(f"Volume: {bar.volume}")
print(f"Turnover: {bar.turnover}")
print("-" * 40)
def test_bar_generator_update_tick():
db = get_database()
symbol = "IF2509"
exchange = Exchange.CFFEX
start = datetime(2025, 8, 4, 9, 30, 0)
end = datetime(2025, 8, 4, 9, 32, 0,microsecond=200000) #为了取得9:30和9:31分中数据
# 从数据库读取该分钟全部tick数据
tick_list: list[TickData] = db.load_tick_data(
symbol=symbol,
exchange=exchange,
start=start,
end=end
)
print(f"读取到 {len(tick_list)} 条tick")
print("第一条tick时间:", tick_list[0].datetime)
print("最后一条tick时间:", tick_list[-1].datetime)
if not tick_list:
print("❌数据库没有找到该时间段tick数据")
return
# 创建BarGenerator,合成1分钟K线,绑定on_bar回调
bg = BarGenerator(on_bar)
# 逐个tick送入update_tick,模拟实盘推送tick
for tick in tick_list:
bg.update_tick(tick)
# 校验09:30结果
print("\n===== 校验 09:30 Bar =====")
if target_bar_930:
#
expect_low = 4017.4 # 来自米筐数据2025-08-04 9:30:0.2 的tick中出现tick.low_price
expect_volume = 1619 # 来自米筐数据2025-08-04 9:30:59.7的tick中出现tick.volume,应为要包含第一个tick9:30:0.2的量157,现有算法不包含初始量157.
actual_low = target_bar_930.low_price
actual_volume = target_bar_930.volume
if actual_low == expect_low:
print(f"✅校验通过,09:30该分钟最低价{actual_low}")
else:
print(f"❌校验不匹配,实际low={actual_low}")
if actual_volume == expect_volume:
print(f"✅校验通过,09:30该分钟成交量{actual_volume}")
else:
print(f"❌校验不匹配,实际成交量={actual_volume}")
else:
print("❌没有生成09:30那根分钟bar,请检查tick数据或时间")
# 校验09:31结果
print("\n===== 校验 09:31 Bar =====")
if target_bar_931:
expect_low = 4019.4 # 来自米筐数据2025-08-04 9:31:09.7 的tick中出现tick.last_price
expect_volume = 955 # 来自米筐数据2025-08-04 9:31:59.7的tick中出现tick.volume:2574-tick9:30:59.7的量1619=955.
actual_low = target_bar_931.low_price
actual_volume = target_bar_931.volume
if actual_low == expect_low:
print(f"✅校验通过,09:31该分钟最低价{actual_low}")
else:
print(f"❌校验不匹配,实际low={actual_low}")
if actual_volume == expect_volume:
print(f"✅校验通过,09:31该分钟成交量{actual_volume}")
else:
print(f"❌校验不匹配,实际成交量={actual_volume}")
else:
print("❌没有生成09:31那根分钟bar,请检查tick数据或时间")
if __name__ == "__main__":
test_bar_generator_update_tick()
二、测试数据来自米筐的2025年8月4日9:30到9:32分的IF2509.CFFEX tick数据