3. 行情数据格式解析:交易所标准行情快照格式、逐笔成交与逐笔委托格式、增量更新机制
做量化交易这些年,我接触过不少行情协议。坦白说,数据格式就是你和市场之间的「翻译官」。翻译错了,再好的策略也是白搭。
今天咱们就聊聊期货行情的三种核心格式:快照、逐笔、增量。嗯,我会结合自己踩过的坑来讲。
3.1 标准行情快照格式
快照,说白了就是「当前市场长什么样」。交易所每隔几百毫秒给你拍一张「照片」。
以中金所为例,快照消息体大致长这样:
struct MarketDataSnapshot {
uint32_t 交易日; // YYYYMMDD
uint32_t 更新时间; // 自当天0点起的微秒数
char 合约代码[8]; // 如 "IF2106"
double 最新价;
int64_t 总成交量;
double 持仓量;
double 开盘价;
double 最高价;
double 最低价;
double 昨收盘;
double 涨停价;
double 跌停价;
// 五档行情
double 买价[5];
int64_t 买量[5];
double 卖价[5];
int64_t 卖量[5];
};
这里有个细节——时间戳的精度。中金所用的是微秒级,但有些交易所用毫秒。我曾在一次回测中忽略了这一点,结果订单排序全乱了。你想想看,高频场景下差几百微秒,可能就是一笔成交的先后顺序。
3.2 逐笔成交与逐笔委托格式
快照不够用怎么办?那就上逐笔数据。
逐笔成交:每一笔实际发生的交易。格式通常包含:
- 成交编号(全局唯一)
- 成交时间(纳秒级)
- 成交价格
- 成交量
- 买卖方向(主动买/主动卖)
逐笔委托:每一笔挂单和撤单。格式包含:
- 委托编号
- 委托时间
- 委托价格
- 委托量
- 委托类型(限价/市价)
- 操作类型(新单/撤单/修改)
我个人习惯把逐笔数据称为「显微镜」。它能让你看到市场最微观的博弈过程。举个例子:
// 逐笔成交示例(简化版)
时间: 09:30:01.123456789
成交编号: 1000001
价格: 4850.0
量: 5
方向: 主动买 // 买方吃单
我曾经用逐笔数据做过一个「大单识别」模块。当连续出现多笔主动买且单笔量超过阈值时,就判定为机构入场。效果还不错,但要注意——逐笔数据量极大。一天下来,光中金所一个品种就能产生几十万条记录。解析速度跟不上,程序直接崩掉。
3.3 增量更新机制
快照数据太大,逐笔数据太密。有没有折中方案?有——增量更新。
增量更新的思路很简单:
- 先发一个全量快照(比如开盘时)
- 后续只发变化的部分(比如某档价格变了)
- 客户端自己维护一个「本地快照」,不断应用增量
举个例子,上期所的行情协议就是这样:
// 增量消息示例
struct IncrementUpdate {
uint32_t 合约索引;
uint8_t 更新类型; // 0=买价变化, 1=卖价变化, 2=最新价变化
uint8_t 档位; // 0-4 对应五档
double 新价格;
int64_t 新数量;
};
为什么要有增量机制?说白了,为了节省带宽。全量快照一次可能几百字节,但增量可能只有几十字节。在千兆网络环境下,这点差异不明显。但如果你做的是跨市场套利,带宽就是钱。
我记得有一次,交易所临时调整了增量更新的频率。从原来的每 500ms 一次,变成了每 100ms 一次。我们的解码程序没来得及适配,结果本地快照和交易所快照出现了偏差。那天的回测数据全废了。
3.4 三种格式的对比
| 特性 | 快照 | 逐笔 | 增量 |
|---|---|---|---|
| 数据量 | 中等 | 极大 | 小 |
| 延迟 | 高(几百ms) | 低(微秒级) | 中(几十ms) |
| 信息完整度 | 高(全量) | 极高(每笔) | 中(仅变化) |
| 适用场景 | 策略回测、风控 | 高频交易、微观结构分析 | 实时行情推送、低带宽环境 |
3.5 知识体系结构图
下面这张图,是我自己梳理的行情数据格式关系。你可以把它当作一个「地图」:
从这张图能看出来,三种格式不是互斥的。实际项目中,我通常这样搭配:
- 盘前初始化:拉一次全量快照
- 盘中实时:接收增量更新,同时订阅逐笔数据做深度分析
- 盘后回测:用快照数据做基准,逐笔数据做细节验证
嗯,这就是我对行情数据格式的理解。说白了,没有最好的格式,只有最适合你场景的格式。选对了,事半功倍;选错了,天天加班修 Bug。