Update README.md

This commit is contained in:
Mohammad Taha Ranjbar Shirazi 2026-05-29 14:13:28 +03:30
parent 6044610719
commit f49c3c061a

192
README.md
View File

@ -1,7 +1,5 @@
# HT Data Engine | TSE & IFB | WebSocket # HT Data Engine | TSE & IFB | WebSocket
## Real-Time Market Streams (TSE & IFB) ## Real-Time Market Streams (TSE & IFB)
### WebSocket • High-Frequency • Low-Latency ### WebSocket • High-Frequency • Low-Latency
--- ---
@ -9,7 +7,6 @@
## 1. Overview ## 1. Overview
This documentation describes the **HT Data Engine WebSocket API** for subscribing to real-time market data for **TSE & IFB** symbols. This documentation describes the **HT Data Engine WebSocket API** for subscribing to real-time market data for **TSE & IFB** symbols.
The API provides multiple channels delivering structured financial data such as order books, aggregate trades, OHLCV, and fund/contract information. The API provides multiple channels delivering structured financial data such as order books, aggregate trades, OHLCV, and fund/contract information.
All WebSocket endpoints require **JWT token-based authentication** and support multiple symbols or ISINs in a single connection. All WebSocket endpoints require **JWT token-based authentication** and support multiple symbols or ISINs in a single connection.
@ -28,13 +25,13 @@ Send a `POST` request to:
https://core.hedgetech.ir/auth/user/token/issue https://core.hedgetech.ir/auth/user/token/issue
``` ```
**Headers:** Headers:
``` ```
Content-Type: application/x-www-form-urlencoded Content-Type: application/x-www-form-urlencoded
``` ```
**Body:** Body:
``` ```
username=your_username&password=your_password username=your_username&password=your_password
@ -72,7 +69,6 @@ Authorization: <your_token>
| `wss://core.hedgetech.ir/data-engine/tse-ifb/live/data/websocket/symbol/name` | Subscribe using **symbol names** | | `wss://core.hedgetech.ir/data-engine/tse-ifb/live/data/websocket/symbol/name` | Subscribe using **symbol names** |
**Note:** The output payload structure is identical for both endpoints, except the symbol identifier field: **Note:** The output payload structure is identical for both endpoints, except the symbol identifier field:
- `symbolIsin` for `/symbol/isin` - `symbolIsin` for `/symbol/isin`
- `symbolName` for `/symbol/name` - `symbolName` for `/symbol/name`
@ -93,15 +89,14 @@ The data engine provides **two separate WebSocket endpoints**:
``` ```
Both endpoints deliver **identical payload structures**, including the same channels and the same `data` schema. Both endpoints deliver **identical payload structures**, including the same channels and the same `data` schema.
The **only difference** is the identifier field inside each message: The **only difference** is the identifier field inside each message:
| Endpoint | Identifier Field in Payload | | Endpoint | Identifier Field in Payload |
|----------|----------------------------| |----------|------------------------------|
| `/symbol/isin` | `"symbolIsin": "<ISIN>"` | | `/symbol/isin` | `"symbolIsin": "<ISIN>"` |
| `/symbol/name` | `"symbolName": "<Symbol>"` | | `/symbol/name` | `"symbolName": "<Symbol>"` |
**Example for ISIN endpoint:** Example for ISIN endpoint:
```json ```json
{ {
@ -112,7 +107,7 @@ The **only difference** is the identifier field inside each message:
} }
``` ```
**Example for Symbol-Name endpoint:** Example for Symbol-Name endpoint:
```json ```json
{ {
@ -136,23 +131,25 @@ No other structural difference exists between these two WebSocket services.
## 4. Connection Flow (Updated) ## 4. Connection Flow (Updated)
1. Establish WebSocket connection with the proper `Authorization` header. 1. Establish WebSocket connection with the proper `Authorization` header.
2. Include query parameters in the URL. **Each symbol/ISIN and channel is repeated as a separate query parameter**: 2. Include query parameters in the URL. **Each symbol/ISIN and channel is repeated as a separate query parameter**:
For ISIN endpoint: ```
``` wss://core.hedgetech.ir/data-engine/tse-ifb/live/data/websocket/symbol/isin?
wss://core.hedgetech.ir/data-engine/tse-ifb/live/data/websocket/symbol/isin? channels=<channel1>&channels=<channel2>&
channels=<channel1>&channels=<channel2>& symbol_isins=<ISIN1>&symbol_isins=<ISIN2>
symbol_isins=<ISIN1>&symbol_isins=<ISIN2> ```
```
For symbol names endpoint: or for symbol names:
```
wss://core.hedgetech.ir/data-engine/tse-ifb/live/data/websocket/symbol/name? ```
channels=<channel1>&channels=<channel2>& wss://core.hedgetech.ir/data-engine/tse-ifb/live/data/websocket/symbol/name?
symbol_names=<symbol1>&symbol_names=<symbol2> channels=<channel1>&channels=<channel2>&
``` symbol_names=<symbol1>&symbol_names=<symbol2>
```
3. If verification passes, the WebSocket connection is accepted. 3. If verification passes, the WebSocket connection is accepted.
4. Real-time messages are streamed continuously until the connection is closed. 4. Real-time messages are streamed continuously until the connection is closed.
**Important:** Unauthorized connections are closed immediately with **code 1008**. **Important:** Unauthorized connections are closed immediately with **code 1008**.
@ -230,6 +227,8 @@ All messages are delivered in the following **JSON structure**:
} }
``` ```
---
### 6.2 Example: `order-book` ### 6.2 Example: `order-book`
```json ```json
@ -238,24 +237,26 @@ All messages are delivered in the following **JSON structure**:
"symbolIsin": "IRO1XYZ1234", "symbolIsin": "IRO1XYZ1234",
"timestamp": "2025-11-14T12:00:00.000000", "timestamp": "2025-11-14T12:00:00.000000",
"data": { "data": {
"Buy": [ "Buy": [
{ {
"price": 0, "price": 0,
"quantity": 0, "quantity": 0,
"count": 0 "count": 0
} }
], ],
"Sell": [ "Sell": [
{ {
"price": 0, "price": 0,
"quantity": 0, "quantity": 0,
"count": 0 "count": 0
} }
] ]
} }
} }
``` ```
---
### 6.3 Example: `ohlcv-last-1m` ### 6.3 Example: `ohlcv-last-1m`
```json ```json
@ -273,6 +274,8 @@ All messages are delivered in the following **JSON structure**:
} }
``` ```
---
### 6.4 Example: `aggregate` ### 6.4 Example: `aggregate`
```json ```json
@ -281,21 +284,23 @@ All messages are delivered in the following **JSON structure**:
"symbolIsin": "IRO1XYZ1234", "symbolIsin": "IRO1XYZ1234",
"timestamp": "2025-11-14T12:00:00.000000", "timestamp": "2025-11-14T12:00:00.000000",
"data": { "data": {
"date": "string", "date": "string",
"time": "string", "time": "string",
"trade_count": 0, "trade_count": 0,
"total_volume": 0, "total_volume": 0,
"total_value": 0, "total_value": 0,
"closing_price": 0, "closing_price": 0,
"last_price": 0, "last_price": 0,
"low_price": 0, "low_price": 0,
"high_price": 0, "high_price": 0,
"open_price": 0, "open_price": 0,
"previous_close": 0 "previous_close": 0
} }
} }
``` ```
---
### 6.5 Example: `institutional-vs-individual` ### 6.5 Example: `institutional-vs-individual`
```json ```json
@ -304,18 +309,20 @@ All messages are delivered in the following **JSON structure**:
"symbolIsin": "IRO1XYZ1234", "symbolIsin": "IRO1XYZ1234",
"timestamp": "2025-11-14T12:00:00.000000", "timestamp": "2025-11-14T12:00:00.000000",
"data": { "data": {
"buy_count_individual": 0, "buy_count_individual": 0,
"buy_volume_individual": 0, "buy_volume_individual": 0,
"buy_count_institution": 0, "buy_count_institution": 0,
"buy_volume_institution": 0, "buy_volume_institution": 0,
"sell_count_individual": 0, "sell_count_individual": 0,
"sell_volume_individual": 0, "sell_volume_individual": 0,
"sell_count_institution": 0, "sell_count_institution": 0,
"sell_volume_institution": 0 "sell_volume_institution": 0
} }
} }
``` ```
---
### 6.6 Example: `contract-info` ### 6.6 Example: `contract-info`
```json ```json
@ -324,13 +331,15 @@ All messages are delivered in the following **JSON structure**:
"symbolIsin": "IRO1XYZ1234", "symbolIsin": "IRO1XYZ1234",
"timestamp": "2025-11-14T12:00:00.000000", "timestamp": "2025-11-14T12:00:00.000000",
"data": { "data": {
"open_interest": 0, "open_interest": 0,
"initial_margin": 0, "initial_margin": 0,
"required_margin": 0 "required_margin": 0
} }
} }
``` ```
---
### 6.7 Example: `fund-info` ### 6.7 Example: `fund-info`
```json ```json
@ -339,17 +348,34 @@ All messages are delivered in the following **JSON structure**:
"symbolIsin": "IRO1XYZ1234", "symbolIsin": "IRO1XYZ1234",
"timestamp": "2025-11-14T12:00:00.000000", "timestamp": "2025-11-14T12:00:00.000000",
"data": { "data": {
"nav": 0, "nav": 0,
"units": 0, "units": 0,
"marketCap": 0, "marketCap": 0,
"as_of": "2025-11-14T22:10:42.802Z" "as_of": "2025-11-14T22:10:42.802Z"
} }
} }
``` ```
--- ---
## 7. Error Handling ### 7. Other Channels
Payload models follow the **Pydantic models** provided (`Aggregate`, `OrderBook`, `InstitutionalVsIndividual`, `ContractInfo`, `FundInfo`) and always adhere to the format:
```json
{
"channel": "<channel-name>",
"symbolIsin": "<symbolIsin>",
"timestamp": "<ISO8601 timestamp>",
"data": { ...channel-specific data... }
}
```
> NOTE: Replace `symbolIsin` with `symbolName` when using the `/symbol/name` endpoint.
---
## 8. Error Handling
| Code | Description | | Code | Description |
|------|-------------| |------|-------------|
@ -358,9 +384,11 @@ All messages are delivered in the following **JSON structure**:
--- ---
## 8. Code Examples ## 9. Examples
### 8.1 Python (WebSocket Client) ### 9.1 Python (WebSocket Client)
This example adds a small helper to **support both endpoints** by checking which identifier field exists in incoming messages.
```python ```python
import asyncio import asyncio
@ -385,7 +413,7 @@ token = "<your_token>"
asyncio.run(subscribe(url, token)) asyncio.run(subscribe(url, token))
``` ```
### 8.2 JavaScript (WebSocket Client) ### 9.2 JavaScript (WebSocket Client)
```javascript ```javascript
const WebSocket = require('ws'); const WebSocket = require('ws');
@ -409,7 +437,7 @@ ws.on('message', (data) => {
ws.on('close', () => console.log('Disconnected')); ws.on('close', () => console.log('Disconnected'));
``` ```
### 8.3 Go (WebSocket Client) ### 9.3 Go (WebSocket Client)
```go ```go
package main package main
@ -452,7 +480,25 @@ func main() {
} }
``` ```
### 8.4 Rust (WebSocket Client) ### 9.4 Julia (WebSocket Client)
```julia
using WebSockets, JSON
url = "wss://core.hedgetech.ir/data-engine/tse-ifb/live/data/websocket/symbol/isin?channels=order-book&channels=best-limit&symbol_isins=IRT3SATF0001"
token = "<your_token>"
WebSockets.open(url, extra_headers=["Authorization" => token]) do ws
while !eof(ws)
msg = String(readavailable(ws))
data = JSON.parse(msg)
symbol = get(data, "symbolIsin", get(data, "symbolName", ""))
println(data["timestamp"], " ", symbol, " ", data["channel"])
end
end
```
### 9.5 Rust (WebSocket Client)
```rust ```rust
use tokio_tungstenite::connect_async; use tokio_tungstenite::connect_async;
@ -479,7 +525,9 @@ async fn main() {
} }
``` ```
### 8.5 Subscription Notes ---
### 9.6 Subscription Notes
- Multiple symbols and channels can be subscribed in a **single WebSocket connection**. - Multiple symbols and channels can be subscribed in a **single WebSocket connection**.
- The server streams messages continuously; handle them asynchronously. - The server streams messages continuously; handle them asynchronously.
@ -487,7 +535,7 @@ async fn main() {
--- ---
## 9. Best Practices ## 10. Best Practices
- Reconnect with **exponential backoff** in case of disconnects. - Reconnect with **exponential backoff** in case of disconnects.
- Validate your JWT **before subscribing**. - Validate your JWT **before subscribing**.
@ -498,7 +546,7 @@ async fn main() {
--- ---
## Appendix: Quick Developer Checklist (for avoiding common mistakes) ## Appendix: Quick developer checklist (for avoiding common mistakes)
- ✅ Use correct endpoint for your identifier type (`symbol/isin` vs `symbol/name`). - ✅ Use correct endpoint for your identifier type (`symbol/isin` vs `symbol/name`).
- ✅ Provide `Authorization` header with a valid token on the WebSocket handshake. - ✅ Provide `Authorization` header with a valid token on the WebSocket handshake.
@ -506,3 +554,5 @@ async fn main() {
- ✅ Handle both `symbolIsin` and `symbolName` in your message parsing to make the client resilient. - ✅ Handle both `symbolIsin` and `symbolName` in your message parsing to make the client resilient.
- ✅ Treat messages' `data` payload consistently across endpoints (same schema). - ✅ Treat messages' `data` payload consistently across endpoints (same schema).
- ✅ Monitor for WS close code `1008` to detect authorization or policy errors. - ✅ Monitor for WS close code `1008` to detect authorization or policy errors.
---