2. 逐笔事件类型解析
逐笔重建订单簿的第一步,不是写代码,而是把交易所推送的每一条事件“翻译”成对订单簿状态的确定性影响。同一条消息,字段读错一个,重建出的盘口就可能整体偏移。本节围绕新增订单、撤单、成交、订单修改四类核心事件,逐项拆解字段含义,并重点说明那些“看起来相似、语义却完全不同”的差异点。
一、四类事件的语义定位
可以把订单簿看作一个由订单ID索引的状态机,每类事件都是对状态机的一次操作:新增订单是插入一条新记录并占用对应价位的流动性;撤单是删除记录并释放流动性;成交是减少记录中的剩余数量,可能部分成交也可能全部成交;订单修改则是对已有记录的数量或价格做变更,往往等价于“先撤后增”。理解这四种操作的差异,是保证重建结果与交易所快照一致的前提。
需要特别强调的是:事件类型不能只看消息名称,而要看字段组合。例如某些行情源把“成交”和“撤单”合并为同一条“订单删除”消息,此时必须结合“是否有成交量字段”“剩余数量是否归零”来判断真实语义。
二、新增订单:字段与陷阱
新增订单事件通常携带 order_id、side(买/卖)、price、quantity、timestamp。重建时要做三件事:以 order_id 为键写入映射表;按 side 和 price 定位到对应价位;把 quantity 累加到该价位的总量上。这里最常见的错误是把“订单数量”当成“增量”——新增订单的数量是该订单的初始总量,不是相对上一状态的差值。
另一个陷阱是价格精度与方向约定。若交易所用整数表示价格(如以 0.01 元为单位),重建时必须统一换算,否则同一价位会被拆成多个档位。方向字段也要确认:有的源用 1/2 表示买卖,有的用 B/S,混用会导致买卖盘颠倒。
三、撤单:部分撤单与全量撤单
撤单事件至少包含 order_id 和撤单数量。若撤单数量等于该订单当前剩余量,则删除记录;若小于剩余量,则是部分撤单,必须保留记录并扣减数量。很多实现只处理“全撤”,遇到部分撤单直接删除订单,结果盘口深度偏薄。判断依据应写成:剩余量 -= 撤单量;若剩余量 == 0 则删除。
还要注意“撤单”与“成交导致的删除”在消息层面的区别:撤单没有成交价和对手方,成交则有。若行情源把两者合并为“订单删除”,就必须依赖成交量字段是否大于零来区分。
四、成交:扣减剩余量而非删除
成交事件的关键字段是 maker_order_id(被动方订单)、trade_price、trade_quantity。重建时只应扣减被动方订单的剩余量,主动方订单通常已通过新增/撤单事件体现,重复处理会导致数量被扣两次。若 trade_quantity 等于剩余量,订单归零并删除;否则保留并更新数量。
成交价与订单原价可能不同(价格改善),但订单簿中该订单仍挂在原价位,直到数量归零。这一点常被误解为“成交后订单立即移到成交价”,实际并非如此。
五、订单修改:最容易被低估的事件
订单修改事件携带 order_id 及新价格或新数量。语义上它等价于“撤销原订单 + 以新参数新增订单”。若只改数量而不改价格,订单在原价位保留但优先级可能重置;若改了价格,则必须从原价位移除、在新价位插入。实现上建议统一走“先按撤单处理,再按新增处理”的路径,避免遗漏价位聚合的更新。
六、字段语义对照速查
| 事件 | 核心字段 | 对订单簿的操作 | 常见误判 |
|---|---|---|---|
| 新增 | order_id、side、price、quantity | 插入记录,价位总量增加 | 把 quantity 当增量 |
| 撤单 | order_id、cancel_qty | 扣减剩余量,归零则删除 | 忽略部分撤单 |
| 成交 | maker_order_id、trade_qty、trade_price | 扣减被动方剩余量 | 重复扣减主动方 |
| 修改 | order_id、new_price、new_qty | 先撤后增,更新价位 | 当作成交处理 |
把上述四类事件的字段含义与操作语义固化为重建逻辑的分支判断,后续再叠加时间戳排序与快照校验,才能保证逐笔重建的订单簿在任意时刻都与交易所一致。