3、交易所API对接:API密钥管理、REST API与WebSocket的区别、获取行情数据、下单与撤单

做量化交易,第一步就是跟交易所打交道。说白了,你得让程序能替你去交易所里看行情、下订单。这一步要是没走稳,后面全是白搭。我见过不少新手,策略写得挺漂亮,结果API密钥泄露了,或者WebSocket断连了没重连,亏得莫名其妙。嗯,这节课咱们就把这些坑一个个填上。

3.1 API密钥管理——你的数字身份证

每个交易所都会给你一对密钥:API KeySecret Key。API Key 相当于你的用户名,Secret Key 相当于你的密码。但注意,它俩的用法跟普通网站登录不太一样。

⚠️ 警告:Secret Key 一旦泄露,别人就能用你的账户交易、提币。我建议你永远不要把密钥硬编码在代码里,更不要上传到 GitHub。

我个人习惯用环境变量来管理密钥。这样代码里只写变量名,密钥本身存在服务器上。举个例子:

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()
🔑 关键点:WebSocket 一定要写重连逻辑。网络波动是常态,我曾经在实盘中遇到过交易所 WebSocket 服务器半夜重启,如果没有自动重连,策略就变成了瞎子,一直按旧价格下单,亏了不少。

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("订单已成交,无需撤单")
⚠️ 注意:撤单接口也有频率限制。我曾经在策略里写了个循环,每秒撤单 50 次,结果被交易所限制了 IP 访问,整整 10 分钟无法下单。那 10 分钟里行情暴跌,我眼睁睁看着账户缩水。所以,控制撤单频率,建议每次撤单后至少等 200 毫秒。

3.5 本章知识体系图

下面这张图帮你理清 API 对接的整体脉络:

交易所API对接 API密钥管理 环境变量 + 权限控制 REST vs WebSocket 下单用REST,行情用WS 行情 & 下单撤单 Ticker/深度/限价/市价 不硬编码 · 不传Git 延迟对比 · 场景选择 频率限制 · 重连机制 核心:安全第一 · 稳定第二 · 速度第三

嗯,API 对接这部分,说白了就是三个事:管好密钥、选对通信方式、控制好频率。你把这些基础打牢了,后面写策略才能放心跑实盘。我当年第一次对接交易所时,光调试 WebSocket 重连就花了一整天,但后来再也没有因为断连出过问题——这时间花得值。

💡 最后提醒:实盘之前,一定要先用交易所的测试网(Testnet)跑一遍。测试网里的币是假的,但接口逻辑跟实盘一模一样。我每个新策略都会先在测试网跑一周,确认没问题再上实盘。别嫌麻烦,这能帮你省下真金白银。

公众号:蓝海资料掘金营,微信deep3321