Go to file
2026-05-23 16:26:12 +03:30
README.md Update README.md 2026-05-23 16:26:12 +03:30

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

{
    "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")