3. 数据格式与协议解析:Level2行情数据常用格式与协议结构

Level2行情数据在传输和存储过程中,为了兼顾效率、可读性和跨平台兼容性,主要采用三种数据格式:二进制、Protobuf 和 JSON。每种格式在协议结构上均遵循“消息头 + 消息体 + 校验和”的通用框架。本章将深入解析这三种格式的差异、协议结构以及关键字段含义,为后续的标签校准实操打下坚实基础。

3.1 三种数据格式对比与选型

不同的数据格式在性能、可读性和开发成本上各有优劣。下表从多个维度对三种格式进行对比,帮助你在实际项目中做出合理选择。

对比维度 二进制格式 Protobuf JSON
编码方式 原始字节流,无压缩 二进制序列化,自带压缩 文本字符串,UTF-8
数据体积 最小(固定长度字段) 较小(可变长度编码) 最大(冗余标签和引号)
解析速度 极快(直接内存映射) 快(预编译Schema) 慢(字符串解析)
可读性 差(需对照协议文档) 中(需.proto文件辅助) 好(人类可直接阅读)
跨语言支持 需手动处理字节序 官方支持多语言 几乎所有语言原生支持
Schema约束 无(依赖固定偏移) 强(字段编号+类型) 弱(可动态扩展)
典型场景 交易所原始快照、极速行情 券商内部推送、量化平台 调试、日志、Web API
选型建议:在Level2行情标签校准场景中,二进制格式常用于接收交易所原始数据,Protobuf适合内部微服务间的高效传输,而JSON则多用于离线分析或调试阶段。校准工具通常需要同时支持这三种格式的解析。

3.2 通用协议结构解析

无论采用哪种数据格式,Level2行情数据包都遵循一个三层协议结构:消息头(Header)、消息体(Body) 和 校验和(Checksum)。理解这一结构是正确解析数据的前提。

Level2行情数据包通用协议结构 消息头(Header) 固定长度 | 包含消息类型、长度、时间戳、会话ID等元信息 消息体(Body) 可变长度 | 承载具体行情数据(逐笔成交、快照、委托队列等) 字段排列方式取决于数据格式(二进制/Protobuf/JSON) 校验和(Checksum)

3.2.1 消息头(Header)

消息头是数据包的“身份证”,通常为固定长度(如32字节或64字节)。解析器必须先读取并解析消息头,才能正确理解后续的消息体。消息头包含以下核心字段:

  • 消息类型(MsgType):标识该数据包是逐笔成交、快照、委托队列还是指数行情。
  • 消息体长度(BodyLength):指明消息体的字节数,用于从流中切分出完整的消息体。
  • 时间戳(Timestamp):交易所生成该数据的时间,精度通常为微秒或纳秒。
  • 会话ID(SessionID):标识数据来源的会话通道,用于多路复用。
  • 序列号(SeqNo):该数据包在会话中的递增序号,用于检测丢包和重排序。

3.2.2 消息体(Body)

消息体承载具体的行情数据,其内部字段排列方式取决于数据格式:

  • 二进制格式:字段按固定偏移排列,无字段名,解析时需按协议文档逐字节读取。
  • Protobuf:字段以Tag-Length-Value(TLV)方式编码,通过.proto文件定义字段编号和类型。
  • JSON:字段以键值对形式组织,字段名即为键,可读性最高但体积最大。

3.2.3 校验和(Checksum)

校验和用于验证数据包在传输过程中是否被篡改或损坏。常见的校验算法包括:

  • CRC32:循环冗余校验,检测能力强,常用于二进制和Protobuf格式。
  • MD5:消息摘要算法,安全性较高,但计算开销较大。
  • XOR校验:简单异或,计算速度快,但检测能力较弱。
校准实操要点:在标签校准过程中,如果发现数据异常(如价格突变、买卖方向错乱),应首先检查校验和是否正确。校验失败的数据包必须丢弃,避免污染校准结果。

3.3 常见字段含义详解

无论数据格式如何,Level2行情数据中都有一些核心字段是共通的。理解这些字段的精确含义,是进行标签校准的基础。

