DhanHQ 2.3.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.
Files changed (52) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +50 -1
  3. data/CODE_REVIEW_ISSUES.md +2 -2
  4. data/GUIDE.md +2 -2
  5. data/README.md +194 -741
  6. data/REVIEW_SUMMARY.md +2 -2
  7. data/{README1.md → docs/ARCHIVE_README.md} +4 -4
  8. data/docs/AUTHENTICATION.md +116 -2
  9. data/docs/CONFIGURATION.md +109 -0
  10. data/docs/SUPER_ORDERS.md +284 -0
  11. data/docs/TESTING_GUIDE.md +8 -8
  12. data/docs/TROUBLESHOOTING.md +117 -0
  13. data/docs/WEBSOCKET_PROTOCOL.md +154 -0
  14. data/docs/live_order_updates.md +2 -2
  15. data/docs/rails_integration.md +7 -7
  16. data/docs/standalone_ruby_websocket_integration.md +24 -24
  17. data/docs/technical_analysis.md +1 -1
  18. data/docs/websocket_integration.md +4 -4
  19. data/examples/comprehensive_websocket_examples.rb +2 -2
  20. data/examples/instrument_finder_test.rb +2 -2
  21. data/examples/market_depth_example.rb +2 -2
  22. data/examples/market_feed_example.rb +2 -2
  23. data/examples/order_update_example.rb +2 -2
  24. data/examples/trading_fields_example.rb +2 -2
  25. data/lib/DhanHQ/auth/token_generator.rb +33 -0
  26. data/lib/DhanHQ/auth/token_manager.rb +88 -0
  27. data/lib/DhanHQ/auth/token_renewal.rb +25 -0
  28. data/lib/DhanHQ/auth.rb +91 -31
  29. data/lib/DhanHQ/client.rb +42 -2
  30. data/lib/DhanHQ/configuration.rb +2 -2
  31. data/lib/DhanHQ/contracts/order_contract.rb +0 -23
  32. data/lib/DhanHQ/contracts/trade_by_order_id_contract.rb +12 -0
  33. data/lib/DhanHQ/contracts/trade_contract.rb +0 -65
  34. data/lib/DhanHQ/contracts/trade_history_contract.rb +52 -0
  35. data/lib/DhanHQ/core/auth_api.rb +21 -0
  36. data/lib/DhanHQ/helpers/request_helper.rb +1 -1
  37. data/lib/DhanHQ/models/alert_order.rb +22 -0
  38. data/lib/DhanHQ/models/edis.rb +110 -0
  39. data/lib/DhanHQ/models/kill_switch.rb +22 -0
  40. data/lib/DhanHQ/models/margin.rb +49 -0
  41. data/lib/DhanHQ/models/pnl_exit.rb +130 -0
  42. data/lib/DhanHQ/models/position.rb +22 -0
  43. data/lib/DhanHQ/models/postback.rb +123 -0
  44. data/lib/DhanHQ/models/token_response.rb +88 -0
  45. data/lib/DhanHQ/resources/kill_switch.rb +8 -0
  46. data/lib/DhanHQ/resources/margin_calculator.rb +9 -0
  47. data/lib/DhanHQ/resources/pnl_exit.rb +37 -0
  48. data/lib/DhanHQ/resources/positions.rb +8 -0
  49. data/lib/DhanHQ/version.rb +1 -1
  50. data/lib/dhan_hq.rb +31 -81
  51. metadata +46 -4
  52. data/lib/DhanHQ/config.rb +0 -33
data/REVIEW_SUMMARY.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # DhanHQ Client Gem - Review Summary
2
2
 
3
- **API Documentation**: https://api.dhan.co/v2/#/
3
+ **API Documentation**: https://api.dhan.co/v2/#/
4
4
  **Review Date**: 2025-01-27
5
5
 
6
6
  ## 🚨 Top 10 Critical Issues
@@ -15,7 +15,7 @@
15
15
  - **File**: `lib/DhanHQ/client.rb:40`
16
16
  - **Issue**: Partial configuration can lead to runtime authentication failures
17
17
  - **Impact**: Silent failures, difficult debugging
18
- - **Fix**: Validate both `CLIENT_ID` and `ACCESS_TOKEN` before proceeding
18
+ - **Fix**: Validate both `DHAN_CLIENT_ID` and `DHAN_ACCESS_TOKEN` before proceeding
19
19
 
