Skip to content

WebSocket Market Data API

Real-time market data streaming with ultra-low latency for institutional-grade trading applications. Get live price feeds, market depth, and trading analytics through persistent WebSocket connections.

Overview

The WebSocket Market Data API delivers professional-grade real-time market data with sub-second latency, making it essential for algorithmic trading, portfolio management platforms, and advanced trading applications requiring instant market updates.

Key Features

  • Ultra-Low Latency: Sub-second market data updates with optimized binary protocols
  • Multiple Data Modes: Choose from LTP, LTPC, Quote, or Full market depth based on your needs
  • Efficient Binary Protocol: Compact data packets (13-229 bytes) for minimal bandwidth usage
  • Real-Time Market Depth: Complete order book with 5-level bid/ask data
  • Cross-Exchange Support: Unified data stream across NSE, BSE, NFO, BFO, and MCX
  • Scalable Architecture: Handle thousands of instrument subscriptions simultaneously

Connection Setup

WebSocket Endpoint

wss://stream.arrow.trade

Authentication Parameters

Connect using your application credentials as query parameters:

JavaScript

# Test connection
websocat "wss://stream.arrow.trade?appID=<YOUR_APP_ID>&token=<YOUR_TOKEN>"
const wsUrl = `wss://stream.arrow.trade?appID=${YOUR_APP_ID}&token=${YOUR_TOKEN}`;
const ws = new WebSocket(wsUrl);

ws.onopen = function(event) {
    console.log('WebSocket connected');
};

ws.onmessage = function(event) {
    // Handle binary market data
    const data = new Uint8Array(event.data);
    const marketData = parseMarketData(data);
};
import websocket
import struct

ws_url = f"wss://stream.arrow.trade?appID={YOUR_APP_ID}&token={YOUR_TOKEN}"

def on_message(ws, message):
    # Parse binary market data
    market_data = parse_market_data(message)

def on_open(ws):
    print("WebSocket connected")

ws = websocket.WebSocketApp(ws_url,
                        on_message=on_message,
                        on_open=on_open)
ws.run_forever()

Subscription Management

Message Format

All subscription messages follow this JSON structure:

{
    "code": "SUBSCRIPTION_MODE",
    "mode": "DATA_MODE", 
    "DATA_MODE": [array_of_tokens]
}

Subscription Modes

Mode Description Use Case
sub Subscribe to instruments Start receiving data for specified tokens
unsub Unsubscribe from instruments Stop data feed for specified tokens

Data Modes

Mode Description Packet Size Update Frequency Best For
ltpc Last Traded Price + Previous Close 17 bytes Real-time Basic price monitoring
ltp Last Traded Price only 13 bytes Real-time High-frequency price feeds
quote Complete quote without depth 81 bytes Real-time Comprehensive market view
full Complete data with market depth 229 bytes Real-time Order book analysis

Subscription Examples

Subscribe to Basic Price Data (LTPC)

{
    "code": "sub",
    "mode": "ltpc", 
    "ltpc": [26009, 26000, 256265]
}

Subscribe to High-Frequency Price Feed (LTP)

{
    "code": "sub",
    "mode": "ltp",
    "ltp": [26009, 26000]
}

Subscribe to Complete Market Data (Quote)

{
    "code": "sub", 
    "mode": "quote",
    "quote": [26009, 26000, 256265]
}

Subscribe to Full Market Depth

{
    "code": "sub",
    "mode": "full", 
    "full": [26009, 26000]
}

Unsubscribe from Instruments

{
    "code": "unsub",
    "mode": "ltpc",
    "ltpc": [26009]
}

Binary Data Structures

All market data is received as binary packets for maximum efficiency. Parse using the appropriate structure based on your subscription mode.

LTPC Mode (17 bytes)

Enhanced LTP data with previous day closing price for accurate P&L calculations.

Bytes Field Type Description
0-4 Token int32 Unique instrument identifier
4-8 LTP int32 Last traded price (scaled)
8-9 Net Change Indicator int8 Direction: +1 (up), -1 (down), 0 (unchanged)
9-13 Net Change int32 Absolute price change from previous close
13-17 Previous Close int32 Previous trading session close price

Note: LTPC mode includes previous day closing price, enabling accurate net change calculations based on closing price rather than settlement price.

Complete Market Data Structure (LTP, Quote, Full)

