DhanHQ 2.6.2 → 2.6.3

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 (61) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +15 -3
  3. data/ARCHITECTURE.md +113 -0
  4. data/CHANGELOG.md +31 -0
  5. data/README.md +2 -0
  6. data/docs/API_VERIFICATION.md +10 -8
  7. data/docs/ENDPOINTS_AND_SANDBOX.md +12 -0
  8. data/lib/DhanHQ/auth.rb +2 -2
  9. data/lib/DhanHQ/client.rb +42 -34
  10. data/lib/DhanHQ/configuration.rb +5 -6
  11. data/lib/DhanHQ/constants.rb +67 -7
  12. data/lib/DhanHQ/contracts/alert_order_contract.rb +23 -16
  13. data/lib/DhanHQ/contracts/expired_options_data_contract.rb +4 -2
  14. data/lib/DhanHQ/contracts/forever_order_contract.rb +55 -0
  15. data/lib/DhanHQ/contracts/historical_data_contract.rb +17 -19
  16. data/lib/DhanHQ/contracts/intraday_historical_data_contract.rb +12 -0
  17. data/lib/DhanHQ/contracts/margin_calculator_contract.rb +19 -17
  18. data/lib/DhanHQ/contracts/market_feed_contract.rb +42 -0
  19. data/lib/DhanHQ/contracts/multi_scrip_margin_calc_request_contract.rb +8 -5
  20. data/lib/DhanHQ/contracts/option_chain_contract.rb +17 -19
  21. data/lib/DhanHQ/contracts/pnl_based_exit_contract.rb +1 -1
  22. data/lib/DhanHQ/contracts/slice_order_contract.rb +10 -10
  23. data/lib/DhanHQ/core/auth_api.rb +1 -1
  24. data/lib/DhanHQ/core/base_api.rb +9 -9
  25. data/lib/DhanHQ/core/base_model.rb +4 -1
  26. data/lib/DhanHQ/core/error_handler.rb +2 -2
  27. data/lib/DhanHQ/errors.rb +14 -2
  28. data/lib/DhanHQ/helpers/request_helper.rb +11 -2
  29. data/lib/DhanHQ/helpers/response_helper.rb +48 -19
  30. data/lib/DhanHQ/helpers/validation_helper.rb +4 -2
  31. data/lib/DhanHQ/models/alert_order.rb +6 -2
  32. data/lib/DhanHQ/models/edis.rb +20 -13
  33. data/lib/DhanHQ/models/expired_options_data.rb +54 -44
  34. data/lib/DhanHQ/models/forever_order.rb +16 -7
  35. data/lib/DhanHQ/models/historical_data.rb +40 -6
  36. data/lib/DhanHQ/models/instrument_helpers.rb +2 -1
  37. data/lib/DhanHQ/models/margin.rb +62 -82
  38. data/lib/DhanHQ/models/market_feed.rb +14 -3
  39. data/lib/DhanHQ/models/option_chain.rb +50 -150
  40. data/lib/DhanHQ/models/order.rb +19 -4
  41. data/lib/DhanHQ/resources/alert_orders.rb +1 -1
  42. data/lib/DhanHQ/resources/edis.rb +4 -3
  43. data/lib/DhanHQ/resources/instruments.rb +3 -2
  44. data/lib/DhanHQ/resources/ip_setup.rb +4 -1
  45. data/lib/DhanHQ/resources/kill_switch.rb +7 -1
  46. data/lib/DhanHQ/resources/orders.rb +1 -1
  47. data/lib/DhanHQ/resources/super_orders.rb +8 -2
  48. data/lib/DhanHQ/resources/trader_control.rb +13 -4
  49. data/lib/DhanHQ/version.rb +1 -1
  50. data/lib/DhanHQ/ws/base_connection.rb +1 -1
  51. data/lib/DhanHQ/ws/market_depth/client.rb +11 -4
  52. data/lib/dhan_hq.rb +17 -20
  53. data/lib/ta/indicators.rb +15 -18
  54. metadata +6 -9
  55. data/CODE_REVIEW_ISSUES.md +0 -397
  56. data/FIXES_APPLIED.md +0 -373
  57. data/RELEASING.md +0 -60
  58. data/REVIEW_SUMMARY.md +0 -120
  59. data/VERSION_UPDATE.md +0 -82
  60. data/diagram.md +0 -34
  61. data/docs/ARCHIVE_README.md +0 -784
