DhanHQ 3.1.0 → 3.2.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 (97) hide show
  1. checksums.yaml +4 -4
  2. data/ARCHITECTURE.md +47 -1
  3. data/CHANGELOG.md +47 -0
  4. data/README.md +241 -3
  5. data/docs/AUTHENTICATION.md +3 -5
  6. data/docs/CONFIGURATION.md +3 -2
  7. data/docs/CONSTANTS_REFERENCE.md +5 -4
  8. data/docs/ENDPOINTS_AND_SANDBOX.md +1 -0
  9. data/docs/LIVE_ORDER_UPDATES.md +5 -10
  10. data/docs/RAILS_INTEGRATION.md +1 -1
  11. data/docs/RELEASE_GUIDE.md +13 -2
  12. data/docs/STANDALONE_RUBY_WEBSOCKET_INTEGRATION.md +2 -2
  13. data/docs/WEBSOCKET_INTEGRATION.md +13 -20
  14. data/docs/WEBSOCKET_PROTOCOL.md +7 -3
  15. data/lib/DhanHQ/agent/key_coercion.rb +36 -0
  16. data/lib/DhanHQ/agent/tool.rb +37 -0
  17. data/lib/DhanHQ/agent/tool_catalogue.rb +180 -0
  18. data/lib/DhanHQ/agent/tool_handlers.rb +116 -0
  19. data/lib/DhanHQ/agent/tool_registry.rb +17 -261
  20. data/lib/DhanHQ/agent/tool_schemas.rb +160 -0
  21. data/lib/DhanHQ/client.rb +58 -2
  22. data/lib/DhanHQ/concerns/order_audit.rb +35 -4
  23. data/lib/DhanHQ/configuration.rb +70 -1
  24. data/lib/DhanHQ/constants.rb +101 -0
  25. data/lib/DhanHQ/contracts/expired_options_data_contract.rb +1 -9
  26. data/lib/DhanHQ/contracts/global_stocks_estimator_contract.rb +28 -0
  27. data/lib/DhanHQ/contracts/global_stocks_modify_order_contract.rb +26 -0
  28. data/lib/DhanHQ/contracts/global_stocks_order_contract.rb +33 -0
  29. data/lib/DhanHQ/contracts/global_stocks_place_order_contract.rb +75 -0
  30. data/lib/DhanHQ/contracts/historical_data_contract.rb +1 -21
  31. data/lib/DhanHQ/contracts/multi_order_contract.rb +74 -0
  32. data/lib/DhanHQ/contracts/trade_history_contract.rb +1 -8
  33. data/lib/DhanHQ/dry_run/ledger.rb +71 -0
  34. data/lib/DhanHQ/dry_run/simulator.rb +140 -0
  35. data/lib/DhanHQ/helpers/attribute_helper.rb +23 -0
  36. data/lib/DhanHQ/models/alert_order.rb +5 -2
  37. data/lib/DhanHQ/models/global_stocks/funds.rb +56 -0
  38. data/lib/DhanHQ/models/global_stocks/holding.rb +101 -0
  39. data/lib/DhanHQ/models/global_stocks/margin.rb +54 -0
  40. data/lib/DhanHQ/models/global_stocks/market_status.rb +63 -0
  41. data/lib/DhanHQ/models/global_stocks/order.rb +189 -0
  42. data/lib/DhanHQ/models/global_stocks/order_estimate.rb +61 -0
  43. data/lib/DhanHQ/models/global_stocks/trade.rb +74 -0
  44. data/lib/DhanHQ/models/margin.rb +5 -1
  45. data/lib/DhanHQ/models/multi_order.rb +130 -0
  46. data/lib/DhanHQ/resources/global_stocks/funds.rb +25 -0
  47. data/lib/DhanHQ/resources/global_stocks/holdings.rb +22 -0
  48. data/lib/DhanHQ/resources/global_stocks/margin_calculator.rb +70 -0
  49. data/lib/DhanHQ/resources/global_stocks/market_status.rb +22 -0
  50. data/lib/DhanHQ/resources/global_stocks/orders.rb +112 -0
  51. data/lib/DhanHQ/resources/global_stocks/trades.rb +31 -0
  52. data/lib/DhanHQ/resources/multi_orders.rb +58 -0
  53. data/lib/DhanHQ/version.rb +1 -1
  54. data/lib/DhanHQ/write_paths.rb +57 -0
  55. data/lib/DhanHQ/ws/client.rb +117 -2
  56. data/lib/DhanHQ/ws/connection.rb +40 -11
  57. data/lib/DhanHQ/ws/sub_state.rb +14 -0
  58. data/lib/dhanhq/analysis/options_buying_advisor.rb +11 -10
  59. metadata +30 -40
  60. data/.rspec +0 -3
  61. data/.rubocop.yml +0 -50
  62. data/.rubocop_todo.yml +0 -217
  63. data/AGENTS.md +0 -23
  64. data/CODE_OF_CONDUCT.md +0 -132
  65. data/Rakefile +0 -14
  66. data/TAGS +0 -10
  67. data/core +0 -0
  68. data/diagram.html +0 -184
  69. data/skills/dhanhq-ruby/SKILL.md +0 -207
  70. data/skills/dhanhq-ruby/examples/fetch_option_chain.rb +0 -54
  71. data/skills/dhanhq-ruby/examples/gtt_forever_order.rb +0 -65
  72. data/skills/dhanhq-ruby/examples/historical_data_analysis.rb +0 -89
  73. data/skills/dhanhq-ruby/examples/iron_condor.rb +0 -137
  74. data/skills/dhanhq-ruby/examples/live_feed_setup.rb +0 -43
  75. data/skills/dhanhq-ruby/examples/margin_check.rb +0 -42
  76. data/skills/dhanhq-ruby/examples/order_management.rb +0 -105
  77. data/skills/dhanhq-ruby/examples/place_equity_order.rb +0 -36
  78. data/skills/dhanhq-ruby/examples/place_fno_order.rb +0 -76
  79. data/skills/dhanhq-ruby/examples/portfolio_summary.rb +0 -74
  80. data/skills/dhanhq-ruby/examples/super_order_with_sl.rb +0 -57
  81. data/skills/dhanhq-ruby/references/backtesting-with-dhan.md +0 -65
  82. data/skills/dhanhq-ruby/references/common-workflows.md +0 -76
  83. data/skills/dhanhq-ruby/references/error-codes.md +0 -50
  84. data/skills/dhanhq-ruby/references/funds.md +0 -67
  85. data/skills/dhanhq-ruby/references/instruments.md +0 -85
  86. data/skills/dhanhq-ruby/references/live-feed.md +0 -83
  87. data/skills/dhanhq-ruby/references/market-data.md +0 -119
  88. data/skills/dhanhq-ruby/references/option-chain.md +0 -71
  89. data/skills/dhanhq-ruby/references/options-analysis-patterns.md +0 -76
  90. data/skills/dhanhq-ruby/references/orders.md +0 -201
  91. data/skills/dhanhq-ruby/references/portfolio.md +0 -93
  92. data/skills/dhanhq-ruby/references/scanx-data.md +0 -62
  93. data/skills/dhanhq-ruby/scripts/dhan_helpers.rb +0 -323
  94. data/skills/dhanhq-ruby/scripts/resolve_security.rb +0 -168
  95. data/skills/dhanhq-ruby/scripts/trade_logger.rb +0 -131
  96. data/skills/dhanhq-ruby/scripts/validate_order.rb +0 -169
  97. data/watchlist.csv +0 -3
