跳到主要内容

WebSocket

连接入口:GET /api/v1/ws · 12 个推送主题(交易 / 行情 / 链与区块三类) 非 JSON-RPC over WS · 订阅/推送协议

统一协议:链读写与读侧主题共用一套 WebSocket:GET /api/v1/ws + {"op":"subscribe","topics":[...]} 订阅制。旧读侧协议(method/subscriptionblock_ingested 自动推送、/api/v1/ws/hl、HL 格式帧)已删除——见 [接口变更清单 §5](changelog.md#5-websocket-统一破坏性。

1. 连接

Upgrade GET /api/v1/ws。连接后发送订阅消息,每块收到增量推送。可随时重发订阅变更主题。

服务端参数:

参数
最大消息/帧大小65536 字节
心跳间隔15 秒
空闲超时30 秒(无消息即断开)
最大并发连接1024
单次 send 超时5 秒(慢消费者断开)

2. 订阅协议

2.1 订阅请求

{
"op": "subscribe",
"topics": ["block", "book.BTC-USDT", "trades.ETH-USDT", "account.0x1111...0000"]
}
字段类型说明
opString固定 "subscribe"
topicsString[]主题列表

2.2 订阅确认

全部接受:

{ "op": "subscribed", "topics": ["block", "book.BTC-USDT"] }

部分拒绝(未知/非法主题不产生 error 帧,而是返回到 rejected 数组):

{ "op": "subscribed", "topics": ["block"], "rejected": ["unknown topic `foo.bar`"] }

rejected 为空时省略该字段。

2.3 退订请求

增量退订:只移除指定主题,其余主题保持订阅。subscribe 仍是全量替换语义,二者可混用(例如先 subscribe 全量替换,再 unsubscribe 增量清理)。连接的主题集合清空后连接保持打开,可继续接收后续 subscribe 帧。

{ "op": "unsubscribe", "topics": ["book.BTC-USDT", "trades.ETH-USDT"] }
字段类型说明
opString固定 "unsubscribe"
topicsString[]要移除的主题列表(无需是当前已订阅的子集)

确认帧 op"unsubscribed",结构与订阅确认一致;rejected 同样列出未知/非法主题。

2.4 协议错误

无效 JSON / 非 subscribe / unsubscribe op:

{ "op": "error", "message": "invalid json: ..." }

通用帧字段

  • seq:每帧 JSON 自带单调递增序号(推送、订阅确认均有)。用于对端检测丢帧;
  • gap:服务端检测到向该连接的推送积压丢帧(outbox FIFO 满)时下发 { "type": "gap", "seq": N }客户端收到 gap 应重连并重拉 HTTP 快照(或按需补拉), 不可静默忽略。

主题一览

主题推送时机初始推送
主题推送时机初始推送
--------------------------
block每块订阅后首块
book.{symbol}[.{depth}]盘口变化订阅即推全量快照。{depth} 省略或为 0 时取默认深度 50 档;显式 depth 上限 500(超出钳到 500)
trades.{symbol}有成交
account.{hex}账户被触及,或每 3 秒兜底订阅即推全量快照
external_quote.{symbol}部分市场
bbo.{symbol}最优价变化订阅即推初始快照
userFills.{hex}用户有成交
orderUpdates.{hex}订单变更
triggerUpdates.{hex}触发单 / OCO 变更
marksmark 变化订阅即推全量快照
blocks.live每 ingest 一个新区块(读侧)订阅后每块推全量(block + envelopes + 水位)
candles.{symbol}.{interval}K 线柱更新(读侧)订阅即推当前 open bucket

交易与账户主题

account.{hex}

账户余额 / 持仓 / 衍生指标推送。两条路径格式完全一致,前端无需区分来源:

  1. 事件驱动:账户在本块被 touch(余额 / 持仓 / 挂单 IM 等变化)时立即推送。返佣分发(Core::RebatePaid / BalanceChanged{RebateToInviter/RebateToInvitee})会 touch 交易者邀请者双方——须分别订阅 account.{trader}account.{inviter} 才能实时看到返佣入账(事件明细见 events.md §2)。
  2. 定时兜底:连接存续且已订阅时,每 3 秒对全部已订阅地址全量推送一次(mark 变动导致 uPnL / 强平价变化但账户未 touch 时仍能更新)。

订阅 / 重订阅时立即推一份当前快照(与上述格式相同)。

{
"topic": "account.0x1111222233334444555566667777888899990000",
"address": "0x1111222233334444555566667777888899990000",
"height": 12346,
"timestamp_ms": 1717200001000,
"balance": "100000.000000",
"nonce": 42,
"role_mask": 1,
"account_value": "101117.250000",
"total_margin_used": "4861.725000",
"total_notional": "48617.250000",
"withdrawable": "96255.525000",
"cross_cash_available": "95138.275000",
"cross_trading_available": "96255.525000",
"positions": {
"1": {
"symbol": "BTC-USDT",
"mark_price": "97225.00",
"size": "0.50000",
"entry_vwap": "95000.00",
"margin_mode": "Cross",
"leverage": 10,
"isolated_margin": "0",
"margin_used": "4861.725000",
"unrealized_pnl": "1117.250000",
"notional": "48617.250000",
"liquidation_price": "87450.00",
"roe": "0.2298"
}
}
}
字段类型说明
topicString主题名
addressAddress20账户地址
heightu64所在块高
timestamp_msu64块时间戳
balanceString余额(SCALE_6)
nonceu64当前 nonce
role_masku64角色位掩码
account_valueString总价值 = balance + Σ(uPnL)(SCALE_6);mark==0 的市场 uPnL 计 0
total_margin_usedString已用保证金(SCALE_6)
total_notionalString总名义价值(SCALE_6)
withdrawableString展示用近似值 = max(0, account_value − total_margin_used)(SCALE_6,含 uPnL)。≠ 链上提现 / 划逐仓上限;见 write-actions §7.0
cross_cash_availableString全仓 cash 可用 = balance − Σ(Cross IM)(SCALE_6,不含 uPnL)。链上 admission 口径;可为负
cross_trading_availableString全仓交易可用 = cross_cash_available + Cross uPnL(SCALE_6)。链上 admission 口径;可为负
positionsMap<MarketId, PositionResponse>持仓映射(字段同 getAccount 的 PositionResponse)。仅含已开仓(size != 0)的市场,与 HTTP getAccount 同口径。liquidation_price 公式见 下单预估与强平价

聚合字段 account_value/total_margin_used/total_notional/withdrawable 与 HTTP getAccount 同源(服务端复用同一 account_margin_summary 原语),可用于账户概况展示下单 / 提现能否成功须按 write-actions §7.0 链上 admission 口径判断(withdrawable 含 uPnL,链上提现不含)。agents/dms_deadline_ms 仍仅在 HTTP getAccount 提供(极少变动,不进逐块/3 秒推送)。

userFills.{hex}

用户成交实时推送。

{
"topic": "userFills.0x1111222233334444555566667777888899990000",
"address": "0x1111222233334444555566667777888899990000",
"height": 12346,
"timestamp_ms": 1717200001000,
"fills": [{
"block_height": 12346,
"event_seq": 3,
"timestamp_ms": 1717200001000,
"market_id": 1,
"symbol": "BTC-USDT",
"order_id": 1001,
"client_order_id": "my-cloid-1",
"price": "97234.50",
"qty": "0.50000",
"notional": "48617.250000",
"fee": "24.308625",
"is_taker": true,
"aggressor_side": "Bid"
}]
}
字段类型说明
fills[].block_heightu64成交块高
fills[].event_sequ64块内事件序号
fills[].timestamp_msu64落块时间戳(毫秒)
fills[].market_idu32市场 ID
fills[].symbolString市场显示名
fills[].order_idu64该用户侧订单 ID
fills[].client_order_idString?客户端订单 ID(open 簿 / closed archive 补全)
fills[].priceString成交价(px 精度)
fills[].qtyString成交量(sz 精度)
fills[].notionalString名义价值(SCALE_6)
fills[].feeStringgross fee(SCALE_6,≥ 0;不含 Layer ⑥.2 返佣拆分)
fills[].is_takerbool该用户是否为吃单方
fills[].aggressor_sideSide吃单方向

orderUpdates.{hex}

订单状态变更实时推送。accepted/resting 帧携带订单明细字段(symbol/side/price/ qty/tif/reduce_only/client_order_id),前端可仅凭本通道增量自洽维护「当前委托」 列表——无需回查 getAccountOrders

  • resting(含 in-place 改单后重挂簿、或 cancel-and-replace 后仍有剩余)→ 按 order_id upsert 一行(明细字段齐全 + remaining)。
  • done/expired → 按 order_id 摘除该行(不携带明细字段)。改价 / 改 TIF 等走撤单 + 重挂时,同块内可能先出现 doneaccepted/resting/userFills(Book B 改价 crossing 当前 quote 时可能即时成交)。
  • accepted → 下单或改单重挂意图确认(带 side/price/qty/tif不带 reduce_only/client_order_id,这两个随同块 resting 补齐);完全成交 / IOC 不挂簿的单不会再有 resting,前端无需将其计入挂单列表。
{
"topic": "orderUpdates.0x1111222233334444555566667777888899990000",
"address": "0x1111222233334444555566667777888899990000",
"height": 12346,
"timestamp_ms": 1717200001000,
"updates": [{
"block_height": 12346,
"event_seq": 3,
"order_id": 1001,
"market_id": 1,
"symbol": "BTC-USDT",
"kind": "resting",
"remaining": "1.00000",
"side": "Bid",
"price": "97100.00",
"qty": "1.00000",
"tif": "Gtc",
"reduce_only": false,
"client_order_id": "my-cloid-1"
}]
}
字段类型说明
updates[].block_heightu64事件块高
updates[].event_sequ64块内事件序号
updates[].order_idu64订单 ID
updates[].market_idu32市场 ID
updates[].symbolString交易对名
updates[].kindOrderUpdateKind"accepted" · "resting" · "done" · "expired"
updates[].remainingString剩余量(sz 精度)
updates[].reasonString?仅 done/expired 时出现,DoneReason 标签(如 "Filled""Cancelled""AmendClampedAtFilled"
updates[].sideSide?仅 accepted/resting:"Bid" · "Ask"
updates[].priceString?仅 accepted/resting:限价(px 精度)
updates[].qtyString?仅 accepted/resting:原始下单数量(sz 精度)
updates[].tifTimeInForce?仅 accepted/resting:"Gtc" · "Ioc" · "Fok" · "PostOnly"
updates[].reduce_onlybool?仅 resting:是否仅减仓
updates[].client_order_idString?仅 resting 且下单时携带:客户端订单 ID

triggerUpdates.{hex}

触发单 / OCO(条件委托)生命周期实时推送。placed / amended 帧携带完整明细,前端可仅凭本通道 增量自洽维护「条件委托」列表——无需轮询 getTriggerOrders / getOcoPairs

  • placed / amended → 按 trigger_id upsert 一行(明细字段齐全)。
  • activated/cancelled/expired/fire_failed → 按 trigger_id 摘除该行;activated 表示已促升下单,结果挂单/成交分别走 orderUpdates / userFills
  • oco_placed/oco_resolved → 带 pair_idoco_placed 另带 client_pair_id、可选 parent_order_id);OCO 两腿的 详情由各腿自身的 triggerUpdates(Stop 腿)/ orderUpdates(限价腿)描述。
{
"topic": "triggerUpdates.0x1111222233334444555566667777888899990000",
"address": "0x1111222233334444555566667777888899990000",
"height": 12346,
"timestamp_ms": 1717200001000,
"updates": [{
"block_height": 12346,
"event_seq": 3,
"kind": "placed",
"market_id": 1,
"symbol": "BTC-USDT",
"trigger_id": 7001,
"side": "Ask",
"order_type": "limit",
"qty": "0.50000",
"trigger_price": "95000.00",
"trigger_direction": "Below",
"limit_price": "94900.00",
"tif": "Gtc",
"reduce_only": true,
"client_order_id": "sl-1",
"expires_at_ms": null
}]
}
字段类型说明
updates[].block_heightu64事件块高
updates[].event_sequ64块内事件序号
updates[].kindTriggerUpdateKind"placed" · "amended" · "activated" · "cancelled" · "expired" · "fire_failed" · "oco_placed" · "oco_resolved"
updates[].market_idu32市场 ID
updates[].symbolString交易对名
updates[].trigger_idu64?触发单 ID(OCO place/resolve 缺省)
updates[].pair_idu64?OCO 对 ID(仅 oco_placed/oco_resolved)
updates[].reasonString?终态原因:cancelledTriggerCancelReason"ByOwner" · "OnFailure" · "MarketDelisted" · "OcoSibling");oco_resolvedOcoResolveReason"OrderResolved" · "TriggerFired" · "ManualCancel" · "PairCancelled" · "TriggerExpired");fire_failedRejectReason
updates[].sideSide?仅 placed/amended:"Bid" · "Ask"
updates[].order_typeString?仅 placed/amended:"market"(IOC 吃穿)· "limit"
updates[].qtyString?仅 placed/amended:下单数量(sz 精度)
updates[].trigger_priceString?仅 placed/amended:触发线(px 精度)
updates[].trigger_directionTriggerDirection?仅 placed/amended:"Above" · "Below"
updates[].limit_priceString?仅 placed/amended 且 limit:限价(px 精度)
updates[].tifTimeInForce?仅 placed/amended
updates[].reduce_onlybool?仅 placed/amended
updates[].client_order_idString?仅 placed/amended 且下单时携带
updates[].expires_at_msu64?仅 placed/amended:GTD 过期时刻(毫秒);null = 永不过期
updates[].client_pair_idu64?oco_placed:客户端 OCO pair ID
updates[].parent_order_idu64?oco_placed:Bracket 关联的 entry 系统 order_id;无绑定时缺省

