3、交易所API对接:REST API与WebSocket API的区别、API密钥管理、获取行情数据、下单与撤单
做市交易框架的核心,说白了就是跟交易所打交道。你想想看,没有数据来源,策略就是瞎子;没有下单通道,策略就是瘸子。这一章,我们就来聊聊怎么跟交易所「对话」。
我个人习惯把交易所API分成两类:REST API 和 WebSocket API。很多新手上来就混着用,结果踩了不少坑。嗯,我们先把这个搞清楚。
REST API vs WebSocket API:到底选哪个?
先问个问题:你平时刷网页,点一下等结果,这是什么模式?这就是REST。你打开微信聊天,消息实时推送过来,这又是什么模式?这就是WebSocket。
放到交易所场景里,区别就很明显了:
| 对比维度 | REST API | WebSocket API |
|---|---|---|
| 通信方式 | 请求-响应(一问一答) | 全双工(实时推送) |
| 适用场景 | 下单、撤单、查询账户 | 订阅行情、深度、成交 |
| 延迟 | 较高(50-200ms) | 极低(1-10ms) |
| 连接方式 | 短连接,用完即断 | 长连接,保持心跳 |
| 资源消耗 | 低 | 较高(需维护连接) |
我在项目中遇到过一件事:有个同事用REST API去拉取tick级别的行情,每秒请求200次。结果呢?交易所直接把他IP封了。为什么?REST API本质上是为低频操作设计的,你拿它做高频数据流,肯定不行。
我的建议是:
- 行情数据、深度数据、成交数据 → 用WebSocket
- 下单、撤单、查询余额 → 用REST API
- 订单状态更新 → 用WebSocket监听(如果有的话)
API密钥管理:别让你的钱打水漂
说到密钥管理,我见过最离谱的事——有人把API密钥直接写在代码里,然后上传到GitHub公开仓库。结果呢?几分钟内账户被洗劫一空。嗯,这不是段子,是真事。
API密钥通常包含两部分:
- API Key:相当于你的用户名,公开也没太大关系
- Secret Key:相当于你的密码,绝对不能泄露
怎么管理?我分享几个实战经验:
- 环境变量:把密钥写在.env文件里,用python-dotenv加载。千万别硬编码。
- 权限最小化:交易所一般支持设置API权限。只开你需要的——比如只开交易权限,不开提币权限。
- IP白名单:如果你的服务器IP固定,一定要设置白名单。这样就算密钥泄露,别人也用不了。
- 定期轮换:我习惯每3个月换一次密钥。虽然麻烦,但安全第一。
代码示例:用Python加载密钥
import os
from dotenv import load_dotenv
load_dotenv()
API_KEY = os.getenv("EXCHANGE_API_KEY")
SECRET_KEY = os.getenv("EXCHANGE_SECRET_KEY")
# 千万别这样写:
# API_KEY = "your-api-key-here"
# SECRET_KEY = "your-secret-key-here"
获取行情数据:从深度到K线
行情数据是做市策略的「眼睛」。没有它,你就是在闭眼开车。
常见的行情数据类型:
- Ticker:最新价、24小时涨跌幅、成交量等概要信息
- Order Book(深度):当前所有买单和卖单的挂单情况
- Trade(成交):最近发生的每一笔交易
- K线(Candlestick):按时间聚合的开高低收数据
我个人习惯用WebSocket订阅深度和成交数据,用REST API拉取历史K线做回测。为什么?因为深度数据变化太快,REST根本跟不上节奏。
举个例子,用WebSocket订阅深度数据:
import websocket
import json
def on_message(ws, message):
data = json.loads(message)
# 处理深度数据
bids = data['bids'] # 买单
asks = data['asks'] # 卖单
print(f"最新深度: 买一 {bids[0][0]}, 卖一 {asks[0][0]}")
def on_error(ws, error):
print(f"连接出错: {error}")
def on_close(ws, close_status_code, close_msg):
print("连接关闭,尝试重连...")
def on_open(ws):
# 订阅深度数据
subscribe_msg = {
"op": "subscribe",
"args": ["depth.BTC-USDT"]
}
ws.send(json.dumps(subscribe_msg))
# 启动WebSocket连接
ws = websocket.WebSocketApp(
"wss://api.exchange.com/ws",
on_open=on_open,
on_message=on_message,
on_error=on_error,
on_close=on_close
)
ws.run_forever()
下单与撤单:REST API的拿手好戏
下单这事儿,用REST API最稳。为什么?因为下单需要确认——你到底有没有成交?成交了多少?这些信息需要明确的响应。
下单的基本流程:
- 构造订单参数(交易对、方向、价格、数量)
- 用API密钥签名请求
- 发送POST请求到交易所
- 解析响应,获取订单ID
- 用订单ID查询成交状态
代码示例:用REST API下单
import requests
import time
import hmac
import hashlib
import base64
def place_order(api_key, secret_key, symbol, side, order_type, price, quantity):
timestamp = int(time.time() * 1000)
# 构造请求参数
params = {
"symbol": symbol,
"side": side, # "buy" 或 "sell"
"type": order_type, # "limit" 或 "market"
"price": price,
"quantity": quantity,
"timestamp": timestamp
}
# 签名(不同交易所签名方式不同,这里以常见方式为例)
message = f"{timestamp}{symbol}{side}{order_type}{price}{quantity}"
signature = hmac.new(
secret_key.encode(),
message.encode(),
hashlib.sha256
).hexdigest()
headers = {
"X-API-KEY": api_key,
"X-SIGNATURE": signature,
"X-TIMESTAMP": str(timestamp)
}
response = requests.post(
"https://api.exchange.com/v1/order",
json=params,
headers=headers
)
if response.status_code == 200:
order_id = response.json()["order_id"]
print(f"下单成功,订单ID: {order_id}")
return order_id
else:
print(f"下单失败: {response.text}")
return None
# 使用示例
order_id = place_order(
api_key=API_KEY,
secret_key=SECRET_KEY,
symbol="BTC-USDT",
side="buy",
order_type="limit",
price=50000.0,
quantity=0.01
)
撤单就更简单了,只需要订单ID:
def cancel_order(api_key, secret_key, symbol, order_id):
timestamp = int(time.time() * 1000)
params = {
"symbol": symbol,
"order_id": order_id,
"timestamp": timestamp
}
# 签名过程同上...
response = requests.delete(
"https://api.exchange.com/v1/order",
json=params,
headers=headers
)
if response.status_code == 200:
print(f"撤单成功: {order_id}")
else:
print(f"撤单失败: {response.text}")
知识体系总览
说了这么多,我们来画个图,把这一章的核心逻辑串起来:
这张图把这一章的核心逻辑都串起来了。你想想看,左边是REST API负责的「稳」操作,右边是WebSocket负责的「快」数据,底部是贯穿始终的密钥管理。做市系统就是这么搭建起来的。
嗯,这一章的内容就到这里。API对接是基本功,但也是最容易出问题的地方。我见过太多人因为密钥泄露或者API选型错误而翻车。记住一句话:稳字当头,快字为辅。
公众号:蓝海资料掘金营,微信deep3321