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.
@@ -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
- DhanHQ.configure do |config| config.client_id = ENV["DHAN_CLIENT_ID"] config.access_token = ENV["DHAN_ACCESS_TOKEN"]end
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
- DhanHQ.configure do |config| config.client_id = ENV["DHAN_CLIENT_ID"] config.access_token_provider = -> { MyTokenStore.fetch_current_token } config.on_token_expired = ->(error) { MyTokenStore.refresh!(error) }end
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
- DhanHQ.configure_from_token_endpoint( base_url: "https://myapp.com", bearer_token: ENV["DHAN_TOKEN_ENDPOINT_BEARER"])# expects JSON: { access_token: "...", client_id: "...", base_url: "..." (optional) }
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
- totp = DhanHQ::Auth.generate_totp(ENV["DHAN_TOTP_SECRET"])response = DhanHQ::Auth.generate_access_token( dhan_client_id: ENV["DHAN_CLIENT_ID"], pin: ENV["DHAN_PIN"], totp: totp)token = response["accessToken"]expiry = response["expiryTime"]
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
- client = DhanHQ::Client.new(api_type: :order_api)token = client.generate_access_token( dhan_client_id: ENV["DHAN_CLIENT_ID"], pin: ENV["DHAN_PIN"], totp_secret: ENV["DHAN_TOTP_SECRET"] # or `totp:` if you computed it)# auto-applies token + client_id to `DhanHQ.configuration`
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
- client = DhanHQ::Client.new(api_type: :order_api)client.enable_auto_token_management!( dhan_client_id: ENV["DHAN_CLIENT_ID"], pin: ENV["DHAN_PIN"], totp_secret: ENV["DHAN_TOTP_SECRET"])# From now on, `client.request` auto-ensures a valid token.
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
- response = DhanHQ::Auth.renew_token( access_token: current_token, client_id: ENV["DHAN_CLIENT_ID"])
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
- client.renew_access_token (returns TokenResponse, updates config).
154
- TokenManager#refresh! internally uses Auth::TokenRenewal.
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/)