数据源接入:实时行情API(WebSocket vs REST)
做量化交易,第一关就是数据。没有数据,策略就是空中楼阁。
我个人习惯把数据接入比作「做饭的食材采购」。你想想看,食材不新鲜、买错了、或者买回来没处理好,再好的厨艺也白搭。今天我们就聊聊怎么搞定实时行情数据。
WebSocket vs REST:两个流派
先说结论:实盘交易,WebSocket 是标配,REST 是备胎。
REST 接口,说白了就是「你问一句,它答一句」。你发个 HTTP 请求过去,服务器给你返回当前价格。简单吧?但问题来了——你要多快的数据?
我早期做的一个项目,用的就是 REST 轮询。每秒请求一次,看起来还行。但有一次行情剧烈波动,我眼睁睁看着价格从 100 跳到 105,中间那几秒的报价全丢了。嗯,那次回撤让我记忆犹新。
- REST:请求-响应模式,适合历史数据查询、低频操作
- WebSocket:全双工通信,服务器主动推送,适合实时行情
WebSocket 建立连接后,服务器会持续推送数据给你。你不需要反复问「现在多少钱」,服务器会主动告诉你「价格变了」。延迟通常在毫秒级,比 REST 的几百毫秒快了一个数量级。
为什么会这样?因为 WebSocket 省去了 HTTP 的握手开销。每次 REST 请求都要建立 TCP 连接、发送 HTTP 头、等待响应。而 WebSocket 一次握手,长期保持连接。
- 回测、分析用 REST 拉历史数据
- 实盘监控、交易执行用 WebSocket
- 如果交易所只提供 REST,那就用多线程并发轮询,但要做好限流
数据清洗与对齐:脏活累活
拿到原始数据,别急着用。我见过太多人直接拿交易所的原始 tick 数据跑策略,结果回测漂亮,实盘一塌糊涂。
为什么?因为原始数据有「脏」的地方。
常见的坑:
- 重复数据:网络重传导致同一笔成交收到两次
- 乱序数据:不同交易所的行情到达顺序不一致
- 缺失数据:网络抖动丢包,某个 tick 没收到
- 异常值:价格突然跳到一个离谱的数字(比如 0.01 或 999999)
我曾经在实盘中遇到过一个问题:某次行情剧烈波动,WebSocket 推送的数据包顺序乱了。我的策略收到一个「最新价 100.5」,然后收到「最新价 100.2」,结果策略以为价格在下跌,触发了止损。实际上价格是在上涨,只是数据包到达顺序反了。
那次之后,我加了一个数据对齐模块。核心逻辑很简单:
# 伪代码:数据对齐逻辑
class DataAligner:
def __init__(self):
self.buffer = [] # 缓存未对齐的数据
self.last_seq = 0 # 上一笔的序列号
def process(self, tick):
# 检查序列号是否连续
if tick.seq != self.last_seq + 1:
# 有缺失或乱序,先缓存
self.buffer.append(tick)
return None
# 正常数据,直接返回
self.last_seq = tick.seq
return tick
def flush(self):
# 按序列号排序后输出
self.buffer.sort(key=lambda x: x.seq)
for tick in self.buffer:
yield tick
self.buffer.clear()
- 不同交易所的序列号机制不同,有的用时间戳,有的用递增整数
- 对齐时要注意时间戳精度,毫秒级和微秒级要统一
- 如果数据延迟超过阈值(比如 5 秒),建议直接丢弃,避免影响实时性
Tick 级与 K 线级数据存储
数据存哪里?存什么格式?存多久?这些问题在系统设计阶段就要想清楚。
先看一个对比表格:
| 维度 | Tick 级数据 | K 线级数据 |
|---|---|---|
| 数据量 | 极大(每天几百万条) | 较小(每天几千条) |
| 存储成本 | 高 | 低 |
| 查询速度 | 慢(需要索引优化) | 快 |
| 用途 | 高频策略、回测、分析 | 中低频策略、监控、展示 |
| 保留周期 | 短期(1-3个月) | 长期(数年) |
我个人习惯的做法是:
- Tick 数据:用时序数据库(比如 InfluxDB、ClickHouse)存储,按天分区
- K 线数据:用关系型数据库(比如 PostgreSQL)或者直接存 CSV 文件
- 实时数据:内存缓存(Redis)保留最近 1 小时的 tick,用于策略计算
这里有个经验:不要把所有 tick 数据都存到同一个表里。我早期犯过这个错,一张表里塞了上亿条记录,查询一次要几十秒。后来按日期分表,查询速度提升了 100 倍。
- Tick 数据:按「交易所_品种_日期」命名表,比如 binance_btcusdt_20240101
- K 线数据:按「交易所_品种_周期」命名表,比如 binance_btcusdt_1m
- 定期归档:超过 3 个月的 tick 数据压缩后存到冷存储(比如 S3)
整体架构图
下面这张图展示了数据接入的完整流程,从交易所到策略计算:
这张图展示了我常用的三层架构。数据从交易所进来,经过清洗对齐,存入不同层级的存储,最后供策略消费。每一层都有独立的职责,方便后期扩展和维护。
- 我曾经因为 WebSocket 断线重连逻辑没写好,导致实盘中断了 10 分钟。后来加了心跳检测和自动重连,才解决。
- 数据对齐时,不要只依赖时间戳。不同交易所的时钟可能有偏差,最好用交易所自带的序列号。
- Tick 数据存储要控制写入频率。批量写入比逐条写入效率高 10 倍以上。
好了,数据接入这块就聊这么多。记住一句话:数据质量决定策略上限。把数据搞干净了,后面的工作才能顺利推进。