DhanHQ 3.2.0 → 3.3.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 (57) hide show
  1. checksums.yaml +4 -4
  2. data/AGENTS.md +23 -0
  3. data/CHANGELOG.md +74 -0
  4. data/CODE_OF_CONDUCT.md +132 -0
  5. data/GUIDE.md +16 -0
  6. data/README.md +54 -1
  7. data/docs/CONFIGURATION.md +38 -14
  8. data/docs/RELEASE_GUIDE.md +1 -1
  9. data/lib/DhanHQ/ai/prompt_helpers.rb +17 -7
  10. data/lib/DhanHQ/client.rb +53 -34
  11. data/lib/DhanHQ/concerns/bang_writes.rb +69 -0
  12. data/lib/DhanHQ/concerns/tracked_writes.rb +56 -0
  13. data/lib/DhanHQ/configuration.rb +20 -1
  14. data/lib/DhanHQ/core/base_model.rb +6 -13
  15. data/lib/DhanHQ/deprecation.rb +62 -0
  16. data/lib/DhanHQ/models/alert_order.rb +8 -1
  17. data/lib/DhanHQ/models/forever_order.rb +9 -0
  18. data/lib/DhanHQ/models/global_stocks/order.rb +9 -0
  19. data/lib/DhanHQ/models/iceberg_order.rb +9 -0
  20. data/lib/DhanHQ/models/multi_order.rb +7 -0
  21. data/lib/DhanHQ/models/order.rb +28 -0
  22. data/lib/DhanHQ/models/pnl_exit.rb +7 -0
  23. data/lib/DhanHQ/models/super_order.rb +9 -0
  24. data/lib/DhanHQ/models/twap_order.rb +9 -0
  25. data/lib/DhanHQ/risk/pipeline.rb +0 -2
  26. data/lib/DhanHQ/version.rb +1 -1
  27. data/lib/DhanHQ/write_result.rb +163 -0
  28. data/lib/dhan_hq.rb +9 -0
  29. data/skills/dhanhq-ruby/SKILL.md +208 -0
  30. data/skills/dhanhq-ruby/examples/fetch_option_chain.rb +54 -0
  31. data/skills/dhanhq-ruby/examples/gtt_forever_order.rb +65 -0
  32. data/skills/dhanhq-ruby/examples/historical_data_analysis.rb +89 -0
  33. data/skills/dhanhq-ruby/examples/iron_condor.rb +137 -0
  34. data/skills/dhanhq-ruby/examples/live_feed_setup.rb +43 -0
  35. data/skills/dhanhq-ruby/examples/margin_check.rb +42 -0
  36. data/skills/dhanhq-ruby/examples/order_management.rb +112 -0
  37. data/skills/dhanhq-ruby/examples/place_equity_order.rb +36 -0
  38. data/skills/dhanhq-ruby/examples/place_fno_order.rb +76 -0
  39. data/skills/dhanhq-ruby/examples/portfolio_summary.rb +74 -0
  40. data/skills/dhanhq-ruby/examples/super_order_with_sl.rb +57 -0
  41. data/skills/dhanhq-ruby/references/backtesting-with-dhan.md +65 -0
  42. data/skills/dhanhq-ruby/references/common-workflows.md +76 -0
  43. data/skills/dhanhq-ruby/references/error-codes.md +50 -0
  44. data/skills/dhanhq-ruby/references/funds.md +67 -0
  45. data/skills/dhanhq-ruby/references/instruments.md +91 -0
  46. data/skills/dhanhq-ruby/references/live-feed.md +83 -0
  47. data/skills/dhanhq-ruby/references/market-data.md +119 -0
  48. data/skills/dhanhq-ruby/references/option-chain.md +71 -0
  49. data/skills/dhanhq-ruby/references/options-analysis-patterns.md +76 -0
  50. data/skills/dhanhq-ruby/references/orders.md +203 -0
  51. data/skills/dhanhq-ruby/references/portfolio.md +100 -0
  52. data/skills/dhanhq-ruby/references/scanx-data.md +62 -0
  53. data/skills/dhanhq-ruby/scripts/dhan_helpers.rb +323 -0
  54. data/skills/dhanhq-ruby/scripts/resolve_security.rb +168 -0
  55. data/skills/dhanhq-ruby/scripts/trade_logger.rb +131 -0
  56. data/skills/dhanhq-ruby/scripts/validate_order.rb +169 -0
  57. metadata +36 -2