@@ -1,83 +0,0 @@
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`).
@@ -1,119 +0,0 @@
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
- ```
@@ -1,71 +0,0 @@
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`
@@ -1,76 +0,0 @@
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
- ```
@@ -1,201 +0,0 @@
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
-
7
- ## Regular Orders
8
-
9
- ### Place Order
10
-
11
- In the Ruby SDK, prefer using the model class `DhanHQ::Models::Order.place(params)`:
12
-
13
- ```ruby
14
- order = DhanHQ::Models::Order.place(
15
- security_id: "2885",
16
- exchange_segment: DhanHQ::Constants::ExchangeSegment::NSE_EQ,
17
- transaction_type: DhanHQ::Constants::TransactionType::BUY,
18
- quantity: 10,
19
- order_type: DhanHQ::Constants::OrderType::LIMIT,
20
- product_type: DhanHQ::Constants::ProductType::CNC,
21
- price: 2450.0,
22
- validity: DhanHQ::Constants::Validity::DAY,
23
- correlation_id: "rebalance_001"
24
- )
25
-
26
- if order
27
- puts "Placed Order ID: #{order.order_id}, Status: #{order.order_status}"
28
- end
29
- ```
30
-
31
- Alternatively, you can use the ActiveRecord-style `new` and `save` flow:
32
-
33
- ```ruby
34
- order = DhanHQ::Models::Order.new(
35
- security_id: "2885",
36
- exchange_segment: "NSE_EQ",
37
- transaction_type: "BUY",
38
- quantity: 10,
39
- order_type: "LIMIT",
40
- product_type: "CNC",
41
- price: 2450.0,
42
- validity: "DAY"
43
- )
44
- order.save # Places the order via API
45
- ```
46
-
47
- ### Slice Order
48
-
49
- If placing a large quantity that exceeds exchange freeze limits, the SDK handles slicing automatically when using the slice API:
50
-
51
- ```ruby
52
- order.slice_order(
53
- slice_quantity: 1000
54
- )
55
- ```
56
-
57
- ### Modify Order
58
-
59
- Modify a pending order directly on the model instance:
60
-
61
- ```ruby
62
- order = DhanHQ::Models::Order.find("112111182198")
63
- if order.pending?
64
- order.modify(
65
- price: 2455.0,
66
- quantity: 10,
67
- validity: "DAY"
68
- )
69
- end
70
- ```
71
-
72
- The modify request expects the full placed quantity, not the pending quantity.
73
-
74
- ### Cancel Order
75
-
76
- Cancel a pending order directly on the model instance:
77
-
78
- ```ruby
79
- order = DhanHQ::Models::Order.find("112111182198")
80
- order.cancel # Returns true on success
81
- ```
82
-
83
- ### Order Retrieval
84
-
85
- ```ruby
86
- # Fetch all orders for today
87
- orders = DhanHQ::Models::Order.all
88
-
89
- # Find order by ID
90
- order = DhanHQ::Models::Order.find("112111182198")
91
-
92
- # Find order by correlation ID
93
- order = DhanHQ::Models::Order.find_by_correlation("rebalance_001")
94
-
95
- # Fetch today's trades
96
- trades = DhanHQ::Models::Trade.today
97
-
98
- # Find trades by order ID
99
- trade = DhanHQ::Models::Trade.find_by_order_id("112111182198")
100
-
101
- # Fetch trade history
102
- history = DhanHQ::Models::Trade.history(
103
- from_date: "2025-01-01",
104
- to_date: "2025-01-31",
105
- page: 0
106
- )
107
- ```
108
-
109
- ---
110
-
111
- ## Super Orders (Bracket/Cover Orders)
112
-
113
- ### Place Super Order
114
-
115
- Use the `DhanHQ::Models::SuperOrder.create` method:
116
-
117
- ```ruby
118
- super_order = DhanHQ::Models::SuperOrder.create(
119
- security_id: "2885",
120
- exchange_segment: "NSE_EQ",
121
- transaction_type: "BUY",
122
- quantity: 1,
123
- order_type: "LIMIT",
124
- product_type: "INTRADAY",
125
- price: 2450.0,
126
- target_price: 2500.0,
127
- stop_loss_price: 2420.0,
128
- trailing_jump: 10.0
129
- )
130
-
131
- puts "Placed Super Order ID: #{super_order.order_id}"
132
- ```
133
-
134
- ### Modify Super Order
135
-
136
- ```ruby
137
- super_order.modify(
138
- leg_name: "ENTRY_LEG",
139
- price: 2455.0,
140
- quantity: 1,
141
- target_price: 2510.0,
142
- stop_loss_price: 2425.0,
143
- trailing_jump: 10.0
144
- )
145
- ```
146
-
147
- - `ENTRY_LEG` can modify the whole structure while the entry order is `PENDING` or `PART_TRADED`.
148
- - After entry is `TRADED`, only price and trailing jump of `TARGET_LEG` and `STOP_LOSS_LEG` can be modified.
149
-
150
- ### Cancel Super Order
151
-
152
- ```ruby
153
- super_order.cancel("ENTRY_LEG") # Cancels all legs
154
- ```
155
-
156
- ---
157
-
158
- ## Forever Orders (GTT Orders)
159
-
160
- ### Place Forever Order
161
-
162
- Use the `DhanHQ::Models::ForeverOrder.create` method:
163
-
164
- ```ruby
165
- # Single Trigger
166
- gtt_order = DhanHQ::Models::ForeverOrder.create(
167
- security_id: "2885",
168
- exchange_segment: "NSE_EQ",
169
- transaction_type: "BUY",
170
- product_type: "CNC",
171
- order_type: "LIMIT",
172
- quantity: 5,
173
- price: 2300.0,
174
- trigger_price: 2305.0,
175
- order_flag: "SINGLE",
176
- validity: "DAY"
177
- )
178
-
179
- # OCO (One Cancels Other) target + stop loss
180
- oco_order = DhanHQ::Models::ForeverOrder.create(
181
- security_id: "2885",
182
- exchange_segment: "NSE_EQ",
183
- transaction_type: "SELL",
184
- product_type: "CNC",
185
- order_type: "LIMIT",
186
- quantity: 5,
187
- price: 2700.00, # Target price (price of first leg)
188
- trigger_price: 2695.00, # Target trigger price (trigger of first leg)
189
- price1: 2200.00, # Stop loss price (price of second leg)
190
- trigger_price1: 2205.00, # Stop loss trigger price (trigger of second leg)
191
- quantity1: 5, # Stop loss quantity (quantity of second leg)
192
- order_flag: "OCO",
193
- validity: "DAY"
194
- )
195
- ```
196
-
197
- ### Cancel Forever Order
198
-
199
- ```ruby
200
- gtt_order.cancel # Returns true on success
201
- ```
@@ -1,93 +0,0 @@
1
- # Portfolio And Positions — Complete Reference (Ruby SDK)
2
-
3
- ## Holdings
4
-
5
- Use `DhanHQ::Models::Holding.all`:
6
-
7
- ```ruby
8
- holdings = DhanHQ::Models::Holding.all
9
-
10
- holdings.each do |holding|
11
- puts "#{holding.trading_symbol} available=#{holding.available_qty}"
12
- end
13
- ```
14
-
15
- Useful holding fields:
16
- - `exchange`
17
- - `trading_symbol`
18
- - `security_id`
19
- - `isin`
20
- - `total_qty`
21
- - `dp_qty`
22
- - `t1_qty`
23
- - `available_qty`
24
- - `collateral_qty`
25
- - `avg_cost_price`
26
-
27
- ---
28
-
29
- ## Positions
30
-
31
- Use `DhanHQ::Models::Position.all`:
32
-
33
- ```ruby
34
- positions = DhanHQ::Models::Position.all
35
- open_positions = positions.select { |p| p.net_qty.to_i != 0 }
36
- ```
37
-
38
- Useful position fields:
39
- - `trading_symbol`
40
- - `security_id`
41
- - `position_type` # "LONG" or "SHORT"
42
- - `exchange_segment`
43
- - `product_type`
44
- - `buy_avg`
45
- - `buy_qty`
46
- - `sell_avg`
47
- - `sell_qty`
48
- - `net_qty`
49
- - `realized_profit`
50
- - `unrealized_profit`
51
-
52
- ---
53
-
54
- ## Convert Position
55
-
56
- Convert an open position (e.g. from Intraday to CNC/Carry Forward):
57
-
58
- ```ruby
59
- # In the Ruby SDK, call convert directly on a Position model instance
60
- position = DhanHQ::Models::Position.all.first
61
- position.convert(
62
- from_product_type: "INTRADAY",
63
- to_product_type: "CNC",
64
- position_type: "LONG",
65
- convert_qty: 1
66
- )
67
- ```
68
-
69
- ---
70
-
71
- ## eDIS Authorization
72
-
73
- For selling delivery holdings, authorization is handled via `DhanHQ::Models::EDIS`:
74
-
75
- ### Step 1: Generate TPIN
76
- ```ruby
77
- DhanHQ::Models::EDIS.generate_tpin
78
- ```
79
-
80
- ### Step 2: Open Browser Authorization
81
- ```ruby
82
- DhanHQ::Models::EDIS.open_browser_for_tpin(
83
- isin: "INE002A01018",
84
- qty: 5,
85
- exchange: "NSE"
86
- )
87
- ```
88
-
89
- ### Step 3: Inquiry eDIS Approval
90
- ```ruby
91
- inquiry = DhanHQ::Models::EDIS.inquiry(isin: "INE002A01018")
92
- puts "Approved Qty: #{inquiry.aprvd_qty}, Status: #{inquiry.status}"
93
- ```
@@ -1,62 +0,0 @@
1
- # ScanX — Fundamental and Technical Data
2
-
3
- Use ScanX when Dhan APIs do not cover the needed data. Dhan provides execution, quotes, OHLC, option chain, and portfolio. ScanX provides fundamentals, technical indicators, shareholding, and screeners.
4
-
5
- ## Capability Gap
6
-
7
- | Data needed | Use |
8
- |------------|-----|
9
- | PE ratio, EPS, Book Value, PB Ratio | ScanX |
10
- | Revenue, Net Profit, EBITDA | ScanX |
11
- | Debt-to-equity, Return on Equity | ScanX |
12
- | RSI(14), MACD(12,26), ADX(14), ATR(14) | ScanX |
13
- | Promoter %, FII %, DII %, Public % | ScanX |
14
- | Quarterly results history (2015–present) | ScanX |
15
- | Balance Sheet, Cash Flows | ScanX |
16
- | Stock screeners (fundamental/technical) | ScanX |
17
- | Live quotes, OHLC, option chain | Dhan |
18
- | Order execution, portfolio | Dhan |
19
-
20
- ---
21
-
22
- ## Company Page URL Pattern
23
-
24
- `https://scanx.trade/company/{slug}`
25
-
26
- Slug rules:
27
- - Lowercase the full registered company name.
28
- - Replace spaces with hyphens.
29
- - Include "ltd" if part of the official name.
30
-
31
- ---
32
-
33
- ## Combined Workflow: Analyze on ScanX → Execute on Dhan
34
-
35
- ```ruby
36
- # Step 1: fetch ScanX page for fundamentals/technicals
37
- # -> https://scanx.trade/company/reliance-industries-ltd
38
- # -> Extract metrics: PE, RSI, etc.
39
-
40
- # Step 2: resolve security_id from Dhan security master
41
- require_relative "../scripts/dhan_helpers"
42
- get_client
43
-
44
- row = resolve_symbol("RELIANCE", "NSE_EQ")
45
- security_id = row["security_id"]
46
-
47
- # Step 3: Get live quotes from Dhan
48
- quote_resp = DhanHQ::Models::MarketFeed.ltp("NSE_EQ" => [security_id.to_i])
49
- ltp = quote_resp[:data]["NSE_EQ"][security_id.to_s][:last_price].to_f
50
-
51
- # Step 4: Place order via Dhan
52
- order = DhanHQ::Models::Order.place(
53
- security_id: security_id,
54
- exchange_segment: "NSE_EQ",
55
- transaction_type: "BUY",
56
- quantity: 1,
57
- order_type: "LIMIT",
58
- product_type: "CNC",
59
- price: ltp,
60
- validity: "DAY"
61
- )
62
- ```