DhanHQ 2.4.0 → 2.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +33 -1
- data/README.md +194 -752
- data/docs/AUTHENTICATION.md +71 -9
- data/docs/CONFIGURATION.md +109 -0
- data/docs/SUPER_ORDERS.md +284 -0
- data/docs/TROUBLESHOOTING.md +117 -0
- data/docs/WEBSOCKET_PROTOCOL.md +154 -0
- data/lib/DhanHQ/models/alert_order.rb +22 -0
- data/lib/DhanHQ/models/edis.rb +110 -0
- data/lib/DhanHQ/models/kill_switch.rb +22 -0
- data/lib/DhanHQ/models/margin.rb +49 -0
- data/lib/DhanHQ/models/pnl_exit.rb +130 -0
- data/lib/DhanHQ/models/position.rb +22 -0
- data/lib/DhanHQ/models/postback.rb +123 -0
- data/lib/DhanHQ/resources/kill_switch.rb +8 -0
- data/lib/DhanHQ/resources/margin_calculator.rb +9 -0
- data/lib/DhanHQ/resources/pnl_exit.rb +37 -0
- data/lib/DhanHQ/resources/positions.rb +8 -0
- data/lib/DhanHQ/version.rb +1 -1
- metadata +11 -3
- /data/{README1.md → docs/ARCHIVE_README.md} +0 -0
data/docs/AUTHENTICATION.md
CHANGED
|
@@ -113,50 +113,112 @@ Rescue `AuthenticationError` for local config/token resolution failures; rescue
|
|
|
113
113
|
# SUPPORTED AUTHENTICATION AND TOKEN GENERATION APPROACHES:
|
|
114
114
|
|
|
115
115
|
We now support five distinct authentication approaches in this gem.
|
|
116
|
+
|
|
116
117
|
1️⃣ Static token (manual, simplest)
|
|
117
118
|
What: You paste a token you got from Dhan (web, OAuth, partner, whatever) into config.
|
|
118
119
|
How:
|
|
119
|
-
|
|
120
|
+
```ruby
|
|
121
|
+
DhanHQ.configure do |config|
|
|
122
|
+
config.client_id = ENV["DHAN_CLIENT_ID"]
|
|
123
|
+
config.access_token = ENV["DHAN_ACCESS_TOKEN"]
|
|
124
|
+
end
|
|
125
|
+
```
|
|
120
126
|
When: You’re OK rotating tokens manually (e.g. cron job, ops runbook).
|
|
127
|
+
|
|
121
128
|
2️⃣ Dynamic token via access_token_provider
|
|
122
129
|
What: Gem asks you for a token on every request (proc/lambda).
|
|
123
130
|
How:
|
|
124
|
-
|
|
131
|
+
```ruby
|
|
132
|
+
DhanHQ.configure do |config|
|
|
133
|
+
config.client_id = ENV["DHAN_CLIENT_ID"]
|
|
134
|
+
config.access_token_provider = -> { MyTokenStore.fetch_current_token }
|
|
135
|
+
config.on_token_expired = ->(error) { MyTokenStore.refresh!(error) }
|
|
136
|
+
end
|
|
137
|
+
```
|
|
125
138
|
Behavior:
|
|
126
139
|
On 401 (auth failure), client calls on_token_expired, then retries once using a fresh token from access_token_provider.
|
|
140
|
+
|
|
127
141
|
3️⃣ Fetch-from-token-endpoint (configure_from_token_endpoint)
|
|
128
142
|
What: Gem calls your HTTP endpoint once to get access_token + client_id.
|
|
129
143
|
How:
|
|
130
|
-
|
|
144
|
+
```ruby
|
|
145
|
+
DhanHQ.configure_from_token_endpoint(
|
|
146
|
+
base_url: "https://myapp.com",
|
|
147
|
+
bearer_token: ENV["DHAN_TOKEN_ENDPOINT_BEARER"]
|
|
148
|
+
)
|
|
149
|
+
# expects JSON: { access_token: "...", client_id: "...", base_url: "..." (optional) }
|
|
150
|
+
```
|
|
131
151
|
When: Multi-tenant or central credential service, you don’t want tokens in ENV directly.
|
|
152
|
+
|
|
132
153
|
4️⃣ TOTP-based token generation (new DhanHQ::Auth flow)
|
|
133
154
|
Module API (low-level)
|
|
134
155
|
What: Direct call to Dhan’s generateAccessToken endpoint using TOTP.
|
|
135
|
-
|
|
156
|
+
```ruby
|
|
157
|
+
totp = DhanHQ::Auth.generate_totp(ENV["DHAN_TOTP_SECRET"])
|
|
158
|
+
response = DhanHQ::Auth.generate_access_token(
|
|
159
|
+
dhan_client_id: ENV["DHAN_CLIENT_ID"],
|
|
160
|
+
pin: ENV["DHAN_PIN"],
|
|
161
|
+
totp: totp
|
|
162
|
+
)
|
|
163
|
+
token = response["accessToken"]
|
|
164
|
+
expiry = response["expiryTime"]
|
|
165
|
+
```
|
|
166
|
+
|
|
136
167
|
Client API (high-level, returns TokenResponse)
|
|
137
|
-
|
|
168
|
+
```
|
|
169
|
+
client = DhanHQ::Client.new(api_type: :order_api)
|
|
170
|
+
token = client.generate_access_token(
|
|
171
|
+
dhan_client_id: ENV["DHAN_CLIENT_ID"],
|
|
172
|
+
pin: ENV["DHAN_PIN"],
|
|
173
|
+
totp_secret: ENV["DHAN_TOTP_SECRET"] # or `totp:` if you computed it
|
|
174
|
+
)
|
|
175
|
+
# auto-applies token + client_id to `DhanHQ.configuration`
|
|
176
|
+
```
|
|
177
|
+
|
|
138
178
|
When: Fully automated individual setup (no manual web token generation).
|
|
179
|
+
|
|
139
180
|
5️⃣ Auto token lifecycle management (TokenManager)
|
|
140
181
|
What: Gem handles generate + renew + retry around every API call.
|
|
141
182
|
How:
|
|
142
|
-
|
|
183
|
+
```ruby
|
|
184
|
+
client = DhanHQ::Client.new(api_type: :order_api)
|
|
185
|
+
client.enable_auto_token_management!(
|
|
186
|
+
dhan_client_id: ENV["DHAN_CLIENT_ID"],
|
|
187
|
+
pin: ENV["DHAN_PIN"],
|
|
188
|
+
totp_secret: ENV["DHAN_TOTP_SECRET"]
|
|
189
|
+
)
|
|
190
|
+
# From now on, `client.request` auto-ensures a valid token.
|
|
191
|
+
```
|
|
143
192
|
Behavior:
|
|
144
193
|
On first use: calls TOTP TokenGenerator → applies token.
|
|
145
194
|
Before each request: ensure_valid_token!:
|
|
146
195
|
If no token → generate.
|
|
147
196
|
If needs_refresh? → TokenRenewal (POST /v2/RenewToken) with current token + dhanClientId.
|
|
148
197
|
If renewal fails with auth error → falls back to full generate.
|
|
198
|
+
|
|
149
199
|
6️⃣ Web-token renewal only (RenewToken)
|
|
150
200
|
Module API:
|
|
151
|
-
|
|
201
|
+
```ruby
|
|
202
|
+
response = DhanHQ::Auth.renew_token(
|
|
203
|
+
access_token: current_token,
|
|
204
|
+
client_id: ENV["DHAN_CLIENT_ID"]
|
|
205
|
+
)
|
|
206
|
+
```
|
|
152
207
|
Client / manager (high-level):
|
|
153
|
-
|
|
154
|
-
|
|
208
|
+
|
|
209
|
+
`client.renew_access_token` (returns TokenResponse, updates config).
|
|
210
|
+
`TokenManager#refresh!` internally uses Auth::TokenRenewal.
|
|
155
211
|
When: You’re using web-generated 24h tokens and want to extend them without switching to TOTP.
|
|
212
|
+
|
|
156
213
|
TL;DR
|
|
157
214
|
Manual: Static token (access_token)
|
|
215
|
+
|
|
158
216
|
Dynamic: access_token_provider (+ optional on_token_expired)
|
|
217
|
+
|
|
159
218
|
Central service: configure_from_token_endpoint
|
|
219
|
+
|
|
160
220
|
Fully automated: TOTP generate (Auth / Client#generate_access_token)
|
|
221
|
+
|
|
161
222
|
Production-grade automation: enable_auto_token_management! (generate + renew)
|
|
223
|
+
|
|
162
224
|
If you tell me your exact deployment style (single user box, multi-user SaaS, on-prem, etc.), I can tell you which one you should actually use and what to delete as overkill.
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
# Configuration Reference
|
|
2
|
+
|
|
3
|
+
This document covers all configuration options for the DhanHQ Ruby client.
|
|
4
|
+
|
|
5
|
+
## Quick Setup
|
|
6
|
+
|
|
7
|
+
```ruby
|
|
8
|
+
require 'dhan_hq'
|
|
9
|
+
DhanHQ.configure_with_env
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
`configure_with_env` reads `DHAN_CLIENT_ID` and `DHAN_ACCESS_TOKEN` from `ENV` and raises if either is missing.
|
|
13
|
+
|
|
14
|
+
## Required Environment Variables
|
|
15
|
+
|
|
16
|
+
| Variable | Purpose |
|
|
17
|
+
| -------------------- | ------------------------------------------------- |
|
|
18
|
+
| `DHAN_CLIENT_ID` | Trading account client ID issued by Dhan |
|
|
19
|
+
| `DHAN_ACCESS_TOKEN` | API access token generated from the Dhan console |
|
|
20
|
+
|
|
21
|
+
## Optional Environment Variables
|
|
22
|
+
|
|
23
|
+
Set these _before_ calling `configure_with_env` to override defaults:
|
|
24
|
+
|
|
25
|
+
| Variable | Default | Description |
|
|
26
|
+
| -------------------------------- | ---------- | ------------------------------------------------------- |
|
|
27
|
+
| `DHAN_LOG_LEVEL` | `INFO` | Logger verbosity (`DEBUG`, `INFO`, `WARN`, `ERROR`) |
|
|
28
|
+
| `DHAN_BASE_URL` | Dhan prod | Point REST calls to a different API hostname |
|
|
29
|
+
| `DHAN_WS_VERSION` | latest | Pin WebSocket connections to a specific API version |
|
|
30
|
+
| `DHAN_WS_ORDER_URL` | Dhan prod | Override the order update WebSocket endpoint |
|
|
31
|
+
| `DHAN_WS_USER_TYPE` | `SELF` | Switch between `SELF` and `PARTNER` streaming modes |
|
|
32
|
+
| `DHAN_PARTNER_ID` | — | Required when `DHAN_WS_USER_TYPE=PARTNER` |
|
|
33
|
+
| `DHAN_PARTNER_SECRET` | — | Required when `DHAN_WS_USER_TYPE=PARTNER` |
|
|
34
|
+
| `DHAN_CONNECT_TIMEOUT` | `10` | Connection timeout in seconds |
|
|
35
|
+
| `DHAN_READ_TIMEOUT` | `30` | Read timeout in seconds |
|
|
36
|
+
| `DHAN_WRITE_TIMEOUT` | `30` | Write timeout in seconds |
|
|
37
|
+
| `DHAN_WS_MAX_TRACKED_ORDERS` | `10000` | Maximum orders to track in WebSocket |
|
|
38
|
+
| `DHAN_WS_MAX_ORDER_AGE` | `604800` | Maximum order age in seconds before cleanup (7 days) |
|
|
39
|
+
|
|
40
|
+
## `.env` File Setup
|
|
41
|
+
|
|
42
|
+
Create a `.env` file in your project root:
|
|
43
|
+
|
|
44
|
+
```dotenv
|
|
45
|
+
DHAN_CLIENT_ID=your_client_id
|
|
46
|
+
DHAN_ACCESS_TOKEN=your_access_token
|
|
47
|
+
|
|
48
|
+
# Optional overrides
|
|
49
|
+
DHAN_LOG_LEVEL=DEBUG
|
|
50
|
+
DHAN_CONNECT_TIMEOUT=15
|
|
51
|
+
DHAN_READ_TIMEOUT=60
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
The gem requires `dotenv/load`, so these variables are loaded automatically when you require `dhan_hq`.
|
|
55
|
+
|
|
56
|
+
## Block-Style Configuration
|
|
57
|
+
|
|
58
|
+
```ruby
|
|
59
|
+
DhanHQ.configure do |config|
|
|
60
|
+
config.client_id = ENV["DHAN_CLIENT_ID"]
|
|
61
|
+
config.access_token = ENV["DHAN_ACCESS_TOKEN"]
|
|
62
|
+
end
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
## Logging
|
|
66
|
+
|
|
67
|
+
```ruby
|
|
68
|
+
DhanHQ.logger.level = (ENV["DHAN_LOG_LEVEL"] || "INFO").upcase.then { |level| Logger.const_get(level) }
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
Set `DHAN_LOG_LEVEL=DEBUG` for full HTTP request/response and WebSocket frame logging during development.
|
|
72
|
+
|
|
73
|
+
## Dynamic Access Token
|
|
74
|
+
|
|
75
|
+
For production or OAuth-style flows, resolve the token at **request time**:
|
|
76
|
+
|
|
77
|
+
```ruby
|
|
78
|
+
DhanHQ.configure do |config|
|
|
79
|
+
config.client_id = ENV["DHAN_CLIENT_ID"]
|
|
80
|
+
config.access_token_provider = lambda do
|
|
81
|
+
token = YourTokenStore.active_token # e.g. from DB or OAuth
|
|
82
|
+
raise "Token expired or missing" unless token
|
|
83
|
+
token
|
|
84
|
+
end
|
|
85
|
+
# Optional: called when the API returns 401/token-expired
|
|
86
|
+
config.on_token_expired = ->(error) { YourTokenStore.refresh! }
|
|
87
|
+
end
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
- **`access_token_provider`**: Callable (Proc/lambda) returning the token string. Called on every request (no memoization). When set, the gem uses it instead of `access_token`.
|
|
91
|
+
- **`on_token_expired`**: Optional callable invoked when a 401/token-expired triggers a **single retry** (only when `access_token_provider` is set).
|
|
92
|
+
|
|
93
|
+
For detailed authentication flows, see [AUTHENTICATION.md](AUTHENTICATION.md).
|
|
94
|
+
|
|
95
|
+
## Available Resources
|
|
96
|
+
|
|
97
|
+
| Resource | Model | Actions |
|
|
98
|
+
| ------------------------ | -------------------------------------- | --------------------------------------------------- |
|
|
99
|
+
| Orders | `DhanHQ::Models::Order` | `find`, `all`, `where`, `place`, `update`, `cancel` |
|
|
100
|
+
| Trades | `DhanHQ::Models::Trade` | `all`, `find_by_order_id` |
|
|
101
|
+
| Forever Orders | `DhanHQ::Models::ForeverOrder` | `create`, `find`, `modify`, `cancel`, `all` |
|
|
102
|
+
| Holdings | `DhanHQ::Models::Holding` | `all` |
|
|
103
|
+
| Positions | `DhanHQ::Models::Position` | `all`, `find`, `exit!` |
|
|
104
|
+
| Funds & Margin | `DhanHQ::Models::Fund` | `fund_limit`, `margin_calculator` |
|
|
105
|
+
| Ledger | `DhanHQ::Models::Ledger` | `all` |
|
|
106
|
+
| Market Feeds | `DhanHQ::Models::MarketFeed` | `ltp`, `ohlc`, `quote` |
|
|
107
|
+
| Historical Data (Charts) | `DhanHQ::Models::HistoricalData` | `daily`, `intraday` |
|
|
108
|
+
| Option Chain | `DhanHQ::Models::OptionChain` | `fetch`, `fetch_expiry_list` |
|
|
109
|
+
| Super Orders | `DhanHQ::Models::SuperOrder` | `create`, `modify`, `cancel`, `all` |
|
|
@@ -0,0 +1,284 @@
|
|
|
1
|
+
# Super Orders — Full API Reference
|
|
2
|
+
|
|
3
|
+
Super orders are built for smart execution. They club the entry, target, and stop-loss legs (with optional trailing jump) into a single request so you can manage risk immediately after entry.
|
|
4
|
+
|
|
5
|
+
This gem exposes the full REST surface to create, modify, cancel, and list super orders across all supported exchanges and segments.
|
|
6
|
+
|
|
7
|
+
## Endpoints
|
|
8
|
+
|
|
9
|
+
| Method | Path | Description |
|
|
10
|
+
| -------- | -------------------------------------- | ------------------------------------- |
|
|
11
|
+
| `POST` | `/super/orders` | Create a new super order |
|
|
12
|
+
| `PUT` | `/super/orders/{order_id}` | Modify a pending super order |
|
|
13
|
+
| `DELETE` | `/super/orders/{order_id}/{order_leg}` | Cancel a pending super order leg |
|
|
14
|
+
| `GET` | `/super/orders` | Retrieve the list of all super orders |
|
|
15
|
+
|
|
16
|
+
---
|
|
17
|
+
|
|
18
|
+
## Place Super Order
|
|
19
|
+
|
|
20
|
+
The place endpoint lets you submit a new super order that can include entry, target, stop-loss, and optional trailing jump definitions. It is available across exchanges and segments, and supports intraday, carry-forward, or MTF orders.
|
|
21
|
+
|
|
22
|
+
> ℹ️ Static IP whitelisting with Dhan support is required before invoking this API.
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
curl --request POST \
|
|
26
|
+
--url https://api.dhan.co/v2/super/orders \
|
|
27
|
+
--header 'Content-Type: application/json' \
|
|
28
|
+
--header 'access-token: JWT' \
|
|
29
|
+
--data '{Request JSON}'
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
### Request body
|
|
33
|
+
|
|
34
|
+
```json
|
|
35
|
+
{
|
|
36
|
+
"dhan_client_id": "1000000003",
|
|
37
|
+
"correlation_id": "123abc678",
|
|
38
|
+
"transaction_type": "BUY",
|
|
39
|
+
"exchange_segment": "NSE_EQ",
|
|
40
|
+
"product_type": "CNC",
|
|
41
|
+
"order_type": "LIMIT",
|
|
42
|
+
"security_id": "11536",
|
|
43
|
+
"quantity": 5,
|
|
44
|
+
"price": 1500,
|
|
45
|
+
"target_price": 1600,
|
|
46
|
+
"stop_loss_price": 1400,
|
|
47
|
+
"trailing_jump": 10
|
|
48
|
+
}
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
### Parameters
|
|
52
|
+
|
|
53
|
+
| Field | Type | Description |
|
|
54
|
+
| ------------------ | ------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
55
|
+
| `dhan_client_id` | string *(required)* | User specific identification generated by Dhan. When you call through `DhanHQ::Models::SuperOrder`, the gem injects your configured client id so you can omit this key locally. |
|
|
56
|
+
| `correlation_id` | string | Caller generated correlation identifier |
|
|
57
|
+
| `transaction_type` | enum string *(required)* | Trading side. `BUY` or `SELL`. |
|
|
58
|
+
| `exchange_segment` | enum string *(required)* | Exchange segment (see appendix). |
|
|
59
|
+
| `product_type` | enum string *(required)* | Product type. `CNC`, `INTRADAY`, `MARGIN`, or `MTF`. |
|
|
60
|
+
| `order_type` | enum string *(required)* | Order type. `LIMIT` or `MARKET`. |
|
|
61
|
+
| `security_id` | string *(required)* | Exchange standard security identifier. |
|
|
62
|
+
| `quantity` | integer *(required)* | Number of shares for the order. |
|
|
63
|
+
| `price` | float *(required)* | Price at which the entry leg is placed. |
|
|
64
|
+
| `target_price` | float *(required)* | Target price for the super order. |
|
|
65
|
+
| `stop_loss_price` | float *(required)* | Stop-loss price for the super order. |
|
|
66
|
+
| `trailing_jump` | float *(required)* | Price jump size used to trail the stop-loss. |
|
|
67
|
+
|
|
68
|
+
> 🐍 When you call `DhanHQ::Models::SuperOrder.create`, pass snake_case keys as shown above. The client automatically camelizes
|
|
69
|
+
> them before posting to Dhan's REST API and injects your configured `dhan_client_id`, so you can omit that key in Ruby code.
|
|
70
|
+
|
|
71
|
+
### Response
|
|
72
|
+
|
|
73
|
+
```json
|
|
74
|
+
{
|
|
75
|
+
"order_id": "112111182198",
|
|
76
|
+
"order_status": "PENDING"
|
|
77
|
+
}
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
| Field | Type | Description |
|
|
81
|
+
| -------------- | ----------- | --------------------------------------------------- |
|
|
82
|
+
| `order_id` | string | Order identifier generated by Dhan |
|
|
83
|
+
| `order_status` | enum string | Latest status. `TRANSIT`, `PENDING`, or `REJECTED`. |
|
|
84
|
+
|
|
85
|
+
---
|
|
86
|
+
|
|
87
|
+
## Modify Super Order
|
|
88
|
+
|
|
89
|
+
Use the modify endpoint to update any leg while the super order remains in `PENDING` or `PART_TRADED` status.
|
|
90
|
+
|
|
91
|
+
> ℹ️ Static IP whitelisting with Dhan support is required before invoking this API.
|
|
92
|
+
|
|
93
|
+
```bash
|
|
94
|
+
curl --request PUT \
|
|
95
|
+
--url https://api.dhan.co/v2/super/orders/{order_id} \
|
|
96
|
+
--header 'Content-Type: application/json' \
|
|
97
|
+
--header 'access-token: JWT' \
|
|
98
|
+
--data '{Request JSON}'
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
### Request body
|
|
102
|
+
|
|
103
|
+
```json
|
|
104
|
+
{
|
|
105
|
+
"dhan_client_id": "1000000009",
|
|
106
|
+
"order_id": "112111182045",
|
|
107
|
+
"order_type": "LIMIT",
|
|
108
|
+
"leg_name": "ENTRY_LEG",
|
|
109
|
+
"quantity": 40,
|
|
110
|
+
"price": 1300,
|
|
111
|
+
"target_price": 1450,
|
|
112
|
+
"stop_loss_price": 1350,
|
|
113
|
+
"trailing_jump": 20
|
|
114
|
+
}
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
### Parameters
|
|
118
|
+
|
|
119
|
+
| Field | Type | Description |
|
|
120
|
+
| ----------------- | -------------------------------------- | ------------------------------------------------------------------------------------------------------------------------- |
|
|
121
|
+
| `dhan_client_id` | string *(required)* | User specific identification generated by Dhan. Automatically added when you call through the Ruby models. |
|
|
122
|
+
| `order_id` | string *(required)* | Super order identifier generated by Dhan. |
|
|
123
|
+
| `order_type` | enum string *(conditionally required)* | `LIMIT` or `MARKET`. Required when modifying `ENTRY_LEG`. |
|
|
124
|
+
| `leg_name` | enum string *(required)* | `ENTRY_LEG`, `TARGET_LEG`, or `STOP_LOSS_LEG`. Entry leg updates entire order while status is `PENDING` or `PART_TRADED`. |
|
|
125
|
+
| `quantity` | integer *(conditionally required)* | Quantity update for `ENTRY_LEG`. |
|
|
126
|
+
| `price` | float *(conditionally required)* | Entry price update for `ENTRY_LEG`. |
|
|
127
|
+
| `target_price` | float *(conditionally required)* | Target price update for `ENTRY_LEG` or `TARGET_LEG`. |
|
|
128
|
+
| `stop_loss_price` | float *(conditionally required)* | Stop-loss price update for `ENTRY_LEG` or `STOP_LOSS_LEG`. |
|
|
129
|
+
| `trailing_jump` | float *(conditionally required)* | Trailing jump update for `ENTRY_LEG` or `STOP_LOSS_LEG`. Omit or set to `0` to cancel trailing. |
|
|
130
|
+
|
|
131
|
+
> ℹ️ Once the entry leg status becomes `TRADED`, only the `TARGET_LEG` and `STOP_LOSS_LEG` can be modified (price and trailing jump).
|
|
132
|
+
|
|
133
|
+
### Response
|
|
134
|
+
|
|
135
|
+
```json
|
|
136
|
+
{
|
|
137
|
+
"order_id": "112111182045",
|
|
138
|
+
"order_status": "TRANSIT"
|
|
139
|
+
}
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
| Field | Type | Description |
|
|
143
|
+
| -------------- | ----------- | ------------------------------------------------------------- |
|
|
144
|
+
| `order_id` | string | Order identifier generated by Dhan |
|
|
145
|
+
| `order_status` | enum string | Latest status. `TRANSIT`, `PENDING`, `REJECTED`, or `TRADED`. |
|
|
146
|
+
|
|
147
|
+
---
|
|
148
|
+
|
|
149
|
+
## Cancel Super Order
|
|
150
|
+
|
|
151
|
+
Cancel a pending or active super order leg using the order ID. Cancelling the entry leg removes every leg. Cancelling a specific target or stop-loss leg removes only that leg and it cannot be re-added.
|
|
152
|
+
|
|
153
|
+
> ℹ️ Static IP whitelisting with Dhan support is required before invoking this API.
|
|
154
|
+
|
|
155
|
+
```bash
|
|
156
|
+
curl --request DELETE \
|
|
157
|
+
--url https://api.dhan.co/v2/super/orders/{order_id}/{order_leg} \
|
|
158
|
+
--header 'Content-Type: application/json' \
|
|
159
|
+
--header 'access-token: JWT'
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
### Path parameters
|
|
163
|
+
|
|
164
|
+
| Field | Description | Example |
|
|
165
|
+
| ----------- | ------------------------------------------------------------- | ------------- |
|
|
166
|
+
| `order_id` | Super order identifier. | `11211182198` |
|
|
167
|
+
| `order_leg` | Leg to cancel. `ENTRY_LEG`, `TARGET_LEG`, or `STOP_LOSS_LEG`. | `ENTRY_LEG` |
|
|
168
|
+
|
|
169
|
+
### Response
|
|
170
|
+
|
|
171
|
+
```json
|
|
172
|
+
{
|
|
173
|
+
"order_id": "112111182045",
|
|
174
|
+
"order_status": "CANCELLED"
|
|
175
|
+
}
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
| Field | Type | Description |
|
|
179
|
+
| -------------- | ----------- | ---------------------------------------------------------------- |
|
|
180
|
+
| `order_id` | string | Order identifier generated by Dhan |
|
|
181
|
+
| `order_status` | enum string | Latest status. `TRANSIT`, `PENDING`, `REJECTED`, or `CANCELLED`. |
|
|
182
|
+
|
|
183
|
+
---
|
|
184
|
+
|
|
185
|
+
## Super Order List
|
|
186
|
+
|
|
187
|
+
List every super order placed during the trading day. The API nests leg details under the entry leg, and individual legs also appear in the main order book.
|
|
188
|
+
|
|
189
|
+
```bash
|
|
190
|
+
curl --request GET \
|
|
191
|
+
--url https://api.dhan.co/v2/super/orders \
|
|
192
|
+
--header 'Content-Type: application/json' \
|
|
193
|
+
--header 'access-token: JWT'
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
### Response
|
|
197
|
+
|
|
198
|
+
```json
|
|
199
|
+
[
|
|
200
|
+
{
|
|
201
|
+
"dhan_client_id": "1100003626",
|
|
202
|
+
"order_id": "5925022734212",
|
|
203
|
+
"correlation_id": "string",
|
|
204
|
+
"order_status": "PENDING",
|
|
205
|
+
"transaction_type": "BUY",
|
|
206
|
+
"exchange_segment": "NSE_EQ",
|
|
207
|
+
"product_type": "CNC",
|
|
208
|
+
"order_type": "LIMIT",
|
|
209
|
+
"validity": "DAY",
|
|
210
|
+
"trading_symbol": "HDFCBANK",
|
|
211
|
+
"security_id": "1333",
|
|
212
|
+
"quantity": 10,
|
|
213
|
+
"remaining_quantity": 10,
|
|
214
|
+
"ltp": 1660.95,
|
|
215
|
+
"price": 1500,
|
|
216
|
+
"after_market_order": false,
|
|
217
|
+
"leg_name": "ENTRY_LEG",
|
|
218
|
+
"exchange_order_id": "11925022734212",
|
|
219
|
+
"create_time": "2025-02-27 19:09:42",
|
|
220
|
+
"update_time": "2025-02-27 19:09:42",
|
|
221
|
+
"exchange_time": "2025-02-27 19:09:42",
|
|
222
|
+
"oms_error_description": "",
|
|
223
|
+
"average_traded_price": 0,
|
|
224
|
+
"filled_qty": 0,
|
|
225
|
+
"leg_details": [
|
|
226
|
+
{
|
|
227
|
+
"order_id": "5925022734212",
|
|
228
|
+
"leg_name": "STOP_LOSS_LEG",
|
|
229
|
+
"transaction_type": "SELL",
|
|
230
|
+
"total_quantity": 0,
|
|
231
|
+
"remaining_quantity": 0,
|
|
232
|
+
"triggered_quantity": 0,
|
|
233
|
+
"price": 1400,
|
|
234
|
+
"order_status": "PENDING",
|
|
235
|
+
"trailing_jump": 10
|
|
236
|
+
},
|
|
237
|
+
{
|
|
238
|
+
"order_id": "5925022734212",
|
|
239
|
+
"leg_name": "TARGET_LEG",
|
|
240
|
+
"transaction_type": "SELL",
|
|
241
|
+
"remaining_quantity": 0,
|
|
242
|
+
"triggered_quantity": 0,
|
|
243
|
+
"price": 1550,
|
|
244
|
+
"order_status": "PENDING",
|
|
245
|
+
"trailing_jump": 0
|
|
246
|
+
}
|
|
247
|
+
]
|
|
248
|
+
}
|
|
249
|
+
]
|
|
250
|
+
```
|
|
251
|
+
|
|
252
|
+
### Response Parameters
|
|
253
|
+
|
|
254
|
+
| Field | Type | Description |
|
|
255
|
+
| ----------------------- | ----------- | --------------------------------------------------------------------------------------------------- |
|
|
256
|
+
| `dhan_client_id` | string | User specific identification generated by Dhan. |
|
|
257
|
+
| `order_id` | string | Order identifier generated by Dhan. |
|
|
258
|
+
| `correlation_id` | string | Correlation identifier supplied by the caller. |
|
|
259
|
+
| `order_status` | enum string | Latest status. `TRANSIT`, `PENDING`, `CLOSED`, `REJECTED`, `CANCELLED`, `PART_TRADED`, or `TRADED`. |
|
|
260
|
+
| `transaction_type` | enum string | Trading side. `BUY` or `SELL`. |
|
|
261
|
+
| `exchange_segment` | enum string | Exchange segment. |
|
|
262
|
+
| `product_type` | enum string | Product type. `CNC`, `INTRADAY`, `MARGIN`, or `MTF`. |
|
|
263
|
+
| `order_type` | enum string | Order type. `LIMIT` or `MARKET`. |
|
|
264
|
+
| `validity` | enum string | Order validity. `DAY`. |
|
|
265
|
+
| `trading_symbol` | string | Trading symbol reference. |
|
|
266
|
+
| `security_id` | string | Exchange security identifier. |
|
|
267
|
+
| `quantity` | integer | Ordered quantity. |
|
|
268
|
+
| `remaining_quantity` | integer | Quantity pending execution. |
|
|
269
|
+
| `ltp` | float | Last traded price. |
|
|
270
|
+
| `price` | float | Order price. |
|
|
271
|
+
| `after_market_order` | boolean | Indicates if the order was placed after market hours. |
|
|
272
|
+
| `leg_name` | enum string | Leg identifier: `ENTRY_LEG`, `TARGET_LEG`, or `STOP_LOSS_LEG`. |
|
|
273
|
+
| `trailing_jump` | float | Trailing jump for stop-loss. |
|
|
274
|
+
| `exchange_order_id` | string | Exchange-generated order identifier. |
|
|
275
|
+
| `create_time` | string | Order creation timestamp. |
|
|
276
|
+
| `update_time` | string | Latest update timestamp. |
|
|
277
|
+
| `exchange_time` | string | Exchange timestamp. |
|
|
278
|
+
| `oms_error_description` | string | OMS error description when applicable. |
|
|
279
|
+
| `average_traded_price` | float | Average traded price. |
|
|
280
|
+
| `filled_qty` | integer | Quantity traded on the exchange. |
|
|
281
|
+
| `triggered_quantity` | integer | Quantity triggered for stop-loss or target legs. |
|
|
282
|
+
| `leg_details` | array | Nested leg details for the super order. |
|
|
283
|
+
|
|
284
|
+
> ✅ `CLOSED` indicates the entry leg plus either target or stop-loss leg completed for the entire quantity. `TRIGGERED` appears on target and stop-loss legs to show which leg fired; inspect `triggered_quantity` for the executed quantity.
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
# Troubleshooting
|
|
2
|
+
|
|
3
|
+
Common issues and solutions when working with the DhanHQ Ruby client.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## 429: Unexpected Response Code
|
|
8
|
+
|
|
9
|
+
**Symptom:** WebSocket connection fails with a 429 status.
|
|
10
|
+
|
|
11
|
+
**Cause:** Too many connections opened in quick succession, or exceeding the per-user WebSocket connection limit (5 per user).
|
|
12
|
+
|
|
13
|
+
**Solution:**
|
|
14
|
+
- The client automatically cools off for **60 seconds** and retries with exponential backoff.
|
|
15
|
+
- Prefer `ws.disconnect!` before reconnecting to cleanly release server-side resources.
|
|
16
|
+
- Call `DhanHQ::WS.disconnect_all_local!` to kill any straggler connections.
|
|
17
|
+
- Avoid rapid connect/disconnect loops — the client handles backoff internally.
|
|
18
|
+
|
|
19
|
+
```ruby
|
|
20
|
+
# Kill all local WebSocket connections
|
|
21
|
+
DhanHQ::WS.disconnect_all_local!
|
|
22
|
+
|
|
23
|
+
# Wait before reconnecting
|
|
24
|
+
sleep(2)
|
|
25
|
+
|
|
26
|
+
# Reconnect
|
|
27
|
+
client = DhanHQ::WS.connect(mode: :ticker) { |tick| puts tick[:ltp] }
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
---
|
|
31
|
+
|
|
32
|
+
## No Ticks After Reconnect
|
|
33
|
+
|
|
34
|
+
**Symptom:** WebSocket reconnects successfully but no market data arrives.
|
|
35
|
+
|
|
36
|
+
**Cause:** Subscriptions were not restored after the connection dropped.
|
|
37
|
+
|
|
38
|
+
**Solution:**
|
|
39
|
+
- The client **automatically resends** the current subscription snapshot on reconnect — this should work transparently.
|
|
40
|
+
- If you're managing connections manually, ensure you re-subscribe after a clean start.
|
|
41
|
+
- Check that your instruments are valid and the market is open.
|
|
42
|
+
|
|
43
|
+
---
|
|
44
|
+
|
|
45
|
+
## Binary Parse Errors
|
|
46
|
+
|
|
47
|
+
**Symptom:** Errors in logs related to binary frame parsing.
|
|
48
|
+
|
|
49
|
+
**Cause:** Malformed or unexpected binary frames from the server.
|
|
50
|
+
|
|
51
|
+
**Solution:**
|
|
52
|
+
- The client safely drops malformed frames and keeps the event loop alive.
|
|
53
|
+
- Run with `DHAN_LOG_LEVEL=DEBUG` to inspect raw frames:
|
|
54
|
+
|
|
55
|
+
```bash
|
|
56
|
+
export DHAN_LOG_LEVEL=DEBUG
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
```ruby
|
|
60
|
+
DhanHQ.logger.level = Logger::DEBUG
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
---
|
|
64
|
+
|
|
65
|
+
## Authentication Errors
|
|
66
|
+
|
|
67
|
+
| Error Class | Meaning |
|
|
68
|
+
| ------------------------------------ | ---------------------------------------------------------- |
|
|
69
|
+
| `DhanHQ::AuthenticationError` | Token could not be resolved (missing config, nil provider) |
|
|
70
|
+
| `DhanHQ::InvalidAuthenticationError` | API returned 401 or error code DH-901 |
|
|
71
|
+
| `DhanHQ::TokenExpiredError` | API returned error code 807 (token expired) |
|
|
72
|
+
| `DhanHQ::InvalidTokenError` | API returned error code 809 (invalid token) |
|
|
73
|
+
|
|
74
|
+
**Solutions:**
|
|
75
|
+
- Verify `DHAN_CLIENT_ID` and `DHAN_ACCESS_TOKEN` are set correctly.
|
|
76
|
+
- If using `access_token_provider`, ensure it returns a non-nil string.
|
|
77
|
+
- For 401 retries: the client retries **once** with a fresh token when `access_token_provider` is configured.
|
|
78
|
+
- See [AUTHENTICATION.md](AUTHENTICATION.md) for detailed token lifecycle handling.
|
|
79
|
+
|
|
80
|
+
---
|
|
81
|
+
|
|
82
|
+
## Connection Timeouts
|
|
83
|
+
|
|
84
|
+
**Symptom:** REST API calls hang or fail with timeout errors.
|
|
85
|
+
|
|
86
|
+
**Solution:** Adjust timeout settings via environment variables:
|
|
87
|
+
|
|
88
|
+
```dotenv
|
|
89
|
+
DHAN_CONNECT_TIMEOUT=15 # default: 10 seconds
|
|
90
|
+
DHAN_READ_TIMEOUT=60 # default: 30 seconds
|
|
91
|
+
DHAN_WRITE_TIMEOUT=60 # default: 30 seconds
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
---
|
|
95
|
+
|
|
96
|
+
## Debug Logging
|
|
97
|
+
|
|
98
|
+
Enable full debug output to diagnose any issue:
|
|
99
|
+
|
|
100
|
+
```ruby
|
|
101
|
+
DhanHQ.logger.level = Logger::DEBUG
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
Or via environment:
|
|
105
|
+
|
|
106
|
+
```bash
|
|
107
|
+
export DHAN_LOG_LEVEL=DEBUG
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
This logs HTTP requests/responses, WebSocket frames, and internal state transitions.
|
|
111
|
+
|
|
112
|
+
---
|
|
113
|
+
|
|
114
|
+
## Getting Help
|
|
115
|
+
|
|
116
|
+
- [DhanHQ GitHub Issues](https://github.com/shubhamtaywade82/dhanhq-client/issues)
|
|
117
|
+
- [Dhan API Documentation](https://dhanhq.co/docs/v2/)
|