DhanHQ 2.1.3 → 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 (63) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +2 -0
  3. data/.rubocop_todo.yml +185 -0
  4. data/CHANGELOG.md +31 -0
  5. data/GUIDE.md +173 -31
  6. data/README.md +437 -133
  7. data/README1.md +267 -26
  8. data/docs/live_order_updates.md +319 -0
  9. data/docs/rails_integration.md +1 -1
  10. data/docs/rails_websocket_integration.md +847 -0
  11. data/docs/standalone_ruby_websocket_integration.md +1588 -0
  12. data/docs/technical_analysis.md +1 -0
  13. data/docs/websocket_integration.md +871 -0
  14. data/examples/comprehensive_websocket_examples.rb +148 -0
  15. data/examples/instrument_finder_test.rb +195 -0
  16. data/examples/live_order_updates.rb +118 -0
  17. data/examples/market_depth_example.rb +144 -0
  18. data/examples/market_feed_example.rb +81 -0
  19. data/examples/order_update_example.rb +105 -0
  20. data/examples/trading_fields_example.rb +215 -0
  21. data/lib/DhanHQ/config.rb +1 -0
  22. data/lib/DhanHQ/configuration.rb +16 -1
  23. data/lib/DhanHQ/contracts/expired_options_data_contract.rb +103 -0
  24. data/lib/DhanHQ/contracts/modify_order_contract.rb +1 -0
  25. data/lib/DhanHQ/contracts/option_chain_contract.rb +11 -1
  26. data/lib/DhanHQ/contracts/trade_contract.rb +70 -0
  27. data/lib/DhanHQ/errors.rb +2 -0
  28. data/lib/DhanHQ/models/expired_options_data.rb +331 -0
  29. data/lib/DhanHQ/models/instrument.rb +96 -2
  30. data/lib/DhanHQ/models/option_chain.rb +2 -0
  31. data/lib/DhanHQ/models/order_update.rb +235 -0
  32. data/lib/DhanHQ/models/trade.rb +118 -31
  33. data/lib/DhanHQ/rate_limiter.rb +4 -2
  34. data/lib/DhanHQ/resources/expired_options_data.rb +22 -0
  35. data/lib/DhanHQ/version.rb +1 -1
  36. data/lib/DhanHQ/ws/base_connection.rb +249 -0
  37. data/lib/DhanHQ/ws/client.rb +1 -1
  38. data/lib/DhanHQ/ws/connection.rb +3 -3
  39. data/lib/DhanHQ/ws/decoder.rb +3 -3
  40. data/lib/DhanHQ/ws/market_depth/client.rb +376 -0
  41. data/lib/DhanHQ/ws/market_depth/decoder.rb +131 -0
  42. data/lib/DhanHQ/ws/market_depth.rb +74 -0
  43. data/lib/DhanHQ/ws/orders/client.rb +177 -10
  44. data/lib/DhanHQ/ws/orders/connection.rb +41 -83
  45. data/lib/DhanHQ/ws/orders.rb +31 -2
  46. data/lib/DhanHQ/ws/registry.rb +1 -0
  47. data/lib/DhanHQ/ws/segments.rb +21 -5
  48. data/lib/DhanHQ/ws/sub_state.rb +1 -1
  49. data/lib/DhanHQ/ws.rb +3 -2
  50. data/lib/{DhanHQ.rb → dhan_hq.rb} +5 -0
  51. data/lib/dhanhq/analysis/helpers/bias_aggregator.rb +18 -18
  52. data/lib/dhanhq/analysis/helpers/moneyness_helper.rb +1 -0
  53. data/lib/dhanhq/analysis/multi_timeframe_analyzer.rb +2 -0
  54. data/lib/dhanhq/analysis/options_buying_advisor.rb +4 -3
  55. data/lib/dhanhq/contracts/options_buying_advisor_contract.rb +1 -0
  56. data/lib/ta/candles.rb +1 -0
  57. data/lib/ta/fetcher.rb +1 -0
  58. data/lib/ta/indicators.rb +2 -1
  59. data/lib/ta/market_calendar.rb +4 -3
  60. data/lib/ta/technical_analysis.rb +3 -2
  61. metadata +38 -4
  62. data/lib/DhanHQ/ws/errors.rb +0 -0
  63. /data/lib/DhanHQ/contracts/{modify_order_contract copy.rb → modify_order_contract_copy.rb} +0 -0
