跳到主要内容

用户余额

通道名

spot:user:balances

鉴权:必须。参见鉴权

描述

为已鉴权用户推送现货余额变更。服务端按钱包地址过滤推送,每个发生变更的 (token) 行发送一条消息。一笔同时涉及用户 DF 和 USDT 的成交,会分别针对每个代币发出两条独立推送。

订阅

先完成鉴权,再订阅:

{ "type": "auth_token", "token": "eyJhbGciOiJIUzI1..." }
{ "type": "subscribe", "channel": "spot:user:balances" }

响应:

{ "type": "subscribed", "channel": "spot:user:balances" }

不发送快照。订阅后请调用 GET /spot/balances 初始化本地余额状态,再通过后续 spot_user_balance 推送保持同步。

推送格式

spot_user_balance

每个发生变更的 (token) 行推送一条消息。每次推送均完整替换该代币的余额 — 无增量差值。

{
"type": "spot_user_balance",
"channel": "spot:user:balances",
"data": {
"token": "DF",
"available": "10000",
"frozen": "100",
"ts": 1778400000
}
}

示例 — DFUSDT 成交后两个代币同时更新(两条连续推送):

第 1 条(买方收到 DF):

{
"type": "spot_user_balance",
"channel": "spot:user:balances",
"data": {
"token": "DF",
"available": "10030",
"frozen": "0",
"ts": 1778400010
}
}

第 2 条(买方 USDT 被扣除):

{
"type": "spot_user_balance",
"channel": "spot:user:balances",
"data": {
"token": "USDT",
"available": "4985",
"frozen": "0",
"ts": 1778400010
}
}

字段说明

字段类型描述
tokenstring代币符号,如 DFUSDT。参见 Enums — 代币
availablestring (decimal)可用余额 — 与 GET /spot/balances 中的 available 字段一致。
frozenstring (decimal)冻结余额 — 被挂单或待提现占用。
tsinteger本次余额计算时间 — Unix

推送频率

每当 (user_address, token) 余额行发生变更时,即推送一条 spot_user_balance

触发事件涉及代币
成交(作为吃单方或挂单方)DFUSDT 市场的基础资产(DF)和计价资产(USDT)各一条
限价单下单买单冻结计价资产(USDT);卖单冻结基础资产(DF
撤单已冻结的代币被释放
管理员充值 / 扣款被充值 / 扣款的代币
内部划转(合约保证金 ↔ 现货钱包)被划转的代币

每个受影响的代币单独推送一条。一笔成交触发的两条推送,顺序不作保证。

代码示例

const ws = new WebSocket('wss://api-sepolia.p99.world/ws');

// Local balance map: token -> { available, frozen }
const balances = new Map();

ws.onopen = () => {
// 1. Authenticate
ws.send(JSON.stringify({ type: 'auth_token', token: '<JWT>' }));
setInterval(() => ws.send(JSON.stringify({ type: 'ping' })), 30000);
};

ws.onmessage = (event) => {
const msg = JSON.parse(event.data);

if (msg.type === 'auth_result' && msg.success) {
// 2. Seed local state from REST
fetch('https://api-sepolia.p99.world/api/v1/spot/balances', {
headers: { Authorization: 'Bearer <JWT>' },
})
.then((r) => r.json())
.then((list) => list.forEach((b) => balances.set(b.token, b)));

// 3. Subscribe
ws.send(JSON.stringify({ type: 'subscribe', channel: 'spot:user:balances' }));
}

if (msg.type === 'spot_user_balance') {
const { token, available, frozen, ts } = msg.data;

// Replace the entire token row
balances.set(token, { available, frozen });
console.log(`[${token}] available=${available} frozen=${frozen}`);

// Re-render wallet panel
renderWallet(balances);
}
};

function renderWallet(balances) {
for (const [token, { available, frozen }] of balances) {
console.log(` ${token}: ${available} free, ${frozen} frozen`);
}
}