@@ -1,784 +0,0 @@
1
- # DhanHQ - Ruby Client for DhanHQ API
2
-
3
- DhanHQ is a **Ruby client** for interacting with **Dhan API v2.0**. It provides **ActiveRecord-like** behavior, **RESTful resource management**, and **ActiveModel validation** for seamless integration into **algorithmic trading applications**.
4
-
5
- ## ⚡ Features
6
-
7
- ✅ **ORM-like Interface** (`find`, `all`, `where`, `save`, `update`, `destroy`)
8
- ✅ **ActiveModel Integration** (`validations`, `errors`, `serialization`)
9
- ✅ **Resource Objects for Trading** (`Orders`, `Positions`, `Holdings`, etc.)
10
- ✅ **Supports WebSockets for Market Feeds**
11
- ✅ **Error Handling & Validations** (`ActiveModel::Errors`)
12
- ✅ **DRY & Modular Code** (`Helpers`, `Contracts`, `Request Handling`)
13
-
14
- ---
15
-
16
- ## 📌 Installation
17
-
18
- Add this line to your application's Gemfile:
19
-
20
- ```ruby
21
- gem 'DhanHQ', git: 'https://github.com/shubhamtaywade82/dhanhq-client.git', branch: 'main'
22
- ```
23
-
24
- Then execute:
25
-
26
- ```
27
- bundle install
28
- ```
29
-
30
- Or install it manually:
31
-
32
- ```
33
- gem install dhanhq
34
- ```
35
-
36
- 🔹 Configuration
37
- Set your DhanHQ API credentials:
38
-
39
- ```ruby
40
- require 'DhanHQ'
41
-
42
- DhanHQ.configure_with_env
43
- DhanHQ.logger.level = (ENV["DHAN_LOG_LEVEL"] || "INFO").upcase.then { |level| Logger.const_get(level) }
44
- ```
45
-
46
- **Minimum environment variables**
47
-
48
- * `DHAN_CLIENT_ID` – trading account client id issued by Dhan.
49
- * `DHAN_ACCESS_TOKEN` – API access token generated from the Dhan console.
50
-
51
- If either key is missing `configure_with_env` raises an error. Ensure your
52
- application loads them into `ENV` before requiring the gem.
53
-
54
- **Optional overrides**
55
-
56
- * `DHAN_LOG_LEVEL` – change logger verbosity (`INFO` default).
57
- * `DHAN_BASE_URL` – point REST calls to an alternate host.
58
- * `DHAN_WS_VERSION` – lock WebSocket connections to a specific version.
59
- * `DHAN_WS_ORDER_URL` – override the order update WebSocket endpoint.
60
- * `DHAN_WS_USER_TYPE` – choose between `SELF` and `PARTNER` streaming modes.
61
- * `DHAN_PARTNER_ID` / `DHAN_PARTNER_SECRET` – required when streaming as a partner.
62
-
63
- Create a `.env` file in your project root to supply the minimum values (and any
64
- optional overrides you need):
65
-
66
- ```dotenv
67
- DHAN_CLIENT_ID=your_client_id
68
- DHAN_ACCESS_TOKEN=your_access_token
69
- ```
70
-
71
- The gem requires `dotenv/load`, so these variables are loaded automatically when you require `DhanHQ`.
72
-
73
- To override defaults (base URL, WebSocket settings, partner credentials), set
74
- `DHAN_BASE_URL`, `DHAN_WS_VERSION`, `DHAN_WS_ORDER_URL`, `DHAN_WS_USER_TYPE`,
75
- `DHAN_PARTNER_ID`, and `DHAN_PARTNER_SECRET` before calling
76
- `configure_with_env`.
77
-
78
- ## 🚀 Usage
79
-
80
- ✅ Placing an Order
81
-
82
- ```ruby
83
- order = DhanHQ::Models::Order.new(
84
- transaction_type: DhanHQ::Constants::TransactionType::BUY,
85
- exchange_segment: DhanHQ::Constants::ExchangeSegment::NSE_FNO,
86
- product_type: DhanHQ::Constants::ProductType::MARGIN,
87
- order_type: DhanHQ::Constants::OrderType::LIMIT,
88
- validity: DhanHQ::Constants::Validity::DAY,
89
- security_id: "43492",
90
- quantity: 125,
91
- price: 100.0
92
- )
93
-
94
- order.save
95
- puts order.persisted? # true
96
- ```
97
-
98
- ✅ Fetching an Order
99
-
100
- ```ruby
101
- order = DhanHQ::Models::Order.find("452501297117")
102
- puts order.price # Current price of the order
103
- ```
104
-
105
- ✅ Updating an Order
106
-
107
- ```ruby
108
- order.update(price: 105.0)
109
- puts order.price # 105.0
110
- ```
111
-
112
- ✅ Canceling an Order
113
-
114
- ```ruby
115
- order.cancel
116
- ```
117
-
118
- ✅ Fetching All Orders
119
-
120
- ```ruby
121
- orders = DhanHQ::Models::Order.all
122
- puts orders.count
123
- ```
124
-
125
- ✅ Querying Orders
126
-
127
- ```ruby
128
- pending_orders = DhanHQ::Models::Order.where(status: DhanHQ::Constants::OrderStatus::PENDING)
129
- puts pending_orders.first.order_id
130
- ```
131
-
132
- ✅ Exiting Positions
133
-
134
- ```ruby
135
- positions = DhanHQ::Models::Position.all
136
- position = positions.first
137
- position.exit!
138
- ```
139
-
140
- ### Orders
141
-
142
- #### Place
143
-
144
- ```ruby
145
- order = DhanHQ::Models::Order.new(transaction_type: DhanHQ::Constants::TransactionType::BUY, security_id: "123", quantity: 1)
146
- order.save
147
- ```
148
-
149
- #### Modify
150
-
151
- ```ruby
152
- order.modify(price: 102.5)
153
- ```
154
-
155
- #### Cancel
156
-
157
- ```ruby
158
- order.cancel
159
- ```
160
-
161
- ### Trades
162
-
163
- ```ruby
164
- DhanHQ::Models::Trade.today
165
- DhanHQ::Models::Trade.find_by_order_id("452501297117")
166
- ```
167
-
168
- ### Positions
169
-
170
- ```ruby
171
- positions = DhanHQ::Models::Position.all
172
- active = DhanHQ::Models::Position.active
173
- DhanHQ::Models::Position.convert(position_id: "1", product_type: DhanHQ::Constants::ProductType::CNC)
174
- ```
175
-
176
- ### Holdings
177
-
178
- ```ruby
179
- DhanHQ::Models::Holding.all
180
- ```
181
-
182
- ### Funds
183
-
184
- ```ruby
185
- DhanHQ::Models::Funds.fetch
186
- balance = DhanHQ::Models::Funds.balance
187
- ```
188
-
189
- ### Option Chain
190
-
191
- ```ruby
192
- DhanHQ::Models::OptionChain.fetch(security_id: "1333", expiry_date: "2024-06-30")
193
- DhanHQ::Models::OptionChain.fetch_expiry_list(security_id: "1333")
194
- ```
195
-
196
- ### Historical Data
197
-
198
- ```ruby
199
- DhanHQ::Models::HistoricalData.daily(security_id: "1333", exchange_segment: DhanHQ::Constants::ExchangeSegment::NSE_EQ, instrument: DhanHQ::Constants::InstrumentType::EQUITY, from_date: "2024-01-01", to_date: "2024-01-31")
200
- DhanHQ::Models::HistoricalData.intraday(security_id: "1333", exchange_segment: DhanHQ::Constants::ExchangeSegment::NSE_EQ, instrument: DhanHQ::Constants::InstrumentType::EQUITY, interval: "15", from_date: "2024-09-11", to_date: "2024-09-15")
201
- ```
202
-
203
- ### Instrument Model with Convenience Methods
204
-
205
- The Instrument model provides convenient instance methods that automatically use the instrument's attributes:
206
-
207
- ```ruby
208
- # Find an instrument
209
- instrument = DhanHQ::Models::Instrument.find("IDX_I", "NIFTY")
210
-
211
- # Market Feed Methods - automatically uses instrument's attributes
212
- ltp_data = instrument.ltp # Last traded price
213
- ohlc_data = instrument.ohlc # OHLC data
214
- quote_data = instrument.quote # Full quote depth
215
-
216
- # Historical Data Methods
217
- daily_data = instrument.daily(from_date: "2024-01-01", to_date: "2024-01-31")
218
- intraday_data = instrument.intraday(from_date: "2024-09-11", to_date: "2024-09-15", interval: "15")
219
-
220
- # Option Chain Methods
221
- expiries = instrument.expiry_list
222
- chain = instrument.option_chain(expiry: "2024-02-29")
223
- ```
224
-
225
- ## 🔹 Available Resources
226
-
227
- | Resource | Model | Actions |
228
- | ------------------------ | ---------------------------------------- | --------------------------------------------------- |
229
- | Orders | `DhanHQ::Models::Models::Order` | `find`, `all`, `where`, `place`, `update`, `cancel` |
230
- | Trades | `DhanHQ::Models::Models::Trade` | `all`, `find_by_order_id` |
231
- | Forever Orders | `DhanHQ::Models::Models::ForeverOrder` | `create`, `find`, `modify`, `cancel`, `all` |
232
- | Holdings | `DhanHQ::Models::Models::Holding` | `all` |
233
- | Positions | `DhanHQ::Models::Models::Position` | `all`, `find`, `exit!` |
234
- | Funds & Margin | `DhanHQ::Models::Models::Funds` | `fund_limit`, `margin_calculator` |
235
- | Ledger | `DhanHQ::Models::Models::Ledger` | `all` |
236
- | Market Feeds | `DhanHQ::Models::Models::MarketFeed` | `ltp, ohlc`, `quote` |
237
- | Historical Data (Charts) | `DhanHQ::Models::Models::HistoricalData` | `daily`, `intraday` |
238
- | Option Chain | `DhanHQ::Models::Models::OptionChain` | `fetch`, `fetch_expiry_list` |
239
-
240
- ## 📌 Development
241
-
242
- Set `DHAN_DEBUG=true` to log HTTP requests during development:
243
-
244
- ```bash
245
- export DHAN_DEBUG=true
246
- ```
247
-
248
- Running Tests
249
-
250
- ```bash
251
- bundle exec rspec
252
- ```
253
-
254
- Installing Locally
255
-
256
- ```bash
257
- bundle exec rake install
258
- ```
259
-
260
- Releasing a New Version
261
-
262
- ```bash
263
- bundle exec rake release
264
- ```
265
- ---
266
-
267
- ## WebSocket Market Feed (NEW)
268
-
269
- ### What you get
270
-
271
- * **Modes**
272
-
273
- * `:ticker` → LTP + LTT
274
- * `:quote` → OHLCV + totals (recommended default)
275
- * `:full` → quote + **OI** + **best-5 depth**
276
- * **Normalized ticks** (Hash):
277
-
278
- ```ruby
279
- {
280
- kind: :quote, # :ticker | :quote | :full | :oi | :prev_close | :misc
281
- segment: DhanHQ::Constants::ExchangeSegment::NSE_FNO, # string enum
282
- security_id: "12345",
283
- ltp: 101.5,
284
- ts: 1723791300, # LTT epoch (sec) if present
285
- vol: 123456, # quote/full
286
- atp: 100.9, # quote/full
287
- day_open: 100.1, day_high: 102.4, day_low: 99.5, day_close: nil,
288
- oi: 987654, # full or OI packet
289
- bid: 101.45, ask: 101.55 # from depth (mode :full)
290
- }
291
- ```
292
-
293
- ### Start, subscribe, stop
294
-
295
- ```ruby
296
- DhanHQ.configure_with_env
297
- DhanHQ.logger.level = (ENV["DHAN_LOG_LEVEL"] || "INFO").upcase.then { |level| Logger.const_get(level) }
298
-
299
- ws = DhanHQ::WS::Client.new(mode: :quote).start
300
-
301
- ws.on(:tick) do |t|
302
- puts "[#{t[:segment]}:#{t[:security_id]}] LTP=#{t[:ltp]} kind=#{t[:kind]}"
303
- end
304
-
305
- # Subscribe instruments (≤100 per frame; send multiple frames if needed)
306
- ws.subscribe_one(segment: DhanHQ::Constants::ExchangeSegment::IDX_I, security_id: "13") # NIFTY index value
307
- ws.subscribe_one(segment: DhanHQ::Constants::ExchangeSegment::NSE_FNO, security_id: "12345") # an option
308
-
309
- # Unsubscribe
310
- ws.unsubscribe_one(segment: DhanHQ::Constants::ExchangeSegment::NSE_FNO, security_id: "12345")
311
-
312
- # Graceful disconnect (sends broker disconnect code 12, no reconnect)
313
- ws.disconnect!
314
-
315
- # Or hard stop (no broker message, just closes and halts loop)
316
- ws.stop
317
-
318
- # Safety: kill all local sockets (useful in IRB)
319
- DhanHQ::WS.disconnect_all_local!
320
- ```
321
-
322
- ### Under the hood
323
-
324
- * **Request codes** (per Dhan docs)
325
-
326
- * Subscribe: **15** (ticker), **17** (quote), **21** (full)
327
- * Unsubscribe: **16**, **18**, **22**
328
- * Disconnect: **12**
329
- * **Limits**
330
-
331
- * Up to **100 instruments per SUB/UNSUB** message (client auto-chunks)
332
- * Up to 5 WS connections per user (per Dhan)
333
- * **Backoff & 429 cool-off**
334
-
335
- * Exponential backoff with jitter
336
- * Handshake **429** triggers a **60s cool-off** before retry
337
- * **Reconnect & resubscribe**
338
-
339
- * On reconnect the client resends the **current subscription snapshot** (idempotent)
340
- * **Graceful shutdown**
341
-
342
- * `ws.disconnect!` or `ws.stop` prevents reconnects
343
- * An `at_exit` hook stops all registered WS clients to avoid leaked sockets
344
-
345
- ---
346
-
347
- ## Exchange Segment Enums
348
-
349
- Use the string enums below in WS `subscribe_*` and REST params:
350
-
351
- | Enum | Exchange | Segment |
352
- | -------------- | -------- | ----------------- |
353
- | `IDX_I` | Index | Index Value |
354
- | `NSE_EQ` | NSE | Equity Cash |
355
- | `NSE_FNO` | NSE | Futures & Options |
356
- | `NSE_CURRENCY` | NSE | Currency |
357
- | `BSE_EQ` | BSE | Equity Cash |
358
- | `MCX_COMM` | MCX | Commodity |
359
- | `BSE_CURRENCY` | BSE | Currency |
360
- | `BSE_FNO` | BSE | Futures & Options |
361
-
362
- ---
363
-
364
- ## Accessing ticks elsewhere in your app
365
-
366
- ### Direct handler
367
-
368
- ```ruby
369
- ws.on(:tick) { |t| do_something_fast(t) } # avoid heavy work here
370
- ```
371
-
372
- ### Shared TickCache (recommended)
373
-
374
- ```ruby
375
- # app/services/live/tick_cache.rb
376
- class TickCache
377
- MAP = Concurrent::Map.new
378
- def self.put(t) = MAP["#{t[:segment]}:#{t[:security_id]}"] = t
379
- def self.get(seg, sid) = MAP["#{seg}:#{sid}"]
380
- def self.ltp(seg, sid) = get(seg, sid)&.dig(:ltp)
381
- end
382
-
383
- ws.on(:tick) { |t| TickCache.put(t) }
384
- ltp = TickCache.ltp("NSE_FNO", "12345")
385
- ```
386
-
387
- ### Filtered callback
388
-
389
- ```ruby
390
- def on_tick_for(ws, segment:, security_id:, &blk)
391
- key = "#{segment}:#{security_id}"
392
- ws.on(:tick){ |t| blk.call(t) if "#{t[:segment]}:#{t[:security_id]}" == key }
393
- end
394
- ```
395
-
396
- ---
397
-
398
- ## Rails integration (example)
399
-
400
- **Goal:** Generate signals from clean **Historical Intraday OHLC** (5-min bars), and use **WebSocket** only for **exits/trailing** on open option legs.
401
-
402
- 1. **Initializer**
403
- `config/initializers/dhanhq.rb`
404
-
405
- ```ruby
406
- DhanHQ.configure_with_env
407
- DhanHQ.logger.level = (ENV["DHAN_LOG_LEVEL"] || "INFO").upcase.then { |level| Logger.const_get(level) }
408
- ```
409
-
410
- 2. **Start WS supervisor**
411
- `config/initializers/stream.rb`
412
-
413
- ```ruby
414
- INDICES = [
415
- { segment: DhanHQ::Constants::ExchangeSegment::IDX_I, security_id: "13" }, # NIFTY index value
416
- { segment: DhanHQ::Constants::ExchangeSegment::IDX_I, security_id: "25" } # BANKNIFTY index value
417
- ]
418
-
419
- Rails.application.config.to_prepare do
420
- $WS = DhanHQ::WS::Client.new(mode: :quote).start
421
- $WS.on(:tick) do |t|
422
- TickCache.put(t)
423
- Execution::PositionGuard.instance.on_tick(t) # trailing & fast exits
424
- end
425
- INDICES.each { |i| $WS.subscribe_one(segment: i[:segment], security_id: i[:security_id]) }
426
- end
427
- ```
428
-
429
- 3. **Bar fetch (every 5 min) via Historical API**
430
-
431
- * Fetch intraday OHLC at 5-minute boundaries.
432
- * Update your `CandleSeries`; on each closed bar, run strategy to emit signals.
433
- *(Use your existing `Bars::FetchLoop` + `CandleSeries` code.)*
434
-
435
- 4. **Routing & orders**
436
-
437
- * On signal: place **Super Order** (SL/TP/TSL) or fallback to Market + local trailing.
438
- * After a successful place, **register** the leg in `PositionGuard` and **subscribe** its option on WS.
439
-
440
- 5. **Shutdown**
441
-
442
- ```ruby
443
- at_exit { DhanHQ::WS.disconnect_all_local! }
444
- ```
445
-
446
- ---
447
-
448
- ## Super Orders
449
-
450
- Super orders are built for smart execution. They bundle entry, target, and stop-loss legs (with optional trailing jump) into one atomic request so the broker manages risk for you.
451
-
452
- The gem exposes all related REST endpoints so you can create, modify, cancel, and list super orders programmatically across exchanges and segments.
453
-
454
- ### Endpoints
455
-
456
- | Method | Path | Description |
457
- | -------- | -------------------------------------- | ------------------------------------- |
458
- | `POST` | `/super/orders` | Create a new super order |
459
- | `PUT` | `/super/orders/{order_id}` | Modify a pending super order |
460
- | `DELETE` | `/super/orders/{order_id}/{order_leg}` | Cancel a pending super order leg |
461
- | `GET` | `/super/orders` | Retrieve the list of all super orders |
462
-
463
- ### Place Super Order
464
-
465
- Use this endpoint to create a new super order consisting of entry, target, stop-loss, and optional trailing jump. It is available for intraday, carry-forward, and MTF across supported exchanges.
466
-
467
- > ℹ️ Static IP whitelisting with Dhan support is required before invoking this API.
468
-
469
- ```bash
470
- curl --request POST \
471
- --url https://api.dhan.co/v2/super/orders \
472
- --header 'Content-Type: application/json' \
473
- --header 'access-token: JWT' \
474
- --data '{Request JSON}'
475
- ```
476
-
477
- #### Request body
478
-
479
- ```json
480
- {
481
- "dhan_client_id": "1000000003",
482
- "correlation_id": "123abc678",
483
- "transaction_type": "BUY",
484
- "exchange_segment": "NSE_EQ",
485
- "product_type": "CNC",
486
- "order_type": "LIMIT",
487
- "security_id": "11536",
488
- "quantity": 5,
489
- "price": 1500,
490
- "target_price": 1600,
491
- "stop_loss_price": 1400,
492
- "trailing_jump": 10
493
- }
494
- ```
495
-
496
- #### Parameters
497
-
498
- | Field | Type | Description |
499
- | ------------------ | ------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
500
- | `dhan_client_id` | string *(required)* | User specific identification generated by Dhan. When you call via `DhanHQ::Models::SuperOrder`, the gem injects your configured client id so you can omit the key in Ruby. |
501
- | `correlation_id` | string | Caller supplied identifier for tracking |
502
- | `transaction_type` | enum string *(required)* | Trading side. `BUY` or `SELL`. |
503
- | `exchange_segment` | enum string *(required)* | Exchange segment (see appendix). |
504
- | `product_type` | enum string *(required)* | Product type. `CNC`, `INTRADAY`, `MARGIN`, or `MTF`. |
505
- | `order_type` | enum string *(required)* | Order type. `LIMIT` or `MARKET`. |
506
- | `security_id` | string *(required)* | Exchange standard security identifier. |
507
- | `quantity` | integer *(required)* | Quantity for the entry leg. |
508
- | `price` | float *(required)* | Entry price. |
509
- | `target_price` | float *(required)* | Target price for the super order. |
510
- | `stop_loss_price` | float *(required)* | Stop-loss price for the super order. |
511
- | `trailing_jump` | float *(required)* | Price jump size for trailing stop-loss. |
512
-
513
- > 🐍 When you call `DhanHQ::Models::SuperOrder.create`, supply snake_case keys exactly as shown. The client automatically camelizes
514
- > them before submitting to Dhan's REST API and injects your configured `dhan_client_id`, allowing you to omit the key in Ruby code.
515
-
516
- #### Response
517
-
518
- ```json
519
- {
520
- "order_id": "112111182198",
521
- "order_status": "PENDING"
522
- }
523
- ```
524
-
525
- | Field | Type | Description |
526
- | -------------- | ----------- | --------------------------------------------------- |
527
- | `order_id` | string | Order identifier generated by Dhan |
528
- | `order_status` | enum string | Latest status. `TRANSIT`, `PENDING`, or `REJECTED`. |
529
-
530
- ### Modify Super Order
531
-
532
- Modify any leg while the order is `PENDING` or `PART_TRADED`. Once the entry leg trades fully, only the target and stop-loss legs (price and trailing jump) remain editable.
533
-
534
- > ℹ️ Static IP whitelisting with Dhan support is required before invoking this API.
535
-
536
- ```bash
537
- curl --request PUT \
538
- --url https://api.dhan.co/v2/super/orders/{order_id} \
539
- --header 'Content-Type: application/json' \
540
- --header 'access-token: JWT' \
541
- --data '{Request JSON}'
542
- ```
543
-
544
- #### Request body
545
-
546
- ```json
547
- {
548
- "dhan_client_id": "1000000009",
549
- "order_id": "112111182045",
550
- "order_type": "LIMIT",
551
- "leg_name": "ENTRY_LEG",
552
- "quantity": 40,
553
- "price": 1300,
554
- "target_price": 1450,
555
- "stop_loss_price": 1350,
556
- "trailing_jump": 20
557
- }
558
- ```
559
-
560
- #### Parameters
561
-
562
- | Field | Type | Description |
563
- | ----------------- | -------------------------------------- | ------------------------------------------------------------------------------------------------------------------- |
564
- | `dhan_client_id` | string *(required)* | User specific identification generated by Dhan. Automatically merged when using the Ruby model helpers. |
565
- | `order_id` | string *(required)* | Super order identifier generated by Dhan. |
566
- | `order_type` | enum string *(conditionally required)* | `LIMIT` or `MARKET`. Required when changing `ENTRY_LEG`. |
567
- | `leg_name` | enum string *(required)* | `ENTRY_LEG`, `TARGET_LEG`, or `STOP_LOSS_LEG`. Entry edits allowed only while status is `PENDING` or `PART_TRADED`. |
568
- | `quantity` | integer *(conditionally required)* | Quantity update for `ENTRY_LEG`. |
569
- | `price` | float *(conditionally required)* | Entry price update for `ENTRY_LEG`. |
570
- | `target_price` | float *(conditionally required)* | Target price update for `ENTRY_LEG` or `TARGET_LEG`. |
571
- | `stop_loss_price` | float *(conditionally required)* | Stop-loss price update for `ENTRY_LEG` or `STOP_LOSS_LEG`. |
572
- | `trailing_jump` | float *(conditionally required)* | Trailing jump update for `ENTRY_LEG` or `STOP_LOSS_LEG`. Omit or set to `0` to cancel trailing. |
573
-
574
- #### Response
575
-
576
- ```json
577
- {
578
- "order_id": "112111182045",
579
- "order_status": "TRANSIT"
580
- }
581
- ```
582
-
583
- | Field | Type | Description |
584
- | -------------- | ----------- | ------------------------------------------------------------- |
585
- | `order_id` | string | Order identifier generated by Dhan |
586
- | `order_status` | enum string | Latest status. `TRANSIT`, `PENDING`, `REJECTED`, or `TRADED`. |
587
-
588
- ### Cancel Super Order
589
-
590
- Cancel a pending or active super order leg with the order ID. Cancelling the entry leg removes all legs. Cancelling an individual target or stop-loss leg removes only that leg and it cannot be recreated on the same order.
591
-
592
- > ℹ️ Static IP whitelisting with Dhan support is required before invoking this API.
593
-
594
- ```bash
595
- curl --request DELETE \
596
- --url https://api.dhan.co/v2/super/orders/{order_id}/{order_leg} \
597
- --header 'Content-Type: application/json' \
598
- --header 'access-token: JWT'
599
- ```
600
-
601
- #### Path parameters
602
-
603
- | Field | Description | Example |
604
- | ----------- | ------------------------------------------------------------- | ------------- |
605
- | `order_id` | Super order identifier. | `11211182198` |
606
- | `order_leg` | Leg to cancel. `ENTRY_LEG`, `TARGET_LEG`, or `STOP_LOSS_LEG`. | `ENTRY_LEG` |
607
-
608
- #### Response
609
-
610
- ```json
611
- {
612
- "order_id": "112111182045",
613
- "order_status": "CANCELLED"
614
- }
615
- ```
616
-
617
- | Field | Type | Description |
618
- | -------------- | ----------- | ---------------------------------------------------------------- |
619
- | `order_id` | string | Order identifier generated by Dhan |
620
- | `order_status` | enum string | Latest status. `TRANSIT`, `PENDING`, `REJECTED`, or `CANCELLED`. |
621
-
622
- ### Super Order List
623
-
624
- Fetch all super orders placed during the trading day. The response nests leg details under the entry leg, and each leg is also available in the standard order book.
625
-
626
- ```bash
627
- curl --request GET \
628
- --url https://api.dhan.co/v2/super/orders \
629
- --header 'Content-Type: application/json' \
630
- --header 'access-token: JWT'
631
- ```
632
-
633
- #### Response
634
-
635
- ```json
636
- [
637
- {
638
- "dhan_client_id": "1100003626",
639
- "order_id": "5925022734212",
640
- "correlation_id": "string",
641
- "order_status": "PENDING",
642
- "transaction_type": "BUY",
643
- "exchange_segment": "NSE_EQ",
644
- "product_type": "CNC",
645
- "order_type": "LIMIT",
646
- "validity": "DAY",
647
- "trading_symbol": "HDFCBANK",
648
- "security_id": "1333",
649
- "quantity": 10,
650
- "remaining_quantity": 10,
651
- "ltp": 1660.95,
652
- "price": 1500,
653
- "after_market_order": false,
654
- "leg_name": "ENTRY_LEG",
655
- "exchange_order_id": "11925022734212",
656
- "create_time": "2025-02-27 19:09:42",
657
- "update_time": "2025-02-27 19:09:42",
658
- "exchange_time": "2025-02-27 19:09:42",
659
- "oms_error_description": "",
660
- "average_traded_price": 0,
661
- "filled_qty": 0,
662
- "leg_details": [
663
- {
664
- "order_id": "5925022734212",
665
- "leg_name": "STOP_LOSS_LEG",
666
- "transaction_type": "SELL",
667
- "total_quantity": 0,
668
- "remaining_quantity": 0,
669
- "triggered_quantity": 0,
670
- "price": 1400,
671
- "order_status": "PENDING",
672
- "trailing_jump": 10
673
- },
674
- {
675
- "order_id": "5925022734212",
676
- "leg_name": "TARGET_LEG",
677
- "transaction_type": "SELL",
678
- "remaining_quantity": 0,
679
- "triggered_quantity": 0,
680
- "price": 1550,
681
- "order_status": "PENDING",
682
- "trailing_jump": 0
683
- }
684
- ]
685
- }
686
- ]
687
- ```
688
-
689
- #### Parameters
690
-
691
- | Field | Type | Description |
692
- | ----------------------- | ----------- | --------------------------------------------------------------------------------------------------- |
693
- | `dhan_client_id` | string | User specific identification generated by Dhan. |
694
- | `order_id` | string | Order identifier generated by Dhan. |
695
- | `correlation_id` | string | Caller supplied correlation identifier. |
696
- | `order_status` | enum string | Latest status. `TRANSIT`, `PENDING`, `CLOSED`, `REJECTED`, `CANCELLED`, `PART_TRADED`, or `TRADED`. |
697
- | `transaction_type` | enum string | Trading side. `BUY` or `SELL`. |
698
- | `exchange_segment` | enum string | Exchange segment. |
699
- | `product_type` | enum string | Product type. `CNC`, `INTRADAY`, `MARGIN`, or `MTF`. |
700
- | `order_type` | enum string | Order type. `LIMIT` or `MARKET`. |
701
- | `validity` | enum string | Order validity. `DAY`. |
702
- | `trading_symbol` | string | Trading symbol reference. |
703
- | `security_id` | string | Exchange security identifier. |
704
- | `quantity` | integer | Ordered quantity. |
705
- | `remaining_quantity` | integer | Quantity pending execution. |
706
- | `ltp` | float | Last traded price. |
707
- | `price` | float | Order price. |
708
- | `after_market_order` | boolean | Indicates if order was placed after market hours. |
709
- | `leg_name` | enum string | Leg identifier: `ENTRY_LEG`, `TARGET_LEG`, or `STOP_LOSS_LEG`. |
710
- | `trailing_jump` | float | Trailing jump for stop-loss. |
711
- | `exchange_order_id` | string | Exchange generated identifier. |
712
- | `create_time` | string | Order creation timestamp. |
713
- | `update_time` | string | Latest update timestamp. |
714
- | `exchange_time` | string | Exchange timestamp. |
715
- | `oms_error_description` | string | OMS error description when applicable. |
716
- | `average_traded_price` | float | Average traded price. |
717
- | `filled_qty` | integer | Quantity traded on the exchange. |
718
- | `triggered_quantity` | integer | Quantity triggered for stop-loss or target legs. |
719
- | `leg_details` | array | Nested leg details for the super order. |
720
-
721
- > ✅ `CLOSED` indicates the entry leg and either target or stop-loss completed for the full quantity. `TRIGGERED` appears on target and stop-loss legs to highlight which one fired; inspect `triggered_quantity` for executed quantity.
722
-
723
- ---
724
-
725
- ## Packet parsing (for reference)
726
-
727
- * **Response Header (8 bytes)**:
728
- `feed_response_code (u8, BE)`, `message_length (u16, BE)`, `exchange_segment (u8, BE)`, `security_id (i32, LE)`
729
- * **Packets supported**:
730
-
731
- * **1** Index (surface as raw/misc unless documented)
732
- * **2** Ticker: `ltp`, `ltt`
733
- * **4** Quote: `ltp`, `ltt`, `atp`, `volume`, totals, `day_*`
734
- * **5** OI: `open_interest`
735
- * **6** Prev Close: `prev_close`, `oi_prev`
736
- * **7** Market Status (raw/misc unless documented)
737
- * **8** Full: quote + `open_interest` + 5× depth (bid/ask)
738
- * **50** Disconnect: reason code
739
-
740
- ---
741
-
742
- ## Best practices
743
-
744
- * Keep the `on(:tick)` handler **non-blocking**; push work to a queue/thread.
745
- * Use `mode: :quote` for most strategies; switch to `:full` only if you need depth/OI in real-time.
746
- * Call **`ws.disconnect!`** (or `ws.stop`) when leaving IRB / tests.
747
- Use **`DhanHQ::WS.disconnect_all_local!`** to be extra safe.
748
- * Don’t exceed **100 instruments per SUB frame** (the client auto-chunks).
749
- * Avoid rapid connect/disconnect loops; the client already **backs off & cools off** when server replies 429.
750
-
751
- ---
752
-
753
- ## Troubleshooting
754
-
755
- * **429: Unexpected response code**
756
- You connected too frequently or have too many sockets. The client auto-cools off for **60s** and backs off. Prefer `ws.disconnect!` before reconnecting; and call `DhanHQ::WS.disconnect_all_local!` to kill stragglers.
757
- * **No ticks after reconnect**
758
- Ensure you re-subscribed after a clean start (the client resends the snapshot automatically on reconnect).
759
- * **Binary parse errors**
760
- Run with `DHAN_LOG_LEVEL=DEBUG` to inspect; we safely drop malformed frames and keep the loop alive.
761
-
762
- ---
763
-
764
-
765
- ## 📌 Contributing
766
-
767
- Bug reports and pull requests are welcome on GitHub at:
768
- 🔗 <https://github.com/shubhamtaywade82/dhanhq>
769
-
770
- This project follows a code of conduct to maintain a safe and welcoming community.
771
-
772
- ## 📌 License
773
-
774
- This gem is available under the MIT License.
775
- 🔗 <https://opensource.org/licenses/MIT>
776
-
777
- ## 📌 Code of Conduct
778
-
779
- Everyone interacting in the DhanHQ project is expected to follow the
780
- 🔗 Code of Conduct.
781
-
782
- ```markdown
783
- This **README.md** file is structured and formatted for **GitHub** or any **Markdown-compatible** documentation system. 🚀
784
- ```