| README.md | ||
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
https://core.hedgetech.ir/auth/user/token/issue
Request
Headers
Content-Type: application/x-www-form-urlencoded
Body
username=your_username&password=your_password
Response Example
{
"Authorization": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
}
WebSocket Authorization Header
Authorization: <your_token>
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
wss://core.hedgetech.ir/data-engine/ime/live/data/websocket/contract/name
Messages contain:
"contractName": "<CONTRACT_NAME>"
Contract ID Endpoint
wss://core.hedgetech.ir/data-engine/ime/live/data/websocket/contract/id
Messages contain:
"contractId": "<CONTRACT_ID>"
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:
identifier = message.get("contractId") or message.get("contractName")
This prevents parsing issues when switching between endpoints.
5. Connection Flow
-
Establish WebSocket connection
-
Provide the Authorization header
-
Pass query parameters in the URL
-
Server validates:
- JWT token
- Contract identifiers
- Subscription permissions
-
If validation succeeds:
- WebSocket connection is accepted
- Real-time streams begin immediately
-
If validation fails:
- Connection closes with code
1008
- Connection closes with code
6. Query Parameters
Contract Name Endpoint
| Parameter | Type | Description |
|---|---|---|
contract_name |
repeated string | IME contract names |
Example
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
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
{
"channel": "IME Stream",
"contractName": "GOLD-APR",
"timestamp": "2026-05-29T12:00:00.000000Z",
"data": {
"...": "..."
}
}
Contract ID Endpoint Example
{
"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
{
"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
{
"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
{
"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
{
"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
{
"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
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 = "<your_token>"
asyncio.run(subscribe(url, token))
12. JavaScript Example
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 = '<your_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
contractIdandcontractName - 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
datapayload 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.