跳到主要内容

Prediction Market — WebSocket Push API(接入指南)

1. 字段格式规范

1.1 amount(金额)

规则:截取到小数点后 2 位(DOWN 模式,向 0 截断)

示例:
2.5678 → "2.56"
0.009 → "<0.01" ← 小于 0.01 的统一展示
null → "<0.01"
100 → "100.00"

接入建议

  • 直接用 amount 字段渲染给用户
  • 如需精确数值计算,请通过业务后端 REST API 获取,不要反向解析此字段

1.2 topic(市场标题)

规则:超过 12 字符则截断后追加 ".."(共最长 14 字符)

示例:
"BTC will hit 100K" → "BTC will hi.."
"Short title" → "Short title"
null → ""

1.3 outcome(结果名称)

规则:原样透传,未截断

可能值:
"Yes" / "No"(二元市场)
"Trump" / "Biden" / 其他(多元市场)
""(数据缺失时)

1.4 pushId(幂等键)

格式见 Wallet Events WebSocket API 第 3.3 节。所有 topic 必有强烈建议客户端用此字段做去重


2. 接入完整流程示例

2.1 完整流程

1. 通过 REST API 在用户登录态获取 API Key + Secret Key
2. 准备 timestamp + random
3. 拼接所有 URL 参数(按字母序)
4. HMAC SHA256 计算 signature
5. 构造完整 WSS URL
6. WebSocket 连接,Header 加 X-MBX-APIKEY
7. on('open') → 连接成功,可发送 SUBSCRIBE 命令(如未在 URL 中订阅)
8. on('message') → 解析消息(先 outer JSON, 再 inner JSON)
9. on('close') → 触发重连逻辑(重新生成 timestamp + signature)
10. setInterval 30s → 发送空 PING

2.2 Node.js 示例

const WebSocket = require('ws');
const crypto = require('crypto');

const API_KEY = process.env.BINANCE_API_KEY;
const SECRET_KEY = process.env.BINANCE_SECRET_KEY;
const BASE_URL = 'wss://api.binance.com/sapi/wss';

// 订阅的所有 prediction topics
const TOPICS = [
'web3_prediction_pm_market_buy_success',
'web3_prediction_pm_market_buy_fail',
'web3_prediction_pm_market_sell_success',
'web3_prediction_pm_market_sell_fail',
'web3_prediction_pm_limit_submit_success',
'web3_prediction_pm_limit_submit_fail',
'web3_prediction_pm_limit_order_filled',
'web3_prediction_pm_limit_order_partial_fill',
'web3_prediction_pm_claim_success',
'web3_prediction_pm_claim_fail',
'web3_prediction_pm_claim_partial_success',
'web3_prediction_pm_transfer_success',
'web3_prediction_pm_transfer_fail',
'web3_prediction_pm_market_close',
];

function buildConnectionUrl() {
const params = {
random: crypto.randomBytes(16).toString('hex'),
topic: TOPICS.join('|'),
recvWindow: '30000',
timestamp: Date.now().toString(),
};
const sortedKeys = Object.keys(params).sort();
const payload = sortedKeys.map(k => `${k}=${params[k]}`).join('&');
const signature = crypto
.createHmac('sha256', SECRET_KEY)
.update(payload)
.digest('hex');
return `${BASE_URL}?${payload}&signature=${signature}`;
}

const seenPushIds = new Set(); // 简单 LRU 推荐生产替换

function connect() {
const url = buildConnectionUrl();
const ws = new WebSocket(url, [], {
headers: { 'X-MBX-APIKEY': API_KEY },
});

ws.on('open', () => {
console.log('[ws] connected');
// 心跳
setInterval(() => {
if (ws.readyState === WebSocket.OPEN) ws.ping();
}, 30_000);
});

ws.on('message', (raw) => {
const envelope = JSON.parse(raw.toString());

// 命令响应
if (envelope.type === 'COMMAND') {
console.log('[ws] command response:', envelope);
return;
}

// 业务推送
if (envelope.type === 'TOPIC') {
const payload = JSON.parse(envelope.data); // ⚠️ data 是字符串化 JSON
handlePredictionPush(envelope.topic, payload);
}
});

ws.on('close', (code, reason) => {
console.warn('[ws] closed', code, reason.toString());
setTimeout(connect, 3_000); // 简单重连,生产建议指数退避
});

ws.on('error', (err) => {
console.error('[ws] error', err);
});

return ws;
}

function handlePredictionPush(topic, payload) {
// 1. 幂等去重
if (seenPushIds.has(payload.pushId)) return;
seenPushIds.add(payload.pushId);

// 2. 提取 scenarioCode(topic 去前缀)
const scenarioCode = topic.replace('web3_prediction_', '');

// 3. 字段兜底
const amount = payload.amount ?? '<0.01';
const marketTopic = payload.topic ?? 'the market';
const outcome = payload.outcome ?? '';

// 4. 按场景分发
switch (scenarioCode) {
case 'pm_market_buy_success':
console.log(`Bought ${amount} USDT in ${marketTopic}`);
break;
case 'pm_claim_success':
console.log(`Successfully claimed ${amount} USDT.`);
break;
case 'pm_claim_fail':
console.log(`Failed to claim ${amount} from ${outcome}.`);
break;
case 'pm_market_close':
console.log(`Market ${marketTopic} resolved.`);
break;
// ... 其他 11 个场景
default:
console.log(`[unknown scenario] ${scenarioCode}`, payload);
}
}

connect();

2.3 端到端业务示例:限价 BUY 全流程

T+0s   用户提交限价单
← Topic: pm_limit_submit_success
← data: {"pushId":"pm_O1_pm_limit_submit_success_xx","amount":"100.00","topic":"BTC will hi.."}

T+30s 撮合到部分流动性
← Topic: pm_limit_order_partial_fill
← data: {"pushId":"pm_O1_pm_limit_order_partial_fill_yy","amount":"30.00","topic":"BTC will hi.."}

T+5min 完全成交
← Topic: pm_limit_order_filled
← data: {"pushId":"pm_O1_pm_limit_order_filled_zz","amount":"100.00","topic":"BTC will hi.."}