WebSocket 行情推送
状态:已实现。
通过 WebSocket 由服务端推送的行情数据,采用 Binance Spot 的流格式。公开、无需鉴权,以按 IP 的连接数上限而非请求权重进行限流。
端点
| URL | 用途 | 帧格式 |
|---|---|---|
wss://api.rocky.exchange/ws/<stream> | 单个流 | 裸帧 |
wss://api.rocky.exchange/stream?streams=<a>/<b>/<c> | 组合流(以 / 分隔) | {"stream":"<name>","data":<frame>} |
沙盒主机见你的 集成包(Integration Pack)。
可用的流
| 流 | 频率 | 页面 |
|---|---|---|
<symbol>@depth5、@depth10、@depth20 | 每 250 ms 一次订单簿快照 | 有限档深度信息 |
<symbol>@trade | 每笔成交 | 逐笔交易 |
<symbol>@kline_1m | 有未收盘 K 线时每 1 秒一次,收盘时另推一次 | K 线与 Ticker |
<symbol>@ticker | 每 1 秒 | K 线与 Ticker |
<symbol>@depth(增量深度) | — | 空缺 |
流名
<symbol>@<kind>,其中 <symbol> 为小写化且保留连字符的市场交易对:
cbtc-usdcb@trade ✔
ceth-cbtc@depth10 ✔
CBTC-USDCB@trade ✖ uppercase anywhere is refused
cbtcusdcb@trade ✖ the hyphen is part of the name
cbtc-usdcb@depth ✖ diff depth is not implemented
cbtc-usdcb@kline_5m ✖ only kline_1m
这是与 Binance 命名唯一一处刻意的不同——Binance 的交易对没有分隔符(btcusdt@trade)。Rocky 保留连字符,因为去掉它会让不同的资产对发生冲突。帧本身携带规范的大写交易对("s":"CBTC-USDCB"),与所有 REST 响应一致。
交易对集合即实时的 exchangeInfo 列表(ledger.markets 中处于 active 状态的现货市场),每 60 秒刷新;已下市的市场在订阅时被拒绝。已下市交易对上已打开的 @depth* 流会被关闭(当连接上的所有流都已关闭时,连接随之结束);已打开的 @trade、@kline_1m 与 @ticker 流则只是不再有数据。
连接治理
| 限制 | 值 | 超出时的行为 |
|---|---|---|
| 每连接的流数 | 32(按请求数计,去重前) | 关闭 1008 |
| 每客户端 IP 的并发连接数 | 16(默认值;运营方可配置,豁免 IP 不受限) | upgrade 阶段返回 HTTP 429 {"code":-1003,…}——没有 101 |
| 服务端 ping 间隔 | 20 秒 | — |
| 容忍的静默时长 | 60 秒(连续三次未回 pong) | 服务端关闭连接 |
| 缓冲帧数 | 每个流 256 帧,外加每连接 256 帧的队列 | 慢消费者会跳过帧(见下文) |
- 服务端每 20 秒发送一次 WebSocket ping。请以 pong 回应(所有主流客户端库都会自动处理)。任何入站帧——pong、ping 或文本——都算作存活证明。
- 代理超时为 300 秒,因此一个空闲但持续 ping 的连接可以无限期保持。
- 停止读取的对端同样处理:某一帧在 60 秒内无法写出时,服务端关闭连接。
- 跟不上的连接不会对其他订阅者施加背压:它错过的帧会被丢弃,并从当前帧继续。对深度流来说这是正确的恢复方式(过时的订单簿毫无价值);对
@trade则意味着非常慢的客户端可能错过成交,应通过 REST 对账。
关闭码 1008 的原因
不合法的订阅会在 upgrade 之后以关闭帧(代码 1008,「policy violation」)拒绝,以便原因可读——否则浏览器客户端只能看到一个不透明的握手失败。
| 原因文本 | 成因 |
|---|---|
no streams requested | /stream 未带 streams=,或列表为空/全空。 |
too many streams: N requested, 32 allowed | 列表超过 32 项。 |
unknown stream: <name> | 未知交易对、不支持的类型、大小写错误、列表成员为空,或增量深度/不支持周期的名字。会引用出问题的名字(截断到 96 字节)。一个坏名字会拒绝整个列表。 |
协议说明
- 没有订阅/取消订阅协议。 流列表由 URL 固定。你发送的文本或二进制帧会被忽略(不算错误);要更改流,请打开新连接。
- 组合列表中的重复名字会被合并;每一帧你只收到一次。
- 组合流的帧在
stream中原样回显你订阅时使用的名字。 - 没有
listenKey/ 用户数据流;订单与账户状态只能通过 REST 获取。
示例
import asyncio, json, websockets # pip install websockets
async def main():
url = "wss://api.rocky.exchange/stream?streams=cbtc-usdcb@depth5/cbtc-usdcb@trade"
async with websockets.connect(url) as ws: # ping/pong handled by the library
async for raw in ws:
msg = json.loads(raw)
print(msg["stream"], msg["data"])
asyncio.run(main())
与 Binance 的差异
- 流名为小写带连字符(
cbtc-usdcb@trade)。 - 没有
SUBSCRIBE/UNSUBSCRIBE/LIST_SUBSCRIPTIONS消息;没有带id标记的响应。 - 没有
@aggTrade、@bookTicker、@miniTicker、!ticker@arr、!miniTicker@arr、@depth/@depth@100ms增量流、1m以外的@kline_<interval>,或@avgPrice。 - 没有用户数据流,也没有通过 WebSocket 下单的
/ws-api。 - 成交 id 为 UUID 字符串。
- 连接上限为每 IP 16 个(Binance:每 5 分钟 300 个);在 upgrade 阶段以 HTTP 429 拒绝。
- 深度流为 250 ms 快照(Binance:100 ms / 1000 ms),其
lastUpdateId是时间戳。