HT_DataEngine_IME_websocket/README.md
2026-05-29 14:50:33 +03:30

594 lines
11 KiB
Markdown

# 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: <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
```text
wss://core.hedgetech.ir/data-engine/ime/live/data/websocket/contract/name
```
Messages contain:
```json
"contractName": "<CONTRACT_NAME>"
```
---
## Contract ID Endpoint
```text
wss://core.hedgetech.ir/data-engine/ime/live/data/websocket/contract/id
```
Messages contain:
```json
"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:
```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 = "<your_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 = '<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 `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.