# HT Data Engine | IME | WebSocket ## Real-Time Mercantile Exchange Streams (IME) ### WebSocket • Real-Time • Low-Latency --- # 1. Overview This documentation describes the **HT Data Engine IME WebSocket API** for subscribing to real-time market data streams from the **Iran Mercantile Exchange (IME)**. The service provides low-latency real-time updates for IME contracts including: - Best bid/ask limits - Aggregate trading statistics - Allowed price ranges - Contract open interest information The API supports two independent subscription endpoints: - Subscription using **Contract Names** - Subscription using **Contract IDs** All WebSocket connections require valid authentication and support subscribing to multiple contracts in a single connection. --- # 2. Authentication All IME WebSocket endpoints require authentication. Clients must provide a valid JWT token during the WebSocket handshake. ## Token Endpoint ```text https://core.hedgetech.ir/auth/user/token/issue ``` ## Request ### Headers ```text Content-Type: application/x-www-form-urlencoded ``` ### Body ```text username=your_username&password=your_password ``` --- ## Response Example ```json { "Authorization": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..." } ``` --- ## WebSocket Authorization Header ```text Authorization: ``` --- ## Important Notes - Unauthorized connections are rejected with WebSocket close code `1008` - Tokens may be invalidated if account policies or security rules change - Ensure your account has access to IME live market data services --- # 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 | --- # 4. Important Clarification: Contract Name vs Contract ID Endpoints The IME data engine exposes two separate WebSocket endpoints. ## Contract Name Endpoint ```text wss://core.hedgetech.ir/data-engine/ime/live/data/websocket/contract/name ``` Messages contain: ```json "contractName": "" ``` --- ## Contract ID Endpoint ```text wss://core.hedgetech.ir/data-engine/ime/live/data/websocket/contract/id ``` Messages contain: ```json "contractId": "" ``` --- ## Important Both endpoints return: - The exact same market data - The same payload structure - The same channel schema The only difference is the identifier field. | Endpoint | Identifier Field | |---|---| | `/contract/name` | `contractName` | | `/contract/id` | `contractId` | --- ## Why This Matters Client applications must correctly detect which identifier field exists in incoming messages. Recommended approach: ```python identifier = message.get("contractId") or message.get("contractName") ``` This prevents parsing issues when switching between endpoints. --- # 5. Connection Flow 1. Establish WebSocket connection 2. Provide the Authorization header 3. Pass query parameters in the URL 4. Server validates: - JWT token - Contract identifiers - Subscription permissions 5. If validation succeeds: - WebSocket connection is accepted - Real-time streams begin immediately 6. If validation fails: - Connection closes with code `1008` --- # 6. Query Parameters ## Contract Name Endpoint | Parameter | Type | Description | |---|---|---| | `contract_name` | repeated string | IME contract names | ### Example ```text wss://core.hedgetech.ir/data-engine/ime/live/data/websocket/contract/name?contract_name=GOLD-APR&contract_name=CEMENT-MAY ``` --- ## Contract ID Endpoint | Parameter | Type | Description | |---|---|---| | `contract_id` | repeated string | IME contract IDs | ### Example ```text wss://core.hedgetech.ir/data-engine/ime/live/data/websocket/contract/id?contract_id=1001&contract_id=1002 ``` --- # 7. Message Structure All WebSocket messages follow the same envelope structure. --- ## Contract Name Endpoint Example ```json { "channel": "IME Stream", "contractName": "GOLD-APR", "timestamp": "2026-05-29T12:00:00.000000Z", "data": { "...": "..." } } ``` --- ## Contract ID Endpoint Example ```json { "channel": "IME Stream", "contractId": "1001", "timestamp": "2026-05-29T12:00:00.000000Z", "data": { "...": "..." } } ``` --- # 8. Data Payload Schema The `data` field contains multiple market data sections grouped into a single payload. --- # 8.1 BestLimit Top bid/ask levels for the contract. ## Example ```json { "BestLimit": { "1": { "buy_quantity": 150, "buy_price": 245000, "sell_quantity": 100, "sell_price": 246000 }, "2": { "buy_quantity": 120, "buy_price": 244500, "sell_quantity": 90, "sell_price": 246500 }, "3": { "buy_quantity": 80, "buy_price": 244000, "sell_quantity": 70, "sell_price": 247000 } } } ``` --- ## Fields | Field | Description | |---|---| | `buy_quantity` | Total bid quantity | | `buy_price` | Bid price | | `sell_quantity` | Total ask quantity | | `sell_price` | Ask price | --- # 8.2 Aggregate Aggregate trading statistics for the contract. ## Example ```json { "Aggregate": { "date": "2026-05-29", "time": "12:00:00", "trade_count": 120, "total_volume": 4500, "total_value": 1102500000, "closing_price": 245500, "last_price": 245700, "low_price": 244000, "high_price": 247000, "open_price": 244500, "previous_close": 243000 } } ``` --- ## Fields | Field | Description | |---|---| | `trade_count` | Number of executed trades | | `total_volume` | Total traded volume | | `total_value` | Total traded value | | `closing_price` | Current settlement/closing price | | `last_price` | Last traded price | | `low_price` | Session low | | `high_price` | Session high | | `open_price` | Session open | | `previous_close` | Previous settlement/close price | --- # 8.3 AllowedPriceRange Allowed trading price boundaries for the contract. ## Example ```json { "AllowedPriceRange": { "minAllowedPrice": 220000, "maxAllowedPrice": 270000 } } ``` --- ## Fields | Field | Description | |---|---| | `minAllowedPrice` | Minimum allowed trading price | | `maxAllowedPrice` | Maximum allowed trading price | --- # 8.4 ContractInfo Contract open interest statistics. ## Example ```json { "ContractInfo": { "open_interest": 5400, "open_interest_changes": 120 } } ``` --- ## Fields | Field | Description | |---|---| | `open_interest` | Current open interest | | `open_interest_changes` | Change in open interest | --- # 9. Complete Example Payload ```json { "channel": "IME Stream", "contractId": "1001", "timestamp": "2026-05-29T12:00:00.000000Z", "data": { "BestLimit": { "1": { "buy_quantity": 150, "buy_price": 245000, "sell_quantity": 100, "sell_price": 246000 }, "2": { "buy_quantity": 120, "buy_price": 244500, "sell_quantity": 90, "sell_price": 246500 }, "3": { "buy_quantity": 80, "buy_price": 244000, "sell_quantity": 70, "sell_price": 247000 } }, "Aggregate": { "date": "2026-05-29", "time": "12:00:00", "trade_count": 120, "total_volume": 4500, "total_value": 1102500000, "closing_price": 245500, "last_price": 245700, "low_price": 244000, "high_price": 247000, "open_price": 244500, "previous_close": 243000 }, "AllowedPriceRange": { "minAllowedPrice": 220000, "maxAllowedPrice": 270000 }, "ContractInfo": { "open_interest": 5400, "open_interest_changes": 120 } } } ``` --- # 10. Error Handling | Code | Description | |---|---| | `1008` | Invalid token, invalid contract, or unauthorized access | | Connection Closed | Internal server error or Redis stream interruption | --- # 11. Python Example ```python import asyncio import json import websockets async def subscribe(url: str, token: str): headers = { "Authorization": token } async with websockets.connect( url, additional_headers=headers ) as ws: async for message in ws: payload = json.loads(message) identifier = ( payload.get("contractId") or payload.get("contractName") ) print( payload["timestamp"], identifier, payload["channel"] ) print(payload["data"]) url = ( "wss://core.hedgetech.ir/" "data-engine/ime/live/data/websocket/contract/id" "?contract_id=1001" "&contract_id=1002" ) token = "" asyncio.run(subscribe(url, token)) ``` --- # 12. JavaScript Example ```javascript const WebSocket = require('ws'); const url = 'wss://core.hedgetech.ir/data-engine/ime/live/data/websocket/contract/id?contract_id=1001&contract_id=1002'; const token = ''; const ws = new WebSocket( url, { headers: { Authorization: token } } ); ws.on('open', () => { console.log('Connected'); }); ws.on('message', (message) => { const data = JSON.parse(message); const identifier = data.contractId || data.contractName; console.log( data.timestamp, identifier, data.channel ); console.log(data.data); }); ws.on('close', () => { console.log('Disconnected'); }); ``` --- # 13. Subscription Notes - Multiple contracts can be subscribed in a single connection - Query parameters must be repeated - Streams are pushed continuously in real time - Connections should be handled asynchronously - Invalid contracts cause immediate connection rejection --- # 14. Best Practices - Reconnect using exponential backoff - Subscribe only to required contracts - Handle disconnects gracefully - Normalize `contractId` and `contractName` - Monitor WebSocket close code `1008` - Use asynchronous processing pipelines for high-frequency streams --- # 15. Developer Checklist - Use the correct endpoint - Provide valid Authorization header - Pass repeated query parameters - Handle both identifier field types - Parse the nested `data` payload correctly - Monitor connection lifecycle events - Handle reconnect scenarios --- # 16. Future Compatibility Notes The IME streaming architecture is designed to remain schema-compatible across both endpoint types. Future extensions may include: - Additional market channels - Incremental order-book streams - Snapshot recovery - Binary transport protocols - Sequence numbers - Compression support - Dynamic subscribe/unsubscribe actions Clients are encouraged to write flexible parsers to remain forward-compatible.