作为一名在量化交易领域摸爬滚打 5 年的工程师,我第一次接触 WebSocket 时也是一头雾水——服务器推送是什么?双向通信怎么实现?为什么我的行情数据总是慢半拍?这些问题困扰了我很久。今天我要用最通俗的语言,带你从零掌握 OKX WebSocket 实时行情的接入方法,保证看完就能跑通代码。

一、WebSocket 是什么?为什么获取行情必须用它?

想象你给餐厅打电话订餐:传统 HTTP 就像你每隔 5 秒打一次电话问「菜做好了吗」,而 WebSocket 就像服务员做好菜后主动给你送过来。WebSocket 建立连接后,服务器可以随时主动把数据「推」给你,不需要你一直去问。

在加密货币交易场景中,价格每秒可能变动几十次,用传统方式询问会:

所以,获取实时行情 WebSocket 是必选项。

二、准备工作:注册 OKX 账号并创建 API Key

第一步:注册 OKX 账号

访问 OKX 官网,使用手机号或邮箱完成注册。国内用户建议用手机号,验证更便捷。

(文字提示:截图位置 — 注册页面,填写手机号和验证码的区域)

第二步:创建 API Key

登录后按以下路径操作:我的面板 → API → 创建 API Key

(文字提示:截图位置 — 顶部导航栏「我的面板」下拉菜单)

创建时注意勾选以下权限:

(文字提示:截图位置 — API 权限设置页面,3 个可勾选复选框)

第三步:保存关键信息

创建成功后页面会显示 API Key 和 Secret Key,页面关闭后无法再查看 Secret Key,请立即复制保存到本地 txt 文件。建议格式如下:

API Key: a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6
Secret Key: A1B2C3D4E5F6G7H8I9J0K1L2M3N4O5P6Q7R8S9T0
Passphrase: your_passphrase_here

三、Python 环境准备

安装 Python(如果还没安装)

前往 Python 官网 下载最新版本。安装时务必勾选「Add Python to PATH」。

(文字提示:截图位置 — Python 安装界面,底部「Add Python to PATH」复选框)

安装行情库

打开命令行(Windows 按 Win+R,输入 cmd),依次执行:

pip install websocket-client requests

如果遇到权限报错,改用:

pip install websocket-client requests --user

四、连接 OKX WebSocket 获取实时行情

1. 获取实时成交数据

import websocket
import json
import pandas as pd
import threading

全局变量存储实时数据

latest_trades = [] def on_message(ws, message): """收到消息时的回调函数""" data = json.loads(message) # 成交数据的 event 为 trade if data.get('data'): for trade in data['data']: latest_trades.append({ 'inst_id': trade['instId'], 'price': float(trade['px']), 'size': float(trade['sz']), 'side': trade['side'], # buy 或 sell 'timestamp': trade['ts'] }) print(f"【成交】{trade['instId']} | 价格: {trade['px']} | 数量: {trade['sz']} | 方向: {trade['side']}") def on_error(ws, error): """错误处理""" print(f"WebSocket 错误: {error}") def on_close(ws, close_status_code, close_msg): """连接关闭时的回调""" print(f"连接已关闭,状态码: {close_status_code}") def on_open(ws): """连接建立时的回调 - 订阅频道""" # 订阅 BTC-USDT 的成交数据 subscribe_msg = { "op": "subscribe", "args": [ { "channel": "trades", "instId": "BTC-USDT" } ] } ws.send(json.dumps(subscribe_msg)) print("已订阅 BTC-USDT 成交数据")

建立连接

ws = websocket.WebSocketApp( "wss://ws.okx.com:8443/ws/v5/public", on_message=on_message, on_error=on_error, on_close=on_close, on_open=on_open )

启动连接(非阻塞方式)

ws_thread = threading.Thread(target=ws.run_forever) ws_thread.start()

运行 30 秒后关闭

import time time.sleep(30) ws.close() print(f"采集完成,共获取 {len(latest_trades)} 条成交记录")

2. 获取实时 K 线数据(1分钟)

import websocket
import json
import time

def on_message(ws, message):
    """处理接收到的 K 线数据"""
    data = json.loads(message)
    if data.get('data'):
        for candle in data['data']:
            print(f"""
【K线更新】
品种: {candle['instId']}
时间: {candle['ts']}
开盘: {candle['open']}
最高: {candle['high']}
最低: {candle['low']}
收盘: {candle['close']}
成交量: {candle['vol']}
""")

def on_open(ws):
    """订阅 1 分钟 K 线"""
    subscribe_msg = {
        "op": "subscribe",
        "args": [
            {
                "channel": "candle1m",
                "instId": "BTC-USDT"
            }
        ]
    }
    ws.send(json.dumps(subscribe_msg))
    print("已订阅 BTC-USDT 1分钟K线")

简化的连接方式

