DhanHQ 2.4.0 → 2.6.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 (82) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +9 -1
  3. data/CHANGELOG.md +103 -7
  4. data/GUIDE.md +57 -39
  5. data/README.md +198 -755
  6. data/docs/API_DOCS_GAPS.md +128 -0
  7. data/docs/API_VERIFICATION.md +10 -11
  8. data/{README1.md → docs/ARCHIVE_README.md} +16 -16
  9. data/docs/AUTHENTICATION.md +72 -10
  10. data/docs/CONFIGURATION.md +109 -0
  11. data/docs/CONSTANTS_REFERENCE.md +477 -0
  12. data/docs/DATA_API_PARAMETERS.md +7 -7
  13. data/docs/{rails_websocket_integration.md → RAILS_WEBSOCKET_INTEGRATION.md} +10 -10
  14. data/docs/{standalone_ruby_websocket_integration.md → STANDALONE_RUBY_WEBSOCKET_INTEGRATION.md} +32 -32
  15. data/docs/SUPER_ORDERS.md +284 -0
  16. data/docs/{technical_analysis.md → TECHNICAL_ANALYSIS.md} +3 -3
  17. data/docs/TESTING_GUIDE.md +84 -82
  18. data/docs/TROUBLESHOOTING.md +117 -0
  19. data/docs/{websocket_integration.md → WEBSOCKET_INTEGRATION.md} +19 -19
  20. data/docs/WEBSOCKET_PROTOCOL.md +154 -0
  21. data/lib/DhanHQ/constants.rb +456 -151
  22. data/lib/DhanHQ/contracts/alert_order_contract.rb +37 -10
  23. data/lib/DhanHQ/contracts/base_contract.rb +22 -0
  24. data/lib/DhanHQ/contracts/edis_contract.rb +25 -0
  25. data/lib/DhanHQ/contracts/margin_calculator_contract.rb +27 -4
  26. data/lib/DhanHQ/contracts/modify_order_contract.rb +65 -12
  27. data/lib/DhanHQ/contracts/multi_scrip_margin_calc_request_contract.rb +23 -0
  28. data/lib/DhanHQ/contracts/order_contract.rb +171 -39
  29. data/lib/DhanHQ/contracts/place_order_contract.rb +14 -141
  30. data/lib/DhanHQ/contracts/pnl_based_exit_contract.rb +20 -0
  31. data/lib/DhanHQ/contracts/position_conversion_contract.rb +15 -3
  32. data/lib/DhanHQ/contracts/slice_order_contract.rb +10 -1
  33. data/lib/DhanHQ/contracts/user_ip_contract.rb +14 -0
  34. data/lib/DhanHQ/core/base_model.rb +13 -4
  35. data/lib/DhanHQ/helpers/response_helper.rb +2 -2
  36. data/lib/DhanHQ/helpers/validation_helper.rb +1 -1
  37. data/lib/DhanHQ/models/alert_order.rb +29 -11
  38. data/lib/DhanHQ/models/concerns/api_response_handler.rb +46 -0
  39. data/lib/DhanHQ/models/edis.rb +101 -0
  40. data/lib/DhanHQ/models/expired_options_data.rb +6 -12
  41. data/lib/DhanHQ/models/forever_order.rb +8 -5
  42. data/lib/DhanHQ/models/historical_data.rb +0 -8
  43. data/lib/DhanHQ/models/instrument.rb +1 -7
  44. data/lib/DhanHQ/models/instrument_helpers.rb +4 -4
  45. data/lib/DhanHQ/models/kill_switch.rb +23 -11
  46. data/lib/DhanHQ/models/margin.rb +51 -2
  47. data/lib/DhanHQ/models/order.rb +107 -126
  48. data/lib/DhanHQ/models/order_update.rb +7 -13
  49. data/lib/DhanHQ/models/pnl_exit.rb +122 -0
  50. data/lib/DhanHQ/models/position.rb +23 -1
  51. data/lib/DhanHQ/models/postback.rb +114 -0
  52. data/lib/DhanHQ/models/profile.rb +0 -10
  53. data/lib/DhanHQ/models/super_order.rb +13 -3
  54. data/lib/DhanHQ/models/trade.rb +11 -23
  55. data/lib/DhanHQ/resources/ip_setup.rb +16 -5
  56. data/lib/DhanHQ/resources/kill_switch.rb +17 -7
  57. data/lib/DhanHQ/resources/margin_calculator.rb +9 -0
  58. data/lib/DhanHQ/resources/orders.rb +41 -41
  59. data/lib/DhanHQ/resources/pnl_exit.rb +37 -0
  60. data/lib/DhanHQ/resources/positions.rb +8 -0
  61. data/lib/DhanHQ/version.rb +1 -1
  62. data/lib/DhanHQ/ws/cmd_bus.rb +1 -1
  63. data/lib/DhanHQ/ws/orders/client.rb +6 -6
  64. data/lib/DhanHQ/ws/singleton_lock.rb +2 -1
  65. data/lib/dhanhq/analysis/options_buying_advisor.rb +2 -2
  66. data/lib/rubocop/cop/dhanhq/use_constants.rb +171 -0
  67. metadata +29 -24
  68. data/TODO-1.md +0 -14
  69. data/TODO.md +0 -127
  70. data/app/services/live/order_update_guard_support.rb +0 -75
  71. data/app/services/live/order_update_hub.rb +0 -76
  72. data/app/services/live/order_update_persistence_support.rb +0 -68
  73. data/docs/PR_2.2.0.md +0 -48
  74. data/examples/comprehensive_websocket_examples.rb +0 -148
  75. data/examples/instrument_finder_test.rb +0 -195
  76. data/examples/live_order_updates.rb +0 -118
  77. data/examples/market_depth_example.rb +0 -144
  78. data/examples/market_feed_example.rb +0 -81
  79. data/examples/order_update_example.rb +0 -105
  80. data/examples/trading_fields_example.rb +0 -215
  81. /data/docs/{live_order_updates.md → LIVE_ORDER_UPDATES.md} +0 -0
  82. /data/docs/{rails_integration.md → RAILS_INTEGRATION.md} +0 -0
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: b583e87aa598f3891e937f44859f9b451fc8f6bcec7a304cc0c33790fe9117ea
4
- data.tar.gz: 1a090fa608fba141da311ba8aaf21d36e750f78e90497f728c5f96d580044c7e
3
+ metadata.gz: 30825da308f5f85870f4efe9b702f7e2f653840b396b3265d36383d4a8c56f5e
4
+ data.tar.gz: b2874edeb45b60b6e68b4d8c69b9e9e3c6a0b6d2734ca645168626afd27cde76
5
5
  SHA512:
