Overview
The Exness Trading Platform provides real-time market data through a WebSocket connection. The server broadcasts live bid/ask prices for all supported trading instruments as they update, enabling you to build responsive trading interfaces with live price feeds.
Connection
WebSocket URL
In production, replace localhost:7070 with your production WebSocket server URL. The WebSocket server runs separately from the REST API.
Establishing a Connection
The WebSocket server accepts connections without authentication. Once connected, you’ll automatically receive real-time market data updates.
Price Update Messages
The server sends JSON messages containing bid/ask prices for trading instruments:
{
"asset": "BTCUSDT",
"bid": "9650050000000",
"ask": "9650150000000"
}
The trading symbol (e.g., BTCUSDT, ETHUSDT, SOLUSDT)
The current bid price in micro-units (divide by 100,000,000 to get the actual price)
The current ask price in micro-units (divide by 100,000,000 to get the actual price)
Price Conversion Required: All prices are transmitted in micro-units (multiplied by 100,000,000) to avoid floating-point precision issues. You must divide by 100000000 to get the actual price.
Client Implementation
JavaScript/TypeScript Example
const ws = new WebSocket("ws://localhost:7070/");
ws.onopen = () => {
console.log("WebSocket connection opened");
};
ws.onmessage = (event) => {
const data = JSON.parse(event.data);
if (data.asset && data.bid && data.ask) {
// Convert from micro-units to regular price
const bidPrice = Number(data.bid) / 100000000;
const askPrice = Number(data.ask) / 100000000;
const midPrice = (bidPrice + askPrice) / 2;
console.log(`${data.asset}: Bid=${bidPrice}, Ask=${askPrice}, Mid=${midPrice}`);
// Update your UI with the new prices
updatePriceDisplay(data.asset, bidPrice, askPrice, midPrice);
}
};
ws.onerror = (error) => {
console.error("WebSocket error:", error);
};
ws.onclose = () => {
console.log("WebSocket connection closed");
// Implement reconnection logic here
};
React Hook Example
import { useEffect, useState } from 'react';
interface MarketData {
[symbol: string]: {
bid: number;
ask: number;
price: number;
lastUpdated: number;
};
}
export function useMarketData() {
const [marketData, setMarketData] = useState<MarketData>({});
const [connected, setConnected] = useState(false);
useEffect(() => {
const ws = new WebSocket("ws://localhost:7070/");
ws.onopen = () => {
console.log("Connected to market data stream");
setConnected(true);
};
ws.onmessage = (event) => {
const data = JSON.parse(event.data);
if (data.asset && data.bid && data.ask) {
const bidPrice = Number(data.bid) / 100000000;
const askPrice = Number(data.ask) / 100000000;
const midPrice = (bidPrice + askPrice) / 2;
setMarketData((prev) => ({
...prev,
[data.asset]: {
bid: bidPrice,
ask: askPrice,
price: midPrice,
lastUpdated: Date.now(),
},
}));
}
};
ws.onerror = (error) => {
console.error("WebSocket error:", error);
setConnected(false);
};
ws.onclose = () => {
console.log("Disconnected from market data stream");
setConnected(false);
};
return () => {
ws.close();
};
}, []);
return { marketData, connected };
}
Python Example
import asyncio
import json
import websockets
async def subscribe_market_data():
uri = "ws://localhost:7070/"
async with websockets.connect(uri) as websocket:
print("Connected to market data stream")
async for message in websocket:
data = json.loads(message)
if "asset" in data and "bid" in data and "ask" in data:
# Convert from micro-units to regular price
bid_price = float(data["bid"]) / 100000000
ask_price = float(data["ask"]) / 100000000
mid_price = (bid_price + ask_price) / 2
print(f"{data['asset']}: Bid={bid_price:.2f}, Ask={ask_price:.2f}, Mid={mid_price:.2f}")
if __name__ == "__main__":
asyncio.run(subscribe_market_data())
Supported Symbols
The WebSocket stream broadcasts updates for the following trading symbols:
- BTCUSDT - Bitcoin / Tether
- ETHUSDT - Ethereum / Tether
- SOLUSDT - Solana / Tether
Additional symbols may be added in the future. Check the data stream to see all available symbols.
Architecture
Redis Pub/Sub Integration
The WebSocket server operates as a bridge between Redis Pub/Sub and WebSocket clients:
- Market data sources publish price updates to Redis channel
binance:pubsub
- WebSocket server subscribes to this Redis channel
- Connected clients receive real-time broadcasts of all price updates
This architecture ensures:
- Low-latency price distribution
- Scalability across multiple WebSocket server instances
- Decoupling of data sources from client connections
Best Practices
1. Implement Reconnection Logic
function connectWebSocket() {
const ws = new WebSocket("ws://localhost:7070/");
ws.onclose = () => {
console.log("Connection lost. Reconnecting in 5 seconds...");
setTimeout(connectWebSocket, 5000);
};
return ws;
}
2. Handle Price Updates Efficiently
Avoid blocking the main thread with heavy computations in the message handler:
ws.onmessage = (event) => {
const data = JSON.parse(event.data);
// Queue updates for batched processing
requestAnimationFrame(() => {
processMarketData(data);
});
};
3. Debounce UI Updates
When receiving high-frequency updates, debounce UI rendering to maintain smooth performance:
import { debounce } from 'lodash';
const updateUI = debounce((symbol: string, price: number) => {
document.getElementById(symbol).textContent = price.toFixed(2);
}, 100);
4. Monitor Connection Health
Implement heartbeat monitoring to detect stale connections:
let lastMessageTime = Date.now();
ws.onmessage = (event) => {
lastMessageTime = Date.now();
// Process message...
};
setInterval(() => {
if (Date.now() - lastMessageTime > 30000) {
console.warn("No messages received for 30 seconds");
ws.close();
}
}, 10000);
5. Calculate Spreads and Mid-Prices
function calculateMetrics(bid: number, ask: number) {
const midPrice = (bid + ask) / 2;
const spread = ask - bid;
const spreadBps = (spread / midPrice) * 10000; // Spread in basis points
return { midPrice, spread, spreadBps };
}
Error Handling
Connection Errors
The server may reject connections under high load or during maintenance:
ws.onerror = (error) => {
console.error("WebSocket error:", error);
// Implement exponential backoff for reconnection
};
Always validate message structure before processing:
ws.onmessage = (event) => {
try {
const data = JSON.parse(event.data);
if (!data.asset || !data.bid || !data.ask) {
console.warn("Received incomplete market data:", data);
return;
}
// Process valid data
processMarketData(data);
} catch (error) {
console.error("Failed to parse WebSocket message:", error);
}
};
- Message Frequency: Expect high-frequency updates during volatile market conditions (potentially multiple updates per second per symbol)
- Bandwidth: Each price update is ~50-100 bytes. With 3 symbols updating 10 times/second, expect ~1.5-3 KB/s
- Processing: Use efficient data structures (e.g., Maps) for storing and looking up symbol data
- Memory: Implement data retention policies to prevent memory leaks from accumulating historical ticks
Testing
You can test the WebSocket connection using command-line tools:
Using wscat
npm install -g wscat
wscat -c ws://localhost:7070/
Using websocat
websocat ws://localhost:7070/
Both tools will display the real-time price stream in your terminal.