# WebSocket API Documentation | IME Live Market Data ## Real-Time Market Streams (IME) ### WebSocket • High-Frequency • Low-Latency --- ## 1. Overview This documentation describes the **HT Data Engine WebSocket API** for subscribing to real-time market data for **IME (Iran Mercantile Exchange)** contracts. The API provides **live market overview streams** including: - Best limit order book (top 3 levels) - Aggregate trade data (OHLC, volume, trade count) - Allowed price range (min/max) - Contract information (open interest & changes) All WebSocket endpoints require **JWT token-based authentication** and support multiple contract names or IDs in a single connection. --- ## 2. Authentication To access protected WebSocket endpoints, a valid **JWT token** is required. ### How to get the token Send a `POST` request to: https://core.hedgetech.ir/auth/user/token/issue text **Headers:** Content-Type: application/x-www-form-urlencoded text **Body:** username=your_username&password=your_password text ### Response Example ```json { "Authorization": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..." } Usage Include the token in the WebSocket connection headers: text Authorization: Important Notes: Tokens are bound to your IP and browser fingerprint. A change invalidates the token. Ensure your account is registered and approved by an admin. Unauthorized connections are closed with WS code 1008. 3. WebSocket Endpoints Endpoint Description wss://core.hedgetech.ir/data-engine/ime/live/data/websocket/contract/name Subscribe using contract names wss://core.hedgetech.ir/data-engine/ime/live/data/websocket/contract/id Subscribe using contract IDs Note: The output payload structure is identical for both endpoints, except the identifier field: contractName for /contract/name ContractId for /contract/id 🔶 Important Clarification: Contract Name vs Contract ID WebSocket Endpoints The data engine provides two separate WebSocket endpoints: Subscribe using Contract Names text wss://core.hedgetech.ir/data-engine/ime/live/data/websocket/contract/name Subscribe using Contract IDs text wss://core.hedgetech.ir/data-engine/ime/live/data/websocket/contract/id Both endpoints deliver identical payload structures, including the same channel and the same data schema. The only difference is the identifier field inside each message: Endpoint Identifier Field in Payload /contract/name "contractName": "" /contract/id "ContractId": "" Example for Contract Name endpoint: json { "channel": "IME Stream", "contractName": "IMEFutures_Sample", "timestamp": "2025-11-14T12:00:00.000000", "data": { ... } } Example for Contract ID endpoint: json { "channel": "IME Stream", "ContractId": "IME123456789", "timestamp": "2025-11-14T12:00:00.000000", "data": { ... } } No other structural difference exists between these two WebSocket services. Why this clarification matters: Consumers might assume that subscribing to the contract-name endpoint returns a different schema — it does not. Client implementations should be prepared to handle either identifier field (contractName or ContractId) depending on which endpoint they connect to. This avoids confusing bugs (for example: looking for ContractId in messages coming from the /contract/name endpoint). 4. Connection Flow Establish WebSocket connection with the proper Authorization header. Include query parameters in the URL. Each contract name/ID is repeated as a separate query parameter: For Contract Name endpoint: text wss://core.hedgetech.ir/data-engine/ime/live/data/websocket/contract/name?contract_names=&contract_names= For Contract ID endpoint: text wss://core.hedgetech.ir/data-engine/ime/live/data/websocket/contract/id?contract_id=&contract_id= If verification passes, the WebSocket connection is accepted. Real-time messages are streamed continuously until the connection is closed. Important: Unauthorized connections are closed immediately with code 1008. 5. Query Parameters Parameter Type Description contract_names list of strings List of contract names to subscribe (for /contract/name) contract_id list of strings List of contract IDs to subscribe (for /contract/id) Example URL (Contract Name): text wss://core.hedgetech.ir/data-engine/ime/live/data/websocket/contract/name?contract_names=IMEFutures_Sample&contract_names=IMEOption_Sample Example URL (Contract ID): text wss://core.hedgetech.ir/data-engine/ime/live/data/websocket/contract/id?contract_id=IME123456789&contract_id=IME987654321 6. Channel & Payload Schema All messages are delivered in the following JSON structure: json { "channel": "IME Stream", "contractName": "IMEFutures_Sample", "timestamp": "2025-11-14T12:00:00.000000", "data": { ... } } NOTE: For the /contract/id endpoint, the contractName field above is replaced by ContractId. Everything else remains the same. Complete Data Payload Structure The data field contains a comprehensive market overview with four subsections: json { "BestLimit": { "1": { "buy_quantity": 0, "buy_price": 0, "sell_quantity": 0, "sell_price": 0 }, "2": { "buy_quantity": 0, "buy_price": 0, "sell_quantity": 0, "sell_price": 0 }, "3": { "buy_quantity": 0, "buy_price": 0, "sell_quantity": 0, "sell_price": 0 } }, "Aggregate": { "date": "", "time": "", "trade_count": 0, "total_volume": 0, "total_value": 0, "closing_price": 0, "last_price": 0, "low_price": 0, "high_price": 0, "open_price": 0, "previous_close": 0 }, "AllowedPriceRange": { "minAllowedPrice": 1, "maxAllowedPrice": 9999999999 }, "ContractInfo": { "open_interest": 0, "open_interest_changes": 0 } } 6.1 Field Descriptions BestLimit (Top 3 Order Book Levels) Field Type Description buy_quantity int Total buy quantity at this level buy_price int Buy price at this level sell_quantity int Total sell quantity at this level sell_price int Sell price at this level Aggregate (Trading Statistics) Field Type Description date str Trading date (YYYY-MM-DD) time str Time of last trade update (HH:MM:SS) trade_count int Number of trades total_volume int Total volume traded total_value int Total value of trades closing_price float Closing price of the contract last_price float Last traded price low_price float Lowest traded price high_price float Highest traded price open_price float Opening price previous_close float Previous day's closing price AllowedPriceRange (Price Limits) Field Type Description minAllowedPrice float Minimum allowed price for the contract maxAllowedPrice float Maximum allowed price for the contract ContractInfo (Position Information) Field Type Description open_interest int Open interest for the contract open_interest_changes int Change in open interest compared to previous period 7. Error Handling Code Description 1008 Policy violation (invalid JWT, invalid contract names/IDs) Connection closed Occurs if Redis stream fails or server error 8. Examples 8.1 Python (WebSocket Client) python import asyncio import websockets import json async def subscribe_ime(url: str, token: str): headers = {"Authorization": token} async with websockets.connect(url, extra_headers=headers) as ws: async for message in ws: data = json.loads(message) identifier = data.get("contractName") or data.get("ContractId") payload = data.get("data") print(f"{data['timestamp']} | {identifier}") print(f" Last Price: {payload.get('Aggregate', {}).get('last_price')}") print(f" Best Buy: {payload.get('BestLimit', {}).get('1', {}).get('buy_price')}") # Usage url = "wss://core.hedgetech.ir/data-engine/ime/live/data/websocket/contract/name?contract_names=IMEFutures_Sample" token = "" asyncio.run(subscribe_ime(url, token)) 8.2 JavaScript (WebSocket Client) javascript const WebSocket = require('ws'); const url = 'wss://core.hedgetech.ir/data-engine/ime/live/data/websocket/contract/name?contract_names=IMEFutures_Sample'; const token = ''; const ws = new WebSocket(url, { headers: { Authorization: token } }); ws.on('open', () => console.log('Connected')); ws.on('message', (data) => { const msg = JSON.parse(data); const identifier = msg.contractName || msg.ContractId; console.log(`${msg.timestamp} | ${identifier}`); console.log(' Last Price:', msg.data.Aggregate.last_price); }); ws.on('close', () => console.log('Disconnected')); 8.3 Go (WebSocket Client) go package main import ( "encoding/json" "fmt" "github.com/gorilla/websocket" "log" ) func main() { url := "wss://core.hedgetech.ir/data-engine/ime/live/data/websocket/contract/name?contract_names=IMEFutures_Sample" header := map[string][]string{"Authorization": {""}} c, _, err := websocket.DefaultDialer.Dial(url, header) if err != nil { log.Fatal(err) } defer c.Close() for { _, message, _ := c.ReadMessage() var msg map[string]interface{} json.Unmarshal(message, &msg) identifier := msg["contractName"] if identifier == nil { identifier = msg["ContractId"] } data := msg["data"].(map[string]interface{}) agg := data["Aggregate"].(map[string]interface{}) fmt.Printf("%s | %v | Last Price: %v\n", msg["timestamp"], identifier, agg["last_price"]) } } 9. Best Practices Reconnect with exponential backoff in case of disconnects. Validate your JWT before subscribing. Subscribe only to the contracts you need to reduce bandwidth. Handle both contractName and ContractId in your message parsing. The data payload is the same for both endpoints; reuse your parsing logic. Appendix: Quick Developer Checklist ✅ Use correct endpoint (/contract/name vs /contract/id) ✅ Provide Authorization header with valid token ✅ Include contract_names or contract_id as repeated query params ✅ Handle both contractName and ContractId in message parsing ✅ Monitor for WS close code 1008 for authorization errors ✅ BestLimit contains only top 3 levels (keys: "1", "2", "3")