Real-Time Prediction Market Monitoring
Lesson 2 of 2
Building Live Market Dashboards with WebSocket Updates
Estimated time: 10 minutes
Building Live Market Dashboards with WebSocket Updates
Why WebSockets?
Polling has limitations:
- Wasteful if data doesn't change
- Maximum update frequency capped by polling interval
- High latency (30+ seconds delay typical)
WebSockets and SSE enable true real-time updates with microsecond latency.
Option 1: Server-Sent Events (SSE)
SSE is simpler than WebSockets for one-directional server-to-client streaming.
const express = require('express');
const app = express();
app.get('/market-stream/:ticker', (req, res) => {
const { ticker } = req.params;
res.setHeader('Content-Type', 'text/event-stream');
res.setHeader('Cache-Control', 'no-cache');
res.setHeader('Connection', 'keep-alive');
const monitor = new MarketMonitor(5000); // Poll every 5 seconds
monitor.onMarketUpdate = (data) => {
res.write(`data: ${JSON.stringify(data)}\n\n`);
};
monitor.watch(ticker);
res.on('close', () => monitor.stop());
});
app.listen(3000, () => console.log('Streaming on :3000'));
Client-side:
const eventSource = new EventSource('/market-stream/KXSB');
eventSource.onmessage = (event) => {
const data = JSON.parse(event.data);
updateDashboard(data);
};
eventSource.onerror = (error) => {
console.error('Stream error:', error);
eventSource.close();
};
Option 2: WebSockets for Bi-Directional Communication
WebSockets allow client and server to send messages at any time.
const WebSocket = require('ws');
const http = require('http');
const express = require('express');
const app = express();
const server = http.createServer(app);
const wss = new WebSocket.Server({ server });
wss.on('connection', (ws) => {
const subscriptions = new Set();
ws.on('message', (message) => {
const { action, ticker } = JSON.parse(message);
if (action === 'subscribe') {
subscriptions.add(ticker);
ws.send(JSON.stringify({ type: 'subscribed', ticker }));
} else if (action === 'unsubscribe') {
subscriptions.delete(ticker);
}
});
const monitor = new MarketMonitor(5000);
monitor.onMarketUpdate = (ticker, data) => {
if (subscriptions.has(ticker)) {
ws.send(JSON.stringify({
type: 'update',
ticker,
data
}));
}
};
// Monitor all subscribed tickers
const checkInterval = setInterval(() => {
for (const ticker of subscriptions) {
monitor.watch(ticker);
}
}, 1000);
ws.on('close', () => {
clearInterval(checkInterval);
monitor.stop();
});
});
server.listen(3000);
Client-side:
const ws = new WebSocket('ws://localhost:3000');
ws.onopen = () => {
ws.send(JSON.stringify({ action: 'subscribe', ticker: 'KXSB' }));
ws.send(JSON.stringify({ action: 'subscribe', ticker: 'KXEL' }));
};
ws.onmessage = (event) => {
const message = JSON.parse(event.data);
if (message.type === 'update') {
updateMarketRow(message.ticker, message.data);
}
};
ws.onerror = (error) => {
console.error('WebSocket error:', error);
ws.close();
};
ws.onclose = () => {
console.log('Disconnected. Reconnecting in 3 seconds...');
setTimeout(() => location.reload(), 3000);
};
SSE vs WebSockets
| Feature | SSE | WebSocket |
|---|---|---|
| Browser Support | Chrome/Firefox/Safari | All modern |
| Bi-directional | No (server → client only) | Yes |
| Latency | Lower | Lowest |
| Complexity | Simpler | More complex |
| Best for | Live notifications | Interactive dashboards |
Use SSE if: Broadcasting live market prices one-way to many clients. Use WebSockets if: Clients need to subscribe/unsubscribe dynamically.
Dashboard UI Example
<table id="markets">
<thead>
<tr>
<th>Ticker</th>
<th>Title</th>
<th>Yes Price</th>
<th>No Price</th>
<th>Volume (24h)</th>
</tr>
</thead>
<tbody id="market-rows"></tbody>
</table>
<script>
function updateMarketRow(ticker, markets) {
let row = document.querySelector(`[data-ticker="${ticker}"]`);
if (!row) {
row = document.createElement('tr');
row.setAttribute('data-ticker', ticker);
document.querySelector('#market-rows').appendChild(row);
}
const m = markets[0]; // First market in response
row.innerHTML = `
<td>${ticker}</td>
<td>${m.title}</td>
<td>${(m.yes_price / 100).toFixed(2)}</td>
<td>${(m.no_price / 100).toFixed(2)}</td>
<td>${(m.volume_24h / 1e6).toFixed(1)}M</td>
`;
}
</script>
Next Steps
Add persistence: log all price changes to a database, then build a chart showing price history and volatility over time.