Skip to main content

Prediction Market — WebSocket Push API (Integration Guide)

1. Field Format Specification

1.1 amount (Money)

Rule: truncate to 2 decimal places (DOWN mode, towards zero)

Examples:
2.5678 → "2.56"
0.009 → "<0.01" ← Unified display for amounts < 0.01
null → "<0.01"
100 → "100.00"

Integration tips:

  • Render the amount field directly to the user
  • For precise numerical computation, fetch via REST API — do NOT reverse-parse this field

1.2 topic (Market Title)

Rule: if longer than 12 chars, truncate and append ".." (max 14 chars total)

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

1.3 outcome (Outcome Name)

Rule: passed through as-is, not truncated

Possible values:
"Yes" / "No" (binary markets)
"Trump" / "Biden" / … (multi-outcome markets)
"" (when data is missing)

1.4 pushId (Idempotency Key)

Format: see Wallet Events WebSocket API Section 3.3. Present in every topic, strongly recommended for client-side deduplication.


2. End-to-End Integration Example

2.1 Full Flow

1. Acquire API Key + Secret Key under user's logged-in session via REST API
2. Prepare timestamp + random
3. Sort all URL parameters alphabetically and concatenate
4. Compute signature with HMAC SHA256
5. Build the full WSS URL
6. Open WebSocket connection with header X-MBX-APIKEY
7. on('open') → connection established; send SUBSCRIBE if not in URL
8. on('message') → parse the message (outer JSON, then inner JSON)
9. on('close') → trigger reconnect logic (regenerate timestamp + signature)
10. setInterval 30s → send empty PING frame

2.2 Node.js Sample

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';

// All prediction topics to subscribe
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(); // For production, replace with a proper LRU cache

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

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

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

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

// Business push
if (envelope.type === 'TOPIC') {
const payload = JSON.parse(envelope.data); // ⚠️ data is a stringified JSON
handlePredictionPush(envelope.topic, payload);
}
});

ws.on('close', (code, reason) => {
console.warn('[ws] closed', code, reason.toString());
setTimeout(connect, 3_000); // Simple reconnect; use exponential backoff in production
});

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

return ws;
}

function handlePredictionPush(topic, payload) {
// 1. Idempotency
if (seenPushIds.has(payload.pushId)) return;
seenPushIds.add(payload.pushId);

// 2. Extract scenarioCode (strip topic prefix)
const scenarioCode = topic.replace('web3_prediction_', '');

// 3. Field fallbacks
const amount = payload.amount ?? '<0.01';
const marketTopic = payload.topic ?? 'the market';
const outcome = payload.outcome ?? '';

// 4. Dispatch by scenario
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;
// ... other 11 scenarios
default:
console.log(`[unknown scenario] ${scenarioCode}`, payload);
}
}

connect();

2.3 End-to-End Business Example: Limit BUY Flow

T+0s   User submits a limit order
← Topic: pm_limit_submit_success
← data: {"pushId":"pm_O1_pm_limit_submit_success_xx","amount":"100.00","topic":"BTC will hi.."}

T+30s Partial liquidity matched
← 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 Fully filled
← Topic: pm_limit_order_filled
← data: {"pushId":"pm_O1_pm_limit_order_filled_zz","amount":"100.00","topic":"BTC will hi.."}