Comprehensive market data including OHLC, volume, and open interest.

Bytes Field Type Description
0-4 Token int32 Instrument identifier
4-8 LTP int32 Last traded price
8-9 Net Change Indicator int8 Price direction
9-13 Net Change int32 Price change amount
13-17 LTQ int32 Last traded quantity
17-21 Average Price int32 Volume-weighted average price
21-29 Total Buy Quantity int64 Cumulative buy orders
29-37 Total Sell Quantity int64 Cumulative sell orders
37-41 Open int32 Session opening price
41-45 High int32 Session highest price
45-49 Close int32 Previous session close
49-53 Low int32 Session lowest price
53-61 Volume int64 Total traded volume
61-65 LTT int32 Last trade time (epoch)
65-69 Exchange Time int32 Exchange timestamp
69-73 Open Interest int32 Current open interest
73-77 OI Day High int32 Highest OI during session
77-81 OI Day Low int32 Lowest OI during session
81-85 Lower Circuit int32 Lower price limit
85-89 Upper Circuit int32 Upper price limit
89-229 Market Depth bytes Order book data (Full mode only)

Market Depth Structure (Full Mode)

The market depth provides a complete 5-level order book with bid/ask prices, quantities, and order counts.

Depth Layout (140 bytes total)

Position Level Data Structure
89-103 Bid 1 Best buy orders Quantity (8) + Price (4) + Orders (2)
103-117 Bid 2 Second best buy Quantity (8) + Price (4) + Orders (2)
117-131 Bid 3 Third best buy Quantity (8) + Price (4) + Orders (2)
131-145 Bid 4 Fourth best buy Quantity (8) + Price (4) + Orders (2)
145-159 Bid 5 Fifth best buy Quantity (8) + Price (4) + Orders (2)
159-173 Ask 1 Best sell orders Quantity (8) + Price (4) + Orders (2)
173-187 Ask 2 Second best sell Quantity (8) + Price (4) + Orders (2)
187-201 Ask 3 Third best sell Quantity (8) + Price (4) + Orders (2)
201-215 Ask 4 Fourth best sell Quantity (8) + Price (4) + Orders (2)
215-229 Ask 5 Fifth best sell Quantity (8) + Price (4) + Orders (2)

Implementation Examples

Complete WebSocket Client

JavaScript

class MarketDataClient {
    constructor(appID, token) {
        this.wsUrl = `wss://stream.arrow.trade?appID=${appID}&token=${token}`;
        this.ws = null;
        this.subscribers = new Map();
    }

    connect() {
        return new Promise((resolve, reject) => {
            this.ws = new WebSocket(this.wsUrl);

            this.ws.onopen = () => {
                console.log('WebSocket connected');
                resolve();
            };

            this.ws.onmessage = (event) => {
                this.handleMessage(event.data);
            };

            this.ws.onerror = (error) => {
                console.error('WebSocket error:', error);
                reject(error);
            };

            this.ws.onclose = () => {
                console.log('WebSocket disconnected');
                this.reconnect();
            };
        });
    }

    subscribe(mode, tokens, callback) {
        const message = {
            code: 'sub',
            mode: mode,
            [mode]: tokens
        };

        this.ws.send(JSON.stringify(message));

        tokens.forEach(token => {
            this.subscribers.set(token, callback);
        });
    }

    handleMessage(data) {
        const buffer = new Uint8Array(data);
        const marketData = this.parseMarketData(buffer);

        const callback = this.subscribers.get(marketData.token);
        if (callback) {
            callback(marketData);
        }
    }

    parseMarketData(buffer) {
        if (buffer.length === 17) {
            return this.parseLTPC(buffer);
        } else if (buffer.length === 13) {
            return this.parseLTP(buffer);
        } else if (buffer.length >= 81) {
            return this.parseComplete(buffer);
        }
    }

    // Parser methods here...
}

// Usage
const client = new MarketDataClient('your_app_id', 'your_token');
await client.connect();

client.subscribe('ltpc', [26009, 26000], (data) => {
    console.log(`${data.token}: LTP=${data.ltp}, Change=${data.netChange}`);
});
import websocket
import json
import struct
from threading import Thread