data/README.md CHANGED
@@ -4,8 +4,28 @@ A clean Ruby client for **Dhan API v2** with ORM-like models (Orders, Positions,
4
4
 
5
5
  * ActiveRecord-style models: `find`, `all`, `where`, `save`, `update`, `cancel`
6
6
  * Validations & errors exposed via ActiveModel-like interfaces
7
- * REST coverage: Orders, Super Orders, Forever Orders, Trades, Positions, Holdings, Funds/Margin, HistoricalData, OptionChain, MarketFeed
8
- * **WebSocket**: subscribe/unsubscribe dynamically, auto-reconnect with backoff, 429 cool-off, idempotent subs, header+payload binary parsing, normalized ticks
7
+ * REST coverage: Orders, Super Orders, Forever Orders, Trades, Positions, Holdings, Funds/Margin, HistoricalData, OptionChain, MarketFeed, ExpiredOptionsData
8
+ * **WebSocket**: Orders, Market Feed, Market Depth - subscribe/unsubscribe dynamically, auto-reconnect with backoff, 429 cool-off, idempotent subs, header+payload binary parsing, normalized ticks
9
+
10
+ ## ⚠️ BREAKING CHANGE NOTICE
11
+
12
+ **IMPORTANT**: Starting from version 2.1.5, the require statement has changed:
13
+
14
+ ```ruby
15
+ # OLD (deprecated)
16
+ require 'DhanHQ'
17
+
18
+ # NEW (current)
19
+ require 'dhan_hq'
20
+ ```
21
+
22
+ **Migration**: Update all your `require 'DhanHQ'` statements to `require 'dhan_hq'` in your codebase. This change affects:
23
+ - All Ruby files that require the gem
24
+ - Documentation examples
25
+ - Scripts and automation tools
26
+ - Rails applications using this gem
27
+
28
+ The gem name remains `DhanHQ` in your Gemfile, only the require statement changes.
9
29
 
10
30
  ---
11
31
 
@@ -36,7 +56,7 @@ gem install DhanHQ
36
56
  ### From ENV / .env
37
57
 
38
58
  ```ruby
39
- require 'DhanHQ'
59
+ require 'dhan_hq'
40
60
 
41
61
  DhanHQ.configure_with_env
42
62
  DhanHQ.logger.level = (ENV["DHAN_LOG_LEVEL"] || "INFO").upcase.then { |level| Logger.const_get(level) }
@@ -126,153 +146,194 @@ initializers, service objects, workers, and ActionCable wiring tailored for the
126
146
 
127
147
  ---
128
148
 
129
- ## WebSocket Market Feed (NEW)
149
+ ## WebSocket Integration (Orders, Market Feed, Market Depth)
130
150
 
131
- ### What you get
151
+ The DhanHQ gem provides comprehensive WebSocket integration with three distinct WebSocket types, featuring improved architecture, security, and reliability:
132
152
 
133
- * **Modes**
153
+ ### Key Features
134
154
 
135
- * `:ticker` LTP + LTT
136
- * `:quote` → OHLCV + totals (recommended default)
137
- * `:full` → quote + **OI** + **best-5 depth**
138
- * **Normalized ticks** (Hash):
155
+ - **🔒 Secure Logging** - Sensitive information (access tokens) are automatically sanitized from logs
156
+ - **⚡ Rate Limit Protection** - Built-in protection against 429 errors with proper connection management
157
+ - **🔄 Automatic Reconnection** - Exponential backoff with 60-second cool-off periods
158
+ - **🧵 Thread-Safe Operation** - Safe for Rails applications and multi-threaded environments
159
+ - **📊 Comprehensive Examples** - Ready-to-use examples for all WebSocket types
160
+ - **🛡️ Error Handling** - Robust error handling and connection management
161
+ - **🔍 Dynamic Symbol Resolution** - Easy instrument lookup using `.find()` method
139
162
 
140
- ```ruby
141
- {
142
- kind: :quote, # :ticker | :quote | :full | :oi | :prev_close | :misc
143
- segment: "NSE_FNO", # string enum
144
- security_id: "12345",
145
- ltp: 101.5,
146
- ts: 1723791300, # LTT epoch (sec) if present
147
- vol: 123456, # quote/full
148
- atp: 100.9, # quote/full
149
- day_open: 100.1, day_high: 102.4, day_low: 99.5, day_close: nil,
150
- oi: 987654, # full or OI packet
151
- bid: 101.45, ask: 101.55 # from depth (mode :full)
152
- }
153
- ```
163
+ ### 1. Orders WebSocket - Real-time Order Updates
154
164
 
155
- ### Start, subscribe, stop
165
+ Receive live updates whenever your orders transition between states (placed → traded → cancelled, etc.).
156
166
 
157
167
  ```ruby
158
- require 'DhanHQ'
159
-
160
- DhanHQ.configure_with_env
161
- DhanHQ.logger.level = (ENV["DHAN_LOG_LEVEL"] || "INFO").upcase.then { |level| Logger.const_get(level) }
162
-
163
- ws = DhanHQ::WS::Client.new(mode: :quote).start
164
-
165
- ws.on(:tick) do |t|
166
- puts "[#{t[:segment]}:#{t[:security_id]}] LTP=#{t[:ltp]} kind=#{t[:kind]}"
168
+ # Simple connection
169
+ DhanHQ::WS::Orders.connect do |order_update|
170
+ puts "Order Update: #{order_update.order_no} - #{order_update.status}"
171
+ puts " Symbol: #{order_update.symbol}"
172
+ puts " Quantity: #{order_update.quantity}"
173
+ puts " Traded Qty: #{order_update.traded_qty}"
174
+ puts " Price: #{order_update.price}"
175
+ puts " Execution: #{order_update.execution_percentage}%"
167
176
  end
168
177
 
169
- # Subscribe instruments (≤100 per frame; send multiple frames if needed)
170
- ws.subscribe_one(segment: "IDX_I", security_id: "13") # NIFTY index value
171
- ws.subscribe_one(segment: "NSE_FNO", security_id: "12345") # an option
172
-
173
- # Unsubscribe
174
- ws.unsubscribe_one(segment: "NSE_FNO", security_id: "12345")
178
+ # Advanced usage with multiple event handlers
179
+ client = DhanHQ::WS::Orders.client
180
+ client.on(:update) { |order| puts "📝 Order updated: #{order.order_no}" }
181
+ client.on(:status_change) { |change| puts "🔄 Status: #{change[:previous_status]} -> #{change[:new_status]}" }
182
+ client.on(:execution) { |exec| puts "✅ Executed: #{exec[:new_traded_qty]} shares" }
183
+ client.on(:order_rejected) { |order| puts " Rejected: #{order.order_no}" }
184
+ client.start
185
+ ```
175
186
 
176
- # Graceful disconnect (sends broker disconnect code 12, no reconnect)
177
- ws.disconnect!
187
+ ### 2. Market Feed WebSocket - Live Market Data
178
188
 
179
- # Or hard stop (no broker message, just closes and halts loop)
180
- ws.stop
189
+ Subscribe to real-time market data for indices and stocks.
181
190
 
182
- # Safety: kill all local sockets (useful in IRB)
183
- DhanHQ::WS.disconnect_all_local!
184
- ```
191
+ ```ruby
192
+ # Ticker data (LTP updates) - Recommended for most use cases
193
+ market_client = DhanHQ::WS.connect(mode: :ticker) do |tick|
194
+ timestamp = tick[:ts] ? Time.at(tick[:ts]) : Time.now
195
+ puts "Market Data: #{tick[:segment]}:#{tick[:security_id]} = #{tick[:ltp]} at #{timestamp}"
196
+ end
185
197
 
186
- ### Under the hood
198
+ # Subscribe to major Indian indices
199
+ market_client.subscribe_one(segment: "IDX_I", security_id: "13") # NIFTY
200
+ market_client.subscribe_one(segment: "IDX_I", security_id: "25") # BANKNIFTY
201
+ market_client.subscribe_one(segment: "IDX_I", security_id: "29") # NIFTYIT
202
+ market_client.subscribe_one(segment: "IDX_I", security_id: "51") # SENSEX
187
203
 
188
- * **Request codes** (per Dhan docs)
204
+ # Quote data (LTP + volume + OHLC)
205
+ DhanHQ::WS.connect(mode: :quote) do |quote|
206
+ puts "#{quote[:symbol]}: LTP=#{quote[:ltp]}, Volume=#{quote[:vol]}"
207
+ end
189
208
 
190
- * Subscribe: **15** (ticker), **17** (quote), **21** (full)
191
- * Unsubscribe: **16**, **18**, **22**
192
- * Disconnect: **12**
193
- * **Limits**
209
+ # Full market data
210
+ DhanHQ::WS.connect(mode: :full) do |full|
211
+ puts "#{full[:symbol]}: #{full.inspect}"
212
+ end
213
+ ```
194
214
 
195
- * Up to **100 instruments per SUB/UNSUB** message (client auto-chunks)
196
- * Up to 5 WS connections per user (per Dhan)
197
- * **Backoff & 429 cool-off**
215
+ ### 3. Market Depth WebSocket - Real-time Market Depth
198
216
 
199
- * Exponential backoff with jitter
200
- * Handshake **429** triggers a **60s cool-off** before retry
201
- * **Reconnect & resubscribe**
217
+ Get real-time market depth data including bid/ask levels and order book information.
202
218
 
203
- * On reconnect the client resends the **current subscription snapshot** (idempotent)
204
- * **Graceful shutdown**
219
+ ```ruby
220
+ # Real-time market depth for stocks (using dynamic symbol resolution with underlying_symbol)
221
+ reliance = DhanHQ::Models::Instrument.find("NSE_EQ", "RELIANCE")
222
+ tcs = DhanHQ::Models::Instrument.find("NSE_EQ", "TCS")
205
223
 
206
- * `ws.disconnect!` or `ws.stop` prevents reconnects
207
- * An `at_exit` hook stops all registered WS clients to avoid leaked sockets
224
+ symbols = []
225
+ if reliance
226
+ symbols << { symbol: "RELIANCE", exchange_segment: reliance.exchange_segment, security_id: reliance.security_id }
227
+ end
228
+ if tcs
229
+ symbols << { symbol: "TCS", exchange_segment: tcs.exchange_segment, security_id: tcs.security_id }
230
+ end
208
231
 
209
- ---
232
+ DhanHQ::WS::MarketDepth.connect(symbols: symbols) do |depth_data|
233
+ puts "Market Depth: #{depth_data[:symbol]}"
234
+ puts " Best Bid: #{depth_data[:best_bid]}"
235
+ puts " Best Ask: #{depth_data[:best_ask]}"
236
+ puts " Spread: #{depth_data[:spread]}"
237
+ puts " Bid Levels: #{depth_data[:bids].size}"
238
+ puts " Ask Levels: #{depth_data[:asks].size}"
239
+ end
240
+ ```
210
241
 
211
- ## Order Update WebSocket (NEW)
242
+ ### Unified WebSocket Architecture
212
243
 
213
- Receive live updates whenever your orders transition between states (placed → traded → cancelled, etc.).
244
+ All WebSocket connections provide:
245
+ - **Automatic reconnection** with exponential backoff
246
+ - **Thread-safe operation** for Rails applications
247
+ - **Consistent event handling** patterns
248
+ - **Built-in error handling** and logging
249
+ - **429 rate limiting** protection with cool-off periods
250
+ - **Secure logging** with automatic credential sanitization
214
251
 
215
- ### Standalone Ruby script
252
+ ### Connection Management
216
253
 
217
254
  ```ruby
218
- require 'DhanHQ'
255
+ # Sequential connections to avoid rate limiting (recommended)
256
+ orders_client = DhanHQ::WS::Orders.connect { |order| puts "Order: #{order.order_no}" }
257
+ orders_client.stop
258
+ sleep(2) # Wait between connections
219
259
 
220
- DhanHQ.configure_with_env
221
- DhanHQ.logger.level = (ENV["DHAN_LOG_LEVEL"] || "INFO").upcase.then { |level| Logger.const_get(level) }
260
+ market_client = DhanHQ::WS.connect(mode: :ticker) { |tick| puts "Market: #{tick[:symbol]}" }
261
+ market_client.stop
262
+ sleep(2)
222
263
 
223
- ou = DhanHQ::WS::Orders::Client.new.start
264
+ depth_client = DhanHQ::WS::MarketDepth.connect(symbols: symbols) { |depth| puts "Depth: #{depth[:symbol]}" }
265
+ depth_client.stop
224
266
 
225
- ou.on(:update) do |payload|
226
- data = payload[:Data] || {}
227
- puts "ORDER #{data[:OrderNo]} #{data[:Status]} traded=#{data[:TradedQty]} avg=#{data[:AvgTradedPrice]}"
228
- end
267
+ # Check connection status
268
+ puts "Orders connected: #{orders_client.connected?}"
269
+ puts "Market connected: #{market_client.connected?}"
270
+ puts "Depth connected: #{depth_client.connected?}"
229
271
 
230
- # Keep the script alive (CTRL+C to exit)
231
- sleep
232
-
233
- # Later, stop the socket
234
- ou.stop
272
+ # Graceful shutdown
273
+ DhanHQ::WS.disconnect_all_local!
235
274
  ```
236
275
 
237
- Or, if you just need a quick callback:
276
+ ### Examples
238
277
 
239
- ```ruby
240
- DhanHQ::WS::Orders.connect do |payload|
241
- # handle :update callbacks only
242
- end
243
- ```
278
+ The gem includes comprehensive examples in the `examples/` directory:
244
279
 
245
- ### Rails bot integration
280
+ - `market_feed_example.rb` - Market Feed WebSocket with major indices
281
+ - `order_update_example.rb` - Order Update WebSocket with event handling
282
+ - `market_depth_example.rb` - Market Depth WebSocket with RELIANCE and TCS
283
+ - `comprehensive_websocket_examples.rb` - All three WebSocket types
246
284
 
247
- Mirror the market-feed supervisor by adding an Order Update hub singleton that hydrates your local DB and hands off to execution services.
285
+ Run examples:
248
286
 
249
- 1. **Service** – `app/services/live/order_update_hub.rb`
287
+ ```bash
288
+ # Individual examples
289
+ bundle exec ruby examples/market_feed_example.rb
290
+ bundle exec ruby examples/order_update_example.rb
291
+ bundle exec ruby examples/market_depth_example.rb
250
292
 
251
- ```ruby
252
- Live::OrderUpdateHub.instance.start!
253
- ```
293
+ # Comprehensive example
294
+ bundle exec ruby examples/comprehensive_websocket_examples.rb
295
+ ```
254
296
 
255
- The hub wires `DhanHQ::WS::Orders::Client` to:
297
+ ### Instrument Model with Trading Fields
256
298
 
257
- * Upsert local `BrokerOrder` rows so UIs always reflect current broker status.
258
- * Auto-subscribe traded entry legs on your existing `Live::WsHub` (if defined).
259
- * Refresh `Execution::PositionGuard` (if present) with fill prices/qty for trailing exits.
299
+ The Instrument model now includes comprehensive trading fields for order validation, risk management, and compliance:
260
300
 
261
- 2. **Initializer** – `config/initializers/order_update_hub.rb`
301
+ ```ruby
302
+ # Find instrument with trading fields
303
+ reliance = DhanHQ::Models::Instrument.find("NSE_EQ", "RELIANCE")
304
+
305
+ # Trading permissions and restrictions
306
+ puts "Trading Allowed: #{reliance.buy_sell_indicator == 'A'}"
307
+ puts "Bracket Orders: #{reliance.bracket_flag == 'Y' ? 'Supported' : 'Not Supported'}"
308
+ puts "Cover Orders: #{reliance.cover_flag == 'Y' ? 'Supported' : 'Not Supported'}"
309
+ puts "ASM/GSM Status: #{reliance.asm_gsm_flag == 'Y' ? reliance.asm_gsm_category : 'No Restrictions'}"
310
+
311
+ # Margin and leverage information
312
+ puts "ISIN: #{reliance.isin}"
313
+ puts "MTF Leverage: #{reliance.mtf_leverage}x"
314
+ puts "Buy Margin %: #{reliance.buy_co_min_margin_per}%"
315
+ puts "Sell Margin %: #{reliance.sell_co_min_margin_per}%"
316
+ ```
262
317
 
263
- ```ruby
264
- if ENV["ENABLE_WS"] == "true"
265
- Rails.application.config.to_prepare do
266
- Live::OrderUpdateHub.instance.start!
267
- end
318
+ **Available Trading Fields:**
319
+ - `isin` - International Securities Identification Number
320
+ - `instrument_type` - Classification (ES, INDEX, FUT, OPT)
321
+ - `expiry_flag` - Whether instrument has expiry
322
+ - `bracket_flag` - Bracket order support
323
+ - `cover_flag` - Cover order support
324
+ - `asm_gsm_flag` - Additional Surveillance Measure status
325
+ - `buy_sell_indicator` - Trading permission
326
+ - `buy_co_min_margin_per` - Buy CO minimum margin percentage
327
+ - `sell_co_min_margin_per` - Sell CO minimum margin percentage
328
+ - `mtf_leverage` - Margin Trading Facility leverage
268
329
 
269
- at_exit { Live::OrderUpdateHub.instance.stop! }
270
- end
271
- ```
330
+ ### Comprehensive Documentation
272
331
 
273
- Flip `ENABLE_WS=true` in your Procfile or `.env` to boot the hub alongside the existing feed supervisor. On shutdown the client is stopped cleanly to avoid leaked sockets.
332
+ The gem includes detailed documentation for different integration scenarios:
274
333
 
275
- The hub is resilient to missing dependencies—if you do not have a `BrokerOrder` model, it safely skips persistence while keeping downstream callbacks alive.
334
+ - **[WebSocket Integration Guide](docs/websocket_integration.md)** - Complete guide covering all WebSocket types and trading fields
335
+ - **[Rails Integration Guide](docs/rails_websocket_integration.md)** - Rails-specific patterns and best practices
336
+ - **[Standalone Ruby Guide](docs/standalone_ruby_websocket_integration.md)** - Scripts, daemons, and CLI tools
276
337
 
277
338
  ---
278
339
 
@@ -377,40 +438,283 @@ end
377
438
 
378
439
  ---
379
440
 
380
- ## Super Orders (example)
441
+ ## Super Orders
381
442
 
382
- ```ruby
383
- intent = {
384
- exchange_segment: "NSE_FNO",
385
- security_id: "12345", # option
386
- transaction_type: "BUY",
387
- quantity: 50,
388
- # derived risk params from ATR/ADX
389
- take_profit: 0.35, # 35% target
390
- stop_loss: 0.18, # 18% SL
391
- trailing_sl: 0.12 # 12% trail
443
+ 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.
444
+
445
+ This gem exposes the full REST surface to create, modify, cancel, and list super orders across all supported exchanges and segments.
446
+
447
+ ### Endpoints
448
+
449
+ | Method | Path | Description |
450
+ | -------- | -------------------------------------- | ------------------------------------- |
451
+ | `POST` | `/super/orders` | Create a new super order |
452
+ | `PUT` | `/super/orders/{order_id}` | Modify a pending super order |
453
+ | `DELETE` | `/super/orders/{order_id}/{order_leg}` | Cancel a pending super order leg |
454
+ | `GET` | `/super/orders` | Retrieve the list of all super orders |
455
+
456
+ ### Place Super Order
457
+
458
+ 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.
459
+
460
+ > ℹ️ Static IP whitelisting with Dhan support is required before invoking this API.
461
+
462
+ ```bash
463
+ curl --request POST \
464
+ --url https://api.dhan.co/v2/super/orders \
465
+ --header 'Content-Type: application/json' \
466
+ --header 'access-token: JWT' \
467
+ --data '{Request JSON}'
468
+ ```
469
+
470
+ #### Request body
471
+
472
+ ```json
473
+ {
474
+ "dhan_client_id": "1000000003",
475
+ "correlation_id": "123abc678",
476
+ "transaction_type": "BUY",
477
+ "exchange_segment": "NSE_EQ",
478
+ "product_type": "CNC",
479
+ "order_type": "LIMIT",
480
+ "security_id": "11536",
481
+ "quantity": 5,
482
+ "price": 1500,
483
+ "target_price": 1600,
484
+ "stop_loss_price": 1400,
485
+ "trailing_jump": 10
392
486
  }
487
+ ```
393
488
 
394
- # If your SuperOrder model exposes create/modify:
395
- o = DhanHQ::Models::SuperOrder.create(intent)
396
- # or fallback:
397
- mkt = DhanHQ::Models::Order.new(
398
- transaction_type: "BUY", exchange_segment: "NSE_FNO",
399
- order_type: "MARKET", validity: "DAY",
400
- security_id: "12345", quantity: 50
401
- ).save
489
+ #### Parameters
490
+
491
+ | Field | Type | Description |
492
+ | ------------------ | ------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
493
+ | `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. |
494
+ | `correlation_id` | string | Caller generated correlation identifier |
495
+ | `transaction_type` | enum string *(required)* | Trading side. `BUY` or `SELL`. |
496
+ | `exchange_segment` | enum string *(required)* | Exchange segment (see appendix). |
497
+ | `product_type` | enum string *(required)* | Product type. `CNC`, `INTRADAY`, `MARGIN`, or `MTF`. |
498
+ | `order_type` | enum string *(required)* | Order type. `LIMIT` or `MARKET`. |
499
+ | `security_id` | string *(required)* | Exchange standard security identifier. |
500
+ | `quantity` | integer *(required)* | Number of shares for the order. |
501
+ | `price` | float *(required)* | Price at which the entry leg is placed. |
502
+ | `target_price` | float *(required)* | Target price for the super order. |
503
+ | `stop_loss_price` | float *(required)* | Stop-loss price for the super order. |
504
+ | `trailing_jump` | float *(required)* | Price jump size used to trail the stop-loss. |
505
+
506
+ > 🐍 When you call `DhanHQ::Models::SuperOrder.create`, pass snake_case keys as shown above. The client automatically camelizes
507
+ > them before posting to Dhan's REST API and injects your configured `dhan_client_id`, so you can omit that key in Ruby code.
508
+
509
+ #### Response
510
+
511
+ ```json
512
+ {
513
+ "order_id": "112111182198",
514
+ "order_status": "PENDING"
515
+ }
402
516
  ```
403
517
 
404
- If you placed a Super Order and want to trail SL upward using WS ticks:
518
+ | Field | Type | Description |
519
+ | -------------- | ----------- | --------------------------------------------------- |
520
+ | `order_id` | string | Order identifier generated by Dhan |
521
+ | `order_status` | enum string | Latest status. `TRANSIT`, `PENDING`, or `REJECTED`. |
405
522
 
406
- ```ruby
407
- DhanHQ::Models::SuperOrder.modify(
408
- order_id: o.order_id,
409
- stop_loss: new_abs_price, # broker API permitting
410
- trailing_sl: nil
411
- )
523
+ ### Modify Super Order
524
+
525
+ Use the modify endpoint to update any leg while the super order remains in `PENDING` or `PART_TRADED` status.
526
+
527
+ > ℹ️ Static IP whitelisting with Dhan support is required before invoking this API.
528
+
529
+ ```bash
530
+ curl --request PUT \
531
+ --url https://api.dhan.co/v2/super/orders/{order_id} \
532
+ --header 'Content-Type: application/json' \
533
+ --header 'access-token: JWT' \
534
+ --data '{Request JSON}'
535
+ ```
536
+
537
+ #### Request body
538
+
539
+ ```json
540
+ {
541
+ "dhan_client_id": "1000000009",
542
+ "order_id": "112111182045",
543
+ "order_type": "LIMIT",
544
+ "leg_name": "ENTRY_LEG",
545
+ "quantity": 40,
546
+ "price": 1300,
547
+ "target_price": 1450,
548
+ "stop_loss_price": 1350,
549
+ "trailing_jump": 20
550
+ }
412
551
  ```
413
552
 
553
+ #### Parameters
554
+
555
+ | Field | Type | Description |
556
+ | ----------------- | -------------------------------------- | ------------------------------------------------------------------------------------------------------------------------- |
557
+ | `dhan_client_id` | string *(required)* | User specific identification generated by Dhan. Automatically added when you call through the Ruby models. |
558
+ | `order_id` | string *(required)* | Super order identifier generated by Dhan. |
559
+ | `order_type` | enum string *(conditionally required)* | `LIMIT` or `MARKET`. Required when modifying `ENTRY_LEG`. |
560
+ | `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`. |
561
+ | `quantity` | integer *(conditionally required)* | Quantity update for `ENTRY_LEG`. |
562
+ | `price` | float *(conditionally required)* | Entry price update for `ENTRY_LEG`. |
563
+ | `target_price` | float *(conditionally required)* | Target price update for `ENTRY_LEG` or `TARGET_LEG`. |
564
+ | `stop_loss_price` | float *(conditionally required)* | Stop-loss price update for `ENTRY_LEG` or `STOP_LOSS_LEG`. |
565
+ | `trailing_jump` | float *(conditionally required)* | Trailing jump update for `ENTRY_LEG` or `STOP_LOSS_LEG`. Omit or set to `0` to cancel trailing. |
566
+
567
+ > ℹ️ Once the entry leg status becomes `TRADED`, only the `TARGET_LEG` and `STOP_LOSS_LEG` can be modified (price and trailing jump).
568
+
569
+ #### Response
570
+
571
+ ```json
572
+ {
573
+ "order_id": "112111182045",
574
+ "order_status": "TRANSIT"
575
+ }
576
+ ```
577
+
578
+ | Field | Type | Description |
579
+ | -------------- | ----------- | ------------------------------------------------------------- |
580
+ | `order_id` | string | Order identifier generated by Dhan |
581
+ | `order_status` | enum string | Latest status. `TRANSIT`, `PENDING`, `REJECTED`, or `TRADED`. |
582
+
583
+ ### Cancel Super Order
584
+
585
+ 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.
586
+
587
+ > ℹ️ Static IP whitelisting with Dhan support is required before invoking this API.
588
+
589
+ ```bash
590
+ curl --request DELETE \
591
+ --url https://api.dhan.co/v2/super/orders/{order_id}/{order_leg} \
592
+ --header 'Content-Type: application/json' \
593
+ --header 'access-token: JWT'
594
+ ```
595
+
596
+ #### Path parameters
597
+
598
+ | Field | Description | Example |
599
+ | ----------- | ------------------------------------------------------------- | ------------- |
600
+ | `order_id` | Super order identifier. | `11211182198` |
601
+ | `order_leg` | Leg to cancel. `ENTRY_LEG`, `TARGET_LEG`, or `STOP_LOSS_LEG`. | `ENTRY_LEG` |
602
+
603
+ #### Response
604
+
605
+ ```json
606
+ {
607
+ "order_id": "112111182045",
608
+ "order_status": "CANCELLED"
609
+ }
610
+ ```
611
+
612
+ | Field | Type | Description |
613
+ | -------------- | ----------- | ---------------------------------------------------------------- |
614
+ | `order_id` | string | Order identifier generated by Dhan |
615
+ | `order_status` | enum string | Latest status. `TRANSIT`, `PENDING`, `REJECTED`, or `CANCELLED`. |
616
+
617
+ ### Super Order List
618
+
619
+ 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.
620
+
621
+ ```bash
622
+ curl --request GET \
623
+ --url https://api.dhan.co/v2/super/orders \
624
+ --header 'Content-Type: application/json' \
625
+ --header 'access-token: JWT'
626
+ ```
627
+
628
+ #### Response
629
+
630
+ ```json
631
+ [
632
+ {
633
+ "dhan_client_id": "1100003626",
634
+ "order_id": "5925022734212",
635
+ "correlation_id": "string",
636
+ "order_status": "PENDING",
637
+ "transaction_type": "BUY",
638
+ "exchange_segment": "NSE_EQ",
639
+ "product_type": "CNC",
640
+ "order_type": "LIMIT",
641
+ "validity": "DAY",
642
+ "trading_symbol": "HDFCBANK",
643
+ "security_id": "1333",
644
+ "quantity": 10,
645
+ "remaining_quantity": 10,
646
+ "ltp": 1660.95,
647
+ "price": 1500,
648
+ "after_market_order": false,
649
+ "leg_name": "ENTRY_LEG",
650
+ "exchange_order_id": "11925022734212",
651
+ "create_time": "2025-02-27 19:09:42",
652
+ "update_time": "2025-02-27 19:09:42",
653
+ "exchange_time": "2025-02-27 19:09:42",
654
+ "oms_error_description": "",
655
+ "average_traded_price": 0,
656
+ "filled_qty": 0,
657
+ "leg_details": [
658
+ {
659
+ "order_id": "5925022734212",
660
+ "leg_name": "STOP_LOSS_LEG",
661
+ "transaction_type": "SELL",
662
+ "total_quantity": 0,
663
+ "remaining_quantity": 0,
664
+ "triggered_quantity": 0,
665
+ "price": 1400,
666
+ "order_status": "PENDING",
667
+ "trailing_jump": 10
668
+ },
669
+ {
670
+ "order_id": "5925022734212",
671
+ "leg_name": "TARGET_LEG",
672
+ "transaction_type": "SELL",
673
+ "remaining_quantity": 0,
674
+ "triggered_quantity": 0,
675
+ "price": 1550,
676
+ "order_status": "PENDING",
677
+ "trailing_jump": 0
678
+ }
679
+ ]
680
+ }
681
+ ]
682
+ ```
683
+
684
+ #### Parameters
685
+
686
+ | Field | Type | Description |
687
+ | ----------------------- | ----------- | --------------------------------------------------------------------------------------------------- |
688
+ | `dhan_client_id` | string | User specific identification generated by Dhan. |
689
+ | `order_id` | string | Order identifier generated by Dhan. |
690
+ | `correlation_id` | string | Correlation identifier supplied by the caller. |
691
+ | `order_status` | enum string | Latest status. `TRANSIT`, `PENDING`, `CLOSED`, `REJECTED`, `CANCELLED`, `PART_TRADED`, or `TRADED`. |
692
+ | `transaction_type` | enum string | Trading side. `BUY` or `SELL`. |
693
+ | `exchange_segment` | enum string | Exchange segment. |
694
+ | `product_type` | enum string | Product type. `CNC`, `INTRADAY`, `MARGIN`, or `MTF`. |
695
+ | `order_type` | enum string | Order type. `LIMIT` or `MARKET`. |
696
+ | `validity` | enum string | Order validity. `DAY`. |
697
+ | `trading_symbol` | string | Trading symbol reference. |
698
+ | `security_id` | string | Exchange security identifier. |
699
+ | `quantity` | integer | Ordered quantity. |
700
+ | `remaining_quantity` | integer | Quantity pending execution. |
701
+ | `ltp` | float | Last traded price. |
702
+ | `price` | float | Order price. |
703
+ | `after_market_order` | boolean | Indicates if the order was placed after market hours. |
704
+ | `leg_name` | enum string | Leg identifier: `ENTRY_LEG`, `TARGET_LEG`, or `STOP_LOSS_LEG`. |
705
+ | `trailing_jump` | float | Trailing jump for stop-loss. |
706
+ | `exchange_order_id` | string | Exchange-generated order identifier. |
707
+ | `create_time` | string | Order creation timestamp. |
708
+ | `update_time` | string | Latest update timestamp. |
709
+ | `exchange_time` | string | Exchange timestamp. |
710
+ | `oms_error_description` | string | OMS error description when applicable. |
711
+ | `average_traded_price` | float | Average traded price. |
712
+ | `filled_qty` | integer | Quantity traded on the exchange. |
713
+ | `triggered_quantity` | integer | Quantity triggered for stop-loss or target legs. |
714
+ | `leg_details` | array | Nested leg details for the super order. |
715
+
716
+ > ✅ `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.
717
+
414
718
  ---
415
719
 
416
720
  ## Packet parsing (for reference)