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
Authentication Parameters
Connect using your application credentials as query parameters:
JavaScript
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:
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)
Subscribe to High-Frequency Price Feed (LTP)
Subscribe to Complete Market Data (Quote)
Subscribe to Full Market Depth
Unsubscribe from Instruments
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 | 2575 → 25.75 |
| F&O | 100 | 4750 → 47.50 |
| Currency | 10000 | 7525000 → 75.25 |
| Commodity | 100 | 6125 → 61.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
});
}
}
};
}