ws = websocket.WebSocketApp( "wss://ws.okx.com:8443/ws/v5/public", on_message=on_message, on_open=on_open ) print("开始接收 K 线数据(5秒后自动关闭)...") ws.run_forever(ping_timeout=20) time.sleep(5) ws.close()

3. 获取实时深度数据(订单簿)

import websocket
import json

def on_message(ws, message):
    """处理深度数据"""
    data = json.loads(message)
    if data.get('data'):
        depth = data['data'][0]
        asks = depth.get('asks', [])[:5]  # 卖盘前5档
        bids = depth.get('bids', [])[:5]  # 买盘前5档
        
        print("【订单簿深度】")
        print("-" * 40)
        print("卖盘(Asks):")
        for price, size in asks:
            print(f"  价格: {price:>12} | 数量: {size}")
        print("-" * 40)
        print("买盘(Bids):")
        for price, size in bids:
            print(f"  价格: {price:>12} | 数量: {size}")
        print("-" * 40)

def on_open(ws):
    """订阅深度数据(5档)"""
    subscribe_msg = {
        "op": "subscribe",
        "args": [
            {
                "channel": "books5",  # books5 = 5档深度
                "instId": "BTC-USDT"
            }
        ]
    }
    ws.send(json.dumps(subscribe_msg))

ws = websocket.WebSocketApp(
    "wss://ws.okx.com:8443/ws/v5/public",
    on_message=on_message,
    on_open=on_open
)

ws.run_forever(ping_timeout=20)

运行结果预览

执行上面任意一个脚本,你应该能看到类似这样的输出:

已订阅 BTC-USDT 成交数据
【成交】BTC-USDT | 价格: 67842.50 | 数量: 0.0152 | 方向: buy
【成交】BTC-USDT | 价格: 67842.30 | 数量: 0.0200 | 方向: sell
【成交】BTC-USDT | 价格: 67843.00 | 数量: 0.0100 | 方向: buy
...

(文字提示:截图位置 — 终端运行窗口,显示实时成交数据滚动)

五、常见报错排查

错误一:ConnectionRefusedError 连接被拒绝

错误信息:

ConnectionRefusedError: [WinError 10061] 由于目标计算机积极拒绝,无法连接。

原因分析:

解决方案:

# 方法1:检查地址是否正确

正确地址(现货/合约通用)

ws_url = "wss://ws.okx.com:8443/ws/v5/public"

方法2:测试网络连通性

在命令行执行:

ping ws.okx.com

方法3:配置代理(如果需要)

import os os.environ["HTTP_PROXY"] = "http://127.0.0.1:7890" os.environ["HTTPS_PROXY"] = "http://127.0.0.1:7890"

方法4:使用国内可访问的节点(如果 OKX 提供)

某些行情服务商会提供镜像节点

ws_url = "wss://wspush.okxcn.com:8443/ws/v5/public"

错误二:IllegalArgument 订阅格式错误

错误信息:

{'event': 'error', 'msg': 'Illegal argument: unknown channel type', 'code': '60021'}

原因分析:

解决方案:

# 常见 channel 类型对照表
channels = {
    "trades": "成交数据",
    "candle1m": "1分钟K线",
    "candle5m": "5分钟K线",
    "candle1H": "1小时K线",
    "books5": "5档深度",
    "books50": "50档深度",
    "tickers": "行情快照"
}

交易对格式对照表

inst_id_examples = { "BTC-USDT": "BTC/USDT 现货", "BTC-USDT-SWAP": "BTC/USDT 永续合约", "ETH-USDT-SWAP": "ETH/USDT 永续合约", "BTC-USD-230930": "BTC/USD 交割合约(到期日230930)" }

正确订阅示例

