1. 课程导入与场景锚定
在量化交易、交易所运维和券商自营的技术团队里,逐笔回放(tick-by-tick replay)是一项高频使用的基础能力。它把历史行情按原始逐笔成交、逐笔委托、快照等消息的时间顺序重新“播放”一遍,让策略、撮合、风控、监控等系统在接近真实的时间序列上重新运行。与直接读取历史K线或日频数据不同,逐笔回放保留了每一笔委托的到达顺序、价格档位变化和成交明细,因此能复现出盘中那些稍纵即逝的细节。
但逐笔数据量极大。以沪深A股为例,一只活跃股票一天的逐笔委托与逐笔成交合计可达数十万条,全市场一天的消息量在亿级;期货主力合约在夜盘与日盘叠加时,单合约单日十万级消息也很常见。如果按真实时间 1:1 回放,复盘一个交易日的异常往往要花掉一整个交易日。于是“倍速控制”和“断点跳转”成为回放系统里两个最直接、也最影响效率的功能:前者决定你多快看完一段行情,后者决定你能否直接落到关心的那一刻。
三类典型场景,三种不同的诉求
交易复盘关注的是“当时为什么做出这个决策”。研究员通常需要从开盘集合竞价看到收盘,重点观察某几笔大单出现前后的盘口变化。此时倍速控制的价值在于:平淡时段用 8 倍甚至 16 倍快速掠过,临近关键时点切回 1 倍逐笔细看。断点跳转则用于直接定位到某笔成交的时间戳,例如“10:23:41.500 那笔 5000 手买单前后 30 秒”。
策略验证关注的是“策略在完整历史序列上的表现是否可复现”。回测引擎本身是事件驱动的,但人工抽检某段行情时,需要把回放速度调到接近实盘节奏,观察信号触发、下单、成交回报的时序是否合理。这里倍速不能随意拉高,因为部分策略逻辑依赖消息间隔;断点跳转则用于反复回到同一段行情做参数对比。
异常排查关注的是“某个错误发生在哪一毫秒、由哪条消息触发”。例如撮合引擎在 14:57 集合竞价阶段出现委托价格越界,工程师需要精确跳到该时间点,以 0.5 倍甚至逐条单步的方式查看消息流。此时断点跳转的精度(能否按时间戳、按消息序号、按业务主键定位)比倍速更重要。
倍速控制:不只是“快”,而是节奏可控
倍速控制的本质,是把消息时间戳之间的真实间隔按比例压缩或拉伸。设相邻两条消息的真实时间差为 Δt,倍速为 s,则回放时实际等待时间为 Δt / s。s=8 时,原本 1 秒的间隔变成 125 毫秒;s=0.5 时则拉长到 2 秒。看似简单,但工程上有三个必须处理的问题。
第一是时间基准。逐笔消息的时间戳可能来自交易所撮合时钟,也可能来自本地接收时钟,两者存在毫秒级偏差。回放系统应以消息自带的时间戳为准,而不是本地接收时间,否则倍速放大后偏差会被同步放大。第二是高倍速下的消息堆积。s=16 时,若某秒内有 2000 条消息,回放线程每秒要处理 32000 条,消费端(策略、撮合模拟器)可能来不及处理。常见做法是设置“最大消息速率”上限,超过时自动降速或丢弃非关键消息并记录告警。第三是倍速切换的平滑性。从 8× 切到 1× 时,若直接改参数,正在等待的那条消息可能被延迟很久;正确做法是在下一条消息到达时生效,并重置等待计时器。
一个可参考的实现片段如下(示例参数,非实测):
// 回放调度核心(伪代码,示例)
double speed = 8.0; // 当前倍速
long lastTs = -1; // 上一条消息时间戳(微秒)
long maxMsgPerSec = 50000; // 消费端吞吐上限
void onMessage(TickMsg m) {
if (lastTs >= 0) {
long gapUs = m.ts - lastTs; // 真实间隔
long waitUs = (long)(gapUs / speed); // 按倍速缩放
if (waitUs > 0) sleepMicros(waitUs);
}
lastTs = m.ts;
dispatch(m); // 交给策略/撮合/监控
}
注意 speed 的修改应放在消息处理之外,由控制线程写入一个 volatile 变量,调度线程在每条消息前读取,这样切换倍速不会打断当前等待。
断点跳转:定位精度与状态重建
断点跳转要回答两个问题:跳到哪和跳过去之后系统状态对不对。定位方式通常有三种:按时间戳(如 10:23:41.500)、按全局消息序号(第 1,234,567 条)、按业务主键(某笔委托号或成交编号)。时间戳定位最直观,但同一毫秒内可能有多条消息,需要约定“跳到该时间戳的第一条”还是“最后一条”;消息序号定位最精确,适合排查;业务主键定位最贴近业务语义,但需要建立索引。
更关键的是状态重建。逐笔回放不是无状态地读文件——策略持仓、订单簿、风控额度都依赖此前所有消息的累积。如果直接从第 100 万条开始播,订单簿是空的,策略会做出错误判断。因此断点跳转通常有两种模式:快速重建(从当日开盘或最近快照开始,以最高倍速静默重放到断点,不触发业务回调,只更新内部状态)和快照加载(若系统定期保存订单簿与持仓快照,直接加载断点前最近的快照,再补放少量消息)。前者实现简单但耗时,后者快但依赖快照机制的完整性。
本课程的任务地图
围绕这两个能力,本课程将沿着一条从“看懂”到“做对”的路径展开。先建立逐笔消息模型与时间轴的基本认知,理解委托、成交、快照三类消息的字段与顺序关系;再分别深入倍速控制的调度算法、速率上限、切换语义,以及断点跳转的定位索引、状态重建与快照配合;随后进入工程实现,包括回放引擎的线程模型、背压处理、与策略/撮合/监控系统的对接方式;最后通过交易复盘、策略验证、异常排查三个综合案例,把倍速与跳转组合成可复用的操作套路,并给出常见故障(时间戳乱序、跳转后状态漂移、高倍速丢消息)的排查思路。