# Arrow API & Python SDK — documentation export for LLMs > Canonical documentation site: https://docs.arrow.trade > Compact index: https://docs.arrow.trade/llms.txt Keywords for retrieval: Arrow Trade, arrow.trade, broker API, trading API, stock broker REST API, Python SDK, WebSocket market data, orders, margin, iRage. --- ## Source file: `python-sdk/api-reference.md` **Canonical URL:** https://docs.arrow.trade/python-sdk/api-reference/ # API Reference Complete reference documentation for all PyArrow SDK classes, methods, and constants. ## ArrowClient The main client class for interacting with Arrow Trading APIs. ### Initialization ```python from pyarrow_client import ArrowClient client = ArrowClient(app_id="your_app_id") ``` | Parameter | Type | Required | Description | |-----------|------|----------|-------------| | `app_id` | string | ✓ | Your application identifier | --- ## Authentication Methods ### login() Exchange request token for access token. ```python response = client.login(request_token="token", api_secret="secret") ``` | Parameter | Type | Required | Description | |-----------|------|----------|-------------| | `request_token` | string | ✓ | Token from OAuth callback | | `api_secret` | string | ✓ | Your application secret | **Returns:** `Dict[str, str]` - User data with access token --- ### auto_login() Automated login with credentials and TOTP. ```python response = client.auto_login( user_id="user_id", password="password", api_secret="api_secret", totp_secret="totp_secret" ) ``` | Parameter | Type | Required | Description | |-----------|------|----------|-------------| | `user_id` | string | ✓ | User ID | | `password` | string | ✓ | Account password | | `api_secret` | string | ✓ | Application secret | | `totp_secret` | string | ✓ | Base32 TOTP secret | **Returns:** `Dict[str, str]` - User data with access token --- ### login_url() Get the OAuth login URL. ```python url = client.login_url() ``` **Returns:** `str` - Login URL --- ### set_token() Manually set the access token. ```python client.set_token("your_access_token") ``` | Parameter | Type | Required | Description | |-----------|------|----------|-------------| | `token` | string | ✓ | Access token | **Returns:** `None` --- ### get_token() Get the current access token. ```python token = client.get_token() ``` **Returns:** `str` - Current access token --- ### invalidate_session() Clear the current session. ```python client.invalidate_session() ``` **Returns:** `None` --- ## Order Methods ### place_order() Place a new trading order. ```python order_id = client.place_order( exchange=Exchange.NSE, symbol="RELIANCE-EQ", quantity=1, disclosed_quantity=0, product=ProductType.CNC, order_type=OrderType.LIMIT, variety=Variety.REGULAR, transaction_type=TransactionType.BUY, price=1450.0, validity=Retention.DAY ) ``` | Parameter | Type | Required | Description | |-----------|------|----------|-------------| | `exchange` | Exchange | ✓ | Exchange (NSE, BSE, NFO, BFO) | | `symbol` | string | ✓ | Trading symbol | | `quantity` | int | ✓ | Order quantity | | `disclosed_quantity` | int | ✓ | Disclosed quantity (0 for none) | | `product` | ProductType | ✓ | Product type (MIS, CNC, NRML) | | `order_type` | OrderType | ✓ | Order type (MKT, LIMIT, SL_LMT, SL_MKT) | | `variety` | Variety | ✓ | Order variety (REGULAR, COVER) | | `transaction_type` | TransactionType | ✓ | Buy or Sell | | `price` | float | ✓ | Order price (0 for market) | | `validity` | Retention | ✓ | Order validity (DAY, IOC) | | `trigger_price` | float | - | Trigger price for SL orders | | `remarks` | string | - | Custom order tag | **Returns:** `str` - Order ID --- ### modify_order() Modify an existing open order. ```python message = client.modify_order( order_id="24012400000321", exchange=Exchange.NSE, symbol="RELIANCE-EQ", quantity=2, price=1500.0, disclosed_qty=0, product=ProductType.CNC, transaction_type=TransactionType.BUY, order_type=OrderType.LIMIT, validity=Retention.DAY, remarks="Modified" ) ``` | Parameter | Type | Required | Description | |-----------|------|----------|-------------| | `order_id` | string | ✓ | Order ID to modify | | `exchange` | Exchange | ✓ | Exchange | | `symbol` | string | ✓ | Trading symbol | | `quantity` | int | ✓ | New quantity | | `price` | float | ✓ | New price | | `disclosed_qty` | int | ✓ | Disclosed quantity | | `product` | ProductType | ✓ | Product type | | `transaction_type` | TransactionType | ✓ | Transaction type | | `order_type` | OrderType | ✓ | Order type | | `validity` | Retention | ✓ | Order validity | | `remarks` | string | - | Custom remarks | **Returns:** `str` - Modification message --- ### cancel_order() Cancel a pending order. ```python message = client.cancel_order(order_id="24012400000321") ``` | Parameter | Type | Required | Description | |-----------|------|----------|-------------| | `order_id` | string | ✓ | Order ID to cancel | **Returns:** `str` - Cancellation message --- ### cancel_all_orders() Cancel all open/pending orders. ```python results = client.cancel_all_orders() # Returns: List[Tuple[order_id, status, result]] ``` **Returns:** `List[Tuple]` - List of (order_id, status, result) tuples --- ### get_order_details() Get detailed order history. ```python details = client.get_order_details(order_id="24012400000321") ``` | Parameter | Type | Required | Description | |-----------|------|----------|-------------| | `order_id` | string | ✓ | Order ID | **Returns:** `List[Dict]` - Order history with status changes --- ### get_order_book() Get all orders for the day. ```python orders = client.get_order_book() ``` **Returns:** `List[Dict]` - All user orders --- ### get_trade_book() Get all executed trades. ```python trades = client.get_trade_book() ``` **Returns:** `List[Dict]` - All user trades --- ## Portfolio Methods ### get_positions() Get current positions. ```python positions = client.get_positions() ``` **Returns:** `List[Dict]` - Current positions --- ### get_holdings() Get delivery holdings. ```python holdings = client.get_holdings() ``` **Returns:** `Dict` - Holdings data --- ## User Methods ### get_user_details() Get user profile information. ```python user = client.get_user_details() ``` **Returns:** `Dict` - User profile data --- ### get_user_limits() Get margin and trading limits. ```python limits = client.get_user_limits() ``` **Returns:** `List[Dict]` - User limits by segment --- ## Margin Methods ### order_margin() Calculate margin for a single order. ```python margin = client.order_margin( exchange=Exchange.NSE, symbol="RELIANCE-EQ", quantity=100, product=ProductType.CNC, order_type=OrderType.LIMIT, transaction_type=TransactionType.BUY, price=1500.0, include_positions=False ) ``` | Parameter | Type | Required | Description | |-----------|------|----------|-------------| | `exchange` | Exchange | ✓ | Exchange | | `symbol` | string | ✓ | Trading symbol | | `quantity` | int | ✓ | Order quantity | | `product` | ProductType | ✓ | Product type | | `order_type` | OrderType | ✓ | Order type | | `transaction_type` | TransactionType | ✓ | Buy or Sell | | `price` | float | ✓ | Order price | | `include_positions` | bool | - | Include existing positions | **Returns:** `Dict` - Margin requirement --- ### basket_margin() Calculate margin for multiple orders. ```python orders = [ { "exchange": "NSE", "symbol": "RELIANCE-EQ", "quantity": "100", "product": ProductType.CNC, "order": OrderType.LIMIT, "transactionType": TransactionType.BUY, "price": "1500.00" } ] margin = client.basket_margin(orders, include_positions=False) ``` | Parameter | Type | Required | Description | |-----------|------|----------|-------------| | `orders` | List[Dict] | ✓ | List of order dictionaries | | `include_positions` | bool | - | Include existing positions | **Returns:** `Dict` - Combined margin requirement --- ## Market Data Methods ### get_quote() Get quote for a single instrument. ```python quote = client.get_quote(QuoteMode.LTP, "RELIANCE-EQ", Exchange.NSE) ``` | Parameter | Type | Required | Description | |-----------|------|----------|-------------| | `mode` | QuoteMode | ✓ | Quote mode (LTP, OHLCV, FULL) | | `symbol` | string | ✓ | Trading symbol | | `exchange` | Exchange | ✓ | Exchange | **Returns:** `Dict` - Quote data --- ### get_quotes() Get quotes for multiple instruments. ```python quotes = client.get_quotes( QuoteMode.LTP, symbols=[("RELIANCE-EQ", Exchange.NSE), ("INFY-EQ", Exchange.NSE)] ) ``` | Parameter | Type | Required | Description | |-----------|------|----------|-------------| | `mode` | QuoteMode | ✓ | Quote mode | | `symbols` | List[Tuple] | ✓ | List of (symbol, exchange) tuples | **Returns:** `List[Dict]` - Quote data for all symbols --- ### candle_data() Get historical candle data. ```python candles = client.candle_data( exchange=Exchange.NSE, token="3045", interval="5min", from_timestamp="2024-01-15T09:15:00", to_timestamp="2024-01-15T15:30:00", oi=False ) ``` | Parameter | Type | Required | Description | |-----------|------|----------|-------------| | `exchange` | Exchange | ✓ | Exchange | | `token` | string | ✓ | Instrument token | | `interval` | string | ✓ | Candle interval | | `from_timestamp` | string | ✓ | Start timestamp (ISO format) | | `to_timestamp` | string | ✓ | End timestamp (ISO format) | | `oi` | bool | - | Include Open Interest | **Intervals:** `min`, `3min`, `5min`, `15min`, `30min`, `hour`, `day` **Returns:** `Dict` - Candle data --- ### get_instruments() Get all tradable instruments. ```python instruments = client.get_instruments() ``` **Returns:** `Any` - List of instruments --- ### get_holidays() Get market holidays. ```python holidays = client.get_holidays() ``` **Returns:** `Dict` - Holiday calendar --- ### get_index_list() Get available indices. ```python indices = client.get_index_list() ``` **Returns:** `List[Dict]` - Index listings --- ### get_option_chain_symbols() Get option chain symbols. ```python symbols = client.get_option_chain_symbols() ``` **Returns:** `Any` - Option chain symbols --- ### get_expiry_dates() Get expiry dates for options (static method). ```python expiries = ArrowClient.get_expiry_dates("NIFTY", 2024) ``` | Parameter | Type | Required | Description | |-----------|------|----------|-------------| | `symbol` | string | ✓ | Underlying symbol | | `year` | int | ✓ | Year | **Returns:** `Any` - Expiry dates --- ## ArrowStreams WebSocket streaming class for real-time data. ### Initialization ```python from pyarrow_client import ArrowStreams streams = ArrowStreams( appID="your_app_id", token="your_access_token", debug=True ) ``` | Parameter | Type | Required | Description | |-----------|------|----------|-------------| | `appID` | string | ✓ | Application ID | | `token` | string | ✓ | Access token | | `debug` | bool | - | Enable debug logging | --- ### Connection Methods | Method | Description | |--------|-------------| | `connect_order_stream()` | Connect to order updates | | `connect_data_stream()` | Connect to market data | | `connect_all()` | Connect to both streams | | `disconnect_all()` | Disconnect all streams | | `get_status()` | Get connection status | --- ### Subscription Methods | Method | Description | |--------|-------------| | `subscribe_market_data(mode, tokens)` | Subscribe to market data | | `unsubscribe_market_data(mode, tokens)` | Unsubscribe from data | --- ### Event Handlers #### Data Stream Events | Event | Parameters | Description | |-------|------------|-------------| | `on_ticks` | `MarketTick` | Market data received | | `on_connect` | None | Connection established | | `on_disconnect` | None | Connection lost | | `on_error` | `Exception` | Error occurred | | `on_close` | `close_status_code`, `close_msg` | Connection closed | | `on_reconnect` | `attempt`, `delay` | Reconnection attempt | | `on_no_reconnect` | None | Max attempts reached | #### Order Stream Events | Event | Parameters | Description | |-------|------------|-------------| | `on_order_update` | `Dict` | Order status changed | --- ## Constants ### Exchange ```python from pyarrow_client import Exchange Exchange.NSE # National Stock Exchange Exchange.BSE # Bombay Stock Exchange Exchange.NFO # NSE F&O Exchange.BFO # BSE F&O ``` --- ### OrderType ```python from pyarrow_client import OrderType OrderType.MKT # Market Order OrderType.LIMIT # Limit Order OrderType.SL_LMT # Stop Loss Limit OrderType.SL_MKT # Stop Loss Market ``` --- ### ProductType ```python from pyarrow_client import ProductType ProductType.MIS # Intraday ProductType.CNC # Cash and Carry (Delivery) ProductType.NRML # Normal (F&O) ``` --- ### TransactionType ```python from pyarrow_client import TransactionType TransactionType.BUY # Buy (B) TransactionType.SELL # Sell (S) ``` --- ### Variety ```python from pyarrow_client import Variety Variety.REGULAR # Regular order Variety.COVER # Cover order ``` --- ### Retention ```python from pyarrow_client import Retention Retention.DAY # Day order Retention.IOC # Immediate or Cancel ``` --- ### QuoteMode ```python from pyarrow_client import QuoteMode QuoteMode.LTP # Last Traded Price QuoteMode.OHLCV # OHLC + Volume QuoteMode.FULL # Full market depth ``` --- ### DataMode ```python from pyarrow_client import DataMode DataMode.LTPC # LTP + Close (17 bytes) DataMode.QUOTE # Detailed quote (93 bytes) DataMode.FULL # Full depth (241 bytes) ``` --- ## MarketTick Properties | Property | Type | Mode | Description | |----------|------|------|-------------| | `token` | int | All | Instrument token | | `ltp` | float | All | Last traded price | | `mode` | str | All | Data mode | | `close` | float | All | Previous close | | `net_change` | float | All | % change | | `change_flag` | int | All | 43(+), 45(-), 32(=) | | `open` | float | Quote+ | Open price | | `high` | float | Quote+ | High price | | `low` | float | Quote+ | Low price | | `volume` | int | Quote+ | Volume | | `ltq` | int | Quote+ | Last traded qty | | `avg_price` | float | Quote+ | Average price | | `total_buy_quantity` | int | Quote+ | Total buy qty | | `total_sell_quantity` | int | Quote+ | Total sell qty | | `ltt` | datetime | Quote+ | Last trade time | | `time` | datetime | Quote+ | Timestamp | | `oi` | int | Quote+ | Open interest | | `oi_day_high` | int | Quote+ | OI day high | | `oi_day_low` | int | Quote+ | OI day low | | `upper_limit` | float | Full | Upper circuit | | `lower_limit` | float | Full | Lower circuit | | `bids` | List | Full | 5 bid levels | | `asks` | List | Full | 5 ask levels | --- ## Error Codes | Code | Description | |------|-------------| | `INVALID_TOKEN` | Invalid or expired access token | | `INVALID_APP_ID` | Invalid application ID | | `MARGIN_ERROR` | Insufficient margin | | `INVALID_ORDER` | Invalid order parameters | | `ORDER_NOT_FOUND` | Order ID not found | | `RATE_LIMIT` | API rate limit exceeded | | `EXCHANGE_ERROR` | Exchange connectivity issue | --- ## Source file: `python-sdk/authentication.md` **Canonical URL:** https://docs.arrow.trade/python-sdk/authentication/ # Authentication The PyArrow SDK provides secure OAuth-based authentication with support for manual web-based login and fully automated login with TOTP integration. ## Prerequisites Before authenticating, ensure you have: - Valid Arrow user credentials - Registered redirect URL in the [Developer Apps](https://app.arrow.trade) section - Your `appID` and `appSecret` from the Trading API section - Static IP registered (mandatory per [SEBI Circular](https://www.sebi.gov.in/legal/circulars/feb-2025/safer-participation-of-retail-investors-in-algorithmic-trading_91614.html)) ## Authentication Methods ### Method 1: Web-based Login (Manual) This method redirects users to Arrow's login page for authentication. ```python from pyarrow_client import ArrowClient # Initialize the client client = ArrowClient(app_id="your_app_id") # Step 1: Get the login URL login_url = client.login_url() print(f"Please visit: {login_url}") # Step 2: User completes login and gets redirected to your callback URL # Extract the request_token from the callback URL query parameters # Step 3: Exchange request token for access token client.login( request_token="token_from_callback_url", api_secret="your_api_secret" ) # Verify authentication print(f"Access Token: {client.get_token()}") ``` !!! info "Callback URL" After successful login, Arrow redirects to your registered URL with: - `request-token`: Temporary authentication token - `checksum`: SHA256 hash for verification ### Method 2: Automated Login (TOTP) For fully automated systems, use the `auto_login` method with TOTP. ```python from pyarrow_client import ArrowClient client = ArrowClient(app_id="your_app_id") # Automated login with all credentials client.auto_login( user_id="your_user_id", password="your_password", api_secret="your_api_secret", totp_secret="your_totp_secret" # Base32 TOTP secret ) # Client is now authenticated print(f"Logged in successfully!") print(f"Token: {client.get_token()}") ``` !!! tip "TOTP Secret" The `totp_secret` is the base32 encoded secret used to generate time-based one-time passwords. You can find this when setting up 2FA in your Arrow account. ## Session Management ### Get Current Token ```python # Retrieve the current access token token = client.get_token() print(f"Current token: {token}") ``` ### Set Token Manually If you have a valid token from a previous session: ```python # Set an existing token client.set_token("your_existing_access_token") ``` ### Invalidate Session Clear the current session and token: ```python # Logout and clear session client.invalidate_session() print("Session invalidated") ``` ## Token Lifecycle | Aspect | Details | |--------|---------| | **Validity** | 24 hours from generation | | **Refresh** | New login required after expiration | | **Storage** | Store securely; never expose in client-side code | !!! warning "Token Expiration" Access tokens expire after 24 hours due to regulatory compliance. Implement proper token refresh mechanisms in your application. ## Authentication Response Successful authentication returns user details: ```python response = client.login( request_token="your_request_token", api_secret="your_api_secret" ) print(response) # { # "name": "ABHISHEK JAIN", # "token": "eyJhbGciOiJFZERTQSIsInR5cCI6IkpXVCJ9...", # "userID": "AJ0001" # } ``` | Field | Type | Description | |-------|------|-------------| | `name` | string | User's full name | | `token` | string | JWT access token | | `userID` | string | Unique user identifier | ## User Information After authentication, retrieve user details: ```python # Get user profile user = client.get_user_details() print(f"User: {user}") # Get trading limits and margins limits = client.get_user_limits() print(f"Available margin: {limits}") ``` ## Error Handling Handle authentication errors gracefully: ```python from pyarrow_client import ArrowClient client = ArrowClient(app_id="your_app_id") try: client.auto_login( user_id="your_user_id", password="your_password", api_secret="your_api_secret", totp_secret="your_totp_secret" ) print("Login successful!") except Exception as e: print(f"Authentication failed: {e}") ``` ### Common Errors | Error | Cause | Solution | |-------|-------|----------| | Invalid checksum | Incorrect SHA256 generation | Verify `appID:appSecret:request-token` format | | Token expired | Request token timeout | Restart authentication flow | | Invalid credentials | Wrong user ID or password | Verify credentials | | Invalid TOTP | Incorrect or expired OTP | Check TOTP secret and system time sync | ## Security Best Practices !!! danger "Security Notice" - **Never** expose `appSecret` in client-side code - **Never** commit credentials to version control - **Always** use environment variables for sensitive data - **Always** use HTTPS for all API communications ### Environment Variables Example ```python import os from pyarrow_client import ArrowClient client = ArrowClient(app_id=os.environ["ARROW_APP_ID"]) client.auto_login( user_id=os.environ["ARROW_USER_ID"], password=os.environ["ARROW_PASSWORD"], api_secret=os.environ["ARROW_API_SECRET"], totp_secret=os.environ["ARROW_TOTP_SECRET"] ) ``` ## Complete Example ```python import os from pyarrow_client import ArrowClient def initialize_arrow_client(): """Initialize and authenticate Arrow client.""" # Initialize client client = ArrowClient(app_id=os.environ["ARROW_APP_ID"]) # Automated login try: client.auto_login( user_id=os.environ["ARROW_USER_ID"], password=os.environ["ARROW_PASSWORD"], api_secret=os.environ["ARROW_API_SECRET"], totp_secret=os.environ["ARROW_TOTP_SECRET"] ) print("✓ Authentication successful") # Verify by fetching user details user = client.get_user_details() print(f"✓ Logged in as: {user.get('name', 'Unknown')}") return client except Exception as e: print(f"✗ Authentication failed: {e}") return None # Usage if __name__ == "__main__": client = initialize_arrow_client() if client: # Your trading logic here pass ``` --- ## Source file: `python-sdk/getting-started.md` **Canonical URL:** https://docs.arrow.trade/python-sdk/getting-started/ # Getting Started Welcome to PyArrow, the official Python SDK for the Arrow Trading Platform. This SDK provides comprehensive access to trading APIs, real-time market data, and order management capabilities built for traders, quants, and fintech developers. ## Key Features
- :material-chart-line:{ .lg .middle } **Market Data & Analytics** --- Real-time quotes, OHLC, LTP, market depth, historical candles, option chains, and more. - :material-cart:{ .lg .middle } **Order Management** --- Place, modify, and cancel orders across NSE, BSE, NFO, BFO exchanges with support for all order types. - :material-broadcast:{ .lg .middle } **Real-time Streaming** --- WebSocket-based live market data feeds with automatic reconnection and thread-safe event handling. - :material-shield-lock:{ .lg .middle } **Secure Authentication** --- OAuth-based authentication with TOTP integration and automatic session management.
## Installation Install the PyArrow client using pip: ```bash pip install pyarrow-client ``` The package is published on PyPI as `pyarrow-client` (hyphen). In Python, import it as `pyarrow_client` (underscore)—not `pyarrow`, which is the [Apache Arrow](https://pypi.org/project/pyarrow/) library and is unrelated to this SDK. ### Requirements | Package | Version | |---------|---------| | Python | 3.7+ | | requests | Latest | | websocket-client | Latest | | pyotp | Latest | | python-dateutil | Latest | ## Quick Start ### 1. Initialize the Client ```python from pyarrow_client import ArrowClient # Initialize with your application ID client = ArrowClient(app_id="your_app_id") ``` ### 2. Authenticate === "Web-based Login" ```python # Get the login URL login_url = client.login_url() print(f"Visit: {login_url}") # After user authorizes, extract request_token from callback URL client.login( request_token="token_from_callback", api_secret="your_api_secret" ) ``` === "Automated Login" ```python # Fully automated login with credentials client.auto_login( user_id="your_user_id", password="your_password", api_secret="your_api_secret", totp_secret="your_totp_secret" ) ``` ### 3. Place Your First Order ```python from pyarrow_client import Exchange, OrderType, ProductType, TransactionType, Variety, Retention # Place a buy order order_id = client.place_order( exchange=Exchange.NSE, symbol="RELIANCE-EQ", quantity=1, disclosed_quantity=0, product=ProductType.CNC, order_type=OrderType.LIMIT, variety=Variety.REGULAR, transaction_type=TransactionType.BUY, price=1450.0, validity=Retention.DAY ) print(f"Order placed successfully: {order_id}") ``` ### 4. Get Market Data ```python from pyarrow_client import QuoteMode, Exchange # Get real-time quote quote = client.get_quote(QuoteMode.LTP, "RELIANCE-EQ", Exchange.NSE) print(f"Last Traded Price: ₹{quote['ltp']}") # Get detailed OHLCV data ohlcv = client.get_quote(QuoteMode.OHLCV, "RELIANCE-EQ", Exchange.NSE) print(f"Open: {ohlcv['open']} | High: {ohlcv['high']} | Low: {ohlcv['low']} | Close: {ohlcv['close']}") ``` ### 5. Stream Live Data ```python from pyarrow_client import ArrowStreams, DataMode # Initialize WebSocket streams streams = ArrowStreams( appID="your_app_id", token="your_access_token", debug=True ) # Handle incoming market data def on_tick(tick): print(f"Token: {tick.token} | LTP: ₹{tick.ltp} | Change: {tick.net_change}%") streams.data_stream.on_ticks = on_tick # Connect and subscribe streams.connect_all() streams.subscribe_market_data(DataMode.QUOTE, [3045, 1594]) # RELIANCE, INFY tokens # Keep the connection alive import time try: while True: time.sleep(1) except KeyboardInterrupt: streams.disconnect_all() ``` ## What's Next? Now that you have the basics, explore the detailed documentation: | Topic | Description | |-------|-------------| | [Authentication](authentication.md) | Detailed authentication flows and session management | | [Orders](orders.md) | Complete order placement, modification, and cancellation | | [Portfolio](portfolio.md) | Positions, holdings, order book, and trade book | | [Market Data](market-data.md) | Quotes, instruments, and historical data | | [WebSocket Streaming](websocket-streaming.md) | Real-time market data and order updates | | [API Reference](api-reference.md) | Complete method reference and constants | ## Support | Resource | Link | |----------|------| | Documentation | [https://docs.arrow.trade](https://docs.arrow.trade) | | Support Email | [support@arrow.trade](mailto:support@arrow.trade) | | GitHub Issues | Report bugs or request features | !!! tip "Pro Tip" Start with the [Authentication](authentication.md) guide to understand the complete login flow before integrating with your trading application. --- ## Source file: `python-sdk/market-data.md` **Canonical URL:** https://docs.arrow.trade/python-sdk/market-data/ # Market Data The PyArrow SDK provides comprehensive market data access including real-time quotes, historical candles, instruments, and option chain information. ## Quote Modes | Mode | Code | Description | Data Included | |------|------|-------------|---------------| | LTP | `QuoteMode.LTP` | Last Traded Price | LTP, change | | OHLCV | `QuoteMode.OHLCV` | OHLC & Volume | Open, High, Low, Close, Volume | | FULL | `QuoteMode.FULL` | Complete market depth | All data + bid/ask levels | ## Single Instrument Quote Get real-time market data for a single instrument. ### LTP Quote ```python from pyarrow_client import ArrowClient, QuoteMode, Exchange client = ArrowClient(app_id="your_app_id") # ... authenticate ... # Get last traded price quote = client.get_quote(QuoteMode.LTP, "RELIANCE-EQ", Exchange.NSE) print(f"LTP: ₹{quote['ltp']}") print(f"Change: {quote['change']}%") ``` ### OHLCV Quote ```python # Get OHLC and volume data quote = client.get_quote(QuoteMode.OHLCV, "RELIANCE-EQ", Exchange.NSE) print(f"Open: ₹{quote['open']}") print(f"High: ₹{quote['high']}") print(f"Low: ₹{quote['low']}") print(f"Close: ₹{quote['close']}") print(f"Volume: {quote['volume']}") ``` ### Full Quote (Market Depth) ```python # Get complete market depth quote = client.get_quote(QuoteMode.FULL, "RELIANCE-EQ", Exchange.NSE) print(f"LTP: ₹{quote['ltp']}") print(f"Volume: {quote['volume']}") print(f"Open Interest: {quote.get('oi', 'N/A')}") # Bid-Ask spread print(f"\nBest Bid: ₹{quote['bids'][0]['price']} x {quote['bids'][0]['quantity']}") print(f"Best Ask: ₹{quote['asks'][0]['price']} x {quote['asks'][0]['quantity']}") # Full depth (5 levels) print("\n--- BID DEPTH ---") for i, bid in enumerate(quote['bids'][:5]): print(f"Level {i+1}: ₹{bid['price']:>10} x {bid['quantity']:>8}") print("\n--- ASK DEPTH ---") for i, ask in enumerate(quote['asks'][:5]): print(f"Level {i+1}: ₹{ask['price']:>10} x {ask['quantity']:>8}") ``` ## Multiple Instrument Quotes Get quotes for multiple instruments in a single API call. ### LTP for Multiple Symbols ```python # Get LTP for multiple symbols quotes = client.get_quotes( QuoteMode.LTP, symbols=[ ("RELIANCE-EQ", Exchange.NSE), ("INFY-EQ", Exchange.NSE), ("TCS-EQ", Exchange.NSE), ("IDEA-EQ", Exchange.BSE) ] ) for quote in quotes: print(f"{quote['symbol']}: ₹{quote['ltp']} ({quote['change']}%)") ``` ### OHLCV for Multiple Symbols ```python # Get OHLCV for multiple symbols quotes = client.get_quotes( QuoteMode.OHLCV, symbols=[ ("RELIANCE-EQ", Exchange.NSE), ("IDEA-EQ", Exchange.BSE) ] ) for quote in quotes: print(f"\n{quote['symbol']} ({quote['exchange']})") print(f" O: {quote['open']} | H: {quote['high']} | L: {quote['low']} | C: {quote['close']}") print(f" Volume: {quote['volume']}") ``` ### Full Quote for Multiple Symbols ```python # Get full market depth for multiple symbols quotes = client.get_quotes( QuoteMode.FULL, symbols=[ ("RELIANCE-EQ", Exchange.NSE), ("IDEA-EQ", Exchange.BSE) ] ) for quote in quotes: print(f"\n{quote['symbol']} - LTP: ₹{quote['ltp']}") print(f" Bid: ₹{quote['bids'][0]['price']} | Ask: ₹{quote['asks'][0]['price']}") ``` ## Historical Candle Data Retrieve historical OHLCV candle data for technical analysis. ### Available Intervals | Interval | Code | Description | |----------|------|-------------| | 1 Minute | `min` | 1-minute candles | | 3 Minutes | `3min` | 3-minute candles | | 5 Minutes | `5min` | 5-minute candles | | 15 Minutes | `15min` | 15-minute candles | | 30 Minutes | `30min` | 30-minute candles | | 1 Hour | `hour` | Hourly candles | | 1 Day | `day` | Daily candles | ### Basic Usage ```python from pyarrow_client import Exchange # Get 5-minute candle data candles = client.candle_data( exchange=Exchange.NSE, token="3045", # RELIANCE token interval="5min", from_timestamp="2024-01-15T09:15:00", to_timestamp="2024-01-15T15:30:00", oi=False # Include Open Interest (for F&O) ) print(f"Candles: {candles}") ``` ### Intraday Candles ```python from datetime import datetime, timedelta # Get today's 5-minute candles today = datetime.now() market_open = today.replace(hour=9, minute=15, second=0) market_close = today.replace(hour=15, minute=30, second=0) candles = client.candle_data( exchange=Exchange.NSE, token="3045", interval="5min", from_timestamp=market_open.strftime("%Y-%m-%dT%H:%M:%S"), to_timestamp=market_close.strftime("%Y-%m-%dT%H:%M:%S"), oi=False ) # Process candles for candle in candles.get('data', []): print(f"Time: {candle['time']}") print(f" O: {candle['open']} H: {candle['high']} L: {candle['low']} C: {candle['close']}") print(f" Volume: {candle['volume']}") ``` ### Daily Candles ```python # Get daily candles for the past month from datetime import datetime, timedelta end_date = datetime.now() start_date = end_date - timedelta(days=30) candles = client.candle_data( exchange=Exchange.NSE, token="3045", interval="day", from_timestamp=start_date.strftime("%Y-%m-%dT09:15:00"), to_timestamp=end_date.strftime("%Y-%m-%dT15:30:00"), oi=False ) print(f"Retrieved {len(candles.get('data', []))} daily candles") ``` ### F&O Candles with Open Interest ```python # Get F&O candles with OI data candles = client.candle_data( exchange=Exchange.NFO, token="46799", # NIFTY option token interval="15min", from_timestamp="2024-01-15T09:15:00", to_timestamp="2024-01-15T15:30:00", oi=True # Include Open Interest ) for candle in candles.get('data', []): print(f"Time: {candle['time']} | OI: {candle.get('oi', 'N/A')}") ``` ## Instruments Get the complete list of tradable instruments. ```python # Get all instruments instruments = client.get_instruments() # Filter NSE equity instruments nse_equity = [i for i in instruments if i.get('exchange') == 'NSE' and i.get('segment') == 'EQ'] print(f"NSE Equity instruments: {len(nse_equity)}") # Find specific instrument reliance = next((i for i in instruments if i.get('symbol') == 'RELIANCE-EQ'), None) if reliance: print(f"RELIANCE Token: {reliance['token']}") print(f"Lot Size: {reliance['lotSize']}") print(f"Tick Size: {reliance['tickSize']}") ``` ### Instrument Fields | Field | Description | |-------|-------------| | `token` | Unique instrument token | | `symbol` | Trading symbol | | `name` | Instrument name | | `exchange` | Exchange code | | `segment` | Market segment | | `lotSize` | Minimum trading lot | | `tickSize` | Minimum price movement | | `expiry` | Expiry date (for derivatives) | | `strike` | Strike price (for options) | | `optionType` | CE (Call) / PE (Put) | ## Option Chain Get option chain symbols and data. ### Get Option Chain Symbols ```python # Get all option chain symbols option_symbols = client.get_option_chain_symbols() print(f"Available option chains: {option_symbols}") ``` ### Get Expiry Dates ```python # Get expiry dates for NIFTY options (static method) expiries = ArrowClient.get_expiry_dates("NIFTY", 2024) print(f"NIFTY Expiries: {expiries}") ``` ## Index List Get available index listings. ```python # Get all indices indices = client.get_index_list() for index in indices: print(f"{index['name']}: {index['symbol']}") ``` ## Market Holidays Get market holiday calendar. ```python # Get market holidays holidays = client.get_holidays() print("Market Holidays:") for holiday in holidays.get('data', []): print(f" {holiday['date']}: {holiday['description']}") ``` ## Example: Market Scanner ```python from pyarrow_client import ArrowClient, QuoteMode, Exchange def market_scanner(client, symbols): """Scan multiple stocks and identify opportunities.""" # Build symbol list for quotes symbol_list = [(sym, Exchange.NSE) for sym in symbols] # Get OHLCV quotes quotes = client.get_quotes(QuoteMode.OHLCV, symbols=symbol_list) gainers = [] losers = [] high_volume = [] for quote in quotes: symbol = quote['symbol'] change = float(quote.get('change', 0)) volume = int(quote.get('volume', 0)) high = float(quote.get('high', 0)) low = float(quote.get('low', 0)) ltp = float(quote.get('ltp', 0)) # Categorize if change > 2: gainers.append((symbol, change)) elif change < -2: losers.append((symbol, change)) # High volume check (example threshold) if volume > 1000000: high_volume.append((symbol, volume)) # Display results print("\n📈 TOP GAINERS (>2%)") for sym, chg in sorted(gainers, key=lambda x: x[1], reverse=True)[:5]: print(f" {sym}: +{chg:.2f}%") print("\n📉 TOP LOSERS (<-2%)") for sym, chg in sorted(losers, key=lambda x: x[1])[:5]: print(f" {sym}: {chg:.2f}%") print("\n📊 HIGH VOLUME") for sym, vol in sorted(high_volume, key=lambda x: x[1], reverse=True)[:5]: print(f" {sym}: {vol:,}") # Usage watchlist = ["RELIANCE-EQ", "TCS-EQ", "INFY-EQ", "HDFCBANK-EQ", "ICICIBANK-EQ"] market_scanner(client, watchlist) ``` ## Example: Technical Analysis Setup ```python from pyarrow_client import ArrowClient, Exchange from datetime import datetime, timedelta def get_technical_data(client, symbol, token, days=30): """Fetch data for technical analysis.""" end_date = datetime.now() start_date = end_date - timedelta(days=days) # Get daily candles candles = client.candle_data( exchange=Exchange.NSE, token=token, interval="day", from_timestamp=start_date.strftime("%Y-%m-%dT09:15:00"), to_timestamp=end_date.strftime("%Y-%m-%dT15:30:00"), oi=False ) data = candles.get('data', []) if not data: print(f"No data for {symbol}") return None # Calculate simple moving averages closes = [float(c['close']) for c in data] def sma(prices, period): if len(prices) < period: return None return sum(prices[-period:]) / period sma_5 = sma(closes, 5) sma_20 = sma(closes, 20) latest = data[-1] print(f"\n📊 {symbol} Technical Summary") print("=" * 40) print(f"Latest Close: ₹{latest['close']}") print(f"5-Day SMA: ₹{sma_5:.2f}" if sma_5 else "5-Day SMA: N/A") print(f"20-Day SMA: ₹{sma_20:.2f}" if sma_20 else "20-Day SMA: N/A") # Simple trend detection if sma_5 and sma_20: if sma_5 > sma_20: print("Trend: 📈 BULLISH (5 SMA > 20 SMA)") else: print("Trend: 📉 BEARISH (5 SMA < 20 SMA)") # 52-week high/low from available data highs = [float(c['high']) for c in data] lows = [float(c['low']) for c in data] print(f"\n{days}-Day High: ₹{max(highs):.2f}") print(f"{days}-Day Low: ₹{min(lows):.2f}") return { 'symbol': symbol, 'close': float(latest['close']), 'sma_5': sma_5, 'sma_20': sma_20, 'period_high': max(highs), 'period_low': min(lows) } # Usage tech_data = get_technical_data(client, "RELIANCE-EQ", "3045", days=30) ``` ## Quote Response Fields ### LTP Mode Fields | Field | Type | Description | |-------|------|-------------| | `symbol` | string | Trading symbol | | `ltp` | number | Last traded price | | `change` | number | Price change % | | `timestamp` | string | Quote timestamp | ### OHLCV Mode Fields | Field | Type | Description | |-------|------|-------------| | `open` | number | Opening price | | `high` | number | Day's high | | `low` | number | Day's low | | `close` | number | Previous close | | `ltp` | number | Last traded price | | `volume` | number | Traded volume | ### Full Mode Additional Fields | Field | Type | Description | |-------|------|-------------| | `oi` | number | Open Interest | | `oi_day_high` | number | OI day high | | `oi_day_low` | number | OI day low | | `upper_limit` | number | Upper circuit limit | | `lower_limit` | number | Lower circuit limit | | `bids` | array | 5 bid levels | | `asks` | array | 5 ask levels | | `total_buy_quantity` | number | Total buy quantity | | `total_sell_quantity` | number | Total sell quantity | --- ## Source file: `python-sdk/orders.md` **Canonical URL:** https://docs.arrow.trade/python-sdk/orders/ # Orders The PyArrow SDK provides comprehensive order management capabilities including placing, modifying, and canceling orders across all supported exchanges. ## Order Parameters ### Exchanges | Exchange | Code | Description | |----------|------|-------------| | NSE | `Exchange.NSE` | National Stock Exchange - Equity | | BSE | `Exchange.BSE` | Bombay Stock Exchange - Equity | | NFO | `Exchange.NFO` | NSE Futures & Options | | BFO | `Exchange.BFO` | BSE Futures & Options | ### Order Types | Type | Code | Description | Use Case | |------|------|-------------|----------| | Market | `OrderType.MKT` | Execute at best available price | Immediate execution required | | Limit | `OrderType.LIMIT` | Execute at specified price or better | Price control priority | | Stop Loss Limit | `OrderType.SL_LMT` | Limit order activated at trigger | Risk management with price control | | Stop Loss Market | `OrderType.SL_MKT` | Market order activated at trigger | Risk management with speed priority | ### Product Types | Product | Code | Description | Settlement | |---------|------|-------------|------------| | Intraday | `ProductType.MIS` | Same-day position closure | Auto-squared off at 3:15 PM | | Cash & Carry | `ProductType.CNC` | Equity delivery orders | T+1 settlement | | Normal | `ProductType.NRML` | F&O margin orders | Standard margin | ### Order Validity | Validity | Code | Description | |----------|------|-------------| | Day | `Retention.DAY` | Valid until market close | | IOC | `Retention.IOC` | Immediate or Cancel | ### Transaction Types | Type | Code | Description | |------|------|-------------| | Buy | `TransactionType.BUY` | Buy transaction | | Sell | `TransactionType.SELL` | Sell transaction | ## Place Order Place a new trading order with full parameter control. ### Basic Example ```python from pyarrow_client import ( ArrowClient, Exchange, OrderType, ProductType, TransactionType, Variety, Retention ) client = ArrowClient(app_id="your_app_id") # ... authenticate ... # Place a limit buy order order_id = client.place_order( exchange=Exchange.NSE, symbol="RELIANCE-EQ", quantity=1, disclosed_quantity=0, product=ProductType.CNC, order_type=OrderType.LIMIT, variety=Variety.REGULAR, transaction_type=TransactionType.BUY, price=1450.0, validity=Retention.DAY ) print(f"Order placed: {order_id}") ``` ### Market Order ```python # Market order - executes immediately at best price order_id = client.place_order( exchange=Exchange.NSE, symbol="RELIANCE-EQ", quantity=10, disclosed_quantity=0, product=ProductType.MIS, # Intraday order_type=OrderType.MKT, variety=Variety.REGULAR, transaction_type=TransactionType.BUY, price=0, # Price is 0 for market orders validity=Retention.DAY ) ``` ### Limit Order ```python # Limit order - executes at specified price or better order_id = client.place_order( exchange=Exchange.NSE, symbol="INFY-EQ", quantity=5, disclosed_quantity=0, product=ProductType.CNC, # Delivery order_type=OrderType.LIMIT, variety=Variety.REGULAR, transaction_type=TransactionType.BUY, price=1500.50, validity=Retention.DAY ) ``` ### Stop Loss Order ```python # Stop Loss Limit order order_id = client.place_order( exchange=Exchange.NSE, symbol="TCS-EQ", quantity=2, disclosed_quantity=0, product=ProductType.CNC, order_type=OrderType.SL_LMT, variety=Variety.REGULAR, transaction_type=TransactionType.SELL, price=3400.0, # Limit price trigger_price=3410.0, # Trigger price validity=Retention.DAY ) ``` ### F&O Order ```python # Futures & Options order on NFO order_id = client.place_order( exchange=Exchange.NFO, symbol="NIFTY02JAN25C26000", # NIFTY Call Option quantity=75, # Lot size disclosed_quantity=0, product=ProductType.NRML, order_type=OrderType.LIMIT, variety=Variety.REGULAR, transaction_type=TransactionType.BUY, price=150.0, validity=Retention.DAY ) ``` ### Iceberg Order (Disclosed Quantity) ```python # Large order with disclosed quantity order_id = client.place_order( exchange=Exchange.NSE, symbol="RELIANCE-EQ", quantity=1000, disclosed_quantity=100, # Only 100 visible at a time product=ProductType.CNC, order_type=OrderType.LIMIT, variety=Variety.REGULAR, transaction_type=TransactionType.BUY, price=1450.0, validity=Retention.DAY ) ``` ## Modify Order Modify an existing open order's price, quantity, or other parameters. ```python # Modify an existing order message = client.modify_order( order_id="24012400000321", exchange=Exchange.NSE, symbol="RELIANCE-EQ", quantity=2, # Changed quantity price=1500.0, # New price disclosed_qty=0, product=ProductType.CNC, transaction_type=TransactionType.BUY, order_type=OrderType.LIMIT, validity=Retention.DAY, remarks="Modified order" ) print(f"Modification result: {message}") ``` !!! warning "Modification Restrictions" - Cannot modify executed orders - Cannot change exchange or symbol - Cannot change transaction type (Buy/Sell) ## Cancel Order Cancel a pending or open order. ### Cancel Single Order ```python # Cancel a specific order message = client.cancel_order(order_id="24012400000321") print(f"Cancel result: {message}") ``` ### Cancel All Orders Cancel all open/pending orders in bulk using threading for faster execution: ```python # Cancel all open orders results = client.cancel_all_orders() for order_id, status, result in results: print(f"Order {order_id}: {status} - {result}") ``` !!! info "Bulk Cancel" The `cancel_all_orders()` method uses threading to cancel multiple orders in parallel for faster execution. ## Order Tracking ### Get Order Details Retrieve comprehensive order history and execution details: ```python # Get specific order details order_details = client.get_order_details(order_id="24012400000321") for detail in order_details: print(f"Status: {detail['orderStatus']}") print(f"Report Type: {detail['reportType']}") print(f"Filled: {detail['cumulativeFillQty']}/{detail['quantity']}") print(f"Average Price: {detail['averagePrice']}") ``` ### Order Status Types | Status | Description | Next Action | |--------|-------------|-------------| | `PENDING` | Order submitted, awaiting confirmation | Monitor for updates | | `OPEN` | Order active in the market | Can modify or cancel | | `COMPLETE` | Order fully executed | Review execution details | | `CANCELLED` | Order cancelled by user/system | No further action | | `REJECTED` | Order rejected by exchange | Check rejection reason | ### Get Order Book Access your complete order book: ```python # Get all orders for the day orders = client.get_order_book() for order in orders: print(f"Order ID: {order['id']}") print(f"Symbol: {order['symbol']}") print(f"Status: {order['orderStatus']}") print(f"Type: {order['transactionType']} {order['order']}") print(f"Qty: {order['quantity']} @ ₹{order['price']}") print("-" * 40) ``` ### Get Trade Book Review all executed trades: ```python # Get all trades for the day trades = client.get_trade_book() for trade in trades: print(f"Order ID: {trade['id']}") print(f"Symbol: {trade['symbol']}") print(f"Fill Price: ₹{trade['fillPrice']}") print(f"Fill Qty: {trade['fillQuantity']}") print(f"Fill Time: {trade['fillTime']}") print("-" * 40) ``` ## Order Response Fields ### Order Book Response | Field | Type | Description | |-------|------|-------------| | `id` | string | Order ID | | `exchange` | string | Exchange code | | `symbol` | string | Trading symbol | | `price` | string | Order price | | `quantity` | string | Order quantity | | `product` | string | Product type (I/C/M) | | `orderStatus` | string | Current status | | `transactionType` | string | Buy (B) / Sell (S) | | `order` | string | Order type | | `cumulativeFillQty` | string | Quantity filled | | `averagePrice` | string | Average fill price | | `exchangeOrderID` | string | Exchange order number | | `orderTime` | string | Order timestamp | | `rejectReason` | string | Rejection reason (if any) | ## Complete Example ```python from pyarrow_client import ( ArrowClient, Exchange, OrderType, ProductType, TransactionType, Variety, Retention ) def trading_example(): # Initialize and authenticate client = ArrowClient(app_id="your_app_id") client.auto_login( user_id="your_user_id", password="your_password", api_secret="your_api_secret", totp_secret="your_totp_secret" ) # Place a buy order order_id = client.place_order( exchange=Exchange.NSE, symbol="RELIANCE-EQ", quantity=1, disclosed_quantity=0, product=ProductType.CNC, order_type=OrderType.LIMIT, variety=Variety.REGULAR, transaction_type=TransactionType.BUY, price=1450.0, validity=Retention.DAY ) print(f"✓ Order placed: {order_id}") # Check order status details = client.get_order_details(order_id) print(f"✓ Order status: {details[0]['orderStatus']}") # Modify order price if details[0]['orderStatus'] == 'OPEN': client.modify_order( order_id=order_id, exchange=Exchange.NSE, symbol="RELIANCE-EQ", quantity=1, price=1455.0, disclosed_qty=0, product=ProductType.CNC, transaction_type=TransactionType.BUY, order_type=OrderType.LIMIT, validity=Retention.DAY ) print("✓ Order modified") # View order book orders = client.get_order_book() print(f"✓ Total orders today: {len(orders)}") # View trade book trades = client.get_trade_book() print(f"✓ Total trades today: {len(trades)}") if __name__ == "__main__": trading_example() ``` ## Error Handling ```python try: order_id = client.place_order( exchange=Exchange.NSE, symbol="RELIANCE-EQ", quantity=1, disclosed_quantity=0, product=ProductType.CNC, order_type=OrderType.LIMIT, variety=Variety.REGULAR, transaction_type=TransactionType.BUY, price=1450.0, validity=Retention.DAY ) print(f"Order placed: {order_id}") except Exception as e: print(f"Order failed: {e}") ``` ### Common Order Errors | Error | Cause | Solution | |-------|-------|----------| | Insufficient margin | Not enough funds | Add funds or reduce quantity | | Price outside DPR | Price beyond daily range | Adjust price within limits | | Invalid symbol | Symbol not found | Verify symbol format | | Market closed | Outside trading hours | Wait for market hours | | Quantity not in lot | F&O lot size mismatch | Use correct lot multiples | !!! tip "Best Practices" - Always validate order parameters before submission - Implement proper error handling for all order operations - Monitor position limits and margin requirements - Use remarks field for order identification in bulk operations --- ## Source file: `python-sdk/portfolio.md` **Canonical URL:** https://docs.arrow.trade/python-sdk/portfolio/ # Portfolio Access and manage your portfolio including positions, holdings, order book, and trade book through the PyArrow SDK. ## Positions Get your current trading positions across all segments. ```python from pyarrow_client import ArrowClient client = ArrowClient(app_id="your_app_id") # ... authenticate ... # Get all positions positions = client.get_positions() for position in positions: print(f"Symbol: {position['symbol']}") print(f"Exchange: {position['exchange']}") print(f"Quantity: {position['quantity']}") print(f"Average Price: ₹{position['averagePrice']}") print(f"P&L: ₹{position['pnl']}") print("-" * 40) ``` ### Position Fields | Field | Type | Description | |-------|------|-------------| | `symbol` | string | Trading symbol | | `exchange` | string | Exchange code (NSE/BSE/NFO/BFO) | | `quantity` | string | Net position quantity | | `averagePrice` | string | Average entry price | | `ltp` | string | Last traded price | | `pnl` | string | Profit/Loss | | `product` | string | Product type (I/C/M) | | `buyQuantity` | string | Buy quantity | | `sellQuantity` | string | Sell quantity | | `buyValue` | string | Buy value | | `sellValue` | string | Sell value | ### Example: Position Analysis ```python def analyze_positions(client): """Analyze current positions and calculate total P&L.""" positions = client.get_positions() if not positions: print("No open positions") return total_pnl = 0 print("=" * 60) print(f"{'Symbol':<20} {'Qty':>10} {'P&L':>15}") print("=" * 60) for pos in positions: symbol = pos.get('symbol', 'N/A') qty = pos.get('quantity', '0') pnl = float(pos.get('pnl', 0)) total_pnl += pnl pnl_str = f"₹{pnl:,.2f}" print(f"{symbol:<20} {qty:>10} {pnl_str:>15}") print("=" * 60) print(f"{'Total P&L':<31} ₹{total_pnl:>14,.2f}") return total_pnl # Usage total = analyze_positions(client) ``` ## Holdings Get your delivery holdings (stocks held in demat account). ```python # Get all holdings holdings = client.get_holdings() print(f"Holdings: {holdings}") ``` ### Holdings Structure ```python # Iterate through holdings for holding in holdings.get('data', []): print(f"Symbol: {holding['symbol']}") print(f"Quantity: {holding['quantity']}") print(f"Average Price: ₹{holding['averagePrice']}") print(f"Current Value: ₹{holding['currentValue']}") print(f"P&L: ₹{holding['pnl']}") print("-" * 40) ``` ### Holdings Fields | Field | Type | Description | |-------|------|-------------| | `symbol` | string | Stock symbol | | `isin` | string | ISIN code | | `quantity` | string | Number of shares held | | `averagePrice` | string | Average purchase price | | `ltp` | string | Last traded price | | `currentValue` | string | Current market value | | `pnl` | string | Unrealized profit/loss | | `pnlPercent` | string | P&L percentage | ### Example: Holdings Summary ```python def holdings_summary(client): """Generate holdings portfolio summary.""" holdings = client.get_holdings() total_invested = 0 total_current = 0 print("\n📊 PORTFOLIO HOLDINGS") print("=" * 70) print(f"{'Symbol':<15} {'Qty':>8} {'Avg':>10} {'LTP':>10} {'P&L':>12} {'%':>8}") print("-" * 70) for holding in holdings.get('data', []): symbol = holding.get('symbol', 'N/A') qty = int(holding.get('quantity', 0)) avg = float(holding.get('averagePrice', 0)) ltp = float(holding.get('ltp', 0)) pnl = float(holding.get('pnl', 0)) pnl_pct = float(holding.get('pnlPercent', 0)) invested = qty * avg current = qty * ltp total_invested += invested total_current += current print(f"{symbol:<15} {qty:>8} {avg:>10.2f} {ltp:>10.2f} {pnl:>12.2f} {pnl_pct:>7.2f}%") print("=" * 70) total_pnl = total_current - total_invested total_pnl_pct = (total_pnl / total_invested * 100) if total_invested > 0 else 0 print(f"{'Invested:':<35} ₹{total_invested:>12,.2f}") print(f"{'Current Value:':<35} ₹{total_current:>12,.2f}") print(f"{'Total P&L:':<35} ₹{total_pnl:>12,.2f} ({total_pnl_pct:.2f}%)") return { 'invested': total_invested, 'current': total_current, 'pnl': total_pnl, 'pnl_percent': total_pnl_pct } # Usage summary = holdings_summary(client) ``` ## Order Book Access your complete order book for the trading day. ```python # Get all orders orders = client.get_order_book() for order in orders: print(f"Order ID: {order['id']}") print(f"Symbol: {order['symbol']} ({order['exchange']})") print(f"Type: {order['transactionType']} {order['order']}") print(f"Qty: {order['quantity']} @ ₹{order['price']}") print(f"Status: {order['orderStatus']}") print(f"Filled: {order['cumulativeFillQty']}") print("-" * 40) ``` ### Order Book Fields | Field | Type | Description | |-------|------|-------------| | `id` | string | Order ID | | `exchange` | string | Exchange code | | `symbol` | string | Trading symbol | | `price` | string | Order price | | `quantity` | string | Order quantity | | `product` | string | Product type | | `orderStatus` | string | PENDING/OPEN/COMPLETE/CANCELLED/REJECTED | | `transactionType` | string | B (Buy) / S (Sell) | | `order` | string | Order type (LMT/MKT/SL-LMT/SL-MKT) | | `cumulativeFillQty` | string | Quantity filled so far | | `averagePrice` | string | Average execution price | | `exchangeOrderID` | string | Exchange order number | | `orderTime` | string | Order timestamp | | `rejectReason` | string | Rejection reason (if rejected) | ### Example: Order Analysis ```python def order_analysis(client): """Analyze today's orders by status.""" orders = client.get_order_book() status_count = { 'PENDING': 0, 'OPEN': 0, 'COMPLETE': 0, 'CANCELLED': 0, 'REJECTED': 0 } for order in orders: status = order.get('orderStatus', 'UNKNOWN') if status in status_count: status_count[status] += 1 print("\n📋 ORDER SUMMARY") print("=" * 30) for status, count in status_count.items(): emoji = { 'PENDING': '⏳', 'OPEN': '📖', 'COMPLETE': '✅', 'CANCELLED': '❌', 'REJECTED': '🚫' }.get(status, '•') print(f"{emoji} {status}: {count}") print("=" * 30) print(f"Total Orders: {len(orders)}") return status_count # Usage order_stats = order_analysis(client) ``` ## Trade Book Review all executed trades for the day. ```python # Get all trades trades = client.get_trade_book() for trade in trades: print(f"Order ID: {trade['id']}") print(f"Symbol: {trade['symbol']} ({trade['exchange']})") print(f"Type: {trade['transactionType']}") print(f"Fill Price: ₹{trade['fillPrice']}") print(f"Fill Qty: {trade['fillQuantity']}") print(f"Time: {trade['exchangeUpdateTime']}") print("-" * 40) ``` ### Trade Book Fields | Field | Type | Description | |-------|------|-------------| | `id` | string | Order ID | | `exchange` | string | Exchange code | | `symbol` | string | Trading symbol | | `quantity` | string | Order quantity | | `product` | string | Product type | | `transactionType` | string | B (Buy) / S (Sell) | | `order` | string | Order type | | `fillPrice` | string | Execution price | | `fillQuantity` | string | Executed quantity | | `fillTime` | string | Execution timestamp | | `fillID` | string | Trade/Fill ID | | `exchangeOrderID` | string | Exchange order number | | `averagePrice` | string | Average price | ### Example: Trade Summary ```python def trade_summary(client): """Generate trade summary with buy/sell breakdown.""" trades = client.get_trade_book() buy_value = 0 sell_value = 0 buy_count = 0 sell_count = 0 print("\n📈 TRADE BOOK") print("=" * 80) print(f"{'Time':<20} {'Symbol':<15} {'Type':<6} {'Qty':>8} {'Price':>12} {'Value':>15}") print("-" * 80) for trade in trades: time = trade.get('exchangeUpdateTime', '')[:19] symbol = trade.get('symbol', 'N/A') txn_type = 'BUY' if trade.get('transactionType') == 'B' else 'SELL' qty = int(trade.get('fillQuantity', 0)) price = float(trade.get('fillPrice', 0)) value = qty * price if trade.get('transactionType') == 'B': buy_value += value buy_count += 1 else: sell_value += value sell_count += 1 print(f"{time:<20} {symbol:<15} {txn_type:<6} {qty:>8} ₹{price:>11.2f} ₹{value:>14,.2f}") print("=" * 80) print(f"\n💰 Buy Trades: {buy_count} | Value: ₹{buy_value:,.2f}") print(f"💰 Sell Trades: {sell_count} | Value: ₹{sell_value:,.2f}") print(f"📊 Net Turnover: ₹{buy_value + sell_value:,.2f}") return { 'buy_value': buy_value, 'sell_value': sell_value, 'total_trades': len(trades) } # Usage summary = trade_summary(client) ``` ## User Limits & Funds Get available margins and trading limits. ```python # Get user limits limits = client.get_user_limits() for limit in limits: print(f"Segment: {limit.get('segment')}") print(f"Available Margin: ₹{limit.get('availableMargin')}") print(f"Used Margin: ₹{limit.get('usedMargin')}") print(f"Total Margin: ₹{limit.get('totalMargin')}") print("-" * 40) ``` ### User Limits Fields | Field | Type | Description | |-------|------|-------------| | `segment` | string | Trading segment | | `availableMargin` | string | Available funds for trading | | `usedMargin` | string | Margin currently in use | | `totalMargin` | string | Total margin value | | `openingBalance` | string | Opening balance | | `payIn` | string | Funds added today | | `payOut` | string | Funds withdrawn today | ## User Details Get authenticated user's profile information. ```python # Get user details user = client.get_user_details() print(f"User ID: {user.get('userID')}") print(f"Name: {user.get('name')}") print(f"Email: {user.get('email')}") print(f"Phone: {user.get('phone')}") print(f"PAN: {user.get('pan')}") ``` ## Complete Example ```python from pyarrow_client import ArrowClient def portfolio_dashboard(client): """Complete portfolio dashboard.""" print("\n" + "=" * 60) print("📊 PORTFOLIO DASHBOARD") print("=" * 60) # User Info user = client.get_user_details() print(f"\n👤 Welcome, {user.get('name', 'Trader')}!") # Funds Summary limits = client.get_user_limits() if limits: total_available = sum(float(l.get('availableMargin', 0)) for l in limits) print(f"\n💰 Available Margin: ₹{total_available:,.2f}") # Holdings holdings = client.get_holdings() if holdings.get('data'): print(f"\n📦 Holdings: {len(holdings['data'])} stocks") # Positions positions = client.get_positions() if positions: total_pnl = sum(float(p.get('pnl', 0)) for p in positions) print(f"\n📈 Open Positions: {len(positions)}") print(f" Day P&L: ₹{total_pnl:,.2f}") else: print("\n📈 No open positions") # Orders orders = client.get_order_book() open_orders = [o for o in orders if o.get('orderStatus') in ['PENDING', 'OPEN']] print(f"\n📋 Today's Orders: {len(orders)}") print(f" Open Orders: {len(open_orders)}") # Trades trades = client.get_trade_book() print(f"\n✅ Trades Executed: {len(trades)}") print("\n" + "=" * 60) # Usage client = ArrowClient(app_id="your_app_id") # ... authenticate ... portfolio_dashboard(client) ``` --- ## Source file: `python-sdk/websocket-streaming.md` **Canonical URL:** https://docs.arrow.trade/python-sdk/websocket-streaming/ # WebSocket Streaming The PyArrow SDK provides real-time WebSocket streaming: the **Order Stream** (JSON updates), the **Data Stream** (token-based ticks), and the **HFT Data Stream** (symbol- or token-based binary protocol). All sockets share automatic reconnection, heartbeat/read timeouts, and thread-based event delivery. ## Overview `ArrowStreams` exposes three WebSocket connections: | Stream | Endpoint | Purpose | Events | |--------|----------|---------|--------| | **Order Stream** | `wss://order-updates.arrow.trade` | Order and position updates | `on_order_update` (JSON) | | **Data Stream** | `wss://ds.arrow.trade` | Standard market data (token-based, binary ticks) | `on_ticks` (`MarketTick`) | | **HFT Data Stream** | `wss://socket.arrow.trade` | Low-latency market data (symbol or token IDs, binary protocol) | `on_ltp_tick`, `on_full_tick`, `on_response` | ## Quick Start ```python from pyarrow_client import ArrowStreams, DataMode # Initialize streams streams = ArrowStreams( appID="your_app_id", token="your_access_token", debug=True # Enable debug logging ) # Define event handlers def on_order_update(order): print(f"Order Update: {order['id']} - {order['orderStatus']}") def on_tick(tick): print(f"Token: {tick.token} | LTP: ₹{tick.ltp} | Change: {tick.net_change}%") def on_connect(): print("✓ Connected to market data stream") def on_disconnect(): print("✗ Disconnected from stream") # Attach handlers streams.order_stream.on_order_update = on_order_update streams.data_stream.on_ticks = on_tick streams.data_stream.on_connect = on_connect streams.data_stream.on_disconnect = on_disconnect # connect_all() connects order, data, and HFT streams streams.connect_all() # Subscribe to market data (Data Stream — integer tokens) tokens = [3045, 1594] # RELIANCE, INFY streams.subscribe_market_data(DataMode.QUOTE, tokens) # Keep connection alive import time try: while True: time.sleep(1) except KeyboardInterrupt: streams.disconnect_all() ``` ## Order Stream The **Order Stream** (`OrderStream`) uses `wss://order-updates.arrow.trade?appID=&token=`. Incoming messages are **JSON text**. The SDK parses each message and calls **`on_order_update`** when the object contains an **`id`** field (order identifier). ```python streams.order_stream.on_order_update = lambda o: print(o["id"], o.get("orderStatus")) streams.connect_order_stream() ``` Updates typically include fields such as `orderStatus`, `symbol`, `quantity`, `price`, `transactionType`, `averagePrice`, and `rejectReason`. Use the REST **orders** documentation for the full payload shape. ## Data Stream The **Data Stream** (`DataStream`) uses integer **instrument tokens** and a **big-endian** binary tick format (aligned with the JS client). Subscribe with `DataMode` and `subscribe_market_data` / `unsubscribe_market_data`. ### LTP Mode (13 bytes) Last traded price only. ```python streams.subscribe_market_data(DataMode.LTP, [3045, 1594]) def on_tick(tick): print(f"Token: {tick.token} | LTP: {tick.ltp} | mode: {tick.mode}") # mode == 'ltp' ``` | Field | Description | |-------|-------------| | `token` | Instrument token | | `ltp` | Last traded price | | `mode` | `"ltp"` | ### LTPC Mode (17 bytes) Minimal data for price tracking. ```python from pyarrow_client import DataMode # Subscribe to LTPC mode streams.subscribe_market_data(DataMode.LTPC, [3045, 1594]) def on_tick(tick): print(f"Token: {tick.token}") print(f"LTP: ₹{tick.ltp}") print(f"Close: ₹{tick.close}") print(f"Net Change: {tick.net_change}%") print(f"Change Flag: {tick.change_flag}") # 43(+), 45(-), 32(no change) ``` | Field | Description | |-------|-------------| | `token` | Instrument token | | `ltp` | Last traded price | | `close` | Previous close | | `net_change` | % change | | `change_flag` | Direction indicator | ### Quote Mode (93 bytes) Detailed quotes with OHLCV and OI data. ```python # Subscribe to Quote mode streams.subscribe_market_data(DataMode.QUOTE, [3045]) def on_tick(tick): print(f"Token: {tick.token}") print(f"LTP: ₹{tick.ltp} | Volume: {tick.volume}") print(f"Open: {tick.open} | High: {tick.high} | Low: {tick.low} | Close: {tick.close}") print(f"OI: {tick.oi} | OI High: {tick.oi_day_high} | OI Low: {tick.oi_day_low}") print(f"Buy Qty: {tick.total_buy_quantity} | Sell Qty: {tick.total_sell_quantity}") print(f"LTQ: {tick.ltq} | Avg Price: {tick.avg_price}") print(f"LTT: {tick.ltt} | Time: {tick.time}") ``` | Field | Description | |-------|-------------| | All LTPC fields | + | | `open`, `high`, `low` | OHLC prices | | `volume` | Traded volume | | `oi`, `oi_day_high`, `oi_day_low` | Open Interest | | `ltq` | Last traded quantity | | `avg_price` | Average traded price | | `total_buy_quantity` | Total buy quantity | | `total_sell_quantity` | Total sell quantity | | `ltt` | Last trade time | | `time` | Timestamp | ### Full Mode (241 bytes) Complete market depth with 5 levels of bids/asks. ```python # Subscribe to Full mode streams.subscribe_market_data(DataMode.FULL, [3045]) def on_tick(tick): print(f"Token: {tick.token} | LTP: ₹{tick.ltp}") print(f"Upper Limit: ₹{tick.upper_limit} | Lower Limit: ₹{tick.lower_limit}") # Market Depth - Bids print("\n--- BID DEPTH ---") for i, bid in enumerate(tick.bids): print(f"Level {i+1}: {bid['quantity']:>8} @ ₹{bid['price']:>10.2f} ({bid['orders']} orders)") # Market Depth - Asks print("\n--- ASK DEPTH ---") for i, ask in enumerate(tick.asks): print(f"Level {i+1}: {ask['quantity']:>8} @ ₹{ask['price']:>10.2f} ({ask['orders']} orders)") ``` | Field | Description | |-------|-------------| | All Quote fields | + | | `upper_limit` | Upper circuit limit | | `lower_limit` | Lower circuit limit | | `bids` | List of 5 bid levels | | `asks` | List of 5 ask levels | ### Subscribe to market data ```python # Subscribe tokens to a specific mode tokens = [3045, 1594, 5533] # RELIANCE, INFY, SBIN streams.subscribe_market_data(DataMode.QUOTE, tokens) # Subscribe with different modes streams.subscribe_market_data(DataMode.LTPC, [3045]) # Light data streams.subscribe_market_data(DataMode.FULL, [1594]) # Full depth ``` ### Unsubscribe from market data ```python # Unsubscribe specific tokens from a mode streams.unsubscribe_market_data(DataMode.QUOTE, [3045]) # Unsubscribe all streams.unsubscribe_market_data(DataMode.LTPC, [3045, 1594, 5533]) ``` ### `MarketTick` object The **Data Stream** delivers `MarketTick` instances (dataclass). Numeric fields are populated depending on mode; unused fields may be `0` or empty lists. ```python from dataclasses import dataclass, field from typing import List @dataclass class MarketTick: token: int ltp: float = 0.0 mode: str = "" # "ltp" | "ltpc" | "quote" | "full" open: float = 0.0 high: float = 0.0 low: float = 0.0 close: float = 0.0 volume: int = 0 net_change: float = 0.0 change_flag: int = 32 # 43 '+', 45 '-', 32 flat ltq: int = 0 avg_price: float = 0.0 total_buy_quantity: int = 0 total_sell_quantity: int = 0 ltt: int = 0 time: int = 0 oi: int = 0 oi_day_high: int = 0 oi_day_low: int = 0 upper_limit: float = 0.0 lower_limit: float = 0.0 bids: List[dict] = field(default_factory=list) # keys: price, quantity, orders asks: List[dict] = field(default_factory=list) ``` HFT ticks are **not** `MarketTick`; use the dict fields described in [HFT tick and response shapes](#hft-tick-and-response-shapes). ## HFT Data Stream The **HFT Data Stream** (`HFTDataStream`) targets `wss://socket.arrow.trade?appID=&token=`. It uses a **little-endian** binary protocol on the wire. Outbound commands are **JSON** (`code`: `sub` / `unsub`). ### Protocol summary | Topic | Detail | |-------|--------| | Multi-byte integers | Little-endian | | Prices | Paise (1 rupee = 100 paise) | | Packet framing | First 2 bytes: signed int16 **packet size**; byte index 2: **packet type** | **Packet types** | Type | Value | Size (bytes) | Handler callback | |------|-------|----------------|------------------| | LTP | `1` (`PKT_TYPE_LTP`) | 40 | `on_ltp_tick` | | Full depth | `2` (`PKT_TYPE_FULL`) | 196 | `on_full_tick` | | Server response | `99` (`PKT_TYPE_RESPONSE`) | 540 | `on_response` | **Exchange segments** (`HFTDataStream` constants) | Constant | Value | Segment | |----------|-------|---------| | `EXCH_NSE_CM` | `0` | NSE cash | | `EXCH_NSE_FO` | `1` | NSE F&O | | `EXCH_BSE_CM` | `2` | BSE cash | | `EXCH_BSE_FO` | `3` | BSE F&O | **Symbol string formats** (when subscribing with names instead of token IDs) | Segment | Example format | |---------|----------------| | NSE CM | `NSE.-` — e.g. `NSE.SBIN-EQ` | | NSE FO options | Underlying + expiry + **C** or **P** + strike — e.g. `NYKAA30DEC25C232.5` | | NSE FO futures | Underlying + expiry + **F** — e.g. `BANKNIFTY30DEC25F` | | BSE CM | `BSE.` — e.g. `BSE.SBIN` | | BSE FO options | Asset type + expiry + **C** or **P** + strike — e.g. `SENSEX01JAN26C74900` | | BSE FO futures | `
F` — e.g. `SENSEX01JAN26F` | **Limits** - Up to **100 requests per second** per connection - Up to **4096 symbols** per subscription - Max request size **16 KB** **Product note:** LTP-style streaming is available for **NSE FO** and **NSE CM**; full depth for other segments as supported by the service. ### Connect and subscribe ```python from pyarrow_client import ArrowStreams, HFTDataStream streams = ArrowStreams(appID="your_app_id", token="your_token", debug=True) streams.hft_data_stream.on_ltp_tick = lambda t: print("LTP:", t) streams.hft_data_stream.on_full_tick = lambda t: print("Full:", t) streams.hft_data_stream.on_response = lambda r: print("Response:", r) streams.connect_hft_data_stream() # Mode: 'ltpc' or 'l' (LTP), 'full' or 'f' (depth) # latency: milliseconds between ticks (50–60000, default 1000) streams.subscribe_hft_data( "ltpc", ["NSE.SBIN-EQ", "BSE.RELIANCE"], latency=100, ) # Integer token IDs (default segment NSE_CM / 0 in the wire message) streams.subscribe_hft_data("full", [5042, 4449], latency=200) ``` **Explicit exchange segments** (token IDs per segment): ```python streams.hft_data_stream.subscribe_by_segment( "full", { HFTDataStream.EXCH_NSE_FO: [5042, 4449, 91], HFTDataStream.EXCH_BSE_CM: [100, 200], }, latency=100, ) ``` ### Unsubscribe ```python streams.unsubscribe_hft_data("ltpc", ["NSE.SBIN-EQ"]) streams.hft_data_stream.unsubscribe_by_segment( "full", {HFTDataStream.EXCH_NSE_FO: [5042]}, ) ``` Mixed types (strings and ints in one list) are **not** supported for a single subscribe/unsubscribe call. ### HFT tick and response shapes Callbacks receive **plain dicts** (parsed binary). **LTP packet (`on_ltp_tick`)** — fields include: `size`, `pkt_type`, `exch_seg`, `token`, `ltp`, `vwap`, `volume`, `ltt`, `atv`, `btv` (see SDK `_parse_ltp_packet` for offsets; prices in paise). **Full packet (`on_full_tick`)** — includes OHLC, `ltq`, `vwap`, `ltt`, day range `dpr_l` / `dpr_h`, `tbq`, `tsq`, `volume`, five levels of `bid_px`, `ask_px`, `bid_size`, `ask_size`, `bid_ord`, `ask_ord`, `oi`, server `ts`, `atv`, `btv`. **Response packet (`on_response`)** — subscribe/unsubscribe acknowledgement: `error_code`, `error_msg`, `request_type` / `request_type_str`, `mode` / `mode_str`, `success_count`, `error_count`. Typical `error_code` values include: `SUCCESS`, `E_PARTIAL`, `E_ALL_INVALID`, `E_INVALID_JSON`, `E_MISSING_FIELD`, `E_INVALID_PARAM`, `E_PARSE_ERROR`. ### Resubscription Like `DataStream`, `HFTDataStream` **resubscribes** from its local subscription map after `on_open` (modes `ltpc` and `full`). ## Connection Management ### Connect Streams ```python # Connect all three streams (order, data, HFT) streams.connect_all() # Or connect individually streams.connect_order_stream() streams.connect_data_stream() streams.connect_hft_data_stream() ``` ### Disconnect Streams ```python # Disconnect from all streams streams.disconnect_all() # Check connection status status = streams.get_status() print(f"Order Stream: {status['order_stream']}") print(f"Data Stream: {status['data_stream']}") print(f"HFT Data Stream: {status['hft_data_stream']}") ``` ### Connection Status ```python status = streams.get_status() # Returns (values are SocketStatus string values, e.g. 'connected', 'connecting', 'disconnected'): # { # 'order_stream': 'connected', # 'data_stream': 'connected', # 'hft_data_stream': 'connected', # } ``` ### `ConnectionConfig` (shared) `ArrowStreams(appID=..., token=..., debug=...)` builds a `ConnectionConfig` used by all streams. Notable fields (defaults match the JS client behaviour in the SDK): `enable_reconnect`, `max_reconnect_attempts` (e.g. 300), `max_reconnect_delay` (seconds), `immediate_reconnect_attempts` (first N reconnects with no delay), `read_timeout`, `ping_interval`. The socket layer sends periodic `PONG` text on a timer and closes the connection if incoming data stalls beyond `read_timeout`. ## Event Handlers ### Order Stream Events ```python # Order update received def on_order_update(order): """Handle order status updates.""" print(f"Order: {order['id']}") print(f"Status: {order['orderStatus']}") print(f"Symbol: {order['symbol']}") print(f"Qty: {order['quantity']} @ {order['price']}") # Attach handler streams.order_stream.on_order_update = on_order_update ``` ### Data Stream Events ```python # Market tick received def on_ticks(tick): """Handle incoming market data.""" print(f"Tick: {tick.token} @ {tick.ltp}") # Connection established def on_connect(): """Called when connection is established.""" print("Connected!") # Re-subscribe after reconnection streams.subscribe_market_data(DataMode.QUOTE, [3045]) # Connection lost def on_disconnect(): """Called when connection is lost.""" print("Disconnected!") # Error occurred def on_error(error): """Handle connection errors.""" print(f"Error: {error}") # Connection closed def on_close(close_status_code, close_msg): """Called when connection is closed.""" print(f"Closed: {close_status_code} - {close_msg}") # Reconnection attempt def on_reconnect(attempt, delay): """Called during reconnection attempts.""" print(f"Reconnecting... Attempt {attempt}, waiting {delay}s") # Max reconnection attempts reached def on_no_reconnect(): """Called when max reconnection attempts reached.""" print("Max reconnection attempts reached!") # Attach handlers streams.data_stream.on_ticks = on_ticks streams.data_stream.on_connect = on_connect streams.data_stream.on_disconnect = on_disconnect streams.data_stream.on_error = on_error streams.data_stream.on_close = on_close streams.data_stream.on_reconnect = on_reconnect streams.data_stream.on_no_reconnect = on_no_reconnect ``` ### HFT Data Stream Events ```python def on_ltp_tick(tick): """Binary LTP packet decoded to dict (40-byte payload).""" print(tick["token"], tick["ltp"]) def on_full_tick(tick): """Binary full-depth packet (196 bytes).""" print(tick["token"], tick["bid_px"], tick["ask_px"]) def on_response(resp): """Server response to sub/unsub (540 bytes).""" print(resp["error_code"], resp["success_count"], resp["error_count"]) streams.hft_data_stream.on_ltp_tick = on_ltp_tick streams.hft_data_stream.on_full_tick = on_full_tick streams.hft_data_stream.on_response = on_response # BaseSocket handlers also apply: on_connect, on_disconnect, on_error, on_close, # on_reconnect, on_no_reconnect ``` ## Example: Order Monitor ```python from pyarrow_client import ArrowClient, ArrowStreams def order_monitor(app_id, token): """Monitor real-time order updates.""" streams = ArrowStreams(appID=app_id, token=token, debug=False) def on_order_update(order): status = order.get('orderStatus', 'UNKNOWN') symbol = order.get('symbol', 'N/A') order_id = order.get('id', 'N/A') txn = "BUY" if order.get('transactionType') == 'B' else "SELL" qty = order.get('quantity', 0) price = order.get('price', 0) # Status emoji emoji = { 'PENDING': '⏳', 'OPEN': '📖', 'COMPLETE': '✅', 'CANCELLED': '❌', 'REJECTED': '🚫' }.get(status, '❓') print(f"\n{emoji} ORDER UPDATE") print(f" ID: {order_id}") print(f" {txn} {qty} x {symbol} @ ₹{price}") print(f" Status: {status}") if status == 'REJECTED': print(f" Reason: {order.get('rejectReason', 'N/A')}") if status == 'COMPLETE': print(f" Avg Price: ₹{order.get('averagePrice', 'N/A')}") streams.order_stream.on_order_update = on_order_update print("Starting order monitor...") streams.connect_order_stream() try: while True: import time time.sleep(1) except KeyboardInterrupt: streams.disconnect_all() # Usage order_monitor("your_app_id", "your_token") ``` ## Complete Example: Live Dashboard ```python from pyarrow_client import ArrowClient, ArrowStreams, DataMode import time from datetime import datetime class LiveDashboard: def __init__(self, app_id, token): self.streams = ArrowStreams(appID=app_id, token=token, debug=False) self.prices = {} self.setup_handlers() def setup_handlers(self): self.streams.order_stream.on_order_update = self.on_order self.streams.data_stream.on_ticks = self.on_tick self.streams.data_stream.on_connect = self.on_connect self.streams.data_stream.on_disconnect = self.on_disconnect self.streams.data_stream.on_error = self.on_error def on_tick(self, tick): """Process incoming market data.""" self.prices[tick.token] = { 'ltp': tick.ltp, 'change': tick.net_change, 'volume': tick.volume, 'time': datetime.now() } self.display() def on_connect(self): print("\n✓ Connected to Arrow Market Data Stream") print("=" * 60) def on_disconnect(self): print("\n✗ Disconnected from stream") def on_error(self, error): print(f"\n⚠ Error: {error}") def on_order(self, order): print(f"\n📋 Order Update: {order['symbol']} - {order['orderStatus']}") def display(self): """Display live prices.""" # Clear screen (optional) # print("\033[2J\033[H") # Uncomment to clear terminal print(f"\n{'Token':<10} {'LTP':>12} {'Change':>10} {'Volume':>15}") print("-" * 50) for token, data in self.prices.items(): change_str = f"{data['change']:+.2f}%" color = "📈" if data['change'] > 0 else "📉" if data['change'] < 0 else "➡️" print(f"{token:<10} ₹{data['ltp']:>10.2f} {change_str:>10} {data['volume']:>15,} {color}") def start(self, tokens): """Start the dashboard.""" # connect_all() opens order, data, and HFT sockets; use connect_data_stream / # connect_order_stream only if you do not need HFT. self.streams.connect_all() time.sleep(1) # Wait for connection self.streams.subscribe_market_data(DataMode.QUOTE, tokens) print(f"Subscribed to {len(tokens)} tokens") try: while True: time.sleep(1) except KeyboardInterrupt: print("\n\nShutting down...") self.streams.disconnect_all() # Usage if __name__ == "__main__": # Initialize client and authenticate client = ArrowClient(app_id="your_app_id") client.auto_login( user_id="your_user_id", password="your_password", api_secret="your_api_secret", totp_secret="your_totp_secret" ) # Start dashboard dashboard = LiveDashboard( app_id="your_app_id", token=client.get_token() ) # Subscribe to tokens watchlist = [3045, 1594, 5533, 2885, 3499] # RELIANCE, INFY, SBIN, ICICIBANK, HDFCBANK dashboard.start(watchlist) ``` ## Best Practices !!! tip "Performance Tips" - **Order Stream:** use `connect_order_stream()` alone when you only need fills and status updates - **Data Stream:** use `DataMode.LTP` or `DataMode.LTPC` for large watchlists when you only need price; use `DataMode.QUOTE` for OHLCV; use `DataMode.FULL` only when you need depth - **HFT Data Stream:** tune `latency` (ms) to balance rate vs CPU; respect 100 req/s and 4096 symbols per subscription - Unsubscribe from instruments you no longer need on each stream you use !!! warning "Connection Handling" - Always implement `on_connect` to re-subscribe after reconnection - Handle `on_error` and `on_disconnect` for graceful degradation - Use `on_reconnect` to track reconnection attempts - Set `debug=True` during development for detailed logging !!! info "Thread Safety" - Event handlers are called from WebSocket threads - Use thread-safe data structures when sharing state - Avoid blocking operations in event handlers --- ## Source file: `rest-api/FlatBuffer Market data subscription.md` **Canonical URL:** https://docs.arrow.trade/rest-api/FlatBuffer Market data subscription/ ##### RequestWrapper.fbs ``` namespace fbs; table LoginRequest { ucc: string; token: string; // auth-token } table SubscriptionBlock { symbol: string; // symbolName or symName as per syminfo. For Example ZOMATO25FEBFUT (fut in case of utick) sub_type: string; // either ["ltp" or "mtick" or "utick"] expiry: int; // expiry date in YYYYMMDD format (req only in utick) } table SubscribeRequest { id: string; // any unqiue string XXXX subscription_list: [SubscriptionBlock]; } table UnsubscribeRequest { id: string; // any unqiue string YYYY subscription_list: [SubscriptionBlock]; } // A union for the message types union RequestType { LoginRequest, SubscribeRequest, UnsubscribeRequest } // A wrapper table for the message table RequestWrapper { content: RequestType; // The actual message content } root_type RequestWrapper; ``` This Request is what the backend expects from frontend. So consider it as a client to server messaging schema. The request can be one of the RequestType: - LoginRequest - SubscribeRequest - UnsubscribeRequest **LoginRequest** is what is expected while establishing the websocket connection. **SubscribeRequest** is what the client sends to server in case a new message type's subscription is needed. Each subscribe request must have a unique string (id). This `id` will be returned back in SubscriptionResponse. Each subscribe request must have a subscription_list of SubscriptionBlock type. **sub_type** is one of [ltp, mtick or utick] - ltp returns LtpPayload of the symbol - mtick returns MtickPayload of the symbol - utick returns UtickPayload of the symbol (must be a FUT symbol) **UnsubscribeRequest** is what is sent by the client to unsubscribe a SubscriptionBlock. Each of these SubscriptionBlock in `subscription_list` of UnsubscribeRequest should be those which were subscribed before in SubscribeRequest. ##### ResponseWrapper.fbs ``` namespace fbs; table LoginResponse { status: string; // "error description" or "OK" } table SubscriptionResponse { id: string; // same as in subscription request XXXX status: string; // "error description" or "OK" } table UnsubscriptionResponse { id: string; status: string; } struct Level { bid: int; // bid price in paise bid_qty: int; // not in lots ask: int; ask_qty: int; } table MtickPayload { ltp: int; // in paise ltq: int; // not in lots levels: [Level]; } table LtpPayload { ltp: int; ltq: int; } struct OptionInfo { strike_price: int; // in paise option_type: byte; // 'C' or 'P' ltp: int; oi: float; volume: int; } table UtickPayload { iv: float; spot_price: int; // FUT symbol's price options: [OptionInfo]; } union MarketDataPayload { MtickPayload, LtpPayload, UtickPayload } table MarketDataBlock { symbol: string; sub_type: string; payload: MarketDataPayload; (if NONE then no data update) } table MarketDataMessage { sending_time: int64; seq_no: uint32; // incremental for a connection data: [MarketDataBlock]; } // A union for the message types union ResponseType { LoginResponse, SubscriptionResponse, UnsubscriptionResponse, MarketDataMessage } // A wrapper table for the message table ResponseWrapper { content: ResponseType; } root_type ResponseWrapper; ``` Each response message from the server to the client can be one of - LoginResponse (received for each LoginRequest) - SubscriptionResponse (received for each SubscribeRequest) - MarketDataResponse (received continuously after successful subscription) - UnsubscriptionResponse (received for each UnsubscribeRequest) Additional Notes: - Please note that this websocket standard websocket. We are not using nats for this communication - base uri for connection is : `ws://arrow.irage.in:9091` --- ## Source file: `rest-api/api-sdk.md` **Canonical URL:** https://docs.arrow.trade/rest-api/api-sdk/ # Python API SDK for Brokerage Client ## Overview The `BrokerageClient` Python SDK provides an interface to interact with the brokerage API, enabling users to manage orders, trades, and positions efficiently. It supports authentication, order creation, modification, cancellation, and data retrieval. --- ## Table of Contents 1. [Installation](#installation) 2. [Initialization](#initialization) 3. [Authentication](#authentication) 4. [Order Management](#order-management) - [Create Order](#create-order) - [Modify Order](#modify-order) - [Delete Order](#delete-order) - [Cancel Order](#cancel-order) 5. [Data Retrieval](#data-retrieval) - [Get Orders](#get-orders) - [Get Trades](#get-trades) - [Get Positions](#get-positions) 6. [Logging](#logging) 7. [Error Handling](#error-handling) 8. [Support](#support) --- ## Installation Ensure you have the required dependencies installed: ```sh pip install requests asyncio nats-py ``` --- ## Initialization First, initialize the client: ```python from brokerage_client import BrokerageClient client = BrokerageClient() ``` --- ## Authentication ### Set Token The user need to have a valid authentication token which can be manually set using the following function: ```python client.setToken("your_auth_token") ``` --- ## Order Management #### Order parameters These parameters are common across different order varieties. | **Parameter** | **Type** | **Description** | |-------------------|-----------|-------------------------------------------------------------------------| | ui_id | string | Unique order id given by client | | symbol_id | string | Unique symbol ID for the underlying asset | | exchange | enum string | Name of the exchange ```NSE``` ```BSE``` ``` MCX```| | segment | enum string | Segment ```FO``` ```CM``` | | price | float32 | Price at which order is placed | | quantity | int | Number of units in multiples of lot_size | | side | enum string | Side ```BUY``` ```SELL``` | | order_type | enum string | Order type ```LIMIT``` ```MARKET``` | | validity | enum string | Validity of order ```DAY``` ```IOC``` | | disclosed_quantity | int | Number of shares visible (Keep more than 30% of quantity) | | user_tag | enum string | An optional tag to apply to an order to identify it (alphanumeric, max 20 chars) | ### Create Order Creates a new order with the specified parameters. #### Request Format: ```python client.create_order( UI_id=54, Price=100, Quantity=10, symbol_id=5678, Side=1, Exchange="NSE", Segment="FO", update="NEW_ORDER", order_type="LIMIT" ) ``` #### API Request Body: ```json { "ui_id": 54, "price": 100, "quantity":10, "symbol_id":5678, "side": 1, "exchange": "NSE", "segment": "FO", "update_type": "NEW_ORDER", "order_type": "LIMIT" } ``` #### Response: - Success Response: - Code : 201 - Response: ```json { "success": true, "message": "Order created ...", "data": 54, "timestamp": { "start_time": "2025-03-24T11:46:36.303115137+05:30", "end_time": "2025-03-24T11:46:36.303249484+05:30" } } ``` **Note** - "data" represents the ui_id here - Error Response: - Code: ```400 Bad Request ``` - Response: ```{error :No Order Created!.}``` --- ### Modify Order Modifies an existing order. #### Request: ```python client.modify_order( UI_id=54, Price=101, Quantity=11, SymbolID=5678, Side=1, Exchange="NSE", Segment="FO", update="MODIFY_ORDER", order_type="LIMIT" ) ``` #### API Request: ```json { "ui_id": 54, "price": 101, "quantity":11, "symbol_id":5678, "side": 1, "exchange": "NSE", "segment": "FO", "update_type": "MODIFY_ORDER", "order_type": "LIMIT"` } ``` #### Response: - Success Response: - Code : 201 - Response: ```json { "success": true, "message": "Order modified ...", "data": 54, "timestamp": { "start_time": "2025-03-24T11:54:59.364828106+05:30", "end_time": "2025-03-24T11:54:59.365077399+05:30" } } ``` **Note** - "data" represents the ui_id here - Error Response: - Code: ```401 Unauthorized ``` - Response: ```json { "error": "Invalid credentials", "status": "failure" } ``` --- ### Delete Order Cancels an existing order. #### Request: ```python client.delete_order( UI_id=54, Price=100, Quantity=10, SymbolID=5678, Side=1, Exchange="NSE", Segment="FO", update="CANCEL_ORDER", order_type="LIMIT" ) ``` #### API Request: ```json { "ui_id": 54, "price": 100, "quantity":10, "symbol_id":5678, "side": 1, "exchange": "NSE", "segment": "FO", "update_type": "CANCEL_ORDER", "order_type": "LIMIT", } ``` #### Response: - Success Response: - Code : 201 - Response ```json { "success": true, "message": "Order created ...", "data": 40098, "timestamp": { "start_time": "2025-03-24T11:46:36.303115137+05:30", "end_time": "2025-03-24T11:46:36.303249484+05:30" } } ``` **Note** - "data" represents the ui_id here - Error Response: - Code: ```400 Bad Request ``` - Response: ```{error :No Order Created!.}``` --- ## Data Retrieval ### Get Orders Retrieves a list of all orders for the client. ### Regular order parameters These parameters are common across different order varieties. | **Parameter** | **Type** | **Description** | |-------------------|-----------|-------------------------------------------------------------------------| | cl_ordID | string | Unique client order ID | | exchg_ordID | string | The user/partner generated ID for tracking back | | order_status | enum string | Last updated status of the order ```PENDING``` ```REJECTED``` ```CANCELLED``` ```TRADED``` ```EXPIRED``` | | exchange | enum string | Name of the exchange ```NSE``` ```BSE``` ``` MCX```| | segment | enum string | Segment ```FO``` ```CM``` | | side | enum string | Side ```BUY``` ```SELL``` | | validity | enum string | Validity of order ```DAY``` ```IOC``` | | order_type | enum string | Order type ```LIMIT``` ```MARKET``` | | symbol | string | Symbol names where you can trade both regular shares and F&O, such as ```ACC``` ```HDFC BANK``` ```BANKNIFTY24O1651900CE``` | | symbol_id | string | Unique symbol ID | | quantity | int | Number of units in multiples of lot_size | | disclosed_quantity | int | Number of shares visible (Keep more than 30% of quantity) | | price | float32 | Price at which order is placed | | exchange_time | string | Exchange time | | created_time | string | Created time refers to the time when the order is placed | | updated_time | string | Updated time refers to the time when the order is updated | | avg_trade_price | float32 | Average trade price is the accumulated average price when multiple shares are traded at different prices| | user_tag | enum string | An optional tag to apply to an order to identify it (alphanumeric, max 20 chars) | | filled_qty | int | Filled quantity refers to the number of shares that have been traded | | oms_error_code | string | OMS Error code in case the order is ```REJECTED``` or ```FAILED```| | oms_error_description | string | Description of error in case the order is ```REJECTED``` or ```FAILED```| #### Request: ```python orders = client.get_orders() ``` #### Response: - Success Response: - Code : 200 - Content: ```json { "success": true, "message": "Orders retrieved ...", "data": [ { "ui_id": 2013, "user_order_id": 1000006, "exchg_order_id": "101000009", "order_status": "STANDING", "exchange": "NSE", "segment": "FO", "side": 1, "validity": "", "order_type": "LIMIT", "symbol": "MIDCPNIFTY25MAR8675CE", "symbol_id": 35068, "quantity": 6, "disclosed_quantity": 0, "price": 207845, "avg_trade_price": 0, "user_tag": "my_user_tag", "remaining_quantity": 6, "filled_qty": 0, "oms_error_code": 0, "oms_error_description": "", "exchange_time": "", "created_time": "2025-03-24 11:41:02.197656879 +0530 IST", "updated_time": "" } ], "timestamp": { "start_time": "2025-03-24T11:44:32.754177584+05:30", "end_time": "2025-03-24T11:44:32.754408119+05:30" } } ``` - Error Response: - Code: ```404 NOT FOUND ``` - Content: ```{error :Order not found!.}``` ### Get Trades Fetches trade details for executed orders. ### Regular trade parameters These parameters are common across different trades. | **Parameter** | **Type** | **Description** | |-------------------|-----------|-------------------------------------------------------------------------| | symbol_id | string | Unique symbol ID | | exchange | enum string | Name of the exchange ```NSE``` ```BSE``` ``` MCX```| | segment | enum string | Segment ```FO``` ```CM``` | | side | enum string | Side ```BUY``` ```SELL``` | | symbol | string | Symbol names where you can trade both regular shares and F&O, such as ```ACC.NSE.EQ``` ```HDFCBANK.NSE.EQ``` ```BANKNIFTY24O1651900CE``` | | price | float32 | Price at which order is placed | | quantity | int | Number of units in multiples of lot_size | | fill_id | string | Trade specific identification generated by exchange | | order_id | string | Order specific identification generated by iRage | | fill_timestamp | string | Timestamp at which the trade was filled at the exchange | | created_time | string | Created time refers to the time when the order is placed | | exchange_timestamp| string | Timestamp at which the order was registered by the exchange | #### Request: ```python trades = client.get_trades() ``` #### Response: - Success Response: - Code : 201 - Content: ```json { "success": true, "message": "Order created ...", "data": 40098, "timestamp": { "start_time": "2025-03-24T11:46:36.303115137+05:30", "end_time": "2025-03-24T11:46:36.303249484+05:30" } } ``` - Error Response: - Code: ```400 Bad Request ``` - Content: ```{error :No Order Created!.}``` ### Get Positions Retrieves the current positions of the user. ### Regular positions parameters These parameters are common across different positions. | **Parameter** | **Type** | **Description** | |-------------------|-----------|-------------------------------------------------------------------------| | security_id | string | Unique security ID | | symbol | string | Symbol names where you can trade both regular shares and F&O, such as ```ACC``` ```HDFC BANK``` ```BANKNIFTY24O1651900CE``` | | exchange | enum string | Name of the exchange ```NSE``` ```BSE``` ``` MCX```| | segment | enum string | Segment ```FO``` ```CM``` | | buy_avg | float32 | Buy Avg | | buy_qty | int | Buy quantity | | sell_avg | float32 | Average sell price of the share | | net_qty | int | Net quantity | | realized_profit | float32 | Realized Profit | | unrealized_profit | float32 | Unrealized Profit | | multiplier | int | multiplier | | day_buy_quantity | int | In day buying quantity | | day_buy_price | float32 | In day buying price | | day_sell_price | float32 | In day selling price| | day_sell_quantity | int | In day selling quantity | | overnight_quantity| int | In overnight quantity | #### Request: ```python positions = client.get_positions() ``` #### Response: - Success Response: - Code : 200 - Content: ```json { "success": true, "message": "Positions retrieved ...", "data": [ { "ucc": "KESHAV", "exchange_destination": "", "symbol_id": 35068, "symbol_name": "", "expiry": "", "strike": 0, "instrument_type": "", "underlying": "", "buy_quantity": 2280, "buy_value": 473949240, "buy_avg_price": 207872, "sell_quantity": 0, "sell_value": 0, "sell_avg_price": 0, "net_quantity": 2280, "realized_pnl": 473949240, "order_type": 0, "user_tag": "my_user_tag" } ], "timestamp": { "start_time": "2025-03-24T11:41:05.313009683+05:30", "end_time": "2025-03-24T11:41:05.313535334+05:30" } } ``` - Error Response: - Code: ```404 NOT FOUND ``` - Content: ```{error :No Positions Found!.}``` --- ## Logging The SDK logs API interactions to `api-client.log`. This helps in debugging and tracking API requests and responses. --- ## Error Handling The SDK includes error handling mechanisms to manage HTTP errors, SSL verification issues, and other exceptions. --- ## Support For any issues, contact IRAGE BROKING SERVICES LLP --- ## Source file: `rest-api/api.md` **Canonical URL:** https://docs.arrow.trade/rest-api/api/ # Arrow API Endpoints All requests use the base URL: `https://arrow.irage.in:9097/` **Note:** Every API request must include the following header: ``` Authorization: Bearer ``` ## 1. Symbol Information Endpoint **Endpoint URL:** `/v1/symbolinfo?exchange=e&segment=s&limit=l&query=q&compress=c` **Description:** where, - allowed `e`+`s` combination is one of ["NSEFO", "NSECD", "MCXFO", "NSECM", "NSEINDEX", "BSEFO", "BSECD", "BSECM", "NSECO"] - if `c` = true then `Content-Encoding = gzip` is applied - `q` is prefix of of symbol. all prefixMatching symbols are returned as response - `l` max-number of matching responses required **Sample Query**: `https://arrow.irage.in:9097/v1/symbolinfo?exchange=NSE&segment=CM&limit=1&query=IDEAFORGE` **Sample Response:** ```json [ { "Id": 17140, "UnderlyingId": -1, "LotSize": 1, "TickSize": 0.05, "Unit": 1, "Strike": -1, "PriceMul": 100, "StrikeMul": 100, "Segment": "CM", "Exchange": "NSE", "Underlying": "IDEAFORGE", "Expiry": "-1", "ExpiryTimestamp": 315512999, "Name": "IDEAFORGE.NSE.EQ", "UnderlyingName": "", "InstrumentType": "XX", "SecurityType": "EQ", "Code": "", "Series": "EQ", "IsNotEligible": false, "WkHigh52": 864, "WkLow52": 322 } ] ``` ## 2. Underlying Information Endpoint **Endpoint URL:** `/v1/symbolinfo/underlyinginfo?exchange=e&segment=s&underlying=u&query=q&expiry=exp&limit=l&compress=c` **Description:** where, - allowed `e`+`s` combination is one of ["NSEFO", "NSECD", "MCXFO", "NSECM", "NSEINDEX", "BSEFO", "BSECD", "BSECM", "NSECO"] - if `c` = true then `Content-Encoding = gzip` is applied - `l` max-number of matching responses required - `exp` is expiry of underlying in YYYYMMDD format. 19700101 for CM stocks - `q` is one of - q=listUnderlying: list all underlyings of `e`+`s` (u,exp are ignore in this) - q=listExpiry: list all expiries of `e`+`s`, `u` (exp is ignored in this) - q=symbols: list all symbols belonging to `e`+`s`, `u`, `exp` **Sample Query**: `https://arrow.irage.in:9097/v1/symbolinfo/underlyinginfo?limit=-1&compress=true&exchange=NSE&segment=FO&query=listUnderlying` **Sample Response:**: ``` ["BANKNIFTY","FINNIFTY","MIDCPNIFTY","NIFTY","NIFTYNXT50","011NSETEST","021NSETEST","031NSETEST","041NSETEST","051NSETEST","061NSETEST","071NSETEST","081NSETEST","091NSETEST","101NSETEST","111NSETEST","121NSETEST","131NSETEST","141NSETEST","151NSETEST","161NSETEST","171NSETEST","181NSETEST","AARTIIND","ABB","ABCAPITAL","ABFRL","ACC","ADANIENSOL","ADANIENT","ADANIGREEN","ADANIPORTS","ALKEM","AMBUJACEM","ANGELONE","APLAPOLLO","APOLLOHOSP","APOLLOTYRE","ASHOKLEY","ASIANPAINT","ASTRAL","ATGL","AUBANK","AUROPHARMA","AXISBANK","BAJAJ-AUTO","BAJAJFINSV","BAJFINANCE","BALKRISIND","BANDHANBNK","BANKBARODA","BANKINDIA","BEL","BERGEPAINT","BHARATFORG","BHARTIARTL","BHEL","BIOCON","BOSCHLTD","BPCL","BRITANNIA","BSE","BSOFT","CAMS","CANBK","CDSL","CESC","CGPOWER","CHAMBLFERT","CHOLAFIN","CIPLA","COALINDIA","COFORGE","COLPAL","CONCOR","CROMPTON","CUMMINSIND","CYIENT","DABUR","DALBHARAT","DEEPAKNTR","DELHIVERY","DIVISLAB","DIXON","DLF","DMART","DRREDDY","EICHERMOT","ESCORTS","EXIDEIND","FEDERALBNK","GAIL","GLENMARK","GMRAIRPORT","GODREJCP","GODREJPROP","GRANULES","GRASIM","HAL","HAVELLS","HCLTECH","HDFCAMC","HDFCBANK","HDFCLIFE","HEROMOTOCO","HFCL","HINDALCO","HINDCOPPER","HINDPETRO","HINDUNILVR","HINDZINC","HUDCO","ICICIBANK","ICICIGI","ICICIPRULI","IDEA","IDFCFIRSTB","IEX","IGL","IIFL","INDHOTEL","INDIANB","INDIGO","INDUSINDBK","INDUSTOWER","INFY","INOXWIND","IOC","IRB","IRCTC","IREDA","IRFC","ITC","India VIX","JINDALSTEL","JIOFIN","JSL","JSWENERGY","JSWSTEEL","JUBLFOOD","KALYANKJIL","KEI","KOTAKBANK","KPITTECH","LAURUSLABS","LICHSGFIN","LICI","LODHA","LT","LTF","LTIM","LUPIN","M&M","M&MFIN","MANAPPURAM","MARICO","MARUTI","MAXHEALTH","MCX","MFSL","MGL","MOTHERSON","MPHASIS","MRF","MUTHOOTFIN","NATIONALUM","NAUKRI","NBCC","NCC","NESTLEIND","NHPC","NMDC","NTPC","NYKAA","Nifty 50","Nifty Bank","Nifty Fin Service","Nifty Midcap 50","OBEROIRLTY","OFSS","OIL","ONGC","PAGEIND","PATANJALI","PAYTM","PEL","PERSISTENT","PETRONET","PFC","PHOENIXLTD","PIDILITIND","PIIND","PNB","PNBHOUSING","POLICYBZR","POLYCAB","POONAWALLA","POWERGRID","PRESTIGE","RAMCOCEM","RBLBANK","RECLTD","RELIANCE","SAIL","SBICARD","SBILIFE","SBIN","SHREECEM","SHRIRAMFIN","SIEMENS","SJVN","SOLARINDS","SONACOMS","SRF","SUNPHARMA","SUPREMEIND","SYNGENE","TATACHEM","TATACOMM","TATACONSUM","TATAELXSI","TATAMOTORS","TATAPOWER","TATASTEEL","TATATECH","TCS","TECHM","TIINDIA","TITAGARH","TITAN","TORNTPHARM","TORNTPOWER","TRENT","TVSMOTOR","ULTRACEMCO","UNIONBANK","UNITDSPR","UPL","VBL","VEDL","VOLTAS","WIPRO","YESBANK","ZOMATO","ZYDUSLIFE"] ``` ## 3. Symbol Information (List) Endpoint **Endpoint URL:** `/v1/symbolinfo?list=symName1,symName2&limit=l&compress=c` **Description:** where, - `symName(i)` = comma separated symNames of the instrument as from symbolinfo api - if `c` = true then `Content-Encoding = gzip` is applied - `l` max-number of matching responses required **Sample Query**: `https://arrow.irage.in:9097/v1/symbolinfo/list?symbols=IDEA.NSE.EQ,IDEAFORGE.NSE.EQ,IDEA25APRFUT,&limit=-1` **Sample Response**: ``` [ { "Id": 14366, "UnderlyingId": -1, "LotSize": 1, "TickSize": 0.01, "Unit": 1, "Strike": -1, "PriceMul": 100, "StrikeMul": 100, "Segment": "CM", "Exchange": "NSE", "Underlying": "IDEA", "Expiry": "-1", "ExpiryTimestamp": 315512999, "Name": "IDEA.NSE.EQ", "UnderlyingName": "", "InstrumentType": "XX", "SecurityType": "EQ", "Code": "", "Series": "EQ", "IsNotEligible": false, "WkHigh52": 19, "WkLow52": 6 }, { "Id": 17140, "UnderlyingId": -1, "LotSize": 1, "TickSize": 0.05, "Unit": 1, "Strike": -1, "PriceMul": 100, "StrikeMul": 100, "Segment": "CM", "Exchange": "NSE", "Underlying": "IDEAFORGE", "Expiry": "-1", "ExpiryTimestamp": 315512999, "Name": "IDEAFORGE.NSE.EQ", "UnderlyingName": "", "InstrumentType": "XX", "SecurityType": "EQ", "Code": "", "Series": "EQ", "IsNotEligible": false, "WkHigh52": 864, "WkLow52": 322 }, { "Id": 73397, "UnderlyingId": 14366, "LotSize": 40000, "TickSize": 0.01, "Unit": 1, "Strike": -1, "PriceMul": 100, "StrikeMul": 100, "Segment": "FO", "Exchange": "NSE", "Underlying": "IDEA", "Expiry": "20250424", "ExpiryTimestamp": 1745485200, "Name": "IDEA25APRFUT", "UnderlyingName": "IDEA.NSE.EQ", "InstrumentType": "XX", "SecurityType": "FUTSTK", "Code": "", "Series": "", "IsNotEligible": false, "WkHigh52": 0, "WkLow52": 0 } ] ``` **Sample Index Query**: `https://arrow.irage.in:9097/v1/symbolinfo/list?limit=-1&symbols=NIFTYMIDSELECT,Nifty50,NiftyFinService,NiftyBank,NiftyNext50` ``` [ { "Id": 0, "UnderlyingId": 0, "LotSize": 0, "TickSize": 0, "Unit": 0, "Strike": 0, "PriceMul": 100, "StrikeMul": 100, "Segment": "INDEX", "Exchange": "NSE", "Underlying": "NIFTYMIDSELECT", "Expiry": "", "ExpiryTimestamp": 0, "Name": "NIFTYMIDSELECT", "UnderlyingName": "", "InstrumentType": "", "SecurityType": "", "Code": "-", "Series": "", "IsNotEligible": false, "WkHigh52": 0, "WkLow52": 0 }, { "Id": 0, "UnderlyingId": 0, "LotSize": 0, "TickSize": 0, "Unit": 0, "Strike": 0, "PriceMul": 100, "StrikeMul": 100, "Segment": "INDEX", "Exchange": "NSE", "Underlying": "NIFTY50", "Expiry": "", "ExpiryTimestamp": 0, "Name": "Nifty50", "UnderlyingName": "", "InstrumentType": "", "SecurityType": "", "Code": "-", "Series": "", "IsNotEligible": false, "WkHigh52": 0, "WkLow52": 0 }, { "Id": 0, "UnderlyingId": 0, "LotSize": 0, "TickSize": 0, "Unit": 0, "Strike": 0, "PriceMul": 100, "StrikeMul": 100, "Segment": "INDEX", "Exchange": "NSE", "Underlying": "NIFTYFINSERVICE", "Expiry": "", "ExpiryTimestamp": 0, "Name": "NiftyFinService", "UnderlyingName": "", "InstrumentType": "", "SecurityType": "", "Code": "-", "Series": "", "IsNotEligible": false, "WkHigh52": 0, "WkLow52": 0 }, { "Id": 0, "UnderlyingId": 0, "LotSize": 0, "TickSize": 0, "Unit": 0, "Strike": 0, "PriceMul": 100, "StrikeMul": 100, "Segment": "INDEX", "Exchange": "NSE", "Underlying": "NIFTYBANK", "Expiry": "", "ExpiryTimestamp": 0, "Name": "NiftyBank", "UnderlyingName": "", "InstrumentType": "", "SecurityType": "", "Code": "-", "Series": "", "IsNotEligible": false, "WkHigh52": 0, "WkLow52": 0 }, { "Id": 0, "UnderlyingId": 0, "LotSize": 0, "TickSize": 0, "Unit": 0, "Strike": 0, "PriceMul": 100, "StrikeMul": 100, "Segment": "INDEX", "Exchange": "NSE", "Underlying": "NIFTYNEXT50", "Expiry": "", "ExpiryTimestamp": 0, "Name": "NiftyNext50", "UnderlyingName": "", "InstrumentType": "", "SecurityType": "", "Code": "-", "Series": "", "IsNotEligible": false, "WkHigh52": 0, "WkLow52": 0 } ] ``` ## 4. Broadcast Information Endpoint **Endpoint URL:** `/v1/bcastinfo?symbols=symName1,symName2` **Description** where, - `symName(i)` = comma separated symNames of the instrument as from symbolinfo api | price_multiplier across segments | FO | CM | CD | CO | | --------------------------------- | --- | --- | -------- | ----- | | ltp/bid_price/ask_price/avg_price/open/high/low/close | 100 | 100 | 10000000 | 10000 | **Sample Query**: `https://arrow.irage.in:9097/v1/bcastinfo?symbols=NIFTY25APRFUT,IDEA.NSE.EQ` **Sample Response**: ```json [ { "timestamp": "2025-04-02T13:57:45.050351518+05:30", "symid": 54452, "open": 23326.95, "high": 23442.15, "low": 23300, "close": 23321.4, "price": 23397.55, "volume": 3642525, "ltp": 23399.95, "ltq": 75, "avg_price": 23384.18, "ask_price1": 23398.8, "bid_price1": 23396.3, "ask_price2": 23399.95, "bid_price2": 23396.25, "ask_price3": 0, "bid_price3": 0, "ask_price4": 0, "bid_price4": 0, "ask_price5": 0, "bid_price5": 0, "ask_qty1": 300, "bid_qty1": 225, "ask_qty2": 150, "bid_qty2": 75, "ask_qty3": 0, "bid_qty3": 0, "ask_qty4": 0, "bid_qty4": 0, "ask_qty5": 0, "bid_qty5": 0, "spread": 2.5, "symbol_name": "NIFTY25APRFUT", "oi": 12716175, "iv": 12.400009, "52_week_high": null, "52_week_low": null, "dpr_high": 25653.55, "dpr_low": 20989.3 }, { "timestamp": "2025-04-02T13:57:44.730863731+05:30", "symid": 14366, "open": 8.31, "high": 8.34, "low": 7.94, "close": 8.1, "price": 8.14, "volume": 725961244, "ltp": 8.15, "ltq": 1, "avg_price": 8.1, "ask_price1": 8.15, "bid_price1": 8.14, "ask_price2": 8.16, "bid_price2": 8.13, "ask_price3": 8.17, "bid_price3": 8.12, "ask_price4": 8.18, "bid_price4": 8.11, "ask_price5": 8.19, "bid_price5": 8.1, "ask_qty1": 686208, "bid_qty1": 524103, "ask_qty2": 630381, "bid_qty2": 1891121, "ask_qty3": 540179, "bid_qty3": 1334711, "ask_qty4": 1012610, "bid_qty4": 2153020, "ask_qty5": 869310, "bid_qty5": 2530635, "spread": 0.009999999999999787, "symbol_name": "IDEA.NSE.EQ", "oi": null, "iv": null, "52_week_high": 19, "52_week_low": 6, "dpr_high": 8.91, "dpr_low": 7.29 } ] ``` **Sample Query**: `https://arrow.irage.in:9097/v1/bcastinfo?symbols=Nifty50` **Sample Response**: ```json [ { "timestamp": "2025-04-02T13:34:23.931413088+05:30", "symid": null, "open": 23192.6, "high": 23250.6, "low": 23189.35, "close": 23165.7, "price": 23295.2, "volume": null, "ltp": 23295.2, "ltq": null, "avg_price": null, "ask_price1": null, "bid_price1": null, "ask_price2": null, "bid_price2": null, "ask_price3": null, "bid_price3": null, "ask_price4": null, "bid_price4": null, "ask_price5": null, "bid_price5": null, "ask_qty1": null, "bid_qty1": null, "ask_qty2": null, "bid_qty2": null, "ask_qty3": null, "bid_qty3": null, "ask_qty4": null, "bid_qty4": null, "ask_qty5": null, "bid_qty5": null, "spread": null, "symbol_name": "Nifty50", "oi": null, "iv": null, "52_week_high": null, "52_week_low": null, "dpr_high": null, "dpr_low": null } ] ``` --- ## Source file: `rest-api/authentication.md` **Canonical URL:** https://docs.arrow.trade/rest-api/authentication/ # Authentication Authentication forms the cornerstone of the Arrow Developer API Suite, ensuring secure access to all platform services. This guide provides comprehensive instructions for generating authentication tokens and establishing secure API connections. ## Prerequisites Before proceeding with authentication, Please make sure you have - Valid Arrow user credentials - Registered your redirect URL in the developer apps section (click on the profile icon and then click on the Trading API's on the dropdown menu) of the main Trading App - Filled in the Form with the required data (static IP is now mandatory as per the latest SEBI Circular) - You have your application credentials: `appID` and `appSecret` handy (Clicking on the +Create New button in the Trading API Section) ## Authentication Flow The Arrow API employs a secure authentication process combining OAuth-style redirects with SHA256 cryptographic verification. ### Step 1: Initiate Login Session Navigate to the Arrow authentication endpoint with your application ID: ``` https://app.arrow.trade/app/login?appID= ``` ### Step 2: Complete User Authentication 1. Enter your **User ID**, **Password**, and **TOTP** (Time-based One-Time Password) 2. Upon successful authentication, you'll be redirected to your registered redirect URL 3. Extract the following parameters from the redirect URL query string: - `request-token`: Temporary authentication token - `checksum`: SHA256 hash of `request-token:appID` for verification ### Step 3: Generate Access Token Create a secure checksum by generating the SHA256 hash of the concatenated string: ``` appID:appSecret:request-token ``` !!! warning "Security Notice" Ensure proper concatenation with colons (`:`) as delimiters. Incorrect formatting will result in authentication failure. ### Step 4: Token Exchange Submit a POST request to exchange your request token for a permanent access token: ```json title="Sample Request" curl --location 'https://edge.arrow.trade/auth/app/authenticate-token' \ --header 'Content-Type: application/json' \ --data '{ "checkSum": "", "token": "", "appID": "" }' ``` ### Successful Response Upon successful authentication, you'll receive: ```json { "data": { "name": "ABHISHEK JAIN", "token": "eyJhbGciOiJFZERTQSIsInR5cCI6IkpXVCJ9.eyJqdGkiOiJBSjAmdakajehrk23432k4h32kl4n32l4kl4324zZDc3YzQ4YyIsImV4cCI6MTc2OTc5Nzc5OSwiaWF0IjoxNzY5NzUzMDA4fQ.hQvnAFJEF1MOt4jygdEFVDyqVPPVqlsX3nvWvEveHI8oHNdrnv1C6fBG12UhMnXfuUc3-6hd77UESaiVrJ3dDw", "userID": "AJ0001" }, "status": "success" } ``` ## Using Your Access Token Include both your `token` and `appID` in all subsequent API requests. The token serves as your authentication credential for accessing Arrow trading services. ## Token Management ### Token Expiration Access tokens have a limited lifespan (24hrs) due to regulatory compliance. Monitor token expiration and implement proper renewal mechanisms in your application. ### Refresh Token Support For applications requiring extended session management or automatic token renewal capabilities, please contact our development team at [tech@arrow.trade](mailto:apisupport@arrow.trade) to discuss refresh token implementation. ## Security Best Practices !!! tip "Security Recommendations" - Store your `appSecret` securely and never expose it in client-side code - Implement proper error handling for authentication failures - Use HTTPS for all authentication requests - Regularly rotate your application credentials - Monitor for unusual authentication patterns ## Troubleshooting | Error | Cause | Solution | |-------|-------|----------| | Invalid checksum | Incorrect SHA256 generation | Verify concatenation format: `appID:appSecret:request-token` | | Token expired | Request token timeout | Restart authentication flow | | Invalid redirect | Unregistered redirect URL | Update redirect URL in Developer Portal | --- ## Source file: `rest-api/error-codes.md` **Canonical URL:** https://docs.arrow.trade/rest-api/error-codes/ # Error Codes Error codes are standardized identifiers that help developers quickly understand and handle different types of failures in API requests. When an API call fails, the system returns a structured error response containing both an HTTP status code and a specific error code to provide precise information about what went wrong. ## Understanding Error Responses Every error response follows this format: ```json title="Error Response Structure" { "status": "error", "message": "Human-readable error description", "errorCode": "STANDARD_ERROR_CODE" } ``` The error code serves multiple purposes: - **Programmatic Handling** - Allows your application to respond differently to different error types - **Debugging** - Helps developers quickly identify the root cause of issues - **User Experience** - Enables appropriate user-facing error messages - **Monitoring** - Facilitates error tracking and system health monitoring ## Complete Error Code Reference ### Client Errors (4xx) These errors occur due to issues with the client request - invalid parameters, authentication problems, or malformed requests. | HTTP Status | Error Code | Error Message | Description | Common Causes | |-------------|------------|---------------|-------------|---------------| | `400` | `BAD_REQUEST_ERROR` | BadRequestError | Missing or invalid request parameters or values | • Missing required fields
• Invalid data format
• Malformed JSON
• Invalid parameter values | | `401` | `UNAUTHORIZED` | UnAuthorized | Your session has expired. You must relogin to access the resource | • Expired authentication token
• Missing API key
• Invalid credentials
• Session timeout | | `403` | `FORBIDDEN` | Forbidden | You don't have permission to access this resource | • Insufficient user privileges
• Resource access restrictions
• Account limitations | | `404` | `NOT_FOUND_ERROR` | NotFoundError | The requested resource could not be found | • Invalid endpoint URL
• Resource has been deleted
• Incorrect resource ID | | `405` | `METHOD_NOT_ALLOWED` | Forbidden | The requested HTTP method is not allowed for this endpoint | • Using GET instead of POST
• Using POST on read-only endpoint
• Incorrect HTTP verb | | `429` | `TOO_MANY_REQUESTS` | TooManyRequests | Rate limit exceeded - too many requests in a given time period | • Exceeding API rate limits
• Burst request patterns
• Multiple concurrent connections | ### Server Errors (5xx) These errors indicate problems on the server side that are typically outside of the client's control. | HTTP Status | Error Code | Error Message | Description | Resolution | |-------------|------------|---------------|-------------|------------| | `500` | `INTERNAL_ERROR` | InternalError | An unexpected error occurred on the server | • Retry the request after a delay
• Contact support if persistent
• Check system status page | | `502` | `OMS_ERROR` | OMSError | The Order Management System (OMS) is down or unreachable | • Retry after a few minutes
• Check OMS system status
• Use alternative endpoints if available | ## Error Code Categories ### Authentication & Authorization - `UNAUTHORIZED` - Session or credential issues - `FORBIDDEN` - Permission and access control issues ### Request Validation - `BAD_REQUEST_ERROR` - Invalid request format or parameters - `NOT_FOUND_ERROR` - Resource doesn't exist - `METHOD_NOT_ALLOWED` - Incorrect HTTP method ### Rate Limiting - `TOO_MANY_REQUESTS` - API usage limits exceeded ### System Issues - `INTERNAL_ERROR` - General server problems - `OMS_ERROR` - Specific service unavailability ## Troubleshooting Guide ### Common Solutions by Error Type #### `BAD_REQUEST_ERROR` 1. **Validate request payload** - Ensure all required fields are present 2. **Check data formats** - Verify dates, numbers, and other formatted fields 3. **Review API documentation** - Confirm parameter names and types 4. **Test with minimal payload** - Start with basic required fields only #### `UNAUTHORIZED` 1. **Check authentication token** - Verify token is valid and not expired 2. **Refresh session** - Re-authenticate if session has timed out 3. **Verify API key** - Ensure API key is correctly configured 4. **Check token format** - Confirm proper Bearer token format #### `TOO_MANY_REQUESTS` 1. **Review rate limits** - Check your current usage against limits 2. **Implement request queuing** - Space out API calls appropriately 3. **Use bulk operations** - Combine multiple operations where possible 4. **Consider caching** - Reduce redundant API calls #### `INTERNAL_ERROR` / `OMS_ERROR` 1. **Retry the request** - Often temporary issues resolve quickly 2. **Check system status** - Verify if there are known service issues 3. **Contact support** - Report persistent errors with request details 4. **Implement fallback logic** - Use cached data or alternative workflows !!! tip "Pro Tips" - Always log the full error context, not just the error message - Set up alerts for error rate spikes or new error types - Monitor error patterns to identify systemic issues - Include request IDs in logs for easier debugging --- ## Source file: `rest-api/fix-sdk.md` **Canonical URL:** https://docs.arrow.trade/rest-api/fix-sdk/ # Irage FIX Python API Documentation --- A. APIs - [Overview](#overview) - [Installation And Dependencies](#Installation-and-Dependencies) - [Configuration File](#configuration-file) - [API Methods](#api-methods) - [Initialization](#initialization) - [Starting the Client](#Starting-the-Client) - [New Order](#new-order) - [Modify Order](#modify-order) - [Cancel Order](#cancel-order) - [Logout](#logout) - [Stop](#stop) - [Logging](#logging) B. FIX Messages - [Connection Flow](#connection-flow) - [Irage FIX Messages](#irage-fix-messages) - [Logon Message](#logon-message) - [Logon Response](#logon-response) - [Logout Message](#logout-message) - [Logout Response](#logout-response-message) - [Heartbeat Message](#heartbeat-message) - [New Order](#new-order-message) - [Modify Order](#modify-order-message) - [Cancel Order](#cancel-order) - [Order Response](#order-response-message) [Common Error Handling](#common-error-handling) - [Login Errors](#login-errors) - [Order Processing Errors](#order-processing-errors) [Appendix](#appendix) C.Algo Orders [VWAP Order API Documentation](#vwap-order-api-documentation) - [Overview](#vwap-overview) - [VWAP Order Parameters](#vwap-order-parameters) - [Detailed Parameter Descriptions](#detailed-parameter-descriptions) - [Order Placement](#order-placement) - [JSON Format Request Example](#json-format-request-example) - [FIX Format Request Example](#fix-format-request-example) - [FIX Tag Mapping](#fix-tag-mapping) - [Response Format](#response-format) - [JSON Response Example (Success)](#json-response-example-success) - [JSON Response Example (Error)](#json-response-example-error) - [FIX Response Example (Success)](#fix-response-example-success) - [FIX Response Example (Error)](#fix-response-example-error) - [Limitations and Considerations](#limitations-and-considerations) - [Best Practices](#best-practices) - [Error Codes](#error-codes) --- ## Overview Welcome to the Irage FIX Python API Documentation. This document explains how to use the Irage FIX Python API client and describes the FIX messages used by the Irage trading platform. The client encapsulates the FIX protocol details and provides a simple interface for establishing connections and sending orders. ## Installation and Dependencies The client requires: - Python 3.7+ - QuickFIX library (`pip install quickfix`) ## Configuration File Create a configuration file (e.g., `client.cfg`) with the following: ```ini [DEFAULT] ConnectionType = initiator ReconnectInterval = 2 FileStorePath = store FileLogPath = log StartTime = 00:00:00 EndTime = 00:00:00 UseDataDictionary = Y DataDictionary = spec/FIX42.xml ValidateUserDefinedFields = N ValidateFieldsOutOfOrder = N [SESSION] BeginString = FIX.4.2 SenderCompID=YOUR_SENDER_ID TargetCompID = IRZ HeartBtInt = 30 SocketConnectHost = YOUR_HOST SocketConnectPort = YOUR_PORT # Client Authentication Information (Required) UCC = PARTH Settlor = TEST6 PANnumber = PARTH123 CpCode = CPCODE6 AuthToken = YOUR_JWT_TOKEN AuthToken length = LENGTH_OF_TOKEN ``` ### Configuration Parameters | Parameter | Description | |-----------|-------------| | `ConnectionType` | Type of connection (`initiator` or `acceptor`). | | `ReconnectInterval` | Time in seconds to wait before attempting to reconnect. | | `FileStorePath` | Directory to store FIX messages. | | `FileLogPath` | Directory to store log files. | | `StartTime` | Session start time in HH:MM:SS format. | | `EndTime` | Session end time in HH:MM:SS format. | | `UseDataDictionary` | Whether to use a data dictionary (`Y` or `N`). | | `DataDictionary` | Path to the FIX data dictionary file. | | `ValidateUserDefinedFields` | Whether to validate custom fields (`Y` or `N`). | | `ValidateFieldsOutOfOrder` | Whether to allow out-of-order fields (`Y` or `N`). | | `BeginString` | FIX protocol version (e.g., `FIX.4.2`). | | `SenderCompID` | Sender's unique identifier. | | `TargetCompID` | Receiver's unique identifier. | | `HeartBtInt` | Heartbeat interval in seconds. | | `SocketConnectHost` | Host address of the FIX server. | | `SocketConnectPort` | Port number of the FIX server. | | `UCC` | Unique Client Code for authentication. | | `Settlor` | Settlor name. | | `PAN Number` | PAN number of the client. | | `CpCode` | Client participant code. | | `AuthToken` | JWT authentication token. | | `AuthToken length` | Length of the authentication token. | ## API Methods ### Initialization ```python client = FixClient("client.cfg") ``` Initializes a new FIX client with the specified configuration file. - **Parameters:** - `config_file` (str): Path to the configuration file ### Starting the Client ```python client.start() ``` Starts the FIX session and performs the login process. This method blocks until the login is complete or fails. **Returns:** None **Raises:** - `FixClientError`: If there's an error starting the session **Example:** ```python try: client.start() print("Successfully logged in") except FixClientError as e: print(f"Failed to start: {e}") ``` **Response Handling:** After successful login, you'll receive a logon response with margin information in the respective logs (refer to logging section): ``` 8=FIX.4.2|9=111|35=A|34=1|49=IRZ|52=20250128-12:30:31.959572800|56=R001-1|1=R001|98=0|108=30|1000=31815520|1001=20000000000000|10=036| ``` Key fields in the response: - `1000`: Margin Utilized - `1001`: Margin Allocated ### ```python client.send_new_order(order_id, side, ordertype, quantity, price, symbolid, NSEFO) ``` Sends a new order to the market. - **Parameters:** - `order_id` (str): Unique order identifier - `side` (str): Buy/Sell (1=Buy, 2=Sell) - `ordertype` (str): Order type (1=Market, 2=Limit) - `quantity` (int): Order quantity (raw quantity value, not in lots). Must be a multiple of the contract's lot size - `price` (float): Price at which order is placed - `symbolid` (str): Security identifier - `NSEFO` (str): Exchange destination (e.g., "NSEFO") **Returns:** None **Example:** ```python # Send a buy limit order client.send_new_order("112", "1", "2", 100, 4629535, "35012", "NSEFO") ``` **Response Handling:** After sending a new order, you'll receive an execution report in the respective logs (refer to logging section): ``` 8=FIX.4.2|9=285|35=8|49=IRZ|56=R001-1|34=2|52=20250127-13:59:30.64264414|39=0|100=NSEFO|44=4629535|48=35012|38=100|151=100|11=1456|37=101000011|14=0|54=1|55=BANKNIFTY|20=0|150=0|17=0|6=4629535|9010=0|4000=20250127-13:59:30.56993432|4001=20250127-13:59:30.57016567|4002=20250127-13:59:30.64263776|1=R001|10=220| ``` ### Modify Order ```python client.send_modify_order(order_id, origcid, side, ordertype, quantity, price, symbolid, NSEFO) ``` Modifies an existing order. - **Parameters:** - `order_id` (str): New order identifier (can be same as original) - `origcid` (str): Original order identifier to modify - `side` (str): Buy/Sell (1=Buy, 2=Sell) - `ordertype` (str): Order type (1=Market, 2=Limit) - `quantity` (int): New order quantity (raw quantity value, not in lots). Must be a multiple of the contract's lot size - `price` (float): Price at which order is placed - `symbolid` (str): Security identifier - `NSEFO` (str): Exchange destination (e.g., "NSEFO") **Returns:** None **Example:** ```python # Modify an existing order to change the price client.send_modify_order("112", "112", "1", "2", 100, 4629536, "35012", "NSEFO") ``` **Response Handling:** After sending a modify order request, you'll receive an execution report in the respective logs (refer to logging section): ``` 8=FIX.4.2|9=297|35=8|34=2|49=IRZ|52=20250127-14:30:00.414959626|56=R001-1|1=R001|6=4629536|11=6541|14=0|17=0|20=0|37=101000003|38=100|39=5|41=6541|44=4629536|48=35012|54=1|55=BANKNIFTY|100=NSEFO|150=5|151=100|4000=20250127-14:30:00.406215357|4001=20250127-14:30:00.406236307|4002=20250127-14:30:00.414959129|9010=0|10=239| ``` ### Cancel Order ```python client.send_cancel_order(order_id, orig_order_id, side, ordertype, symbolid, NSEFO) ``` Cancels an existing order. - **Parameters:** - `order_id` (str): Order identifier for this cancel request - `orig_order_id` (str): Original order identifier to cancel - `side` (str): Buy/Sell (1=Buy, 2=Sell) - `ordertype` (str): Order type (1=Market, 2=Limit) - `symbolid` (str): Security identifier - `NSEFO` (str): Exchange destination (e.g., "NSEFO") **Returns:** None **Example:** ```python # Cancel an existing order client.send_cancel_order("112", "112", "1", "2", "35012", "NSEFO") ``` **Response Handling:** After sending a cancel request, you'll receive an execution report in the respective logs (refer to logging section): ``` 8=FIX.4.2|9=306|35=8|49=IRZ|56=R001-1|34=2|52=20250127-13:28:57.47541976|39=4|100=NSEFO|44=4629535|48=35012|38=100|151=100|41=1237|11=1237|37=101000009|14=0|54=1|55=BANKNIFTY|20=0|150=4|84=100|103=0|17=0|6=4629535|9010=0|4000=20250127-13:28:57.30488917|4001=20250127-13:28:57.30496289|4002=20250127-13:28:57.47531646|1=R001|10=173| ``` ### Logout ```python client.logout() ``` Sends a logout message and closes the FIX session. **Returns:** None **Example:** ```python # Cleanly close the session client.logout() ``` **Response Handling:** After sending a logout request, you'll receive a logout message in the respective logs (refer to logging section): ``` 8=FIX.4.2|9=075|35=5|49=IRZ|56=12345|34=3|52=20241017-10:59:25.99|58=User initiated Logout|10=15| ``` Key fields in the response: - `35`: Message type (5=Logout) - `58`: Text message confirming logout ### Stop ```python client.stop() ``` Stops the FIX session without sending a logout message. This is called automatically by `logout()`. **Returns:** None ## Logging All the messages sent and recieved by client will be stored in the logging path specified in configuration. The file name will be of the following format . ```python FIX.4.2-{SENDER-COMP-ID}-IRZ.event.current.log ``` ## Complete Usage Example ```python from fix_client import FixClient # Initialize client client = FixClient("client.cfg") try: # Start session (login) client.start() print("Login successful") # Send a new order client.send_new_order("112", "1", "2", 100, 4629535, "35012", "NSEFO") print("Order sent") # Wait for order to be processed time.sleep(2) # Modify the order client.send_modify_order("112", "112", "1", "2", 100, 4629536, "35012", "NSEFO") print("Order modified") time.sleep(2) # Cancel the order client.send_cancel_order("112", "112", "1", "2", "35012", "NSEFO") print("Order cancelled") # Logout client.logout() print("Logged out") except Exception as e: print(f"Error: {e}") client.stop() ``` --- **Irage FIX MESSAGES** **Version 1.0** Irage FIX Messages are based on the industry-standard [FIX 4.2](https://www.fixtrading.org/standards/fix-4-2/), with minor modifications to support custom functionality. This document describes the workflow and modifications from standard FIX. ## Connection Flow For clients to initiate the FIX connection, they first need to obtain an Authorization token, by login in to **Irage Authorization Gateway\*\***, which will then provide the necessary auth token. Currently this can be obtained via the irage python-connect wrapper. (***Gateway and login details are shared upon account creation***) After obtaining the authorization token, the client can then initiate the Fix connection, and provide the Logon message as described below.(There are few fields which are custom to the Irage System to be sent in the Logon message). Once the login is successful, client can then proceed with sending normal order related messages as described in the sections below --- ## Irage FIX Messages ### Logon Message The Logon `` message authenticates a user establishing a connection to a remote system. The Logon `` message must be the first message sent by the application requesting to initiate a FIX session. Below fields have been added to Irage Logon Fix message **Additional Fields for Logon:** | Tag | Value | | ---- | ------------------------------ | | 96 | AuthToken | | 439 | Settlor (clearing/NSE) | | 1 | UCC | | 9002 | PAN Number (Custom Field) | | 9003 | CpCode (BSE) | | 95 | AuthToken length | **Sample Message:** ``` 8=FIX.4.2|9=121|35=A|34=1|49=12345|52=20240930-08:24:22.507|56=IRZ|95=7|96=nse@123|98=0|108=30|141=Y|439=TEST1|1=R001|9002=Pan|10=156| ``` --- ### Logon Response The logon response message builds on the original FIX message, with additional fields as mentioned below | Tag | Value | | ---- | ------------------------------- | | 1000 | Margin Utilized (Custom Field) | | 1001 | Margin Allocated (Custom Field) | **Sample Message:** ``` 8=FIX.4.2|9=111|35=A|34=1|49=IRZ|52=20250128-12:30:31.959572800|56=R001-1|1=R001|98=0|108=30|1000=31815520|1001=20000000000000|10=036| ``` --- ### Logout Message The Logout <35=5> message initiates or confirms the termination of a FIX session. Disconnection without the exchange of Logout <35=5> messages should be interpreted as an abnormal condition. | Tag | Value | | --- | ----- | | 1 | UCC | **Sample Message:** ``` 8=FIX.4.2|9=60|35=5|34=3|49=12345|52=20241017-05:27:23.022|56=IRZ|1=R001|10=124| ``` --- ### Logout Response Message Below is the logout response message client should receive on successful processing of the message . **Sample Message:** ``` 8=FIX.4.2|9=075|35=5|49=IRZ|56=12345|34=3|52=20241017-10:59:25.99|58=User initiated Logout|10=15| ``` --- ### Heartbeat Message The Heartbeat <35=0> monitors the status of the communication link and identifies when the last of a string of messages was not received. Following field has been added and is mandatory to be sent for our systems | Tag | Value | | --- | ----- | | 1 | UCC | **Sample Message:** ``` 8=FIX.4.2|9=60|35=0|34=8|49=12345|52=20240930-10:14:51.326|56=IRZ|1=R001|10=127| ``` --- ### New Order Message While sending a new message , below fields should be used based on necessity. Few of these fields are optionals and have been marked appropriately. **Mandatory Fields:** | Tag | Value | |-------|-------------------------------| | 48 | SecurityID | | 100 | ExDestination | | 1 | UCC | | 11 | ClOrdID | | 55 | Symbol (Optional) | | 54 | Side | | 40 | OrdType | | 38 | OrdQty | | 44 | Price | | 9368 | AlgoId (Optional) | | 9369 | AlgoCategory (Optional) | | 18 | ExecInst (Optional) | | 111 | Max single order qty (Optional) | | 59 | TimeInforce (Optional) | | 168 | StartTime (Optional) | | 126 | EndTime (Optional) | | 211 | Price Buffer (Optional) | | 389 | Limit Price Increment (Optional)| | 9011 | MaxTry Order (Optional) | | 9012 | Order Alternative (Optional) | | 9013 | Wait time (Optional) | | 9014 | Order Level (Optional) | | 9015 | Time Interval (Optional) | | 9010 | User Defined OrderTag (It will be echoed) | ExecInst <18> field can be used to provide Instructions for order handling on exchange trading floor. If more than one instruction is applicable to an order, this field can contain multiple instructions separated by space. The purpose of this field is to enable clients to be able to provide a execution type e.g. VWAP, TWAP etc. Few of the valid fields for this tag is mentioned below | Code | Meaning | |------|--------------------------------------| | 9 | Stay on bidside | | 0 | Stay on offerside | | L | Last peg (last sale) | | M | Mid-price peg (midprice of inside quote) | | O | Opening peg | | P | Market peg | | W | Peg to VWAP | Max single order qty <111> field can be used in conjunction with the ExecInst <18>, to provide the value for the maximum single order quantity to be sent to exchange Some of the fields added above have special meaning and more details for them have been specified in Appendix. **Sample Message:** ``` 8=FIX.4.2|9=112|35=D|34=2|49=R001-1|52=20250127-08:27:31.575|56=IRZ|1=R001|11=1456|38=100|40=2|44=4629535|48=35012|54=1|100=NSEFO|10=105| ``` --- ### Modify Order Message While sending a modify message, client needs to populate the following fields using the OrderCancelReplaceRequest Fix message . | Tag | Value | |-------|------------------------------------| | 41 | OrigClOrdID | | 11 | ClOrdID | | 55 | Symbol (Optional) | | 54 | Side | | 40 | OrdType | | 38 | OrdQty | | 44 | Price | | 48 | SecurityID | | 1 | UCC | | 100 | ExDestination | | 9010 | User Defined OrderTag (Optional, echoed) | **Sample Message:** ``` 8=FIX.4.2|9=139|35=G|34=2|49=R001-1|52=20250127-08:58:01.925|56=IRZ|1=R001|11=6541|38=100|40=2|41=6541|44=4629536|48=35012|54=1|55=APPL|100=NSEFO|440=R001-1|10=213| ``` --- ### Cancel Order For canceling an existing order client needs to use OrderCancelRequest FIX message, populated with the following information. | Tag | Value | |-------|---------------| | 41 | OrigClOrdID | | 11 | ClOrdID | | 48 | SecurityID | | 54 | Side | | 1 | UCC | | 100 | ExDestination | | 40 | OrdType | **Sample Message:** ``` 8=FIX.4.2|9=120|35=F|34=2|49=R001-1|52=20250127-07:56:58.547|56=IRZ|1=R001|11=1237|38=100|40=2|41=1237|44=4629535|48=35012|54=1|100=NSEFO|10=224| ``` --- ### Order Response Message For a successful message we provide the standard Execution Report message.(Purpose of Execution Report is to send order information to a FIX client, such as confirmations, fills, and unsolicited changes) The Execution Report <35=8> message is used to: - Confirm the receipt of an order - Confirm changes to an existing order (i.e. accept cancel and replace requests) - Relay order status information - Relay fill information on working orders - Reject orders - Report post-trade fees calculations associated with a trade Below are some of the response messages using the Execution Report, to communicate the order status. | Tag | Value | |-------|-------------------------------------------| | 39 | OrdStatus | | 100 | ExDestination | | 44 | Price | | 48 | SecurityID | | 38 | OrderQty | | 151 | LeavesQty | | 11 | ClOrdID | | 37 | OrderID | | 14 | CumQty | | 54 | Side | | 55 | Symbol | | 20 | ExecTransType | | 150 | ExecType | | 17 | ExecID | | 6 | AvgPx | | 1 | UCC | | 9010 | User Defined OrderTag (Optional, echoed) | | 4000 | Timestamp when received by OMS | | 4001 | Timestamp when sent to exchange | | 4002 | Timestamp of response from exchange | | 4003 | Timestamp when sent to client | **Sample Message:** **Sample Create Order Response:** ``` 8=FIX.4.2|9=285|35=8|49=IRZ|56=R001-1|34=2|52=20250127-13:59:30.64264414|39=0|100=NSEFO|44=4629535|48=35012|38=100|151=100|11=1456|37=101000011|14=0|54=1|55=BANKNIFTY|20=0|150=0|17=0|6=4629535|9010=0|4000=20250127-13:59:30.56993432|4001=20250127-13:59:30.57016567|4002=20250127-13:59:30.64263776|1=R001|10=220| ``` **Sample Modify Order Response:** ``` 8=FIX.4.2|9=297|35=8|34=2|49=IRZ|52=20250127-14:30:00.414959626|56=R001-1|1=R001|6=4629536|11=6541|14=0|17=0|20=0|37=101000003|38=100|39=5|41=6541|44=4629536|48=35012|54=1|55=BANKNIFTY|100=NSEFO|150=5|151=100|4000=20250127-14:30:00.406215357|4001=20250127-14:30:00.406236307|4002=20250127-14:30:00.414959129|9010=0|10=239| ``` **Sample Cancel Order Response:** ``` 8=FIX.4.2|9=306|35=8|49=IRZ|56=R001-1|34=2|52=20250127-13:28:57.47541976|39=4|100=NSEFO|44=4629535|48=35012|38=100|151=100|41=1237|11=1237|37=101000009|14=0|54=1|55=BANKNIFTY|20=0|150=4|84=100|103=0|17=0|6=4629535|9010=0|4000=20250127-13:28:57.30488917|4001=20250127-13:28:57.30496289|4002=20250127-13:28:57.47531646|1=R001|10=173| ``` --- ### Common Error Handling ### Login Errors | Type | Message | | ------------------------------ | ---------------------------------------------------- | | Invalid UCC | 58=UCC(1) not/wrong provided | | Invalid Settlor | 58=Wrong settlor(439) Provided: Immediate Disconnect | | Invalid PAN | 58=Wrong PAN(9002) Provided: Immediate Disconnect | | Invalid CpCode | 58=Wrong CpCode(9003) Provided: Immediate Disconnect | | Invalid Credentials | 58=Invalid Credentials | | Invalid Credentials Raw Length | 58=RawData(95 and 96) not/wrong provided | ### Order Processing Errors **Common Checks During Create, Update, Cancel** | Type | Message | | ---------------------------- | ------------------------------------------------ | | Invalid Quantity | 58=Qty(38) not/wrong provided | | Invalid Exchange Destination | 58=EXCHANGE\_DESTINATION(100) not/wrong provided | | Invalid Price | 58=New Order Failed - Reason Code = E\_RMS\_DPR | | Invalid SecurityID | 58=securityId(48) not/wrong provided | | Invalid Order ID | 58=cid(11) not/wrong provided | | Invalid Order Type | 58=OrdType(40) not/wrong provided | | Invalid Side | 58=side(54) not/wrong provided | ### Specific Checks During Order Operations **During Create:** | Type | Message | | ------------------ | -------------------------------------------------------- | | Duplicate Order ID | 58=NEW REJ: ORDER ALREADY STANDING ON THIS CID | **During Update/Cancel:** | Type | Message | | ----------------------------- | ----------------------------------------------------------------- | | Modify on Same Price/Quantity | 58=Rpl Order Failed - Reason Code = REPLACE ON SAME PRICE AND QTY | | Modify Order with Side Change | 58=MOD/CXL REJ: NO ORDER STANDING ON ORIG_CID(41) | Modify Order with Side Change | 58=CANNOT CHANGE SECURITY_ID(48) ON MOD/CXL | Exchange order id mismatch | 58=CANNOT CHANGE EXCH_ORDER_ID(37) ON MOD/CXL | Duplicate Order ID | 58=MOD/CXL REJ: ORDER ALREADY STANDING ON NEW CID(11) | Side Mismatch | 58=CANNOT CHANGE SIDE(54) ON MOD/CXL --- ## Appendix **Fields Description** ### Authentication & Settlement **AuthToken (Tag 96)** - Used for authentication purposes. - Example - ``` 96=eyJhbGciOiJIUzI1NiIsInR5cCIgOiAiSldUIiwia2lkIiA6ICJkNTQ2M2EwZi01OGI5LTRjMmMtYTI0YS1jZDVmZWQ5NDJlMzYifQ.eyJleHAiOjE3NDAwMjk1MDQsImlhdCI6MTczOTk0MzEwNCwianRpIjoiZWViMDNmNDEtNTY1Ny00MjYzLTg5ZWEtZDg3MmM3ZDIyMTBjIiwiaXNzIjoiaHR0cHM6Ly9hcnJvdy5pcmFnZS5pbjo4NDQzL3JlYWxtcy9JQlNMIiwidHlwIjoiQmVhcmVyIiwiYXpwIjoiSUJTTDkwMDY4Iiwic2lkIjoiZDQ3MWM0ZmUtOTAyMy00MWEzLWFhODItNzc4YmFlMDRmNmUzIiwic2NvcGUiOiJvcGVuaWQgcHJvZmlsZSBlbWFpbCJ9.OzJ_0IQDGWFIH4rxc_xs1B3rTIdkzHSxvfWGmX8yZ6w ``` **Settlor (Tag 439)** - Represents the entity responsible for settlement. - Example ``` 439=TEST1 ``` **UCC (Tag 1)** - Unique Client Code used for identification. - Example ``` 1=R001 ``` **PAN (Tag 9002)** - Permanent Account Number used for tax and financial transactions. - Example ``` 9002=ABCDEF123456 ``` **AuthToken length (Tag 95)** - Specifies the length of the authentication token. - Example ``` 95=461 ``` ### Security & Order Identifiers **SecurityID (Tag 48)** - Identifies the security being traded. - Example ``` 48=35012 ``` **ExDestination (Tag 100)** - Specifies Execution destination as defined by client when order is entered. - Example ``` 100=NSEFO ``` **OrigClordID (Tag 41)** - ClOrdID <11> of the previous order (NOT the initial order of the day) as assigned by the institution, used to identify the previous order in cancel and cancel/replace requests. - Example ``` 41=1237 ``` **ClordID (Tag 11)** - Unique identifier for Order as assigned by client for each order. - Example ``` 11=1237 ``` ### Order Details **Side (Tag 54)** - Indicates the buy or sell side of the order. - **Valid Values:** | Value | Meaning | |-------|---------| | 1 | BUY | | 2 | SELL | **OrdType (Tag 40)** - Specifies the type of order (e.g., Market, Limit). - **Valid Values:** | Value | Meaning | |-------|---------| | 1 | MARKET | | 2 | LIMIT | | 3 | STOP | **OrdQty (Tag 38)** - Defines the quantity of the order. - Example ``` 38=10 ``` **Price (Tag 44)** - Specifies the price at which the order is placed. - Example ``` 44=84451 ``` ### **Execution Details** **OrdStatus (Tag 39)** - Represents the current status of the order. - **Valid Values:** | Value | Meaning | |-------|---------| | 0 | NEW | | 1 | PARTIALLY FILLED | | 2 | FILLED | | 3 | DONE FOR DAY | | 4 | CANCELLED | | 5 | REPLACED | **LeavesQty (Tag 151)** - Quantity of the order yet to be executed. - LeavesQty <151> = OrderQty <38> - CumQty <14>. - Example ``` 11=100 ``` **OrderID (Tag 37)** - Unique identifier assigned to the order by the OMS. - Example ``` 37=101000011 ``` **CumQty (Tag 14)** - Cumulative quantity executed so far. - Example ``` 14=0 ``` **ExecTransType (Tag 20)** - Indicates the transaction type (e.g., New, Cancel, Correct). - **Valid Values:** | Value | Meaning | |-------|---------| | 0 | NEW | | 1 | CANCEL | | 2 | CORRECT | | 3 | STATUS | **ExecType (Tag 150)** - Describes the specific Execution Report (i.e. Pending Cancel) while OrdStatus <39> will always identify the current order status (i.e. Partially Filled) - **Valid Values:** | Value | Meaning | |-------|---------| | 0 | NEW | | 1 | REPLACED | | 2 | REPLACED | | 3 | DONE FOR DAY | | 4 | CANCELLED | | 5 | REPLACED | **ExecID (Tag 17)** - Unique identifier for the execution event assigned by the OMS. - Example ``` 17=0 ``` **AvgPx (Tag 6)** - Average price of the executed order. - As average price is not been calculated on OMS side , it will always be filled 0. ## NewOrderSingle Message - Special Algo Fields This document provides details on specific FIX fields that have special meaning when used with certain algorithms. ### Special Algo Fields **EffectiveTime (Tag 168)** - Used as **Start Time** for a particular algo. - Format: `YYYYMMDD-HH:MM:SS` **ExpireTime (Tag 126)** - Used as **End Time** for a particular algo. - Format: `YYYYMMDD-HH:MM:SS` **PegDifference (Tag 211)** - Used for the **Price Buffer** parameter associated with an algo. **DiscretionOffset (Tag 389)** - Used for the **Limit Price Increment** parameter associated with an algo. **MaxTry (Tag 9011)** - Specifies the **maximum number of times** an existing order can be modified. - Used for the **Limit Price max Try** parameter in an algo. **Order Alternative (Tag 9012)** - Used for the **Limit Price Alternative**, which is specific to some algos. **Valid Values:** | Value | Meaning | |-------|---------| | 0 | MARKET | | 1 | CANCEL | | 2 | PENDING | **Wait Time (Tag 9013)** - Specifies the **time in milliseconds** to wait before modifying/canceling an order post its update. - Used for the **Limit Price Wait** parameter in an algo. **Order Level (Tag 9014)** - Used for the **Place At** parameter in an algo. **Valid Values:** | Value | Meaning | |-------|---------| | 0 | BID | | 1 | ASK | | 2 | LTP | | 3 | BID_2 | | 4 | ASK_2 | **Time Interval (Tag 9015)** - Specifies the **execution time** for an algo. - Used in conjunction with **Start Time (Tag 168)**. ## VWAP Order API Documentation ### VWAP Overview The Volume-Weighted Average Price (VWAP) algorithm is designed to execute orders over a specified time period with the goal of achieving an average execution price close to the volume-weighted average price of the security during that period. This document outlines how to use the VWAP order API, including parameter specifications, limitations, and request/response formats. ### VWAP Order Parameters Parameter | FIX Tag | Description | Data Type | Required | Notes | |-----------|---------|-------------|-----------|----------|-------| | `total_qty` | 13013 | Total quantity to be executed | Integer | Yes | Total number of units to be traded | | `start_time` | 13010 | Execution start time | Time (HH:MM:SS) | Yes | When the VWAP execution should begin | | `duration` | 13011 | Duration of VWAP execution | Integer | Yes | Time in seconds for the VWAP execution | | `symid` | 13008 | Symbol identifier | Integer | Yes | Unique identifier for the trading instrument | | `side` | 13009 | Order side | String/Enum | Yes | "BUY" or "SELL" | | `max_order_size` | 13000 | Maximum size for individual orders | Integer | Yes | Limits the size of each child order | | `start_price_point` | 13001 | Reference price point | Integer | Yes | -1 for Last Traded Price, ≥0 for a specific book level | | `start_price_side` | N/A | Side for reference price | String/Enum | Conditional | Required when `start_price_point` ≥ 0. "BID" or "ASK" | | `price_buffer` | 13002 | Price buffer for quoting | Integer | Yes | In paisa (e.g., +10 means 10 paisa above reference) | | `min_price_change` | 13003 | Minimum price change for replacement | Integer | Yes | Threshold that triggers order replacement | | `max_replace_count` | 13004 | Maximum replacement count per order | Integer | Yes | After this limit is reached, order is canceled and a new one is placed | | `do_after_vwap_run` | 13005 | Action after VWAP completion | Integer/Enum | Yes | 0=Cancel, 1=Market Hit, 2=Keep Standing | | `min_replace_time_gap` | 13006 | Minimum time between replacements | Integer | Yes | In microseconds | | `max_outstanding_order` | 13007 | Maximum concurrent orders | Integer | Yes | Limit on number of standing orders at any time | | `is_ioc` | 13012 | Immediate-Or-Cancel flag | Boolean | Yes | Default is false | ### Detailed Parameter Descriptions **total_qty** The total number of units to be executed by the VWAP algorithm over the specified duration. **start_time** The time at which the VWAP execution should begin, in HH:MM:SS format. **duration** The time period (in seconds) over which the VWAP algorithm will execute the order. **symid** The unique identifier for the trading instrument (e.g., 58958 for BANKNIFTY25MARFUT). **side** Specifies whether the order is to buy or sell the instrument. - BUY: Purchase the specified quantity - SELL: Sell the specified quantity **max_order_size** The maximum size for any individual order placed by the VWAP algorithm. This helps manage market impact. **start_price_point** Defines the reference price point for order placement: - -1: Use the Last Traded Price (LTP) - ≥0: Use a specific book level (e.g., 0 for top of book) **start_price_side** When `start_price_point` ≥ 0, this parameter specifies which side of the order book to use: - BID: Use the bid side of the book - ASK: Use the ask side of the book **price_buffer** The price adjustment (in paisa) applied to the reference price when quoting a new order. For example: - For a BUY order with a buffer of +5, the price will be LTP + 5 paisa - For a SELL order with a buffer of -5, the price will be LTP - 5 paisa **min_price_change** The minimum price movement (in paisa) that will trigger a replacement of an existing order. **max_replace_count** The maximum number of times an order can be replaced before it is canceled and a new order is placed. **do_after_vwap_run** Specifies the action to take after the VWAP execution period ends: - 0 (kCancel): Cancel all remaining standing orders - 1 (kMarketHit): Aggressively execute any remaining quantity at market price - 2 (kKeepStanding): Leave any remaining orders standing in the market **min_replace_time_gap** The minimum time (in microseconds) that must elapse between consecutive order replacements. **max_outstanding_order** The maximum number of orders that can be active in the market simultaneously. **is_ioc** If set to true, orders will be executed with Immediate-Or-Cancel behavior, meaning any portion that cannot be executed immediately will be canceled. ### Order Placement #### JSON Format Request Example ```json { "order_type": "VWAP", "parameters": { "total_qty": 30000, "start_time": "11:20:00", "duration": 10, "symid": 58958, "side": "BUY", "max_order_size": 90, "start_price_point": -1, "price_buffer": 5, "min_price_change": 1, "max_replace_count": 5, "do_after_vwap_run": 0, "min_replace_time_gap": 25000, "max_outstanding_order": 3, "is_ioc": false } } ``` #### FIX Format Request Example ``` 8=FIX.4.2|9=356|35=D|34=124|49=CLIENT|56=SERVER|52=20250225-11:19:45|11=order123|55=BANKNIFTY25MARFUT|54=1|38=30000|40=D|59=0|847=VWAP|13013=30000|13010=11:20:00|13011=10|13008=58958|13009=1|13000=90|13001=-1|13002=5|13003=1|13004=5|13005=0|13006=25000|13007=3|13012=0|10=248| ``` #### FIX Tag Mapping | Parameter | FIX Tag | Values | |-----------|---------|--------| | total_qty | 13013 | Integer | | start_time | 13010 | Time (HH:MM:SS) | | duration | 13011 | Integer (seconds) | | symid | 13008 | Integer | | side | 13009 | 1=BUY, 2=SELL | | max_order_size | 13000 | Integer | | start_price_point | 13001 | Integer (-1 for LTP, ≥0 for book level) | | price_buffer | 13002 | Integer (paisa) | | min_price_change | 13003 | Integer (paisa) | | max_replace_count | 13004 | Integer | | do_after_vwap_run | 13005 | 0=Cancel, 1=Market Hit, 2=Keep Standing | | min_replace_time_gap | 13006 | Integer (microseconds) | | max_outstanding_order | 13007 | Integer | | is_ioc | 13012 | 0=false, 1=true | ### Response Format #### JSON Response Example (Success) ```json { "status": "accepted", "order_id": "vwap_123456", "message": "VWAP order accepted", "timestamp": "2025-02-25T11:19:48.123Z" } ``` #### JSON Response Example (Error) ```json { "status": "rejected", "error_code": "INVALID_PARAMETER", "message": "Invalid start_time format. Expected HH:MM:SS", "timestamp": "2025-02-25T11:19:48.123Z" } ``` #### FIX Response Example (Success) ``` 8=FIX.4.2|9=129|35=8|34=125|49=SERVER|56=CLIENT|52=20250225-11:19:48|11=order123|37=vwap_123456|150=0|39=0|847=VWAP|58=VWAP order accepted|10=165| ``` #### FIX Response Example (Error) ``` 8=FIX.4.2|9=143|35=8|34=125|49=SERVER|56=CLIENT|52=20250225-11:19:48|11=order123|37=NONE|150=8|39=8|847=VWAP|58=Invalid start_time format. Expected HH:MM:SS|10=112| ``` ### Limitations and Considerations 1.**Time Precision**: The `start_time` must be provided in HH:MM:SS format. Orders placed with start times in the past will be rejected. 2.**Quantity Constraints**: - `total_qty` must be greater than zero and less than the exchange-defined maximum for the symbol - `max_order_size` must be less than or equal to `total_qty` - **Error on invalid quantity:** ``` 8=FIX.4.2|9=202|35=U8|49=IRZ|56=R001-1|34=255|52=20250408-14:30:30.768060853|39=8|100=NSEFO|41=01|11=500018|47=1|103=99|9010=ALGO_ORDER|18049=0|4000=1744102830076789666|4003=1744102830076806085|58=Invalid Qty for Vwap Algo|10=246 ``` 3.**Duration Constraints**: - Minimum duration: 1 second - Maximum duration: 86400 seconds (24 hours) 4.**Price Buffer**: - For BUY orders: Positive values increase the reference price, negative values decrease it - For SELL orders: Positive values decrease the reference price, negative values increase it 5.**Order State Management**: - If `max_replace_count` is reached for an order, it will be canceled and replaced with a new order - If market conditions prevent order replacement within `min_replace_time_gap`, the replacement will be queued 6.**Market Hours**: - VWAP orders can only be executed during regular market hours - If the VWAP duration extends beyond market close, the algorithm will complete execution by market close 7.**Rate Limiting**: - Maximum of 10 VWAP orders per second per client - Maximum of 100 VWAP orders per day per client ### Best Practices 1.**Duration Selection**: - For liquid securities, shorter durations (minutes to hours) are typically sufficient - For less liquid securities, longer durations may be necessary to minimize market impact 2.**Order Size Management**: - Set `max_order_size` to a small percentage of average daily volume to minimize market impact - Consider market liquidity when setting `max_outstanding_order` 3.**Price Buffer Management**: - For highly volatile securities, consider using larger values for `min_price_change` - Adjust `price_buffer` based on typical bid-ask spreads for the security 4.**Post-VWAP Action**: - Use `do_after_vwap_run=0` (Cancel) for time-sensitive orders where partial fills are acceptable - Use `do_after_vwap_run=1` (Market Hit) when full execution is more important than price - Use `do_after_vwap_run=2` (Keep Standing) for less time-sensitive orders where price is more important 5.**Cancelling or Stopping an Algo**: - To cancel or stop an algo, send a FIX message with `35=UF` and repeat the same `ClientID` used in the original order - Example: ``` 8=FIX.4.2|35=UF|34=125|49=SERVER|56=CLIENT|41=ClientID|11=ClientID ``` ### Error Codes | Error Code | Description | |------------|-------------| | INVALID_PARAMETER | One or more parameters have invalid values | | MISSING_PARAMETER | One or more required parameters are missing | | INSTRUMENT_NOT_FOUND | The specified instrument ID does not exist | | MARKET_CLOSED | The market is currently closed | | INSUFFICIENT_BALANCE | Insufficient balance to execute the order | | RATE_LIMIT_EXCEEDED | Client has exceeded the rate limit for VWAP orders | | INTERNAL_ERROR | An internal server error occurred | ## Disclaimer This document is subject to updates. Users should check regularly for changes. --- ## Source file: `rest-api/funds.md` **Canonical URL:** https://docs.arrow.trade/rest-api/funds/ # Funds API Access comprehensive margin and fund availability information for portfolio management and trade planning across all market segments. ## Overview The Funds API provides detailed insights into available cash, margin utilization, collateral positions, and trading limits, enabling effective risk management and position sizing decisions. ## Endpoint Reference ### Get User Limits Retrieve complete margin and fund details for the authenticated user account. !!! info "Endpoint Details" **Method:** `GET` **URL:** `/user/limits` **Header:** Required (`appID` + `token`) #### Request Headers | Header | Type | Required | Description | |--------|------|----------|-------------| | `appID` | string | ✓ | Your application identifier | | `token` | string | ✓ | User authentication token | #### Request Example ```bash curl --location 'https://edge.arrow.trade/user/limits' \ --header 'appID: ' \ --header 'token: ' ``` ## Response Schema ### Success Response The API returns comprehensive fund and margin information across all trading segments. ```json { "data": { "allocations": [ { "cashCurrent": "0", "cashEqCurrent": "0", "cashEqOpening": "0", "cashOpening": "0", "nonCashCurrent": "0", "nonCashOpening": "0", "segment": "CM" }, { "cashCurrent": "226462.37", "cashEqCurrent": "0", "cashEqOpening": "0", "cashOpening": "226462.37", "nonCashCurrent": "0", "nonCashOpening": "0", "segment": "FO" }, { "cashCurrent": "0", "cashEqCurrent": "0", "cashEqOpening": "0", "cashOpening": "0", "nonCashCurrent": "0", "nonCashOpening": "0", "segment": "MCX" } ], "margin": { "allocated": "226462.37", "utilized": "-3641.75", "brokerage": "0", "buyOptionPremium": "12506.25", "cashUsed": "-3641.75", "exposureMargin": "0", "intradayCashMargin": "0", "otherMargin": "0", "realizedPnl": "0", "sellOptionPremium": "16148", "spanMargin": "0", "totalMargin": "0", "unrealizedPnl": "1180.75" } }, "status": "success" } ``` ## Integration Guidelines !!! tip "Best Practices" - **Pre-trade validation:** Check available margin before order placement - **Real-time monitoring:** Track margin utilization continuously - **Risk management:** Set appropriate exposure limits - **Settlement tracking:** Monitor payIn/payOut cycles !!! info "Optimization Tips" - Cache fund data for 30-60 seconds to reduce API calls - Implement margin alerts at 80% utilization - Use segment-wise breakdown for detailed analysis - Monitor MTM percentage for portfolio risk assessment --- ## Source file: `rest-api/hft-stream.md` **Canonical URL:** https://docs.arrow.trade/rest-api/hft-stream/ # Arrow HFT Datastream API Real-time market data streaming via WebSocket with binary protocol for high-performance trading applications. ## Overview The Arrow Dataserver API provides institutional-grade real-time market data streaming through persistent WebSocket connections. Built with a binary protocol for maximum efficiency, it supports Last Traded Price (LTP) and Full market depth data across NSE and BSE exchanges. ### Key Features - **Binary Protocol**: Optimized binary frames for minimal latency and bandwidth - **Dual Data Modes**: LTPC (compact) or Full (complete market depth) - **Flexible Symbol Formats**: Subscribe using symbol names or numeric IDs - **Cross-Exchange Coverage**: Unified data stream across NSE CM, NSE FO, BSE CM, and BSE FO - **Configurable Latency**: Adjust tick intervals upto 50ms (Super fast) - **Scalable Architecture**: Support for up to 4,096 simultaneous symbol subscriptions ## Connection Setup ### WebSocket Endpoint ``` wss://socket.arrow.trade?appID=&token= ``` ### Connection Parameters | Parameter | Value | Description | |-----------|-------|-------------| | `appID` | `APP_ID` | Application identifier | | `token` | `TOKEN` | Authentication token | ### Establishing Connection === "JavaScript" ```javascript const wsUrl = 'wss://socket.arrow.trade?appID=APP_ID&token=TOKEN'; const ws = new WebSocket(wsUrl); ws.binaryType = 'arraybuffer'; ws.onopen = function(event) { console.log('Connected to Arrow Dataserver'); }; ws.onmessage = function(event) { const data = new Uint8Array(event.data); const marketData = parseMarketData(data); console.log(marketData); }; ``` === "Python" ```python import websocket import struct ws_url = "wss://socket.arrow.trade?appID=APP_ID&token=TOKEN" def on_message(ws, message): market_data = parse_market_data(message) print(market_data) def on_open(ws): print("Connected to Arrow Dataserver") ws = websocket.WebSocketApp( ws_url, on_message=on_message, on_open=on_open ) ws.run_forever() ``` === "Bash" ```bash # Test connection using websocat websocat "wss://socket.arrow.trade?appID=&token=" ``` ## Subscription Management ### Request Format All subscription requests are sent as JSON strings over the WebSocket connection. ```json { "code": "string", "mode": "string", "symbols": ["string"], "latency": integer } ``` ### Request Fields | Field | Required | Type | Description | |-------|----------|------|-------------| | `code` | Yes | string | Action: `sub`/`s` (subscribe) or `unsub`/`u` (unsubscribe) | | `mode` | Yes | string | Data mode: `ltpc`/`l` (compact) or `full`/`f` (complete) | | `symbols` | Conditional | array | Symbol names (mutually exclusive with `symIds`) | | `symIds` | Conditional | array | Symbol ID objects (mutually exclusive with `symbols`) | | `latency` | No | integer | Tick interval in ms (upto 50, default: 1000) | ### Data Modes | Mode | Shorthand | Packet Size | Description | Best For | |------|-----------|-------------|-------------|----------| | `ltpc` | `l` | 40 bytes | Last Traded Price Compact | Basic price monitoring, high-frequency feeds | | `full` | `f` | 196 bytes | Complete market data with depth | Order book analysis, full market view | ## Symbol Naming Conventions ### Symbol Format Overview | Segment | Pattern | Example | |---------|---------|---------| | NSE Cash Market | `NSE.-` | `NSE.SBIN-EQ` | | NSE Futures | `
F` | `BANKNIFTY30DEC25F` | | NSE Options | `
` | `NYKAA30DEC25C232.5` | | BSE Cash Market | `BSE.` | `BSE.SBIN` | | BSE Futures | `
F` | `SENSEX01JAN26F` | | BSE Options | `
` | `SENSEX01JAN26C74900` | ### NSE Cash Market (NSECM) **Format**: `NSE.-` ``` NSE.SBIN-EQ → State Bank of India, Equity series NSE.RELIANCE-EQ → Reliance Industries, Equity series NSE.TCS-EQ → Tata Consultancy Services, Equity series ``` ### NSE Futures & Options (NSEFO) **Options Format**: `
` - `C` = Call Option - `P` = Put Option - Strike prices are in rupees (drop trailing `.0` for whole numbers) ``` NYKAA30DEC25C232.5 → NYKAA Call, 30-Dec-2025 expiry, Strike ₹232.50 NYKAA30DEC25P360 → NYKAA Put, 30-Dec-2025 expiry, Strike ₹360 NIFTY25JAN25C24000 → NIFTY Call, 25-Jan-2025 expiry, Strike ₹24,000 ``` **Futures Format**: `
F` ``` BANKNIFTY30DEC25F → Bank Nifty Future, 30-Dec-2025 expiry NIFTY25JAN25F → Nifty Future, 25-Jan-2025 expiry ``` ### BSE Cash Market (BSECM) **Format**: `BSE.` ``` BSE.SBIN → State Bank of India BSE.RELIANCE → Reliance Industries BSE.HDFC → HDFC Bank ``` ### BSE Futures & Options (BSEFO) **Options Format**: `
` ``` SENSEX01JAN26C74900 → SENSEX Call, 01-Jan-2026 expiry, Strike ₹74,900 SENSEX01JAN26P74000 → SENSEX Put, 01-Jan-2026 expiry, Strike ₹74,000 ``` **Futures Format**: `
F` ``` SENSEX01JAN26F → SENSEX Future, 01-Jan-2026 expiry ``` ## Subscription Examples ### Subscribe Using Symbol Names ```json { "code": "sub", "mode": "full", "symbols": ["NSE.SBIN-EQ", "BSE.RELIANCE"], "latency": 200 } ``` ### Subscribe Using Symbol IDs ```json { "code": "sub", "mode": "full", "symIds": [ { "exch_seg": 1, "ids": [5042, 4449, 91] }, { "exch_seg": 2, "ids": [100, 200] } ] } ``` ### Subscribe to LTPC Mode ```json { "code": "sub", "mode": "ltpc", "symbols": ["NSE.SBIN-EQ", "NSE.RELIANCE-EQ"], "latency": 100 } ``` ### Unsubscribe from Symbols ```json { "code": "unsub", "mode": "full", "symbols": ["NSE.SBIN-EQ"] } ``` ### Using Shorthand Codes ```json { "code": "s", "mode": "l", "symbols": ["NSE.SBIN-EQ"], "l": 500 } ``` ## Exchange Segments | Value | Segment | Description | |-------|---------|-------------| | 0 | NSE_CM | NSE Cash Market | | 1 | NSE_FO | NSE Futures & Options | | 2 | BSE_CM | BSE Cash Market | | 3 | BSE_FO | BSE Futures & Options | ## Binary Response Format ### Response Packet Structure (540 bytes) After each subscription request, the server sends a binary acknowledgment packet. | Offset | Size | Type | Field | Description | |--------|------|------|-------|-------------| | 0 | 4 | uint32 | size | Total packet size | | 4 | 1 | uint8 | pktType | 99 (PKT_TYPE_RESPONSE) | | 5 | 1 | uint8 | exchSeg | 0 (not used) | | 6 | 16 | char[] | error_code | Error code string (null-terminated) | | 22 | 512 | char[] | error_msg | Error message (null-terminated) | | 534 | 1 | uint8 | request_type | 0=subscribe, 1=unsubscribe | | 535 | 1 | uint8 | mode | 0=ltpc, 1=full | | 536 | 2 | uint16 | success_count | Number of successful symbols | | 538 | 2 | uint16 | error_count | Number of failed symbols | ### Error Codes | Code | Description | |------|-------------| | `SUCCESS` | All symbols processed successfully | | `E_PARTIAL` | Some symbols failed | | `E_ALL_INVALID` | All symbols failed | | `E_INVALID_JSON` | Malformed JSON request | | `E_MISSING_FIELD` | Required field missing | | `E_INVALID_PARAM` | Invalid parameter value | | `E_PARSE_ERROR` | General parsing error | ## Market Data Packets ### LTP Packet (LTPC Mode - 40 bytes) Compact price data for high-frequency monitoring. | Offset | Size | Type | Field | Description | |--------|------|------|-------|-------------| | 0 | 2 | int16 | size | Packet size (40) | | 2 | 1 | uint8 | pktType | 1 (PKT_TYPE_LTP) | | 3 | 1 | uint8 | exchSeg | Exchange segment | | 4 | 4 | int32 | symId | Symbol/Token ID | | 8 | 4 | int32 | ltp | Last traded price (paise) | | 12 | 4 | int32 | vwap | Volume-weighted average price | | 16 | 8 | int64 | volume | Traded volume (units) | | 24 | 8 | uint64 | ltt | Last traded time (seconds) | | 32 | 4 | uint32 | atv | Ask traded volume | | 36 | 4 | uint32 | btv | Buy traded volume | ### Full Packet (Full Mode - 196 bytes) Complete market data with 5-level order book depth. | Offset | Size | Type | Field | Description | |--------|------|------|-------|-------------| | 0 | 2 | int16 | size | Packet size (196) | | 2 | 1 | uint8 | pktType | 2 (PKT_TYPE_FULL) | | 3 | 1 | uint8 | exchSeg | Exchange segment | | 4 | 4 | int32 | token | Symbol/Token ID | | 8 | 4 | int32 | ltp | Last traded price | | 12 | 4 | int32 | ltq | Last traded quantity | | 16 | 4 | int32 | vwap | Volume-weighted average price | | 20 | 4 | int32 | open | Open price | | 24 | 4 | int32 | high | High price | | 28 | 4 | int32 | close | Close price | | 32 | 4 | int32 | low | Low price | | 36 | 4 | int32 | ltt | Last traded time (seconds) | | 40 | 4 | int32 | dpr_l | Day price range low | | 44 | 4 | int32 | dpr_h | Day price range high | | 48 | 8 | int64 | tbq | Total buy quantity | | 56 | 8 | int64 | tsq | Total sell quantity | | 64 | 8 | int64 | volume | Total volume | | 72 | 20 | int32[5] | bid_px | Best 5 bid prices | | 92 | 20 | int32[5] | ask_px | Best 5 ask prices | | 112 | 20 | int32[5] | bid_size | Best 5 bid quantities | | 132 | 20 | int32[5] | ask_size | Best 5 ask quantities | | 152 | 10 | uint16[5] | bid_ord | Bid order counts (levels 1-5) | | 162 | 10 | uint16[5] | ask_ord | Ask order counts (levels 1-5) | | 172 | 8 | uint64 | oi | Open interest | | 180 | 8 | uint64 | ts | Server timestamp (epoch ns) | | 188 | 4 | uint32 | atv | Ask traded volume | | 192 | 4 | uint32 | btv | Buy traded volume | ## Implementation Examples ### Complete WebSocket Client === "JavaScript" ```javascript class ArrowDataserverClient { constructor() { this.wsUrl = 'wss://socket.arrow.trade?appID=APP_ID&token=TOKEN'; this.ws = null; this.callbacks = new Map(); } connect() { return new Promise((resolve, reject) => { this.ws = new WebSocket(this.wsUrl); this.ws.binaryType = 'arraybuffer'; this.ws.onopen = () => { console.log('Connected to Arrow Dataserver'); 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('Disconnected from Arrow Dataserver'); this.reconnect(); }; }); } subscribe(mode, symbols, latency, callback) { const message = { code: 'sub', mode: mode, symbols: symbols, latency: latency || 1000 }; this.ws.send(JSON.stringify(message)); symbols.forEach(sym => this.callbacks.set(sym, callback)); } subscribeByIds(mode, symIds, latency, callback) { const message = { code: 'sub', mode: mode, symIds: symIds, latency: latency || 1000 }; this.ws.send(JSON.stringify(message)); } unsubscribe(mode, symbols) { const message = { code: 'unsub', mode: mode, symbols: symbols }; this.ws.send(JSON.stringify(message)); symbols.forEach(sym => this.callbacks.delete(sym)); } handleMessage(data) { const buffer = new DataView(data); const size = buffer.getInt16(0, true); if (size === 40) { const tick = this.parseLTP(buffer); this.notifyCallbacks(tick); } else if (size === 196) { const tick = this.parseFull(buffer); this.notifyCallbacks(tick); } else if (size === 540) { const response = this.parseResponse(buffer); console.log('Subscription response:', response); } } parseLTP(buffer) { return { pktType: buffer.getUint8(2), exchSeg: buffer.getUint8(3), symId: buffer.getInt32(4, true), ltp: buffer.getInt32(8, true) / 100, vwap: buffer.getInt32(12, true) / 100, volume: Number(buffer.getBigInt64(16, true)), ltt: Number(buffer.getBigUint64(24, true)), atv: buffer.getUint32(32, true), btv: buffer.getUint32(36, true) }; } parseFull(buffer) { const tick = { pktType: buffer.getUint8(2), exchSeg: buffer.getUint8(3), token: buffer.getInt32(4, true), ltp: buffer.getInt32(8, true) / 100, ltq: buffer.getInt32(12, true), vwap: buffer.getInt32(16, true) / 100, open: buffer.getInt32(20, true) / 100, high: buffer.getInt32(24, true) / 100, close: buffer.getInt32(28, true) / 100, low: buffer.getInt32(32, true) / 100, ltt: buffer.getInt32(36, true), dprLow: buffer.getInt32(40, true) / 100, dprHigh: buffer.getInt32(44, true) / 100, tbq: Number(buffer.getBigInt64(48, true)), tsq: Number(buffer.getBigInt64(56, true)), volume: Number(buffer.getBigInt64(64, true)), bidPrices: [], askPrices: [], bidSizes: [], askSizes: [], bidOrders: [], askOrders: [], oi: Number(buffer.getBigUint64(172, true)), timestamp: Number(buffer.getBigUint64(180, true)), atv: buffer.getUint32(188, true), btv: buffer.getUint32(192, true) }; // Parse 5-level depth for (let i = 0; i < 5; i++) { tick.bidPrices.push(buffer.getInt32(72 + i * 4, true) / 100); tick.askPrices.push(buffer.getInt32(92 + i * 4, true) / 100); tick.bidSizes.push(buffer.getInt32(112 + i * 4, true)); tick.askSizes.push(buffer.getInt32(132 + i * 4, true)); tick.bidOrders.push(buffer.getUint16(152 + i * 2, true)); tick.askOrders.push(buffer.getUint16(162 + i * 2, true)); } return tick; } parseResponse(buffer) { const decoder = new TextDecoder(); const errorCodeBytes = new Uint8Array(buffer.buffer, 6, 16); const errorMsgBytes = new Uint8Array(buffer.buffer, 22, 512); return { size: buffer.getUint32(0, true), pktType: buffer.getUint8(4), errorCode: decoder.decode(errorCodeBytes).replace(/\0/g, ''), errorMsg: decoder.decode(errorMsgBytes).replace(/\0/g, ''), requestType: buffer.getUint8(534) === 0 ? 'subscribe' : 'unsubscribe', mode: buffer.getUint8(535) === 0 ? 'ltpc' : 'full', successCount: buffer.getUint16(536, true), errorCount: buffer.getUint16(538, true) }; } notifyCallbacks(tick) { this.callbacks.forEach((callback) => { callback(tick); }); } reconnect() { setTimeout(() => { console.log('Reconnecting...'); this.connect(); }, 5000); } } // Usage const client = new ArrowDataserverClient(); await client.connect(); client.subscribe('full', ['NSE.SBIN-EQ', 'NSE.RELIANCE-EQ'], 200, (tick) => { console.log(`Token: ${tick.token}, LTP: ₹${tick.ltp}, Volume: ${tick.volume}`); }); ``` === "Python" ```python import websocket import json import struct from threading import Thread class ArrowDataserverClient: def __init__(self): self.ws_url = "wss://socket.arrow.trade?appID=APP_ID&token=TOKEN" self.ws = None self.callbacks = {} def connect(self): 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 on_open(self, ws): print("Connected to Arrow Dataserver") def on_error(self, ws, error): print(f"WebSocket error: {error}") def on_close(self, ws, close_status_code, close_msg): print("Disconnected from Arrow Dataserver") def subscribe(self, mode, symbols, latency=1000, callback=None): message = { 'code': 'sub', 'mode': mode, 'symbols': symbols, 'latency': latency } self.ws.send(json.dumps(message)) if callback: for sym in symbols: self.callbacks[sym] = callback def subscribe_by_ids(self, mode, sym_ids, latency=1000): message = { 'code': 'sub', 'mode': mode, 'symIds': sym_ids, 'latency': latency } self.ws.send(json.dumps(message)) def unsubscribe(self, mode, symbols): message = { 'code': 'unsub', 'mode': mode, 'symbols': symbols } self.ws.send(json.dumps(message)) def on_message(self, ws, message): data = bytes(message) size = struct.unpack(' { this.reconnectAttempts = 0; this.resubscribeAll(); }; this.ws.onclose = () => { if (this.reconnectAttempts < this.maxReconnectAttempts) { const delay = this.reconnectInterval * Math.pow(2, this.reconnectAttempts); setTimeout(() => { this.reconnectAttempts++; this.connect(); }, delay); } }; } resubscribeAll() { this.subscriptions.forEach(sub => { this.ws.send(JSON.stringify(sub)); }); } } ``` ### Common Error Scenarios | Error | Cause | Solution | |-------|-------|----------| | `E_INVALID_JSON` | Malformed JSON request | Validate JSON before sending | | `E_MISSING_FIELD` | Required field missing | Include all required fields | | `E_INVALID_PARAM` | Invalid parameter value | Check parameter constraints | | `E_ALL_INVALID` | All symbols invalid | Verify symbol name formats | | `E_PARTIAL` | Some symbols failed | Check error_msg for details | | Connection timeout | Network issues | Implement reconnection with backoff | ## Rate Limits and Constraints | Constraint | Limit | |------------|-------| | Requests per second | 100 per connection | | Maximum symbols per subscription | 4,096 | | Maximum request size | 16 KB | | Latency range | 50ms - 60,000ms | ## Data Type Notes | Type | Description | |------|-------------| | Prices | All prices are in paise (1 rupee = 100 paise) | | Timestamps | Nanoseconds since Unix epoch | | Byte order | Little-endian for all multi-byte integers | ## Best Practices ### Subscription Management - **Batch subscriptions**: Subscribe to multiple symbols in a single request to reduce overhead - **Mode selection**: Use `ltpc` for minimal data when you only need last traded prices; use `full` for market depth and complete price information - **Symbol validation**: Verify symbol names/IDs before sending requests - **Latency tuning**: Adjust latency based on your application needs (lower for real-time, higher for reduced bandwidth) ### Error Handling - **Connection errors**: Reconnect with exponential backoff - **Invalid symbols**: Check the `error_msg` field for specific invalid symbols - **Subscription limits**: Split large requests into smaller batches - **Parse errors**: Validate JSON format before sending ### Performance Optimization - **Memory management**: Parse and process data efficiently to prevent memory leaks - **Binary parsing**: Use optimized binary parsers for high-frequency data - **Buffer handling**: Implement proper buffer management for binary data streams - **Connection pooling**: Reuse connections where possible ## Market Data Applications ### Real-Time Price Monitoring ```javascript function formatTick(tick) { const change = ((tick.ltp - tick.close) / tick.close * 100).toFixed(2); const direction = tick.ltp >= tick.close ? '▲' : '▼'; return `${tick.token}: ₹${tick.ltp} ${direction} ${change}%`; } ``` ### Order Book Analysis ```javascript function analyzeOrderBook(tick) { const bestBid = tick.bidPrices[0]; const bestAsk = tick.askPrices[0]; const spread = bestAsk - bestBid; const spreadPercent = (spread / bestBid * 100).toFixed(4); const totalBidDepth = tick.bidSizes.reduce((a, b) => a + b, 0); const totalAskDepth = tick.askSizes.reduce((a, b) => a + b, 0); return { spread: spread, spreadPercent: spreadPercent, bidDepth: totalBidDepth, askDepth: totalAskDepth, imbalance: (totalBidDepth - totalAskDepth) / (totalBidDepth + totalAskDepth) }; } ``` ### Volume Analysis ```javascript function analyzeVolume(tick) { return { totalVolume: tick.volume, buyVolume: tick.btv, sellVolume: tick.atv, buyRatio: (tick.btv / (tick.btv + tick.atv) * 100).toFixed(2), vwap: tick.vwap }; } ``` --- ## Source file: `rest-api/historical-candle-data.md` **Canonical URL:** https://docs.arrow.trade/rest-api/historical-candle-data/ # Historical Data API The Historical Data API provides access to comprehensive historical market data, enabling backtesting, technical analysis, and chart visualization. The API supports various timeframes from 1-minute to monthly intervals and includes optional Open Interest data for derivatives. ## Endpoint Reference ### Get Historical Candle Data Retrieve historical OHLCV data for a specific instrument across configurable time intervals. !!! info "Endpoint Details" **Method:** `GET` **URL:** `/candle/:exchange/:token/:interval` **Header:** Required (`appID` + `token`) #### URL Parameters | Parameter | Type | Required | Description | |-----------|------|----------|-------------| | `exchange` | string | ✓ | Exchange identifier (nse, nfo, bse, etc.) | | `token` | string | ✓ | Unique instrument token (e.g., 3045 for SBIN) | | `interval` | string | ✓ | Candle interval (see supported intervals below) | #### Supported Intervals | Interval | Description | |----------|-------------| | `min` | 1 minute | | `3min` | 3 minutes | | `5min` | 5 minutes | | `10min` | 10 minutes | | `15min` | 15 minutes | | `30min` | 30 minutes | | `hour` | 1 hour | | `2hours` | 2 hours | | `3hours` | 3 hours | | `4hours` | 4 hours | | `day` | 1 day | | `week` | 1 week | | `month` | 1 month | #### Query Parameters | Parameter | Type | Required | Description | |-----------|------|----------|-------------| | `from` | string | ✓ | Start date/time in `yyyy-MM-ddTHH:mm:ss` format | | `to` | string | ✓ | End date/time in `yyyy-MM-ddTHH:mm:ss` format | | `oi` | integer | ✗ | Include Open Interest data (1=enabled, NFO only) | #### Request Headers | Header | Type | Required | Description | |--------|------|----------|-------------| | `appID` | string | ✓ | Your application identifier | | `token` | string | ✓ | User authentication token | --- ### NSE Historical Data Retrieve historical equity data from the National Stock Exchange. #### Request Example === "cURL" ```bash curl --location 'https://historical-api.arrow.trade/candle/nse/3045/day?from=2023-05-01T00:00:00&to=2023-06-29T20:00:00' \ --header 'appID: ' \ --header 'token: ' ``` #### Success Response ```json [ [ "2023-05-02T00:00:00+0530", 58000, 58085, 57315, 57530, 13667511 ], [ "2023-05-03T00:00:00+0530", 57065, 57500, 56900, 57050, 9699527 ], [ "2023-05-04T00:00:00+0530", 57020, 58080, 56850, 58000, 12533761 ], [ "2023-05-05T00:00:00+0530", 58020, 58825, 57504, 57650, 18163461 ], [ "2023-05-08T00:00:00+0530", 57765, 58760, 57735, 58360, 12990869 ], [ "2023-05-09T00:00:00+0530", 58500, 58645, 57210, 57350, 18959065 ], [ "2023-05-10T00:00:00+0530", 57500, 57500, 56325, 57220, 18561315 ] ] ``` #### Response Format (NSE) Each candle is represented as an array with the following structure: | Index | Field | Type | Description | |-------|-------|------|-------------| | 0 | Timestamp | string | Date/time in ISO 8601 format with timezone | | 1 | Open | integer | Opening price × 100 | | 2 | High | integer | Highest price × 100 | | 3 | Low | integer | Lowest price × 100 | | 4 | Close | integer | Closing price × 100 | | 5 | Volume | integer | Total traded volume | !!! note "Price Scaling for NSE" **IMPORTANT:** Open, High, Low, and Close values are multiplied by 100. To get actual prices, divide by 100. **Example:** ```json [ "2023-05-02T00:00:00+0530", 58000, 58085, 57315, 57530, 13667511 ] ``` ``` Actual Values: - Open: ₹580.00 (58000 ÷ 100) - High: ₹580.85 (58085 ÷ 100) - Low: ₹573.15 (57315 ÷ 100) - Close: ₹575.30 (57530 ÷ 100) - Volume: 13,667,511 shares ``` --- ### NFO Historical Data Retrieve historical derivatives data from the National Stock Exchange Futures & Options segment, with optional Open Interest information. #### Request Example ```bash curl --location 'https://historical-api.arrow.trade/candle/nfo/41927/day?from=2024-03-05T00:00:00&to=2024-03-08T00:00:00&oi=1' \ --header 'appID: ' \ --header 'token: ' ``` #### Success Response (with Open Interest) ```json [ [ "2024-03-05T00:00:00+0530", 26000, 41505, 21400, 33330, 2950530, 487485 ], [ "2024-03-06T00:00:00+0530", 28930, 60000, 25520, 40705, 13948425, 1778205 ], [ "2024-03-07T00:00:00+0530", 42400, 44800, 28810, 32915, 26726955, 3863355 ] ] ``` #### Response Format (NFO) Each candle is represented as an array with the following structure: | Index | Field | Type | Description | |-------|-------|------|-------------| | 0 | Timestamp | string | Date/time in ISO 8601 format with timezone | | 1 | Open | integer | Opening price × 100 | | 2 | High | integer | Highest price × 100 | | 3 | Low | integer | Lowest price × 100 | | 4 | Close | integer | Closing price × 100 | | 5 | Volume | integer | Total traded volume | | 6 | Open Interest | integer | Open Interest (only when `oi=1`) | !!! note "Price Scaling and Open Interest for NFO" **IMPORTANT:** Open, High, Low, and Close values are multiplied by 100. To get actual prices, divide by 100. Open Interest data is only included when the `oi=1` query parameter is added to the request. **Example:** ```json [ "2024-03-05T00:00:00+0530", 26000, 41505, 21400, 33330, 2950530, 487485 ] ``` ``` Actual Values: - Open: ₹260.00 (26000 ÷ 100) - High: ₹415.05 (41505 ÷ 100) - Low: ₹214.00 (21400 ÷ 100) - Close: ₹333.30 (33330 ÷ 100) - Volume: 2,950,530 contracts - Open Interest: 487,485 contracts ``` ## Error Handling The API follows standard HTTP status codes and returns structured error responses: ```json { "status": "error", "message": "Invalid date range", "code": "INVALID_DATE_RANGE" } ``` !!! warning "Common Issues" - **400 Bad Request:** Invalid parameters (exchange, token, interval, or date format) - **401 Unauthorized:** Invalid or expired token - **403 Forbidden:** Insufficient permissions - **404 Not Found:** Invalid instrument token or exchange - **422 Unprocessable Entity:** Date range exceeds maximum allowed period - **429 Too Many Requests:** Rate limit exceeded - **500 Internal Server Error:** Server-side processing error ## Integration Notes !!! tip "Best Practices" - Always divide price values (Open, High, Low, Close) by 100 to get actual prices - Use appropriate intervals based on your analysis needs (smaller intervals generate more data) - Request reasonable date ranges to avoid timeout or size limit issues - Cache historical data locally to minimize API calls for backtesting - Use the `oi=1` parameter only when Open Interest data is needed (NFO only) - Implement proper error handling for missing data or date range issues - Validate date formats match `yyyy-MM-ddTHH:mm:ss` specification !!! note "Data Availability" Historical data availability varies by instrument and exchange. Some instruments may have limited historical data, especially for newly listed securities or derivatives contracts. !!! warning "Date Range Limits" There may be maximum date range limits depending on the interval selected. For very large datasets, consider breaking requests into smaller time periods. --- ## Source file: `rest-api/holdings.md` **Canonical URL:** https://docs.arrow.trade/rest-api/holdings/ # Holdings API Retrieve comprehensive portfolio holdings data for authenticated users, including position details, profit/loss calculations, and collateral information. ## Overview The Holdings API provides complete visibility into a user's equity positions across multiple exchanges, enabling portfolio management and risk assessment capabilities. Holdings are consolidated by instrument, with support for multi-exchange trading symbols. ## Endpoint Reference ### Get User Holdings Retrieve all current holdings for the authenticated user account. !!! info "Endpoint Details" **Method:** `GET` **URL:** `/user/holdings` **Header:** Required (`appID` + `token`) #### Request Example ```bash curl --location 'https://edge.arrow.trade/user/holdings' \ --header 'appID: ' \ --header 'token: ' ``` ## Response Schema ### Success Response The API returns a structured response containing an array of holding objects. Each holding represents a consolidated position across multiple exchanges where the instrument is traded. ```json { "data": [ { "symbols": [ { "symbol": "GROWW", "tradingSymbol": "GROWW-EQ", "exchange": "NSE", "token": "759806", "isin": "INE0HOQ01053" }, { "symbol": "GROWW", "tradingSymbol": "GROWW", "exchange": "BSE", "token": "1543603", "isin": "INE0HOQ01053" } ], "avgPrice": "152.00", "qty": "2", "usedQty": "0", "t1Qty": "0", "depositoryQty": "0", "collateralQty": "0", "brokerCollateralQty": "2.00", "authorizedQty": "0", "unPledgedQty": "0", "nonPOAQty": "0", "haircut": "0.00", "effectiveQty": "2", "sellableQty": "2", "ltp": "", "pnl": "", "close": "" }, { "symbols": [ { "symbol": "CDSL", "tradingSymbol": "CDSL-EQ", "exchange": "NSE", "token": "21174", "isin": "INE736A01011" }, { "symbol": "CDSL", "tradingSymbol": "CDSL", "exchange": "BSE", "token": "1200232", "isin": "INE736A01011" } ], "avgPrice": "1346.00", "qty": "1", "usedQty": "0", "t1Qty": "1", "depositoryQty": "0", "collateralQty": "0", "brokerCollateralQty": "0.00", "authorizedQty": "0", "unPledgedQty": "0", "nonPOAQty": "0", "haircut": "0.00", "effectiveQty": "1", "sellableQty": "0", "ltp": "", "pnl": "", "close": "" }, { "symbols": [ { "symbol": "GOLDETF", "tradingSymbol": "GOLDETF-EQ", "exchange": "NSE", "token": "14286", "isin": "INF769K01JP9" }, { "symbol": "GOLDETF", "tradingSymbol": "GOLDETF", "exchange": "BSE", "token": "1542781", "isin": "INF769K01JP9" } ], "avgPrice": "107.00", "qty": "1", "usedQty": "0", "t1Qty": "0", "depositoryQty": "0", "collateralQty": "0", "brokerCollateralQty": "1.00", "authorizedQty": "0", "unPledgedQty": "0", "nonPOAQty": "0", "haircut": "0.00", "effectiveQty": "1", "sellableQty": "1", "ltp": "", "pnl": "", "close": "" }, { "symbols": [ { "symbol": "SBIN", "tradingSymbol": "SBIN-EQ", "exchange": "NSE", "token": "3045", "isin": "INE062A01020" }, { "symbol": "SBIN", "tradingSymbol": "SBIN", "exchange": "BSE", "token": "1499112", "isin": "INE062A01020" } ], "avgPrice": "882.00", "qty": "3", "usedQty": "0", "t1Qty": "0", "depositoryQty": "3", "collateralQty": "0", "brokerCollateralQty": "0.00", "authorizedQty": "0", "unPledgedQty": "0", "nonPOAQty": "0", "haircut": "0.00", "effectiveQty": "3", "sellableQty": "3", "ltp": "", "pnl": "", "close": "" }, { "symbols": [ { "symbol": "GOLDBEES", "tradingSymbol": "GOLDBEES-EQ", "exchange": "NSE", "token": "14428", "isin": "INF204KB17I5" }, { "symbol": "GOLDBEES", "tradingSymbol": "GOLDBEES", "exchange": "BSE", "token": "1589095", "isin": "INF204KB17I5" } ], "avgPrice": "91.00", "qty": "1", "usedQty": "0", "t1Qty": "0", "depositoryQty": "1", "collateralQty": "0", "brokerCollateralQty": "0.00", "authorizedQty": "0", "unPledgedQty": "0", "nonPOAQty": "0", "haircut": "0.00", "effectiveQty": "1", "sellableQty": "1", "ltp": "", "pnl": "", "close": "" }, { "symbols": [ { "symbol": "IDEA", "tradingSymbol": "IDEA-EQ", "exchange": "NSE", "token": "14366", "isin": "INE669E01016" }, { "symbol": "IDEA", "tradingSymbol": "IDEA", "exchange": "BSE", "token": "1531822", "isin": "INE669E01016" } ], "avgPrice": "7.00", "qty": "1190", "usedQty": "0", "t1Qty": "0", "depositoryQty": "412", "collateralQty": "0", "brokerCollateralQty": "778.00", "authorizedQty": "0", "unPledgedQty": "0", "nonPOAQty": "0", "haircut": "0.00", "effectiveQty": "1190", "sellableQty": "1190", "ltp": "", "pnl": "", "close": "" }, { "symbols": [ { "symbol": "NSDL", "tradingSymbol": "NSDL", "exchange": "BSE", "token": "1543467", "isin": "INE301O01023" } ], "avgPrice": "1284.00", "qty": "6", "usedQty": "0", "t1Qty": "0", "depositoryQty": "0", "collateralQty": "0", "brokerCollateralQty": "6.00", "authorizedQty": "0", "unPledgedQty": "0", "nonPOAQty": "0", "haircut": "0.00", "effectiveQty": "6", "sellableQty": "6", "ltp": "", "pnl": "", "close": "" }, { "symbols": [ { "symbol": "BSE", "tradingSymbol": "BSE-EQ", "exchange": "NSE", "token": "19585", "isin": "INE118H01025" } ], "avgPrice": "2367.00", "qty": "2", "usedQty": "0", "t1Qty": "0", "depositoryQty": "2", "collateralQty": "0", "brokerCollateralQty": "0.00", "authorizedQty": "0", "unPledgedQty": "0", "nonPOAQty": "0", "haircut": "0.00", "effectiveQty": "2", "sellableQty": "2", "ltp": "", "pnl": "", "close": "" }, { "symbols": [ { "symbol": "YESBANK", "tradingSymbol": "YESBANK-EQ", "exchange": "NSE", "token": "11915", "isin": "INE528G01035" }, { "symbol": "YESBANK", "tradingSymbol": "YESBANK", "exchange": "BSE", "token": "1531648", "isin": "INE528G01035" } ], "avgPrice": "19.00", "qty": "2", "usedQty": "0", "t1Qty": "0", "depositoryQty": "2", "collateralQty": "0", "brokerCollateralQty": "0.00", "authorizedQty": "0", "unPledgedQty": "0", "nonPOAQty": "0", "haircut": "0.00", "effectiveQty": "2", "sellableQty": "2", "ltp": "", "pnl": "", "close": "" } ], "status": "success" } ``` ## Integration Notes !!! tip "Best Practices" - Holdings are consolidated across exchanges - use the `symbols` array to identify specific exchange tokens - Handle empty market data fields (`ltp`, `pnl`, `close`) gracefully - Cache holdings data appropriately to minimize API calls - Implement proper error handling and retry logic - Use the `haircut` field for risk assessment calculations !!! note "Data Freshness" Holdings data is updated in real-time during market hours. Market data fields may be empty outside trading hours or during system maintenance. Position quantities and average prices are always available. --- ## Source file: `rest-api/margin-calculation.md` **Canonical URL:** https://docs.arrow.trade/rest-api/margin-calculation/ # Margin API Calculate margin requirements and trading charges for individual orders and basket orders, taking into account existing positions and open orders. ## Overview The Margin API enables precise calculation of capital requirements before order placement, helping traders assess available margin and understand the cost structure of their trades. The API supports both individual order margin calculations with detailed charge breakdowns and basket-level margin calculations for multi-instrument strategies. ## Endpoint Reference ### Calculate Order Margin Calculate margin requirements and detailed charges for a single order, considering existing positions and open orders. !!! info "Endpoint Details" **Method:** `POST` **URL:** `/margin/order` **Header:** Required (`appID` + `token`) #### Request Headers | Header | Type | Required | Description | |--------|------|----------|-------------| | `appID` | string | ✓ | Your application identifier | | `token` | string | ✓ | User authentication token | | `Content-Type` | string | ✓ | Must be `application/json` | #### Request Body | Field | Type | Required | Description | |-------|------|----------|-------------| | `exchange` | string | ✓ | Exchange identifier (NSE, BSE, NFO, etc.) | | `token` | string | ✓ | Unique instrument token | | `quantity` | string | ✓ | Order quantity | | `product` | string | ✓ | Product type (M=Margin, C=Cash, etc.) | | `price` | string | ✓ | Order price | | `transactionType` | string | ✓ | Transaction type (B=Buy, S=Sell) | | `order` | string | ✓ | Order type (LMT=Limit, MKT=Market, etc.) | #### Request Example ```bash curl --location 'https://edge.arrow.trade/margin/order' \ --header 'appID: ' \ --header 'token: ' \ --header 'Content-Type: application/json' \ --data '{ "exchange": "NSE", "symbol": "SBIN-EQ" "quantity": "100", "product": "C", "price": "800", "transactionType": "B", "order": "LMT" }' ``` #### Success Response ```json { "data": { "requiredMargin": 80000, "minimumCashRequired": 80000, "marginUsedAfterTrade": 0, "charge": { "brokerage": 0, "exchangeTxnFee": 2.376, "gst": { "cgst": 0, "igst": 0.45648000000000005, "sgst": 0, "total": 0.45648000000000005 }, "ipft": 0.08, "sebiCharges": 0.08, "stampDuty": 12, "total": 94.99248, "transactionTax": 80 } }, "status": "success" } ``` ## Integration Notes !!! tip "Best Practices" - Use Order Margin API for single orders to get detailed charge breakdowns - Use Basket Margin API for multi-leg strategies to benefit from portfolio-level margin optimization - Always validate margin availability before order placement - Cache margin calculations appropriately to minimize API calls - Implement proper error handling and retry logic - Consider margin requirements when designing trading strategies !!! note "Margin vs Charges" The `margin` field represents the capital blocked for the position, while `charge.total` represents the transaction costs. Both are deducted from available cash upon order execution. !!! warning "Product Types" Different product types (M=Margin, C=Cash) have different margin requirements and leverage. Ensure you understand the implications before trading. --- ## Source file: `rest-api/market-data.md` **Canonical URL:** https://docs.arrow.trade/rest-api/market-data/ # 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-241 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://ds.arrow.trade ``` ### Authentication Parameters Connect using your application credentials as query parameters: #### JavaScript === "Bash" ```bash # Test connection websocat "wss://ds.arrow.trade?appID=&token=" ``` === "Javascript" ```javascript const wsUrl = `wss://ds.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); }; ``` === "Python" ```python import websocket import struct ws_url = f"wss://ds.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: ```json { "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 | 93 bytes | Real-time | Comprehensive market view | | `full` | Complete data with market depth | 241 bytes | Real-time | Order book analysis | ## Subscription Examples ### Subscribe to Basic Price Data (LTPC) ```json { "code": "sub", "mode": "ltpc", "ltpc": [26009, 26000, 256265] } ``` ### Subscribe to High-Frequency Price Feed (LTP) ```json { "code": "sub", "mode": "ltp", "ltp": [26009, 26000] } ``` ### Subscribe to Complete Market Data (Quote) ```json { "code": "sub", "mode": "quote", "quote": [26009, 26000, 256265] } ``` ### Subscribe to Full Market Depth ```json { "code": "sub", "mode": "full", "full": [26009, 26000] } ``` ### Unsubscribe from Instruments ```json { "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 (Quote Mode - 93 bytes) 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-77 | Open Interest | `int64` | Current open interest | | 77-85 | OI Day High | `int64` | Highest OI during session | | 85-93 | OI Day Low | `int64` | Lowest OI during session | ### Full Market Data Structure (241 bytes) Complete market data with 5-level order book depth. | Bytes | Field | Type | Description | |-------|-------|------|-------------| | 0-93 | Base Market Data | `bytes` | Same structure as Quote mode | | 93-97 | Lower Circuit | `int32` | Lower price limit | | 97-101 | Upper Circuit | `int32` | Upper price limit | | 101-241 | Market Depth | `bytes` | Order book data (5 bids + 5 asks) | ## 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, starting at offset 101) Each depth level contains 14 bytes: - **Quantity**: 8 bytes (`int64`) - **Price**: 4 bytes (`int32`) - **Orders**: 2 bytes (`int16`) | Position | Level | Data | Structure | |----------|-------|------|-----------| | 101-115 | Bid 1 | Best buy orders | Quantity (8) + Price (4) + Orders (2) | | 115-129 | Bid 2 | Second best buy | Quantity (8) + Price (4) + Orders (2) | | 129-143 | Bid 3 | Third best buy | Quantity (8) + Price (4) + Orders (2) | | 143-157 | Bid 4 | Fourth best buy | Quantity (8) + Price (4) + Orders (2) | | 157-171 | Bid 5 | Fifth best buy | Quantity (8) + Price (4) + Orders (2) | | 171-185 | Ask 1 | Best sell orders | Quantity (8) + Price (4) + Orders (2) | | 185-199 | Ask 2 | Second best sell | Quantity (8) + Price (4) + Orders (2) | | 199-213 | Ask 3 | Third best sell | Quantity (8) + Price (4) + Orders (2) | | 213-227 | Ask 4 | Fourth best sell | Quantity (8) + Price (4) + Orders (2) | | 227-241 | Ask 5 | Fifth best sell | Quantity (8) + Price (4) + Orders (2) | ## Implementation Examples ### Complete WebSocket Client #### JavaScript === "Javascript" ```javascript class MarketDataClient { constructor(appID, token) { this.wsUrl = `wss://ds.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 === 93) { return this.parseQuote(buffer); } else if (buffer.length === 241) { return this.parseFull(buffer); } } parseLTPC(data) { const tick = {}; tick.token = this.bigEndianToInt(data.slice(0, 4)); tick.ltp = this.bigEndianToInt(data.slice(4, 8)); tick.close = this.bigEndianToInt(data.slice(13, 17)); tick.netChange = Math.round( (((tick.ltp - tick.close) / tick.close) * 100 + Number.EPSILON) * 100 ) / 100 || 0; if (tick.ltp > tick.close) { tick.changeFlag = 43; // ascii code for + } else if (tick.ltp < tick.close) { tick.changeFlag = 45; // ascii code for - } else { tick.changeFlag = 32; // no change } return tick; } parseQuote(data) { const tick = this.parseLTPC(data); tick.ltq = this.bigEndianToInt(data.slice(13, 17)); tick.avgPrice = this.bigEndianToInt(data.slice(17, 21)); tick.totalBuyQuantity = this.bigEndianToInt(data.slice(21, 29)); tick.totalSellQuantity = this.bigEndianToInt(data.slice(29, 37)); tick.open = this.bigEndianToInt(data.slice(37, 41)); tick.high = this.bigEndianToInt(data.slice(41, 45)); tick.close = this.bigEndianToInt(data.slice(45, 49)); tick.low = this.bigEndianToInt(data.slice(49, 53)); tick.volume = this.bigEndianToInt(data.slice(53, 61)); tick.ltt = this.bigEndianToInt(data.slice(61, 65)); tick.time = this.bigEndianToInt(data.slice(65, 69)); tick.oi = this.bigEndianToInt(data.slice(69, 77)); tick.oiDayHigh = this.bigEndianToInt(data.slice(77, 85)); tick.oiDayLow = this.bigEndianToInt(data.slice(85, 93)); // Recalculate net change with correct close price tick.netChange = Math.round( (((tick.ltp - tick.close) / tick.close) * 100 + Number.EPSILON) * 100 ) / 100 || 0; if (tick.ltp > tick.close) { tick.changeFlag = 43; // ascii code for + } else if (tick.ltp < tick.close) { tick.changeFlag = 45; // ascii code for - } else { tick.changeFlag = 32; // no change } return tick; } parseFull(data) { const tick = this.parseQuote(data); tick.lowerLimit = this.bigEndianToInt(data.slice(93, 97)); tick.upperLimit = this.bigEndianToInt(data.slice(97, 101)); const bids = []; const asks = []; for (let i = 0; i < 10; i++) { const offset = 101 + i * 14; const quantity = this.bigEndianToInt(data.slice(offset, offset + 8)); const price = this.bigEndianToInt(data.slice(offset + 8, offset + 12)); const orders = this.bigEndianToInt(data.slice(offset + 12, offset + 14)); if (i >= 5) { asks.push({ price, quantity, orders }); } else { bids.push({ price, quantity, orders }); } } tick.bids = bids; tick.asks = asks; return tick; } bigEndianToInt(buffer) { let result = 0; for (let i = 0; i < buffer.length; i++) { result = (result << 8) | buffer[i]; } return result; } } // 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}%`); }); ``` === "Python" ```python import websocket import json import struct from threading import Thread class MarketDataClient: def __init__(self, app_id, token): self.ws_url = f"wss://ds.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) == 93: return self.parse_quote(data) elif len(data) == 241: return self.parse_full(data) def parse_ltpc(self, data): tick = {} tick['token'] = struct.unpack('>I', data[0:4])[0] tick['ltp'] = struct.unpack('>I', data[4:8])[0] tick['close'] = struct.unpack('>I', data[13:17])[0] if tick['close'] != 0: tick['net_change'] = round( (((tick['ltp'] - tick['close']) / tick['close']) * 100), 2 ) else: tick['net_change'] = 0 return tick def parse_quote(self, data): tick = self.parse_ltpc(data) tick['ltq'] = struct.unpack('>I', data[13:17])[0] tick['avg_price'] = struct.unpack('>I', data[17:21])[0] tick['total_buy_quantity'] = struct.unpack('>Q', data[21:29])[0] tick['total_sell_quantity'] = struct.unpack('>Q', data[29:37])[0] tick['open'] = struct.unpack('>I', data[37:41])[0] tick['high'] = struct.unpack('>I', data[41:45])[0] tick['close'] = struct.unpack('>I', data[45:49])[0] tick['low'] = struct.unpack('>I', data[49:53])[0] tick['volume'] = struct.unpack('>Q', data[53:61])[0] tick['ltt'] = struct.unpack('>I', data[61:65])[0] tick['time'] = struct.unpack('>I', data[65:69])[0] tick['oi'] = struct.unpack('>Q', data[69:77])[0] tick['oi_day_high'] = struct.unpack('>Q', data[77:85])[0] tick['oi_day_low'] = struct.unpack('>Q', data[85:93])[0] # Recalculate net change with correct close price if tick['close'] != 0: tick['net_change'] = round( (((tick['ltp'] - tick['close']) / tick['close']) * 100), 2 ) else: tick['net_change'] = 0 return tick def parse_full(self, data): tick = self.parse_quote(data) tick['lower_limit'] = struct.unpack('>I', data[93:97])[0] tick['upper_limit'] = struct.unpack('>I', data[97:101])[0] bids = [] asks = [] for i in range(10): offset = 101 + i * 14 quantity = struct.unpack('>Q', data[offset:offset+8])[0] price = struct.unpack('>I', data[offset+8:offset+12])[0] orders = struct.unpack('>H', data[offset+12:offset+14])[0] if i >= 5: asks.append({'price': price, 'quantity': quantity, 'orders': orders}) else: bids.append({'price': price, 'quantity': quantity, 'orders': orders}) tick['bids'] = bids tick['asks'] = asks return tick # 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) ``` ## Error Handling and Reconnection ### Connection Management ```javascript 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 ## Market Data Applications ### Real-Time Portfolio Monitoring ```javascript // 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 ```javascript // 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 ```javascript // 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 }); } } }; } ``` --- ## Source file: `rest-api/mktData-Readme.md` **Canonical URL:** https://docs.arrow.trade/rest-api/mktData-Readme/ # MarketDataClient Documentation The `MarketDataClient` is a WebSocket client designed to connect to a market data streaming service, allowing you to subscribe to real-time market data for various instruments. This documentation provides an overview of how to use the `MarketDataClient` class, including setup, callbacks, methods, and FlatBuffer message schemas. --- 1. [Introduction](#introduction) 2. [Message Types and Flows](#message-types-and-flows) 3. [Initialization](#initialization) 4. [Connecting to the WebSocket](#connecting-to-the-websocket) 5. [Setting Up Callbacks](#setting-up-callbacks) 6. [Subscription Types](#subscription-types) 7. [Market Data Payload Examples](#market-data-payload-examples) 8. [Example Usage](#example-usage) 9. [Limitations](#limitations) 10. [FlatBuffer Message Schema](#flatbuffer-message-schema) - [Request Schema](#request-schema-requestwrapperfbs) - [Response Schema](#response-schema-responsewrapperfbs) 11. [Conclusion](#conclusion) --- ## Introduction The `MarketDataClient` allows users to connect to a market data WebSocket, subscribe to various instruments, and receive real-time tick data. It supports multiple subscription types and leverages FlatBuffers for efficient message processing. --- ## Message Types and Flows 1. **Login Flow** - Client sends `LoginRequest` upon WebSocket connection. - Server responds with `LoginResponse`. 2. **Subscription Flow** - Client sends `SubscribeRequest`. - Server responds with `SubscriptionResponse`. - Server sends `MarketDataMessage` continuously. 3. **Unsubscription Flow** - Client sends `UnsubscribeRequest`. - Server responds with `UnsubscriptionResponse`. --- ## Initialization To start using the `MarketDataClient`, initialize the client: ```python from brokerage_client.data import MarketDataClient # Initialize the MarketDataClient instance client = MarketDataClient() ``` --- ## Connecting to the WebSocket Provide your `UCC` (User Client Code) and `AUTH_TOKEN` to connect: ```python AUTH_TOKEN = "your_auth_token_here" UCC = "your_ucc_here" client.connect(UCC, AUTH_TOKEN) ``` If you want the WebSocket connection to run in a separate thread, pass ```threaded=True```: ```python AUTH_TOKEN = "your_auth_token_here" UCC = "your_ucc_here" client.connect(UCC, AUTH_TOKEN, threaded=True) ``` When using ```threaded=True```, the WebSocket connection runs in the background. You must ensure that the main thread remains active; otherwise, the program may exit before processing data. Example: ```python client.connect(UCC, AUTH_TOKEN, threaded=True) try: while True: # Keeps the main thread alive pass except KeyboardInterrupt: print("KeyboardInterrupt received, closing connection...") client.close() ``` --- ## Setting Up Callbacks The `MarketDataClient` provides several callbacks that you can assign to handle different events: - **`on_connect`**: Triggered when the WebSocket connection is successfully established. - **`on_ticks`**: Triggered when market data (ticks) are received. - **`on_close`**: Triggered when the WebSocket connection is closed. Example: ```python def on_connect(ws): print("Connection established.") subscription_list = [ {"symbol": "RELIANCE.NSE.EQ", "sub_type": "ltp"}, {"symbol": "NIFTY25FEBFUT", "sub_type": "mtick", "expiry": 20250227}, {"symbol": "NIFTY25FEBFUT", "sub_type": "utick", "expiry": 20250227}, ] client.subscribe("sub1", subscription_list) def on_ticks(ws, ticks): for i, tick in enumerate(ticks): print(f"\nTick {i + 1}:") for key, value in tick.items(): print(f" {key}: {value}") def on_close(ws): print(f"WebSocket Connection closed") # Initialize the MarketDataClient instance client = MarketDataClient() # Assign callback functions client.init(on_connect=on_connect,on_ticks=on_ticks,on_close=on_close) ``` --- ## Subscription Types | `sub_type` | Description | |-------------|-------------| | `ltp` | Last Traded Price data | | `mtick` | Market depth data | | `utick` | Options chain data (Futures symbol required) | --- ## Market Data Payload Examples The tick structure represents real-time market data updates and is passed to the on_ticks callback. Each tick contains key market attributes such as price, volume, and market depth levels. Before receiving market data for the requested symbols, you will first receive the DPR (Daily Price Range) values for those symbols in ticks . ### Response Example - DPR Values ```json [ { "response_type": "Subscription DPR Values", "request_id": "sub1", "symbol": "RELIANCE.NSE.EQ", "type": "DprPayload", "dpr_low": 117480.25, "dpr_high": 143580.78 }, { "response_type": "Subscription DPR Values", "request_id": "sub1", "symbol": "NIFTY25FEBFUT", "type": "DprPayload", "dpr_low": 2118090.21, "dpr_high": 2588775.34 }, { "response_type": "Subscription DPR Values", "request_id": "sub1", "symbol": "NIFTY25FEBFUT", "type": "DprPayload", "dpr_low": 2118090.45, "dpr_high": 2588775.56 } ] ``` ### `UtickPayload` Example ```json { "response_type": "Market Data", "symbol": "NIFTY25FEBFUT", "seq_no": 1, "sub_type": "utick", "sending_time": 1740462338221772895, "type": "UtickPayload", "iv": 0.12, "spot_price": 2352210.00, "options": [ { "strike_price": 2330000.00, "option_type": 67, "ltp": 61440.00, "oi": 220425.00, "volume": 133800 }, { "strike_price": 2335000.00, "option_type": 67, "ltp": 58515.00, "oi": 21075.00, "volume": 22875 }, { "strike_price": 2340000.00, "option_type": 67, "ltp": 55785.00, "oi": 177225.00, "volume": 133275 }, { "strike_price": 2345000.00, "option_type": 67, "ltp": 52810.00, "oi": 56625.00, "volume": 13950 }, { "strike_price": 2350000.00, "option_type": 67, "ltp": 50205.00, "oi": 890175.00, "volume": 703575 } ....................................... ] } ``` ### `MtickPayload` Example ```json { "response_type": "Market Data", "symbol": "NIFTY25FEBFUT", "seq_no": 1, "sub_type": "mtick", "sending_time": 1740462338221772895, "type": "MtickPayload", "ltp": 2352210.00, "volume": 75, "levels": [ { "bid": 2352260.00, "bid_qty": 300, "ask": 2352995.00, "ask_qty": 600 }, { "bid": 2352200.00, "bid_qty": 75, "ask": 2353000.00, "ask_qty": 825 }, { "bid": 2352195.00, "bid_qty": 225, "ask": 2353385.00, "ask_qty": 75 }, { "bid": 2352190.00, "bid_qty": 600, "ask": 2353495.00, "ask_qty": 75 }, { "bid": 2352185.00, "bid_qty": 375, "ask": 2353515.00, "ask_qty": 750 } ] } ``` ### `LtpPayload` ```json { "response_type": "Market Data", "symbol": "RELIANCE.NSE.EQ", "seq_no": 1, "sub_type": "ltp", "sending_time": 1740462338221772895, "type": "LtpPayload", "ltp": 129315.00, "volume": 5 } ``` ### `DprPayload` Incase if the DPR range for any symbol changes in middle of the day ```json { "response_type": "Dpr Update", "symbol": "NIFTY25FEBFUT", "type": "DprUpdate", "dpr_low": 2119887.00, "dpr_high": 2679554.00 } ``` ### Full Response Example - Market Data ```json [ { "response_type": "Market Data", "symbol": "RELIANCE.NSE.EQ", "seq_no": 1, "sub_type": "ltp", "sending_time": 1740462338221772895, "type": "LtpPayload", "ltp": 129315.00, "ltq": 5 }, { "response_type": "Market Data", "symbol": "NIFTY25FEBFUT", "seq_no": 1, "sub_type": "mtick", "sending_time": 1740462338221772895, "type": "MtickPayload", "ltp": 2352210.00, "ltq": 75, "levels": [ { "bid": 2352260.00, "bid_qty": 300, "ask": 2352995.00, "ask_qty": 600 }, { "bid": 2352200.00, "bid_qty": 75, "ask": 2353000.00, "ask_qty": 825 }, { "bid": 2352195.00, "bid_qty": 225, "ask": 2353385.00, "ask_qty": 75 }, { "bid": 2352190.00, "bid_qty": 600, "ask": 2353495.00, "ask_qty": 75 }, { "bid": 2352185.00, "bid_qty": 375, "ask": 2353515.00, "ask_qty": 750 } ] }, { "response_type": "Market Data", "symbol": "NIFTY25FEBFUT", "seq_no": 1, "sub_type": "utick", "sending_time": 1740462338221772895, "type": "UtickPayload", "iv": 0.12, "spot_price": 2352210.00, "options": [ { "strike_price": 2330000.00, "option_type": 67, "ltp": 61440.00, "oi": 220425.00, "volume": 133800 }, { "strike_price": 2335000.00, "option_type": 67, "ltp": 58515.00, "oi": 21075.00, "volume": 22875 }, { "strike_price": 2340000.00, "option_type": 67, "ltp": 55785.00, "oi": 177225.00, "volume": 133275 }, { "strike_price": 2345000.00, "option_type": 67, "ltp": 52810.00, "oi": 56625.00, "volume": 13950 }, { "strike_price": 2350000.00, "option_type": 67, "ltp": 50205.00, "oi": 890175.00, "volume": 703575 }, ........................................................ ] } ] ``` --- ## Limitations 1. A user can establish up to 10 WebSocket connections. 2. Each subscription request can include up to 200 symbols, but a user can subscribe to a maximum of 2000 symbols by making multiple subscription requests. --- ## FlatBuffer Message Schema ### Request Schema ([RequestWrapper.fbs](./RequestWrapper.fbs)) ### Response Schema ([ResponseWrapper.fbs](./ResponseWrapper.fbs)) --- ## Example Usage ```python from brokerage_client.data import MarketDataClient AUTH_TOKEN = "your_auth_token_here" UCC = "your_ucc_here" def on_connect(ws): print("Connection established.") subscription_list = [ {"symbol": "RELIANCE.NSE.EQ", "sub_type": "ltp"}, {"symbol": "NIFTY25FEBFUT", "sub_type": "mtick", "expiry": 20250227}, {"symbol": "NIFTY25FEBFUT", "sub_type": "utick", "expiry": 20250227}, ] client.subscribe("sub1", subscription_list) def on_ticks(ws, ticks): for i, tick in enumerate(ticks): print(f"\nTick {i + 1}:") for key, value in tick.items(): print(f" {key}: {value}") def on_close(ws): print(f"WebSocket Connection closed") # Initialize the MarketDataClient instance client = MarketDataClient() client.init(on_connect=on_connect,on_ticks=on_ticks,on_close=on_close) # Connect to the market data WebSocket client.connect(UCC, AUTH_TOKEN) ``` ## Conclusion The `MarketDataClient` provides a robust way to connect to a market data streaming service using FlatBuffer serialization. By following this documentation, you can establish connections, subscribe to market data, and process the incoming data efficiently. --- ## Source file: `rest-api/order-data.md` **Canonical URL:** https://docs.arrow.trade/rest-api/order-data/ # 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 ``` ## 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: ```json { "updateType": "ORDER_UPDATE", "userID": "AJ0001", "accountID": "AJ0001", "exchange": "NFO", "symbol": "NIFTY27JAN26C25300", "id": "26012301000023", "price": "0.05", "quantity": "65", "product": "M", "orderStatus": "PENDING", "reportType": "PendingNew", "transactionType": "B", "order": "LMT", "cumulativeFillQty": "0", "fillShares": "0", "averagePrice": "0", "exchangeOrderID": "0", "cancelQuantity": "0", "orderTriggerPrice": "0", "validity": "DAY", "pricePrecision": "2", "tickSize": "0.05", "lotSize": "65", "token": "58695", "orderTime": "2026-01-23T13:40:53", "orderSource": "WEB", "leavesQuantity": "65" } ``` ### Field Reference | Field | Type | Description | Example | |-------|------|-------------|---------| | `updateType` | string | Message type (always "ORDER_UPDATE") | `"ORDER_UPDATE"` | | `userID` | string | User identifier | `"AJ0001"` | | `accountID` | string | Trading account identifier | `"AJ0001"` | | `exchange` | string | Exchange code | `"NFO"`, `"NSE"`, `"BSE"` | | `symbol` | string | Trading symbol | `"NIFTY27JAN26C25300"` | | `id` | string | Unique order identifier | `"26012301000023"` | | `price` | string | Order price | `"0.05"` | | `quantity` | string | Order quantity | `"65"` | | `product` | string | Product type | `"M"` (Margin), `"C"` (Cash) | | `orderStatus` | string | Current order status | See Order Status table | | `reportType` | string | Type of update report | See Report Types table | | `transactionType` | string | Buy or Sell | `"B"` (Buy), `"S"` (Sell) | | `order` | string | Order type | `"LMT"`, `"MKT"`, `"SL"`, `"SL-M"` | | `cumulativeFillQty` | string | Total filled quantity | `"0"` | | `fillShares` | string | Shares filled in this update | `"0"` | | `averagePrice` | string | Average execution price | `"0"` | | `exchangeOrderID` | string | Exchange-assigned order ID | `"0"` | | `cancelQuantity` | string | Cancelled quantity | `"0"` | | `orderTriggerPrice` | string | Trigger price for stop orders | `"0"` | | `validity` | string | Order validity | `"DAY"`, `"IOC"` | | `pricePrecision` | string | Decimal precision for price | `"2"` | | `tickSize` | string | Minimum price increment | `"0.05"` | | `lotSize` | string | Trading lot size | `"65"` | | `token` | string | Instrument token | `"58695"` | | `orderTime` | string | Order timestamp (ISO format) | `"2026-01-23T13:40:53"` | | `orderSource` | string | Order entry source | `"WEB"`, `"API"`, `"MOBILE"` | | `leavesQuantity` | string | Remaining unfilled quantity | `"65"` | ### Report Types The `reportType` field indicates the type of order update: | Report Type | Description | Example Scenario | |-------------|-------------|------------------| | `PendingNew` | Order submitted, awaiting exchange | New order placed | | `New` | Order accepted by exchange | Order becomes active | | `Replaced` | Order modified successfully | Price or quantity changed | | `Canceled` | Order cancelled | User cancellation | | `Rejected` | Order rejected by exchange | Insufficient funds | | `PartiallyFilled` | Partial execution | Some quantity filled | | `Filled` | Complete execution | Order fully filled | | `Expired` | Order expired | End of trading day | | `Triggered` | Stop order triggered | Stop price reached | ### 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 | ### Order Type Codes | Code | Full Name | Description | |------|-----------|-------------| | `LMT` | Limit | Order at specified price or better | | `MKT` | Market | Order at current market price | | `SL` | Stop Loss | Stop order with limit price | | `SL-M` | Stop Loss Market | Stop order at market price | ### Transaction Type Codes | Code | Full Name | |------|-----------| | `B` | Buy | | `S` | Sell | ### Product Type Codes | Code | Full Name | Description | |------|-----------|-------------| | `M` | Margin | Intraday trading | | `C` | Cash | Delivery trading | | `I` | Intraday | Intraday trading | ## Troubleshooting | Issue | Cause | Solution | |-------|--------|----------| | **Connection Fails** | Invalid appID/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 | | **Wrong Field Names** | Using old documentation | Use `id` instead of `orderId`, `symbol` instead of `tradingSymbol` | | **String vs Number** | Field type mismatch | Parse string fields to numbers: `parseInt()`, `parseFloat()` | --- ## Source file: `rest-api/order-margin.md` **Canonical URL:** https://docs.arrow.trade/rest-api/order-margin/ # Margin API Calculate margin requirements and trading charges for individual orders and basket positions, enabling accurate cost estimation and risk management. ## Overview The Margin API provides precise margin calculations and detailed charge breakdowns for trading positions, supporting both single instrument analysis and complex basket strategies with real-time risk assessment. ## Endpoint Reference ### Calculate Order Margin Calculate margin requirements and charges for a specific order, considering existing positions and open orders. !!! info "Endpoint Details" **Method:** `POST` **URL:** `/margin/order` **Header:** Required (`appID` + `token`) #### Request Headers | Header | Type | Required | Description | |--------|------|----------|-------------| | `appID` | string | ✓ | Your application identifier | | `token` | string | ✓ | User authentication token | | `Content-Type` | string | ✓ | Must be `application/json` | #### Request Body | Field | Type | Required | Description | |-------|------|----------|-------------| | `exchange` | string | ✓ | Exchange identifier (NSE, BSE, NFO, etc.) | | `token` | string | ✓ | Instrument token | | `quantity` | string | ✓ | Order quantity | | `product` | string | ✓ | Product type (`M` = Margin, `C` = Cash, etc.) | | `price` | string | ✓ | Order price | | `transactionType` | string | ✓ | Transaction type (`B` = Buy, `S` = Sell) | | `order` | string | ✓ | Order type (`LMT` = Limit, `MKT` = Market) | #### Request Example === "CURL" ```bash curl --location 'https://edge.arrow.trade/margin/order' \ --header 'appID: ' \ --header 'token: ' \ --header 'Content-Type: application/json' \ --data '{ "exchange": "NFO", "token": "72734", "quantity": "25", "product": "M", "price": "905", "transactionType": "S", "order": "LMT" }' ``` === "JavaScript" ```javascript const orderMargin = { exchange: "NFO", token: "72734", quantity: "25", product: "M", price: "905", transactionType: "S", order: "LMT" }; const response = await fetch('https://edge.arrow.trade/margin/order', { method: 'POST', headers: { 'appID': '', 'token': '', 'Content-Type': 'application/json' }, body: JSON.stringify(orderMargin) }); const margin = await response.json(); ``` === "Python" ```python import requests headers = { 'appID': '', 'token': '', 'Content-Type': 'application/json' } payload = { "exchange": "NFO", "token": "72734", "quantity": "25", "product": "M", "price": "905", "transactionType": "S", "order": "LMT" } response = requests.post( 'https://edge.arrow.trade/margin/order', headers=headers, json=payload ) margin = response.json() ``` ### Calculate Basket Margin Calculate consolidated margin requirements for multiple instruments, optimizing margin efficiency through position netting. !!! info "Endpoint Details" **Method:** `POST` **URL:** `/margin/basket` **Header:** Required (`appID` + `token`) !!! note "Basket vs Order Margin" Basket margin calculations use different RMS logic that may show slight variations from single order margins. For individual instruments, use the Order Margin API for precise calculations including detailed charges. #### Request Body Expects an array of order objects with the same structure as Order Margin requests. #### Request Example === "cURL" ```bash curl --location 'https://edge.arrow.trade/margin/basket' \ --header 'appID: ' \ --header 'token: ' \ --header 'Content-Type: application/json' \ --data '[ { "exchange": "NFO", "token": "51641", "quantity": "900", "price": "133.5", "triggerPrice": "0", "product": "M", "transactionType": "B", "order": "LMT" }, { "exchange": "NFO", "token": "46283", "quantity": "900", "price": "132", "triggerPrice": "0", "product": "M", "transactionType": "S", "order": "LMT" } ]' ``` === "JavaScript" ```javascript const basketOrders = [ { exchange: "NFO", token: "51641", quantity: "900", price: "133.5", triggerPrice: "0", product: "M", transactionType: "B", order: "LMT" }, { exchange: "NFO", token: "46283", quantity: "900", price: "132", triggerPrice: "0", product: "M", transactionType: "S", order: "LMT" } ]; const response = await fetch('https://edge.arrow.trade/margin/basket', { method: 'POST', headers: { 'appID': '', 'token': '', 'Content-Type': 'application/json' }, body: JSON.stringify(basketOrders) }); const basketMargin = await response.json(); ``` ## Response Schema ### Order Margin Response ```json { "data": { "cash": "276006.43", "charge": { "brokerage": 20, "sebiCharges": 0.00309225, "exchangeTxnFee": 1.546125, "stampDuty": 0.0927675, "ipft": 0.015461250000000001, "transactionTax": 0, "gst": { "cgst": 0, "sgst": 0, "igst": 3.8816421300000004, "total": 3.8816421300000004 }, "total": 25.523626880000002 }, "margin": "3125.00", "marginUsed": "3125.00" }, "status": "success" } ``` #### Response Fields | Field | Type | Description | |-------|------|-------------| | `cash` | string | Available cash balance | | `margin` | string | Required margin amount | | `marginUsed` | string | Total margin that will be utilized | | `charge` | object | Detailed charge breakdown | #### Charge Breakdown | Field | Type | Description | |-------|------|-------------| | `brokerage` | number | Brokerage fees | | `sebiCharges` | number | SEBI regulatory charges | | `exchangeTxnFee` | number | Exchange transaction fees | | `stampDuty` | number | Government stamp duty | | `ipft` | number | Investor Protection Fund Tax | | `transactionTax` | number | Securities Transaction Tax (STT) | | `gst` | object | Goods and Services Tax breakdown | | `total` | number | Total charges amount | #### GST Breakdown | Field | Type | Description | |-------|------|-------------| | `cgst` | number | Central GST | | `sgst` | number | State GST | | `igst` | number | Integrated GST | | `total` | number | Total GST amount | ### Basket Margin Response ```json { "data": { "marginUsed": "343086.95", "marginUsedAfterTrade": "224261.95" }, "status": "success" } ``` #### Basket Response Fields | Field | Type | Description | |-------|------|-------------| | `marginUsed` | string | Current margin utilization | | `marginUsedAfterTrade` | string | Projected margin after basket execution | ## Margin Calculation Examples ### Options Selling Strategy !!! example "Short Call Analysis" **Instrument:** NFO Token 72734 **Strategy:** Sell 25 lots at ₹905 **Required Margin:** ₹3,125.00 **Total Charges:** ₹25.52 **Charge Distribution:** - Brokerage: ₹20.00 (78.4%) - GST: ₹3.88 (15.2%) - Exchange Fees: ₹1.55 (6.1%) - Regulatory: ₹0.09 (0.3%) ### Spread Strategy !!! example "Bull Call Spread" **Legs:** Long + Short Call Options **Individual Margins:** ₹343,086.95 **Net Margin (After Netting):** ₹224,261.95 **Margin Efficiency:** 34.7% reduction ## Error Handling Standard HTTP status codes with structured error responses: ```json { "status": "error", "message": "Invalid instrument token", "code": "INVALID_TOKEN" } ``` !!! warning "Common Issues" - **400 Bad Request:** Invalid order parameters or missing fields - **401 Unauthorized:** Invalid or expired authentication token - **403 Forbidden:** Insufficient trading permissions - **429 Too Many Requests:** Rate limit exceeded - **500 Internal Server Error:** Margin calculation service error ## Integration Notes !!! tip "Best Practices" - Cache margin calculations for identical order parameters - Use basket margin for multi-leg strategies to get accurate netting - Implement proper validation before order submission - Monitor margin utilization to prevent order rejections !!! note "Calculation Accuracy" Margin calculations are updated in real-time and reflect current market conditions, volatility, and regulatory requirements. Always recalculate margins before order execution to ensure sufficient funds. --- ## Source file: `rest-api/orders.md` **Canonical URL:** https://docs.arrow.trade/rest-api/orders/ # Orders API The Orders API provides enterprise-grade trading capabilities with real-time order management, portfolio tracking, and detailed execution analytics. Built for algorithmic trading, portfolio management platforms, and advanced trading applications. ## Base Configuration ### Authentication & Headers All API requests require the following authentication headers: | Header | Type | Required | Description | |--------|------|----------|-------------| | `appID` | string | ✓ | Your application identifier | | `token` | string | ✓ | User authentication token | ### Base URL ``` https://edge.arrow.trade ``` ## Endpoint Reference | Method | Endpoint | Description | |--------|----------|-------------| | `POST` | `/order/:variety` | Place new trading orders | | `PATCH` | `/order/:variety/:order_id` | Modify existing open orders | | `DELETE` | `/order/:variety/:order_id` | Cancel pending orders | | `GET` | `/order/:order_id` | Retrieve order details and history | | `GET` | `/user/orders` | Get complete order book | | `GET` | `/user/trades` | Access executed trades | ## Trading Parameters ### Order Varieties | Variety | Description | Risk Management | |---------|-------------|-----------------| | `regular` | Standard order execution | Basic order controls | ### Exchange Support | Exchange | Code | Market Segment | Regular Trading Hours | |----------|------|----------------|---------------| | **NSE** | `NSE` | Equity | 9:15 AM - 3:30 PM | | **NFO** | `NFO` | Futures & Options | 9:15 AM - 3:30 PM | | **BSE** | `BSE` | Equity | 9:15 AM - 3:30 PM | | **BFO** | `BFO` | Futures & Options | 9:15 AM - 3:30 PM | ### Order Types | Type | Code | Description | Use Case | |------|------|-------------|----------| | **Market Order** | `MKT` | Execute at best available price | Immediate execution required | | **Limit Order** | `LMT` | Execute at specified price or better | Price control priority | | **Stop Loss Limit** | `SL-LMT` | Limit order activated at trigger | Risk management with price control | | **Stop Loss Market** | `SL-MKT` | Market order activated at trigger | Risk management with speed priority | ### Product Categories | Product | Code | Description | Settlement | |---------|------|-------------|------------| | **Intraday** | `I` | Same-day position closure | Auto-squared off at 3:15 PM| | **Delivery** | `C` | Equity delivery orders | T+1 settlement | | **Normal** | `M` | F&O orders | Standard margin | ### Order Validity | Validity | Code | Description | Expiry | |----------|------|-------------|--------| | **Day Order** | `DAY` | Valid until market close | End of trading session | | **Immediate or Cancel** | `IOC` | Execute immediately or cancel | Immediate | ## Order Execution ### Place Regular Order Execute standard trading orders with full parameter control. !!! info "Endpoint Details" **Method:** `POST` **URL:** `/order/regular` **Header:** Required (`appID` + `token`) #### Request Parameters | Parameter | Type | Required | Description | |-----------|------|----------|-------------| | `exchange` | string | ✓ | Exchange identifier (NSE, NFO, BSE, etc.) | | `symbol` | string | ✓ | Trading symbol | | `quantity` | string | ✓ | Order quantity | | `transactionType` | string | ✓ | Transaction type (`B` = Buy, `S` = Sell) | | `order` | string | ✓ | Order type (LMT, MKT, SL-LMT, SL-MKT) | | `product` | string | ✓ | Product type (`I`, `C`, `M`) | | `price` | string | ✓ | Order price (use "0" for market orders) | | `validity` | string | ✓ | Order validity (`DAY`, `IOC`) | | `disclosedQty` | string | - | Disclosed quantity for iceberg orders | | `remarks` | string | - | Custom tags for order identification | #### Place Regular Order ```bash curl --location 'https://edge.arrow.trade/order/regular' \ --header 'appID: ' \ --header 'token: ' \ --data '{ "exchange": "NSE", "quantity": "2", "disclosedQty": "0", "remarks": "234", "product": "I", "symbol": "IDEA-EQ", "transactionType": "B", "order": "LMT", "price": "7.5", "validity": "DAY" }' ``` #### Success Response ```json { "data": { "orderNo": "24012400000321", "requestTime": "21:45:55 24-01-2024" }, "status": "success" } ``` ### Modify Regular Order !!! info "Endpoint Details" **Method:** `POST` **URL:** `/order/regular/{orderID}` **Header:** Required (`appID` + `token`) #### Request Parameters | Parameter | Type | Required | Description | |-----------|------|----------|-------------| | `exchange` | string | ✓ | Exchange identifier (NSE, NFO, BSE, etc.) | | `symbol` | string | ✓ | Trading symbol | | `quantity` | string | ✓ | Order quantity | | `transactionType` | string | ✓ | Transaction type (`B` = Buy, `S` = Sell) | | `order` | string | ✓ | Order type (LMT, MKT, SL-LMT, SL-MKT) | | `product` | string | ✓ | Product type (`I`, `C`, `M`) | | `price` | string | ✓ | Order price (use "0" for market orders) | | `validity` | string | ✓ | Order validity (`DAY`, `IOC`) | | `disclosedQty` | string | - | Disclosed quantity for iceberg orders | | `remarks` | string | - | Custom tags for order identification | #### Place Regular Order ```bash curl --location 'https://edge.arrow.trade/order/regular' \ --header 'appID: ' \ --header 'token: ' \ --data '{ "exchange": "NSE", "quantity": "2", "disclosedQty": "0", "remarks": "234", "product": "I", "symbol": "IDEA-EQ", "transactionType": "B", "order": "LMT", "price": "7.5", "validity": "DAY" }' ``` #### Success Response ```json { "data": { "orderNo": "24012400000321", "requestTime": "21:45:55 24-01-2024" }, "status": "success" } ``` ### Cancel Order Cancel pending orders that are no longer required. !!! info "Endpoint Details" **Method:** `DELETE` **URL:** `/order/:variety/:order_id` **Header:** Required (`appID` + `token`) #### Request Example ```bash curl --location --request DELETE 'https://edge.arrow.trade/order/regular/24032900000003' \ --header 'appID: ' \ --header 'token: ' ``` #### Success Response ```json { "data": { "message": "order cancellation request accepted" }, "status": "success" } ``` ## Order Tracking ### Get Order Details Retrieve comprehensive order history and execution details for specific orders. !!! info "Endpoint Details" **Method:** `GET` **URL:** `/order/:order_id` **Header:** Required (`appID` + `token`) #### Request Example ```bash curl --location 'https://edge.arrow.trade/order/24030700000042' \ --header 'appID: ' \ --header 'token: ' ``` ```json { "data": [ { "userID": "AJ0001", "accountID": "AJ0001", "exchange": "NFO", "symbol": "NIFTY02DEC25C26100", "id": "25120202000010", "price": "34", "quantity": "75", "product": "M", "orderStatus": "COMPLETE", "reportType": "Fill", "transactionType": "B", "order": "MKT", "cumulativeFillQty": "75", "fillShares": "75", "averagePrice": "34", "exchangeOrderID": "1400000055208129", "cancelQuantity": "0", "validity": "DAY", "pricePrecision": "2", "tickSize": "0.05", "lotSize": "75", "token": "46799", "orderTime": "2025-12-02T10:16:16", "exchangeUpdateTime": "2025-12-02T10:16:16", "exchangeTime": "2025-12-02T10:16:16", "orderSource": "WEB", "isAck": true, "leavesQuantity": "0", "twap": {}, "gorilla": {}, "vwap": {}, "slicer": {}, "pov": {} }, { "userID": "AJ0001", "accountID": "AJ0001", "exchange": "NFO", "symbol": "NIFTY02DEC25C26100", "id": "25120202000010", "price": "34", "quantity": "75", "product": "M", "orderStatus": "OPEN", "reportType": "NewAck", "transactionType": "B", "order": "MKT", "cumulativeFillQty": "0", "fillShares": "0", "averagePrice": "0", "exchangeOrderID": "1400000055208129", "cancelQuantity": "0", "validity": "DAY", "pricePrecision": "2", "tickSize": "0.05", "lotSize": "75", "token": "46799", "orderTime": "2025-12-02T10:16:16", "exchangeUpdateTime": "2025-12-02T10:16:16", "exchangeTime": "2025-12-02T10:16:16", "orderSource": "WEB", "isAck": true, "leavesQuantity": "75", "twap": {}, "gorilla": {}, "vwap": {}, "slicer": {}, "pov": {} }, { "userID": "AJ0001", "accountID": "AJ0001", "exchange": "NFO", "symbol": "NIFTY02DEC25C26100", "id": "25120202000010", "price": "0", "quantity": "75", "product": "M", "orderStatus": "PENDING", "reportType": "PendingNew", "transactionType": "B", "order": "MKT", "cumulativeFillQty": "0", "fillShares": "0", "averagePrice": "0", "exchangeOrderID": "0", "cancelQuantity": "0", "validity": "DAY", "pricePrecision": "2", "tickSize": "0.05", "lotSize": "75", "token": "46799", "orderTime": "2025-12-02T10:16:16", "orderSource": "WEB", "leavesQuantity": "75", "twap": {}, "gorilla": {}, "vwap": {}, "slicer": {}, "pov": {} } ], "status": "success" } ``` ### Order Status Types | Status | Description | Next Action | |--------|-------------|-------------| | `PENDING` | Order submitted, awaiting confirmation | Monitor for updates | | `OPEN` | Order active in the market | Can modify or cancel | | `COMPLETE` | Order fully executed | Review execution details | | `CANCELLED` | Order cancelled by user/system | No further action | | `REJECTED` | Order rejected by exchange | Check rejection reason | ## Portfolio Views ### Get Order Book Access your complete order book for comprehensive order tracking. !!! info "Endpoint Details" **Method:** `GET` **URL:** `/user/orders` **Header:** Required (`appID` + `token`) #### Request Example ```bash curl --location 'https://edge.arrow.trade/user/orders' \ --header 'appID: ' \ --header 'token: ' ``` #### Response Schema ```json { "data": [ { "userID": "AJ0001", "accountID": "AJ0001", "exchange": "NSE", "symbol": "IDEA-EQ", "id": "25120202000013", "price": "10", "quantity": "10", "product": "I", "orderStatus": "CANCELLED", "reportType": "Canceled", "transactionType": "B", "order": "LMT", "cumulativeFillQty": "0", "fillShares": "0", "averagePrice": "0", "exchangeOrderID": "1100000029104706", "cancelQuantity": "10", "remarks": "234", "validity": "DAY", "pricePrecision": "2", "tickSize": "0.01", "lotSize": "1", "token": "14366", "orderTime": "2025-12-02T11:33:07", "exchangeUpdateTime": "2025-12-02T11:33:07", "exchangeTime": "2025-12-02T11:33:07", "orderSource": "WEB", "isAck": true, "leavesQuantity": "0", "twap": {}, "gorilla": {}, "vwap": {}, "slicer": {}, "pov": {} }, { "userID": "AJ0001", "accountID": "AJ0001", "exchange": "NSE", "symbol": "IDEA-EQ", "id": "25120202000012", "rejectReason": "The price lies outside the DPR range", "price": "7.5", "quantity": "2", "product": "I", "orderStatus": "REJECTED", "reportType": "Rejected", "transactionType": "B", "order": "LMT", "cumulativeFillQty": "0", "fillShares": "0", "averagePrice": "0", "exchangeOrderID": "0", "cancelQuantity": "0", "remarks": "234", "validity": "DAY", "pricePrecision": "2", "tickSize": "0.01", "lotSize": "1", "token": "14366", "orderTime": "2025-12-02T11:32:31", "exchangeUpdateTime": "2025-12-02T11:32:31", "exchangeTime": "2025-12-02T11:32:31", "orderSource": "WEB", "isAck": true, "leavesQuantity": "0", "twap": {}, "gorilla": {}, "vwap": {}, "slicer": {}, "pov": {} }, { "userID": "AJ0001", "accountID": "AJ0001", "exchange": "NFO", "symbol": "NIFTY02DEC25C26200", "id": "25120202000011", "price": "10.4", "quantity": "150", "product": "M", "orderStatus": "COMPLETE", "reportType": "Fill", "transactionType": "S", "order": "MKT", "cumulativeFillQty": "150", "fillShares": "75", "averagePrice": "10.45", "exchangeOrderID": "1600000050515119", "cancelQuantity": "0", "validity": "DAY", "pricePrecision": "2", "tickSize": "0.05", "lotSize": "75", "token": "46803", "orderTime": "2025-12-02T10:16:16", "exchangeUpdateTime": "2025-12-02T10:16:16", "exchangeTime": "2025-12-02T10:16:16", "orderSource": "WEB", "isAck": true, "leavesQuantity": "0", "twap": {}, "gorilla": {}, "vwap": {}, "slicer": {}, "pov": {} }, { "userID": "AJ0001", "accountID": "AJ0001", "exchange": "NFO", "symbol": "NIFTY02DEC25C26100", "id": "25120202000010", "price": "34", "quantity": "75", "product": "M", "orderStatus": "COMPLETE", "reportType": "Fill", "transactionType": "B", "order": "MKT", "cumulativeFillQty": "75", "fillShares": "75", "averagePrice": "34", "exchangeOrderID": "1400000055208129", "cancelQuantity": "0", "validity": "DAY", "pricePrecision": "2", "tickSize": "0.05", "lotSize": "75", "token": "46799", "orderTime": "2025-12-02T10:16:16", "exchangeUpdateTime": "2025-12-02T10:16:16", "exchangeTime": "2025-12-02T10:16:16", "orderSource": "WEB", "isAck": true, "leavesQuantity": "0", "twap": {}, "gorilla": {}, "vwap": {}, "slicer": {}, "pov": {} }, { "userID": "AJ0001", "accountID": "AJ0001", "exchange": "BFO", "symbol": "SENSEX04DEC25P85000", "id": "25120202000009", "price": "138.95", "quantity": "20", "product": "M", "orderStatus": "COMPLETE", "reportType": "Fill", "transactionType": "S", "order": "MKT", "cumulativeFillQty": "20", "fillShares": "20", "averagePrice": "139.9", "exchangeOrderID": "1764648423443199412", "cancelQuantity": "0", "validity": "DAY", "pricePrecision": "0", "tickSize": "0.05", "lotSize": "20", "token": "2131475", "orderTime": "2025-12-02T09:38:50", "exchangeUpdateTime": "2025-12-02T09:38:50", "exchangeTime": "2025-12-02T09:38:50", "orderSource": "WEB", "isAck": true, "leavesQuantity": "0", "twap": {}, "gorilla": {}, "vwap": {}, "slicer": {}, "pov": {} }, { "userID": "AJ0001", "accountID": "AJ0001", "exchange": "NFO", "symbol": "NIFTY02DEC25C26100", "id": "25120202000008", "price": "77.55", "quantity": "75", "product": "M", "orderStatus": "COMPLETE", "reportType": "Fill", "transactionType": "S", "order": "LMT", "cumulativeFillQty": "75", "fillShares": "75", "averagePrice": "79.55", "exchangeOrderID": "1400000007439929", "cancelQuantity": "0", "validity": "DAY", "pricePrecision": "2", "tickSize": "0.05", "lotSize": "75", "token": "46799", "orderTime": "2025-12-02T09:21:34", "exchangeUpdateTime": "2025-12-02T09:21:34", "exchangeTime": "2025-12-02T09:21:34", "orderSource": "WEB", "isAck": true, "leavesQuantity": "0", "twap": {}, "gorilla": {}, "vwap": {}, "slicer": {}, "pov": {} }, { "userID": "AJ0001", "accountID": "AJ0001", "exchange": "NFO", "symbol": "NIFTY02DEC25C26200", "id": "25120202000007", "price": "29.45", "quantity": "150", "product": "M", "orderStatus": "COMPLETE", "reportType": "Fill", "transactionType": "B", "order": "LMT", "cumulativeFillQty": "150", "fillShares": "150", "averagePrice": "29.15", "exchangeOrderID": "1600000007077847", "cancelQuantity": "0", "validity": "DAY", "pricePrecision": "2", "tickSize": "0.05", "lotSize": "75", "token": "46803", "orderTime": "2025-12-02T09:21:29", "exchangeUpdateTime": "2025-12-02T09:21:29", "exchangeTime": "2025-12-02T09:21:29", "orderSource": "WEB", "isAck": true, "leavesQuantity": "0", "twap": {}, "gorilla": {}, "vwap": {}, "slicer": {}, "pov": {} }, { "userID": "AJ0001", "accountID": "AJ0001", "exchange": "NFO", "symbol": "NIFTY02DEC25C26100", "id": "25120202000006", "rejectReason": "\u003cerr\u003eMARGIN ERROR: MARGIN BREACH,SHORTFALL=40091.080000", "price": "82.95", "quantity": "75", "product": "M", "orderStatus": "REJECTED", "reportType": "Rejected", "transactionType": "S", "order": "LMT", "cumulativeFillQty": "0", "fillShares": "0", "averagePrice": "0", "exchangeOrderID": "0", "cancelQuantity": "0", "validity": "DAY", "pricePrecision": "2", "tickSize": "0.05", "lotSize": "75", "token": "46799", "orderTime": "2025-12-02T09:21:18", "exchangeUpdateTime": "2025-12-02T09:21:18", "exchangeTime": "2025-12-02T09:21:18", "orderSource": "WEB", "isAck": true, "leavesQuantity": "0", "twap": {}, "gorilla": {}, "vwap": {}, "slicer": {}, "pov": {} }, { "userID": "AJ0001", "accountID": "AJ0001", "exchange": "NFO", "symbol": "NIFTY02DEC25P26200", "id": "25120202000003", "price": "74.2", "quantity": "75", "product": "M", "orderStatus": "COMPLETE", "reportType": "Fill", "transactionType": "B", "order": "MKT", "cumulativeFillQty": "75", "fillShares": "75", "averagePrice": "74.45", "exchangeOrderID": "1600000006186330", "cancelQuantity": "0", "validity": "DAY", "pricePrecision": "2", "tickSize": "0.05", "lotSize": "75", "token": "46804", "orderTime": "2025-12-02T09:20:34", "exchangeUpdateTime": "2025-12-02T09:20:34", "exchangeTime": "2025-12-02T09:20:34", "orderSource": "WEB", "isAck": true, "leavesQuantity": "0", "twap": {}, "gorilla": {}, "vwap": {}, "slicer": {}, "pov": {} }, { "userID": "AJ0001", "accountID": "AJ0001", "exchange": "NFO", "symbol": "NIFTY02DEC25P26200", "id": "25120202000002", "price": "77.55", "quantity": "75", "product": "M", "orderStatus": "COMPLETE", "reportType": "Fill", "transactionType": "S", "order": "LMT", "cumulativeFillQty": "75", "fillShares": "75", "averagePrice": "77.55", "exchangeOrderID": "1600000005064178", "cancelQuantity": "0", "validity": "DAY", "pricePrecision": "2", "tickSize": "0.05", "lotSize": "75", "token": "46804", "orderTime": "2025-12-02T09:19:28", "exchangeUpdateTime": "2025-12-02T09:19:28", "exchangeTime": "2025-12-02T09:19:28", "orderSource": "WEB", "isAck": true, "leavesQuantity": "0", "twap": {}, "gorilla": {}, "vwap": {}, "slicer": {}, "pov": {} } ], "status": "success" } ``` ### Get Trade Book Review all executed trades for performance analysis and reconciliation. !!! info "Endpoint Details" **Method:** `GET` **URL:** `/user/trades` **Header:** Required (`appID` + `token`) #### Response Schema ```json { "data": [ { "userID": "AJ0001", "accountID": "", "exchange": "NFO", "symbol": "NIFTY02DEC25C26100", "id": "25120202000010", "quantity": "75", "product": "M", "transactionType": "B", "order": "MKT", "fillShares": "75", "averagePrice": "34", "exchangeOrderID": "1400000055208129", "remarks": "", "retention": "", "pricePrecision": "2", "tickSize": "0.05", "lotSize": "75", "customFirm": "", "fillTime": "1764650776", "fillID": "4349940", "fillPrice": "34", "fillQuantity": "75", "orderSource": "1", "token": "46799", "exchangeUpdateTime": "2025-12-02T10:16:16", "snoOrderDirection": "", "snoOrderID": "", "requestTime": "2025-12-02T10:16:16", "errorMessage": "" }, { "userID": "AJ0001", "accountID": "", "exchange": "NFO", "symbol": "NIFTY02DEC25C26200", "id": "25120202000011", "quantity": "150", "product": "M", "transactionType": "S", "order": "MKT", "fillShares": "75", "averagePrice": "10.4", "exchangeOrderID": "1600000050515119", "remarks": "", "retention": "", "pricePrecision": "2", "tickSize": "0.05", "lotSize": "75", "customFirm": "", "fillTime": "1764650776", "fillID": "2781151", "fillPrice": "10.45", "fillQuantity": "75", "orderSource": "1", "token": "46803", "exchangeUpdateTime": "2025-12-02T10:16:16", "snoOrderDirection": "", "snoOrderID": "", "requestTime": "2025-12-02T10:16:16", "errorMessage": "" }, { "userID": "AJ0001", "accountID": "", "exchange": "NFO", "symbol": "NIFTY02DEC25C26200", "id": "25120202000011", "quantity": "150", "product": "M", "transactionType": "S", "order": "MKT", "fillShares": "150", "averagePrice": "10.4", "exchangeOrderID": "1600000050515119", "remarks": "", "retention": "", "pricePrecision": "2", "tickSize": "0.05", "lotSize": "75", "customFirm": "", "fillTime": "1764650776", "fillID": "2781152", "fillPrice": "10.45", "fillQuantity": "75", "orderSource": "1", "token": "46803", "exchangeUpdateTime": "2025-12-02T10:16:16", "snoOrderDirection": "", "snoOrderID": "", "requestTime": "2025-12-02T10:16:16", "errorMessage": "" }, { "userID": "AJ0001", "accountID": "", "exchange": "BFO", "symbol": "SENSEX04DEC25P85000", "id": "25120202000009", "quantity": "20", "product": "M", "transactionType": "S", "order": "MKT", "fillShares": "20", "averagePrice": "138.95", "exchangeOrderID": "1764648423443199412", "remarks": "", "retention": "", "pricePrecision": "0", "tickSize": "0.05", "lotSize": "20", "customFirm": "", "fillTime": "1764648530", "fillID": "950660", "fillPrice": "139.9", "fillQuantity": "20", "orderSource": "1", "token": "2131475", "exchangeUpdateTime": "2025-12-02T09:38:50", "snoOrderDirection": "", "snoOrderID": "", "requestTime": "2025-12-02T09:38:50", "errorMessage": "" }, { "userID": "AJ0001", "accountID": "", "exchange": "NFO", "symbol": "NIFTY02DEC25C26100", "id": "25120202000008", "quantity": "75", "product": "M", "transactionType": "S", "order": "LMT", "fillShares": "75", "averagePrice": "77.55", "exchangeOrderID": "1400000007439929", "remarks": "", "retention": "", "pricePrecision": "2", "tickSize": "0.05", "lotSize": "75", "customFirm": "", "fillTime": "1764647494", "fillID": "765295", "fillPrice": "79.55", "fillQuantity": "75", "orderSource": "1", "token": "46799", "exchangeUpdateTime": "2025-12-02T09:21:34", "snoOrderDirection": "", "snoOrderID": "", "requestTime": "2025-12-02T09:21:34", "errorMessage": "" }, { "userID": "AJ0001", "accountID": "", "exchange": "NFO", "symbol": "NIFTY02DEC25C26200", "id": "25120202000007", "quantity": "150", "product": "M", "transactionType": "B", "order": "LMT", "fillShares": "150", "averagePrice": "29.45", "exchangeOrderID": "1600000007077847", "remarks": "", "retention": "", "pricePrecision": "2", "tickSize": "0.05", "lotSize": "75", "customFirm": "", "fillTime": "1764647489", "fillID": "597377", "fillPrice": "29.15", "fillQuantity": "150", "orderSource": "1", "token": "46803", "exchangeUpdateTime": "2025-12-02T09:21:29", "snoOrderDirection": "", "snoOrderID": "", "requestTime": "2025-12-02T09:21:29", "errorMessage": "" }, { "userID": "AJ0001", "accountID": "", "exchange": "NFO", "symbol": "NIFTY02DEC25P26200", "id": "25120202000003", "quantity": "75", "product": "M", "transactionType": "B", "order": "MKT", "fillShares": "75", "averagePrice": "74.2", "exchangeOrderID": "1600000006186330", "remarks": "", "retention": "", "pricePrecision": "2", "tickSize": "0.05", "lotSize": "75", "customFirm": "", "fillTime": "1764647434", "fillID": "539591", "fillPrice": "74.45", "fillQuantity": "75", "orderSource": "1", "token": "46804", "exchangeUpdateTime": "2025-12-02T09:20:34", "snoOrderDirection": "", "snoOrderID": "", "requestTime": "2025-12-02T09:20:34", "errorMessage": "" }, { "userID": "AJ0001", "accountID": "", "exchange": "NFO", "symbol": "NIFTY02DEC25P26200", "id": "25120202000002", "quantity": "75", "product": "M", "transactionType": "S", "order": "LMT", "fillShares": "75", "averagePrice": "77.55", "exchangeOrderID": "1600000005064178", "remarks": "", "retention": "", "pricePrecision": "2", "tickSize": "0.05", "lotSize": "75", "customFirm": "", "fillTime": "1764647368", "fillID": "453996", "fillPrice": "77.55", "fillQuantity": "75", "orderSource": "1", "token": "46804", "exchangeUpdateTime": "2025-12-02T09:19:28", "snoOrderDirection": "", "snoOrderID": "", "requestTime": "2025-12-02T09:19:26", "errorMessage": "" } ], "status": "success" } ``` ## Error Handling Standard HTTP status codes with detailed error responses: ```json { "status": "error", "message": "Insufficient margin for this order", "code": "MARGIN_ERROR" } ``` !!! warning "Common Error Codes" - **400 Bad Request:** Invalid order parameters or missing fields - **401 Unauthorized:** Invalid or expired authentication credentials - **403 Forbidden:** Insufficient trading permissions or account restrictions - **409 Conflict:** Order conflicts with existing positions or limits - **429 Too Many Requests:** Rate limit exceeded - **500 Internal Server Error:** Exchange connectivity or system issues ## Integration Guidelines !!! tip "Best Practices" - **Pre-flight Validation:** Check margin requirements before placing orders - **Order Status Monitoring:** Implement real-time order status tracking - **Error Recovery:** Build robust error handling and retry mechanisms - **Rate Limit Management:** Implement intelligent request throttling - **Idempotency:** Use unique tags to prevent duplicate order submissions !!! note "Market Hours" Orders can be placed during market hours and as AMO (After Market Orders) for next-day execution. Always verify exchange-specific trading hours and holiday schedules. !!! warning "Risk Considerations" - Always validate order parameters before submission - Monitor position limits and margin requirements - Implement proper stop-loss mechanisms for risk management - Test thoroughly in paper trading environments before live deployment --- ## Source file: `rest-api/positions.md` **Canonical URL:** https://docs.arrow.trade/rest-api/positions/ # Positions API The Positions API delivers portfolio monitoring capabilities with real-time position tracking, detailed P&L breakdowns, and comprehensive risk analytics. Essential for portfolio management platforms, algorithmic trading systems, and advanced trading applications requiring precise position data. ## Endpoint Reference ### Get Portfolio Positions Retrieve your complete position book with detailed analytics and market data. !!! info "Endpoint Details" **Method:** `GET` **URL:** `/user/positions` **Header:** Required (`appID` + `token`) #### Request Example ```bash curl --location 'https://edge.arrow.trade/user/positions' \ --header 'appID: ' \ --header 'token: ' ``` ## Response Schema ### Success Response ```json { "data": [ { "userID": "AJ0001", "accountID": "", "token": "1531822", "exchange": "BSE", "symbol": "IDEA", "segment": "CM", "product": "I", "qty": "0", "avgPrice": "0", "dayBuyQty": "1", "daySellQty": "1", "dayBuyAmount": "9.9", "dayBuyAvgPrice": "9.9", "daySellAmount": "10.59", "daySellAvgPrice": "10.59", "carryForwardBuyQty": "", "carryForwardSellQty": "", "carryForwardBuyAmount": "", "carryForwardBuyAvgPrice": "", "carryForwardSellAmount": "", "carryForwardSellAvgPrice": "", "carryForwardAvgPrice": "", "ltp": "11.17", "realisedPnL": "", "unrealisedMarkToMarket": "", "breakEvenPrice": "", "multiplier": "0", "pricePrecision": "0", "priceFactor": "", "tickSize": "0.01", "lotSize": "1", "uploadPrice": "", "netUploadPrice": "" }, { "userID": "AJ0001", "accountID": "", "token": "49795", "exchange": "NFO", "symbol": "NIFTY03FEB26C25300", "segment": "FO", "product": "M", "qty": "0", "avgPrice": "0", "dayBuyQty": "1040", "daySellQty": "1040", "dayBuyAmount": "188353.75", "dayBuyAvgPrice": "181.109375", "daySellAmount": "195737.75", "daySellAvgPrice": "188.209375", "carryForwardBuyQty": "0", "carryForwardSellQty": "0", "carryForwardBuyAmount": "0", "carryForwardBuyAvgPrice": "0", "carryForwardSellAmount": "0", "carryForwardSellAvgPrice": "0", "carryForwardAvgPrice": "", "ltp": "214", "realisedPnL": "", "unrealisedMarkToMarket": "", "breakEvenPrice": "", "multiplier": "0", "pricePrecision": "2", "priceFactor": "", "tickSize": "0.05", "lotSize": "65", "uploadPrice": "", "netUploadPrice": "" }, { "userID": "AJ0001", "accountID": "", "token": "49801", "exchange": "NFO", "symbol": "NIFTY03FEB26C25400", "segment": "FO", "product": "M", "qty": "0", "avgPrice": "0", "dayBuyQty": "1040", "daySellQty": "1040", "dayBuyAmount": "146864.25", "dayBuyAvgPrice": "141.215625", "daySellAmount": "145141.75", "daySellAvgPrice": "139.559375", "carryForwardBuyQty": "0", "carryForwardSellQty": "0", "carryForwardBuyAmount": "0", "carryForwardBuyAvgPrice": "0", "carryForwardSellAmount": "0", "carryForwardSellAvgPrice": "0", "carryForwardAvgPrice": "", "ltp": "161.8", "realisedPnL": "", "unrealisedMarkToMarket": "", "breakEvenPrice": "", "multiplier": "0", "pricePrecision": "2", "priceFactor": "", "tickSize": "0.05", "lotSize": "65", "uploadPrice": "", "netUploadPrice": "" } ], "status": "success" } ``` ## Integration Guidelines !!! tip "Best Practices" - **Efficient Polling**: Request positions data at appropriate intervals (every 1-5 seconds during market hours) - **Data Caching**: Cache position data to minimize API calls while maintaining freshness - **P&L Monitoring**: Implement real-time P&L tracking for risk management - **Position Reconciliation**: Cross-verify with order and trade data for accuracy - **Error Recovery**: Build robust error handling for market data interruptions !!! note "Performance Optimization" - Filter positions client-side to reduce processing overhead - Implement incremental updates where possible - Use WebSocket feeds for real-time price updates when available - Monitor API response times and implement fallback mechanisms !!! warning "Risk Management" - Monitor unrealized P&L for position sizing decisions - Set up alerts for significant position movements - Implement automated stop-loss mechanisms based on position data - Regular reconciliation with broker statements for accuracy --- ## Source file: `rest-api/rate-limits.md` **Canonical URL:** https://docs.arrow.trade/rest-api/rate-limits/ # Rate Limits Rate limiting controls the volume of requests from a single user within a specified timeframe. This prevents abuse and ensures stable performance for all users. ## Rate Limits Product Wise | Endpoint | Rate Limit | |----------|------------| | Orders | 10 req/sec | | Positions | 10 req/sec | | Holdings | 10 req/sec | | Funds | 10 req/sec | | Historical Data | 10 req/sec | | Market Data | 10 req/sec | ## Need Higher Limits? If you require higher rate limits for your use case, contact us at: - **Email:** apisupport@arrow.trade !!! note "Compliance Restrictions" **IMPORTANT:** As you might be aware, SEBI has put restrictions on how an individual user access the trading API's. Adhering to that, the above rate limits have been put. This doesn't mean that we do not support higher rate limits. Anything above 10 orders per second require approval from the exchange. Please mail us on apisupport@arrow.trade and we will get this thing done for you. --- ## Source file: `rest-api/response-structure.md` **Canonical URL:** https://docs.arrow.trade/rest-api/response-structure/ # API Response Structure All API endpoints in our system follow a consistent response format to ensure predictable integration and error handling. This standardized approach helps developers understand and process responses efficiently across all endpoints. ## Overview Every API response will be one of two types: - **Success Response** - When the request is processed successfully - **Error Response** - When the request fails due to validation, server errors, or other issues ## Response Types ### Successful Response When an API call executes successfully, the response follows this structure: ```json title="Successful Response Format" { "status": "success", "data": { // Response payload varies by endpoint } } ``` **Field Descriptions:** | Field | Type | Description | |-------|------|-------------| | `status` | string | Always `"success"` for successful requests | | `data` | object | Contains the actual response data (structure varies by endpoint) | #### Example Success Response ```json title="Example: User Profile Response" { "data": { "email": "abhishek.***********@gmail.com", "exchanges": [ "NFO", "NSE", "BSE", "BFO", "MCX", "NSECO" ], "id": "AJ0001", "name": "ABHISHEK JAIN", "products": [ "NRML", "MIS", "CNC", "CO", "BO", "MTF" ], ... }, "status": "success" } ``` ### Failed Response When an API call fails, the response includes error details to help with debugging and user feedback: ```json title="Error Response Format" { { "message": "Human Readable Error Message", "status": "failure", "errorCode": , "errorData": "More Information about the Error" } } ``` **Field Descriptions:** | Field | Type | Description | |-------|------|-------------| | `message` | string | Human-readable error message explaining what went wrong | | `status` | string | Always `"failed"` for failed requests | | `errorCode` | string | Standardized error code for programmatic error handling | #### Example Error Response ```json title="Example: Validation Error" { "message": "invalid password", "status": "failure", "errorCode": 400, } ``` !!! info "Error Codes Reference" For a complete list of possible error codes and their meanings, see the [Error Codes Documentation](/documentation/error_codes#list-of-possible-error-codes). ## Data Types The API responses use standard JSON data types with specific formatting conventions: ### Primitive Types | Type | Description | Example Values | |------|-------------|----------------| | **integer** | Whole numbers | `6`, `1`, `7`, `4`, `-10` | | **float** | Decimal numbers | `1.729`, `3.14159`, `0.5` | | **boolean** | True/false values | `true`, `false` | | **string** | Text values | `"Hello World"`, `"API_KEY_123"` | ### Formatted Types | Type | Format | Description | Example | |------|--------|-------------|---------| | **Timestamp** | `yyyy-mm-dd hh:mm:ss` | Date and time in 24-hour format | `"2023-01-31 09:30:00"` | | **Date** | `yyyy-mm-dd` | Date only | `"2023-01-31"` | ### Complex Types | Type | Description | Example | |------|-------------|---------| | **Array** | List of values | `[1, 2, 3]`, `["apple", "banana"]` | | **Object** | Nested JSON structure | `{"name": "John", "age": 30}` | | **null** | Represents absence of value | `null` | ## Best Practices ### For API Consumers 1. **Always check the `status` field** before processing the `data` field 2. **Handle error responses gracefully** by checking both the `message` and `errorCode` 3. **Implement proper error logging** using the `errorCode` for categorization 4. **Parse timestamps consistently** using the specified format 5. **Validate data types** when processing the response data --- ## Source file: `rest-api/symbol-quotes.md` **Canonical URL:** https://docs.arrow.trade/rest-api/symbol-quotes/ --- ## Source file: `rest-api/symbols.md` **Canonical URL:** https://docs.arrow.trade/rest-api/symbols/ # Symbols --- The Arrow Trade Instrument API provides comprehensive access to all tradable instruments available on supported exchanges. This unified interface delivers market data in a structured CSV format, enabling seamless integration for algorithmic trading, portfolio management, and market analysis applications. ## Real-Time Instrument Data ### Complete Instrument Universe Access the complete catalog of tradable instruments across all supported exchanges and segments with a single API call. The instrument database is refreshed daily at **8:00 AM IST** to ensure accuracy and relevance for each trading session. **Example Request** ```bash curl --location 'https://edge.arrow.trade/all' \ --header 'appID: ' \ --header 'token: ' ``` > **Important:** Download instrument data only after 8:00 AM IST to ensure you receive the most current trading session tokens. Accessing the data before this time may result in stale instrument tokens from the previous trading session. ## Response Format The API returns comprehensive instrument data in CSV format with the following structure: ```csv Exchange,Segment,ExchSeg,Token,FullName,Symbol,TradingSymbol,Series,ISIN,LotSize,TickSize,PricePrecision,OptionType,Underlying,UnderlyingToken,StrikePrice,Expiry,FreezeQty,Lower Band,Upper Band,SurCodes,Events,ExchangeID NSE,CM,NSECM,3045,STATE BANK OF INDIA,SBIN,SBIN-EQ,EQ,INE062A01020,1,0.10,2,,,0,0.00,,0,1073.20,1311.60,,,3045 NSE,CM,NSECM,2885,RELIANCE INDUSTRIES LTD,RELIANCE,RELIANCE-EQ,EQ,INE002A01018,1,0.10,2,,,0,0.00,,0,1304.10,1593.70,,,2885 NSE,FO,NSEFO,104034,DIVISLAB,DIVISLAB,DIVISLAB30MAR26C6450,XX,,100,0.05,2,CE,DIVISLAB,10940,6450.00,30-Mar-2026,4000,61.75,301.55,,,104034 NSE,FO,NSEFO,104190,DIVISLAB,DIVISLAB,DIVISLAB28APR26P5800,XX,,100,0.05,2,PE,DIVISLAB,10940,5800.00,28-Apr-2026,4000,70.95,230.45,,,104190 NSE,FO,NSEFO,163082,TITAN,TITAN,TITAN30MAR26C3720,XX,,175,0.05,2,CE,TITAN,3506,3720.00,30-Mar-2026,7000,434.15,751.75,,,163082 NSE,FO,NSEFO,59577,BANKNIFTY,BANKNIFTY,BANKNIFTY24FEB26C43800,XX,,30,0.05,2,CE,BANKNIFTY,26009,43800.00,24-Feb-2026,600,15527.25,18524.05,,,59577 NSE,INDEX,NSEIDX,26016,,HangSeng BeES-NAV,,,,,1,,,,,,,,,,,,26016 NSE,INDEX,NSEIDX,26017,,India VIX,,,,,1,,,,,,,,,,,,26017 NSE,INDEX,NSEIDX,26000,,Nifty 50,,,,,1,,,,,,,,,,,,26000 NSE,INDEX,NSEIDX,26008,,Nifty IT,,,,,1,,,,,,,,,,,,26008 NSE,INDEX,NSEIDX,26013,,Nifty Next 50,,,,,1,,,,,,,,,,,,26013 NSE,INDEX,NSEIDX,26009,,Nifty Bank,,,,,1,,,,,,,,,,,,26009 ``` ## Different file Endpoints The following endpoints allows you to download file Segment wise | Segment | Endpoint | |-------|-----------| | ALL | `/all` | | NSE | `/nse` | | BSE | `/bse` | | MCX | `/mcx` | | INDICES | `/indices` | ## Field Specifications ### Core Trading Fields ⭐ The following fields are essential for most trading operations and API interactions: | Field | Data Type | Description | |-------|-----------|-------------| | **ExchSeg** ⭐ | `string` | Exchange segment identifier: `NFO` (NSE F&O), `NSE` (NSE Equity), `BFO` (BSE F&O), `BSE` (BSE Equity) | | **Token** ⭐ | `int64` | Unique instrument identifier required for all API operations | | **TradingSymbol** ⭐ | `string` | Primary trading symbol used in order placement and market data APIs | | **StrikePrice** ⭐ | `int64` | Option strike price (multiplied by 100 for precision) | ### Comprehensive Instrument Data | Field | Data Type | Description | |-------|-----------|-------------| | **LotSize** | `int64` | Minimum tradable quantity per lot | | **Symbol** | `string` | Exchange-recognized symbol identifier | | **CompanyName** | `string` | Complete registered company name | | **Exchange** | `string` | Primary exchange: `NSE` or `BSE` | | **Segment** | `string` | Market segment: `EQT` (Equity), `DER` (Derivatives) | | **Instrument** | `string` | Instrument classification: `EQ`, `FUTIDX`, `OPTIDX`, `FUTSTK`, `OPTSTK`, `BE`, `BL`, `SM` | | **ExpiryDate** | `string` | Contract expiration date (derivatives only) | | **Isin** | `string` | International Securities Identification Number | | **TickSize** | `decimal` | Minimum price movement in paisa | | **PricePrecision** | `int` | Decimal places supported in price quotations | | **Multiplier** | `decimal` | Contract multiplier for position sizing | | **PriceMultiplier** | `decimal` | Price calculation multiplier | | **OptionType** | `string` | Option classification: `CE` (Call European), `PE` (Put European) | | **UnderlyingExchange** | `string` | Exchange of the underlying asset (derivatives) | | **UnderlyingToken** | `int64` | Token identifier of the underlying instrument | | **ExchExpiryDate** | `int64` | Exchange expiry timestamp (add 10 years to epoch time) | | **UpdateTime** | `int64` | Last modification timestamp (epoch time) | | **MessageFlag** | `string` | Internal processing flag | ## Implementation Guidelines **Header:** All requests require valid `appID` and `token` headers for secure access to market data. **Data Freshness:** The instrument database is synchronized daily at 8:00 AM IST. Applications should refresh their local cache accordingly. **Performance:** The API delivers the complete instrument universe in a single response, optimizing for high-frequency trading applications that require comprehensive market coverage. **Integration:** The CSV format enables direct import into popular trading platforms, database systems, and analytical tools without additional processing overhead. --- *For additional ISIN lookups and verification, reference the NSDL Master Search portal.* --- ## Source file: `rest-api/user-info.md` **Canonical URL:** https://docs.arrow.trade/rest-api/user-info/ # User API The User API provides secure access to user profile data, account settings, and trading permissions. Essential for third-party applications, portfolio management platforms, and custom trading interfaces that require user-specific configuration and personalization. ## Endpoint Reference ### Get User Details Retrieve complete user account information and trading permissions. !!! info "Endpoint Details" **Method:** `GET` **URL:** `/user/details` **Headers:** Required (`appID` + `token`) #### Request Example ```js title="Curl" curl --location 'https://edge.arrow.trade/user/details' \ --header 'appID: ' \ --header 'token: ' ``` ## Response Schema ### Success Response ```json { "data": { "bankDetails": [ { "id": "43KJDFDF432DSKFJKD3001NVN", "vpa": "abhishek@axl", "bankName": "HDFC Bank", "accountType": "SAVINGS", "accountNumber": "*1234", "isDefault": true } ], "depository": [ { "dp": "CDSL", "id": "***************" } ], "email": "abhishek.jain@email.com", "exchanges": [ "NSE", "NFO", "NCD", "BSE", "BFO", "BCD", "MCX", "NSESLBM" ], "id": "AJ0000", "image": "***********************************************", "name": "ABHISHEK JAIN", "ordersTypes": 15, "pan": "*123R", "phone": "***********", "products": [ "NRML", "MIS", "CNC", "CO", "BO" ], "totpEnabled": true, "userType": "individual" }, "status": "success" } ``` ## Integration Guidelines !!! tip "Best Practices" - **Cache User Data**: Cache user details to reduce API calls (refresh periodically) - **Permission Validation**: Always verify user permissions before enabling features - **Privacy Compliance**: Handle masked data appropriately in UI displays - **Error Recovery**: Implement graceful fallbacks for user data unavailability - **Security**: Never expose sensitive user data in logs or client-side code