DhanHQ 2.1.5 → 2.1.6

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 (39) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +7 -0
  3. data/GUIDE.md +215 -73
  4. data/README.md +416 -132
  5. data/README1.md +267 -26
  6. data/docs/live_order_updates.md +319 -0
  7. data/docs/rails_websocket_integration.md +847 -0
  8. data/docs/standalone_ruby_websocket_integration.md +1588 -0
  9. data/docs/websocket_integration.md +871 -0
  10. data/examples/comprehensive_websocket_examples.rb +148 -0
  11. data/examples/instrument_finder_test.rb +195 -0
  12. data/examples/live_order_updates.rb +118 -0
  13. data/examples/market_depth_example.rb +144 -0
  14. data/examples/market_feed_example.rb +81 -0
  15. data/examples/order_update_example.rb +105 -0
  16. data/examples/trading_fields_example.rb +215 -0
  17. data/lib/DhanHQ/configuration.rb +16 -1
  18. data/lib/DhanHQ/contracts/expired_options_data_contract.rb +103 -0
  19. data/lib/DhanHQ/contracts/trade_contract.rb +70 -0
  20. data/lib/DhanHQ/errors.rb +2 -0
  21. data/lib/DhanHQ/models/expired_options_data.rb +331 -0
  22. data/lib/DhanHQ/models/instrument.rb +96 -2
  23. data/lib/DhanHQ/models/order_update.rb +235 -0
  24. data/lib/DhanHQ/models/trade.rb +118 -31
  25. data/lib/DhanHQ/resources/expired_options_data.rb +22 -0
  26. data/lib/DhanHQ/version.rb +1 -1
  27. data/lib/DhanHQ/ws/base_connection.rb +249 -0
  28. data/lib/DhanHQ/ws/connection.rb +2 -2
  29. data/lib/DhanHQ/ws/decoder.rb +3 -3
  30. data/lib/DhanHQ/ws/market_depth/client.rb +376 -0
  31. data/lib/DhanHQ/ws/market_depth/decoder.rb +131 -0
  32. data/lib/DhanHQ/ws/market_depth.rb +74 -0
  33. data/lib/DhanHQ/ws/orders/client.rb +175 -11
  34. data/lib/DhanHQ/ws/orders/connection.rb +40 -81
  35. data/lib/DhanHQ/ws/orders.rb +28 -0
  36. data/lib/DhanHQ/ws/segments.rb +18 -2
  37. data/lib/DhanHQ/ws.rb +3 -2
  38. data/lib/dhan_hq.rb +5 -0
  39. metadata +35 -1
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: cff831c4d2dc881c38a4ecc6274ad9ba8601fd6c6910815ab9ec40fde31af564
4
- data.tar.gz: 00b892609e077902e1137b789dcd3a6d0e51fa3e042d38b05c39fc92971da157
3
+ metadata.gz: 5b28412f443ad7ef0da9f10e99cee98dff8194b0d9da3c9d828dba28e3443fce
4
+ data.tar.gz: 96c1411c9e3c579f35637bfb37f9644348e0d75f5fbe9a502da59789da4607ba
5
5
  SHA512:
6
- metadata.gz: f476df43f8f9500515101c85d423b91f713f384e89a01f316ed66265d69502a45b4adf7c2cd06aa6c8d466e65d5548e0337361844305e6f9103df0dd6a7564f3
7
- data.tar.gz: aa14913dfa8590e8ba52e2f49a97b4cf475ad6d0058deac7114c1c56cb3817c70a38655afa4b30ef9741ab3da82f53c9d719dec70bef70bb9e242f7081a7392e
6
+ metadata.gz: 313c052fc68d670ce53005b091f70280617c0414a9fa1f6222d0f641b3181660bcdbf92eae5f6ece36fa500f1a554faee024114b2e132c1b2e3602b384a67cc3
7
+ data.tar.gz: e20baecdc074b57d78024bf5004a8808eb5397d3d1edb5743178ad50aa369e15250219c1f52384e1f00619fc8edd56bc97501ac8df375c95c7e61ca08dca96d9
data/CHANGELOG.md CHANGED
@@ -1,5 +1,9 @@
1
1
  ## [Unreleased]
2
2
 
3
+ ### Changed
4
+ - Align Super Order documentation across README, README1, and GUIDE with the latest API contract (place, modify, cancel, list).
5
+ - Normalise remaining documentation examples to snake_case, including order update WebSocket callbacks and kill switch response guidance.
6
+
3
7
  ## [2.1.5] - 2025-01-27
