4、数据源接入:交易所API介绍(REST vs WebSocket)、行情数据格式(Level1/Level2/Level3)、数据订阅与推送、数据缓存策略
做高频量化,第一关就是搞定数据源。
你策略再牛,模型再准,拿不到一手数据,全是白搭。我见过太多人,策略回测跑得飞起,一上实盘就崩,十有八九是数据源这块没处理好。
今天咱们就把这块彻底讲透。
4.1 交易所API:REST vs WebSocket
交易所给你数据,通常就两种方式:REST和WebSocket。说白了,一个是你主动去问,一个是交易所主动推给你。
4.1.1 REST API
REST就像你去图书馆借书。你问一句“有没有《量化交易实战》这本书?”,图书管理员查一下,告诉你“有,在第三排”。你问一次,他答一次。
在交易里,REST API就是发一个HTTP请求,交易所给你返回数据。比如查账户余额、查历史K线、下单。
- 请求-响应模式,你主动发起
- 适合低频操作,比如查账户、查历史数据
- 有延迟,一次请求几十到几百毫秒
- 有频率限制,比如每秒最多5次
我个人习惯,REST只用来做三件事:获取历史数据、查询账户状态、执行交易指令。行情订阅?千万别用REST,太慢了。
4.1.2 WebSocket API
WebSocket就完全不一样了。它像你打开电视看新闻。电视台一直播,你一直看。你不用每秒钟问一次“现在有什么新闻?”,电视自己会播。
交易所通过WebSocket把行情数据实时推给你。你只需要订阅一次,后面数据就源源不断流过来。
- 推送模式,交易所主动发数据
- 延迟极低,毫秒级甚至微秒级
- 适合高频行情订阅
- 需要维持长连接,有断线重连机制
嗯,这里要注意。WebSocket虽然快,但有个坑——断线。我在项目中遇到过,某次行情剧烈波动,WebSocket连接突然断了,我这边完全没收到数据,策略还在傻傻地按旧数据交易。那一次亏了不少。
4.2 行情数据格式:Level1 / Level2 / Level3
行情数据分等级,就像游戏装备分白装、蓝装、紫装。等级越高,信息越详细,延迟越低,当然价格也越贵。
4.2.1 Level1 行情
Level1就是最基础的行情数据。你打开任何一个股票软件,看到的买一卖一、最新价、涨跌幅,就是Level1。
| 字段 | 说明 |
|---|---|
| 最新价 | 当前最后一笔成交价 |
| 涨跌幅 | 相对于昨收的百分比 |
| 买一价/量 | 最高买入价及对应数量 |
| 卖一价/量 | 最低卖出价及对应数量 |
| 成交量 | 累计成交数量 |
Level1够用吗?做普通交易够了。做高频?远远不够。你想想看,Level1只给你看最上面一档,下面还有九档的挂单你完全看不到,这就像打牌只看对方一张牌。
4.2.2 Level2 行情
Level2就厉害多了。它给你看十档行情,也就是买一到买十、卖一到卖十,全部摆在你面前。
- 十档买卖盘口(买一~买十,卖一~卖十)
- 逐笔成交明细(每一笔成交的时间、价格、数量)
- 委托队列(买一和卖一的前50笔挂单明细)
- 资金流向统计
有了Level2,你能看到什么?你能看到大单在偷偷吃货,能看到主力在挂假单诱多。我习惯用Level2的委托队列来判断主力意图。比如卖一挂了1000手大单,但委托队列里全是小单,那大概率是吓人的假单。
4.2.3 Level3 行情
Level3是最高等级,目前只有少数交易所提供,比如纳斯达克的TotalView。它给你看整个订单簿,所有挂单,每一档都清清楚楚。
Level3的数据量极大。一个热门股票,订单簿可能有几千档。每秒更新几百次。处理起来非常考验系统性能。
4.3 数据订阅与推送
搞清楚了数据格式,接下来就是怎么订阅和接收数据了。
4.3.1 订阅流程
订阅行情,一般分三步走:
- 建立连接:通过WebSocket连接到交易所的行情服务器
- 发送订阅请求:告诉交易所你要订阅哪些品种、哪些数据类型
- 接收推送:交易所开始往你这边推数据
代码示例,以币安的WebSocket订阅为例:
import websocket
import json
def on_message(ws, message):
data = json.loads(message)
# 处理行情数据
print(f"收到行情: {data}")
def on_error(ws, error):
print(f"连接错误: {error}")
def on_close(ws, close_status_code, close_msg):
print("连接关闭,准备重连...")
def on_open(ws):
# 订阅BTC/USDT的深度行情
subscribe_msg = {
"method": "SUBSCRIBE",
"params": ["btcusdt@depth20@100ms"],
"id": 1
}
ws.send(json.dumps(subscribe_msg))
# 启动WebSocket
ws = websocket.WebSocketApp(
"wss://stream.binance.com:9443/ws",
on_open=on_open,
on_message=on_message,
on_error=on_error,
on_close=on_close
)
ws.run_forever()
4.3.2 推送频率控制
交易所不会无限制地推数据。一般有几种频率:
- 实时推送:有变化就推,延迟最低
- 固定间隔:比如每100ms推一次快照
- 增量推送:只推送变化的部分,减少数据量
我个人习惯用增量推送+定时快照的组合。增量推送保证实时性,定时快照用来校验数据一致性。万一增量数据丢了,快照能帮你恢复。
4.4 数据缓存策略
数据来了,不能直接扔给策略。你得先缓存起来,做预处理。为什么?因为行情数据太快了,策略处理不过来。你想想看,Level2数据每秒可能更新几百次,你的策略如果每次都要重新计算,CPU直接拉满。
4.4.1 缓存层级
我一般分三层缓存:
| 层级 | 存储介质 | 用途 |
|---|---|---|
| L1 内存缓存 | Python字典/Redis | 最新行情快照,毫秒级访问 |
| L2 本地缓存 | SQLite/InfluxDB | 分钟级历史数据,用于回测和分析 |
| L3 持久化存储 | ClickHouse/Parquet | 全量历史数据,用于模型训练 |
4.4.2 缓存更新策略
缓存不是存进去就不管了。你得有更新策略:
- 写时更新:每来一条数据,立即更新缓存。适合高频场景。
- 定时更新:每100ms批量更新一次。适合对实时性要求不高的场景。
- 懒更新:策略需要时才去拉最新数据。适合低频策略。
做高频,我推荐写时更新。虽然CPU开销大一点,但数据最新。不过要注意,写时更新要加锁,防止多线程同时写导致数据错乱。
4.4.3 数据去重与校验
交易所偶尔会推送重复数据。你如果不做去重,策略可能会重复下单。我一般用(交易对, 时间戳, 序号)作为唯一键来去重。
另外,数据校验也很重要。比如检查价格是否在合理范围内,成交量是否异常。我曾经遇到过交易所推送的价格突然变成0,如果没做校验,策略直接按0价格下单,后果不堪设想。
知识体系总览
下面这张图,把数据源接入的核心逻辑串起来了:
从交易所到你的策略,数据流就是上面这条链路。每一步都有坑,每一步都需要精心设计。
好了,数据源接入这块就讲到这里。记住一句话:数据是量化交易的血液,血液不干净,策略再强也是白搭。
公众号:蓝海资料掘金营,微信deep3321