HT_DataEngine_IME_websocket/README.md
2026-05-23 16:26:12 +03:30

339 lines
10 KiB
Markdown

# 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: <your_token>
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 Name>"
/contract/id "ContractId": "<Contract ID>"
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=<name1>&contract_names=<name2>
For Contract ID endpoint:
text
wss://core.hedgetech.ir/data-engine/ime/live/data/websocket/contract/id?contract_id=<id1>&contract_id=<id2>
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 = "<your_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 = '<your_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": {"<your_token>"}}
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")