2.2 行情数据类型与处理管线 某天开盘三分钟后,一家机构的策略账户突然连续小幅亏损。事后排查,策略逻辑没改、参数没动,罪魁是行情侧的一个静默故障:增量通道在十几秒前丢了一段报文,订单簿重建器带着错位的状态继续运转,策略看到的是一个不存在于交易所的虚拟盘口。这个事故的教训是本节的开场白:在高频交易里,行情管线不是数据来源,而是系统状态的唯一合法来源——管线错一帧,决策全错。上一节我们拆解了订单簿这个目标物,本节讲如何把交易所发出的原始报文流,逐帧还原成内存里的那份订单簿。 本节目标 区分 Level 1 快照、Level 2 深度、逐笔委托与逐笔成交四档数据颗粒度,说明各自的带宽与信息含量; 理解增量报文加序列号的通道设计,解释为什么要用快照对齐做状态修复;
某天开盘三分钟后,一家机构的策略账户突然连续小幅亏损。事后排查,策略逻辑没改、参数没动,罪魁是行情侧的一个静默故障:增量通道在十几秒前丢了一段报文,订单簿重建器带着错位的状态继续运转,策略看到的是一个不存在于交易所的虚拟盘口。这个事故的教训是本节的开场白:在高频交易里,行情管线不是数据来源,而是系统状态的唯一合法来源——管线错一帧,决策全错。上一节我们拆解了订单簿这个目标物,本节讲如何把交易所发出的原始报文流,逐帧还原成内存里的那份订单簿。
交易所提供的行情是分档出售的商品,颗粒度从粗到细大致四级。Level 1 是最优买卖价加总量,一秒数帧,够看个方向。Level 2 展开若干档深度,仍是周期性快照。逐笔委托流(order by order)把每一笔挂单、撤单的报文原样发出,带宽大一到两个数量级,但信息完整——你能在本地重建出与交易所一致的订单簿。逐笔成交流单独披露成交明细,与逐笔委托配合才能区分"新单成交"与"老单成交"。
| 颗粒度 | 内容 | 典型频率 | 本地可重建订单簿 | 主要用途 |
|---|---|---|---|---|
| Level 1 | 最优买卖价与量 | 每秒数帧 | 否 | 趋势观察、低频研究 |
| Level 2 | 多档深度快照 | 每秒数十帧 | 近似(有误差) | 盘口监控、一般算法交易 |
| 逐笔委托 | 每笔挂撤明细 | 每秒可达十万级 | 是 | 高频做市、微观结构研究 |
| 逐笔成交 | 每笔成交明细 | 与成交活跃度相关 | 配合委托流使用 | 成交归因、滑点分析 |
选择颗粒度本质是在带宽、成本与信息含量之间做交换。做市策略必须用逐笔委托流——它要估算队列位置,快照粒度的深度是算不出来的;跨市场套利用 Level 2 有时够用,因为套利信号来自价差而非队列细节。初学者常犯的错是"越大越好"地买最细的数据,却发现管线吞吐跟不上、存储成本失控。正确的顺序是:先定策略需要回答什么问题,再选能回答它的最小颗粒度。
一条生产级的行情管线分四段。接收段用内核旁路技术把报文直接递到用户态,避免协议栈的微秒级开销(第四章详述)。解码段把交易所的二进制格式拆成字段——这一段是纯 CPU 工作,优化空间在于零拷贝与分支预测友好。重建段维护订单簿状态,处理增量、检测序列号断点。分发段把更新后的状态以单写多读的方式广播给策略与风控线程。四段之间用无锁队列衔接,全链路不落盘、不跨机。
重建段是逻辑最密的地方。交易所的增量通道带序列号,正常情况下严格递增;一旦出现跳号,说明丢包或通道切换,此时本地状态已不可信,必须用快照重新对齐。骨架如下:
def on_packet(seq, payload): if seq == expect_seq: book.apply(decode(payload)) # 正常路径:顺序增量 expect_seq = seq + 1 elif seq > expect_seq: gap = seq - expect_seq metrics.gap_total += gap # 断点:先记账,再修复 request_snapshot(channel_a) # 向任一快照通道要新基线 state = RECOVERY # 进入修复态,暂停对外发布 else: return # 迟到报文:丢弃并计数 if state == RECOVERY and snapshot_ready(book): verify_against_snapshot(book) # 对齐校验通过才恢复发布 state = NORMAL
三个工程细节值得画重点。其一,修复态必须暂停对外发布:带着错误状态给策略供数,比不给数更危险。其二,迟到报文直接丢弃:逐笔流里补发旧报文的情形极少,回放式修复的成本远高于等下一次快照。其三,双通道冗余要防"脑裂":主备两条增量通道各自维护序列号,切换时要校验两边最后处理的序列号,防止用备通道的旧数据覆盖主通道的新状态。

行情报文上通常带有两个时间:交易所引擎的撮合时间戳与发送时间戳;报文进入你的网卡时,本机再打第三个接收时间戳。三者之差就是纯传输延迟,是链路监控最有价值的信号——接收时间戳减去发送时间戳的分布一旦漂移,往往意味着线路拥塞或路径切换,比任何报警都早。做归因分析时,时间戳语义不统一是常见陷阱:用本地接收时间对比另一家交易所的引擎时间,会人为制造出一段不存在的"领先"。纪律只有一条:跨市场比较必须用同源时间戳,本节先立此存照,第四章讲时钟同步时再展开。
历史数据侧同样有坑。回测用数据要过四道筛:序列号连续性校验(补齐断点或明确剔除时段)、重复报文去重(通道切换时的双发)、乱序纠正(按引擎时间重排)、集合竞价与临时停牌时段的特殊处理。漏掉任何一道,回测都会在真实盘口不存在的状态上训练策略,产生看似稳健实则脆弱的假信号。
背景:某团队上线跨市场套利策略前,按规程做故障注入演练:在生产管线的测试实例里随机丢弃增量报文,观察系统的自愈表现。
操作:演练脚本每分钟随机丢一小段报文。头两个场景,重建器正确进入修复态、拉快照对齐、恢复发布,全程不到一秒,符合设计。第三个场景暴露了问题:丢包恰好发生在通道切换窗口,主备通道的序列号校验逻辑用了各自的计数基准,系统把备通道的旧状态当成了新状态,恢复发布的是错位盘口。
结果:修复方案是给每个市场维护全局单调的处理水位,通道切换时以水位校验,落后即视为断点重走修复流程。复跑全部演练场景,自愈行为一致。
解读:这个案例的通用结论是:故障注入是行情管线的必要验收项,光靠代码评审发现不了多通道交互的边角。管线正确性不是"正常时跑对",而是"任何单点故障后能在可预期时间内回到正确"。
变式:演练强度应随策略敏感度分级:做市策略对状态错误零容忍,演练要覆盖连续多次丢包与快照通道同时故障的极端组合;套利策略若设有盘口一致性校验闸门,可以放宽到常规场景。演练矩阵与第七章的风控分级共用同一张表。