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

11 KiB

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

  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

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 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.