Skip to content

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

Production: wss://order-updates.arrow.trade

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 PONG message 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)
  • 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