3. 历史回放环境搭建

涨停盘口的历史回放,本质上不是“把K线重新播一遍”,而是把当日从集合竞价到收盘的每一笔成交、每一次委托队列变化,按真实时间顺序重新驱动一遍。要做到这一点,回放环境必须解决三个问题:数据以什么粒度存储、引擎以什么节奏推进、时间轴如何被精确控制。本节围绕这三个问题展开,给出可落地的搭建思路与一致性验证方法。

一、回放引擎的数据底座:逐笔快照与增量事件

逐笔级回放的最小数据单元通常由两类记录构成。第一类是快照记录,按固定频率(例如每3秒)记录十档买卖盘的价格、数量、委托笔数,以及最新价、成交量、成交额、涨跌停价等字段;第二类是逐笔事件,包括逐笔成交(时间、价格、数量、买卖方向)和逐笔委托(时间、价格、数量、方向、委托类型)。快照负责“还原某一时刻的盘口全貌”,逐笔事件负责“还原两次快照之间的变化过程”。

一个常见的误区是只存快照。仅靠3秒快照回放,封板瞬间的排队变化、大单撤单、板上反复开合都会被抹平,涨停盘口研究恰恰最依赖这些细节。因此回放引擎应以逐笔事件为主时间线,快照作为对齐锚点。建议的数据组织方式如下:

replay_data/
  ├─ 600XXX_20240612_snap.parquet   # 快照:ts, last, bid1~bid10, ask1~ask10, vol, amt
  ├─ 600XXX_20240612_trade.parquet  # 逐笔成交:ts, price, qty, side, seq
  └─ 600XXX_20240612_order.parquet  # 逐笔委托:ts, price, qty, side, type, seq

三张表统一使用毫秒级时间戳,并保留交易所原始序号 seq。序号非常关键:同一毫秒内可能有多笔事件,仅靠时间戳排序会产生歧义,必须用 seq 作为第二排序键,才能保证回放顺序与原始行情完全一致。

二、时间轴控制:按秒、按笔、暂停、跳转与倍速

回放引擎的核心是一个“虚拟时钟”。它不依赖真实系统时间,而是维护一个当前回放时间戳 cursor_ts,所有推进操作都围绕它展开。按秒推进时,引擎把 cursor_ts 向前移动固定步长(如1000ms),并吐出该区间内的全部事件;按笔推进时,引擎只前进到下一笔事件的时间戳,保证“一笔一停”,适合逐笔观察封单变化。

倍速播放的实现要点是:虚拟时钟的推进速度 = 真实流逝时间 × 倍速系数。例如2倍速下,真实经过500ms,cursor_ts 前进1000ms。但要注意,倍速只改变“播放节奏”,不改变“事件顺序”,引擎内部仍必须严格按 (ts, seq) 排序消费事件,否则高速播放时会出现成交先于委托的错乱。

跳转则需要索引支持。建议在加载阶段为事件表建立时间戳索引(如按秒分桶),跳转时先二分定位到目标时间所在的桶,再从桶内第一条事件开始回放。跳转到某一时刻后,盘口状态不能靠“重放全部历史”得到,而应直接加载该时刻之前最近的一条快照作为初始状态,再叠加快照之后到目标时刻之间的逐笔事件,这样跳转才能做到秒级响应。

暂停与单步应作为独立状态处理:暂停时虚拟时钟停止但状态保留;单步(按笔)时每次只消费一条事件并立即刷新盘口视图,便于观察封单被吃掉一笔后的队列变化。

三、回放状态机与推进逻辑

下图给出回放引擎的状态与推进关系:加载数据后进入就绪态,播放时虚拟时钟按倍速推进,暂停、单步、跳转分别对应不同的状态迁移,而每次推进都从事件流中按序取事件并更新盘口状态。

逐笔回放引擎状态与时间轴推进 数据加载 快照 + 逐笔成交 + 逐笔委托 按 (ts, seq) 排序 就绪 cursor_ts 已定位 播放中 虚拟时钟 × 倍速 暂停 状态保留,时钟停 按笔单步 消费 1 条事件 跳转 快照锚点 + 增量 盘口状态更新 十档队列 / 最新价 / 成交量 / 封单量 暂停 单步 跳转 状态回写

四、一致性验证:回放数据与原始快照对齐

环境搭好后,最关键的一步是验证“回放出来的盘口”与“原始快照”是否一致。做法是:选取若干校验时刻(例如每30秒一个点),在回放引擎中跳转到该时刻,读取引擎重建的十档买卖盘、最新价、累计成交量,与原始快照表中同一时间戳的记录逐字段比对。比对容差应设为0(价格、数量为整数或定点数,不应有浮点误差),任何字段不一致都说明事件消费或排序有问题。

常见的不一致来源有三类。其一是时间戳对齐偏差:快照时间戳与逐笔事件时间戳若来自不同时钟源,可能出现几十毫秒错位,导致跳转后多算或少算几笔成交。解决办法是统一以交易所时间戳为准,并在快照与事件之间做序号对齐。其二是撤单事件缺失:若逐笔委托表未包含撤单类型,重建的委托队列会只增不减,封单量虚高。其三是集合竞价处理不当:开盘前的委托在9:25集中撮合,若引擎把竞价期间的委托逐笔叠加,会得到错误的开盘盘口,应以开盘快照为起点。

一致性校验不要只抽查收盘附近。涨停盘口的关键时刻往往在封板瞬间和开板瞬间,应把校验点设在涨跌停价首次触及、封单量突变、板上成交放量等位置,这些位置最容易暴露事件顺序错误。

五、一个可执行的校验清单

把上述要点固化为可重复执行的检查项,能显著降低环境搭建的返工成本。以下清单可直接用于每次数据更新后的回归验证:

检查项方法通过标准
事件排序正确检查 (ts, seq) 是否严格递增无重复、无逆序
快照对齐跳转至校验时刻,比对十档与最新价逐字段完全一致
成交量累计回放累计成交量 vs 快照 vol差值 = 0
封单量还原封板时刻买一量 vs 快照 bid1 量差值 = 0
倍速不改序1倍速与8倍速回放同一区间事件序列完全相同
跳转可复现同一时刻跳转两次盘口状态一致

完成以上搭建与验证后,回放环境才具备“可信”的前提:时间轴能按秒、按笔精确推进,暂停与跳转不破坏状态,倍速不改变事件顺序,回放结果与原始快照逐字段吻合。后续的涨停盘口事件标注,才有可靠的数据基础可以依赖。