4、行情数据协议解析:交易所行情协议格式(如L1/L2)、二进制协议解析优化、硬件加速解析方案

行情数据协议解析,说白了就是要把交易所发过来的「天书」翻译成咱们系统能用的数据。我做了这么多年低延迟系统,见过太多团队在这块栽跟头——协议没吃透,解析效率上不去,延迟直接爆表。

今天咱们就聊聊这个环节。我会从协议格式讲起,再到软件层面的优化技巧,最后聊聊硬件加速方案。嗯,这些都是我踩过坑之后总结出来的经验。

4.1 交易所行情协议格式:L1与L2的差异

先搞清楚一个基本概念:L1和L2到底差在哪?

L1行情,也叫快照行情。它告诉你当前最优的买一卖一价格和数量。说白了就是「现在市场上最好的报价是什么」。数据量小,更新频率低,一般几百毫秒推送一次。

L2行情,也叫逐笔行情或深度行情。它告诉你每一笔委托的详细信息——谁在什么价格挂了多大的单子。数据量大,更新频率高,有时候一秒钟能来几千条。

我当年刚接触L2协议时,第一反应是「这数据量也太恐怖了」。后来才明白,L2才是高频交易的核心数据源。你想想看,没有深度数据,你怎么做订单簿重建?怎么做价格预测?

国内常见的行情协议格式:

交易所 L1协议 L2协议 备注
上交所 MDGW (Level-1) MDGW (Level-2) 基于FIX/STEP
深交所 Binary MDGW Binary MDGW 自定义二进制
中金所 FIX/FAST FIX/FAST 压缩编码
郑商所 自定义二进制 自定义二进制 固定长度

这里有个坑要注意:不同交易所的协议格式差异很大。有的用FIX/STEP这种文本协议,有的用自定义二进制协议。我个人习惯是,拿到新交易所的协议文档后,先花一天时间把报文结构画出来,再动手写解析代码。

4.2 二进制协议解析优化:从逐字节到SIMD

二进制协议解析,核心就一个字:快。你不能像解析JSON那样慢慢来,每一微秒都宝贵。

先说说最基础的优化思路。

4.2.1 避免逐字节解析

很多新手写解析代码,喜欢用memcpy逐字节拷贝。这其实很慢。更好的做法是直接内存映射,用结构体指针强转。

// 不推荐:逐字节拷贝
void parse_slow(const char* buf, Order* order) {
    memcpy(&order->price, buf, 8);
    memcpy(&order->volume, buf + 8, 4);
    // ...
}

// 推荐:结构体指针强转
typedef struct __attribute__((packed)) {
    uint64_t price;
    uint32_t volume;
    uint16_t symbol_id;
    uint8_t  side;
} OrderRaw;

void parse_fast(const char* buf, Order* order) {
    const OrderRaw* raw = (const OrderRaw*)buf;
    order->price = raw->price;
    order->volume = raw->volume;
    // ...
}

嗯,这里要注意字节对齐问题。我当年在郑商所的项目上就吃过这个亏——结构体没加packed属性,解析出来的数据全是错的。排查了整整一个下午。

4.2.2 使用SIMD加速

如果数据量特别大,比如L2行情每秒几千条,单靠CPU逐条解析已经不够了。这时候就要上SIMD。

SIMD说白了就是一条指令处理多条数据。比如你要把16个字节的ASCII数字转成整数,用SSE指令一次就能搞定。

// 使用SSE2加速数字解析
__m128i chunk = _mm_loadu_si128((__m128i*)buf);
// 减去'0'的ASCII码
__m128i zeros = _mm_set1_epi8('0');
__m128i digits = _mm_sub_epi8(chunk, zeros);
// 然后做累加...

说实话,SIMD代码写起来挺痛苦的。可读性差,调试困难。但性能提升是实打实的——我测过,同样的解析逻辑,SIMD版本比逐字节版本快了5-8倍。

4.2.3 零拷贝与内存池

解析过程中最怕什么?内存分配。每次new一个对象,或者malloc一块内存,都会引入不可控的延迟。

我的做法是:

  • 预分配一个大内存池,解析时直接从池里取
  • 用环形缓冲区(Ring Buffer)管理原始数据
  • 解析结果直接写入预分配的结构体数组

这样整个解析过程就没有一次动态内存分配。延迟稳定在纳秒级别。

小技巧: 解析L2行情时,可以先把所有字段的偏移量算好,存成一个查找表。这样解析时直接查表定位,不用每次都计算偏移。我管这叫「预计算偏移法」。

4.3 硬件加速解析方案:FPGA与SmartNIC

软件优化做到极致,延迟大概能到几百纳秒。但如果你需要亚微秒级别的解析,那就得上硬件了。

4.3.1 FPGA方案

FPGA解析行情协议,核心思路是用硬件逻辑代替软件逻辑。数据从网口进来,直接在FPGA内部完成协议解析,然后通过PCIe送到主机内存。

这样做的好处:

  • 延迟极低:从网口到解析完成,一般不超过100纳秒
  • 确定性高:没有操作系统调度抖动
  • 吞吐量大:可以同时处理多个数据流

我记得在Xilinx的某个项目上,我们用FPGA解析中金所的FAST协议。FAST协议用了模板压缩,解析逻辑特别复杂。软件版本延迟大概1.2微秒,FPGA版本直接降到80纳秒。嗯,这个差距是数量级的。

4.3.2 SmartNIC方案

SmartNIC是另一种选择。它本质上是一块带可编程网卡的加速卡,上面有ARM核或者FPGA逻辑。

SmartNIC的优势在于:

  • 部署灵活:可以动态加载不同的解析逻辑
  • 与主机解耦:解析不占用主机CPU
  • 支持热更新:不用重启系统就能换协议

但SmartNIC也有缺点:延迟比纯FPGA方案略高,因为数据要经过网卡内部的总线。

4.3.3 硬件加速的适用场景

不是所有场景都需要硬件加速。我建议你根据延迟要求来判断:

延迟要求 推荐方案 典型场景
> 10微秒 软件解析(优化后) 普通量化、回测
1-10微秒 软件+DPDK 中频交易
100纳秒-1微秒 FPGA/SmartNIC 高频交易
< 100纳秒 专用ASIC 极高频、做市商
避坑指南: 我曾经在一个项目上盲目上了FPGA方案,结果发现协议解析逻辑太复杂,FPGA开发周期长达6个月。最后算下来,还不如用软件优化+多核并行来得划算。所以,硬件加速不是万能的,一定要评估好ROI。

4.4 本章小结:解析架构的核心逻辑

行情数据协议解析,本质上是一个「从原始字节到结构化数据」的转换过程。这个过程的效率,直接决定了整个交易系统的延迟天花板。

下面这张图展示了完整的解析架构:

行情数据协议解析架构 交易所行情数据 协议识别(L1/L2/FAST/FIX) 软件解析(CPU/SIMD) FPGA硬件解析 SmartNIC解析 结构化行情数据(订单簿/逐笔)

从这张图可以看出,解析架构的核心是「分层处理」。数据进来后,先识别协议类型,再选择合适的解析方式,最后输出结构化数据。每一层都可以独立优化,互不干扰。

我个人建议,刚开始做系统时先用软件方案,把协议吃透。等业务量上来了,再考虑硬件加速。毕竟,硬件方案虽然快,但开发成本和维护成本都不低。

好了,行情协议解析这块就聊到这儿。记住一句话:解析效率决定系统下限,协议理解决定系统上限。两者缺一不可。


公众号:蓝海资料掘金营,微信deep3321