external_quote.{symbol}

部分市场的 oracle 报价逐块推送。唯一携带 last_price/volume 的通道(HTTP getMarket.latest_quote 中这两个字段恒为 null)。

{
"topic": "external_quote.BTC-USDT",
"symbol": "BTC-USDT",
"height": 12346,
"timestamp_ms": 1717200001000,
"quote": {
"bid_price": "97100.00",
"ask_price": "97300.00",
"mark_price": "97200.00",
"source_ts_ms": 1717200000000,
"quoter": "0x...",
"last_price": "97234.50",
"volume": "1.50000"
},
"market_stats": {
"long_size": "10.50000",
"short_size": "8.20000",
"net_size": "2.30000",
"oracle_counter_pnl": "150.000000",
"open_interest": "10.50000",
"open_interest_notional": "1021862.500000",
"fills_in_block": 5,
"block_height": 12346
}
}
字段类型说明
topicString主题名
symbolString交易对名
heightu64所在块高
timestamp_msu64块时间戳
quoteOracleQuoteResponse报价详情(bid_price/ask_price/mark_price 为 px 精度;last_price 为 px 精度;volume 为 sz 精度)
market_statsMarketStatsResponse?市场统计快照(字段见 getMarket 的 MarketStatsResponse)

行情主题

book.{symbol}[.{depth}]