4
8
 
5
9
  ### ⚠️ BREAKING CHANGES
@@ -18,6 +22,9 @@
18
22
  ### Fixed
19
23
  - **RuboCop compliance**: Fixed all RuboCop offenses (179 → 0 offenses)
20
24
  - **Documentation**: Updated all documentation examples to use `require 'dhan_hq'`
25
+ - **Documentation**: Correct Super Order examples to use snake_case parameters for `DhanHQ::Models` helpers
26
+ - **Documentation**: Normalise Super Order path placeholders and response fields to snake_case for consistency
27
+ - **Documentation**: Clarified that model helpers auto-inject `dhan_client_id`, removing the need to add it manually in Ruby payloads
21
28
  - **Code quality**: Added comprehensive validation tests for OptionChain methods
22
29
 
23
30
  ### Changed
data/GUIDE.md CHANGED
@@ -42,9 +42,9 @@ DhanHQ.logger.level = (ENV["DHAN_LOG_LEVEL"] || "INFO").upcase.then { |level| Lo
42
42
 
43
43
  `configure_with_env` reads from `ENV` and raises unless both variables are set:
44
44
 
45
- | Variable | Description |
46
- | -------------- | ---------------------------------------------------- |
47
- | `CLIENT_ID` | Your Dhan trading client id. |
45
+ | Variable | Description |
46
+ | --- | --- |
47
+ | `CLIENT_ID` | Your Dhan trading client id. |
48
48
  | `ACCESS_TOKEN` | REST/WebSocket access token generated via Dhan APIs. |
49
49
 
50
50
  Provide them via `.env`, Rails credentials, or your secret manager of choice
@@ -55,14 +55,14 @@ before the initializer runs.
55
55
  Set any of the following environment variables _before_ calling
56
56
  `configure_with_env` to customise runtime behaviour:
57
57
 
58
- | Variable | Purpose |
59
- | ----------------------------------------- | ---------------------------------------------------- |
60
- | `DHAN_LOG_LEVEL` | Change logger verbosity (`INFO` default). |
61
- | `DHAN_BASE_URL` | Override the REST API host. |
62
- | `DHAN_WS_VERSION` | Target a specific WebSocket API version. |
63
- | `DHAN_WS_ORDER_URL` | Customise the order update WebSocket endpoint. |
64
- | `DHAN_WS_USER_TYPE` | Toggle between `SELF` and `PARTNER` streaming modes. |
65
- | `DHAN_PARTNER_ID` / `DHAN_PARTNER_SECRET` | Required when streaming as a partner. |
58
+ | Variable | Purpose |
59
+ | --- | --- |
60
+ | `DHAN_LOG_LEVEL` | Change logger verbosity (`INFO` default). |
61
+ | `DHAN_BASE_URL` | Override the REST API host. |
62
+ | `DHAN_WS_VERSION` | Target a specific WebSocket API version. |
63
+ | `DHAN_WS_ORDER_URL` | Customise the order update WebSocket endpoint. |
64
+ | `DHAN_WS_USER_TYPE` | Toggle between `SELF` and `PARTNER` streaming modes. |
65
+ | `DHAN_PARTNER_ID` / `DHAN_PARTNER_SECRET` | Required when streaming as a partner. |
66
66
 
67
67
  ---
68
68
 
@@ -106,32 +106,32 @@ order.refresh
106
106
 
107
107
  Required fields validated by `DhanHQ::Contracts::PlaceOrderContract`:
108
108
 
109
- | Key | Type | Allowed Values / Notes |
110
- | ------------------ | ------- | -------------------------------------------------- |
111
- | `transaction_type` | String | `BUY`, `SELL` |
112
- | `exchange_segment` | String | Use `DhanHQ::Constants::EXCHANGE_SEGMENTS` |
113
- | `product_type` | String | `CNC`, `INTRADAY`, `MARGIN`, `MTF`, `CO`, `BO` |
114
- | `order_type` | String | `LIMIT`, `MARKET`, `STOP_LOSS`, `STOP_LOSS_MARKET` |
115
- | `validity` | String | `DAY`, `IOC` |
116
- | `security_id` | String | Security identifier from the scrip master |
117
- | `quantity` | Integer | Must be > 0 |
109
+ | Key | Type | Allowed Values / Notes |
110
+ | ----------------- | ------- | ---------------------- |
111
+ | `transaction_type`| String | `BUY`, `SELL` |
112
+ | `exchange_segment`| String | Use `DhanHQ::Constants::EXCHANGE_SEGMENTS` |
113
+ | `product_type` | String | `CNC`, `INTRADAY`, `MARGIN`, `MTF`, `CO`, `BO` |
114
+ | `order_type` | String | `LIMIT`, `MARKET`, `STOP_LOSS`, `STOP_LOSS_MARKET` |
115
+ | `validity` | String | `DAY`, `IOC` |
116
+ | `security_id` | String | Security identifier from the scrip master |
117
+ | `quantity` | Integer | Must be > 0 |
118
118
 
119
119
  Optional fields and special rules:
120
120
 
121
- | Key | Type | Notes |
122
- | -------------------- | ------- | --------------------------------------------------------------------------------- |
123
- | `correlation_id` | String | ≤ 25 chars; useful for idempotency |
124
- | `disclosed_quantity` | Integer | ≥ 0 and ≤ 30% of `quantity` |
125
- | `trading_symbol` | String | Optional label |
126
- | `price` | Float | Mandatory for `LIMIT` |
127
- | `trigger_price` | Float | Mandatory for SL / SLM |
128
- | `after_market_order` | Boolean | Require `amo_time` when true |
129
- | `amo_time` | String | `OPEN`, `OPEN_30`, `OPEN_60` (check `DhanHQ::Constants::AMO_TIMINGS` for updates) |
130
- | `bo_profit_value` | Float | Required with `product_type: "BO"` |
131
- | `bo_stop_loss_value` | Float | Required with `product_type: "BO"` |
132
- | `drv_expiry_date` | String | Pass ISO `YYYY-MM-DD` for derivatives |
133
- | `drv_option_type` | String | `CALL`, `PUT`, `NA` |
134
- | `drv_strike_price` | Float | > 0 |
121
+ | Key | Type | Notes |
122
+ | --------------------- | ------- | ----- |
123
+ | `correlation_id` | String | ≤ 25 chars; useful for idempotency |
124
+ | `disclosed_quantity` | Integer | ≥ 0 and ≤ 30% of `quantity` |
125
+ | `trading_symbol` | String | Optional label |
126
+ | `price` | Float | Mandatory for `LIMIT` |
127
+ | `trigger_price` | Float | Mandatory for SL / SLM |
128
+ | `after_market_order` | Boolean | Require `amo_time` when true |
129
+ | `amo_time` | String | `OPEN`, `OPEN_30`, `OPEN_60` (check `DhanHQ::Constants::AMO_TIMINGS` for updates) |
130
+ | `bo_profit_value` | Float | Required with `product_type: "BO"` |
131
+ | `bo_stop_loss_value` | Float | Required with `product_type: "BO"` |
132
+ | `drv_expiry_date` | String | Pass ISO `YYYY-MM-DD` for derivatives |
133
+ | `drv_option_type` | String | `CALL`, `PUT`, `NA` |
134
+ | `drv_strike_price` | Float | > 0 |
135
135
 
136
136
  Example:
137
137
 
@@ -156,9 +156,9 @@ puts order.order_status # => "TRADED" / "PENDING" / ...
156
156
 
157
157
  `Order#modify` merges the existing attributes with the supplied overrides and validates against `ModifyOrderContract`.
158
158
 
159
- - Required: the instance must have an `order_id` and `dhan_client_id`.
159
+ - Required: the instance must have an `order_id`; the model reuses the saved `dhan_client_id` when building the payload.
160
160
  - At least one of `order_type`, `quantity`, `price`, `trigger_price`, `disclosed_quantity`, `validity` must change.
161
- - Payload is camelised automatically before hitting `/v2/orders/{orderId}`.
161
+ - Payload is camelised automatically before hitting `/v2/orders/{order_id}`.
162
162
 
163
163
  ```ruby
164
164
  order.modify(price: 154.2, trigger_price: 149.5)
@@ -208,42 +208,174 @@ DhanHQ::Models::Order.resource.slicing(payload)
208
208
 
209
209
  ### Super Orders
210
210
 
211
- `DhanHQ::Models::SuperOrder` wraps `/v2/super/orders`.
211
+ `DhanHQ::Models::SuperOrder` wraps the `/v2/super/orders` family. A super order combines entry, target, and stop-loss legs into one atomic instruction and supports an optional trailing jump so risk is managed server-side immediately after entry.
212
212
 
213
- ```ruby
214
- legs = {
215
- transactionType: "BUY",
216
- exchangeSegment: "NSE_FNO",
217
- productType: "CO",
218
- orderType: "LIMIT",
219
- validity: "DAY",
220
- securityId: "43492",
221
- quantity: 50,
222
- price: 100.0,
223
- stopLossPrice: 95.0,
224
- targetPrice: 110.0
213
+ #### Endpoints
214
+
215
+ | Method | Path | Description |
216
+ | --- | --- | --- |
217
+ | `POST` | `/super/orders` | Create a new super order |
218
+ | `PUT` | `/super/orders/{order_id}` | Modify a pending super order |
219
+ | `DELETE` | `/super/orders/{order_id}/{order_leg}` | Cancel a pending super order leg |
220
+ | `GET` | `/super/orders` | Retrieve the list of all super orders |
221
+
222
+ #### Place Super Order
223
+
224
+ > ℹ️ Static IP whitelisting with Dhan support is required before invoking these APIs.
225
+
226
+ ```bash
227
+ curl --request POST \
228
+ --url https://api.dhan.co/v2/super/orders \
229
+ --header 'Content-Type: application/json' \
230
+ --header 'access-token: JWT' \
231
+ --data '{Request JSON}'
232
+ ```
233
+
234
+ Request body:
235
+
236
+ ```json
237
+ {
238
+ "dhan_client_id": "1000000003",
239
+ "correlation_id": "123abc678",
240
+ "transaction_type": "BUY",
241
+ "exchange_segment": "NSE_EQ",
242
+ "product_type": "CNC",
243
+ "order_type": "LIMIT",
244
+ "security_id": "11536",
245
+ "quantity": 5,
246
+ "price": 1500,
247
+ "target_price": 1600,
248
+ "stop_loss_price": 1400,
249
+ "trailing_jump": 10
250
+ }
251
+ ```
252
+
253
+ Key parameters:
254
+
255
+ | Field | Type | Notes |
256
+ | --- | --- | --- |
257
+ | `dhan_client_id` | string *(required)* | User specific identifier generated by Dhan. Automatically merged when you call through the Ruby model helpers. |
258
+ | `correlation_id` | string | Optional caller supplied correlation id. |
259
+ | `transaction_type` | enum string *(required)* | `BUY` or `SELL`. |
260
+ | `exchange_segment` | enum string *(required)* | Exchange segment. |
261
+ | `product_type` | enum string *(required)* | `CNC`, `INTRADAY`, `MARGIN`, or `MTF`. |
262
+ | `order_type` | enum string *(required)* | `LIMIT` or `MARKET`. |
263
+ | `security_id` | string *(required)* | Exchange security identifier. |
264
+ | `quantity` | integer *(required)* | Entry quantity. |
265
+ | `price` | float *(required)* | Entry price. |
266
+ | `target_price` | float *(required)* | Target price for the super order. |
267
+ | `stop_loss_price` | float *(required)* | Stop-loss price for the super order. |
268
+ | `trailing_jump` | float *(required)* | Trailing jump size. |
269
+
270
+ > 🐍 Pass snake_case keys when invoking `DhanHQ::Models::SuperOrder.create`—the client camelizes internally before calling the REST API and injects your configured `dhan_client_id`, so the key is optional in Ruby payloads.
271
+
272
+ Response:
273
+
274
+ ```json
275
+ {
276
+ "order_id": "112111182198",
277
+ "order_status": "PENDING"
225
278
  }
279
+ ```
226
280
 
227
- super_order = DhanHQ::Models::SuperOrder.create(legs)
228
- super_order.modify(trailingJump: 2.5)
229
- super_order.cancel("ENTRY_LEG")
281
+ #### Modify Super Order
282
+
283
+ Modify while the order is `PENDING` or `PART_TRADED`. Entry leg updates adjust the entire super order until the entry trades; afterwards only target and stop-loss legs (price, trailing) remain editable.
284
+
285
+ ```bash
286
+ curl --request PUT \
287
+ --url https://api.dhan.co/v2/super/orders/{order_id} \
288
+ --header 'Content-Type: application/json' \
289
+ --header 'access-token: JWT' \
290
+ --data '{Request JSON}'
291
+ ```
292
+
293
+ Example payload:
294
+
295
+ ```json
296
+ {
297
+ "dhan_client_id": "1000000009",
298
+ "order_id": "112111182045",
299
+ "order_type": "LIMIT",
300
+ "leg_name": "ENTRY_LEG",
301
+ "quantity": 40,
302
+ "price": 1300,
303
+ "target_price": 1450,
304
+ "stop_loss_price": 1350,
305
+ "trailing_jump": 20
306
+ }
307
+ ```
308
+
309
+ Conditional fields:
310
+
311
+ | Field | Required when | Notes |
312
+ | --- | --- | --- |
313
+ | `order_type` | Updating `ENTRY_LEG` | `LIMIT` or `MARKET`. |
314
+ | `quantity` | Updating `ENTRY_LEG` | Adjusts entry quantity. |
315
+ | `price` | Updating `ENTRY_LEG` | Adjusts entry price. |
316
+ | `target_price` | Updating `ENTRY_LEG` or `TARGET_LEG` | Adjusts target price. |
317
+ | `stop_loss_price` | Updating `ENTRY_LEG` or `STOP_LOSS_LEG` | Adjusts stop-loss price. |
318
+ | `trailing_jump` | Updating `ENTRY_LEG` or `STOP_LOSS_LEG` | Pass `0` or omit to cancel trailing. |
319
+
320
+ Response:
321
+
322
+ ```json
323
+ {
324
+ "order_id": "112111182045",
325
+ "order_status": "TRANSIT"
326
+ }
230
327
  ```
231
328
 
329
+ #### Cancel Super Order
330
+
331
+ ```bash
332
+ curl --request DELETE \
333
+ --url https://api.dhan.co/v2/super/orders/{order_id}/{order_leg} \
334
+ --header 'Content-Type: application/json' \
335
+ --header 'access-token: JWT'
336
+ ```
337
+
338
+ Path parameters:
339
+
340
+ | Field | Description | Example |
341
+ | --- | --- | --- |
342
+ | `order_id` | Super order identifier. | `11211182198` |
343
+ | `order_leg` | Leg to cancel (`ENTRY_LEG`, `TARGET_LEG`, or `STOP_LOSS_LEG`). | `ENTRY_LEG` |
344
+
345
+ Response:
346
+
347
+ ```json
348
+ {
349
+ "order_id": "112111182045",
350
+ "order_status": "CANCELLED"
351
+ }
352
+ ```
353
+
354
+ #### Super Order List
355
+
356
+ ```bash
357
+ curl --request GET \
358
+ --url https://api.dhan.co/v2/super/orders \
359
+ --header 'Content-Type: application/json' \
360
+ --header 'access-token: JWT'
361
+ ```
362
+
363
+ The response returns one object per super order with nested `leg_details`. Key attributes include `order_status`, `filled_qty`, `remaining_quantity`, `average_traded_price`, and leg-level trailing configuration. `CLOSED` indicates the entry plus either target or stop-loss filled the entire quantity; `TRIGGERED` surfaces on the target or stop-loss leg that fired.
364
+
232
365
  ### Forever Orders (GTT)
233
366
 
234
367
  `DhanHQ::Models::ForeverOrder` maps to `/v2/forever/orders`.
235
368
 
236
369
  ```ruby
237
370
  params = {
238
- dhanClientId: "123456",
239
- transactionType: "SELL",
240
- exchangeSegment: "NSE_EQ",
241
- productType: "CNC",
242
- orderType: "LIMIT",
371
+ transaction_type: "SELL",
372
+ exchange_segment: "NSE_EQ",
373
+ product_type: "CNC",
374
+ order_type: "LIMIT",
243
375
  validity: "DAY",
244
- securityId: "1333",
376
+ security_id: "1333",
245
377
  price: 200.0,
246
- triggerPrice: 198.0
378
+ trigger_price: 198.0
247
379
  }
248
380
 
249
381
  forever_order = DhanHQ::Models::ForeverOrder.create(params)
@@ -251,6 +383,8 @@ forever_order.modify(price: 205.0)
251
383
  forever_order.cancel
252
384
  ```
253
385
 
386
+ > 🪄 Model helpers merge the configured `dhan_client_id` automatically, so you can omit it when constructing Ruby hashes like the example above.
387
+
254
388
  The forever order helpers accept snake_case parameters and camelize them internally; only the low-level resource requires raw API casing.
255
389
 
256
390
  ---
@@ -268,7 +402,6 @@ Convert an intraday position to delivery (or vice versa):
268
402
 
269
403
  ```ruby
270
404
  convert_payload = {
271
- dhan_client_id: "123456",
272
405
  security_id: "1333",
273
406
  from_product_type: "INTRADAY",
274
407
  to_product_type: "CNC",
@@ -281,6 +414,8 @@ response = DhanHQ::Models::Position.convert(convert_payload)
281
414
  raise response.errors.to_s if response.is_a?(DhanHQ::ErrorObject)
282
415
  ```
283
416
 
417
+ > 🪄 No need to merge `dhan_client_id`; the helper adds it from your configuration before calling `/v2/positions/convert`.
418
+
284
419
  The conversion helper validates the payload with `PositionConversionContract`; missing or invalid fields raise `DhanHQ::Error` before the request is sent.
285
420
 
286
421
  ### Holdings
@@ -335,15 +470,15 @@ Both endpoints return arrays and skip validation because they represent historic
335
470
 
336
471
  `DhanHQ::Models::HistoricalData` enforces `HistoricalDataContract` before delegating to `/v2/charts`.
337
472
 
338
- | Key | Type | Notes |
339
- | ------------------ | ------- | -------------------------------------------------- |
340
- | `security_id` | String | Required |
341
- | `exchange_segment` | String | See `EXCHANGE_SEGMENTS` |
342
- | `instrument` | String | Use `DhanHQ::Constants::INSTRUMENTS` |
343
- | `from_date` | String | `YYYY-MM-DD` |
344
- | `to_date` | String | `YYYY-MM-DD` |
345
- | `expiry_code` | Integer | Optional (`0`, `1`, `2`) |
346
- | `interval` | String | Optional (`1`, `5`, `15`, `25`, `60`) for intraday |
473
+ | Key | Type | Notes |
474
+ | ------------------ | ------ | ----- |
475
+ | `security_id` | String | Required |
476
+ | `exchange_segment` | String | See `EXCHANGE_SEGMENTS` |
477
+ | `instrument` | String | Use `DhanHQ::Constants::INSTRUMENTS` |
478
+ | `from_date` | String | `YYYY-MM-DD` |
479
+ | `to_date` | String | `YYYY-MM-DD` |
480
+ | `expiry_code` | Integer| Optional (`0`, `1`, `2`) |
481
+ | `interval` | String | Optional (`1`, `5`, `15`, `25`, `60`) for intraday |
347
482
 
348
483
  ```ruby
349
484
  bars = DhanHQ::Models::HistoricalData.intraday(
@@ -379,7 +514,6 @@ The model filters strikes where both CE and PE have zero `last_price`, keeping t
379
514
 
380
515
  ```ruby
381
516
  params = {
382
- dhan_client_id: "123456",
383
517
  exchange_segment: "NSE_EQ",
384
518
  transaction_type: "BUY",
385
519
  quantity: 10,
@@ -392,6 +526,8 @@ margin = DhanHQ::Models::Margin.calculate(params)
392
526
  puts margin.total_margin
393
527
  ```
394
528
 
529
+ > 🪄 The margin helper appends `dhan_client_id` from your credentials before calling `/v2/margincalculator`.
530
+
395
531
  If a required field is missing (for example `transaction_type`), the contract raises `DhanHQ::Error` before any API call is issued.
396
532
 
397
533
  ### REST Market Feed (Batch LTP/OHLC/Quote)
@@ -473,14 +609,20 @@ All helpers accept snake_case keys; the client camelizes them before calling `/v
473
609
  ### Kill Switch
474
610
 
475
611
  ```ruby
476
- DhanHQ::Models::KillSwitch.activate # => {"killSwitchStatus"=>"ACTIVATE"}
477
- DhanHQ::Models::KillSwitch.deactivate # => {"killSwitchStatus"=>"DEACTIVATE"}
612
+ activate_payload = DhanHQ::Models::KillSwitch.activate
613
+ deactivate_payload = DhanHQ::Models::KillSwitch.deactivate
614
+
615
+ DhanHQ::Models::KillSwitch.snake_case(activate_payload)
616
+ # => { kill_switch_status: "ACTIVATE" }
617
+
618
+ DhanHQ::Models::KillSwitch.snake_case(deactivate_payload)
619
+ # => { kill_switch_status: "DEACTIVATE" }
478
620
 
479
621
  # Explicit status update
480
622
  DhanHQ::Models::KillSwitch.update("ACTIVATE")
481
623
  ```
482
624
 
483
- Only `"ACTIVATE"` and `"DEACTIVATE"` are accepted—any other value raises `DhanHQ::Error`.
625
+ Only `"ACTIVATE"` and `"DEACTIVATE"` are accepted—any other value raises `DhanHQ::Error`. Use the `snake_case` helper to normalise API responses when you prefer underscore keys.
484
626
 
485
627
  ---
486
628