DhanHQ 2.5.0 → 2.6.1

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 (76) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +9 -1
  3. data/CHANGELOG.md +78 -6
  4. data/GUIDE.md +57 -39
  5. data/README.md +24 -23
  6. data/docs/API_DOCS_GAPS.md +128 -0
  7. data/docs/API_VERIFICATION.md +10 -11
  8. data/docs/ARCHIVE_README.md +16 -16
  9. data/docs/AUTHENTICATION.md +1 -1
  10. data/docs/CONSTANTS_REFERENCE.md +477 -0
  11. data/docs/DATA_API_PARAMETERS.md +7 -7
  12. data/docs/{rails_websocket_integration.md → RAILS_WEBSOCKET_INTEGRATION.md} +10 -10
  13. data/docs/{standalone_ruby_websocket_integration.md → STANDALONE_RUBY_WEBSOCKET_INTEGRATION.md} +32 -32
  14. data/docs/{technical_analysis.md → TECHNICAL_ANALYSIS.md} +3 -3
  15. data/docs/TESTING_GUIDE.md +84 -82
  16. data/docs/{websocket_integration.md → WEBSOCKET_INTEGRATION.md} +19 -19
  17. data/docs/WEBSOCKET_PROTOCOL.md +2 -2
  18. data/lib/DhanHQ/constants.rb +456 -151
  19. data/lib/DhanHQ/contracts/alert_order_contract.rb +37 -10
  20. data/lib/DhanHQ/contracts/base_contract.rb +22 -0
  21. data/lib/DhanHQ/contracts/edis_contract.rb +25 -0
  22. data/lib/DhanHQ/contracts/margin_calculator_contract.rb +27 -4
  23. data/lib/DhanHQ/contracts/modify_order_contract.rb +65 -12
  24. data/lib/DhanHQ/contracts/multi_scrip_margin_calc_request_contract.rb +23 -0
  25. data/lib/DhanHQ/contracts/order_contract.rb +171 -39
  26. data/lib/DhanHQ/contracts/place_order_contract.rb +14 -141
  27. data/lib/DhanHQ/contracts/pnl_based_exit_contract.rb +20 -0
  28. data/lib/DhanHQ/contracts/position_conversion_contract.rb +15 -3
  29. data/lib/DhanHQ/contracts/slice_order_contract.rb +10 -1
  30. data/lib/DhanHQ/contracts/user_ip_contract.rb +14 -0
  31. data/lib/DhanHQ/core/base_model.rb +13 -4
  32. data/lib/DhanHQ/helpers/response_helper.rb +2 -2
  33. data/lib/DhanHQ/helpers/validation_helper.rb +1 -1
  34. data/lib/DhanHQ/models/alert_order.rb +7 -11
  35. data/lib/DhanHQ/models/concerns/api_response_handler.rb +46 -0
  36. data/lib/DhanHQ/models/edis.rb +0 -9
  37. data/lib/DhanHQ/models/expired_options_data.rb +6 -12
  38. data/lib/DhanHQ/models/forever_order.rb +8 -5
  39. data/lib/DhanHQ/models/historical_data.rb +0 -8
  40. data/lib/DhanHQ/models/instrument.rb +1 -7
  41. data/lib/DhanHQ/models/instrument_helpers.rb +4 -4
  42. data/lib/DhanHQ/models/kill_switch.rb +1 -11
  43. data/lib/DhanHQ/models/margin.rb +2 -2
  44. data/lib/DhanHQ/models/order.rb +107 -126
  45. data/lib/DhanHQ/models/order_update.rb +7 -13
  46. data/lib/DhanHQ/models/pnl_exit.rb +1 -9
  47. data/lib/DhanHQ/models/position.rb +1 -1
  48. data/lib/DhanHQ/models/postback.rb +4 -13
  49. data/lib/DhanHQ/models/profile.rb +0 -10
  50. data/lib/DhanHQ/models/super_order.rb +13 -3
  51. data/lib/DhanHQ/models/trade.rb +11 -23
  52. data/lib/DhanHQ/resources/ip_setup.rb +16 -5
  53. data/lib/DhanHQ/resources/kill_switch.rb +9 -7
  54. data/lib/DhanHQ/resources/orders.rb +41 -41
  55. data/lib/DhanHQ/version.rb +1 -1
  56. data/lib/DhanHQ/ws/cmd_bus.rb +1 -1
  57. data/lib/DhanHQ/ws/orders/client.rb +6 -6
  58. data/lib/DhanHQ/ws/singleton_lock.rb +2 -1
  59. data/lib/dhanhq/analysis/options_buying_advisor.rb +2 -2
  60. data/lib/rubocop/cop/dhanhq/use_constants.rb +171 -0
  61. metadata +20 -23
  62. data/TODO-1.md +0 -14
  63. data/TODO.md +0 -127
  64. data/app/services/live/order_update_guard_support.rb +0 -75
  65. data/app/services/live/order_update_hub.rb +0 -76
  66. data/app/services/live/order_update_persistence_support.rb +0 -68
  67. data/docs/PR_2.2.0.md +0 -48
  68. data/examples/comprehensive_websocket_examples.rb +0 -148
  69. data/examples/instrument_finder_test.rb +0 -195
  70. data/examples/live_order_updates.rb +0 -118
  71. data/examples/market_depth_example.rb +0 -144
  72. data/examples/market_feed_example.rb +0 -81
  73. data/examples/order_update_example.rb +0 -105
  74. data/examples/trading_fields_example.rb +0 -215
  75. /data/docs/{live_order_updates.md → LIVE_ORDER_UPDATES.md} +0 -0
  76. /data/docs/{rails_integration.md → RAILS_INTEGRATION.md} +0 -0
