2. 金融数据基础
在构建实时回放与补数链路之前,必须先厘清“数据到底长什么样”。金融数据并非单一表格,而是由行情、交易、订单、成交等多条数据流交织而成。它们各自携带不同的时间戳、序列号,并以快照或增量的形式到达。理解这些基础概念,是后续设计回放引擎、补数策略和一致性校验的前提。
一、核心数据类型:四条并行的数据流
金融实时链路中,最常处理的数据类型可以归纳为四类,它们描述的市场视角完全不同:
- 行情数据(Market Data):描述市场报价状态,包括买一卖一、盘口深度、最新价、成交量、涨跌幅等。行情又分为 Level-1(最优一档)和 Level-2(多档盘口、逐笔委托/成交)。
- 订单数据(Order):描述“委托意图”,即某账户在某个时刻提交的买卖指令,包含订单号、方向、价格、数量、订单类型、状态(已报、部成、全成、撤单)。
- 成交数据(Trade):描述“实际发生的撮合结果”,包含成交编号、成交价、成交量、买卖双方订单号、成交时间。成交是订单状态迁移的触发事件。
- 交易数据(Transaction/Account):描述账户维度的资金与持仓变动,如出入金、冻结、解冻、持仓更新,通常由成交和结算驱动。
这四类数据在回放时往往需要按时间轴对齐。例如,一笔成交会同时改变订单状态、账户持仓和行情最新价。若只回放行情而忽略订单与成交,就无法还原真实的撮合过程。
二、时间戳:三种时间语义不可混用
时间戳是金融数据最容易被低估的字段。同一条数据通常携带多个时间戳,含义截然不同:
| 时间戳类型 | 含义 | 典型用途 |
|---|---|---|
| 事件时间(Event Time) | 交易所或撮合引擎产生该事件的时间 | 回放排序、行情还原、延迟分析 |
| 接收时间(Ingest Time) | 本地网关或采集程序收到数据的时间 | 链路延迟监控、补数边界判断 |
| 处理时间(Process Time) | 下游计算引擎实际处理该数据的时间 | 流式计算窗口、状态更新 |
回放链路必须明确以哪个时间戳作为排序基准。通常以事件时间为主,接收时间用于判断数据是否迟到或乱序。若混用,会导致回放出的行情与真实市场顺序不一致。
三、序列号:判断连续性与丢包的标尺
序列号是数据源为每条消息分配的单调递增编号。它的核心价值在于:通过序列号是否连续,可以判断是否丢包、重复或乱序。
不同数据源的序列号粒度不同:
- 交易所逐笔行情:通常按频道(Channel)或合约维护独立序列号。
- 订单/成交流:按账户或按市场维护序列号。
- 快照流:每个快照携带一个版本号或序列号区间。
# 序列号连续性检查示例
last_seq = 1001
for msg in stream:
if msg.seq == last_seq + 1:
process(msg)
elif msg.seq <= last_seq:
log.warn("duplicate or out-of-order", seq=msg.seq)
else:
log.error("gap detected", expect=last_seq+1, got=msg.seq)
trigger_backfill(last_seq+1, msg.seq-1)
last_seq = msg.seq
补数链路的核心触发条件之一,就是序列号出现空洞。发现 gap 后,需要向数据源或历史库请求缺失区间,再按事件时间插入回放队列。
四、快照与增量:两种数据到达形态
快照(Snapshot)是某一时刻的完整状态切片,增量(Increment/Delta)是相对上一状态的变化量。二者在回放与补数中的处理方式完全不同。
增量:只包含变化字段,体积小、频率高,必须依赖前序状态才能正确应用。
典型场景中,行情系统会先推送一次快照,随后持续推送增量。回放引擎需要维护一个“当前状态”,按序列号顺序应用增量。若中间丢失增量,状态就会漂移,必须通过下一次快照或补数来修复。
五、案例:一笔成交如何贯穿四类数据
假设账户 A 在 10:00:00.123 提交限价买单,订单号 O1001,价格 10.50,数量 1000。交易所撮合后产生成交 T2001,成交价 10.50,数量 500。这条链路会同时影响多个数据流:
- 订单流:O1001 状态从“已报”变为“部成”,剩余 500。
- 成交流:新增 T2001,携带买卖双方订单号、成交价量、事件时间。
- 行情流:最新价更新为 10.50,成交量增加 500,盘口买一数量减少。
- 交易流:账户 A 持仓增加 500,冻结资金相应扣减。
回放时,若只按行情流回放,会看到价格变化但无法解释原因;若只按订单流回放,又缺少市场背景。正确做法是以事件时间为主轴,将四类数据合并排序,按序列号校验连续性,再逐条应用到状态机。
六、误区与复盘
- 误区一:把接收时间当事件时间。补数批量导入时,接收时间集中且乱序,回放结果与真实市场顺序不符。复盘时应统一以事件时间排序,接收时间仅用于延迟统计。
- 误区二:忽略序列号粒度。不同频道、不同合约的序列号独立递增,若用全局序列号判断连续性,会误报大量空洞。应按数据源和频道分别维护水位线。
- 误区三:增量丢失后继续应用后续增量。状态会持续漂移,且无法通过后续增量自愈。正确做法是立即触发补数或等待下一次快照校准。
- 误区四:快照与增量混用同一套解析逻辑。快照可直接覆盖状态,增量必须依赖前序状态。解析层应明确区分消息类型,避免将快照当增量应用导致字段缺失。
掌握这些基础概念后,后续设计回放链路时就能明确:以事件时间排序、以序列号检测空洞、以快照校准状态、以增量驱动更新。这四条原则贯穿实时回放与补数的全过程,也是判断链路是否可靠的核心标尺。