4. Tick与K线的关系

在行情系统中,Tick是市场最细粒度的成交记录,而K线是面向人类阅读与策略计算的聚合结果。二者不是简单的“多对一”关系,而是一套带有时间语义、边界规则和状态维护的映射过程。理解这套映射逻辑,是写出正确K线聚合器的前提。

一、核心原理:Tick到K线的四元映射

每一根K线本质上由四个价格和一个量额构成:开盘价、最高价、最低价、收盘价,以及成交量、成交额。Tick到K线的映射,就是把这六个字段按时间窗口进行归并。其核心规则可以概括为:

  • 开盘价:窗口内第一笔Tick的成交价(或该窗口第一笔有效成交价)。
  • 最高价:窗口内所有Tick成交价的最大值。
  • 最低价:窗口内所有Tick成交价的最小值。
  • 收盘价:窗口内最后一笔Tick的成交价。
  • 成交量/成交额:窗口内所有Tick的成交量/成交额累加。

这里的关键在于“窗口内”三个字。窗口如何定义、如何对齐、边界Tick归属哪根K线,直接决定了聚合结果是否与交易所行情一致。

映射逻辑的本质:K线 = 时间窗口 + 聚合函数(first/max/min/last/sum)。时间窗口决定归属,聚合函数决定数值。

二、时间窗口对齐:左闭右开是默认契约

绝大多数行情系统采用左闭右开区间,即 [start, end)。例如1分钟K线,10:00:00.000 到 10:00:59.999 的Tick归属10:00这根K线,10:01:00.000 的Tick归属下一根。这样做的原因是避免同一时刻的Tick被重复计入两根K线。

时间窗口对齐还需要处理“窗口起点”问题。常见做法有两种:

  1. 自然时间对齐:以交易所开盘时间为基准,按固定周期切分,如09:30、09:31……适用于股票、期货等有明确交易时段的品种。
  2. 滚动对齐:以第一笔Tick时间为起点,向后滚动切分。适用于7×24小时交易的数字货币等品种。

无论哪种对齐方式,聚合器内部都应维护一个“当前窗口”状态:窗口起始时间、当前OHLC、累计量额。每来一笔Tick,先判断其时间戳是否落在当前窗口内;若不在,则先输出当前K线,再开启新窗口。

# 伪代码:窗口归属判断
if tick.timestamp < current_window.end:
    update_current_bar(tick)
else:
    emit(current_bar)
    current_bar = new_bar(tick)
    current_window = next_window(tick.timestamp)

三、边界条件处理:最容易被忽略的坑

边界条件是Tick聚合中最容易出错的地方,常见的有以下几类:

边界场景 问题描述 推荐处理
整点Tick 10:01:00.000 归属10:00还是10:01 左闭右开,归属10:01
无成交窗口 某分钟没有任何Tick 补空K线或跳过,需与下游约定
乱序Tick 时间戳回退的迟到数据 丢弃或按事件时间重排
集合竞价 开盘前集中撮合产生大量Tick 单独窗口或并入首根K线
收盘最后一秒 15:00:00.000 是否计入当日最后一根 按交易所规则,通常计入
乱序Tick是实时聚合中最危险的情况。若不做事件时间处理,一笔迟到的Tick可能被错误地计入当前窗口,导致OHLC与成交量全部偏移。生产环境建议引入水位线(watermark)机制。

四、案例场景:1分钟K线聚合的完整流程

假设我们收到以下Tick序列(时间戳简化表示):

09:30:00.100  价格100.0  量10
09:30:15.200  价格100.5  量20
09:30:59.900  价格99.8   量5
09:31:00.000  价格100.2  量15
09:31:30.500  价格101.0  量30

按左闭右开规则,前三条归属09:30这根K线,后两条归属09:31。聚合结果为:

  • 09:30 K线:开100.0,高100.5,低99.8,收99.8,量35。
  • 09:31 K线:开100.2,高101.0,低100.2,收101.0,量45。

注意09:30:59.900这笔Tick虽然接近整点,但仍属于09:30窗口;而09:31:00.000这笔恰好落在边界上,按左闭右开归属09:31。如果实现时用了 <= 而不是 <,就会把整点Tick错误地计入上一根K线。

Tick到K线的时间窗口映射(左闭右开) 09:30:00 09:31:00 09:32:00 窗口1 [09:30, 09:31) 窗口2 [09:31, 09:32) 100.0 100.5 99.8 100.2 101.0 边界:09:31:00.000 归属窗口1 归属窗口2 窗口1内Tick 窗口2内Tick 窗口边界

五、误区与复盘

误区一:用收盘时间判断归属。有些实现用Tick的“接收时间”而非“交易所时间”做窗口判断,导致网络延迟时Tick被错误归入下一根K线。正确做法是始终使用交易所时间戳。

误区二:忽略空窗口。若某分钟无成交,下游策略可能期望K线连续。此时需要明确约定:是补一根开高低收等于前收的“平K线”,还是直接跳过。两种做法对均线、波动率计算影响很大。

误区三:边界用闭区间。把 [start, end] 当作窗口,会导致整点Tick同时属于两根K线,成交量被重复计算。这是最隐蔽也最致命的错误。

误区四:不处理乱序。实时流中迟到Tick若直接更新当前窗口,会破坏OHLC的单调性。建议在聚合器前加一层按事件时间排序的缓冲,或明确丢弃超过水位线的迟到数据。

复盘方法:用交易所官方K线与你聚合出的K线做逐根对比,重点检查整点、开盘、收盘、无成交分钟四类边界。若全部一致,说明映射逻辑正确。

总结来说,Tick到K线的映射并不复杂,但正确性完全取决于时间窗口的对齐方式和边界条件的处理策略。把“左闭右开”作为默认契约,把乱序和空窗口作为一等公民处理,才能让聚合结果与交易所行情保持一致,为后续策略计算提供可靠基础。