字段名称 数据类型 含义说明 校准注意事项
时间戳 int64 / string 交易所生成该笔数据的UTC时间,精度通常为微秒(如 09:30:01.123456) 校准时间轴时,需注意时区转换和时钟同步偏差
价格 int64 / float 成交价或买卖报价,单位为元,通常保留4位小数(如 12.3456) 注意价格精度和最小变动单位(Tick Size),避免浮点误差
数量 int64 成交量或委托量,单位为股或手(1手=100股) 区分“股”与“手”,部分交易所按股计算,部分按手计算
买卖方向 int8 / string 标识交易方向:'B' 或 1 表示买方主动,'S' 或 2 表示卖方主动 方向错误会导致资金流向计算完全相反,是校准的重点
证券代码 string 股票代码,如 "600519.SH" 或 "000001.SZ" 注意代码格式统一,避免因前缀缺失导致匹配失败
委托序号 int64 交易所为每笔委托生成的唯一序号,用于追踪订单生命周期 可用于关联逐笔成交与委托队列,校验数据一致性

3.4 三种格式的协议结构对比

虽然都遵循“Header + Body + Checksum”的框架,但三种格式在具体实现上存在显著差异。下面的SVG图展示了三种格式在协议结构上的不同侧重点。

三种数据格式的协议结构对比 二进制格式 固定偏移字段排列 无字段名,无分隔符 解析依赖协议文档 性能最高 CRC32 / XOR Protobuf格式 TLV编码(Tag-Length-Value) 字段编号 + 类型 + 值 需.proto文件定义Schema 体积小,解析快 CRC32 / 自定义 JSON格式 键值对文本结构 字段名即为键 可读性最好 体积最大,解析最慢 MD5 / 无校验 核心差异总结 二进制:固定偏移,性能极致,但可维护性差 Protobuf:TLV编码,平衡性能与可维护性,需Schema管理 JSON:键值对文本,可读性最佳,适合调试和低频场景

3.5 代码示例:解析二进制格式的消息头

以下是一个使用Python解析二进制格式消息头的示例。假设消息头固定为32字节,包含消息类型(2字节)、消息体长度(4字节)、时间戳(8字节)和会话ID(8字节)。

import struct
from datetime import datetime

def parse_binary_header(data: bytes) -> dict:
    """
    解析二进制格式的消息头(32字节固定长度)
    字段顺序:MsgType(2) + BodyLength(4) + Timestamp(8) + SessionID(8) + Reserved(10)
    """
    if len(data) < 32:
        raise ValueError("消息头长度不足32字节")

    # 使用struct模块按大端序解析
    msg_type, body_length, timestamp_ns, session_id = struct.unpack('!H I Q Q', data[:22])

    # 将纳秒时间戳转换为可读时间
    readable_time = datetime.fromtimestamp(timestamp_ns / 1_000_000_000)

    header = {
        'msg_type': msg_type,
        'body_length': body_length,
        'timestamp': readable_time,
        'session_id': session_id,
        'raw_timestamp_ns': timestamp_ns
    }
    return header

# 示例用法
if __name__ == '__main__':
    # 模拟32字节消息头(实际数据应从网络或文件读取)
    mock_header = struct.pack('!H I Q Q', 1, 256, 1700000000123456, 1001)
    mock_header += b'\x00' * 10  # 填充保留字段

    result = parse_binary_header(mock_header)
    print(f"消息类型: {result['msg_type']}")
    print(f"消息体长度: {result['body_length']} 字节")
    print(f"时间戳: {result['timestamp']}")
    print(f"会话ID: {result['session_id']}")
校准实操提示:在解析二进制格式时,务必确认字节序(Endianness)。上海证券交易所通常使用大端序(Big-Endian),而深圳证券交易所可能使用小端序(Little-Endian)。字节序错误会导致所有字段解析结果完全错误。

3.6 本章小结

本章详细介绍了Level2行情数据的三种常用格式(二进制、Protobuf、JSON)及其协议结构(消息头、消息体、校验和)。通过对比表格和SVG图,你可以清晰地看到不同格式在性能、可读性和解析复杂度上的差异。同时,对时间戳、价格、数量、买卖方向等核心字段的深入理解,为后续的标签校准工作提供了必要的知识储备。在实际校准过程中,应根据数据来源和场景选择合适的解析方式,并始终关注校验和与字节序等关键细节。