2. 行情数据源接入:交易所行情协议对比与网关架构

高频交易系统的第一道关卡是行情数据源接入。不同交易所采用差异极大的底层传输协议与数据编码方式,直接决定了行情网关的架构设计、延迟指标与运维复杂度。本章从协议对比、网关分层、解码归一化三个维度展开,为后续灰度发布与Serving奠定数据基础。

2.1 交易所行情协议对比(UDP / Multicast / TCP)

主流交易所行情分发协议可归纳为三类:UDP单播、UDP组播(Multicast)、TCP流。下表从传输模型、可靠性、延迟、适用场景四个维度进行对比:

对比维度 UDP 单播 UDP 组播 (Multicast) TCP 流
传输模型 点对点,每个客户端独立数据流 一对多,网络层复制数据包 点对点,面向连接
可靠性 无内置确认/重传,需应用层补偿 无内置确认/重传,依赖FEC或快照补偿 内置ACK、重传、顺序保证
延迟 极低(微秒级),无连接开销 极低(微秒级),网络层多播效率高 较低(毫秒级),连接建立与拥塞控制引入抖动
适用场景 小规模自营、专线直连 大规模机构、交易所标准行情分发 WebSocket行情、历史数据回放、非极速场景
典型交易所 部分DMA接口、ICE CME、Eurex、LSE、上期所(CFFEX) Binance、Coinbase、Bybit(WebSocket)
实战建议:高频场景首选UDP组播。组播模式下,行情网关只需加入一个组播组即可接收全量行情,网络负载与客户端数量解耦。但必须处理丢包补偿——通常采用“快照+增量”双通道机制。

2.2 行情网关架构设计

行情网关是连接交易所原始数据与内部系统的桥梁。一个生产级网关通常包含以下分层:

  1. 网络接入层:负责UDP/TCP socket管理、组播组加入、连接池维护、心跳检测。
  2. 协议解析层:按交易所协议规范(如CME MDP 3.0、Eurex EBS、上期所STEP)解析二进制报文。
  3. 解码与归一化层:将交易所专有字段映射为内部统一行情模型(如Symbol、Bid/Ask、LastPrice、Volume)。
  4. 分发层:将归一化后的行情通过低延迟通道(共享内存、RDMA、ZeroMQ)推送给交易引擎或缓存层。

下图展示行情网关的典型分层架构与数据流:

行情网关分层架构与数据流 行情网关分层架构与数据流 交易所行情源 网络接入层 协议解析层 解码与归一化层 分发层 交易引擎 / 缓存 说明: 网络接入层管理组播组与心跳;协议解析层按CME/Eurex/STEP规范拆包;解码层映射为统一字段;分发层通过共享内存或RDMA推送。 各层之间使用无锁队列或环形缓冲区,避免内核态切换。
延迟关键点:网关内部各层之间必须使用无锁队列或环形缓冲区,避免锁竞争和系统调用。网络接入层与协议解析层通常绑定到独立CPU核心,使用DPDK或AF_XDP绕过内核协议栈。

2.3 数据解码与归一化

不同交易所的行情编码格式差异巨大,解码与归一化是网关中最容易引入延迟和Bug的环节。常见编码格式包括:

  • 二进制定长/变长:CME MDP 3.0使用SBE(Simple Binary Encoding),Eurex使用EBS二进制协议。
  • 文本/JSON:部分加密货币交易所使用JSON over WebSocket,解析开销较高。
  • STEP/FIX:国内期货交易所(上期所、大商所)采用STEP协议,基于FIX标签-值格式。

归一化层需要将上述异构数据映射为统一的内部行情结构体。以下是一个简化的C++归一化结构体示例:

// 内部统一行情结构体(归一化后)
struct UnifiedMarketData {
    uint64_t  timestamp_ns;      // 纳秒级时间戳
    uint32_t  exchange_id;       // 交易所枚举
    uint32_t  symbol_id;         // 内部品种ID
    double    bid_price;         // 买一价
    uint64_t  bid_volume;        // 买一量
    double    ask_price;         // 卖一价
    uint64_t  ask_volume;        // 卖一量
    double    last_price;        // 最新成交价
    uint64_t  last_volume;       // 最新成交量
    uint8_t   data_flags;        // 快照/增量/心跳标志
};
避坑提示:归一化时务必注意字节序(Big-Endian vs Little-Endian)、时间戳精度(秒/毫秒/纳秒)、价格精度(定点数 vs 浮点数)。建议在解码层统一转为纳秒时间戳和双精度浮点,避免后续计算时反复转换。

下图展示从原始交易所报文到归一化结构体的完整解码流程:

行情解码与归一化流程 行情解码与归一化流程 原始二进制报文 协议拆包 字段提取 归一化映射 各步骤关键操作: 1. 原始二进制报文:从UDP/TCP socket读取的字节流,可能包含多个消息帧。 2. 协议拆包:按交易所协议头长度、消息类型、序列号拆分完整消息(如CME的Packet Header + Message Header)。 3. 字段提取:解析SBE/STEP/JSON字段,处理字节序、位掩码、可选字段。 4. 归一化映射:将交易所专有字段名(如“BidPx”)映射为内部统一字段(bid_price),并统一精度与时间单位。

2.4 灰度发布对行情接入的影响

在灰度发布场景下,行情网关需要支持多版本并行与流量切换。关键设计点包括:

  • 协议版本兼容:交易所可能升级协议(如CME MDP 3.0 → 3.1),灰度期间需同时支持新旧版本解码。
  • 归一化字段扩展:新版本可能增加字段(如统计波动率),归一化结构体需预留扩展位或使用动态字段。
  • 灰度路由:按交易所、品种或客户端ID将行情流量路由到不同版本的网关实例,实现平滑升级。
灰度实践:建议在归一化层引入版本号字段,并在分发层根据版本号将行情投递到不同的消费队列。这样交易引擎可以按自身灰度策略选择消费新版或旧版行情,实现完全解耦。