6
- metadata.gz: '0966f2a2aa331b675ecc1675510f6ad3dca6b7a987c8dde15dde2efe506ac659157b6e0979d0c23f480d2835fa603bfd37530f33275d422226abb2419e46bfd0'
7
- data.tar.gz: 74ea10d0744fb01324afd4a1768fa6969b07f4d1fcb9da94d7a6caf01fd01c2863d6a3746d288628d10e497bd55c68687e0b54e237bd91736ad4bb8c1decc9f8
6
+ metadata.gz: fe803337db74aa9689001b458627a532101fd0d298142b74ed0fefd87e7938ef4fd2b5e75bb53d871d8c535df35347065a43428986d930e6a0fa1ebae0c19aaf
7
+ data.tar.gz: aae78dbbbea337312262239f083c8fbb96f65b51b2d70cb9dce2683204f5e506d5a0835c85af6766ce0233262e79aaaf5dde45561eb410d51c52c74a185d5184
data/.rubocop.yml CHANGED
@@ -6,6 +6,7 @@ plugins:
6
6
 
7
7
  require:
8
8
  - rubocop-rake
9
+ - ./lib/rubocop/cop/dhanhq/use_constants.rb
9
10
 
10
11
  AllCops:
11
12
  TargetRubyVersion: 3.2
@@ -25,4 +26,11 @@ RSpec/ExampleLength:
25
26
  Max: 15
26
27
 
27
28
  RSpec/MultipleMemoizedHelpers:
28
- Max: 10
29
+ Max: 10
30
+
31
+ DhanHQ/UseConstants:
32
+ Enabled: true
33
+ Exclude:
34
+ - 'lib/DhanHQ/constants.rb'
35
+ - 'lib/DhanHQ/ws/segments.rb'
36
+ - 'spec/**/*'
data/CHANGELOG.md CHANGED
@@ -1,4 +1,100 @@
1
- ## [Unreleased]
1
+ ## [2.6.0] - 2026-03-06
2
+
3
+ ### Fixed (API docs alignment)
4
+
5
+ - **Kill Switch**: Manage API now uses query parameter per [dhanhq.co/docs/v2/traders-control](https://dhanhq.co/docs/v2/traders-control/). `Resources::KillSwitch#update(status)` sends `POST /v2/killswitch?killSwitchStatus=ACTIVATE` (or `DEACTIVATE`) with no body. `Models::KillSwitch.update("ACTIVATE")` / `.activate` / `.deactivate` unchanged.
6
+ - **IP Setup**: Set/Modify now send required API fields. `Resources::IPSetup#set` and `#update` accept `ip:`, `ip_flag: "PRIMARY"` (or `"SECONDARY"`), and optional `dhan_client_id:` (defaults from `DhanHQ.configuration.client_id`). See [dhanhq.co/docs/v2/authentication/#setup-static-ip](https://dhanhq.co/docs/v2/authentication/#setup-static-ip).
7
+ - **Alert Orders (Conditional Trigger)**: Condition now requires `exchange_segment`, `exp_date`, and `frequency` per [dhanhq.co/docs/v2/conditional-trigger](https://dhanhq.co/docs/v2/conditional-trigger/). `time_frame` is required when `comparison_type` starts with `TECHNICAL`. `AlertOrderContract` and all examples updated.
8
+
9
+ ### Breaking changes
10
+
11
+ - **AlertOrder.create / AlertOrderContract**: Contract expects nested structure (see 2.5.0). Condition hash must include `exchange_segment`, `exp_date`, and `frequency`; for `comparison_type` starting with `TECHNICAL`, `time_frame` is required. See GUIDE.md and [conditional-trigger](https://dhanhq.co/docs/v2/conditional-trigger/).
12
+ - **Resources::KillSwitch#update**: Signature is now `update(status)` (string). Use `update("ACTIVATE")` or `Models::KillSwitch.activate` / `.deactivate`, unchanged.
13
+ - **AlertOrderContract** — expected payload shape:
14
+
15
+ ```ruby
16
+ AlertOrderContract.new.call(
17
+ condition: {
18
+ exchange_segment: "NSE_EQ",
19
+ security_id: "11536",
20
+ comparison_type: "PRICE_WITH_VALUE",
21
+ operator: "GREATER_THAN",
22
+ exp_date: "2026-12-31",
23
+ frequency: "ONCE"
24
+ },
25
+ orders: [
26
+ { transaction_type: "BUY", exchange_segment: "NSE_EQ", product_type: "INTRADAY", order_type: "MARKET", security_id: "11536", quantity: 5, validity: "DAY" }
27
+ ]
28
+ )
29
+ ```
30
+
31
+ ### Added
32
+
33
+ #### Order Model — New Public Methods
34
+ - **`Order#destroy` / `#delete`**: Cancels an order via `DELETE /v2/orders/{id}`. Returns `true` if the API confirms `CANCELLED` status, `false` otherwise. `#delete` is an alias.
35
+ - **`Order#slice_order(params)`**: Splits a large order into multiple legs to exceed freeze-limit quantities on F&O instruments via `POST /v2/slicing`. Delegates to `Resources::Orders#slicing`.
36
+ - **`Order#save`**: ActiveRecord-style save — places a new order for new records, modifies existing records. Returns `true`/`false`.
37
+ - **`Order.place` — `dhan_client_id` auto-injection**: If `dhan_client_id` is not passed in params, it is automatically read from `DhanHQ.configuration.client_id`. Existing code that passes `dhan_client_id` explicitly continues to work unchanged.
38
+
39
+ #### Contract Hardening
40
+ - **`OrderContract`** (base for `PlaceOrderContract` and `ModifyOrderContract`) now enforces:
41
+ - `LIMIT` orders require `price`; `MARKET` orders reject `price`
42
+ - `STOP_LOSS` / `STOP_LOSS_MARKET` require `trigger_price`
43
+ - Stop-loss price relationships: BUY requires `trigger_price >= price`; SELL requires `trigger_price <= price`
44
+ - Bracket Order (BO): both `bo_profit_value` and `bo_stop_loss_value` required; directional profit/loss relationship validated
45
+ - `disclosed_quantity` cannot exceed 30% of `quantity`
46
+ - `amo_time` required when `after_market_order: true`
47
+ - Lot-size and tick-size enforcement when `instrument_meta` is provided
48
+ - Segment-based product restrictions (CNC equity-only, BO/CO currency restrictions)
49
+ - **`ModifyOrderContract`**: Requires at least one modifiable field; inherits all `OrderContract` business rules.
50
+ - **New contracts**: `EdisContract`, `UserIpContract`, `PnlBasedExitContract`, `MultiScripMarginCalcContract`, `SliceOrderContract`.
51
+
52
+ #### Infrastructure
53
+ - **`ApiResponseHandler` concern** (`lib/DhanHQ/models/concerns/api_response_handler.rb`): Shared module for uniform API response handling, attribute merging, and structured logging. Included in `Order` and `ForeverOrder`.
54
+ - **Global Constants Enforcement**: Replaced all hardcoded API strings with `DhanHQ::Constants` across the repository. Built a custom RuboCop cop (`RuboCop::Cop::DhanHQ::UseConstants`) that strictly enforces typed constants instead of loose strings for robust API payloads.
55
+ - **Constants Documentation**: Added `docs/CONSTANTS_REFERENCE.md` detailing all SDK constants (e.g., `ExchangeSegment`, `ProductType`, `OrderType`, etc.).
56
+
57
+ ### Changed
58
+ - Replaced 160+ hardcoded usages of strings like `"NSE_EQ"` and `"BUY"` with `DhanHQ::Constants::ExchangeSegment::NSE_EQ` and `DhanHQ::Constants::TransactionType::BUY`.
59
+ - Added `NO_HOLDINGS` (value `"DH-1111"`) to `TradingErrorCode`.
60
+ - `PlaceOrderContract` refactored to inherit from `OrderContract`, eliminating duplicated validation logic. Derivative-specific fields (`drv_expiry_date`, `drv_option_type`, `drv_strike_price`) remain on `PlaceOrderContract`.
61
+ - `Resources::Orders` now fetches optional instrument metadata (lot size, tick size) to pass into contract validation.
62
+
63
+ ---
64
+
65
+ ## [2.5.0] - 2026-02-21
66
+
67
+ ### Added
68
+
69
+ #### New Endpoints — Full Dhan API v2 Parity
70
+ - **Exit All Positions**: `DhanHQ::Models::Position.exit_all!` — emergency closure of all positions and cancellation of all open orders via `DELETE /v2/positions`. Resource method: `DhanHQ::Resources::Positions#exit_all`.
71
+ - **Kill Switch Status**: `DhanHQ::Models::KillSwitch.status` — query current kill switch state via `GET /v2/killswitch`. Resource method: `DhanHQ::Resources::KillSwitch#status`.
72
+ - **P&L Based Exit**: New `DhanHQ::Models::PnlExit` model and `DhanHQ::Resources::PnlExit` resource for automatic profit/loss-based position exit:
73
+ - `PnlExit.configure(profit_value:, loss_value:, product_type:, enable_kill_switch:)` — `POST /v2/pnlExit`
74
+ - `PnlExit.stop` — `DELETE /v2/pnlExit`
75
+ - `PnlExit.status` — `GET /v2/pnlExit`
76
+ - **Multi-Order Margin Calculator**: `DhanHQ::Models::Margin.calculate_multi` — batch margin calculation with hedge benefit across multiple instruments via `POST /v2/margincalculator/multi`. Resource method: `DhanHQ::Resources::MarginCalculator#calculate_multi`.
77
+ - **EDIS Model**: New `DhanHQ::Models::Edis` wrapping existing `DhanHQ::Resources::Edis` with ORM-style class methods:
78
+ - `Edis.generate_tpin`, `Edis.generate_form`, `Edis.generate_bulk_form`, `Edis.inquire`
79
+ - **Postback Payload Parser**: New `DhanHQ::Models::Postback` utility model for parsing Dhan webhook payloads:
80
+ - `Postback.parse(json_or_hash)` — accepts JSON string or Hash
81
+ - Status predicates: `traded?`, `rejected?`, `pending?`, `cancelled?`
82
+ - **AlertOrder Modify**: Explicit `DhanHQ::Models::AlertOrder.modify(alert_id, params)` class method for updating conditional triggers with better discoverability.
83
+
84
+ #### Tests
85
+ - **28 new specs** across 7 files (442 total, 0 failures):
86
+ - `spec/dhan_hq/models/pnl_exit_spec.rb` — configure, stop, status, defaults, nil handling
87
+ - `spec/dhan_hq/models/edis_spec.rb` — generate_tpin, generate_form, bulk_form, inquire
88
+ - `spec/dhan_hq/models/postback_spec.rb` — JSON/Hash parsing, snake_case support, status predicates
89
+ - Updated: `kill_switch_spec.rb`, `positions_spec.rb`, `margin_spec.rb`, `alert_order_spec.rb`
90
+
91
+ ### Changed
92
+ - **README.md**: Updated Key Features to reflect full API v2 parity including P&L Exit, Postback parser, and EDIS model.
93
+ - **Bundler**: Updated `BUNDLED WITH` to latest version, eliminating platform constant re-definition warnings.
94
+
95
+ ### Notes
96
+ - **Backward Compatible**: All changes are additive — no existing APIs or method signatures changed.
97
+ - **Full API v2 Parity**: The gem now covers every endpoint documented at [dhanhq.co/docs/v2](https://dhanhq.co/docs/v2/).
2
98
 
3
99
  ---
4
100
 
@@ -23,7 +119,7 @@
23
119
 
24
120
  ### Added
25
121
  - **Alert Orders**: `DhanHQ::Resources::AlertOrders` (BaseResource) and `DhanHQ::Models::AlertOrder` with full CRUD. Endpoints: GET/POST `/alerts/orders`, GET/PUT/DELETE `/alerts/orders/{id}` (per API docs). Validation via `DhanHQ::Contracts::AlertOrderContract`.
26
- - **IP Setup**: `DhanHQ::Resources::IPSetup` (resource-only). Methods: `current` (GET `/ip/getIP`), `set(ip:)` (POST `/ip/setIP`), `update(ip:)` (PUT `/ip/modifyIP`) per API docs.
122
+ - **IP Setup**: `DhanHQ::Resources::IPSetup` (resource-only). Methods: `current` (GET `/ip/getIP`), `set(ip:, ip_flag: "PRIMARY", dhan_client_id: nil)` (POST `/ip/setIP`), `update(ip:, ip_flag: "PRIMARY", dhan_client_id: nil)` (PUT `/ip/modifyIP`). Body includes `dhanClientId` (default from config) and `ipFlag` per API docs.
27
123
  - **Trader Control (Kill Switch)**: `DhanHQ::Resources::TraderControl` (resource-only). Methods: `status` (GET `/trader-control`), `enable` (POST action ENABLE), `disable` (POST action DISABLE). `DhanHQ::Resources::KillSwitch` and `DhanHQ::Models::KillSwitch` remain for backward compatibility.
28
124
  - **docs/API_VERIFICATION.md**: Documents alignment with [dhanhq.co/docs/v2](https://dhanhq.co/docs/v2/) and [api.dhan.co/v2](https://api.dhan.co/v2/#/) for EDIS, Alert Orders, IP Setup.
29
125
 
@@ -60,9 +156,9 @@
60
156
  - **README.md**: Note under Dynamic access token for RenewToken via `DhanHQ::Auth.renew_token` and that API key/Partner flows are implemented in the app.
61
157
  - **GUIDE.md**: “Dynamic access token” section extended with RenewToken (`DhanHQ::Auth.renew_token`) and note that API key/Partner flows are in the app.
62
158
  - **docs/TESTING_GUIDE.md**: Optional config comment for RenewToken and pointer to AUTHENTICATION.md (API key/Partner in app).
63
- - **docs/rails_integration.md**: “Dynamic access token” section extended with RenewToken (web-generated tokens) and link to AUTHENTICATION.md.
64
- - **docs/websocket_integration.md**, **docs/live_order_updates.md**: Notes updated for dynamic token, RenewToken, and API key/Partner in app.
65
- - **docs/standalone_ruby_websocket_integration.md**, **docs/rails_websocket_integration.md**: Configuration section updated with RenewToken and AUTHENTICATION.md link.
159
+ - **docs/RAILS_INTEGRATION.md**: “Dynamic access token” section extended with RenewToken (web-generated tokens) and link to AUTHENTICATION.md.
160
+ - **docs/WEBSOCKET_INTEGRATION.md**, **docs/LIVE_ORDER_UPDATES.md**: Notes updated for dynamic token, RenewToken, and API key/Partner in app.
161
+ - **docs/STANDALONE_RUBY_WEBSOCKET_INTEGRATION.md**, **docs/RAILS_WEBSOCKET_INTEGRATION.md**: Configuration section updated with RenewToken and AUTHENTICATION.md link.
66
162
 
67
163
  ### CI / Release
68
164
 
@@ -112,8 +208,8 @@
112
208
  - **GUIDE.md**: Short “Dynamic access token” note and link to docs/AUTHENTICATION.md.
113
209
  - **docs/AUTHENTICATION.md**: New doc for static vs dynamic token, retry-on-401, and auth-related errors.
114
210
  - **docs/TESTING_GUIDE.md**: Optional access_token_provider / on_token_expired in config examples.
115
- - **docs/rails_integration.md**: “Dynamic access token (optional)” with Rails initializer example.
116
- - **docs/websocket_integration.md**, **docs/live_order_updates.md**: Pointer to docs/AUTHENTICATION.md for dynamic token.
211
+ - **docs/RAILS_INTEGRATION.md**: “Dynamic access token (optional)” with Rails initializer example.
212
+ - **docs/WEBSOCKET_INTEGRATION.md**, **docs/LIVE_ORDER_UPDATES.md**: Pointer to docs/AUTHENTICATION.md for dynamic token.
117
213
 
118
214
  ### Backward compatibility
119
215
 
data/GUIDE.md CHANGED
@@ -150,11 +150,11 @@ Example:
150
150
 
151
151
  ```ruby
152
152
  payload = {
153
- transaction_type: "BUY",
154
- exchange_segment: "NSE_EQ",
155
- product_type: "CNC",
156
- order_type: "LIMIT",
157
- validity: "DAY",
153
+ transaction_type: DhanHQ::Constants::TransactionType::BUY,
154
+ exchange_segment: DhanHQ::Constants::ExchangeSegment::NSE_EQ,
155
+ product_type: DhanHQ::Constants::ProductType::CNC,
156
+ order_type: DhanHQ::Constants::OrderType::LIMIT,
157
+ validity: DhanHQ::Constants::Validity::DAY,
158
158
  security_id: "1333",
159
159
  quantity: 10,
160
160
  price: 150.0,
@@ -162,7 +162,7 @@ payload = {
162
162
  }
163
163
 
164
164
  order = DhanHQ::Models::Order.place(payload)
165
- puts order.order_status # => "TRADED" / "PENDING" / ...
165
+ puts order.order_status # => DhanHQ::Constants::OrderStatus::TRADED / "PENDING" / ...
166
166
  ```
167
167
 
168
168
  ### Modification & Cancellation
@@ -193,10 +193,10 @@ Use the same fields as placement, but the contract allows additional validity op
193
193
  ```ruby
194
194
  slice_payload = {
195
195
  order_id: order.order_id,
196
- transaction_type: "BUY",
197
- exchange_segment: "NSE_EQ",
198
- product_type: "STOP_LOSS",
199
- order_type: "STOP_LOSS",
196
+ transaction_type: DhanHQ::Constants::TransactionType::BUY,
197
+ exchange_segment: DhanHQ::Constants::ExchangeSegment::NSE_EQ,
198
+ product_type: DhanHQ::Constants::OrderType::STOP_LOSS,
199
+ order_type: DhanHQ::Constants::OrderType::STOP_LOSS,
200
200
  validity: "GTC",
201
201
  security_id: "1333",
202
202
  quantity: 100,
@@ -381,11 +381,11 @@ The response returns one object per super order with nested `leg_details`. Key a
381
381
 
382
382
  ```ruby
383
383
  params = {
384
- transaction_type: "SELL",
385
- exchange_segment: "NSE_EQ",
386
- product_type: "CNC",
387
- order_type: "LIMIT",
388
- validity: "DAY",
384
+ transaction_type: DhanHQ::Constants::TransactionType::SELL,
385
+ exchange_segment: DhanHQ::Constants::ExchangeSegment::NSE_EQ,
386
+ product_type: DhanHQ::Constants::ProductType::CNC,
387
+ order_type: DhanHQ::Constants::OrderType::LIMIT,
388
+ validity: DhanHQ::Constants::Validity::DAY,
389
389
  security_id: "1333",
390
390
  price: 200.0,
391
391
  trigger_price: 198.0
@@ -416,10 +416,10 @@ Convert an intraday position to delivery (or vice versa):
416
416
  ```ruby
417
417
  convert_payload = {
418
418
  security_id: "1333",
419
- from_product_type: "INTRADAY",
420
- to_product_type: "CNC",
419
+ from_product_type: DhanHQ::Constants::ProductType::INTRADAY,
420
+ to_product_type: DhanHQ::Constants::ProductType::CNC,
421
421
  convert_qty: 10,
422
- exchange_segment: "NSE_EQ",
422
+ exchange_segment: DhanHQ::Constants::ExchangeSegment::NSE_EQ,
423
423
  position_type: "LONG"
424
424
  }
425
425
 
@@ -496,8 +496,8 @@ Both endpoints return arrays and skip validation because they represent historic
496
496
  ```ruby
497
497
  bars = DhanHQ::Models::HistoricalData.intraday(
498
498
  security_id: "13",
499
- exchange_segment: "IDX_I",
500
- instrument: "INDEX",
499
+ exchange_segment: DhanHQ::Constants::ExchangeSegment::IDX_I,
500
+ instrument: DhanHQ::Constants::InstrumentType::INDEX,
501
501
  interval: "5",
502
502
  from_date: "2024-08-14",
503
503
  to_date: "2024-08-14"
@@ -509,13 +509,13 @@ bars = DhanHQ::Models::HistoricalData.intraday(
509
509
  ```ruby
510
510
  chain = DhanHQ::Models::OptionChain.fetch(
511
511
  underlying_scrip: 1333,
512
- underlying_seg: "NSE_FNO",
512
+ underlying_seg: DhanHQ::Constants::ExchangeSegment::NSE_FNO,
513
513
  expiry: "2024-12-26"
514
514
  )
515
515
 
516
516
  expiries = DhanHQ::Models::OptionChain.fetch_expiry_list(
517
517
  underlying_scrip: 1333,
518
- underlying_seg: "NSE_FNO"
518
+ underlying_seg: DhanHQ::Constants::ExchangeSegment::NSE_FNO
519
519
  )
520
520
  ```
521
521
 
@@ -527,10 +527,10 @@ The model filters strikes where both CE and PE have zero `last_price`, keeping t
527
527
 
528
528
  ```ruby
529
529
  params = {
530
- exchange_segment: "NSE_EQ",
531
- transaction_type: "BUY",
530
+ exchange_segment: DhanHQ::Constants::ExchangeSegment::NSE_EQ,
531
+ transaction_type: DhanHQ::Constants::TransactionType::BUY,
532
532
  quantity: 10,
533
- product_type: "INTRADAY",
533
+ product_type: DhanHQ::Constants::ProductType::INTRADAY,
534
534
  security_id: "1333",
535
535
  price: 150.0
536
536
  }
@@ -618,8 +618,8 @@ ws.on(:tick) do |tick|
618
618
  puts "[#{tick[:segment]} #{tick[:security_id]}] LTP=#{tick[:ltp]} kind=#{tick[:kind]}"
619
619
  end
620
620
 
621
- ws.subscribe_one(segment: "IDX_I", security_id: "13")
622
- ws.unsubscribe_one(segment: "IDX_I", security_id: "13")
621
+ ws.subscribe_one(segment: DhanHQ::Constants::ExchangeSegment::IDX_I, security_id: "13")
622
+ ws.unsubscribe_one(segment: DhanHQ::Constants::ExchangeSegment::IDX_I, security_id: "13")
623
623
 
624
624
  ws.disconnect!
625
625
  ```
@@ -643,17 +643,34 @@ If the credentials are invalid the helper raises `DhanHQ::InvalidAuthenticationE
643
643
 
644
644
  ### Alert Orders
645
645
 
646
+ Condition must include `exchange_segment`, `exp_date`, and `frequency` per [conditional-trigger API](https://dhanhq.co/docs/v2/conditional-trigger/). For technical indicators, `time_frame` is also required.
647
+
646
648
  ```ruby
647
649
  # Model (CRUD)
648
650
  DhanHQ::Models::AlertOrder.all
649
651
  alert = DhanHQ::Models::AlertOrder.find("alert-id")
650
652
  alert = DhanHQ::Models::AlertOrder.create(
651
- exchange_segment: "NSE_EQ",
652
- security_id: "11536",
653
- condition: "GTE",
654
- trigger_price: 100.0,
655
- transaction_type: "BUY",
656
- quantity: 10
653
+ condition: {
654
+ security_id: "11536",
655
+ exchange_segment: DhanHQ::Constants::ExchangeSegment::NSE_EQ,
656
+ comparison_type: DhanHQ::Constants::ComparisonType::PRICE_WITH_VALUE,
657
+ operator: DhanHQ::Constants::Operator::GREATER_THAN,
658
+ comparing_value: 100.0,
659
+ exp_date: (Date.today + 365).strftime("%Y-%m-%d"),
660
+ frequency: "ONCE"
661
+ },
662
+ orders: [
663
+ {
664
+ transaction_type: DhanHQ::Constants::TransactionType::BUY,
665
+ exchange_segment: DhanHQ::Constants::ExchangeSegment::NSE_EQ,
666
+ product_type: DhanHQ::Constants::ProductType::CNC,
667
+ order_type: DhanHQ::Constants::OrderType::LIMIT,
668
+ security_id: "11536",
669
+ quantity: 10,
670
+ validity: DhanHQ::Constants::Validity::DAY,
671
+ price: 150.0
672
+ }
673
+ ]
657
674
  )
658
675
  alert.save
659
676
  alert.destroy
@@ -687,13 +704,14 @@ Params use snake_case; the client camelizes them before calling `/edis/...`.
687
704
 
688
705
  ### IP Setup
689
706
 
690
- Resource-only (account configuration) per API docs: GET /ip/getIP, POST /ip/setIP, PUT /ip/modifyIP.
707
+ Resource-only (account configuration) per [API docs](https://dhanhq.co/docs/v2/authentication/#setup-static-ip): GET /ip/getIP, POST /ip/setIP, PUT /ip/modifyIP. Set/Modify require `dhanClientId`, `ip`, and `ipFlag` (PRIMARY or SECONDARY).
691
708
 
692
709
  ```ruby
693
710
  ip = DhanHQ::Resources::IPSetup.new
694
- ip.current # GET /ip/getIP
695
- ip.set(ip: "103.21.58.121")
696
- ip.update(ip: "103.21.58.121")
711
+ ip.current # GET /ip/getIP
712
+ ip.set(ip: "103.21.58.121") # ip_flag defaults to "PRIMARY", dhan_client_id from config
713
+ ip.set(ip: "103.21.58.121", ip_flag: "SECONDARY")
714
+ ip.update(ip: "103.21.58.121", ip_flag: "PRIMARY")
697
715
  ```
698
716
 
699
717
  ### Trader Control (Kill Switch)
@@ -709,7 +727,7 @@ tc.enable # Trading resumed
709
727
 
710
728
  ### Kill Switch (model)
711
729
 
712
- Existing model API for backward compatibility:
730
+ Uses query parameter per API doc: `POST /v2/killswitch?killSwitchStatus=ACTIVATE` (or DEACTIVATE), no body.
713
731
 
714
732
  ```ruby
715
733
  activate_payload = DhanHQ::Models::KillSwitch.activate
@@ -725,7 +743,7 @@ DhanHQ::Models::KillSwitch.snake_case(deactivate_payload)
725
743
  DhanHQ::Models::KillSwitch.update("ACTIVATE")
726
744
  ```
727
745
 
728
- 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.
746
+ Only `"ACTIVATE"` and `"DEACTIVATE"` are accepted. Use the `snake_case` helper to normalise API responses when you prefer underscore keys.
729
747
 
730
748
  ---
731
749