@@ -0,0 +1,91 @@
1
+ # Instruments — Complete Reference (Ruby SDK)
2
+
3
+ Use the security master as the primary source for `security_id`, lot size, expiry, strike, tick size, and display symbol.
4
+
5
+ ## Preferred SDK Entry Point
6
+
7
+ In the Ruby SDK, search and load instruments segment-wise using:
8
+
9
+ ```ruby
10
+ # Retrieve compact list for a single segment (returns Array of Instrument objects)
11
+ instruments = DhanHQ::Models::Instrument.by_segment("NSE_EQ")
12
+ ```
13
+
14
+ Official instrument sources (managed by the SDK internally):
15
+ - Compact CSV: `https://images.dhan.co/api-data/api-scrip-master.csv`
16
+ - Detailed CSV: `https://images.dhan.co/api-data/api-scrip-master-detailed.csv`
17
+
18
+ ---
19
+
20
+ ## Key Columns (Instrument Attributes)
21
+
22
+ | Attribute | Meaning |
23
+ |-----------|---------|
24
+ | `security_id` | Security ID (String) |
25
+ | `exchange` | Exchange ID (`NSE`, `BSE`, `MCX`) |
26
+ | `instrument` | Instrument Type (`EQUITY`, `OPTIDX`, `OPTSTK`, etc.) |
27
+ | `symbol_name` | Exchange trading symbol |
28
+ | `display_name` | Dhan custom symbol |
29
+ | `lot_size` | Lot size (Integer) |
30
+ | `tick_size` | Tick size (Float) |
31
+ | `expiry_date` | Expiry date (String) |
32
+ | `strike_price` | Strike price (Float) |
33
+ | `option_type` | Option Type (`CALL` or `PUT`) |
34
+
35
+ ---
36
+
37
+ ## Recommended Resolution Flow
38
+
39
+ Use the SDK's built-in helper methods on the `Instrument` class:
40
+
41
+ ```ruby
42
+ # Find specific instrument in a segment by symbol name (exact match)
43
+ inst = DhanHQ::Models::Instrument.find("NSE_EQ", "RELIANCE")
44
+
45
+ # Find by security ID instead of symbol name — use this, not `.find`, when you
46
+ # already have a security_id (e.g. from an order, position, or option chain leg)
47
+ inst = DhanHQ::Models::Instrument.find_by_security_id("NSE_EQ", "2885")
48
+
49
+ # Search across multiple segments (finds any match)
50
+ inst = DhanHQ::Models::Instrument.find_anywhere("RELIANCE")
51
+
52
+ # Fuzzy search across multiple segments
53
+ results = DhanHQ::Models::Instrument.search("RELIANCE")
54
+ ```
55
+
56
+ `.find`'s second argument is always a **symbol name**, never a security ID — passing a security ID there silently returns `nil` (it searches symbol/underlying-symbol text, doesn't match on ID). Use `.find_by_security_id` when resolving by ID.
57
+
58
+ Or leverage the helper layer in `scripts/dhan_helpers.rb`:
59
+
60
+ ```ruby
61
+ require_relative "../scripts/dhan_helpers"
62
+
63
+ cash = resolve_symbol("RELIANCE", "NSE_EQ")
64
+ contract = resolve_derivative("NIFTY", strike: 24000, option_type: "CE", expiry: "2025-03-27")
65
+ lot_size = get_lot_size(underlying: "NIFTY")
66
+ ```
67
+
68
+ ---
69
+
70
+ ## Quick-Reference Fallback IDs
71
+
72
+ ### Index Underlyings
73
+
74
+ | Underlying | security_id | Underlying Segment |
75
+ |------------|-------------|-------------------|
76
+ | NIFTY 50 | `13` | `IDX_I` |
77
+ | BANK NIFTY | `25` | `IDX_I` |
78
+ | FINNIFTY | `27` | `IDX_I` |
79
+ | MIDCPNIFTY | `442` | `IDX_I` |
80
+ | SENSEX | `51` | `IDX_I` |
81
+
82
+ ### Common NSE Equities
83
+
84
+ | Symbol | security_id |
85
+ |--------|-------------|
86
+ | RELIANCE | `2885` |
87
+ | HDFCBANK | `1333` |
88
+ | TCS | `11536` |
89
+ | INFY | `1594` |
90
+ | ICICIBANK | `4963` |
91
+ | SBIN | `3045` |
@@ -0,0 +1,83 @@
1
+ # Live Feed — Complete Reference (Ruby SDK)
2
+
3
+ The Ruby SDK provides three distinct WebSocket interfaces under the `DhanHQ::WS` namespace to handle live data streaming.
4
+
5
+ ## 1. Market Feed (`DhanHQ::WS.connect`)
6
+
7
+ Real-time market ticks, last traded prices, quotes, and market depth updates.
8
+
9
+ ### Usage
10
+
11
+ ```ruby
12
+ # Connect to market feed. Modes: :ticker, :quote, :full
13
+ market_client = DhanHQ::WS.connect(mode: :ticker) do |tick|
14
+ timestamp = tick[:ts] ? Time.at(tick[:ts]) : Time.now
15
+ puts "Tick: #{tick[:segment]}:#{tick[:security_id]} LTP=#{tick[:ltp]} at #{timestamp}"
16
+ end
17
+
18
+ # Subscribe to segments and security IDs
19
+ market_client.subscribe_one(segment: "NSE_EQ", security_id: "2885")
20
+ market_client.subscribe_one(segment: "NSE_EQ", security_id: "1333")
21
+
22
+ # Stop connection
23
+ sleep(15)
24
+ market_client.stop
25
+ ```
26
+
27
+ ### Modes
28
+ - `:ticker` - LTP (Last Traded Price) only.
29
+ - `:quote` - OHLC + Volume updates.
30
+ - `:full` - Full quote depth (5 levels) and Open Interest (OI) updates.
31
+
32
+ ---
33
+
34
+ ## 2. Order Updates (`DhanHQ::WS::Orders.connect`)
35
+
36
+ Streams real-time updates for placed, modified, executed, or rejected orders.
37
+
38
+ ### Usage
39
+
40
+ ```ruby
41
+ orders_client = DhanHQ::WS::Orders.connect do |update|
42
+ puts "Order Update: #{update.order_no} status=#{update.status}"
43
+ puts " Symbol: #{update.symbol}, Traded: #{update.traded_qty}/#{update.quantity}"
44
+ end
45
+
46
+ # Register event callbacks
47
+ orders_client.on(:update) { |order| puts "📝 Order Modified: #{order.order_no}" }
48
+ orders_client.on(:execution) { |exec| puts "✅ Executed: #{exec[:new_traded_qty]} shares" }
49
+ orders_client.on(:order_rejected) { |order| puts "❌ Rejected: #{order.order_no}" }
50
+
51
+ sleep(15)
52
+ orders_client.stop
53
+ ```
54
+
55
+ ---
56
+
57
+ ## 3. Market Depth (`DhanHQ::WS::MarketDepth.connect`)
58
+
59
+ Streams order book depth (bid/ask levels). Supports 20-level depth.
60
+
61
+ ### Usage
62
+
63
+ ```ruby
64
+ symbols = [
65
+ { symbol: "RELIANCE", exchange_segment: "NSE_EQ", security_id: "2885" },
66
+ { symbol: "TCS", exchange_segment: "NSE_EQ", security_id: "11536" }
67
+ ]
68
+
69
+ depth_client = DhanHQ::WS::MarketDepth.connect(symbols: symbols) do |depth|
70
+ puts "Symbol: #{depth[:symbol]} Spread: #{depth[:spread]}"
71
+ puts " Best Bid: #{depth[:best_bid]} | Best Ask: #{depth[:best_ask]}"
72
+ end
73
+
74
+ sleep(15)
75
+ depth_client.stop
76
+ ```
77
+
78
+ ---
79
+
80
+ ## Connection Limits & Cleanup
81
+
82
+ - Dhan allows up to **5 concurrent WebSocket connections** per client account.
83
+ - Always call `client.stop` or `DhanHQ::WS.disconnect_all_local!` to prevent socket leaks and rate-limit issues (`429 Too Many Requests`).
@@ -0,0 +1,119 @@
1
+ # Market Data — Complete Reference (Ruby SDK)
2
+
3
+ Timestamps returned by the `HistoricalData` model are automatically normalized into Ruby `Time` objects.
4
+
5
+ ## Historical Daily Data
6
+
7
+ Use `DhanHQ::Models::HistoricalData.daily(params)`:
8
+
9
+ ```ruby
10
+ candles = DhanHQ::Models::HistoricalData.daily(
11
+ security_id: "2885",
12
+ exchange_segment: "NSE_EQ",
13
+ instrument: "EQUITY",
14
+ from_date: "2024-01-01",
15
+ to_date: "2024-12-31",
16
+ expiry_code: 0, # Optional: 0 for current, 1 for next, 2 for far
17
+ oi: false # Optional: true to include open interest
18
+ )
19
+
20
+ first_candle = candles.first
21
+ puts "Date: #{first_candle[:timestamp]}, Close: ₹#{first_candle[:close]}"
22
+ ```
23
+
24
+ Each candle in the returned array is a Hash containing:
25
+ - `:timestamp` (Ruby `Time` object)
26
+ - `:open` (Float)
27
+ - `:high` (Float)
28
+ - `:low` (Float)
29
+ - `:close` (Float)
30
+ - `:volume` (Integer)
31
+ - `:open_interest` (Float, only if `oi: true` was requested)
32
+
33
+ ## Intraday Minute Data
34
+
35
+ Use `DhanHQ::Models::HistoricalData.intraday(params)`:
36
+
37
+ ```ruby
38
+ candles = DhanHQ::Models::HistoricalData.intraday(
39
+ security_id: "2885",
40
+ exchange_segment: "NSE_EQ",
41
+ instrument: "EQUITY",
42
+ interval: "15", # Supported: "1", "5", "15", "25", "60"
43
+ from_date: "2024-09-11 09:30:00",
44
+ to_date: "2024-09-15 13:00:00",
45
+ oi: false
46
+ )
47
+ ```
48
+
49
+ - Max 90 days of data can be polled in a single request.
50
+ - Returns a normalized array of candle hashes.
51
+
52
+ ---
53
+
54
+ ## Market Quote Snapshots
55
+
56
+ REST quote snapshots are accessed via the `DhanHQ::Models::MarketFeed` model.
57
+
58
+ ### Ticker Data (LTP only)
59
+
60
+ ```ruby
61
+ response = DhanHQ::Models::MarketFeed.ltp(
62
+ "NSE_EQ" => [2885, 1333],
63
+ "NSE_FNO" => [49081]
64
+ )
65
+
66
+ ltp = response[:data]["NSE_EQ"]["2885"][:last_price]
67
+ ```
68
+
69
+ ### OHLC Data
70
+
71
+ ```ruby
72
+ response = DhanHQ::Models::MarketFeed.ohlc(
73
+ "NSE_EQ" => [2885]
74
+ )
75
+
76
+ ohlc = response[:data]["NSE_EQ"]["2885"][:ohlc]
77
+ ```
78
+
79
+ ### Quote Data (Full Quote Depth & Analytics)
80
+
81
+ ```ruby
82
+ response = DhanHQ::Models::MarketFeed.quote(
83
+ "NSE_FNO" => [49081]
84
+ )
85
+
86
+ quote = response[:data]["NSE_FNO"]["49081"]
87
+ puts "LTP: #{quote[:last_price]}, OI: #{quote[:oi]}, Vol: #{quote[:volume]}"
88
+ ```
89
+
90
+ ---
91
+
92
+ ## Expired Options Data
93
+
94
+ Use `DhanHQ::Models::ExpiredOptionsData.fetch(params)` (or direct resource access):
95
+
96
+ ```ruby
97
+ response = DhanHQ::Models::ExpiredOptionsData.fetch(
98
+ underlying_scrip: 13,
99
+ exchange_segment: "NSE_FNO",
100
+ expiry_flag: "MONTH",
101
+ expiry_code: 1,
102
+ strike: "ATM",
103
+ option_type: "CALL",
104
+ required_data: ["open", "high", "low", "close", "volume", "oi", "spot"],
105
+ from_date: "2021-08-01",
106
+ to_date: "2021-08-31",
107
+ interval: "1"
108
+ )
109
+ ```
110
+
111
+ ---
112
+
113
+ ## Timestamp Conversion
114
+
115
+ If using raw API responses where timestamps are UNIX epochs, convert them to Ruby Time:
116
+
117
+ ```ruby
118
+ time = Time.at(epoch_timestamp)
119
+ ```
@@ -0,0 +1,71 @@
1
+ # Option Chain — Complete Reference (Ruby SDK)
2
+
3
+ For analysis code, use the helper layer `fetch_chain_df` from `scripts/dhan_helpers.rb`.
4
+
5
+ ## Expiry List
6
+
7
+ Use `DhanHQ::Models::OptionChain.fetch_expiry_list(params)`:
8
+
9
+ ```ruby
10
+ expiries = DhanHQ::Models::OptionChain.fetch_expiry_list(
11
+ underlying_scrip: 13,
12
+ underlying_seg: "IDX_I"
13
+ )
14
+ ```
15
+
16
+ ## Option Chain
17
+
18
+ Use `DhanHQ::Models::OptionChain.fetch(params)`:
19
+
20
+ ```ruby
21
+ chain = DhanHQ::Models::OptionChain.fetch(
22
+ underlying_scrip: 13,
23
+ underlying_seg: "IDX_I",
24
+ expiry: "2025-03-27"
25
+ )
26
+
27
+ # Underlying LTP
28
+ spot = chain[:last_price]
29
+
30
+ # Strikes sorted array
31
+ chain[:strikes].each do |strike_data|
32
+ puts "Strike: #{strike_data[:strike]}"
33
+ puts "Call LTP: #{strike_data[:call][:last_price]}"
34
+ puts "Put Delta: #{strike_data[:put][:greeks][:delta]}"
35
+ end
36
+ ```
37
+
38
+ ### Rate Limits
39
+ - Calls are limited to **1 request every 3 seconds**. The SDK's internal rate limiter handles this.
40
+
41
+ ---
42
+
43
+ ## Normalized Helper Layer
44
+
45
+ ```ruby
46
+ require_relative "../scripts/dhan_helpers"
47
+
48
+ chain_rows, spot = fetch_chain_df(
49
+ under_security_id: 13,
50
+ expiry: "2025-03-27",
51
+ under_exchange_segment: "IDX_I"
52
+ )
53
+
54
+ atm = find_atm_row(chain_rows, spot)
55
+ puts "Spot: #{spot}, ATM Strike: #{atm['strike']}, Call LTP: #{atm['ce_ltp']}"
56
+ ```
57
+
58
+ Normalized columns returned by `fetch_chain_df`:
59
+ - `strike`
60
+ - `ce_security_id`, `pe_security_id`
61
+ - `ce_ltp`, `pe_ltp`
62
+ - `ce_oi`, `pe_oi`
63
+ - `ce_oi_change`, `pe_oi_change`
64
+ - `ce_volume`, `pe_volume`
65
+ - `ce_iv`, `pe_iv`
66
+ - `ce_bid_price`, `pe_bid_price`
67
+ - `ce_ask_price`, `pe_ask_price`
68
+ - `ce_delta`, `pe_delta`
69
+ - `ce_gamma`, `pe_gamma`
70
+ - `ce_theta`, `pe_theta`
71
+ - `ce_vega`, `pe_vega`
@@ -0,0 +1,76 @@
1
+ # Options Analysis Patterns (Ruby SDK)
2
+
3
+ Use the normalized helper output from `scripts/dhan_helpers.rb` for option chain analysis:
4
+
5
+ ```ruby
6
+ require_relative "../scripts/dhan_helpers"
7
+
8
+ chain_rows, spot = fetch_chain_df(
9
+ under_security_id: 13,
10
+ expiry: "2025-03-27",
11
+ under_exchange_segment: "IDX_I"
12
+ )
13
+
14
+ atm = find_atm_row(chain_rows, spot)
15
+ ```
16
+
17
+ ## Put-Call Ratio (PCR)
18
+
19
+ ```ruby
20
+ total_ce_oi = chain_rows.sum { |r| r["ce_oi"].to_f }
21
+ total_pe_oi = chain_rows.sum { |r| r["pe_oi"].to_f }
22
+ pcr = total_ce_oi > 0 ? (total_pe_oi / total_ce_oi) : 0.0
23
+ puts "PCR: #{'%.2f' % pcr}"
24
+ ```
25
+
26
+ ## OI Support / Resistance
27
+
28
+ Find strikes with the highest open interest for resistance (CE) and support (PE):
29
+
30
+ ```ruby
31
+ # Top 3 resistance walls (highest Call OI)
32
+ ce_walls = chain_rows.sort_by { |r| -(r["ce_oi"] || 0) }.first(3)
33
+
34
+ # Top 3 support walls (highest Put OI)
35
+ pe_walls = chain_rows.sort_by { |r| -(r["pe_oi"] || 0) }.first(3)
36
+ ```
37
+
38
+ ## IV Skew
39
+
40
+ ```ruby
41
+ otm_puts = chain_rows.select { |r| r["strike"] < spot }.sort_by { |r| -r["strike"] }.first(3)
42
+ otm_calls = chain_rows.select { |r| r["strike"] > spot }.sort_by { |r| r["strike"] }.first(3)
43
+
44
+ put_iv_avg = otm_puts.sum { |r| r["pe_iv"].to_f } / otm_puts.size.to_f
45
+ call_iv_avg = otm_calls.sum { |r| r["ce_iv"].to_f } / otm_calls.size.to_f
46
+ skew = put_iv_avg - call_iv_avg
47
+ ```
48
+
49
+ ## Max Pain
50
+
51
+ Calculate the option strike price where option buyers would experience the maximum loss:
52
+
53
+ ```ruby
54
+ def calculate_max_pain(chain_rows)
55
+ strikes = chain_rows.map { |r| r["strike"] }
56
+ pain = {}
57
+
58
+ strikes.each do |test_price|
59
+ total = 0.0
60
+ chain_rows.each do |row|
61
+ strike = row["strike"]
62
+ ce_oi = row["ce_oi"].to_f
63
+ pe_oi = row["pe_oi"].to_f
64
+
65
+ total += [test_price - strike, 0.0].max * ce_oi
66
+ total += [strike - test_price, 0.0].max * pe_oi
67
+ end
68
+ pain[test_price] = total
69
+ end
70
+
71
+ pain.min_by { |_strike, total_pain| total_pain }&.first
72
+ end
73
+
74
+ max_pain_strike = calculate_max_pain(chain_rows)
75
+ puts "Max Pain Strike: #{max_pain_strike}"
76
+ ```
@@ -0,0 +1,203 @@
1
+ # Orders — Complete Reference (Ruby SDK)
2
+
3
+ Critical API rules:
4
+ - Order placement, modification, cancellation, super orders, and forever orders require static IP whitelisting.
5
+ - Dhan's current order docs say API market orders are converted to limit orders with MPP.
6
+ - **`ENV["LIVE_TRADING"]="true"` is required for `place`/`create`/`modify`/`cancel` to actually submit anything** — the gem raises `DhanHQ::LiveTradingDisabledError` otherwise, as a safety gate against accidental order placement from a dev machine.
7
+ - **`correlation_id` must be 25 characters or fewer.** Dhan's real API rejects the entire order with a generic `DH-905` error if exceeded — it doesn't say which field was wrong.
8
+
9
+ ## Regular Orders
10
+
11
+ ### Place Order
12
+
13
+ In the Ruby SDK, prefer using the model class `DhanHQ::Models::Order.place(params)`:
14
+
15
+ ```ruby
16
+ order = DhanHQ::Models::Order.place(
17
+ security_id: "2885",
18
+ exchange_segment: DhanHQ::Constants::ExchangeSegment::NSE_EQ,
19
+ transaction_type: DhanHQ::Constants::TransactionType::BUY,
20
+ quantity: 10,
21
+ order_type: DhanHQ::Constants::OrderType::LIMIT,
22
+ product_type: DhanHQ::Constants::ProductType::CNC,
23
+ price: 2450.0,
24
+ validity: DhanHQ::Constants::Validity::DAY,
25
+ correlation_id: "rebalance_001"
26
+ )
27
+
28
+ if order
29
+ puts "Placed Order ID: #{order.order_id}, Status: #{order.order_status}"
30
+ end
31
+ ```
32
+
33
+ Alternatively, you can use the ActiveRecord-style `new` and `save` flow:
34
+
35
+ ```ruby
36
+ order = DhanHQ::Models::Order.new(
37
+ security_id: "2885",
38
+ exchange_segment: "NSE_EQ",
39
+ transaction_type: "BUY",
40
+ quantity: 10,
41
+ order_type: "LIMIT",
42
+ product_type: "CNC",
43
+ price: 2450.0,
44
+ validity: "DAY"
45
+ )
46
+ order.save # Places the order via API
47
+ ```
48
+
49
+ ### Slice Order
50
+
51
+ If placing a large quantity that exceeds exchange freeze limits, the SDK handles slicing automatically when using the slice API:
52
+
53
+ ```ruby
54
+ order.slice_order(
55
+ slice_quantity: 1000
56
+ )
57
+ ```
58
+
59
+ ### Modify Order
60
+
61
+ Modify a pending order directly on the model instance:
62
+
63
+ ```ruby
64
+ order = DhanHQ::Models::Order.find("112111182198")
65
+ if order.pending?
66
+ order.modify(
67
+ price: 2455.0,
68
+ quantity: 10,
69
+ validity: "DAY"
70
+ )
71
+ end
72
+ ```
73
+
74
+ The modify request expects the full placed quantity, not the pending quantity.
75
+
76
+ ### Cancel Order
77
+
78
+ Cancel a pending order directly on the model instance:
79
+
80
+ ```ruby
81
+ order = DhanHQ::Models::Order.find("112111182198")
82
+ order.cancel # Returns true on success
83
+ ```
84
+
85
+ ### Order Retrieval
86
+
87
+ ```ruby
88
+ # Fetch all orders for today
89
+ orders = DhanHQ::Models::Order.all
90
+
91
+ # Find order by ID
92
+ order = DhanHQ::Models::Order.find("112111182198")
93
+
94
+ # Find order by correlation ID
95
+ order = DhanHQ::Models::Order.find_by_correlation("rebalance_001")
96
+
97
+ # Fetch today's trades
98
+ trades = DhanHQ::Models::Trade.today
99
+
100
+ # Find trades by order ID
101
+ trade = DhanHQ::Models::Trade.find_by_order_id("112111182198")
102
+
103
+ # Fetch trade history
104
+ history = DhanHQ::Models::Trade.history(
105
+ from_date: "2025-01-01",
106
+ to_date: "2025-01-31",
107
+ page: 0
108
+ )
109
+ ```
110
+
111
+ ---
112
+
113
+ ## Super Orders (Bracket/Cover Orders)
114
+
115
+ ### Place Super Order
116
+
117
+ Use the `DhanHQ::Models::SuperOrder.create` method:
118
+
119
+ ```ruby
120
+ super_order = DhanHQ::Models::SuperOrder.create(
121
+ security_id: "2885",
122
+ exchange_segment: "NSE_EQ",
123
+ transaction_type: "BUY",
124
+ quantity: 1,
125
+ order_type: "LIMIT",
126
+ product_type: "INTRADAY",
127
+ price: 2450.0,
128
+ target_price: 2500.0,
129
+ stop_loss_price: 2420.0,
130
+ trailing_jump: 10.0
131
+ )
132
+
133
+ puts "Placed Super Order ID: #{super_order.order_id}"
134
+ ```
135
+
136
+ ### Modify Super Order
137
+
138
+ ```ruby
139
+ super_order.modify(
140
+ leg_name: "ENTRY_LEG",
141
+ price: 2455.0,
142
+ quantity: 1,
143
+ target_price: 2510.0,
144
+ stop_loss_price: 2425.0,
145
+ trailing_jump: 10.0
146
+ )
147
+ ```
148
+
149
+ - `ENTRY_LEG` can modify the whole structure while the entry order is `PENDING` or `PART_TRADED`.
150
+ - After entry is `TRADED`, only price and trailing jump of `TARGET_LEG` and `STOP_LOSS_LEG` can be modified.
151
+
152
+ ### Cancel Super Order
153
+
154
+ ```ruby
155
+ super_order.cancel("ENTRY_LEG") # Cancels all legs
156
+ ```
157
+
158
+ ---
159
+
160
+ ## Forever Orders (GTT Orders)
161
+
162
+ ### Place Forever Order
163
+
164
+ Use the `DhanHQ::Models::ForeverOrder.create` method:
165
+
166
+ ```ruby
167
+ # Single Trigger
168
+ gtt_order = DhanHQ::Models::ForeverOrder.create(
169
+ security_id: "2885",
170
+ exchange_segment: "NSE_EQ",
171
+ transaction_type: "BUY",
172
+ product_type: "CNC",
173
+ order_type: "LIMIT",
174
+ quantity: 5,
175
+ price: 2300.0,
176
+ trigger_price: 2305.0,
177
+ order_flag: "SINGLE",
178
+ validity: "DAY"
179
+ )
180
+
181
+ # OCO (One Cancels Other) target + stop loss
182
+ oco_order = DhanHQ::Models::ForeverOrder.create(
183
+ security_id: "2885",
184
+ exchange_segment: "NSE_EQ",
185
+ transaction_type: "SELL",
186
+ product_type: "CNC",
187
+ order_type: "LIMIT",
188
+ quantity: 5,
189
+ price: 2700.00, # Target price (price of first leg)
190
+ trigger_price: 2695.00, # Target trigger price (trigger of first leg)
191
+ price1: 2200.00, # Stop loss price (price of second leg)
192
+ trigger_price1: 2205.00, # Stop loss trigger price (trigger of second leg)
193
+ quantity1: 5, # Stop loss quantity (quantity of second leg)
194
+ order_flag: "OCO",
195
+ validity: "DAY"
196
+ )
197
+ ```
198
+
199
+ ### Cancel Forever Order
200
+
201
+ ```ruby
202
+ gtt_order.cancel # Returns true on success
203
+ ```