行情接口基础:Level-1 vs Level-2 协议
做量化交易,行情数据就是你的眼睛。没有行情,策略就是瞎子。
我个人习惯把行情协议分成两类:Level-1 和 Level-2。说白了,一个是大路货,一个是VIP通道。
Level-1 行情:够用,但不够快
Level-1 是啥?就是你在普通炒股软件上看到的那种行情。3秒刷新一次,只有五档买卖盘,成交明细也是合并后的。
它的数据字段大概长这样:
struct Level1Snapshot {
char symbol[8]; // 股票代码
double open; // 开盘价
double high; // 最高价
double low; // 最低价
double close; // 收盘价(昨收)
double lastPrice; // 最新价
uint64 volume; // 成交量
double amount; // 成交额
double bidPrice[5]; // 买一到买五价格
double askPrice[5]; // 卖一到卖五价格
uint64 bidVolume[5]; // 买一到买五数量
uint64 askVolume[5]; // 卖一到卖五数量
};
嗯,这里要注意:Level-1 的买卖盘只有五档。对于高频策略来说,这远远不够。你想想看,大单藏在十档、二十档后面,你根本看不见。
我的经验:做日内低频策略,Level-1 完全够用。但如果你做的是 tick 级别的策略,别想了,直接上 Level-2。
Level-2 行情:真正的战场
Level-2 就厉害了。它提供的是 逐笔成交 和 十档行情。数据量大概是 Level-1 的 10 到 20 倍。
我曾经在实盘环境中踩过一个坑:Level-2 的逐笔成交数据,高峰期每秒能产生几千条。如果处理不及时,内存直接爆掉。
Level-2 的核心数据结构:
struct Level2Tick {
char symbol[8]; // 股票代码
uint64 timestamp; // 精确到微秒
double price; // 成交价格
uint64 volume; // 成交量
char buySellFlag; // 'B' 买盘成交, 'S' 卖盘成交
uint64 bidVolume[10]; // 十档买量
uint64 askVolume[10]; // 十档卖量
double bidPrice[10]; // 十档买价
double askPrice[10]; // 十档卖价
};
关键区别:Level-2 的逐笔成交能让你看到每一笔交易的细节。大单拆单、对倒、老鼠仓,都能从逐笔数据里看出来。
TCP vs UDP:怎么选?
行情接入方式,主流就两种:TCP 和 UDP。我见过不少新手在这上面栽跟头。
TCP 接入:稳,但慢
TCP 有重传机制,数据不会丢。但代价是延迟高。对于行情来说,TCP 的延迟通常在 1-5 毫秒。
适合场景:
- 盘后数据下载
- 非高频策略
- 历史回测数据获取
UDP 接入:快,但会丢
UDP 没有重传,延迟可以做到 100 微秒以内。但网络抖动时,数据包可能丢失。
我建议的做法是:
- 主链路用 UDP 接收实时行情
- 辅链路用 TCP 做数据补全
- 本地维护一个序列号,发现跳号就从 TCP 补
注意:UDP 的 MTU 限制是 1500 字节。如果行情包超过这个大小,会被分片。分片包一旦丢失一个,整个包都废了。我曾经因为这个原因,丢了一整天的逐笔数据。
行情快照与增量更新
行情数据有两种更新方式:快照 和 增量。这个设计思路,其实跟数据库的 binlog 有点像。
快照机制
快照就是某一时刻的完整行情数据。比如每 3 秒发一次完整的五档盘口。
优点:简单,直接解析就行。
缺点:数据量大,带宽浪费严重。
增量机制
增量只发变化的部分。比如某只股票买一价格变了,只发这一条变化。
增量更新的数据结构:
struct IncrementalUpdate {
char symbol[8]; // 股票代码
uint64 sequence; // 序列号,用于排序
uint8 updateType; // 0: 新增, 1: 修改, 2: 删除
uint8 priceLevel; // 第几档
double price; // 价格
uint64 volume; // 数量
};
嗯,这里有个坑:增量更新必须按顺序处理。如果顺序乱了,盘口数据就全乱了。
我的做法:本地维护一个盘口快照,收到增量就更新快照。同时每 30 秒强制拉一次全量快照,用来校验本地数据是否正确。
核心逻辑流程图
下面这张图,是我自己总结的行情接入核心流程。你看一眼就明白了:
实战中的几个坑
最后,分享几个我踩过的坑:
- UDP 端口被防火墙拦截 —— 有一次上线前才发现,运维把 UDP 端口封了。排查了整整一天。
- 增量序列号溢出 —— 某些交易所的序列号是 uint32,跑几个月就溢出了。记得做环形缓冲区处理。
- 快照和增量时序错乱 —— 先收到增量,后收到快照。这种情况必须丢弃增量,等快照到位后再处理。
一句话总结:行情接入的核心就三件事——选对协议、处理好快照增量、做好异常恢复。这三件事做好了,行情系统就稳了。
公众号:蓝海资料掘金营,微信deep3321