盘口快照。depth 可选,默认 50;0 = 全深度。订阅即推初始快照;之后仅在可见盘口变化时全量推送。

{
"topic": "book.BTC-USDT",
"symbol": "BTC-USDT",
"height": 12346,
"timestamp_ms": 1717200001000,
"state_hash": "0x...",
"bids": [
{ "price": "97200.00", "qty": "1.50000", "cumulative_qty": "1.50000" },
{ "price": "97150.00", "qty": "3.20000", "cumulative_qty": "4.70000" }
],
"asks": [
{ "price": "97250.00", "qty": "2.10000", "cumulative_qty": "2.10000" }
]
}
字段类型说明
topicString主题名
symbolString交易对名
heightu64快照高度
timestamp_msu64块时间戳
state_hashHex32盘口状态哈希
bidsLevel[]买单档位:price(px)/qty(sz)/cumulative_qty(sz)
asksLevel[]卖单档位

trades.{symbol}

成交推送(taker 侧单行,每笔撮合一条)。

{
"topic": "trades.BTC-USDT",
"symbol": "BTC-USDT",
"height": 12346,
"timestamp_ms": 1717200001000,
"trades": [{
"block_height": 12346,
"event_seq": 3,
"timestamp_ms": 1717200001000,
"market_id": 1,
"price": "97234.50",
"qty": "0.50000",
"notional": "48617.250000",
"side": "Bid"
}]
}
字段类型说明
topicString主题名
symbolString交易对名
heightu64所在块高
timestamp_msu64块时间戳
trades[].block_heightu64成交块高
trades[].event_sequ64块内事件序号
trades[].timestamp_msu64成交时刻(块时间,毫秒;本帧内同块同值,与 HTTP getRecentTrades 逐笔对齐)
trades[].market_idu32市场 ID
trades[].priceString成交价(px 精度)
trades[].qtyString成交量(sz 精度)
trades[].notionalString名义价值(SCALE_6)
trades[].sideSidetaker 进攻方向("Bid"/"Ask");前端据此红绿染色