class MarketDataClient:
    def __init__(self, app_id, token):
        self.ws_url = f"wss://stream.arrow.trade?appID={app_id}&token={token}"
        self.ws = None
        self.subscribers = {}

    def connect(self):
        websocket.enableTrace(True)
        self.ws = websocket.WebSocketApp(
            self.ws_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 subscribe(self, mode, tokens, callback):
        message = {
            'code': 'sub',
            'mode': mode,
            mode: tokens
        }

        self.ws.send(json.dumps(message))

        for token in tokens:
            self.subscribers[token] = callback

    def on_message(self, ws, message):
        market_data = self.parse_market_data(message)

        callback = self.subscribers.get(market_data['token'])
        if callback:
            callback(market_data)

    def parse_market_data(self, data):
        if len(data) == 17:
            return self.parse_ltpc(data)
        elif len(data) == 13:
            return self.parse_ltp(data)
        elif len(data) >= 81:
            return self.parse_complete(data)

    # Parser methods here...

# Usage
client = MarketDataClient('your_app_id', 'your_token')
client.connect()

def price_handler(data):
    print(f"{data['token']}: LTP={data['ltp']}, Change={data['net_change']}")

client.subscribe('ltpc', [26009, 26000], price_handler)

Price Scaling and Precision

All prices in the binary data are scaled integers. Convert to actual prices by dividing by the appropriate factor:

Market Price Factor Example
Equity 100 257525.75
F&O 100 475047.50
Currency 10000 752500075.25
Commodity 100 612561.25

Error Handling and Reconnection

Connection Management

class ReconnectingWebSocket {
    constructor(url) {
        this.url = url;
        this.reconnectInterval = 5000;
        this.maxReconnectAttempts = 10;
        this.reconnectAttempts = 0;
    }

    connect() {
        this.ws = new WebSocket(this.url);

        this.ws.onopen = () => {
            this.reconnectAttempts = 0;
            this.resubscribeAll();
        };

        this.ws.onclose = () => {
            if (this.reconnectAttempts < this.maxReconnectAttempts) {
                setTimeout(() => {
                    this.reconnectAttempts++;
                    this.connect();
                }, this.reconnectInterval);
            }
        };
    }
}

Common Error Scenarios

Error Cause Solution
Connection Failed Invalid credentials Verify appID and token
Authentication Error Expired token Refresh authentication token
Data Corruption Network issues Implement data validation
Rate Limiting Too many subscriptions Throttle subscription requests

Performance Optimization

Best Practices

Subscription Management - Batch Subscriptions: Subscribe to multiple instruments in single requests - Mode Selection: Use the minimal data mode required for your use case - Selective Unsubscription: Remove unused subscriptions to reduce bandwidth - Connection Pooling: Reuse connections across multiple data feeds

Resource Management - Memory Usage: Parse and process data efficiently to prevent memory leaks
- CPU Optimization: Use optimized binary parsers for high-frequency data - Network Bandwidth: Monitor data throughput, especially with Full mode subscriptions - Buffer Management: Implement proper buffer handling for binary data streams

Subscription Limits

Account Type Max Instruments Max Modes Rate Limit
Basic 100 2 10/sec
Premium 500 4 50/sec
Enterprise Unlimited Unlimited 1000/sec

Market Data Applications

Real-Time Portfolio Monitoring

// Track P&L in real-time
function trackPortfolioValue(positions, marketData) {
    const currentValue = positions.reduce((total, position) => {
        const ltp = marketData[position.token]?.ltp || 0;
        const positionValue = position.quantity * ltp;
        return total + positionValue;
    }, 0);

    return currentValue;
}

Order Book Analysis

// Calculate bid-ask spread and depth
function analyzeOrderBook(depth) {
    const bestBid = depth.bids[0];
    const bestAsk = depth.asks[0]; 

    return {
        spread: bestAsk.price - bestBid.price,
        spreadPercent: ((bestAsk.price - bestBid.price) / bestBid.price) * 100,
        bidDepth: depth.bids.reduce((sum, level) => sum + level.quantity, 0),
        askDepth: depth.asks.reduce((sum, level) => sum + level.quantity, 0)
    };
}

Alert Systems

// Price movement alerts
function createPriceAlert(token, threshold, callback) {
    return (marketData) => {
        if (marketData.token === token) {
            const changePercent = (marketData.netChange / marketData.previousClose) * 100;
            if (Math.abs(changePercent) >= threshold) {
                callback({
                    token: token,
                    change: changePercent,
                    ltp: marketData.ltp
                });
            }
        }
    };
}