数据源接入:WebSocket与REST API对比,交易所API鉴权

做价差套利,第一步就是搞定数据源。

数据源接不好,后面全是白搭。我见过太多团队,策略写得漂亮,结果数据延迟高了50毫秒,套利机会全没了。说白了,数据接入是套利系统的命门。

REST API:简单但不够快

REST API 大家都熟。发个 HTTP 请求,拿回 JSON 数据。简单直接。

但做套利,REST 有几个硬伤:

  • 轮询延迟:你得不停地问交易所「有数据吗?」。问一次,等一次响应。这个来回少说几十毫秒。
  • 请求频率限制:交易所不是傻子,你一秒发100次请求,直接封你IP。我在币安就吃过这个亏,策略跑着跑着突然断数据了,一查,被限流了。
  • 数据不连续:两次请求之间,行情变了你都不知道。价差机会可能就在那几毫秒里溜走了。

那REST就完全没用吗?也不是。我个人习惯,用REST做三件事:

  • 获取历史K线数据,做回测用
  • 查询账户余额、持仓信息
  • 作为WebSocket断线后的备用数据源

WebSocket:套利系统的标配

WebSocket 就不一样了。建立一次连接,交易所主动往你这边推数据。延迟低,实时性强。

你想想看,价差套利拼的是什么?就是谁先看到价差机会。WebSocket 能让你比别人快几十甚至上百毫秒。

我做过一个测试,同一台服务器,REST轮询平均延迟85ms,WebSocket平均延迟只有12ms。这73ms的差距,在套利世界里就是天壤之别。

核心结论:主数据流用WebSocket,辅助数据用REST。这是套利系统数据接入的黄金法则。

交易所API鉴权:别在这上面翻车

鉴权这事,看着简单,坑却不少。

大部分交易所用 API Key + Secret Key 的方式。流程大概是:

  1. 在交易所后台生成一对密钥
  2. 把请求参数按字典序排序
  3. 拼接成字符串,加上时间戳
  4. 用HMAC-SHA256签名
  5. 把签名放在请求头里发出去

听起来不复杂对吧?但细节决定成败。

时间戳同步

交易所对时间戳很敏感。你的服务器时间跟交易所时间差太多,请求直接拒掉。我曾经因为服务器时区没设对,折腾了一下午才找到原因。

建议:用NTP服务同步时间,最好每5分钟同步一次。

签名算法细节

不同交易所的签名规则略有不同。有的要求参数排序,有的不要求。有的用query string,有的用request body。

我踩过的坑:某交易所的签名要求把参数值做URL编码,但文档里只字未提。调试了整整两天才发现。

我的经验:写一个通用的签名模块,把不同交易所的差异抽象成配置。这样换交易所时,改配置就行,不用重写代码。

WebSocket的鉴权方式

WebSocket鉴权和REST不太一样。常见的有两种:

  • 连接时带参数:在WebSocket URL后面加参数,比如 wss://api.exchange.com/ws?api_key=xxx×tamp=xxx&signature=xxx
  • 连接后发鉴权消息:先建立连接,然后发一条JSON消息,里面带上鉴权信息

我个人更推荐第二种。为什么?因为URL里带密钥,容易被日志系统记录下来,有安全隐患。

代码示例:WebSocket鉴权连接

下面是我常用的一个WebSocket鉴权模板,以币安为例:

import websocket
import hmac
import hashlib
import time
import json

def get_signature(params, secret_key):
    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()
    return signature

def on_message(ws, message):
    data = json.loads(message)
    # 处理行情数据
    print(f"收到数据: {data}")

def on_error(ws, error):
    print(f"连接错误: {error}")

def on_close(ws, close_status_code, close_msg):
    print("连接关闭,准备重连...")

def on_open(ws):
    print("连接成功,发送鉴权消息...")
    timestamp = int(time.time() * 1000)
    params = {
        "apiKey": "your_api_key",
        "timestamp": timestamp
    }
    params["signature"] = get_signature(params, "your_secret_key")
    
    auth_msg = {
        "method": "AUTH",
        "params": [params],
        "id": 1
    }
    ws.send(json.dumps(auth_msg))

# 启动连接
ws = websocket.WebSocketApp(
    "wss://ws-api.binance.com/ws-api/v3",
    on_open=on_open,
    on_message=on_message,
    on_error=on_error,
    on_close=on_close
)
ws.run_forever()

注意:生产环境中,密钥绝对不能硬编码在代码里。用环境变量或者专门的密钥管理服务。我见过有人把密钥提交到GitHub,结果被机器人扫到,账户里的币全被转走了。

WebSocket vs REST:选型对比表

对比维度 WebSocket REST API
延迟 低(10-20ms) 高(50-200ms)
实时性 强(推送模式) 弱(轮询模式)
连接开销 一次连接,持续使用 每次请求都要建连
数据完整性 可能丢包(需重连机制) 请求-响应,可靠
适用场景 实时行情、订单簿 历史数据、账户查询

数据接入的整体架构

说了这么多,画张图总结一下。这是我做套利系统时常用的数据接入架构:

交易所A 交易所B 交易所C WebSocket 实时数据流(行情、订单簿) REST API 备用数据源(断线重连时使用) 数据统一层(标准化、去重、缓存) 价差套利策略引擎

这张图里,WebSocket是主数据流,REST是备用。数据到了统一层后,做标准化处理,然后喂给策略引擎。这样设计的好处是,任何一个交易所的WebSocket断了,系统能自动切到REST,保证数据不中断。

避坑指南:我曾经只依赖WebSocket,结果交易所WebSocket服务挂了半小时,我的策略完全瞎了。后来加了REST备用通道,虽然慢一点,但至少不会断数据。记住,套利系统里,数据可用性比数据速度更重要。

嗯,数据接入这块就聊这么多。核心就三点:WebSocket做主数据源,REST做备用,鉴权细节别马虎。把这三点做好了,你的套利系统就稳了一半。

专注资料整理