4、交易所API对接:REST与WebSocket协议、鉴权机制、限频策略
做市引擎要跟交易所打交道,API对接就是那座桥。桥修不好,车就过不去。我见过太多团队,策略模型跑得漂亮,结果API一接就崩——要么连不上,要么被限频封了,要么鉴权失败。说白了,API对接是基本功,但也是最容易翻车的地方。
今天咱们就聊聊交易所API对接的三个核心问题:用什么协议、怎么证明你是谁、以及如何不被交易所拉黑。
4.1 REST vs WebSocket:什么时候用哪个?
交易所通常提供两种API:REST和WebSocket。很多人纠结选哪个,其实没那么复杂。
REST 是请求-响应模式。你问一句,它答一句。适合做查询、下单这种需要确认结果的操作。比如查账户余额、提交限价单,用REST就很合适。
WebSocket 是推送模式。你连上后,交易所主动给你推数据。适合做行情订阅、订单状态更新。你想想看,如果每秒要刷100次行情,用REST轮询,光请求头就够你受的。
我的经验: 我个人习惯把REST和WebSocket混着用。行情走WebSocket,下单走REST。为什么?因为WebSocket的鉴权一旦断了重连,中间可能漏掉订单状态更新,而REST每次请求都带鉴权,更可靠。
来看一个简单的对比:
| 特性 | REST | WebSocket |
|---|---|---|
| 通信模式 | 请求-响应 | 全双工推送 |
| 延迟 | 较高(每次握手) | 低(长连接) |
| 适用场景 | 下单、查询 | 行情、订单推送 |
| 连接数 | 可并发多连接 | 通常限制1-2个 |
4.2 鉴权机制:别让你的API Key裸奔
交易所的鉴权,说白了就是证明「你是你」。常见的做法是API Key + Secret Key + 签名。
流程大概是这样的:
- 你在交易所后台生成一对Key:API Key(公开)和Secret Key(保密)
- 发请求时,把API Key、时间戳、请求参数拼成一个字符串
- 用Secret Key对这个字符串做HMAC-SHA256签名
- 把签名塞进请求头里发出去
嗯,这里要注意:时间戳很重要。交易所通常会检查时间戳是否在5秒内,防止重放攻击。我遇到过有人服务器时间不准,结果所有请求都被拒了。
避坑指南: 我曾经把Secret Key直接写死在代码里,结果不小心提交到了GitHub。十分钟后,账户里的币被洗劫一空。从那以后,我所有密钥都走环境变量或密钥管理服务。
来看一段Python示例:
import hmac
import hashlib
import time
import requests
def sign_request(api_key, secret_key, params):
# 1. 加入时间戳
params['timestamp'] = int(time.time() * 1000)
# 2. 按字母序排序参数
sorted_params = sorted(params.items())
# 3. 拼接成字符串
query_string = '&'.join([f"{k}={v}" for k, v in sorted_params])
# 4. HMAC-SHA256签名
signature = hmac.new(
secret_key.encode(),
query_string.encode(),
hashlib.sha256
).hexdigest()
# 5. 加入请求头
headers = {
'X-API-Key': api_key,
'X-Signature': signature
}
return headers
# 使用示例
api_key = "your_api_key"
secret_key = "your_secret_key"
params = {'symbol': 'BTCUSDT', 'side': 'BUY', 'quantity': '0.01'}
headers = sign_request(api_key, secret_key, params)
response = requests.post(
'https://api.exchange.com/v1/order',
json=params,
headers=headers
)
4.3 限频策略:别把交易所惹毛了
每个交易所都有限频规则。你发请求太快,它就会给你返回429 Too Many Requests,甚至直接封IP。
限频通常分两种:
- IP限频: 每个IP每秒最多N次请求
- 账户限频: 每个API Key每秒最多M次请求
不同交易所的限频规则差别很大。币安是每分钟1200次权重请求,OKX是每秒100次。你想想看,如果没搞清楚规则,策略一跑起来就撞墙。
我的建议: 别把限频当成束缚,把它当成保护。我习惯在代码里实现一个令牌桶算法,自己先限流,而不是等交易所来限我。
令牌桶的Python实现:
import time
import threading
class TokenBucket:
def __init__(self, rate, capacity):
self.rate = rate # 每秒生成令牌数
self.capacity = capacity # 桶容量
self.tokens = capacity # 当前令牌数
self.last_time = time.time()
self.lock = threading.Lock()
def acquire(self, tokens=1):
with self.lock:
# 先补充令牌
now = time.time()
elapsed = now - self.last_time
self.tokens = min(
self.capacity,
self.tokens + elapsed * self.rate
)
self.last_time = now
# 检查是否有足够令牌
if self.tokens < tokens:
return False
self.tokens -= tokens
return True
# 使用:每秒最多10次请求
bucket = TokenBucket(rate=10, capacity=10)
def safe_request(url, params):
while not bucket.acquire():
time.sleep(0.1) # 等100ms再试
return requests.get(url, params=params)
4.4 知识体系总览
说了这么多,咱们用一张图把核心逻辑串起来:
4.5 实战中的那些坑
最后分享几个我踩过的坑:
- WebSocket断线重连: 交易所的WebSocket连接不是永久的。我遇到过连接断了半小时,行情停了,策略还在傻傻地发单。解决方案是加心跳检测,5秒没收到数据就重连。
- 签名参数顺序: 不同交易所对参数排序要求不一样。有的要按字母序,有的要按出现顺序。我建议写个通用签名函数,针对每个交易所做适配。
- 限频重置时间: 有些交易所的限频窗口是滑动窗口,有些是固定窗口。固定窗口的话,整点重置时会有瞬间的请求洪峰,要注意平滑处理。
一个小技巧: 我习惯在代码里加个API调用日志,记录每次请求的耗时和返回码。这样出了问题,翻日志就能定位是网络问题、鉴权问题还是限频问题。
好了,API对接这块就聊到这儿。记住一句话:对接交易所,慢就是快。把协议、鉴权、限频这三个基本功打扎实了,后面的策略实现才能稳如老狗。