数据源接入:WebSocket与REST API对比,交易所API鉴权
做价差套利,第一步就是搞定数据源。
数据源接不好,后面全是白搭。我见过太多团队,策略写得漂亮,结果数据延迟高了50毫秒,套利机会全没了。说白了,数据接入是套利系统的命门。
REST API:简单但不够快
REST API 大家都熟。发个 HTTP 请求,拿回 JSON 数据。简单直接。
但做套利,REST 有几个硬伤:
- 轮询延迟:你得不停地问交易所「有数据吗?」。问一次,等一次响应。这个来回少说几十毫秒。
- 请求频率限制:交易所不是傻子,你一秒发100次请求,直接封你IP。我在币安就吃过这个亏,策略跑着跑着突然断数据了,一查,被限流了。
- 数据不连续:两次请求之间,行情变了你都不知道。价差机会可能就在那几毫秒里溜走了。
那REST就完全没用吗?也不是。我个人习惯,用REST做三件事:
- 获取历史K线数据,做回测用
- 查询账户余额、持仓信息
- 作为WebSocket断线后的备用数据源
WebSocket:套利系统的标配
WebSocket 就不一样了。建立一次连接,交易所主动往你这边推数据。延迟低,实时性强。
你想想看,价差套利拼的是什么?就是谁先看到价差机会。WebSocket 能让你比别人快几十甚至上百毫秒。
我做过一个测试,同一台服务器,REST轮询平均延迟85ms,WebSocket平均延迟只有12ms。这73ms的差距,在套利世界里就是天壤之别。
核心结论:主数据流用WebSocket,辅助数据用REST。这是套利系统数据接入的黄金法则。
交易所API鉴权:别在这上面翻车
鉴权这事,看着简单,坑却不少。
大部分交易所用 API Key + Secret Key 的方式。流程大概是:
- 在交易所后台生成一对密钥
- 把请求参数按字典序排序
- 拼接成字符串,加上时间戳
- 用HMAC-SHA256签名
- 把签名放在请求头里发出去
听起来不复杂对吧?但细节决定成败。
时间戳同步
交易所对时间戳很敏感。你的服务器时间跟交易所时间差太多,请求直接拒掉。我曾经因为服务器时区没设对,折腾了一下午才找到原因。
建议:用NTP服务同步时间,最好每5分钟同步一次。
签名算法细节
不同交易所的签名规则略有不同。有的要求参数排序,有的不要求。有的用query string,有的用request body。
我踩过的坑:某交易所的签名要求把参数值做URL编码,但文档里只字未提。调试了整整两天才发现。
我的经验:写一个通用的签名模块,把不同交易所的差异抽象成配置。这样换交易所时,改配置就行,不用重写代码。
WebSocket的鉴权方式
WebSocket鉴权和REST不太一样。常见的有两种:
- 连接时带参数:在WebSocket URL后面加参数,比如
wss://api.exchange.com/ws?api_key=xxx×tamp=xxx&signature=xxx - 连接后发鉴权消息:先建立连接,然后发一条JSON消息,里面带上鉴权信息
我个人更推荐第二种。为什么?因为URL里带密钥,容易被日志系统记录下来,有安全隐患。
代码示例:WebSocket鉴权连接
下面是我常用的一个WebSocket鉴权模板,以币安为例:
import websocket
import hmac
import hashlib
import time
import json
def get_signature(params, secret_key):
query_string = '&'.join([f"{k}={v}" for k, v in sorted(params.items())])
signature = hmac.new(
secret_key.encode(),
query_string.encode(),
hashlib.sha256
).hexdigest()
return signature
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):
print("连接成功,发送鉴权消息...")
timestamp = int(time.time() * 1000)
params = {
"apiKey": "your_api_key",
"timestamp": timestamp
}
params["signature"] = get_signature(params, "your_secret_key")
auth_msg = {
"method": "AUTH",
"params": [params],
"id": 1
}
ws.send(json.dumps(auth_msg))
# 启动连接
ws = websocket.WebSocketApp(
"wss://ws-api.binance.com/ws-api/v3",
on_open=on_open,
on_message=on_message,
on_error=on_error,
on_close=on_close
)
ws.run_forever()
注意:生产环境中,密钥绝对不能硬编码在代码里。用环境变量或者专门的密钥管理服务。我见过有人把密钥提交到GitHub,结果被机器人扫到,账户里的币全被转走了。
WebSocket vs REST:选型对比表
| 对比维度 | WebSocket | REST API |
|---|---|---|
| 延迟 | 低(10-20ms) | 高(50-200ms) |
| 实时性 | 强(推送模式) | 弱(轮询模式) |
| 连接开销 | 一次连接,持续使用 | 每次请求都要建连 |
| 数据完整性 | 可能丢包(需重连机制) | 请求-响应,可靠 |
| 适用场景 | 实时行情、订单簿 | 历史数据、账户查询 |
数据接入的整体架构
说了这么多,画张图总结一下。这是我做套利系统时常用的数据接入架构:
这张图里,WebSocket是主数据流,REST是备用。数据到了统一层后,做标准化处理,然后喂给策略引擎。这样设计的好处是,任何一个交易所的WebSocket断了,系统能自动切到REST,保证数据不中断。
避坑指南:我曾经只依赖WebSocket,结果交易所WebSocket服务挂了半小时,我的策略完全瞎了。后来加了REST备用通道,虽然慢一点,但至少不会断数据。记住,套利系统里,数据可用性比数据速度更重要。
嗯,数据接入这块就聊这么多。核心就三点:WebSocket做主数据源,REST做备用,鉴权细节别马虎。把这三点做好了,你的套利系统就稳了一半。