用户订单
通道名
spot:user:orders
鉴权:必须。参见鉴权。
描述
为已鉴权用户推送所有现货市场的订单生命周期事件。服务端按钱包地址过滤推送 — 仅转发属于已鉴权用户的事件。订阅一次即可接收所有交易对的事件。
订阅
先完成鉴权,再订阅:
{ "type": "auth_token", "token": "eyJhbGciOiJIUzI1..." }
{ "type": "subscribe", "channel": "spot:user:orders" }
响应:
{ "type": "subscribed", "channel": "spot:user:orders" }
不发送快照。订阅后请调用 GET /spot/orders 初始化本地订单状态,再通过后续 spot_user_order 推送保持同步。
推送格式
spot_user_order
每次订单生命周期事件触发推送:下单、成交(每笔成交每侧各一条)、撤单、拒单。
示例 — 订单已下单(尚未成交):
{
"type": "spot_user_order",
"channel": "spot:user:orders",
"data": {
"id": "8b3d1a2c-4e5f-6789-abcd-ef0123456789",
"symbol": "DFUSDT",
"side": "buy",
"type": "limit",
"tif": "gtc",
"price": "0.5000",
"quantity": "100",
"quote_quantity": null,
"filled_qty": "0",
"avg_fill_price": "0",
"status": "open",
"reject_reason": null,
"updated_at": 1778400000
}
}
示例 — 部分成交(含 last_fill):
{
"type": "spot_user_order",
"channel": "spot:user:orders",
"data": {
"id": "8b3d1a2c-4e5f-6789-abcd-ef0123456789",
"symbol": "DFUSDT",
"side": "buy",
"type": "limit",
"tif": "gtc",
"price": "0.5000",
"quantity": "100",
"quote_quantity": null,
"filled_qty": "30",
"avg_fill_price": "0.5000",
"status": "partially_filled",
"reject_reason": null,
"updated_at": 1778400010,
"last_fill": {
"trade_id": "9f2a1b3c-4d5e-6f7a-8b9c-0d1e2f3a4b5c",
"price": "0.5000",
"quantity": "30",
"fee": "0.015",
"fee_token": "DF"
}
}
}
示例 — 订单已撤销:
{
"type": "spot_user_order",
"channel": "spot:user:orders",
"data": {
"id": "8b3d1a2c-4e5f-6789-abcd-ef0123456789",
"symbol": "DFUSDT",
"side": "buy",
"type": "limit",
"tif": "gtc",
"price": "0.5000",
"quantity": "100",
"quote_quantity": null,
"filled_qty": "30",
"avg_fill_price": "0.5000",
"status": "canceled",
"reject_reason": null,
"updated_at": 1778400020
}
}
示例 — 订单被拒单:
{
"type": "spot_user_order",
"channel": "spot:user:orders",
"data": {
"id": "7c4e2b1d-5f6a-7890-bcde-f01234567890",
"symbol": "DFUSDT",
"side": "buy",
"type": "limit",
"tif": "post_only",
"price": "0.5001",
"quantity": "50",
"quote_quantity": null,
"filled_qty": "0",
"avg_fill_price": "0",
"status": "rejected",
"reject_reason": "POST_ONLY_REJECT",
"updated_at": 1778400030
}
}
字段说明
| 字段 | 类型 | 描述 |
|---|---|---|
id | string (UUID) | 订单标识符。与 GET /spot/orders 返回值一致。 |
symbol | string | 交易对,如 DFUSDT。 |
side | string | buy 或 sell。参见 Enums — 订单方向。 |
type | string | limit 或 market。参见 Enums — 订单类型。 |
tif | string | gtc、ioc 或 post_only。参见 Enums — 有效期类型。 |
price | string (decimal) 或 null | 限价单价格。市价单为 null。 |
quantity | string (decimal) 或 null | 基础资产数量(DF)。使用 quote_quantity 下单的市价买单为 null。 |
quote_quantity | string (decimal) 或 null | 市价买单的计价资产数量(USDT);其他情况为 null。 |
filled_qty | string (decimal) | 累计已成交基础资产数量。 |
avg_fill_price | string (decimal) | 成交量加权平均价格。尚无成交时为 "0"。 |
status | string | 当前订单状态。参见 Enums — 订单状态。 |
reject_reason | string 或 null | status == "rejected" 时的错误码,如 POST_ONLY_REJECT;否则为 null。 |
updated_at | integer | 本次事件发生时间 — Unix 秒。 |
last_fill | object 或缺失 | 仅在本次推送由成交触发时存在。 普通下单、撤单、拒单时省略。 |
last_fill 字段说明
| 字段 | 类型 | 描述 |
|---|---|---|
trade_id | string (UUID) | 成交标识符(与 GET /spot/trades/me 一致)。 |
price | string (decimal) | 本次成交的执行价格。 |
quantity | string (decimal) | 本次成交的基础资产数量。 |
fee | string (decimal) | 本次成交收取的手续费金额。 |
fee_token | string | 手续费收取的代币 — 买方(或卖方挂单方)收到 DF 时为 DF,收到 USDT(卖方或买方挂单方)时为 USDT。DFUSDT MVP 阶段硬编码;未来市场可能有所不同。 |
推送频率
| 事件 | 触发时机 |
|---|---|
| 订单下单 | 引擎接受后立即推送 |
| 部分成交 | 每笔成交后立即推送(每笔成交每个用户各一条) |
| 全部成交 | 最后一笔成交后立即推送 |
| 订单撤销 | 撤销时立即推送(用户请求、IOC 剩余部分或市场下架) |
| 订单拒单 | 拒单时立即推送 |
代码示例
const ws = new WebSocket('wss://api.prex.world/ws');
// Local order map: id -> order data
const orders = 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.prex.world/api/v1/spot/orders', {
headers: { Authorization: 'Bearer <JWT>' },
})
.then((r) => r.json())
.then((list) => list.forEach((o) => orders.set(o.id, o)));
// 3. Subscribe
ws.send(JSON.stringify({ type: 'subscribe', channel: 'spot:user:orders' }));
}
if (msg.type === 'spot_user_order') {
const o = msg.data;
orders.set(o.id, o);
if (o.last_fill) {
console.log(`Fill on order ${o.id}: qty=${o.last_fill.quantity} @ ${o.last_fill.price} fee=${o.last_fill.fee} ${o.last_fill.fee_token}`);
} else {
console.log(`Order ${o.id} → status=${o.status}${o.reject_reason ? ' (' + o.reject_reason + ')' : ''}`);
}
}
};