subscribe_msg = { "op": "subscribe", "args": [ { "channel": "trades", # 不要写成 "trade" 或 "TRADES" "instId": "BTC-USDT" # 不要写成 "BTC/USDT" 或 "BTC_USDT" } ] }

错误三:心跳超时导致连接断开

错误信息:

ConnectionClosedOK: connection is closed

或长时间无数据输出

原因分析:

解决方案:

import websocket
import threading
import time

class OkxWebSocketClient:
    def __init__(self):
        self.ws = None
        self.running = False
        
    def connect(self):
        """建立连接并自动重连"""
        while self.running:
            try:
                self.ws = websocket.WebSocketApp(
                    "wss://ws.okx.com:8443/ws/v5/public",
                    on_message=self.on_message,
                    on_open=self.on_open,
                    on_close=self.on_close,
                    on_error=self.on_error
                )
                # ping_timeout=25 非常重要!确保自动处理心跳
                self.ws.run_forever(ping_timeout=25, ping_interval=20)
            except Exception as e:
                print(f"连接异常: {e}")
            
            # 断连后等待 5 秒重试
            if self.running:
                print("5 秒后重新连接...")
                time.sleep(5)
    
    def on_open(self, ws):
        """订阅操作"""
        subscribe_msg = {
            "op": "subscribe", 
            "args": [{"channel": "trades", "instId": "BTC-USDT"}]
        }
        ws.send(json.dumps(subscribe_msg))
        print("连接成功,已订阅")
    
    def on_message(self, ws, message):
        print(f"收到数据: {message[:100]}...")
    
    def start(self):
        """启动客户端"""
        self.running = True
        self.thread = threading.Thread(target=self.connect)
        self.thread.start()
    
    def stop(self):
        """停止客户端"""
        self.running = False
        if self.ws:
            self.ws.close()

使用示例

client = OkxWebSocketClient() client.start()

运行 1 小时

time.sleep(3600) client.stop()

错误四:数据解析异常 JSONDecodeError

错误信息:

json.decoder.JSONDecodeError: Expecting value: line 1 column 1 (char 0)

原因分析:

解决方案:

def on_message(ws, message):
    """带容错的消息处理"""
    if not message:  # 忽略空消息
        return
    
    try:
        data = json.loads(message)
        # 处理订阅成功响应
        if data.get('event') == 'subscribe':
            print(f"订阅成功: {data.get('arg')}")
            return
        
        # 处理实际数据
        if data.get('data'):
            # 你的数据处理逻辑
            pass
            
    except json.JSONDecodeError:
        # 如果不是 JSON,可能是其他类型消息
        print(f"收到非JSON消息: {message[:50]}")
    except Exception as e:
        print(f"处理消息时出错: {e}")

六、数据处理实战:将行情存入 CSV

实时数据获取后,通常需要持久化存储用于后续分析。以下是完整的数据采集和存储方案:

import websocket
import json
import csv
from datetime import datetime
import threading
import time

class MarketDataCollector:
    def __init__(self, symbol="BTC-USDT", filename="market_data.csv"):
        self.symbol = symbol
        self.filename = filename
        self.data_buffer = []
        self.ws = None
        self.running = False
        self.setup_csv()
        
    def setup_csv(self):
        """初始化 CSV 文件"""
        with open(self.filename, 'w', newline='', encoding='utf-8') as f:
            writer = csv.writer(f)
            writer.writerow(['timestamp', 'datetime', 'symbol', 'price', 'size', 'side'])
    
    def save_to_csv(self):
        """定期将缓冲区数据写入 CSV"""
        while self.running:
            time.sleep(10)  # 每 10 秒保存一次
            if self.data_buffer:
                with open(self.filename, 'a', newline='', encoding='utf-8') as f:
                    writer = csv.writer(f)
                    writer.writerows(self.data_buffer)
                print(f"已保存 {len(self.data_buffer)} 条数据到 {self.filename}")
                self.data_buffer.clear()
    
    def on_message(self, ws, message):
        try:
            data = json.loads(message)
            if data.get('data'):
                for trade in data['data']:
                    ts = int(trade['ts'])
                    dt = datetime.fromtimestamp(ts/1000).strftime('%Y-%m-%d %H:%M:%S')
                    self.data_buffer.append([
                        trade['ts'],
                        dt,
                        trade['instId'],
                        trade['px'],
                        trade['sz'],
                        trade['side']
                    ])
        except Exception as e:
            print(f"数据处理错误: {e}")
    
    def on_open(self, ws):
        msg = {"op": "subscribe", "args": [{"channel": "trades", "instId": self.symbol}]}
        ws.send(json.dumps(msg))
        print(f"已订阅 {self.symbol} 成交数据")
    
    def start(self):
        """启动采集"""
        self.running = True
        self.ws = websocket.WebSocketApp(
            "wss://ws.okx.com:8443/ws/v5/public",
            on_message=self.on_message,
            on_open=self.on_open
        )
        self.ws_thread = threading.Thread(target=self.ws.run_forever, kwargs={'ping_timeout': 25})
        self.save_thread = threading.Thread(target=self.save_to_csv)
        
        self.ws_thread.start()
        self.save_thread.start()
    
    def stop(self):
        """停止采集"""
        self.running = False
        self.ws.close()
        print("采集已停止")

使用示例:采集 30 分钟 BTC 成交数据

collector = MarketDataCollector("BTC-USDT", "btc_trades.csv") collector.start() print("开始采集数据(将运行 30 分钟)...") time.sleep(1800) collector.stop()

七、高阶用法:订阅多币种多周期

一个连接可以同时订阅多个频道,大幅提升效率:

import websocket
import json

def on_message(ws, message):
    data = json.loads(message)
    if data.get('data'):
        candle = data['data'][0]
        print(f"[{candle['instId']}] {candle['ts']} | O:{candle['open']} H:{candle['high']} L:{candle['low']} C:{candle['close']}")

def on_open(ws):
    # 同时订阅多个币种的多个周期
    subscribe_msg = {
        "op": "subscribe",
        "args": [
            {"channel": "candle1m", "instId": "BTC-USDT"},
            {"channel": "candle1m", "instId": "ETH-USDT"},
            {"channel": "candle5m", "instId": "BTC-USDT"},
            {"channel": "candle1H", "instId": "ETH-USDT"},
            {"channel": "candle1H", "instId": "SOL-USDT"},
        ]
    }
    ws.send(json.dumps(subscribe_msg))
    print("已订阅 5 个频道")

ws = websocket.WebSocketApp(
    "wss://ws.okx.com:8443/ws/v5/public",
    on_message=on_message,
    on_open=on_open
)

ws.run_forever(ping_timeout=25)

八、性能优化建议

当数据量增大时(每秒数百条消息),需要注意以下优化:

1. 使用线程安全队列

from queue import Queue
from threading import Thread

data_queue = Queue(maxsize=10000)

def data_processor():
    """独立线程处理数据,避免阻塞接收"""
    while True:
        data = data_queue.get()
        # 写入数据库、计算指标等耗时操作
        process_data(data)

def on_message(ws, message):
    # 只做最小化处理,快速入队
    data_queue.put(message)

2. 批量写入数据库

# 使用 SQLAlchemy 批量插入
from sqlalchemy import create_engine, Table, Column, Float, String, Integer
from sqlalchemy.orm import sessionmaker

engine = create_engine('sqlite:///trades.db', pool_size=10)
Session = sessionmaker(bind=engine)

批量写入(每 100 条或每 5 秒写入一次)

batch = [] while True: if len(batch) >= 100: session.bulk_insert_mappings(Trade, batch) session.commit() batch.clear()

九、下一步:用 AI 分析实时行情

获取到实时行情后,你可以结合大模型 API 进行智能分析,比如识别价格形态、生成交易信号、自动生成每日行情报告。

import requests

def analyze_market_with_ai(current_price, price_change_24h, volume_24h):
    """
    使用 HolySheep AI 分析当前市场状态
    HolySheep 汇率优势:¥1=$1无损,注册送免费额度
    官方地址:https://www.holysheep.ai
    """
    response = requests.post(
        "https://api.holysheep.ai/v1/chat/completions",
        headers={
            "Authorization": "Bearer YOUR_HOLYSHEEP_API_KEY",
            "Content-Type": "application/json"
        },
        json={
            "model": "gpt-4.1",
            "messages": [
                {
                    "role": "system",
                    "content": "你是一个专业的加密货币分析师,用简洁的语言给出市场分析。"
                },
                {
                    "role": "user", 
                    "content": f"""分析以下 BTC 市场数据:
                    当前价格:${current_price}
                    24小时涨跌:{price_change_24h}%
                    24小时成交量:{volume_24h} BTC
                    请给出简短的走势判断和操作建议。"""
                }
            ],
            "temperature": 0.7,
            "max_tokens": 500
        }
    )
    
    if response.status_code == 200:
        result = response.json()
        return result['choices'][0]['message']['content']
    else:
        return f"API 请求失败: {response.status_code}"

使用示例

analysis = analyze_market_with_ai( current_price=67842.50, price_change_24h=2.35, volume_24h=15234.56 ) print("AI 分析结果:") print(analysis)

推荐使用 立即注册 HolySheep AI,原因如下:

十、常见问题 FAQ

Q1:WebSocket 连接数有上限吗?

OKX 对每个账号限制最多 25 个 WebSocket 连接,每个连接最多订阅 100 个频道。建议使用单个连接订阅所有需要的频道。

Q2:数据是否有延迟?

OKX 官方标注延迟在 100ms 以内,实测国内节点通常在 20-50ms。如果延迟过高,检查网络或考虑使用代理。

Q3:能否获取历史 K 线数据?

WebSocket 只推送实时数据,历史数据需要调用 REST API 的 /api/v5/market/history-candles 接口。

Q4:连接断开后会自动重连吗?

默认不会自动重连。建议在代码中实现自动重连机制(参考本文第五节的代码示例)。

Q5:支持哪些交易产品?

WebSocket 支持现货、币币杠杆、永续合约、交割合约、期权等全品类行情。

总结

本文从零讲解了 OKX WebSocket 实时行情 API 的接入方法,涵盖:

完整代码可以直接复制运行,修改 instId 即可切换不同的交易对。入门后可以进一步研究 OKX REST API 实现下单交易、账户管理等功能,构建完整的量化交易系统。

如果你的量化策略需要 AI 辅助分析或需要更稳定的行情服务,推荐使用 免费注册 HolySheep AI,享受 ¥1=$1 无损汇率和国内直连 50ms 以内的极速体验

👉 免费注册 HolySheep AI,获取首月赠额度