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 知识体系结构图
下面这张图,展示了行情协议解析的核心逻辑:
这张图展示了从原始二进制流到行情数据字段的完整流程。你想想看,FPGA做这个流程的优势在哪?说白了,就是流水线处理。每个阶段都可以用独立的硬件模块并行执行,延迟极低。
3.5 实战中的选择建议
最后,给几个实际建议:
- 国内期货:直接用CTP协议,FPGA解析最简单。注意CTP的交易日字段是BCD码,需要转换。
- 国际期货(CME、ICE等):用FAST协议。FPGA里需要实现模板解析器和变长解码器。
- 混合场景:如果同时接入国内和国际市场,建议在FPGA里设计一个协议适配层,统一输出标准格式的行情数据。
个人经验:我做过一个项目,需要同时解析CTP和FAST行情。我的做法是在FPGA里用两个独立的解析引擎,然后通过一个仲裁器合并输出。这样即使一个协议出问题,另一个还能正常工作。
嗯,协议基础就讲到这里。记住一点:协议解析是FPGA行情加速的第一步,也是最关键的一步。解析做不好,后面再快的处理都是白搭。