2. 盘口数据获取与清洗实战

上一节我们建立了“盘口流动性随时段变化”的分析框架,但真正动手时,第一个拦路虎往往不是分析模型,而是数据本身。交易所Level-2行情包含逐笔委托、逐笔成交和快照三类核心数据,它们的时间戳精度、字段含义、更新节奏各不相同。如果直接拿原始报文做分时段统计,很容易得出“开盘流动性最好”这类与直觉相反的结论——原因往往不是市场,而是数据没有对齐、没有清洗、没有重建。本节我们以一条完整的处理链路,把原始Level-2数据加工成可用于分时段流动性分析的标准化数据集。

一、先看清三类数据的字段与语义差异

逐笔委托(Order)记录每一笔委托的进入、撤单与成交回报,关键字段通常包括:证券代码、委托时间(精确到毫秒或微秒)、委托序号、买卖方向、委托价格、委托数量、委托类型。逐笔成交(Trade)记录撮合结果,关键字段包括成交时间、成交序号、成交价格、成交数量、买卖方向(部分交易所提供主动方标识)。快照(Snapshot)则是按固定频率(如每3秒)对盘口十档或更多档位的切片,包含买一至买十价量、卖一至卖十价量、最新价、累计成交量等。

三者的语义差异决定了清洗策略不同:逐笔委托是“意图流”,逐笔成交是“结果流”,快照是“状态流”。分时段流动性分析通常需要从意图流和结果流重建出高频快照,再与交易所快照交叉校验。这里有一个关键约定必须提前明确:本文所有时间均以交易所撮合时钟为基准,时间戳统一转换为“当日零点起的毫秒数”,后续对齐、分桶、重建都基于这一参照系,避免跨时区或跨字段口径混乱。

二、字段解析:把二进制报文变成结构化表

交易所原始数据多为二进制或定长报文,第一步是按协议文档解析。以逐笔委托为例,解析后的目标结构应至少保留:symbol, ts_ms, seq, side, price, qty, order_type。解析时有两个易错点:一是价格字段常以“分”或“厘”为单位的整数存储,需除以对应倍数还原为元;二是数量字段可能以“股”或“手”为单位,必须与后续流动性指标口径一致。建议在解析阶段就统一为“元”和“股”,并在数据字典中固定下来。

# 示例:逐笔委托解析后的标准化字段(示例数据,非实测)
symbol  ts_ms       seq    side  price  qty   order_type
600000  34200000    1001   B     10.52  300   LIMIT
600000  34200120    1002   S     10.53  500   LIMIT
600000  34200235    1003   B     10.52  200   CANCEL

注意最后一行的CANCEL类型:撤单在逐笔委托中同样占一条记录,若把它当作新增委托计入委托量,会系统性高估盘口深度。解析阶段必须保留order_type,为后续过滤撤单提供依据。

三、时间对齐:让三条流落在同一时间轴上

逐笔委托和逐笔成交的时间戳来自同一撮合时钟,通常可直接比较;但快照的时间戳是“切片时刻”,与逐笔事件存在天然错位。对齐的核心原则是:以事件时间为准,快照只作为校验基准,不参与重建。具体做法是先将逐笔委托与逐笔成交按ts_ms升序合并,同一毫秒内按seq排序,形成一条统一的事件序列。对于跨日数据,还需处理“夜盘归属”问题:若交易所将夜盘计入下一交易日,则需按交易所规则调整trade_date,否则分时段统计会把夜盘错误归入前一日。

另一个常见问题是时间戳重复。同一毫秒内可能有多笔委托和成交,此时不能简单去重,而应保留全部记录并依靠seq维持顺序。若发现seq回退或跳变,说明数据存在丢包或乱序,需要标记该时间段为“不完整区间”,在后续分时段统计中降权或剔除。

四、异常值处理:识别并隔离而非直接删除

Level-2数据中的异常主要有四类:价格越界(如超出当日涨跌停板)、数量为负或为零、时间戳倒挂、以及明显的“胖手指”委托(如价格偏离最新价数十个百分点)。处理原则是先标记、后隔离、再复核,而不是一删了之。建议增加is_valid与invalid_reason两个字段,保留原始记录以便追溯。

  • 价格越界:与当日涨跌停价比较,超出即标记PRICE_OUT_OF_BAND。
  • 数量异常:qty <= 0标记QTY_INVALID;单笔数量超过该股当日平均单笔量50倍标记QTY_OUTLIER,仅隔离不删除。
  • 时间倒挂:ts_ms小于前一条记录且seq也回退,标记TS_REVERSED。
  • 撤单无对应委托:撤单记录的seq找不到原始委托,标记ORPHAN_CANCEL,这类记录不能计入任何方向的委托量。
异常标记字段必须随数据集一同交付。分时段流动性分析中,若某时段异常记录占比超过5%,该时段的盘口深度指标应标注“低置信度”,避免把数据质量问题误读为市场行为变化。

五、快照重建:从事件流还原盘口状态

重建的目标是:在任意给定时刻t,根据此前所有有效委托和成交,还原出买十档与卖十档的价量。实现上维护两个有序结构(买盘按价格降序、卖盘按价格升序),逐条扫描事件序列:新增委托插入对应价位并累加数量;撤单从原价位扣减;成交则同时扣减被动方挂单量。每处理完一个事件,记录当前时刻的盘口状态;若需要固定频率快照(如每3秒),则在跨越时间桶边界时输出一次状态。

# 重建逻辑伪代码(示例)
for event in sorted_events:
    if event.type == 'ORDER' and event.side == 'B':
        bid_book[event.price] += event.qty
    elif event.type == 'CANCEL':
        book = bid_book if event.side == 'B' else ask_book
        book[event.price] -= event.qty
    elif event.type == 'TRADE':
        passive = ask_book if event.aggressor == 'B' else bid_book
        passive[event.price] -= event.qty
    if crossed_bucket(event.ts_ms, last_bucket):
        emit_snapshot(bid_book, ask_book, event.ts_ms)

重建完成后,必须与交易所快照做交叉校验:在相同时间点比较买一价、卖一价和十档总量,若偏差超过预设阈值(例如买一价不一致,或十档总量偏差超过2%),说明事件流存在缺失或解析错误,应回到解析阶段排查。校验通过的数据集,才具备分时段流动性分析的可信度。

六、产出标准化数据集与质量报告

最终交付的数据集建议按“事件表+重建快照表+质量报告”三件套组织。事件表保留全部字段与异常标记;重建快照表按固定频率输出,字段包括ts_ms, bid1~bid10, ask1~ask10, bid_qty_sum, ask_qty_sum, spread;质量报告则汇总各时段的记录数、异常占比、与交易所快照的校验偏差。下面这张图概括了从原始报文到可用数据集的完整处理链路与关键判断点。

Level-2 数据清洗与快照重建链路 逐笔委托 Order 报文 逐笔成交 Trade 报文 交易所快照 校验基准 字段解析 价格:分→元 数量:手→股 保留 order_type 时间对齐 统一毫秒时间轴 ts_ms + seq 排序 夜盘归属校正 异常标记 价格越界 数量异常 时间倒挂 / 孤儿撤单 快照重建 维护买卖十档 逐事件更新价量 按时间桶输出 交叉校验 买一/卖一价比对 十档总量偏差≤2% 不通过则回查 有效事件流 校验基准 标准化数据集:事件表 + 重建快照表 + 质量报告 可直接用于分时段流动性分析