2. 行情数据基础
做行情回放引擎,第一件事不是写回放循环,而是把“回放什么”定义清楚。行情数据并不是一种统一格式,而是由多个粒度、多个通道、多个时间语义叠加而成的数据族。如果数据结构没吃透,速度控制再精细也会出现“回放顺序错乱”“时间戳对不齐”“逐笔和快照互相打架”的问题。本节把六类核心数据对象拆开讲清楚,并给出统一的时间戳规范。
一、六类行情对象的定位与结构
从市场数据生产链路看,交易所先产生逐笔委托和逐笔成交,再聚合成快照,快照再被压缩成 Level-1 或 Level-2 行情,最后按周期聚合成 K 线。Tick 是一个宽泛概念,通常指“带时间戳的最小变动记录”,在不同市场可能指逐笔成交、最优报价变动或快照切片。理解它们的关键是分清三件事:是否全量、是否带档位、是否可还原订单簿。
| 数据对象 | 典型字段 | 粒度 | 能否还原订单簿 | 回放用途 |
|---|---|---|---|---|
| Tick(成交类) | 时间、价格、数量、方向 | 逐笔 | 否 | 成交驱动策略 |
| Level-1 | 最新价、买卖一档、成交量 | 切片/快照 | 否 | 轻量行情展示 |
| Level-2 | 十档/多档委托、逐笔委托、逐笔成交 | 逐笔+快照 | 是(近似) | 盘口与微观结构 |
| K线 | OHLC、成交量、周期 | 聚合 | 否 | 趋势与回测 |
| 快照 | 时间、档位数组、总量 | 定时切片 | 部分 | 状态重建 |
| 逐笔委托 | 委托号、方向、价格、数量、类型 | 逐笔 | 是 | 订单簿重建 |
逐笔委托是还原订单簿的核心。它通常包含委托编号、买卖方向、价格、数量和委托类型(限价、市价、撤单)。逐笔成交则包含成交编号、价格、数量以及主动方方向。快照是某一时刻订单簿的切片,包含若干档位的价格与数量。K 线是时间聚合结果,必须记录周期起点时间,而不是“生成时间”。
二、时间戳规范:三种时间必须分清
行情回放中最容易出错的就是时间戳。至少要区分三类时间:交易所时间(Exchange Time)、本地接收时间(Local Receive Time)、回放逻辑时间(Replay Time)。交易所时间是数据自带的业务时间,通常精确到毫秒或微秒;本地接收时间是网关落库时间,受网络抖动影响;回放逻辑时间是引擎内部推进的虚拟时钟,用于速度控制。
时间戳字段建议统一为 64 位整数纳秒或微秒,避免浮点误差。跨市场数据要标注时区,A 股使用 Asia/Shanghai,期货夜盘要注意交易日与自然日的区别。K 线的时间戳应表示周期开始时间,例如 1 分钟 K 线标记为 09:30:00,而不是 09:30:59。
{
"symbol": "600519.SH",
"exchange_time": 1717032600123456789,
"local_time": 1717032600125000000,
"seq": 88213,
"type": "order",
"side": "bid",
"price": 1680.00,
"qty": 300,
"order_type": "limit"
}
三、回放场景:从逐笔委托重建盘口
假设我们要回放某只股票 09:30 到 09:31 的 Level-2 行情。数据源同时提供逐笔委托、逐笔成交和快照。正确流程是:先按交易所时间排序所有记录,再按类型分发处理。逐笔委托进入订单簿模型,逐笔成交扣减对应档位,快照用于定期校准。回放速度控制模块根据逻辑时间推进,决定每条记录之间的等待间隔。
- 加载数据并按 exchange_time + seq 排序。
- 初始化订单簿,设置初始快照作为基准。
- 逐条处理:委托入簿、成交撮合、撤单移除。
- 每收到快照,与本地订单簿比对并修正偏差。
- 按回放倍速计算 sleep 或事件驱动推进。
四、常见误区与复盘要点
第一个误区是把 K 线当作回放主数据。K 线是聚合结果,丢失了盘中路径,用它回放会掩盖滑点和排队细节。第二个误区是忽略逐笔委托的撤单记录,导致订单簿越回放越厚。第三个误区是直接用本地接收时间排序,网络乱序会让成交出现在委托之前。第四个误区是时间戳单位混用,毫秒和微秒混在一起会造成速度控制数量级错误。