3. Tick数据源与接入:常见交易所Tick数据源、FIX协议接入、WebSocket接入
在量化交易系统中,Tick数据是市场微观结构的原子级记录。本章聚焦于如何获取、接入并解析这些高频数据流。我们将从主流交易所的数据源特性出发,深入对比FIX协议与WebSocket两种接入方式的技术细节与适用场景。
3.1 常见交易所Tick数据源对比
不同交易所的数据粒度、传输延迟和订阅成本差异显著。下表列出了全球主流交易所的Tick数据源关键参数:
| 交易所 | 数据粒度 | 典型延迟 | 接入协议 | 费用模式 |
|---|---|---|---|---|
| NYSE/纳斯达克 | 逐笔成交 + 逐笔报价 | < 1ms (CoLo) | ITCH / FIX / SoupBinTCP | 按数据种类月费 |
| CME Group | 逐笔成交 + 深度快照 | < 500μs (CoLo) | FIX / iLink 3 / MDP 3.0 | 按产品线月费 |
| 币安 (Binance) | 逐笔成交 + 增量深度 | 10-50ms (公网) | WebSocket / REST | 按订阅等级 |
| 欧交所 (Eurex) | 逐笔成交 + 逐笔报价 | < 1ms (CoLo) | FIX / Eurex Enhanced | 按连接月费 |
| 上期所 (SHFE) | 逐笔成交 + 五档快照 | 10-100ms | FIX / 私有TCP | 按席位年费 |
选型建议: 对于高频做市策略,优先选择支持逐笔报价(Market By Order)的交易所,并考虑CoLo(机房托管)以降低物理延迟。对于中低频策略,WebSocket接入公网数据即可满足需求。
3.2 FIX协议接入:企业级Tick数据管道
FIX(Financial Information eXchange)协议是金融行业最成熟的消息标准,广泛应用于机构级市场数据接入。其核心优势在于消息格式标准化、会话层可靠、支持复杂的业务逻辑。
3.2.1 FIX协议核心组件
- 会话层: 基于TCP长连接,通过Logon/Logout/Heartbeat消息管理会话生命周期。
- 应用层: 使用Tag=Value格式编码业务消息,如市场数据增量(Market Data Incremental Refresh)。
- 序列号与重传: 每个消息携带序列号,支持断线重连后的消息补传(Gap Fill / Resend Request)。
- 快照与增量: 交易所通常先发送全量快照(Snapshot),后续推送增量更新(Incremental Refresh)。
3.2.2 FIX接入Tick数据流程
- 建立TCP连接: 连接到交易所指定的FIX网关IP和端口。
- 发送Logon消息: 包含用户名、密码、目标CompID等认证信息。
- 订阅市场数据: 发送Market Data Request消息,指定产品、订阅类型(快照/增量)、深度层级。
- 接收快照: 交易所返回Market Data Snapshot Full Refresh消息,包含当前完整订单簿。
- 处理增量流: 持续接收Market Data Incremental Refresh消息,更新本地订单簿。
- 心跳与重连: 定期发送Heartbeat,检测连接状态;断线后按序列号请求重传。
关键代码片段:FIX消息解析(Java QuickFIX/J)
// 接收市场数据增量消息
public void onMessage(quickfix.fix44.MarketDataIncrementalRefresh msg, SessionID sessionID) {
// 获取MDEntries组
int entryCount = msg.getNoMDEntries().getValue();
for (int i = 1; i <= entryCount; i++) {
Group group = msg.getGroup(i, MDEntryType.FIELD);
char entryType = group.getChar(MDEntryType.FIELD);
double entryPx = group.getDouble(MDEntryPx.FIELD);
double entrySize = group.getDouble(MDEntrySize.FIELD);
// 更新本地订单簿
orderBook.update(entryType, entryPx, entrySize);
}
}
3.3 WebSocket接入:轻量级实时数据流
WebSocket凭借其全双工通信、低开销、浏览器友好等特性,已成为加密货币和部分传统交易所的首选数据接入方式。它通过一个TCP连接提供持久化的双向数据通道。
3.3.1 WebSocket与FIX协议对比
| 对比维度 | FIX协议 | WebSocket |
|---|---|---|
| 消息格式 | Tag=Value 或 FIXML | JSON / BSON / 二进制 |
| 会话管理 | 内置Logon/Logout/Heartbeat | 需应用层实现心跳 |
| 重传机制 | 序列号 + Resend Request | 需应用层实现(如订阅ID) |
| 延迟 | 极低(二进制编码) | 较低(JSON解析开销) |
| 适用场景 | 机构级、高频、低延迟 | 零售、中低频、多平台 |
| 开发复杂度 | 高(需FIX引擎) | 低(标准WebSocket库) |
3.3.2 WebSocket接入Tick数据示例(币安)
- 建立连接:
wss://stream.binance.com:9443/ws - 订阅深度流: 发送JSON订阅消息,指定交易对和深度级别。
- 处理增量事件: 接收包含事件类型、价格、数量的JSON消息。
- 维护本地订单簿: 根据事件类型(新增/更新/删除)修改本地数据结构。
关键代码片段:WebSocket订阅深度流(Python)
import websocket
import json
def on_message(ws, message):
data = json.loads(message)
if data['e'] == 'depthUpdate':
# 处理增量深度更新
for bid in data['b']: # bids: [price, quantity]
order_book.update_bid(float(bid[0]), float(bid[1]))
for ask in data['a']: # asks: [price, quantity]
order_book.update_ask(float(ask[0]), float(ask[1]))
def on_open(ws):
# 订阅BTCUSDT 100ms深度流
sub_msg = {
"method": "SUBSCRIBE",
"params": ["btcusdt@depth@100ms"],
"id": 1
}
ws.send(json.dumps(sub_msg))
ws = websocket.WebSocketApp("wss://stream.binance.com:9443/ws",
on_open=on_open,
on_message=on_message)
ws.run_forever()
3.4 接入架构对比与选型决策
下图展示了两种接入方式在典型量化系统架构中的位置与数据流向:
3.5 接入选型决策矩阵
根据团队技术栈、延迟要求和运维能力,选择最适合的接入方式:
| 决策因素 | 推荐方案 | 理由 |
|---|---|---|
| 延迟敏感 < 1ms | FIX + CoLo | 二进制编码、硬件加速、专用线路 |
| 延迟敏感 1-10ms | FIX 或 WebSocket (二进制) | FIX更稳定,WebSocket需优化JSON解析 |
| 多语言团队 | WebSocket | 标准库支持广泛,开发效率高 |
| 加密货币为主 | WebSocket | 交易所原生支持,文档丰富 |
| 传统金融 + 合规 | FIX | 行业标准,审计日志完善 |
| 快速原型验证 | WebSocket | 代码量少,调试方便 |
常见陷阱:
- FIX协议中序列号管理不当会导致数据丢失或重复,务必实现完整的Gap Fill逻辑。
- WebSocket连接可能因网络抖动断开,必须实现自动重连和订阅恢复机制。
- 不要混用快照和增量数据的时间戳,始终以交易所服务器时间为准。
3.6 灰度发布中的Tick数据接入策略
在灰度发布场景下,新旧系统需要同时接入Tick数据流。推荐采用双订阅 + 数据校验策略:
- 并行订阅: 新旧系统各自建立独立的FIX或WebSocket连接,订阅相同产品。
- 数据比对: 在灰度网关层对两路Tick数据进行逐笔比对,记录差异。
- 平滑切换: 当新系统数据一致性达到99.99%后,将生产流量逐步切至新系统。
- 回滚预案: 保留旧系统的连接,一旦发现异常立即切回。