第三章 交易所API基础:REST与WebSocket的抉择
做市引擎要跟交易所打交道,第一步就是搞懂API。说实话,我见过不少新手一上来就闷头写代码,结果被限流、断连搞得焦头烂额。这一章,咱们把REST和WebSocket这两个基础概念掰扯清楚。
3.1 REST API vs WebSocket API
先问个问题:你下单的时候,是希望「发个请求等结果」,还是「跟交易所保持一条专线随时收发」?
这就是REST和WebSocket最本质的区别。
| 对比维度 | REST API | WebSocket API |
|---|---|---|
| 通信模式 | 请求-响应(一问一答) | 全双工(随时推送) |
| 连接方式 | 短连接,每次请求新建TCP | 长连接,建立后保持 |
| 适用场景 | 下单、查询账户、获取历史数据 | 实时行情、订单簿更新、成交推送 |
| 延迟 | 较高(50-200ms) | 极低(1-10ms) |
| 频率限制 | 严格,通常每秒几次到几十次 | 宽松,数据主动推送 |
我个人习惯:做市引擎的核心逻辑——报价、撤单、再报价——全部走WebSocket。但查询账户余额、获取历史K线这些低频操作,用REST就够了。你想想看,如果每次查个余额都要开一条WebSocket长连接,那资源浪费太严重了。
核心原则:高频、实时 → WebSocket;低频、一次性 → REST。
3.2 获取交易所API Key
要调用交易所的API,你得先有一把「钥匙」——API Key。我在项目中遇到过有人把Key硬编码在代码里,结果代码传到GitHub上,几分钟内账户就被盗了。嗯,这里要注意。
获取流程大致如下:
- 登录交易所官网,进入API管理页面
- 创建API Key,通常需要设置权限(交易、查询、提币等)
- 绑定IP白名单(强烈建议!)
- 保存API Key和Secret Key
警告:Secret Key只在创建时显示一次!我曾经见过有人截图保存,结果截图被同步到云盘,泄露了。建议用密码管理器或离线存储。
拿到Key之后,怎么用?以币安为例,每次请求需要签名:
import hashlib
import hmac
import time
import requests
API_KEY = 'your_api_key'
SECRET_KEY = 'your_secret_key'
def sign_request(params):
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()
params['signature'] = signature
return params
# 查询账户余额
params = {
'timestamp': int(time.time() * 1000),
'recvWindow': 5000
}
signed_params = sign_request(params)
headers = {'X-MBX-APIKEY': API_KEY}
resp = requests.get('https://api.binance.com/api/v3/account',
headers=headers, params=signed_params)
print(resp.json())
这段代码里,timestamp和recvWindow是防重放攻击的关键。我建议recvWindow设成5000毫秒,太短容易超时,太长不安全。
3.3 构建基础的HTTP请求客户端
直接写requests.get()当然可以,但做市引擎里要处理成百上千次请求,必须封装一个健壮的客户端。
先看看我常用的基础框架:
import requests
from requests.adapters import HTTPAdapter
from urllib3.util.retry import Retry
class ExchangeClient:
def __init__(self, api_key, secret_key, base_url):
self.api_key = api_key
self.secret_key = secret_key
self.base_url = base_url
# 配置重试策略
retry_strategy = Retry(
total=3,
backoff_factor=1,
status_forcelist=[429, 500, 502, 503, 504]
)
adapter = HTTPAdapter(max_retries=retry_strategy)
self.session = requests.Session()
self.session.mount('https://', adapter)
self.session.mount('http://', adapter)
def _sign(self, params):
# 签名逻辑同上
pass
def request(self, method, endpoint, params=None):
url = self.base_url + endpoint
headers = {'X-MBX-APIKEY': self.api_key}
if params is None:
params = {}
params['timestamp'] = int(time.time() * 1000)
params = self._sign(params)
try:
resp = self.session.request(method, url,
headers=headers,
params=params,
timeout=5)
resp.raise_for_status()
return resp.json()
except requests.exceptions.RequestException as e:
# 我曾经踩过坑:异常只打印不处理,导致订单状态丢失
# 这里应该记录日志并触发告警
print(f'请求失败: {e}')
return None
# 使用示例
client = ExchangeClient(API_KEY, SECRET_KEY, 'https://api.binance.com')
balance = client.request('GET', '/api/v3/account')
print(balance)
避坑指南:我曾经把timeout设成30秒,结果某个交易所响应慢,线程池被占满,整个引擎卡死。后来改成5秒+重试,问题解决。记住:做市引擎里,超时比失败更可怕。
3.4 核心知识体系
下面这张图,是我做培训时必画的。它把REST和WebSocket在整个做市引擎中的位置说清楚了:
说白了,这张图就一个意思:做市引擎核心根据业务需求,把请求分发给REST或WebSocket,再转发到不同的交易所。我刚开始做的时候,把所有请求都塞进WebSocket,结果连接数爆炸,被交易所封了IP。后来学乖了,该用REST就用REST。
3.5 实战建议
最后给几个实在的建议:
- 先测试再上线:所有交易所都提供测试网(Testnet),用测试Key先跑通流程。我曾经在正式环境调试,不小心下了个市价单,亏了500U。
- 频率控制:REST请求一定要加限流。我习惯用令牌桶算法,每秒最多5次请求,超过就排队。
- 日志记录:每次请求的URL、参数、响应、耗时,全部记下来。排查问题的时候,日志就是你的救命稻草。
- 异常处理:网络超时、签名错误、余额不足……每种异常都要有对应的处理逻辑。别问我怎么知道的,都是血泪教训。
记住:API是连接你和交易所的桥梁。桥塌了,再好的策略也是白搭。把基础打牢,后面的路才走得稳。