欧易 · API 进阶
欧易 WebSocket 怎么接:ccxt.pro 订阅行情和订单,模拟盘地址与连接限制
REST 是你发一次请求、交易所回一次;WebSocket 是建一条长连接,订阅之后由交易所把更新推过来。欧易文档在 WebSocket 概述里写的是「强烈建议开发者使用WebSocket API获取市场行情和买卖深度等信息」。
两种方式差在四处。REST 轮询(fetch_)拿到的数据最多落后一个轮询间隔,WebSocket 推送(watch_)是服务端有更新就推。REST 每轮询一次算一次请求、计入接口限速,WebSocket 订阅一次之后不再为取数发请求。自己的订单,REST 要反复查才知道成交没有,订单频道在状态变化时推送。代价在代码上:REST 是同步的,好调试;WebSocket 是异步的,要处理保活和断线重连。
几分钟甚至几小时才检查一次的策略,REST 就够,少了异步和重连这层复杂度。值得换成 WebSocket 的情况是:要同时盯很多交易对、轮询开始撞限速;或者要在订单成交后马上接着挂下一单。
欧易 WebSocket 的三组地址和常用频道
欧易 API v5 的 WebSocket 有三个地址,实盘和模拟盘各一套(实盘、模拟盘):
| 地址 | 实盘 | 模拟盘 |
|---|---|---|
| 公共频道 | wss://ws.okx.com/ws/v5/public | wss://wspap.okx.com/ws/v5/public |
| 私有频道 | wss://ws.okx.com/ws/v5/private | wss://wspap.okx.com/ws/v5/private |
| 业务频道 | wss://ws.okx.com/ws/v5/business | wss://wspap.okx.com/ws/v5/business |
文档的规则是:订阅公有频道用公有服务的地址,订阅私有频道用私有服务的地址。常用的几个频道:
- 行情(tickers)、盘口深度(books)、逐笔成交(trades)、K 线(candle):公开的市场数据,不需要登录。
- 订单(orders)、持仓(positions)、账户(account):你自己账户的数据,订阅前要先登录,登录用的还是 apiKey、Passphrase 和用 SecretKey 算出的签名。
每个频道在文档里都标了它走哪个地址,自己直连时按那一栏填。用 ccxt.pro 则由库来选:它的 okx 实现里,频道名带 candle 的订阅发往 business 地址,其余公共行情走 public,私有频道走 private。
用 ccxt.pro 订阅行情(watch_ticker)
ccxt 的 WebSocket 部分叫 ccxt.pro,已并入 ccxt 主包。普通 ccxt 走 REST,用 fetch_ 系列;ccxt.pro 走 WebSocket,用 watch_ 系列。底层是长连接,代码要写成异步(async)的循环。
ccxt 的版本要在 4.5.87 及以上(2026 年 10 月 13 日发布):从这一版起,ccxt.pro 的 okx 连的是 443 端口;更早的版本还连 :8443,欧易公网从 2026 年 10 月 31 日起就停用这个端口了。本机是哪一版、怎么升级,见欧易 API 地址现在填哪个里 ccxt 那一节。
下面这段订阅 BTC/USDT 的行情,价格一更新就打印:
import asyncio
import ccxt.pro as ccxtpro # WS 版,watch_ 系列
async def main():
okx = ccxtpro.okx() # 公有行情不需要鉴权
symbol = 'BTC/USDT'
try:
while True:
ticker = await okx.watch_ticker(symbol) # 有新行情才返回
print(ticker['symbol'], ticker['last'])
finally:
await okx.close() # 退出时关掉长连接
asyncio.run(main())
和 REST 的区别在 await okx.watch_ticker(...):它挂在那儿等推送,有新行情才返回。放进 while True 循环,就是一个持续接收行情的流。结尾的 await okx.close() 用来主动关掉长连接。
订阅自己的订单(watch_orders):先连模拟盘
watch_orders 在你的委托单状态变化(部分成交、完全成交、撤单)时返回。它属于私有频道,要带凭证登录。下面的例子连的是模拟盘:凭证填模拟盘那把 Key,再调 set_sandbox_mode(True)。模拟盘的 Key 要在模拟环境里单独建,步骤见 欧易模拟盘怎么用。
import asyncio
import ccxt.pro as ccxtpro
async def main():
okx = ccxtpro.okx({
'apiKey': '模拟盘的_apiKey',
'secret': '模拟盘的_secret',
'password': '模拟盘的_Passphrase', # 私有频道要登录
})
okx.set_sandbox_mode(True) # 切到模拟盘
# 自检:地址里应当是 wspap,第二行应当打印 1;有一项不对就别往下跑
print(okx.urls['api']['ws'])
print(okx.headers.get('x-simulated-trading'))
try:
while True:
orders = await okx.watch_orders() # 订单状态有变化才返回
for o in orders:
print(o['symbol'], o['side'], o['status'], o.get('filled'))
finally:
await okx.close()
asyncio.run(main())
跑起来之后,在模拟交易界面里手动挂一笔单再撤掉,终端里就会打印出这笔订单的状态变化。set_sandbox_mode(True) 在这里把接口地址切到测试那一组(WebSocket 因此换成模拟盘的 wspap.okx.com),并给请求头加上 x-simulated-trading: 1。自检那两行分别核对地址和请求头。
确认逻辑没问题、要切实盘时,去掉 set_sandbox_mode(True),凭证换成实盘的 Key。实盘 Key 的权限只勾「读取」和「交易」,不勾「提现」,并绑定 IP 白名单,见 API 密钥最小权限。
连接限制、保活和断线重连
欧易文档的「连接」和「连接限制」两节给了这几条数字,写重连逻辑时要守住:
- 建连:3 次/秒,按 IP 计。
- 订阅次数:每个连接上,订阅、取消订阅、登录三类请求合计 480 次/小时。
- 30 秒规则:连接成功后 30 秒未订阅,或订阅后 30 秒内服务器没有向你推送数据,系统会自动断开连接。
- 同一频道的连接数:订单、账户、持仓等私有频道,以子账户为维度,每个频道最多 30 个连接。
保活靠的是一条内容为 ping 的字符串:N 秒(N 小于 30)没收到新消息就发 ping,等服务器回 pong。ccxt.pro 的 okx 实现替你做了这件事。自己用 websockets 之类的库直连时要另外写,写法见 欧易 WebSocket 为什么连上 30 秒就断开。
连接断了要能自动重连,重连后要重新订阅,并补查一次当前状态(重新拉一遍挂单和持仓),因为断线期间的推送收不到。写法是把 watch_ 调用包在 try/except 里,只接网络类异常,等待时间逐次加长,连续失败超过上限就退出。下面的例子连的是模拟盘:
import asyncio
import ccxt.pro as ccxtpro
MAX_FAILS = 5 # 连续失败超过 5 次就退出,不无限重试
async def resync(okx, symbol):
# 断线期间的推送收不到,用 REST 重新拉一遍挂单和持仓
open_orders = await okx.fetch_open_orders(symbol)
positions = await okx.fetch_positions()
print('补查:挂单', len(open_orders), '笔,持仓', len(positions), '条')
async def main():
okx = ccxtpro.okx({
'apiKey': '模拟盘的_apiKey',
'secret': '模拟盘的_secret',
'password': '模拟盘的_Passphrase',
})
okx.set_sandbox_mode(True)
symbol = 'BTC/USDT'
fails = 0
try:
while True:
try:
# 出错之后再次调用,由 ccxt.pro 重新建连并订阅
orders = await okx.watch_orders(symbol)
fails = 0 # 收到推送,计数清零
for o in orders:
print(o['symbol'], o['side'], o['status'])
except ccxtpro.NetworkError as e:
fails += 1
if fails > MAX_FAILS:
print('连续失败,退出:', e)
break
wait = min(2 ** fails, 30) # 2、4、8、16、30 秒
print('连接异常,', wait, '秒后重连:', e)
await asyncio.sleep(wait)
try:
await resync(okx, symbol) # 继续订阅之前先补查当前状态
except ccxtpro.NetworkError as e2:
print('补查失败,下一轮再试:', e2)
finally:
await okx.close()
asyncio.run(main())
这段代码只接 NetworkError。鉴权失败、参数错误这类异常没有接,会直接抛出来终止脚本,重试对它们没用。
断线之后别假设状态没变,先补查再继续:失联期间订单可能已经成交,价格也可能已经走远。这一步和 API 自动交易风控 里「重连后先查状态、再动作」是同一件事。
常见问题
欧易的订单频道最多能同时开多少个 WebSocket 连接?
按欧易 API 文档,以子账户为维度,订阅每个 WebSocket 频道的最大连接数是 30 个,受这条限制的有订单、账户、持仓、账户余额和持仓、爆仓风险预警、账户 greeks 这几个频道,不同频道分开计数。超出时,最新订阅的那条连接会先收到订阅成功,随后收到 channel-conn-count-error,表示平台终止了它的订阅。
用 ccxt.pro 订阅欧易 K 线,连的是哪个地址?
不用自己选。ccxt.pro 的 okx 实现里,频道名带 candle 的订阅发往 business 地址,条件单的订单频道也走 business;其余公共行情走 public,私有频道走 private。调用 set_sandbox_mode(True) 之后,这三条路径都换到模拟盘的 wspap 主机下。自己直连时,以文档里该频道标注的地址为准。
ccxt.pro 和普通 ccxt 是一回事吗?
ccxt.pro 是 ccxt 的 WebSocket 扩展,把各交易所的 WS 也封装成了统一接口。普通 ccxt 走 REST(fetch_ 系列),ccxt.pro 走 WS(watch_ 系列,比如 watch_ticker、watch_orders)。如今 ccxt.pro 已并入 ccxt 主包,装好 ccxt 后用异步的 watch_ 方法即可。底层是长连接,所以代码要写成异步循环。