20
20
  ### 3. **Memory Leak in Order Tracker** (HIGH)
21
21
  - **File**: `lib/DhanHQ/ws/orders/client.rb:18`
@@ -45,8 +45,8 @@ DhanHQ.logger.level = (ENV["DHAN_LOG_LEVEL"] || "INFO").upcase.then { |level| Lo
45
45
 
46
46
  **Minimum environment variables**
47
47
 
48
- * `CLIENT_ID` – trading account client id issued by Dhan.
49
- * `ACCESS_TOKEN` – API access token generated from the Dhan console.
48
+ * `DHAN_CLIENT_ID` – trading account client id issued by Dhan.
49
+ * `DHAN_ACCESS_TOKEN` – API access token generated from the Dhan console.
50
50
 
51
51
  If either key is missing `configure_with_env` raises an error. Ensure your
52
52
  application loads them into `ENV` before requiring the gem.
@@ -64,8 +64,8 @@ Create a `.env` file in your project root to supply the minimum values (and any
64
64
  optional overrides you need):
65
65
 
66
66
  ```dotenv
67
- CLIENT_ID=your_client_id
68
- ACCESS_TOKEN=your_access_token
67
+ DHAN_CLIENT_ID=your_client_id
68
+ DHAN_ACCESS_TOKEN=your_access_token
69
69
  ```
70
70
 
71
71
  The gem requires `dotenv/load`, so these variables are loaded automatically when you require `DhanHQ`.
@@ -23,7 +23,7 @@ Set `access_token` once; it is sent on every request:
23
23
  ```ruby
24
24
  DhanHQ.configure do |config|
25
25
  config.client_id = ENV["DHAN_CLIENT_ID"]
26
- config.access_token = ENV["ACCESS_TOKEN"]
26
+ config.access_token = ENV["DHAN_ACCESS_TOKEN"]
27
27
  end
28
28
  ```
29
29
 
@@ -68,7 +68,7 @@ Example: refresh in provider and cache the result until near expiry:
68
68
  config.access_token_provider = lambda do
69
69
  stored = YourTokenStore.current
70
70
  if stored.nil? || stored.expired_soon?
71
- response = DhanHQ::Auth.renew_token(stored&.access_token || ENV["ACCESS_TOKEN"], config.client_id)
71
+ response = DhanHQ::Auth.renew_token(stored&.access_token || ENV["DHAN_ACCESS_TOKEN"], config.client_id)
72
72
  YourTokenStore.update!(response["accessToken"], response["expiryTime"])
73
73
  stored = YourTokenStore.current
74
74
  end
@@ -108,3 +108,117 @@ Rescue `AuthenticationError` for local config/token resolution failures; rescue
108
108
  - [rails_integration.md](rails_integration.md) — Rails initializer with optional `access_token_provider` and RenewToken
109
109
  - [TESTING_GUIDE.md](TESTING_GUIDE.md) — Config examples and RenewToken
110
110
  - [CHANGELOG.md](../CHANGELOG.md) — 2.2.0 and 2.2.1 auth and token changes
111
+
112
+
113
+ # SUPPORTED AUTHENTICATION AND TOKEN GENERATION APPROACHES:
114
+
115
+ We now support five distinct authentication approaches in this gem.
116
+
117
+ 1️⃣ Static token (manual, simplest)
118
+ What: You paste a token you got from Dhan (web, OAuth, partner, whatever) into config.
119
+ How:
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
+ ```
126
+ When: You’re OK rotating tokens manually (e.g. cron job, ops runbook).
127
+
128
+ 2️⃣ Dynamic token via access_token_provider
129
+ What: Gem asks you for a token on every request (proc/lambda).
130
+ How:
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
+ ```
138
+ Behavior:
139
+ On 401 (auth failure), client calls on_token_expired, then retries once using a fresh token from access_token_provider.
140
+
141
+ 3️⃣ Fetch-from-token-endpoint (configure_from_token_endpoint)
142
+ What: Gem calls your HTTP endpoint once to get access_token + client_id.
143
+ How:
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
+ ```
151
+ When: Multi-tenant or central credential service, you don’t want tokens in ENV directly.
152
+
153
+ 4️⃣ TOTP-based token generation (new DhanHQ::Auth flow)
154
+ Module API (low-level)
155
+ What: Direct call to Dhan’s generateAccessToken endpoint using TOTP.
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
+
167
+ Client API (high-level, returns TokenResponse)
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
+
178
+ When: Fully automated individual setup (no manual web token generation).
179
+
180
+ 5️⃣ Auto token lifecycle management (TokenManager)
181
+ What: Gem handles generate + renew + retry around every API call.
182
+ How:
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
+ ```
192
+ Behavior:
193
+ On first use: calls TOTP TokenGenerator → applies token.
194
+ Before each request: ensure_valid_token!:
195
+ If no token → generate.
196
+ If needs_refresh? → TokenRenewal (POST /v2/RenewToken) with current token + dhanClientId.
197
+ If renewal fails with auth error → falls back to full generate.
198
+
199
+ 6️⃣ Web-token renewal only (RenewToken)
200
+ Module API:
201
+ ```ruby
202
+ response = DhanHQ::Auth.renew_token(
203
+ access_token: current_token,
204
+ client_id: ENV["DHAN_CLIENT_ID"]
205
+ )
206
+ ```
207
+ Client / manager (high-level):
208
+
209
+ `client.renew_access_token` (returns TokenResponse, updates config).
210
+ `TokenManager#refresh!` internally uses Auth::TokenRenewal.
211
+ When: You’re using web-generated 24h tokens and want to extend them without switching to TOTP.
212
+
213
+ TL;DR
214
+ Manual: Static token (access_token)
215
+
216
+ Dynamic: access_token_provider (+ optional on_token_expired)
217
+
218
+ Central service: configure_from_token_endpoint
219
+
220
+ Fully automated: TOTP generate (Auth / Client#generate_access_token)
221
+
222
+ Production-grade automation: enable_auto_token_management! (generate + renew)
223
+
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.
@@ -72,8 +72,8 @@ puts "Token provider: #{DhanHQ.configuration.access_token_provider ? 'Set' : 'No
72
72
 
73
73
  ```ruby
74
74
  # Check current environment variables
75
- puts "CLIENT_ID: #{ENV['CLIENT_ID']}"
76
- puts "ACCESS_TOKEN: #{ENV['ACCESS_TOKEN'] ? 'Set' : 'Not Set'}"
75
+ puts "DHAN_CLIENT_ID: #{ENV['DHAN_CLIENT_ID']}"
76
+ puts "DHAN_ACCESS_TOKEN: #{ENV['DHAN_ACCESS_TOKEN'] ? 'Set' : 'Not Set'}"
77
77
  puts "DHAN_LOG_LEVEL: #{ENV['DHAN_LOG_LEVEL'] || 'INFO'}"
78
78
  puts "DHAN_CONNECT_TIMEOUT: #{ENV['DHAN_CONNECT_TIMEOUT'] || '10'}"
79
79
  puts "DHAN_READ_TIMEOUT: #{ENV['DHAN_READ_TIMEOUT'] || '30'}"
@@ -521,7 +521,7 @@ if position
521
521
  to_product_type: "MARGIN",
522
522
  quantity: position.net_qty.abs
523
523
  )
524
-
524
+
525
525
  if result
526
526
  puts "Position converted successfully"
527
527
  else
@@ -784,22 +784,22 @@ if instrument
784
784
  # Get LTP
785
785
  ltp_data = instrument.ltp
786
786
  puts "LTP: ₹#{ltp_data[:last_price]}"
787
-
787
+
788
788
  # Get OHLC
789
789
  ohlc_data = instrument.ohlc
790
790
  puts "OHLC: #{ohlc_data[:ohlc]}"
791
-
791
+
792
792
  # Get Quote
793
793
  quote_data = instrument.quote
794
794
  puts "Quote: #{quote_data[:ltp]}"
795
-
795
+
796
796
  # Get Daily Historical Data
797
797
  daily_data = instrument.daily(
798
798
  from_date: Date.today - 7,
799
799
  to_date: Date.today
800
800
  )
801
801
  puts "Daily candles: #{daily_data.size}"
802
-
802
+
803
803
  # Get Intraday Historical Data
804
804
  intraday_data = instrument.intraday(
805
805
  from_date: Date.today,
@@ -807,7 +807,7 @@ if instrument
807
807
  interval: 5
808
808
  )
809
809
  puts "Intraday candles: #{intraday_data.size}"
810
-
810
+
811
811
  # Get Expiry List (for F&O instruments)
812
812
  if instrument.instrument == "FUTSTK" || instrument.instrument == "OPTSTK"
813
813
  expiry_list = instrument.expiry_list