Update README.md
This commit is contained in:
parent
cba504325a
commit
72fdba121c
339
README.md
339
README.md
@ -0,0 +1,339 @@
|
||||
# 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")
|
||||
Loading…
Reference in New Issue
Block a user