3. 行情数据协议基础:CTP与FIX/FAST协议对比

做期货行情解析,绕不开协议。说白了,行情数据就是按照特定格式打包好的二进制流。你拿到手,得知道怎么拆开它。

我个人习惯把协议分成两类:一类是CTP这种国内期货市场的主流协议,另一类是FIX/FAST这种国际通用的金融协议。今天咱们就掰开揉碎,看看它们到底长什么样。

3.1 CTP协议:国内期货的“普通话”

CTP(综合交易平台)是上期技术开发的。国内几乎所有期货公司都用它。嗯,这里要注意,CTP的行情数据是私有二进制协议,不是文本协议。

我在项目中遇到过一个问题:刚开始接手CTP行情解析时,以为它和FIX一样是文本格式,结果解析出来的全是乱码。后来才明白,CTP的每个字段都是固定偏移、固定长度的。

3.1.1 CTP数据包结构

一个完整的CTP行情包,分为三部分:

  • 消息头:固定7字节,包含交易日期、更新时间、消息类型等
  • 消息体:变长,具体字段取决于消息类型(比如深度行情、逐笔成交)
  • 校验和:1字节,对前面所有字节做异或运算

举个例子,CTP的深度行情消息头长这样:

// CTP消息头结构(7字节)
struct CTPHeader {
    uint16_t 交易日;      // 如 20250321
    uint8_t  更新时间[3]; // HH:MM:SS 的BCD码
    uint16_t 消息长度;    // 消息体长度
    uint8_t  消息类型;    // 0x11=深度行情, 0x12=逐笔成交
};

你想想看,为什么CTP要用固定长度?说白了,就是为了解析速度快。FPGA处理固定长度的数据,一个时钟周期就能定位到字段。我做过测试,CTP的解析延迟可以控制在200纳秒以内

关键点:CTP的消息头里没有“协议版本号”字段。这意味着如果交易所升级协议,你的解析器必须跟着改。我曾经因为没注意这个,导致线上解析失败,亏了一笔手续费。

3.1.2 CTP校验和算法

CTP的校验和很简单:从消息头第一个字节开始,到消息体最后一个字节,全部做异或,结果放在包尾。

// CTP校验和计算(C语言风格)
uint8_t calc_ctp_checksum(uint8_t *buf, int len) {
    uint8_t sum = 0;
    for (int i = 0; i < len; i++) {
        sum ^= buf[i];
    }
    return sum;
}

注意:CTP的校验和只覆盖消息头+消息体,不包含校验和本身。这个细节容易踩坑。

3.2 FIX/FAST协议:国际市场的“通用语”

FIX(金融信息交换)协议是文本协议,用标签-值对表示。比如 35=D 表示消息类型是新订单。但FIX有个问题:太啰嗦了。一个行情包可能几百字节,带宽浪费严重。

FAST(FIX加速传输)就是来解决这个问题的。它用模板+编码的方式,把数据压缩到极致。我见过一个FIX行情包,原始大小500字节,用FAST压缩后只有80字节。

3.2.1 FIX数据包结构

FIX的包结构是文本的,用SOH(0x01)分隔字段:

8=FIX.4.4|9=78|35=W|49=CME|56=CLIENT|34=2|52=20250321-10:30:00|55=ES|270=4500.25|271=100|10=234

其中:

  • 8:协议版本(FIX.4.4)
  • 9:消息体长度(78字节)
  • 35:消息类型(W=行情快照)
  • 55:合约代码(ES=标普500期货)
  • 270:最新价(4500.25)
  • 10:校验和(234)

FIX的校验和是模256累加和。计算方式:从"8="开始到SOH之前,所有字节的ASCII码相加,取低8位。

避坑指南:我曾经在解析FIX时,没注意校验和计算范围。FIX的校验和只覆盖消息头+消息体,不包含"10=xxx"这个字段本身。而且校验和字段是3位数字,不足补0。

3.2.2 FAST协议:压缩的艺术

FAST的核心思想是:用模板定义字段顺序,用编码减少冗余

比如,一个行情包里有连续的时间戳,如果每次只传增量,就能省很多字节。FAST支持多种编码方式:

  • 无符号整数:用变长编码,小数字只占1字节
  • 有符号整数:用Zigzag编码,负数也能高效表示
  • 字符串:用长度前缀+内容,或者用字典索引
  • 十进制数:用指数+尾数,适合表示价格

举个例子,FAST的模板定义:

<template id="1" name="MarketDataSnapshot">
    <field name="Symbol" id="55" />
    <field name="LastPx" id="270" presence="optional" />
    <field name="LastQty" id="271" presence="optional" />
</template>

实际传输时,如果LastPx和LastQty没有变化,就只传Symbol。FPGA解析FAST时,需要预加载模板,然后按模板顺序解析字段。

注意:FAST的模板是动态加载的。如果模板变了,解析器必须重新加载。我在做CME行情接入时,遇到过模板版本不匹配导致解析失败的情况。建议在FPGA里设计一个模板缓存,支持热更新。

3.3 CTP vs FIX/FAST:核心差异对比

咱们用一张表来对比:

维度 CTP FIX FAST
编码方式 二进制固定长度 文本标签-值 二进制模板编码
消息头大小 7字节固定 变长(约20-50字节) 变长(约4-10字节)
校验和算法 异或 模256累加 可选(通常用CRC)
解析复杂度 低(固定偏移) 中(需查找标签) 高(需模板+解码)
FPGA友好度 极高 中(需状态机)
带宽效率

我个人建议:做国内期货,直接用CTP协议,FPGA解析最简单。做国际期货(如CME、ICE),必须用FAST,因为数据量太大,FIX的带宽扛不住。

3.4 知识体系结构图

下面这张图,展示了行情协议解析的核心逻辑:

行情协议解析核心逻辑 原始二进制流 协议识别(CTP / FIX / FAST) CTP:固定偏移解析 FIX:标签查找解析 FAST:模板解码解析 校验和验证(异或/累加/CRC) 行情数据字段

这张图展示了从原始二进制流到行情数据字段的完整流程。你想想看,FPGA做这个流程的优势在哪?说白了,就是流水线处理。每个阶段都可以用独立的硬件模块并行执行,延迟极低。

3.5 实战中的选择建议

最后,给几个实际建议:

  • 国内期货:直接用CTP协议,FPGA解析最简单。注意CTP的交易日字段是BCD码,需要转换。
  • 国际期货(CME、ICE等):用FAST协议。FPGA里需要实现模板解析器和变长解码器。
  • 混合场景:如果同时接入国内和国际市场,建议在FPGA里设计一个协议适配层,统一输出标准格式的行情数据。

个人经验:我做过一个项目,需要同时解析CTP和FAST行情。我的做法是在FPGA里用两个独立的解析引擎,然后通过一个仲裁器合并输出。这样即使一个协议出问题,另一个还能正常工作。

嗯,协议基础就讲到这里。记住一点:协议解析是FPGA行情加速的第一步,也是最关键的一步。解析做不好,后面再快的处理都是白搭。


专注资料整理