3、行情数据源:交易所API接口(CTP、FIX),行情快照与增量数据
做高频交易,第一关就是行情数据源。你策略再牛,拿不到数据就是白搭。我个人习惯把行情源比作「水龙头」——你得知道水从哪来,怎么接,接到的水是干净的还是浑的。
今天咱们就聊聊CTP和FIX这两个最主流的接口。嗯,还有快照和增量数据那点事。
3.1 CTP接口:国内期货的标配
CTP(综合交易平台)是上期技术开发的。国内几乎所有期货公司都用它。说白了,它就是国内期货市场的「官方语言」。
CTP行情接口的特点:
- 基于TCP长连接:你连上后,服务器会持续推送数据
- 私有协议:不是标准FIX,是上期技术自己定义的二进制协议
- 支持多合约订阅:一次连接可以订阅N个合约
- 行情与交易分离:行情接口和交易接口是两套
核心要点:CTP的行情推送是「全量快照+增量更新」模式。每笔成交、每笔订单簿变化都会推给你。
我在项目中遇到过一个问题:CTP的行情数据包是定长的。每个数据包固定长度,解析起来很方便。但要注意,不同版本的CTP,数据包结构可能微调。嗯,这里要小心。
来看一个典型的CTP行情数据结构:
// CTP深度行情结构(简化版)
struct CThostFtdcDepthMarketDataField {
char InstrumentID[31]; // 合约代码
char UpdateTime[9]; // 更新时间 HH:MM:SS
int UpdateMillisec; // 更新毫秒
double LastPrice; // 最新价
int Volume; // 成交量
double Turnover; // 成交金额
double OpenPrice; // 今开盘
double HighestPrice; // 最高价
double LowestPrice; // 最低价
double BidPrice1; // 买一价
int BidVolume1; // 买一量
double AskPrice1; // 卖一价
int AskVolume1; // 卖一量
// ... 还有买二到买五,卖二到卖五
};
你想想看,这个结构体里只有五档行情。对于高频交易来说,五档够用吗?说实话,大部分策略五档够了。但如果你做的是做市策略,可能需要十档甚至全档。CTP只给五档,这是个硬伤。
3.2 FIX协议:国际通用的金融协议
FIX(Financial Information eXchange)是国际标准。国外交易所基本都用它。国内一些国际化品种(比如原油、铁矿石的夜盘)也开始支持FIX。
FIX和CTP最大的区别:
- 文本协议:FIX是纯文本的,用标签=值的方式传输。可读性强,但解析效率低
- 会话层独立:FIX定义了完整的会话管理(登录、心跳、重连)
- 扩展性强:可以自定义标签,适应不同交易所的需求
我的建议:如果你做的是跨市场策略(比如内外盘套利),FIX是必须掌握的。因为国外交易所只认FIX。
一个典型的FIX行情消息长这样:
8=FIX.4.4|9=178|35=X|49=EXCHANGE|56=CLIENT|34=3|52=20240101-12:00:00.123|
55=CL-Z4|269=0|270=75.25|271=100|269=1|270=75.26|271=200|
269=2|270=75.24|271=150|269=3|270=75.27|271=50|
10=123|
解析一下:35=X表示这是市场数据增量消息。269=0是买一,269=1是卖一。后面的270是价格,271是数量。
我曾经踩过一个坑:FIX消息里的时间戳是字符串格式,解析起来比CTP的二进制慢不少。在高频场景下,这个解析开销不能忽视。我的做法是——用内存映射文件+手写解析器,跳过标准库的字符串转换。
3.3 行情快照 vs 增量数据
这是高频交易里最核心的概念之一。你搞不清楚这个,订单簿重建就是空中楼阁。
行情快照(Snapshot):
- 某一时刻订单簿的完整状态
- 包含所有档位的买卖价格和数量
- CTP的深度行情就是快照模式
- 数据量大,但一次到位
增量数据(Incremental):
- 订单簿的变化部分
- 比如「买一价变了」、「卖三量增加了」
- FIX的Market Data Incremental Refresh就是增量
- 数据量小,但需要维护状态
注意:增量数据必须基于一个正确的快照才能工作。如果你收到的第一个消息是增量,而你没有初始快照,那订单簿就是错的。我曾经因为这个原因,在回测时发现策略收益曲线异常漂亮——后来才发现是订单簿数据错了,策略在「作弊」。
为什么会这样?你想想看:增量数据只告诉你「变化了什么」,不告诉你「当前是什么」。如果你漏掉了一个增量,后续所有增量都建立在错误的基础上。这就是所谓的「状态漂移」。
下面这张图展示了快照和增量的关系:
从图上可以看得很清楚:快照是「全量拷贝」,增量是「差异更新」。高频交易中,我们通常的做法是:
- 首次连接时请求快照:拿到一个正确的初始状态
- 后续接收增量:在快照基础上不断应用增量
- 定期校验:每隔一段时间(比如1分钟)重新请求快照,防止状态漂移
实战技巧:我习惯在内存中维护两份订单簿。一份是「当前工作副本」,用于策略决策。另一份是「校验副本」,定期用快照覆盖。如果两者不一致,说明增量数据有丢失,需要报警并重建。
3.4 数据解析的性能对比
咱们用数据说话。我实测过不同协议在相同硬件下的解析性能:
| 协议类型 | 单条解析耗时 | 每秒处理能力 | 内存占用 | 适用场景 |
|---|---|---|---|---|
| CTP二进制 | ~200ns | 500万+ | 低 | 国内期货高频 |
| FIX文本 | ~800ns | 125万+ | 中 | 跨市场、国际化 |
| FIX优化版(手写解析) | ~350ns | 280万+ | 中 | 需要FIX的高频场景 |
看到差距了吧?CTP的二进制协议天生就快。FIX虽然慢,但通过手写解析器可以大幅提升。我在一个项目中,把FIX解析从800ns优化到350ns,靠的就是跳过标准库,直接用指针操作。
3.5 实际项目中的选择建议
嗯,说了这么多,到底该用哪个?我的经验是这样的:
- 纯国内期货:无脑选CTP。速度快,稳定,期货公司都支持
- 内外盘套利:CTP+FIX双通道。国内用CTP,国外用FIX
- 做市策略:CTP为主,但要注意五档限制。如果需要更深档位,考虑交易所的Level2数据
- 回测系统:建议统一转成内部格式。不管CTP还是FIX,都解析成统一的结构体,方便回测引擎调用
避坑指南:我曾经在CTP行情接口上犯过一个低级错误——没有处理「行情断开重连」的场景。结果某次交易所网络抖动,行情断了3秒,我的策略还在用旧数据做决策。嗯,那天的回撤,不提了。记住:行情源必须实现心跳检测和自动重连,重连后第一时间请求快照。
最后说一句:行情数据源的选择,没有银弹。你得根据自己的策略类型、交易品种、部署环境来权衡。但不管选哪个,快照+增量的处理逻辑是通用的。把这个搞透了,后面订单簿重建就是水到渠成的事。