:root{--brand-1:#18794e;--brand-2:#3dd68c;--brand-3:#2bb673;--brand-soft:#3dd68c22;} .hero .kicker{color:var(--brand-2)} .nav .ep{color:var(--brand-2)} .sec h2 .num{color:var(--brand-2)} .quote::before{background:var(--brand-1)} .quote .q-text .em{color:var(--brand-2)} .quote .q-water .pg{color:var(--brand-2)} .widget .w-title{color:var(--brand-2)} .takeaway h3{color:var(--brand-2)} .takeaway ol li::marker{color:var(--brand-2)} .next .nx-label{color:var(--brand-2)} .formula .eq{color:var(--brand-2)} a{color:var(--brand-2)} .comp.c1 .tag{background:var(--brand-soft);color:var(--brand-2)} /* K线合成演示 */ .bargen .controls{display:flex;gap:10px;flex-wrap:wrap;align-items:center;margin:10px 0;font-size:13px} .bargen .controls label{color:var(--t2)} .bargen .controls select{background:var(--bg-mute);border:1px solid var(--divider);color:var(--t1);border-radius:6px;padding:5px 8px;font:inherit;font-size:13px} .bargen .controls button{font-size:12.5px} .bargen .controls button.on{background:var(--brand-soft);color:var(--brand-2);border-color:var(--brand-1)} .bargen .tickstream{background:#0a0e14;border:1px solid var(--divider);border-radius:8px;padding:8px 10px;margin:10px 0;font-family:var(--mono);font-size:11.5px;color:var(--t3);min-height:54px;max-height:80px;overflow:hidden;line-height:1.6} .bargen .tickstream .tk{display:inline-block;margin:2px 4px;padding:1px 6px;border-radius:3px;background:var(--bg-mute);color:var(--t2)} .bargen .tickstream .tk.up{color:var(--green-1)} .bargen .tickstream .tk.dn{color:var(--red-1)} .bargen .chart{width:100%;height:240px;display:block;margin:10px 0} .bargen .ohlc{display:grid;grid-template-columns:repeat(4,1fr);gap:8px;margin:10px 0} .bargen .ohlc .oc{background:var(--bg-mute);border:1px solid var(--divider);border-radius:6px;padding:8px;text-align:center} .bargen .ohlc .oc .ol{font-size:11px;color:var(--t3)} .bargen .ohlc .oc .ov{font-size:16px;font-weight:700;font-family:var(--mono);color:var(--t1)} .bargen .ohlc .oc.o .ov{color:var(--cyan-1)} .bargen .ohlc .oc.h .ov{color:var(--green-1)} .bargen .ohlc .oc.l .ov{color:var(--red-1)} .bargen .ohlc .oc.c .ov{color:var(--brand-2)} .bargen .status{font-size:12.5px;color:var(--t3);text-align:center;margin-top:6px} .bargen .status .now{color:var(--brand-2);font-family:var(--mono);font-weight:600}
量化交易实战 · 第 10 期 · 第二季

给你的是 tick,你要的是 K 线——中间这步叫 BarGenerator

vnpy BarGenerator 合成原理 · tick to bar aggregation

交易所推的是一笔笔 tick(逐笔成交),策略算的是一根根 K 线(OHLC)。中间这步转换叫 BarGenerator:它收 tick,按时间窗口(如 1 分钟)或成交量累计,到点就把这批 tick 的开高低收打包成 Bar,推一个 EVENT_BAR。策略只订阅 bar 事件,不用管 tick。tick 是原料,bar 是策略能用的砖。
⏱ 约 8 分钟 🎯 写分钟级策略、想懂 tick→bar 的人 📦 源:vnpy §6

01tick 和 bar 是两种生物

tick 是"刚刚成交了一笔,价格 100.3,量 5 手"——一笔就是一个 tick,一天几十万笔。bar 是"这一分钟开盘 100.0、最高 100.5、最低 99.8、收盘 100.3"——把一堆 tick 压缩成 4 个数。

策略为什么用 bar 不用 tick?因为 tick 太碎、噪声太大,且大多数策略的逻辑是"看趋势"不是"看每一笔"。1 分钟 bar 把 60000 个 tick 压成 1 根 K 线,信号清晰、计算量小。但高频做市策略反过来——它们要 tick,甚至要 orderbook 深度。

vnpy 的 BarGenerator 就是这个转换器。它有两个模式:按时间合成(默认,每 N 分钟一根)和按成交量合成(每累计 N 手一根,适合做 VWAP)。下面演示的是时间合成。

tick 是原料,
bar 是策略能用的砖。
灏天文库 · 量化交易实战 P.30

02K 线合成演示:tick 流怎么变成 K 线

下面模拟 BarGenerator。选合成周期,点"跑"看 tick 一笔笔进来,当前 bar 的 OHLC 实时更新,到点(如 1 分钟)就封口、推一根新 K 线。注意第一笔 tick 决定 open,之后每笔更新 high/low,最后一笔决定 close。

📊 BarGenerator K 线合成演示
看 tick 流怎么被聚合成一根根 K 线(OHLC)。
Open
—
High
—
Low
—
Close
—
当前价 — · 已合成 0 根 K 线 · 窗口内 0 笔 tick

03合成逻辑:4 行代码的事

BarGenerator 的核心逻辑其实就 4 步,每来一个 tick:

if bar is None: bar = new Bar(tick) // 第一笔,open=high=low=close=tick.price
else: bar.high = max(bar.high, tick.price); bar.low = min(bar.low, tick.price); bar.close = tick.price
if now - bar.start ≥ window: push(EVENT_BAR, bar); bar = None // 封口,推下一根

关键细节:open 是窗口内第一笔 tick 的价格,不是窗口开始时刻的价格——因为可能那时刻没成交。close 是最后一笔,不是窗口结束时刻。high/low 是窗口内所有 tick 的极值。成交量是窗口内所有 tick 量的累加。

另一个坑:跨周期合成。vnpy 支持用 1 分钟 bar 合成 5 分钟、15 分钟 bar——叫 update_bar。原理一样,只是输入从 tick 换成 1 分钟 bar。但要注意时区对齐:5 分钟 bar 的起点要对齐到 0/5/10/15 分,不能从你启动那一刻开始算,否则 K 线和别人的对不上。

open 是第一笔不是第一秒,
close 是最后一笔不是最后一秒。
灏天文库 · 量化交易实战 P.31

04带走这套清单

✅ tick→bar 合成 6 条可执行规则

  1. 策略订阅 bar 不订阅 tick:除非高频,否则用 bar 信号清晰、计算量小。
  2. open=第一笔、close=最后一笔:不是窗口起止时刻的价格,没有成交就没价。
  3. 跨周期要对齐时间:5 分钟 bar 起点必须是 0/5/10 分,不能从启动时刻算。
  4. 成交量要累加:bar.volume = Σ tick.volume,不是 tick 数量。
  5. 按量合成做 VWAP:累计 N 手封一根,适合做成交量加权策略。
  6. 注意 tick 频率:低流动性品种 tick 稀疏,1 分钟 bar 可能只有几笔,信号不可靠。
把 tick 压成 bar,
是把噪声压成信号。
灏天文库 · 量化交易实战 P.32