4、数据解析与标准化:行情数据格式解析、字段映射与标准化、异常数据处理

行情数据从交易所出来,到我们系统里落地,中间这一步非常关键。说白了,就是要把各家交易所的「方言」翻译成我们自己的「普通话」。我见过不少团队,在这上面栽过跟头。今天咱们就聊聊,怎么把这步做扎实。

4.1 行情数据格式解析

各家交易所的行情格式,五花八门。有的用二进制,有的用JSON,还有的用自定义的文本协议。我个人习惯,先把所有格式统一成内部结构体,再往下走。

举个例子,深交所的Level-2行情,用的是二进制定长格式。每个字段在哪个位置、占多少字节,都有严格规定。我早期做这个的时候,就吃过字节序的亏——大端小端没搞对,解析出来的价格直接翻了几百倍。

核心原则: 解析层只做格式转换,不做业务判断。拿到原始字节流,转成结构体,就完事。

常见的行情格式有这几种:

  • 二进制定长:速度快,解析简单。但扩展性差,加个字段就得改协议。
  • 二进制变长:灵活,但解析复杂。需要处理消息头和消息体的长度关系。
  • JSON/Protobuf:可读性好,但性能开销大。适合低频行情或内部传输。
  • 自定义文本协议:比如某些期货公司的FIX协议变种。解析起来最头疼。

嗯,这里要注意:解析层一定要做校验。我见过一个案例,交易所发了一条异常的消息,长度字段写错了,结果解析器直接崩了。所以,长度校验、校验和校验、字段范围校验,一个都不能少。

4.2 字段映射与标准化

解析完原始数据,下一步就是映射。你想想看,上交所的「最新价」字段名叫 LastPx,深交所叫 last_price,到了期货那边又成了 LastPrice。如果不做标准化,下游的每个应用都得适配一遍,那不乱套了?

我建议的做法是:定义一套内部标准字段字典。所有行情数据,进来之后都映射成这套字典里的字段。比如:

内部字段名 含义 类型 示例值
symbol 合约代码 string "000001.SZ"
last_px 最新价 int64 123450(单位:分)
volume 成交量 int64 1000000
timestamp 时间戳(纳秒) int64 1700000000000000000

这里有个坑:精度问题。有些交易所的价格是浮点数,有些是整数。我建议统一用整数,比如价格用「分」为单位,时间用纳秒。这样既避免了浮点精度丢失,又方便做计算。

小技巧: 字段映射可以用配置文件来管理。比如写一个YAML文件,定义每个交易所的字段到内部字段的映射关系。这样新增一个交易所,改配置就行,不用改代码。

4.3 异常数据处理

行情数据里,异常数据是家常便饭。我曾经遇到过:价格突然变成0、时间戳倒流、成交量爆增几百倍……这些数据如果不处理,下游的策略模型直接跑飞。

异常数据分几类:

  • 格式异常:字段缺失、类型不对、长度超限。
  • 业务异常:价格超出涨跌停板、成交量异常大、买卖价差为负。
  • 时序异常:时间戳乱序、重复数据、数据跳变。

我的处理策略是「分层过滤」:

  1. 第一层:格式校验。在解析阶段就干掉格式不对的数据。比如长度不对、校验和不通过,直接丢弃。
  2. 第二层:业务规则校验。比如价格不能为负,成交量不能超过某个阈值。这些规则可以配置,不同品种不一样。
  3. 第三层:时序校验。检查时间戳是否递增,数据是否连续。如果发现跳变,可以标记为「可疑」,但不一定丢弃。
注意: 异常数据不要直接丢弃就完事了。一定要记录日志,并且发送告警。我曾经因为没告警,一个异常数据在系统里跑了三天,最后发现是交易所的测试数据没切干净。

举个例子,代码里可以这样处理:

def normalize_tick(raw_tick):
    # 格式校验
    if not validate_format(raw_tick):
        log_error("格式异常", raw_tick)
        return None
    
    # 字段映射
    internal_tick = map_fields(raw_tick)
    
    # 业务规则校验
    if internal_tick.last_px <= 0:
        log_error("价格异常", internal_tick)
        return None
    
    if internal_tick.volume < 0 or internal_tick.volume > MAX_VOLUME:
        log_error("成交量异常", internal_tick)
        return None
    
    # 时序校验
    if internal_tick.timestamp < last_timestamp:
        log_warning("时间戳乱序", internal_tick)
        # 这里可以选择丢弃,也可以选择修正
    
    return internal_tick

你看,这样一层层下来,能过滤掉大部分异常。但说实话,总有一些边界情况是规则覆盖不到的。所以,我还会加一个统计异常检测。比如,实时计算过去5分钟的价格均值和标准差,如果当前价格偏离超过3个标准差,就触发告警。

总结一下: 数据解析与标准化,核心就三件事——格式解析、字段映射、异常处理。每一步都要做校验,每一步都要留日志。别嫌麻烦,这些工作做好了,下游的兄弟能省不少心。

最后说一句,标准化字段字典一定要版本管理。我见过一个团队,改了字段名没通知所有人,结果下游的数据库直接写崩了。用Git管理字典文件,每次变更都走评审,这是基本操作。


专注资料整理