2. 行情数据源与采集链路剖析

做行情数据质量核验,第一步不是写校验规则,而是把"数据从哪来、经过谁、最后落到哪"这条链路画清楚。只有明确了每个环节的输入输出和可能的失真方式,核验才能有的放矢。本节围绕交易所数据源特征、采集传输落库各环节的异常引入点,以及核验介入点与血缘追踪方法展开。

一、四类数据源的字段特征与核验侧重

国内证券与期货交易所对外提供的行情数据,按粒度大致可分为四类,它们的字段结构、更新频率和异常表现差异很大,核验策略必须区别对待。

Level-1 快照是最基础的行情,通常每 3 秒推送一次,包含最新价、买卖五档或一档、成交量、成交额、持仓量等字段。它的特点是"离散采样",两次快照之间的价格跳动被压缩掉了,因此核验重点在于时间戳连续性、价格是否落在涨跌停区间内、成交量是否单调不减。

Level-2 快照在 Level-1 基础上扩展到十档买卖盘,并附带委托总量、加权平均价等衍生字段。档位增多意味着盘口内部一致性校验更复杂:买一价必须小于卖一价,买一到买十价格递减,卖一到卖十价格递增,任一档位价格为零或越界都说明数据异常。

逐笔委托记录每一笔新增委托的委托号、价格、数量和买卖方向,是重建订单簿的原始素材。它的核心约束是委托号唯一且递增、价格数量为正、撤单必须对应已有委托号。逐笔委托一旦出现委托号重复或跳号,后续订单簿重建就会错位。

逐笔成交记录每一笔撮合成交,包含成交价、成交量、买卖委托号。核验重点是成交价必须落在当时买一卖一之间、成交量不超过对应委托的剩余量、成交时间戳不早于委托时间戳。

四类数据源并非孤立存在:逐笔委托和逐笔成交可以聚合出快照,快照又可以反推成交活跃度。核验时利用这种"跨源一致性"往往比单源字段校验更能发现深层问题。

二、采集、传输、落库三环节的异常引入点

数据从交易所到本地数据库,通常经过采集、传输、落库三个环节,每个环节都有典型的异常模式。

采集环节的问题多与连接和解析有关。行情网关断线重连期间会丢失增量包,表现为时间戳出现空洞;二进制协议解析时字段偏移错位,会把价格解析成明显不合理的数值;多路行情源切换时若未做去重,同一笔成交可能被记录两次。采集端还应关注"心跳超时"——行情停止推送但连接未断,此时数据看似正常实则已经停滞。

传输环节的问题集中在乱序、重复和延迟。TCP 本身保证有序,但多线程消费或消息队列分区时可能打乱顺序;网络抖动会造成时间戳倒挂,即后到的包时间戳反而更早;跨机房传输的延迟尖峰会让某些快照"迟到",落库时间与交易所时间出现明显偏差。

落库环节的问题则偏向工程实现。批量写入时事务回滚导致部分数据丢失;字段类型定义过窄,价格用 float 存储产生精度误差;主键设计不当造成重复插入;时区处理不一致,把交易所的本地时间当成 UTC 存储,导致跨市场对齐时整体偏移八小时。

行情数据采集链路与异常引入点 交易所数据源 Level-1 快照 Level-2 快照 逐笔委托 逐笔成交 采集环节 网关连接 / 协议解析 多源切换 / 去重 心跳监测 异常:断线丢包 字段错位 / 重复 传输环节 消息队列 / 网络链路 多线程消费 跨机房转发 异常:乱序 / 重复 时间戳倒挂 / 延迟 落库环节 批量写入 / 事务 字段类型 / 主键 时区处理 异常:回滚丢失 / 精度 重复插入 / 时区偏移 核验介入点(三道闸门) ① 采集端:时间戳连续性、字段范围 ② 传输端:序号连续性、延迟阈值 ③ 落库端:主键唯一、跨源一致性 数据血缘追踪:以一笔异常成交反查 交易所原始包 seq=88231 采集日志 recv_ts / 网关ID 消息队列偏移 partition / offset 落库记录 batch_id / 入库时间

三、核验介入点的选择

把核验放在链路的哪个位置,直接决定了问题发现的及时性和定位成本。实践中建议设置三道闸门。

采集端闸门最靠近源头,适合做轻量、高频的实时校验:时间戳是否单调递增、价格是否在涨跌停范围内、委托号是否连续。这一层追求低延迟,规则要简单,命中即告警,避免拖慢采集线程。

传输端闸门关注"顺序与时效"。对每条消息携带的序列号做连续性检查,发现跳号立即标记;对落库时间与交易所时间做差值统计,超过设定阈值(例如 500 毫秒)的记为延迟异常。这一层通常以滑动窗口方式实现。

落库端闸门做的是"事后一致性"。主键唯一性检查、跨源聚合比对(用逐笔成交聚合出的分钟成交量与快照成交量对比)、跨市场时间对齐,都在这一层完成。它不追求实时,但覆盖面最广。

四、数据血缘追踪的落地方法

当核验发现一笔异常成交时,能否快速回答"它从哪个网关进来、经过哪个队列分区、落在哪批写入里",决定了排障效率。血缘追踪的关键是让每个环节都留下可关联的标识。

具体做法是:采集端为每条消息附加 gateway_id 和 recv_ts;传输端记录消息队列的 partition 与 offset;落库端记录 batch_id 和入库时间。同时保留交易所原始序列号 seq 作为贯穿全链路的业务主键。这样,给定一个异常 seq,就能沿"原始包 → 采集日志 → 队列偏移 → 落库批次"逐段定位。

-- 血缘反查示例:给定异常成交的交易所序列号
SELECT s.seq, s.exchange_ts, c.gateway_id, c.recv_ts,
       q.partition, q.offset, d.batch_id, d.load_ts
FROM   raw_tick s
JOIN   collect_log c ON c.seq = s.seq
JOIN   mq_offset  q ON q.seq = s.seq
JOIN   db_load    d ON d.seq = s.seq
WHERE  s.seq = 88231;

查询结果中,若 recv_ts 与 exchange_ts 差值异常大,问题在采集或网络;若 offset 出现跳变,问题在传输;若 load_ts 缺失而前三段齐全,问题在落库。这种"分段比对时间与标识"的思路,比盲目翻日志高效得多。

血缘字段本身也会增加存储与写入开销。建议对高频逐笔数据只保留关键标识(seq、gateway_id、batch_id),详细日志按天滚动清理,避免为了追踪而拖垮主链路性能。

总结来看,数据源特征决定了校验规则的内容,链路环节决定了异常的类型,而血缘标识决定了排障的速度。三者结合,才构成一套可落地的行情数据质量核验基础。