3、交易所API对接:API密钥管理、REST API与WebSocket的区别、获取行情数据、下单与撤单
做量化交易,第一步就是跟交易所打交道。说白了,你得让程序能替你去交易所里看行情、下订单。这一步要是没走稳,后面全是白搭。我见过不少新手,策略写得挺漂亮,结果API密钥泄露了,或者WebSocket断连了没重连,亏得莫名其妙。嗯,这节课咱们就把这些坑一个个填上。
3.1 API密钥管理——你的数字身份证
每个交易所都会给你一对密钥:API Key 和 Secret Key。API Key 相当于你的用户名,Secret Key 相当于你的密码。但注意,它俩的用法跟普通网站登录不太一样。
我个人习惯用环境变量来管理密钥。这样代码里只写变量名,密钥本身存在服务器上。举个例子:
import os
from ccxt import binance
api_key = os.getenv('BINANCE_API_KEY')
secret_key = os.getenv('BINANCE_SECRET_KEY')
exchange = binance({
'apiKey': api_key,
'secret': secret_key,
})
你想想看,如果代码里直接写死了密钥,哪天不小心把代码发到群里,那画面……我曾经有个学员,把密钥写在了 Jupyter Notebook 里,然后分享给了同事,结果账户被刷了一堆垃圾订单。从那以后,他再也不敢偷懒了。
3.2 REST API 与 WebSocket 的区别
这两个东西,是程序跟交易所通信的两种方式。我简单给你捋一捋:
| 特性 | REST API | WebSocket |
|---|---|---|
| 通信方式 | 请求-响应(一问一答) | 全双工(实时推送) |
| 延迟 | 较高(几十到几百毫秒) | 极低(几毫秒) |
| 适用场景 | 下单、撤单、查询余额 | 实时行情、深度、成交推送 |
| 连接开销 | 每次请求都要建立连接 | 长连接,只需建立一次 |
为什么会这样?REST API 就像你打电话问朋友“现在比特币多少钱?”,朋友查一下再告诉你。每次都要拨号、等待、挂断。而 WebSocket 就像你一直开着免提,朋友那边价格一变,直接喊给你听。
所以,获取实时行情用 WebSocket,下单撤单用 REST API。这是行业惯例。我在做高频策略时,WebSocket 的延迟优势特别明显。有一次我对比过,同样一个 tick 数据,REST 拉取比 WebSocket 慢了将近 200 毫秒——这时间足够市场翻几个来回了。
3.3 获取行情数据
行情数据分几种:Ticker(最新价)、Order Book(深度图)、K线(蜡烛图)。咱们一个一个来。
3.3.1 用 REST API 获取 Ticker
ticker = exchange.fetch_ticker('BTC/USDT')
print(ticker['last']) # 最新成交价
print(ticker['bid']) # 买一价
print(ticker['ask']) # 卖一价
print(ticker['volume']) # 24小时成交量
这个接口很简单,但注意频率限制。大部分交易所每秒最多请求 10-20 次。你要是写个死循环去刷,很快就会被封 IP。我建议加个 time.sleep(0.1) 或者用异步请求。
3.3.2 用 WebSocket 订阅实时行情
这里我以 Binance 为例,用 websocket 库来订阅 BTC/USDT 的深度数据:
import websocket
import json
def on_message(ws, message):
data = json.loads(message)
print(data['bids'][0]) # 买一档
print(data['asks'][0]) # 卖一档
def on_error(ws, error):
print(f"连接出错: {error}")
def on_close(ws, close_status_code, close_msg):
print("连接关闭,准备重连...")
def on_open(ws):
print("连接成功")
# 订阅深度数据
subscribe_msg = {
"method": "SUBSCRIBE",
"params": ["btcusdt@depth20"],
"id": 1
}
ws.send(json.dumps(subscribe_msg))
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()
3.4 下单与撤单
下单是核心操作。我一般用 REST API 来做,因为下单需要签名认证,WebSocket 虽然也能做,但不如 REST 稳定。
3.4.1 限价单
order = exchange.create_limit_buy_order(
symbol='BTC/USDT',
amount=0.01,
price=50000
)
print(order['id']) # 订单ID,后面撤单要用
限价单的意思是:我只在 50000 这个价格买,不到这个价我就不买。适合做挂单策略。
3.4.2 市价单
order = exchange.create_market_buy_order(
symbol='BTC/USDT',
amount=0.01
)
市价单就是“不管多少钱,马上给我买进来”。适合抢单,但滑点可能比较大。我一般只在行情剧烈波动时用市价单,平时还是限价单更稳妥。
3.4.3 撤单
exchange.cancel_order(
id='订单ID',
symbol='BTC/USDT'
)
撤单很简单,但要注意:撤单请求也可能失败。比如订单已经成交了,你再撤单就会报错。所以最好先查一下订单状态:
order_status = exchange.fetch_order(id='订单ID', symbol='BTC/USDT')
if order_status['status'] == 'open':
exchange.cancel_order(id='订单ID', symbol='BTC/USDT')
else:
print("订单已成交,无需撤单")
3.5 本章知识体系图
下面这张图帮你理清 API 对接的整体脉络:
嗯,API 对接这部分,说白了就是三个事:管好密钥、选对通信方式、控制好频率。你把这些基础打牢了,后面写策略才能放心跑实盘。我当年第一次对接交易所时,光调试 WebSocket 重连就花了一整天,但后来再也没有因为断连出过问题——这时间花得值。
公众号:蓝海资料掘金营,微信deep3321