WebSocket Order Updates API
Real-time order and trade updates with automatic reconnection for monitoring order status, executions, and trade confirmations.
Overview
The WebSocket Order Updates API provides instant notifications for all order-related events including placements, modifications, cancellations, executions, and rejections. This persistent connection ensures you never miss critical trading events.
Key Features
- Real-Time Order Updates: Instant notifications for all order state changes
- Automatic Reconnection: Built-in exponential backoff retry mechanism
- Heartbeat Monitoring: Active connection health checks with 30-second intervals
- Session-Based Authentication: Secure connection using session tokens
- Text-Based Protocol: JSON messages for easy parsing and debugging
Connection Setup
WebSocket Endpoint
Authentication Parameters
Connect using your session credentials as query parameters:
JavaScript
const session = 'YOUR_SESSION_ID';
const token = 'YOUR_AUTH_TOKEN';
const wsUrl = `wss://order-updates.arrow.trade?session=${session}&token=${token}`;
const ws = new WebSocket(wsUrl);
ws.onopen = function() {
console.log('Connected to order updates');
};
ws.onmessage = function(event) {
const orderUpdate = JSON.parse(event.data);
console.log('Order update:', orderUpdate);
};
Python
import websocket
import json
session = 'YOUR_SESSION_ID'
token = 'YOUR_AUTH_TOKEN'
ws_url = f"wss://order-updates.arrow.trade?session={session}&token={token}"
def on_message(ws, message):
order_update = json.loads(message)
print('Order update:', order_update)
def on_open(ws):
print('Connected to order updates')
ws = websocket.WebSocketApp(
ws_url,
on_message=on_message,
on_open=on_open
)
ws.run_forever()
Web Worker
// main.js
const orderWorker = new Worker('order-updates-worker.js');
orderWorker.postMessage(JSON.stringify({
msgType: 'connect',
session: 'YOUR_SESSION_ID',
token: 'YOUR_AUTH_TOKEN'
}));
orderWorker.onmessage = function(event) {
const data = JSON.parse(event.data);
if (data.status) {
console.log('Connection status:', data.status);
} else if (data.updateType) {
console.log('Order update:', data);
}
};
Connection Events
The WebSocket client triggers various events to help you manage connection lifecycle:
| Event | Description | When Triggered |
|---|---|---|
connect |
Connection established | WebSocket opens successfully |
disconnect |
Connection closed | WebSocket closes or errors |
reconnect |
Reconnection attempt | Automatic retry in progress |
noreconnect |
Reconnection exhausted | Max retry attempts reached |
error |
Connection error | WebSocket error occurs |
close |
Connection closed | WebSocket closes cleanly |
message |
Raw message received | Any message from server |
orderUpdate |
Order status changed | Order event received |
Connection Management
Heartbeat Protocol
The connection maintains health through a heartbeat mechanism:
- Client Ping: Sends
PONGmessage every 30 seconds - Read Timeout: 30 seconds without server message triggers reconnection
- Automatic Recovery: Reconnects on timeout or connection loss
Reconnection Strategy
Built-in exponential backoff with configurable parameters:
| Parameter | Default | Description |
|---|---|---|
reconnectMaxDelay |
10 seconds | Maximum delay between attempts |
reconnectMaxTries |
300 | Maximum reconnection attempts |
autoReconnect |
true | Enable automatic reconnection |
Backoff Schedule: - Attempt 1: 1 second delay - Attempt 2: 2 seconds delay (2^1) - Attempt 3: 4 seconds delay (2^2) - Attempt 4: 8 seconds delay (2^3) - Attempt 5+: 10 seconds delay (capped at max)
Order Update Messages
Message Format
All order updates are JSON objects with the following structure:
{
"updateType": "ORDER_STATUS",
"orderId": "240123000123456",
"orderStatus": "COMPLETE",
"tradingSymbol": "SBIN-EQ",
"exchange": "NSE",
"orderType": "LIMIT",
"transactionType": "BUY",
"quantity": 100,
"filledQuantity": 100,
"pendingQuantity": 0,
"price": 625.50,
"averagePrice": 625.45,
"timestamp": "2024-01-23T09:30:45.123Z"
}
Update Types
| Update Type | Description | Example Scenario |
|---|---|---|
ORDER_PLACED |
Order successfully placed | New order submitted to exchange |
ORDER_MODIFIED |
Order parameters changed | Price or quantity updated |
ORDER_CANCELLED |
Order cancelled | User or system cancellation |
ORDER_REJECTED |
Order rejected by exchange | Insufficient funds, invalid params |
ORDER_EXECUTED |
Full order execution | Complete fill at market |
ORDER_PARTIAL |
Partial order execution | Partial fill in limit order |
TRADE_UPDATE |
Individual trade confirmation | Each execution leg |
Order Status Values
| Status | Description | Terminal State |
|---|---|---|
PENDING |
Order submitted, awaiting exchange | No |
OPEN |
Active order in market | No |
COMPLETE |
Fully executed | Yes |
CANCELLED |
Cancelled by user/system | Yes |
REJECTED |
Rejected by exchange | Yes |
TRIGGER_PENDING |
Stop order waiting for trigger | No |
AFTER_MARKET_ORDER_REQ_RECEIVED |
AMO order queued | No |
Complete Implementation
JavaScript Client with Web Worker
order-updates-worker.js
const prodUrl = 'wss://order-updates.arrow.trade';
const devUrl = 'wss://order-updates.dev.arrow.trade';
const apiUrl = prodUrl;
const MSG_TYPE = {
CONNECT: 'connect',
};
const SOCKET_CONNECT = {
CONNECTING: 'connecting',
CONNECTED: 'connected',
RETRYING: 'retrying',
};
const Socket = function(session, token) {
const self = this;
this.session = session;
this.token = token;
const autoReconnect = true;
let conn = null;
let readTimer = null;
let pinger = null;
let lastRead = 0;
let currentReconnectionCount = 0;
let lastReconnectInterval = 0;
const maximumReconnectMaxRetries = 300;
let reconnectTimeout = null;
const readTimeout = 30; // seconds
const reconnectMaxDelay = 10;
const reconnectMaxTries = maximumReconnectMaxRetries;
const triggers = {
connect: [],
disconnect: [],
reconnect: [],
noreconnect: [],
error: [],
close: [],
message: [],
orderUpdate: [],
};
this.connect = function() {
// Skip if already connected
if (conn && (conn.readyState === conn.CONNECTING ||
conn.readyState === conn.OPEN)) {
return;
}
const url = `${apiUrl}?session=${this.session}&token=${this.token}`;
conn = new WebSocket(url);
conn.onopen = function() {
lastReconnectInterval = null;
currentReconnectionCount = 0;
trigger('connect');
clearInterval(readTimer);
clearInterval(pinger);
clearTimeout(reconnectTimeout);
lastRead = new Date();
// Send heartbeat every 30 seconds
pinger = setInterval(() => {
if (conn && conn.readyState === conn.OPEN) {
conn.send('PONG');
}
}, 30000);
// Check for connection timeout
readTimer = setInterval(() => {
if ((new Date() - lastRead) / 1000 >= readTimeout) {
if (conn) {
conn.close();
}
clearInterval(readTimer);
triggerDisconnect();
}
}, readTimeout * 1000);
};
conn.onmessage = function(e) {
lastRead = new Date();
// Parse text messages only (JSON order updates)
if (!(e.data instanceof ArrayBuffer)) {
const data = parseTextMessage(e.data);
if (data.updateType) {
trigger('orderUpdate', [data]);
}
}
};
conn.onerror = function(e) {
trigger('error', [e]);
if (this && this.readyState === this.OPEN) {
this.close();
}
};
conn.onclose = function(e) {
trigger('close', [e]);
triggerDisconnect(e);
};
};
this.disconnect = function() {
if (conn && conn.readyState !== conn.CLOSING &&
conn.readyState !== conn.CLOSED) {
conn.close();
}
};
this.connected = function() {
return conn && conn.readyState === conn.OPEN;
};
this.on = function(e, callback) {
if (triggers.hasOwnProperty(e)) {
triggers[e].push(callback);
}
};
function triggerDisconnect(e) {
conn = null;
trigger('disconnect', [e]);
if (autoReconnect) {
attemptReconnection();
}
}
function trigger(e, args) {
if (!triggers[e]) return;
for (let n = 0; n < triggers[e].length; n++) {
triggers[e][n].apply(triggers[e][n], args || []);
}
}
function parseTextMessage(data) {
try {
return JSON.parse(data);
} catch (e) {
return {};
}
}
function attemptReconnection() {
if (currentReconnectionCount > reconnectMaxTries) {
trigger('noreconnect');
return;
}
if (currentReconnectionCount > 0) {
lastReconnectInterval = 2 ** currentReconnectionCount;
} else if (!lastReconnectInterval) {
lastReconnectInterval = 1;
}
if (lastReconnectInterval > reconnectMaxDelay) {
lastReconnectInterval = reconnectMaxDelay;
}
currentReconnectionCount++;
trigger('reconnect', [currentReconnectionCount, lastReconnectInterval]);
if (reconnectTimeout) {
clearTimeout(reconnectTimeout);
}
console.log(`Reconnecting in ${lastReconnectInterval} seconds...`);
reconnectTimeout = setTimeout(() => {
self.connect();
}, lastReconnectInterval * 1000);
}
};
let worker = null;
self.onmessage = function(msg) {
const parsedData = JSON.parse(msg.data);
if (parsedData.msgType === MSG_TYPE.CONNECT && parsedData.token) {
worker = new Socket(parsedData.session, parsedData.token);
worker.connect();
worker.on('connect', () => {
console.log('Connected to order socket');
self.postMessage(JSON.stringify({
status: SOCKET_CONNECT.CONNECTED
}));
});
worker.on('noreconnect', () => {
console.log('Exhausted reconnect retries');
});
worker.on('close', () => {
console.log('Connection closed, reconnecting...');
self.postMessage(JSON.stringify({
status: SOCKET_CONNECT.CONNECTING
}));
});
worker.on('reconnect', () => {
console.log('Reconnecting to order socket...');
self.postMessage(JSON.stringify({
status: SOCKET_CONNECT.CONNECTING
}));
});
worker.on('disconnect', () => {
console.log('Disconnected from order socket');
self.postMessage(JSON.stringify({
status: SOCKET_CONNECT.CONNECTING
}));
});
worker.on('error', (error) => {
console.log('Order socket error:', error);
self.postMessage(JSON.stringify({
status: SOCKET_CONNECT.CONNECTING
}));
});
worker.on('orderUpdate', (e) => {
self.postMessage(JSON.stringify(e));
});
}
};
main.js
class OrderUpdatesClient {
constructor() {
this.worker = null;
this.orderHandlers = new Map();
this.connectionHandlers = [];
}
connect(session, token) {
this.worker = new Worker('order-updates-worker.js');
this.worker.onmessage = (event) => {
const data = JSON.parse(event.data);
if (data.status) {
// Connection status update
this.connectionHandlers.forEach(handler => {
handler(data.status);
});
} else if (data.updateType) {
// Order update
this.handleOrderUpdate(data);
}
};
this.worker.postMessage(JSON.stringify({
msgType: 'connect',
session: session,
token: token
}));
}
onOrderUpdate(orderId, callback) {
if (!this.orderHandlers.has(orderId)) {
this.orderHandlers.set(orderId, []);
}
this.orderHandlers.get(orderId).push(callback);
}
onAllOrders(callback) {
this.onOrderUpdate('*', callback);
}
onConnectionStatus(callback) {
this.connectionHandlers.push(callback);
}
handleOrderUpdate(update) {
// Call specific order handlers
const handlers = this.orderHandlers.get(update.orderId) || [];
handlers.forEach(handler => handler(update));
// Call wildcard handlers
const wildcardHandlers = this.orderHandlers.get('*') || [];
wildcardHandlers.forEach(handler => handler(update));
}
disconnect() {
if (this.worker) {
this.worker.terminate();
this.worker = null;
}
}
}
// Usage
const client = new OrderUpdatesClient();
client.onConnectionStatus((status) => {
console.log('Connection status:', status);
});
client.onAllOrders((update) => {
console.log('Order update:', update);
if (update.orderStatus === 'COMPLETE') {
console.log(`Order ${update.orderId} executed at ${update.averagePrice}`);
}
});
client.connect('your_session_id', 'your_auth_token');
Python Implementation
import websocket
import json
import time
from threading import Thread, Timer
class OrderUpdatesClient:
def __init__(self, session, token):
self.session = session
self.token = token
self.url = f"wss://order-updates.arrow.trade?session={session}&token={token}"
self.ws = None
self.order_handlers = {}
self.connection_handlers = []
self.reconnect_count = 0
self.max_reconnect = 300
self.reconnect_delay = 1
self.last_read = time.time()
self.read_timeout = 30
self.pinger = None
self.read_timer = None
def connect(self):
self.ws = websocket.WebSocketApp(
self.url,
on_message=self.on_message,
on_error=self.on_error,
on_close=self.on_close,
on_open=self.on_open
)
wst = Thread(target=self.ws.run_forever)
wst.daemon = True
wst.start()
def on_open(self, ws):
print('Connected to order updates')
self.reconnect_count = 0
self.reconnect_delay = 1
self.last_read = time.time()
# Start heartbeat
self.start_heartbeat()
# Notify connection handlers
for handler in self.connection_handlers:
handler('connected')
def on_message(self, ws, message):
self.last_read = time.time()
try:
update = json.loads(message)
if 'updateType' in update:
self.handle_order_update(update)
except json.JSONDecodeError:
pass
def on_error(self, ws, error):
print(f'WebSocket error: {error}')
def on_close(self, ws, close_status_code, close_msg):
print('Connection closed')
self.stop_heartbeat()
# Notify connection handlers
for handler in self.connection_handlers:
handler('disconnected')
# Attempt reconnection
self.reconnect()
def start_heartbeat(self):
def send_ping():
if self.ws and self.ws.sock and self.ws.sock.connected:
self.ws.send('PONG')
self.pinger = Timer(30, send_ping)
self.pinger.start()
def check_timeout():
if time.time() - self.last_read > self.read_timeout:
print('Read timeout, reconnecting...')
self.ws.close()
else:
self.read_timer = Timer(self.read_timeout, check_timeout)
self.read_timer.start()
send_ping()
check_timeout()
def stop_heartbeat(self):
if self.pinger:
self.pinger.cancel()
if self.read_timer:
self.read_timer.cancel()
def reconnect(self):
if self.reconnect_count >= self.max_reconnect:
print('Max reconnection attempts reached')
return
self.reconnect_count += 1
delay = min(2 ** self.reconnect_count, 10)
print(f'Reconnecting in {delay} seconds (attempt {self.reconnect_count})...')
for handler in self.connection_handlers:
handler('reconnecting')
Timer(delay, self.connect).start()
def on_order_update(self, order_id, callback):
if order_id not in self.order_handlers:
self.order_handlers[order_id] = []
self.order_handlers[order_id].append(callback)
def on_all_orders(self, callback):
self.on_order_update('*', callback)
def on_connection_status(self, callback):
self.connection_handlers.append(callback)
def handle_order_update(self, update):
order_id = update.get('orderId')
# Call specific order handlers
if order_id in self.order_handlers:
for handler in self.order_handlers[order_id]:
handler(update)
# Call wildcard handlers
if '*' in self.order_handlers:
for handler in self.order_handlers['*']:
handler(update)
def disconnect(self):
if self.ws:
self.stop_heartbeat()
self.ws.close()
# Usage
def handle_order(update):
print(f"Order update: {update}")
if update['orderStatus'] == 'COMPLETE':
print(f"Order {update['orderId']} executed at {update['averagePrice']}")
def handle_connection(status):
print(f"Connection status: {status}")
client = OrderUpdatesClient('your_session_id', 'your_auth_token')
client.on_all_orders(handle_order)
client.on_connection_status(handle_connection)
client.connect()
Use Cases
Portfolio Monitoring Dashboard
const client = new OrderUpdatesClient();
const orderBook = new Map();
client.onAllOrders((update) => {
orderBook.set(update.orderId, update);
updateDashboard(Array.from(orderBook.values()));
});
function updateDashboard(orders) {
const pending = orders.filter(o => o.orderStatus === 'OPEN').length;
const completed = orders.filter(o => o.orderStatus === 'COMPLETE').length;
const rejected = orders.filter(o => o.orderStatus === 'REJECTED').length;
console.log(`Pending: ${pending}, Completed: ${completed}, Rejected: ${rejected}`);
}
Order Execution Tracker
function trackOrderExecution(orderId) {
return new Promise((resolve, reject) => {
const timeout = setTimeout(() => {
reject(new Error('Order tracking timeout'));
}, 300000); // 5 minutes
client.onOrderUpdate(orderId, (update) => {
console.log(`${orderId}: ${update.orderStatus}`);
if (update.orderStatus === 'COMPLETE') {
clearTimeout(timeout);
resolve(update);
} else if (update.orderStatus === 'REJECTED' ||
update.orderStatus === 'CANCELLED') {
clearTimeout(timeout);
reject(new Error(`Order ${update.orderStatus}`));
}
});
});
}
// Usage
try {
const result = await trackOrderExecution('240123000123456');
console.log(`Order filled at ${result.averagePrice}`);
} catch (error) {
console.error('Order failed:', error);
}
Alert System
const alerts = {
onExecution: [],
onRejection: [],
onLargeOrder: []
};
client.onAllOrders((update) => {
// Execution alerts
if (update.orderStatus === 'COMPLETE') {
alerts.onExecution.forEach(handler => handler(update));
}
// Rejection alerts
if (update.orderStatus === 'REJECTED') {
alerts.onRejection.forEach(handler => handler(update));
}
// Large order alerts
if (update.quantity > 1000) {
alerts.onLargeOrder.forEach(handler => handler(update));
}
});
// Register alert handlers
alerts.onExecution.push((order) => {
sendNotification(`Order executed: ${order.tradingSymbol} @ ${order.averagePrice}`);
});
alerts.onRejection.push((order) => {
sendNotification(`Order rejected: ${order.tradingSymbol} - ${order.rejectionReason}`);
});
Best Practices
Connection Management
- Monitor Connection Status: Track connection events to update UI indicators
- Handle Reconnections Gracefully: Display reconnection attempts to users
- Implement Timeouts: Set reasonable timeouts for order tracking
- Clean Up Resources: Properly terminate workers and connections on unmount
Error Handling
- Network Failures: Display user-friendly messages during connection issues
- Authentication Errors: Prompt for re-login on auth failures
- Message Parsing: Validate JSON structure before processing
- Duplicate Updates: Use order IDs to deduplicate updates if needed
Performance Optimization
- Use Web Workers: Offload WebSocket processing to avoid blocking UI
- Batch UI Updates: Throttle dashboard updates to reduce render cycles
- Filter Updates: Only process relevant order updates
- Memory Management: Clear old orders from tracking maps periodically
Security
- Secure Tokens: Never expose tokens in client-side code or logs
- Validate Messages: Verify message structure and content
- Connection Limits: Monitor and limit connection attempts
- Session Management: Implement proper session timeout and renewal
Troubleshooting
| Issue | Cause | Solution |
|---|---|---|
| Connection Fails | Invalid session/token | Verify credentials are current and valid |
| Frequent Disconnects | Network instability | Check network quality, increase timeout |
| Missing Updates | Connection dropped | Fetch order status via REST API on reconnect |
| Duplicate Updates | Network retry | Deduplicate using order ID + timestamp |
| High CPU Usage | Too many handlers | Optimize callback functions, batch updates |
| Memory Leaks | Handlers not cleaned | Remove handlers when components unmount |
Rate Limits
| Account Type | Max Connections | Reconnect Rate | Message Rate |
|---|---|---|---|
| Basic | 1 | 10/min | N/A (push) |
| Premium | 3 | 30/min | N/A (push) |
| Enterprise | 10 | Unlimited | N/A (push) |
Related APIs
- Market Data WebSocket: Real-time price feeds and market depth
- Orders REST API: Place, modify, and cancel orders
- Positions API: View and manage open positions
- Trade History API: Historical trade data and reports