bbo.{symbol}

最优买卖价轻量推送。订阅即推初始快照;之后仅在最优价变化时推送。

{
"topic": "bbo.BTC-USDT",
"symbol": "BTC-USDT",
"height": 12346,
"timestamp_ms": 1717200001000,
"best_bid": "97200.00",
"best_ask": "97250.00"
}
字段类型说明
topicString主题名
symbolString交易对名
heightu64所在块高
timestamp_msu64块时间戳
best_bidString?买一价(px 精度);空盘时省略或 null
best_askString?卖一价(px 精度);空盘时省略或 null

marks

全市场标记价推送。订阅即推全量快照;之后仅推 mark 变化的市场。

{
"topic": "marks",
"height": 12346,
"timestamp_ms": 1717200001000,
"marks": {
"BTC-USDT": "97225.00",
"ETH-USDT": "3450.00"
}
}
字段类型说明
topicString固定 "marks"
heightu64所在块高
timestamp_msu64块时间戳
marksMap<String, String>symbol → mark_price(各自 px 精度)。注意是 mark(清算/uPnL 公允价),不是盘口中点 mid

candles.{symbol}.{interval}

K 线柱实时推送(读侧,替代旧 HL 方言 channel:"candle")。{symbol} 为完整 symbol 或前缀, {interval} 见 [K 线文档](market-data/candles.md:

{
"topic": "candles.BTC-USDT.1h",
"data": {
"t": 1717000020000,
"T": 1717003620000,
"s": "BTC-USDT",
"i": "1h",
"o": "50000.00",
"c": "50080.00",
"h": "50100.00",
"l": "49950.00",
"v": "45.5",
"n": 12
}
}

链与区块主题

block

每块推送块元数据。digest/state_root 0x 前缀(与 HTTP Hex32 不同)。

{
"topic": "block",
"height": 12346,
"timestamp_ms": 1717200001000,
"digest": "abc123...",
"state_root": "def456...",
"envelope_count": 15,
"event_count": 42
}

blocks.live

读侧新区块全量帧(替代旧读侧 WS 自动推送的 block_ingested)。订阅 blocks.live 后每块推送一次:

{
"topic": "blocks.live",
"block": {
"parent": "0x…",
"height": 12346,
"timestamp_ms": 1717000001000,
"digest": "0x…",
"envelope_count": 5,
"event_count": 42,
"state_root": "0x…",
"envelopes": []
},
"envelopes": [
{
"height": 12346,
"envelope_idx": 0,
"tx_hash": "0x…",
"signer": "0x…",
"nonce": 6,
"status": "accepted",
"action_kind": "PlaceOrder",
"market_id": 1,
"symbol": "BTC-USDT",
"action": { "PlaceOrder": {} },
"reason": null,
"timestamp_ms": 1717000001000
}
],
"watermark": 12346,
"block_count": 12347,
"node_tip": 12350,
"behind": 4
}
字段说明
block新区块头(block.envelopes 恒为空数组)
envelopes该区块完整 EnvelopeView[]
watermark / block_count / node_tip / behindGET /api/v1/status

主题解析规则