3、市场数据接口:FIX协议入门、REST API与WebSocket对比、数据延迟分析
做高频交易,说白了就是跟时间赛跑。你拿到的数据比别人慢1毫秒,可能就错失了一笔利润。这一章我们来聊聊市场数据接口——怎么拿数据,用什么协议拿,哪个最快。
我个人习惯把数据接口分成三类:FIX协议、REST API、WebSocket。它们各有各的脾气,选错了,延迟能差出几个数量级。
3.1 FIX协议入门
FIX(Financial Information eXchange)协议,是金融界的“普通话”。几乎所有交易所、券商、做市商都支持它。我最早接触FIX是在做美股交易的时候,那会儿被它的标签号搞得头晕——后来习惯了,反而觉得它很优雅。
3.1.1 FIX协议长什么样?
FIX协议是纯文本的,用“标签=值”的方式组织。每个字段前面有个数字编号,比如35代表消息类型,55代表股票代码。消息之间用SOH(0x01)分隔。
看个例子,一条简单的订单请求:
8=FIX.4.2|9=78|35=D|49=CLIENT1|56=EXCHANGE|34=1|52=20250315-10:30:00.123|11=ORD001|55=600519|54=1|38=1000|40=2|10=234|
这里我用“|”代替了SOH字符,方便阅读。解释一下关键字段:
- 8:协议版本,这里是FIX 4.2
- 35:消息类型,D表示新订单
- 55:证券代码,600519是贵州茅台
- 54:买卖方向,1是买入
- 38:数量,1000股
- 40:订单类型,2是限价单
- 10:校验和,用于完整性检查
3.1.2 用Python解析FIX消息
实际项目中,我们不会手写FIX解析器——太容易出错了。我推荐用quickfix库,它是C++写的,Python绑定,性能很好。
import quickfix as qf
# 创建一个FIX消息
message = qf.Message()
message.getHeader().setField(qf.BeginString("FIX.4.2"))
message.getHeader().setField(qf.MsgType("D")) # 新订单
message.setField(qf.ClOrdID("ORD001"))
message.setField(qf.Symbol("600519"))
message.setField(qf.Side('1')) # 买入
message.setField(qf.OrderQty(1000))
message.setField(qf.OrdType('2')) # 限价单
message.setField(qf.Price(150.50))
# 发送(假设已建立连接)
# session.send(message)
嗯,这里要注意:FIX协议虽然强大,但学习曲线陡。如果你只是做国内期货或数字货币,可能用不到它——交易所通常提供更简单的接口。
3.2 REST API与WebSocket对比
REST API和WebSocket,是目前最主流的两种数据接口。我经常被问到:“到底该用哪个?”
我的回答是:看场景。
3.2.1 REST API:简单但慢
REST API基于HTTP协议,请求-响应模式。你发一个GET请求,服务器返回数据。优点是简单、无状态、容易调试。
但缺点也很明显——每次请求都要建立TCP连接、发送HTTP头、等待响应。延迟通常在50-200ms之间,对于高频交易来说,太慢了。
import requests
import time
# 获取订单簿快照
start = time.time()
resp = requests.get("https://api.exchange.com/orderbook?symbol=btcusdt")
data = resp.json()
latency = (time.time() - start) * 1000
print(f"延迟:{latency:.2f} ms")
我曾经用REST API做行情展示,发现价格刷新总是慢半拍。后来换成WebSocket,体验完全不一样。
3.2.2 WebSocket:实时推送
WebSocket建立一次连接后,服务器可以主动推送数据。延迟通常在1-10ms,比REST快一个数量级。
看个例子,订阅数字货币的订单簿:
import websocket
import json
def on_message(ws, message):
data = json.loads(message)
# 处理订单簿增量更新
print(f"收到更新:{data['bids'][:3]}")
def on_error(ws, error):
print(f"错误:{error}")
def on_close(ws, close_status_code, close_msg):
print("连接关闭")
def on_open(ws):
# 订阅订单簿
sub_msg = {
"type": "subscribe",
"channel": "orderbook",
"symbol": "btcusdt"
}
ws.send(json.dumps(sub_msg))
ws = websocket.WebSocketApp("wss://ws.exchange.com/stream",
on_open=on_open,
on_message=on_message,
on_error=on_error,
on_close=on_close)
ws.run_forever()
3.2.3 对比表格
| 特性 | REST API | WebSocket | FIX协议 |
|---|---|---|---|
| 通信模式 | 请求-响应 | 全双工推送 | 会话式推送 |
| 典型延迟 | 50-200ms | 1-10ms | 0.1-1ms |
| 协议开销 | 高(HTTP头) | 低(帧头) | 极低(二进制) |
| 适用场景 | 查询、非实时 | 行情订阅、交易 | 高频交易、机构 |
| 学习成本 | 低 | 中 | 高 |
3.3 数据延迟分析
延迟是高频交易的核心指标。你想想看,如果别人比你快1毫秒,他就能在你之前成交。这1毫秒,可能就是盈亏的分界线。
3.3.1 延迟从哪里来?
我总结了一下,延迟主要来自四个环节:
- 网络延迟:数据在网线、光纤里传输的时间。光速是30万公里/秒,但实际受路由、交换机影响,延迟会更高。
- 协议延迟:FIX、WebSocket、REST各自的协议开销不同。FIX的二进制编码比JSON快很多。
- 处理延迟:你的代码解析数据、更新订单簿的时间。Python比C++慢,但开发快。
- 交易所延迟:交易所内部撮合、推送数据的时间。这个我们控制不了,只能选快的交易所。
3.3.2 如何测量延迟?
测量延迟不能只测一次,要统计分布。我习惯用百分位数:P50、P99、P99.9。
import time
import numpy as np
latencies = []
for i in range(1000):
start = time.perf_counter_ns()
# 模拟接收数据并处理
# process_data()
end = time.perf_counter_ns()
latencies.append((end - start) / 1_000_000) # 转为毫秒
latencies.sort()
print(f"P50: {np.percentile(latencies, 50):.3f} ms")
print(f"P99: {np.percentile(latencies, 99):.3f} ms")
print(f"P99.9: {np.percentile(latencies, 99.9):.3f} ms")
为什么看P99而不是平均值?因为平均值会被少数慢请求拉高,而P99能告诉你“最差的1%情况有多慢”。高频交易里,那1%的慢请求可能让你爆仓。
3.3.3 延迟优化思路
如果你发现延迟太高,可以从这几个方向入手:
- 换协议:从REST换成WebSocket,延迟能降一个数量级
- 用二进制:FIX的二进制版本(FAST协议)比文本版更快
- 本地缓存:订单簿数据在本地维护,只接收增量更新
- 选机房:把服务器放在交易所的同一机房,延迟能降到微秒级
3.4 本章知识体系
下面这张图,帮你理清三种数据接口的关系和选择逻辑:
好了,这一章的内容就到这里。三种接口各有千秋,选型时别盲目追求低延迟——如果你的策略是分钟级持仓,REST API完全够用。但如果你做的是高频做市,那FIX协议几乎是唯一选择。
记住一句话:没有最好的接口,只有最适合你策略的接口。
公众号:蓝海数据掘金营,微信deep3321