@@ -1,75 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Live
4
- # Guard-related helpers for interpreting order update payloads.
5
- module OrderUpdateGuardSupport
6
- module_function
7
-
8
- SEGMENT_MAP = {
9
- %w[NSE E] => "NSE_EQ",
10
- %w[BSE E] => "BSE_EQ",
11
- %w[NSE D] => "NSE_FNO",
12
- %w[BSE D] => "BSE_FNO",
13
- %w[NSE C] => "NSE_CURRENCY",
14
- %w[BSE C] => "BSE_CURRENCY",
15
- %w[MCX M] => "MCX_COMM"
16
- }.freeze
17
-
18
- DEFAULT_SL_PCT = 0.15
19
- DEFAULT_TP_PCT = 0.30
20
- DEFAULT_TRAIL_PCT = 0.01
21
-
22
- def map_segment(exchange, segment)
23
- key = [exchange.to_s.upcase, segment.to_s.upcase]
24
- SEGMENT_MAP.fetch(key, "NSE_EQ")
25
- end
26
-
27
- def position_guard_payload(segment, security_id, order_data)
28
- guard_base_payload(segment, security_id, order_data)
29
- .merge(guard_percentage_payload(segment, security_id))
30
- end
31
-
32
- def guard_base_payload(segment, security_id, order_data)
33
- {
34
- pos_id: nil,
35
- exchange_segment: segment,
36
- security_id: security_id,
37
- entry: average_entry(order_data),
38
- qty: order_data[:TradedQty].to_i,
39
- placed_as: placed_as(order_data),
40
- super_order_id: order_data[:OrderNo].to_s
41
- }
42
- end
43
-
44
- def guard_percentage_payload(segment, security_id)
45
- {
46
- sl_pct: default_sl_pct(segment, security_id),
47
- tp_pct: default_tp_pct(segment, security_id),
48
- trail_pct: default_trail_pct(segment, security_id)
49
- }
50
- end
51
-
52
- def placed_as(order_data)
53
- order_data[:Remarks].to_s.match?(/Super Order/i) ? "super" : "plain"
54
- end
55
-
56
- def average_entry(order_data)
57
- first_price = [order_data[:AvgTradedPrice], order_data[:TradedPrice], order_data[:Price]]
58
- .compact
59
- .first
60
- first_price.to_f
61
- end
62
-
63
- def default_sl_pct(_segment, _security_id)
64
- DEFAULT_SL_PCT
65
- end
66
-
67
- def default_tp_pct(_segment, _security_id)
68
- DEFAULT_TP_PCT
69
- end
70
-
71
- def default_trail_pct(_segment, _security_id)
72
- DEFAULT_TRAIL_PCT
73
- end
74
- end
75
- end
@@ -1,76 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require "singleton"
4
- require_relative "order_update_guard_support"
5
- require_relative "order_update_persistence_support"
6
-
7
- module Live
8
- # OrderUpdateHub listens for order updates over WebSocket and wires them into
9
- # local persistence plus downstream execution helpers.
10
- class OrderUpdateHub
11
- include Singleton
12
-
13
- def start!
14
- return self if @started
15
-
16
- @client = DhanHQ::WS::Orders::Client.new.start
17
- @client.on(:update) { |msg| handle(msg) }
18
- @started = true
19
- self
20
- end
21
-
22
- def stop!
23
- @client&.stop
24
- @started = false
25
- end
26
-
27
- private
28
-
29
- def handle(message)
30
- return unless order_alert?(message)
31
-
32
- order_data = message[:Data] || {}
33
- upsert_local_order(order_data)
34
- handle_entry_leg(order_data)
35
- rescue StandardError => e
36
- Rails.logger.error("[OrderUpdateHub] #{e.class}: #{e.message}")
37
- end
38
-
39
- def order_alert?(message)
40
- message&.dig(:Type) == "order_alert"
41
- end
42
-
43
- def handle_entry_leg(order_data)
44
- return unless entry_leg_traded?(order_data)
45
-
46
- segment = OrderUpdateGuardSupport.map_segment(order_data[:Exchange], order_data[:Segment])
47
- security_id = order_data[:SecurityId].to_s
48
- Live::WsHub.instance.subscribe(seg: segment, sid: security_id) if defined?(Live::WsHub)
49
-
50
- register_position_guard(segment, security_id, order_data)
51
- end
52
-
53
- def register_position_guard(segment, security_id, order_data)
54
- return unless defined?(Execution::PositionGuard)
55
-
56
- payload = OrderUpdateGuardSupport.position_guard_payload(segment, security_id, order_data)
57
- Execution::PositionGuard.instance.register(**payload)
58
- end
59
-
60
- def upsert_local_order(order_data)
61
- order_number = order_data[:OrderNo].to_s
62
- record = BrokerOrder.find_or_initialize_by(order_no: order_number)
63
- attributes = OrderUpdatePersistenceSupport.local_order_attributes(order_data)
64
- record.assign_attributes(attributes)
65
- record.save!
66
- rescue NameError
67
- nil
68
- end
69
-
70
- def entry_leg_traded?(order_data)
71
- order_data[:LegNo].to_i == 1 &&
72
- order_data[:Status].to_s.upcase == "TRADED" &&
73
- order_data[:TradedQty].to_i.positive?
74
- end
75
- end
76
- end
@@ -1,68 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require "bigdecimal"
4
-
5
- module Live
6
- # Persistence helper routines for order update payloads.
7
- module OrderUpdatePersistenceSupport
8
- module_function
9
-
10
- def local_order_attributes(order_data)
11
- identity_attributes(order_data)
12
- .merge(quantity_attributes(order_data))
13
- .merge(price_attributes(order_data))
14
- .merge(timestamp_attributes(order_data))
15
- end
16
-
17
- def identity_attributes(order_data)
18
- {
19
- exch_order_no: order_data[:ExchOrderNo].to_s,
20
- status: order_data[:Status],
21
- product: order_data[:ProductName] || order_data[:Product],
22
- txn_type: order_data[:TxnType],
23
- order_type: order_data[:OrderType]
24
- }
25
- end
26
-
27
- def quantity_attributes(order_data)
28
- {
29
- validity: order_data[:Validity],
30
- exchange: order_data[:Exchange],
31
- segment: order_data[:Segment],
32
- security_id: order_data[:SecurityId].to_s,
33
- quantity: order_data[:Quantity].to_i,
34
- traded_qty: order_data[:TradedQty].to_i
35
- }
36
- end
37
-
38
- def price_attributes(order_data)
39
- {
40
- price: decimal(order_data[:Price]),
41
- trigger_price: decimal(order_data[:TriggerPrice]),
42
- traded_price: decimal(order_data[:TradedPrice]),
43
- avg_traded_price: decimal(order_data[:AvgTradedPrice])
44
- }
45
- end
46
-
47
- def timestamp_attributes(order_data)
48
- {
49
- last_update_at: parse_timestamp(order_data[:LastUpdatedTime]),
50
- raw_payload: order_data
51
- }
52
- end
53
-
54
- def decimal(value)
55
- return BigDecimal(0) if value.nil?
56
-
57
- BigDecimal(value.to_s)
58
- end
59
-
60
- def parse_timestamp(timestamp)
61
- return nil if timestamp.nil? || timestamp == ""
62
-
63
- Time.zone.parse(timestamp)
64
- rescue StandardError
65
- nil
66
- end
67
- end
68
- end
data/docs/PR_2.2.0.md DELETED
@@ -1,48 +0,0 @@
1
- # PR: Dynamic access token resolution, auto-expiry detection, retry-on-401
2
-
3
- ## Summary
4
-
5
- Adds production-grade auth handling to `dhanhq-client`:
6
-
7
- 1. **Dynamic access token resolution** — Token can come from a callable (`access_token_provider`) at request time.
8
- 2. **Auto-expiry detection** — API error code 807 (token expired) raises `DhanHQ::TokenExpiredError`.
9
- 3. **Retry-on-401 with token re-fetch** — On 401/token-expired, if `access_token_provider` is set, the client retries once; the next request calls the provider again for a fresh token.
10
- 4. **Optional `on_token_expired` hook** — Invoked before retry when auth failure triggers a retry (e.g. for logging or refreshing token in your store).
11
-
12
- ## Changes
13
-
14
- - **Configuration**: `access_token_provider`, `on_token_expired`, `resolved_access_token`.
15
- - **Errors**: `DhanHQ::AuthenticationError` (local token resolution failure), `DhanHQ::TokenExpiredError` (API 807).
16
- - **Client**: Retry-on-401 for `InvalidAuthenticationError`, `InvalidTokenError`, `TokenExpiredError`, `AuthenticationFailedError` when provider is set (single retry).
17
- - **REST + WebSocket**: All token usage goes through `config.resolved_access_token` (no memoization).
18
-
19
- ## Backward compatibility
20
-
21
- - **Non-breaking**. Existing `config.access_token = "static-token"` still works. `access_token_provider` is optional. Safe as a **minor** version bump (2.2.0).
22
-
23
- ## Testing
24
-
25
- - `spec/dhan_hq/configuration_spec.rb` — `#resolved_access_token` (provider, fallback, nil/empty).
26
- - `spec/dhan_hq/client_spec.rb` — WebMock: 401, 403, 807, retry-on-401 success, retry then raise, `on_token_expired` hook.
27
- - `spec/dhan_hq/helpers/response_helper_spec.rb` — 807 → TokenExpiredError.
28
-
29
- ## Changelog
30
-
31
- See [CHANGELOG.md](../CHANGELOG.md) — section **## [2.2.0] - 2026-01-31**.
32
-
33
- ## README & docs
34
-
35
- - **README.md**: New subsection “Dynamic access token (optional)” under Configuration (access_token_provider, on_token_expired, retry-on-401, AuthenticationError / TokenExpiredError).
36
- - **GUIDE.md**: Short “Dynamic access token” note and link to docs/AUTHENTICATION.md.
37
- - **docs/AUTHENTICATION.md**: New doc for static vs dynamic token, retry-on-401, and auth-related errors.
38
- - **docs/TESTING_GUIDE.md**: Optional access_token_provider / on_token_expired in config examples; verify “Token provider” in console.
39
- - **docs/rails_integration.md**: New “Dynamic access token (optional)” with Rails initializer example.
40
- - **docs/websocket_integration.md**, **docs/live_order_updates.md**: One-line pointer to docs/AUTHENTICATION.md for dynamic token.
41
-
42
- ## Checklist
43
-
44
- - [x] All specs pass
45
- - [x] No breaking changes
46
- - [x] CHANGELOG updated
47
- - [x] Version bumped to 2.2.0
48
- - [x] README and docs updated
@@ -1,148 +0,0 @@
1
- #!/usr/bin/env ruby
2
- # frozen_string_literal: true
3
-
4
- # Comprehensive WebSocket Examples
5
- # This script demonstrates all three DhanHQ WebSocket types:
6
- # 1. Market Feed WebSocket - Real-time market data
7
- # 2. Order Update WebSocket - Real-time order updates
8
- # 3. Market Depth WebSocket - Real-time market depth
9
- # NOTE: Uses sequential connections to avoid rate limiting
10
-
11
- require "dhan_hq"
12
-
13
- # Configure DhanHQ
14
- DhanHQ.configure do |config|
15
- config.client_id = ENV["DHAN_CLIENT_ID"] || "your_client_id"
16
- config.access_token = ENV["DHAN_ACCESS_TOKEN"] || "your_access_token"
17
- config.ws_user_type = ENV["DHAN_WS_USER_TYPE"] || "SELF"
18
- end
19
-
20
- puts "DhanHQ Comprehensive WebSocket Examples"
21
- puts "======================================="
22
- puts "Demonstrates all three WebSocket types:"
23
- puts "1. Market Feed - Real-time market data for indices"
24
- puts "2. Order Update - Real-time order status updates"
25
- puts "3. Market Depth - Real-time bid/ask levels"
26
- puts ""
27
- puts "NOTE: Uses sequential connections to avoid rate limiting (429 errors)"
28
- puts "Dhan allows up to 5 WebSocket connections per user"
29
- puts ""
30
-
31
- # Example 1: Market Feed WebSocket
32
- puts "1. Market Feed WebSocket Example"
33
- puts "================================"
34
-
35
- puts "Creating Market Feed WebSocket connection..."
36
- market_client = DhanHQ::WS.connect(mode: :ticker) do |tick|
37
- timestamp = tick[:ts] ? Time.at(tick[:ts]) : Time.now
38
- puts "Market Data: #{tick[:segment]}:#{tick[:security_id]} = #{tick[:ltp]} at #{timestamp}"
39
- end
40
-
41
- # Subscribe to major Indian indices
42
- puts "\nSubscribing to major Indian indices:"
43
- puts "- Security ID 13: NIFTY (Nifty 50)"
44
- puts "- Security ID 25: BANKNIFTY (Nifty Bank)"
45
- puts "- Security ID 29: NIFTYIT (Nifty IT)"
46
- puts "- Security ID 51: SENSEX (Sensex)"
47
-
48
- market_client.subscribe_one(segment: "IDX_I", security_id: "13") # NIFTY
49
- market_client.subscribe_one(segment: "IDX_I", security_id: "25") # BANKNIFTY
50
- market_client.subscribe_one(segment: "IDX_I", security_id: "29") # NIFTYIT
51
- market_client.subscribe_one(segment: "IDX_I", security_id: "51") # SENSEX
52
-
53
- puts "\nMarket Feed WebSocket connected successfully!"
54
- puts "Waiting 10 seconds to receive market data..."
55
- sleep(10)
56
-
57
- puts "Stopping Market Feed WebSocket to prevent rate limiting..."
58
- market_client.stop
59
- sleep(2)
60
-
61
- # Example 2: Order Update WebSocket
62
- puts "\n2. Order Update WebSocket Example"
63
- puts "=================================="
64
-
65
- puts "Creating Order Update WebSocket connection..."
66
- orders_client = DhanHQ::WS::Orders.connect do |order_update|
67
- puts "Order Update: #{order_update.order_no} - #{order_update.status}"
68
- puts " Symbol: #{order_update.symbol}"
69
- puts " Quantity: #{order_update.quantity}"
70
- puts " Traded Qty: #{order_update.traded_qty}"
71
- puts " Price: #{order_update.price}"
72
- puts " Execution: #{order_update.execution_percentage}%"
73
- puts " ---"
74
- end
75
-
76
- # Add event handlers
77
- orders_client.on(:update) { |order| puts "📝 Order Updated: #{order.order_no}" }
78
- orders_client.on(:execution) { |exec| puts "✅ Execution: #{exec[:new_traded_qty]} shares" }
79
- orders_client.on(:order_rejected) { |order| puts "❌ Order Rejected: #{order.order_no}" }
80
-
81
- puts "\nOrder Update WebSocket connected successfully!"
82
- puts "Waiting 10 seconds to receive order updates..."
83
- sleep(10)
84
-
85
- puts "Stopping Order Update WebSocket to prevent rate limiting..."
86
- orders_client.stop
87
- sleep(2)
88
-
89
- # Example 3: Market Depth WebSocket
90
- puts "\n3. Market Depth WebSocket Example"
91
- puts "=================================="
92
-
93
- puts "Creating Market Depth WebSocket connection..."
94
-
95
- # Find instruments using the new .find method (now uses underlying_symbol for equity)
96
- reliance_instrument = DhanHQ::Models::Instrument.find("NSE_EQ", "RELIANCE")
97
- tcs_instrument = DhanHQ::Models::Instrument.find("NSE_EQ", "TCS")
98
-
99
- # Define symbols with correct exchange segments and security IDs
100
- symbols = []
101
- if reliance_instrument
102
- symbols << { symbol: "RELIANCE", exchange_segment: reliance_instrument.exchange_segment,
103
- security_id: reliance_instrument.security_id }
104
- puts "✅ Found RELIANCE: #{reliance_instrument.symbol_name} (#{reliance_instrument.exchange_segment}:#{reliance_instrument.security_id})"
105
- end
106
-
107
- if tcs_instrument
108
- symbols << { symbol: "TCS", exchange_segment: tcs_instrument.exchange_segment,
109
- security_id: tcs_instrument.security_id }
110
- puts "✅ Found TCS: #{tcs_instrument.symbol_name} (#{tcs_instrument.exchange_segment}:#{tcs_instrument.security_id})"
111
- end
112
-
113
- depth_client = DhanHQ::WS::MarketDepth.connect(symbols: symbols) do |depth_data|
114
- puts "Market Depth: #{depth_data[:symbol]}"
115
- puts " Best Bid: #{depth_data[:best_bid]}"
116
- puts " Best Ask: #{depth_data[:best_ask]}"
117
- puts " Spread: #{depth_data[:spread]}"
118
- puts " Bid Levels: #{depth_data[:bids].size}"
119
- puts " Ask Levels: #{depth_data[:asks].size}"
120
- puts " ---"
121
- end
122
-
123
- puts "\nMarket Depth WebSocket connected successfully!"
124
- puts "Waiting 10 seconds to receive market depth data..."
125
- sleep(10)
126
-
127
- puts "Stopping Market Depth WebSocket to prevent rate limiting..."
128
- depth_client.stop
129
- sleep(2)
130
-
131
- # Final cleanup
132
- puts "\n4. Final Cleanup"
133
- puts "================"
134
-
135
- puts "Ensuring all WebSocket connections are closed..."
136
- DhanHQ::WS.disconnect_all_local!
137
-
138
- puts "\nAll WebSocket connections closed."
139
- puts "Comprehensive example completed!"
140
- puts ""
141
- puts "Summary:"
142
- puts "- Successfully demonstrated all three WebSocket types:"
143
- puts " * Market Feed: Real-time index data (NIFTY, BANKNIFTY, NIFTYIT, SENSEX)"
144
- puts " * Order Update: Real-time order status tracking"
145
- puts " * Market Depth: Real-time bid/ask levels (RELIANCE, TCS) - dynamically resolved"
146
- puts "- Used sequential connections to avoid rate limiting (429 errors)"
147
- puts "- Proper connection cleanup prevents resource leaks"
148
- puts "- No multiple connection issues!"
@@ -1,195 +0,0 @@
1
- #!/usr/bin/env ruby
2
- # frozen_string_literal: true
3
-
4
- # Test script for the new DhanHQ::Models::Instrument.find method
5
- # This script demonstrates how to use the new .find and .find_anywhere methods
6
-
7
- require "dhan_hq"
8
-
9
- # Configure DhanHQ
10
- DhanHQ.configure do |config|
11
- config.client_id = ENV["DHAN_CLIENT_ID"] || "your_client_id"
12
- config.access_token = ENV["DHAN_ACCESS_TOKEN"] || "your_access_token"
13
- config.ws_user_type = ENV["DHAN_WS_USER_TYPE"] || "SELF"
14
- end
15
-
16
- puts "DhanHQ Instrument Finder Test"
17
- puts "============================="
18
- puts "Testing the new .find and .find_anywhere methods"
19
- puts ""
20
-
21
- # Test 1: Find specific instruments in known segments
22
- puts "1. Finding instruments in specific segments:"
23
- puts "-" * 40
24
-
25
- # Find RELIANCE in NSE_EQ (now uses underlying_symbol for equity)
26
- reliance = DhanHQ::Models::Instrument.find("NSE_EQ", "RELIANCE")
27
- if reliance
28
- puts "✅ RELIANCE:"
29
- puts " Symbol Name: #{reliance.symbol_name}"
30
- puts " Underlying Symbol: #{reliance.underlying_symbol}"
31
- puts " Security ID: #{reliance.security_id}"
32
- puts " Display Name: #{reliance.display_name}"
33
- puts " Exchange Segment: #{reliance.exchange_segment}"
34
- else
35
- puts "❌ RELIANCE not found"
36
- end
37
-
38
- puts
39
-
40
- # Find TCS in NSE_EQ (now uses underlying_symbol for equity)
41
- tcs = DhanHQ::Models::Instrument.find("NSE_EQ", "TCS")
42
- if tcs
43
- puts "✅ TCS:"
44
- puts " Symbol Name: #{tcs.symbol_name}"
45
- puts " Underlying Symbol: #{tcs.underlying_symbol}"
46
- puts " Security ID: #{tcs.security_id}"
47
- puts " Display Name: #{tcs.display_name}"
48
- puts " Exchange Segment: #{tcs.exchange_segment}"
49
- else
50
- puts "❌ TCS not found"
51
- end
52
-
53
- puts
54
-
55
- # Find NIFTY in IDX_I
56
- nifty = DhanHQ::Models::Instrument.find("IDX_I", "NIFTY")
57
- if nifty
58
- puts "✅ NIFTY:"
59
- puts " Security ID: #{nifty.security_id}"
60
- puts " Display Name: #{nifty.display_name}"
61
- puts " Exchange Segment: #{nifty.exchange_segment}"
62
- else
63
- puts "❌ NIFTY not found"
64
- end
65
-
66
- puts
67
-
68
- # Find BANKNIFTY in IDX_I
69
- banknifty = DhanHQ::Models::Instrument.find("IDX_I", "BANKNIFTY")
70
- if banknifty
71
- puts "✅ BANKNIFTY:"
72
- puts " Security ID: #{banknifty.security_id}"
73
- puts " Display Name: #{banknifty.display_name}"
74
- puts " Exchange Segment: #{banknifty.exchange_segment}"
75
- else
76
- puts "❌ BANKNIFTY not found"
77
- end
78
-
79
- puts
80
-
81
- # Test 2: Find instruments anywhere (cross-segment search)
82
- puts "2. Finding instruments anywhere (cross-segment search):"
83
- puts "-" * 50
84
-
85
- # Find RELIANCE anywhere (now uses underlying_symbol for equity)
86
- reliance_anywhere = DhanHQ::Models::Instrument.find_anywhere("RELIANCE", exact_match: true)
87
- if reliance_anywhere
88
- puts "✅ RELIANCE found anywhere:"
89
- puts " Symbol Name: #{reliance_anywhere.symbol_name}"
90
- puts " Underlying Symbol: #{reliance_anywhere.underlying_symbol}"
91
- puts " Security ID: #{reliance_anywhere.security_id}"
92
- puts " Display Name: #{reliance_anywhere.display_name}"
93
- puts " Exchange Segment: #{reliance_anywhere.exchange_segment}"
94
- else
95
- puts "❌ RELIANCE not found anywhere"
96
- end
97
-
98
- puts
99
-
100
- # Find TCS anywhere (now uses underlying_symbol for equity)
101
- tcs_anywhere = DhanHQ::Models::Instrument.find_anywhere("TCS", exact_match: true)
102
- if tcs_anywhere
103
- puts "✅ TCS found anywhere:"
104
- puts " Symbol Name: #{tcs_anywhere.symbol_name}"
105
- puts " Underlying Symbol: #{tcs_anywhere.underlying_symbol}"
106
- puts " Security ID: #{tcs_anywhere.security_id}"
107
- puts " Display Name: #{tcs_anywhere.display_name}"
108
- puts " Exchange Segment: #{tcs_anywhere.exchange_segment}"
109
- else
110
- puts "❌ TCS not found anywhere"
111
- end
112
-
113
- puts
114
-
115
- # Find NIFTY anywhere
116
- nifty_anywhere = DhanHQ::Models::Instrument.find_anywhere("NIFTY", exact_match: true)
117
- if nifty_anywhere
118
- puts "✅ NIFTY found anywhere:"
119
- puts " Security ID: #{nifty_anywhere.security_id}"
120
- puts " Display Name: #{nifty_anywhere.display_name}"
121
- puts " Exchange Segment: #{nifty_anywhere.exchange_segment}"
122
- else
123
- puts "❌ NIFTY not found anywhere"
124
- end
125
-
126
- puts
127
-
128
- # Test 3: Advanced search options
129
- puts "3. Testing advanced search options:"
130
- puts "-" * 35
131
-
132
- # Test partial match
133
- reliance_partial = DhanHQ::Models::Instrument.find("NSE_EQ", "RELIANCE")
134
- if reliance_partial
135
- puts "✅ RELIANCE (partial match):"
136
- puts " Symbol: #{reliance_partial.symbol_name}"
137
- puts " Security ID: #{reliance_partial.security_id}"
138
- else
139
- puts "❌ RELIANCE (partial match) not found"
140
- end
141
-
142
- puts
143
-
144
- # Test case insensitive search
145
- reliance_case = DhanHQ::Models::Instrument.find("NSE_EQ", "reliance industries ltd", case_sensitive: false)
146
- if reliance_case
147
- puts "✅ RELIANCE (case insensitive):"
148
- puts " Symbol: #{reliance_case.symbol_name}"
149
- puts " Security ID: #{reliance_case.security_id}"
150
- else
151
- puts "❌ RELIANCE (case insensitive) not found"
152
- end
153
-
154
- puts
155
-
156
- # Test 4: Practical usage for WebSocket subscriptions
157
- puts "4. Practical usage for WebSocket subscriptions:"
158
- puts "-" * 45
159
-
160
- # Find instruments for Market Depth WebSocket
161
- puts "Finding instruments for Market Depth WebSocket:"
162
-
163
- market_depth_symbols = []
164
- symbols_to_find = [
165
- { segment: "NSE_EQ", symbol: "RELIANCE", name: "RELIANCE" },
166
- { segment: "NSE_EQ", symbol: "TCS", name: "TCS" }
167
- ]
168
-
169
- symbols_to_find.each do |search|
170
- instrument = DhanHQ::Models::Instrument.find(search[:segment], search[:symbol])
171
- if instrument
172
- market_depth_symbols << {
173
- symbol: search[:name],
174
- exchange_segment: instrument.exchange_segment,
175
- security_id: instrument.security_id
176
- }
177
- puts "✅ #{search[:name]}: #{instrument.exchange_segment}:#{instrument.security_id}"
178
- else
179
- puts "❌ #{search[:name]}: Not found"
180
- end
181
- end
182
-
183
- puts
184
- puts "Market Depth WebSocket symbols array:"
185
- puts market_depth_symbols.inspect
186
-
187
- puts
188
- puts "Test completed!"
189
- puts "==============="
190
- puts "The new .find and .find_anywhere methods make it easy to:"
191
- puts "- Find instruments by exchange segment and symbol"
192
- puts "- Search across multiple segments"
193
- puts "- Use exact or partial matching"
194
- puts "- Perform case-sensitive or case-insensitive searches"
195
- puts "- Dynamically resolve symbols for WebSocket subscriptions"