WebSocket 基本信息 — 现货
ZTDX 现货 WebSocket 的连接指南、鉴权说明、通道列表及协议规则。
基础地址
| 环境 | WebSocket 地址 |
|---|---|
| 测试网 (Sepolia) | wss://api.prex.world/ws |
| 主网 | (尚未部署) |
现货 WebSocket 与合约 WebSocket 共用同一端点。单个连接可同时订阅任意组合的现货与合约通道。
连接说明
- 所有消息均为 JSON 文本帧(RFC 6455)。
- 每隔 30 秒发送一次
ping以保持连接。 - 单个连接支持多个并发订阅。
- 未知现货通道会返回
INVALID_CHANNEL错误;连接本身保持不断开。
连接示例
- JavaScript
- Python
const ws = new WebSocket('wss://api.prex.world/ws');
ws.onopen = () => {
console.log('Connected');
// Public channel — no auth needed
ws.send(JSON.stringify({
type: 'subscribe',
channel: 'spot:depth:DFUSDT',
}));
// Keep-alive
setInterval(() => {
if (ws.readyState === WebSocket.OPEN) {
ws.send(JSON.stringify({ type: 'ping' }));
}
}, 30000);
};
ws.onmessage = (event) => {
const msg = JSON.parse(event.data);
console.log('Received:', msg);
};
ws.onerror = (err) => console.error('WS error:', err);
ws.onclose = () => console.log('Disconnected');
import asyncio
import json
import websockets
async def main():
uri = 'wss://api.prex.world/ws'
async with websockets.connect(uri) as ws:
print('Connected')
# Public channel — no auth needed
await ws.send(json.dumps({
'type': 'subscribe',
'channel': 'spot:depth:DFUSDT',
}))
async def heartbeat():
while True:
await asyncio.sleep(30)
await ws.send(json.dumps({'type': 'ping'}))
asyncio.create_task(heartbeat())
async for message in ws:
msg = json.loads(message)
print('Received:', msg)
asyncio.run(main())
消息格式
客户端 → 服务端
type | 描述 | 需鉴权 |
|---|---|---|
auth | 通过 EIP-712 签名或 listenKey 进行鉴权 | — |
auth_token | 通过 JWT Token 进行鉴权 | — |
subscribe | 订阅通道(可附带内联 token) | 仅私有通道 |
unsubscribe | 取消订阅通道 | 否 |
ping | 心跳 | 否 |
服务端 → 客户端
type | 描述 |
|---|---|
auth_result | 鉴权结果(success: true/false) |
subscribed | 订阅确认 |
unsubscribed | 取消订阅确认 |
pong | ping 的响应 |
error | 错误帧(code + message) |
spot_depth_snapshot | 完整订单簿快照(订阅 spot:depth:* 时推送) |
spot_depth_diff | 自上次更新以来的变化档位(差分) |
spot_trade | 单笔公开成交 |
spot_ticker | 24 小时滚动统计 |
spot_kline_snapshot | 最新 K 线(订阅 spot:kline:* 时推送) |
spot_kline_update | 当前周期内的实时 OHLCV 更新 |
spot_user_order | 已鉴权用户的订单生命周期事件 |
spot_user_balance | 已鉴权用户的余额行变更 |
所有推送帧均按以下结构封装:
{
"type": "spot_xxx",
"channel": "spot:depth:DFUSDT",
"data": { ... }
}
鉴权
鉴权仅对私有通道(spot:user:orders、spot:user:balances)必须。公开行情通道无需鉴权。
支持三种方式(与合约 WebSocket 完全相同 — 完整载荷格式参见合约 WebSocket 基本信息)。最简单的是 JWT:
JWT(auth_token)
{ "type": "auth_token", "token": "eyJhbGciOiJIUzI1..." }
服务端响应:
{ "type": "auth_result", "success": true, "message": null }
失败时:
{ "type": "auth_result", "success": false, "message": "Invalid or expired token" }
EIP-712 签名(auth)
{
"type": "auth",
"address": "0xYourWalletAddress",
"signature": "0x...",
"timestamp": 1778400000
}
timestamp 为 UNIX 秒,须在服务器时间 5 分钟以内。
listenKey(auth)
{ "type": "auth", "listenKey": "a1b2c3d4e5f6..." }
通过 POST /fapi/v1/listenKey 获取 listenKey。长连接的 WebSocket 等同于隐式 keepalive — socket 保持开启时无需主动调用 PUT /fapi/v1/listenKey。
在 subscribe 中附带 Token
{ "type": "subscribe", "channel": "spot:user:orders", "token": "eyJhbGciOiJIUzI1..." }
服务端会在处理订阅前自动完成鉴权。
通道概览
| 通道 | 需鉴权 | 订阅时发快照 | 实时更新 |
|---|---|---|---|
spot:depth:{symbol} | — | 是 — 前 1000 档 | 每次订单簿变动推送差分 |
spot:trade:{symbol} | — | — | 每笔公开成交推送一条 |
spot:ticker:{symbol} | — | 是 — 当前 24 小时行情 | 仅成交时 |
spot:kline:{symbol}:{interval} | — | 是 — 最新 K 线 | 当前区间内每次成交后推送 |
spot:user:orders | 需鉴权 | — | 下单 / 成交 / 撤单 / 拒单 |
spot:user:balances | 需鉴权 | — | 每个受影响的 (token) 行 |
{symbol} 在 MVP 阶段为 DFUSDT。{interval} ∈ 1m / 5m / 15m / 1h / 4h / 1d — 参见 Enums。
订阅 / 取消订阅 / Ping
订阅
{ "type": "subscribe", "channel": "spot:depth:DFUSDT" }
响应:
{ "type": "subscribed", "channel": "spot:depth:DFUSDT" }
若该通道有快照,将在 subscribed 确认后立即推送。
取消订阅
{ "type": "unsubscribe", "channel": "spot:depth:DFUSDT" }
响应:
{ "type": "unsubscribed", "channel": "spot:depth:DFUSDT" }
Ping / Pong
{ "type": "ping" }
响应:
{ "type": "pong" }
快照与实时同步顺序
spot:depth:* — 需要同步
- 发送订阅 → 服务端将
spot_depth_snapshot加入队列,并开始缓冲实时spot_depth_diff事件。 - 用快照初始化本地订单簿。
- 丢弃所有
update_id_last <= snapshot.last_update_id的差分。 - 依到达顺序应用剩余差分。每个差分的
update_id_first应等于前一差分的update_id_last + 1。若出现间隙,说明帧被丢弃 — 重新订阅以获取新快照。
spot:ticker:* 与 spot:kline:* — 幂等,无需重新同步
每次推送均完整替换上一次的值。漏掉的推送会在数秒内被下一次推送覆盖。
spot:user:* — 先从 REST 初始化,再保持同步
不发送初始快照。鉴权后:
- 调用
GET /spot/orders初始化本地订单状态。 - 调用
GET /spot/balances初始化本地余额状态。 - 通过实时
spot_user_order和spot_user_balance推送保持状态同步。
背压
每个连接有独立的发 送队列,上限为 256 帧。若客户端消费速度过慢,队列溢出时帧将被静默丢弃。
客户端侧指标:深度差分的 update_id_first / update_id_last 出现间隙,说明帧被丢弃 — 重新订阅深度通道以重新同步。
服务端侧指标:内部广播通道溢出时,服务端日志中会出现 lagged 告警。
错误
{ "type": "error", "code": "INVALID_CHANNEL", "message": "Unknown channel: spot:wat" }
| 错误码 | 触发条件 |
|---|---|
INVALID_CHANNEL | 未知通道名(如 spot:depths:DFUSDT — 注意拼写错误)。 |
AUTH_REQUIRED | 未鉴权即订阅 spot:user:* 通道。 |
INVALID_MESSAGE | 帧无法解析为有效的客户端消息。 |
REST 级别错误码另见错误码。