DhanHQ 2.4.0 → 2.6.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 (82) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +9 -1
  3. data/CHANGELOG.md +103 -7
  4. data/GUIDE.md +57 -39
  5. data/README.md +198 -755
  6. data/docs/API_DOCS_GAPS.md +128 -0
  7. data/docs/API_VERIFICATION.md +10 -11
  8. data/{README1.md → docs/ARCHIVE_README.md} +16 -16
  9. data/docs/AUTHENTICATION.md +72 -10
  10. data/docs/CONFIGURATION.md +109 -0
  11. data/docs/CONSTANTS_REFERENCE.md +477 -0
  12. data/docs/DATA_API_PARAMETERS.md +7 -7
  13. data/docs/{rails_websocket_integration.md → RAILS_WEBSOCKET_INTEGRATION.md} +10 -10
  14. data/docs/{standalone_ruby_websocket_integration.md → STANDALONE_RUBY_WEBSOCKET_INTEGRATION.md} +32 -32
  15. data/docs/SUPER_ORDERS.md +284 -0
  16. data/docs/{technical_analysis.md → TECHNICAL_ANALYSIS.md} +3 -3
  17. data/docs/TESTING_GUIDE.md +84 -82
  18. data/docs/TROUBLESHOOTING.md +117 -0
  19. data/docs/{websocket_integration.md → WEBSOCKET_INTEGRATION.md} +19 -19
  20. data/docs/WEBSOCKET_PROTOCOL.md +154 -0
  21. data/lib/DhanHQ/constants.rb +456 -151
  22. data/lib/DhanHQ/contracts/alert_order_contract.rb +37 -10
  23. data/lib/DhanHQ/contracts/base_contract.rb +22 -0
  24. data/lib/DhanHQ/contracts/edis_contract.rb +25 -0
  25. data/lib/DhanHQ/contracts/margin_calculator_contract.rb +27 -4
  26. data/lib/DhanHQ/contracts/modify_order_contract.rb +65 -12
  27. data/lib/DhanHQ/contracts/multi_scrip_margin_calc_request_contract.rb +23 -0
  28. data/lib/DhanHQ/contracts/order_contract.rb +171 -39
  29. data/lib/DhanHQ/contracts/place_order_contract.rb +14 -141
  30. data/lib/DhanHQ/contracts/pnl_based_exit_contract.rb +20 -0
  31. data/lib/DhanHQ/contracts/position_conversion_contract.rb +15 -3
  32. data/lib/DhanHQ/contracts/slice_order_contract.rb +10 -1
  33. data/lib/DhanHQ/contracts/user_ip_contract.rb +14 -0
  34. data/lib/DhanHQ/core/base_model.rb +13 -4
  35. data/lib/DhanHQ/helpers/response_helper.rb +2 -2
  36. data/lib/DhanHQ/helpers/validation_helper.rb +1 -1
  37. data/lib/DhanHQ/models/alert_order.rb +29 -11
  38. data/lib/DhanHQ/models/concerns/api_response_handler.rb +46 -0
  39. data/lib/DhanHQ/models/edis.rb +101 -0
  40. data/lib/DhanHQ/models/expired_options_data.rb +6 -12
  41. data/lib/DhanHQ/models/forever_order.rb +8 -5
  42. data/lib/DhanHQ/models/historical_data.rb +0 -8
  43. data/lib/DhanHQ/models/instrument.rb +1 -7
  44. data/lib/DhanHQ/models/instrument_helpers.rb +4 -4
  45. data/lib/DhanHQ/models/kill_switch.rb +23 -11
  46. data/lib/DhanHQ/models/margin.rb +51 -2
  47. data/lib/DhanHQ/models/order.rb +107 -126
  48. data/lib/DhanHQ/models/order_update.rb +7 -13
  49. data/lib/DhanHQ/models/pnl_exit.rb +122 -0
  50. data/lib/DhanHQ/models/position.rb +23 -1
  51. data/lib/DhanHQ/models/postback.rb +114 -0
  52. data/lib/DhanHQ/models/profile.rb +0 -10
  53. data/lib/DhanHQ/models/super_order.rb +13 -3
  54. data/lib/DhanHQ/models/trade.rb +11 -23
  55. data/lib/DhanHQ/resources/ip_setup.rb +16 -5
  56. data/lib/DhanHQ/resources/kill_switch.rb +17 -7
  57. data/lib/DhanHQ/resources/margin_calculator.rb +9 -0
  58. data/lib/DhanHQ/resources/orders.rb +41 -41
  59. data/lib/DhanHQ/resources/pnl_exit.rb +37 -0
  60. data/lib/DhanHQ/resources/positions.rb +8 -0
  61. data/lib/DhanHQ/version.rb +1 -1
  62. data/lib/DhanHQ/ws/cmd_bus.rb +1 -1
  63. data/lib/DhanHQ/ws/orders/client.rb +6 -6
  64. data/lib/DhanHQ/ws/singleton_lock.rb +2 -1
  65. data/lib/dhanhq/analysis/options_buying_advisor.rb +2 -2
  66. data/lib/rubocop/cop/dhanhq/use_constants.rb +171 -0
  67. metadata +29 -24
  68. data/TODO-1.md +0 -14
  69. data/TODO.md +0 -127
  70. data/app/services/live/order_update_guard_support.rb +0 -75
  71. data/app/services/live/order_update_hub.rb +0 -76
  72. data/app/services/live/order_update_persistence_support.rb +0 -68
  73. data/docs/PR_2.2.0.md +0 -48
  74. data/examples/comprehensive_websocket_examples.rb +0 -148
  75. data/examples/instrument_finder_test.rb +0 -195
  76. data/examples/live_order_updates.rb +0 -118
  77. data/examples/market_depth_example.rb +0 -144
  78. data/examples/market_feed_example.rb +0 -81
  79. data/examples/order_update_example.rb +0 -105
  80. data/examples/trading_fields_example.rb +0 -215
  81. /data/docs/{live_order_updates.md → LIVE_ORDER_UPDATES.md} +0 -0
  82. /data/docs/{rails_integration.md → RAILS_INTEGRATION.md} +0 -0
@@ -95,10 +95,10 @@ market_client = DhanHQ::WS.connect(mode: :ticker) do |tick|
95
95
  end
96
96
 
97
97
  # Subscribe to NIFTY (Security ID: 13, Segment: IDX_I)
98
- market_client.subscribe_one(segment: "IDX_I", security_id: "13")
98
+ market_client.subscribe_one(segment: DhanHQ::Constants::ExchangeSegment::IDX_I, security_id: "13")
99
99
 
100
100
  # Subscribe to BANKNIFTY (Security ID: 25, Segment: IDX_I)
101
- market_client.subscribe_one(segment: "IDX_I", security_id: "25")
101
+ market_client.subscribe_one(segment: DhanHQ::Constants::ExchangeSegment::IDX_I, security_id: "25")
102
102
 
103
103
  # Wait for data (in console, you can continue working)
104
104
  sleep(10)
@@ -117,7 +117,7 @@ ohlc_client = DhanHQ::WS.connect(mode: :ohlc) do |data|
117
117
  end
118
118
 
119
119
  # Subscribe to NIFTY
120
- ohlc_client.subscribe_one(segment: "IDX_I", security_id: "13")
120
+ ohlc_client.subscribe_one(segment: DhanHQ::Constants::ExchangeSegment::IDX_I, security_id: "13")
121
121
 
122
122
  sleep(10)
123
123
  ohlc_client.stop
@@ -135,7 +135,7 @@ quote_client = DhanHQ::WS.connect(mode: :quote) do |data|
135
135
  end
136
136
 
137
137
  # Subscribe to NIFTY
138
- quote_client.subscribe_one(segment: "IDX_I", security_id: "13")
138
+ quote_client.subscribe_one(segment: DhanHQ::Constants::ExchangeSegment::IDX_I, security_id: "13")
139
139
 
140
140
  sleep(10)
141
141
  quote_client.stop
@@ -151,10 +151,10 @@ end
151
151
 
152
152
  # Subscribe to multiple indices
153
153
  indices = [
154
- { segment: "IDX_I", security_id: "13" }, # NIFTY
155
- { segment: "IDX_I", security_id: "25" }, # BANKNIFTY
156
- { segment: "IDX_I", security_id: "29" }, # NIFTYIT
157
- { segment: "IDX_I", security_id: "51" } # SENSEX
154
+ { segment: DhanHQ::Constants::ExchangeSegment::IDX_I, security_id: "13" }, # NIFTY
155
+ { segment: DhanHQ::Constants::ExchangeSegment::IDX_I, security_id: "25" }, # BANKNIFTY
156
+ { segment: DhanHQ::Constants::ExchangeSegment::IDX_I, security_id: "29" }, # NIFTYIT
157
+ { segment: DhanHQ::Constants::ExchangeSegment::IDX_I, security_id: "51" } # SENSEX
158
158
  ]
159
159
 
160
160
  indices.each do |idx|
@@ -169,7 +169,7 @@ market_client.stop
169
169
 
170
170
  ```ruby
171
171
  market_client = DhanHQ::WS.connect(mode: :ticker) { |tick| puts tick[:ltp] }
172
- market_client.subscribe_one(segment: "IDX_I", security_id: "13")
172
+ market_client.subscribe_one(segment: DhanHQ::Constants::ExchangeSegment::IDX_I, security_id: "13")
173
173
 
174
174
  # Check connection state
175
175
  puts "Connected: #{market_client.connected?}"
@@ -326,11 +326,11 @@ depth_client.stop
326
326
  # Place a market order
327
327
  order = DhanHQ::Models::Order.place(
328
328
  dhan_client_id: "1000000003",
329
- transaction_type: "BUY",
330
- exchange_segment: "NSE_EQ",
331
- product_type: "INTRADAY",
332
- order_type: "MARKET",
333
- validity: "DAY",
329
+ transaction_type: DhanHQ::Constants::TransactionType::BUY,
330
+ exchange_segment: DhanHQ::Constants::ExchangeSegment::NSE_EQ,
331
+ product_type: DhanHQ::Constants::ProductType::INTRADAY,
332
+ order_type: DhanHQ::Constants::OrderType::MARKET,
333
+ validity: DhanHQ::Constants::Validity::DAY,
334
334
  security_id: "11536", # TCS
335
335
  quantity: 1
336
336
  )
@@ -345,11 +345,11 @@ puts "Status: #{order.order_status}"
345
345
  # Place a limit order
346
346
  order = DhanHQ::Models::Order.place(
347
347
  dhan_client_id: "1000000003",
348
- transaction_type: "BUY",
349
- exchange_segment: "NSE_EQ",
350
- product_type: "INTRADAY",
351
- order_type: "LIMIT",
352
- validity: "DAY",
348
+ transaction_type: DhanHQ::Constants::TransactionType::BUY,
349
+ exchange_segment: DhanHQ::Constants::ExchangeSegment::NSE_EQ,
350
+ product_type: DhanHQ::Constants::ProductType::INTRADAY,
351
+ order_type: DhanHQ::Constants::OrderType::LIMIT,
352
+ validity: DhanHQ::Constants::Validity::DAY,
353
353
  security_id: "11536",
354
354
  quantity: 1,
355
355
  price: 3500.0
@@ -364,11 +364,11 @@ puts "Order ID: #{order.order_id}"
364
364
  # Place stop loss order
365
365
  order = DhanHQ::Models::Order.place(
366
366
  dhan_client_id: "1000000003",
367
- transaction_type: "BUY",
368
- exchange_segment: "NSE_EQ",
369
- product_type: "INTRADAY",
367
+ transaction_type: DhanHQ::Constants::TransactionType::BUY,
368
+ exchange_segment: DhanHQ::Constants::ExchangeSegment::NSE_EQ,
369
+ product_type: DhanHQ::Constants::ProductType::INTRADAY,
370
370
  order_type: "STOPLOSS",
371
- validity: "DAY",
371
+ validity: DhanHQ::Constants::Validity::DAY,
372
372
  security_id: "11536",
373
373
  quantity: 1,
374
374
  price: 3500.0,
@@ -410,11 +410,11 @@ orders = DhanHQ::Models::Order.all
410
410
  puts "Total orders: #{orders.size}"
411
411
 
412
412
  # Filter pending orders
413
- pending = orders.select { |o| o.order_status == "PENDING" }
413
+ pending = orders.select { |o| o.order_status == DhanHQ::Constants::OrderStatus::PENDING }
414
414
  puts "Pending orders: #{pending.size}"
415
415
 
416
416
  # Filter executed orders
417
- executed = orders.select { |o| o.order_status == "TRADED" }
417
+ executed = orders.select { |o| o.order_status == DhanHQ::Constants::OrderStatus::TRADED }
418
418
  puts "Executed orders: #{executed.size}"
419
419
  ```
420
420
 
@@ -472,11 +472,11 @@ end
472
472
  # Create order instance
473
473
  order = DhanHQ::Models::Order.new(
474
474
  dhan_client_id: "1000000003",
475
- transaction_type: "BUY",
476
- exchange_segment: "NSE_EQ",
477
- product_type: "INTRADAY",
478
- order_type: "MARKET",
479
- validity: "DAY",
475
+ transaction_type: DhanHQ::Constants::TransactionType::BUY,
476
+ exchange_segment: DhanHQ::Constants::ExchangeSegment::NSE_EQ,
477
+ product_type: DhanHQ::Constants::ProductType::INTRADAY,
478
+ order_type: DhanHQ::Constants::OrderType::MARKET,
479
+ validity: DhanHQ::Constants::Validity::DAY,
480
480
  security_id: "11536",
481
481
  quantity: 1
482
482
  )
@@ -499,7 +499,7 @@ positions = DhanHQ::Models::Position.all
499
499
  puts "Total positions: #{positions.size}"
500
500
 
501
501
  # Filter by exchange
502
- nse_positions = positions.select { |p| p.exchange_segment == "NSE_EQ" }
502
+ nse_positions = positions.select { |p| p.exchange_segment == DhanHQ::Constants::ExchangeSegment::NSE_EQ }
503
503
  puts "NSE positions: #{nse_positions.size}"
504
504
 
505
505
  # Filter long positions
@@ -517,8 +517,8 @@ position = DhanHQ::Models::Position.all.first
517
517
  if position
518
518
  result = position.convert(
519
519
  dhan_client_id: "1000000003",
520
- from_product_type: "INTRADAY",
521
- to_product_type: "MARGIN",
520
+ from_product_type: DhanHQ::Constants::ProductType::INTRADAY,
521
+ to_product_type: DhanHQ::Constants::ProductType::MARGIN,
522
522
  quantity: position.net_qty.abs
523
523
  )
524
524
 
@@ -677,7 +677,7 @@ puts " Ask Price: ₹#{quote_data[:ask_price]}"
677
677
  # Get daily candles
678
678
  historical_data = DhanHQ::Models::HistoricalData.daily(
679
679
  security_id: "11536",
680
- exchange_segment: "NSE_EQ",
680
+ exchange_segment: DhanHQ::Constants::ExchangeSegment::NSE_EQ,
681
681
  from_date: Date.today - 30,
682
682
  to_date: Date.today
683
683
  )
@@ -694,7 +694,7 @@ end
694
694
  # Get intraday candles (5-minute interval)
695
695
  historical_data = DhanHQ::Models::HistoricalData.intraday(
696
696
  security_id: "11536",
697
- exchange_segment: "NSE_EQ",
697
+ exchange_segment: DhanHQ::Constants::ExchangeSegment::NSE_EQ,
698
698
  from_date: Date.today,
699
699
  to_date: Date.today,
700
700
  interval: 5
@@ -714,7 +714,7 @@ end
714
714
  # Get expiry list for NIFTY
715
715
  expiry_list = DhanHQ::Models::OptionChain.fetch_expiry_list(
716
716
  underlying_scrip: "NIFTY",
717
- underlying_seg: "IDX_I"
717
+ underlying_seg: DhanHQ::Constants::ExchangeSegment::IDX_I
718
718
  )
719
719
 
720
720
  puts "Available expiries:"
@@ -731,7 +731,7 @@ expiry_date = expiry_list.first[:expiry_date] # Use first expiry from above
731
731
 
732
732
  option_chain = DhanHQ::Models::OptionChain.fetch(
733
733
  underlying_scrip: "NIFTY",
734
- underlying_seg: "IDX_I",
734
+ underlying_seg: DhanHQ::Constants::ExchangeSegment::IDX_I,
735
735
  expiry: expiry_date
736
736
  )
737
737
 
@@ -835,22 +835,22 @@ super_order = DhanHQ::Models::SuperOrder.create(
835
835
  legs: [
836
836
  {
837
837
  leg_name: "ENTRY_LEG",
838
- transaction_type: "BUY",
839
- exchange_segment: "NSE_FNO",
840
- product_type: "MARGIN",
841
- order_type: "LIMIT",
842
- validity: "DAY",
838
+ transaction_type: DhanHQ::Constants::TransactionType::BUY,
839
+ exchange_segment: DhanHQ::Constants::ExchangeSegment::NSE_FNO,
840
+ product_type: DhanHQ::Constants::ProductType::MARGIN,
841
+ order_type: DhanHQ::Constants::OrderType::LIMIT,
842
+ validity: DhanHQ::Constants::Validity::DAY,
843
843
  security_id: "49081",
844
844
  quantity: 50,
845
845
  price: 18000.0
846
846
  },
847
847
  {
848
848
  leg_name: "EXIT_LEG",
849
- transaction_type: "SELL",
850
- exchange_segment: "NSE_FNO",
851
- product_type: "MARGIN",
852
- order_type: "LIMIT",
853
- validity: "DAY",
849
+ transaction_type: DhanHQ::Constants::TransactionType::SELL,
850
+ exchange_segment: DhanHQ::Constants::ExchangeSegment::NSE_FNO,
851
+ product_type: DhanHQ::Constants::ProductType::MARGIN,
852
+ order_type: DhanHQ::Constants::OrderType::LIMIT,
853
+ validity: DhanHQ::Constants::Validity::DAY,
854
854
  security_id: "49081",
855
855
  quantity: 50,
856
856
  price: 18100.0
@@ -891,10 +891,10 @@ puts "Total forever orders: #{forever_orders.size}"
891
891
  # Create a forever order (GTT)
892
892
  forever_order = DhanHQ::Models::ForeverOrder.create(
893
893
  dhan_client_id: "1000000003",
894
- transaction_type: "BUY",
895
- exchange_segment: "NSE_EQ",
896
- product_type: "MARGIN",
897
- order_type: "LIMIT",
894
+ transaction_type: DhanHQ::Constants::TransactionType::BUY,
895
+ exchange_segment: DhanHQ::Constants::ExchangeSegment::NSE_EQ,
896
+ product_type: DhanHQ::Constants::ProductType::MARGIN,
897
+ order_type: DhanHQ::Constants::OrderType::LIMIT,
898
898
  validity: "GTC",
899
899
  security_id: "11536",
900
900
  quantity: 1,
@@ -971,6 +971,8 @@ tc.status
971
971
 
972
972
  #### Kill Switch (model, backward compatible)
973
973
 
974
+ Uses query param per API doc: `POST /v2/killswitch?killSwitchStatus=ACTIVATE` (no body).
975
+
974
976
  ```ruby
975
977
  # Activate kill switch
976
978
  result = DhanHQ::Models::KillSwitch.update("ACTIVATE")
@@ -1010,10 +1012,10 @@ end
1010
1012
  # Calculate margin for an order
1011
1013
  margin = DhanHQ::Models::Margin.calculate(
1012
1014
  dhan_client_id: "1000000003",
1013
- transaction_type: "BUY",
1014
- exchange_segment: "NSE_EQ",
1015
- product_type: "MARGIN",
1016
- order_type: "LIMIT",
1015
+ transaction_type: DhanHQ::Constants::TransactionType::BUY,
1016
+ exchange_segment: DhanHQ::Constants::ExchangeSegment::NSE_EQ,
1017
+ product_type: DhanHQ::Constants::ProductType::MARGIN,
1018
+ order_type: DhanHQ::Constants::OrderType::LIMIT,
1017
1019
  security_id: "11536",
1018
1020
  quantity: 1,
1019
1021
  price: 3500.0
@@ -1054,11 +1056,11 @@ end
1054
1056
  # Test valid order
1055
1057
  valid_params = {
1056
1058
  dhan_client_id: "1000000003",
1057
- transaction_type: "BUY",
1058
- exchange_segment: "NSE_EQ",
1059
- product_type: "INTRADAY",
1060
- order_type: "LIMIT",
1061
- validity: "DAY",
1059
+ transaction_type: DhanHQ::Constants::TransactionType::BUY,
1060
+ exchange_segment: DhanHQ::Constants::ExchangeSegment::NSE_EQ,
1061
+ product_type: DhanHQ::Constants::ProductType::INTRADAY,
1062
+ order_type: DhanHQ::Constants::OrderType::LIMIT,
1063
+ validity: DhanHQ::Constants::Validity::DAY,
1062
1064
  security_id: "11536",
1063
1065
  quantity: 1,
1064
1066
  price: 3500.0
@@ -1074,8 +1076,8 @@ end
1074
1076
 
1075
1077
  # Test invalid order (missing required field)
1076
1078
  invalid_params = {
1077
- transaction_type: "BUY",
1078
- exchange_segment: "NSE_EQ"
1079
+ transaction_type: DhanHQ::Constants::TransactionType::BUY,
1080
+ exchange_segment: DhanHQ::Constants::ExchangeSegment::NSE_EQ
1079
1081
  # Missing required fields
1080
1082
  }
1081
1083
 
@@ -1133,10 +1135,10 @@ end
1133
1135
  # Test valid margin calculation
1134
1136
  valid_params = {
1135
1137
  dhan_client_id: "1000000003",
1136
- transaction_type: "BUY",
1137
- exchange_segment: "NSE_EQ",
1138
- product_type: "MARGIN",
1139
- order_type: "LIMIT",
1138
+ transaction_type: DhanHQ::Constants::TransactionType::BUY,
1139
+ exchange_segment: DhanHQ::Constants::ExchangeSegment::NSE_EQ,
1140
+ product_type: DhanHQ::Constants::ProductType::MARGIN,
1141
+ order_type: DhanHQ::Constants::OrderType::LIMIT,
1140
1142
  security_id: "11536",
1141
1143
  quantity: 1,
1142
1144
  price: 3500.0
@@ -1157,7 +1159,7 @@ end
1157
1159
  # Test valid option chain request
1158
1160
  valid_params = {
1159
1161
  underlying_scrip: "NIFTY",
1160
- underlying_seg: "IDX_I",
1162
+ underlying_seg: DhanHQ::Constants::ExchangeSegment::IDX_I,
1161
1163
  expiry: "2024-01-25"
1162
1164
  }
1163
1165
 
@@ -1183,7 +1185,7 @@ end
1183
1185
  # Test valid historical data request
1184
1186
  valid_params = {
1185
1187
  security_id: "11536",
1186
- exchange_segment: "NSE_EQ",
1188
+ exchange_segment: DhanHQ::Constants::ExchangeSegment::NSE_EQ,
1187
1189
  from_date: Date.today - 7,
1188
1190
  to_date: Date.today,
1189
1191
  interval: 5
@@ -1252,7 +1254,7 @@ end
1252
1254
  begin
1253
1255
  order = DhanHQ::Models::Order.place(
1254
1256
  transaction_type: "INVALID_TYPE",
1255
- exchange_segment: "NSE_EQ"
1257
+ exchange_segment: DhanHQ::Constants::ExchangeSegment::NSE_EQ
1256
1258
  )
1257
1259
  rescue DhanHQ::Error => e
1258
1260
  puts "Validation error caught: #{e.message}"
@@ -1327,10 +1329,10 @@ puts "1. Available Margin: ₹#{funds.available_margin}"
1327
1329
  # 2. Calculate margin
1328
1330
  margin = DhanHQ::Models::Margin.calculate(
1329
1331
  dhan_client_id: "1000000003",
1330
- transaction_type: "BUY",
1331
- exchange_segment: "NSE_EQ",
1332
- product_type: "MARGIN",
1333
- order_type: "LIMIT",
1332
+ transaction_type: DhanHQ::Constants::TransactionType::BUY,
1333
+ exchange_segment: DhanHQ::Constants::ExchangeSegment::NSE_EQ,
1334
+ product_type: DhanHQ::Constants::ProductType::MARGIN,
1335
+ order_type: DhanHQ::Constants::OrderType::LIMIT,
1334
1336
  security_id: "11536",
1335
1337
  quantity: 1,
1336
1338
  price: 3500.0
@@ -1340,11 +1342,11 @@ puts "2. Margin Required: ₹#{margin.margin_required}"
1340
1342
  # 3. Place order
1341
1343
  order = DhanHQ::Models::Order.place(
1342
1344
  dhan_client_id: "1000000003",
1343
- transaction_type: "BUY",
1344
- exchange_segment: "NSE_EQ",
1345
- product_type: "MARGIN",
1346
- order_type: "LIMIT",
1347
- validity: "DAY",
1345
+ transaction_type: DhanHQ::Constants::TransactionType::BUY,
1346
+ exchange_segment: DhanHQ::Constants::ExchangeSegment::NSE_EQ,
1347
+ product_type: DhanHQ::Constants::ProductType::MARGIN,
1348
+ order_type: DhanHQ::Constants::OrderType::LIMIT,
1349
+ validity: DhanHQ::Constants::Validity::DAY,
1348
1350
  security_id: "11536",
1349
1351
  quantity: 1,
1350
1352
  price: 3500.0
@@ -1363,7 +1365,7 @@ orders_client.start
1363
1365
  # 5. Modify order (if pending)
1364
1366
  sleep(2)
1365
1367
  order.refresh
1366
- if order.order_status == "PENDING"
1368
+ if order.order_status == DhanHQ::Constants::OrderStatus::PENDING
1367
1369
  if order.modify(price: 3501.0)
1368
1370
  puts "5. Order Modified"
1369
1371
  end
@@ -1390,7 +1392,7 @@ puts "=== Test Complete ==="
1390
1392
  # Get historical data and analyze
1391
1393
  historical_data = DhanHQ::Models::HistoricalData.daily(
1392
1394
  security_id: "11536",
1393
- exchange_segment: "NSE_EQ",
1395
+ exchange_segment: DhanHQ::Constants::ExchangeSegment::NSE_EQ,
1394
1396
  from_date: Date.today - 30,
1395
1397
  to_date: Date.today
1396
1398
  )
@@ -1419,13 +1421,13 @@ end
1419
1421
  # Get option chain
1420
1422
  expiry_list = DhanHQ::Models::OptionChain.fetch_expiry_list(
1421
1423
  underlying_scrip: "NIFTY",
1422
- underlying_seg: "IDX_I"
1424
+ underlying_seg: DhanHQ::Constants::ExchangeSegment::IDX_I
1423
1425
  )
1424
1426
 
1425
1427
  expiry = expiry_list.first[:expiry_date]
1426
1428
  option_chain = DhanHQ::Models::OptionChain.fetch(
1427
1429
  underlying_scrip: "NIFTY",
1428
- underlying_seg: "IDX_I",
1430
+ underlying_seg: DhanHQ::Constants::ExchangeSegment::IDX_I,
1429
1431
  expiry: expiry
1430
1432
  )
1431
1433
 
@@ -0,0 +1,117 @@
1
+ # Troubleshooting
2
+
3
+ Common issues and solutions when working with the DhanHQ Ruby client.
4
+
5
+ ---
6
+
7
+ ## 429: Unexpected Response Code
8
+
9
+ **Symptom:** WebSocket connection fails with a 429 status.
10
+
11
+ **Cause:** Too many connections opened in quick succession, or exceeding the per-user WebSocket connection limit (5 per user).
12
+
13
+ **Solution:**
14
+ - The client automatically cools off for **60 seconds** and retries with exponential backoff.
15
+ - Prefer `ws.disconnect!` before reconnecting to cleanly release server-side resources.
16
+ - Call `DhanHQ::WS.disconnect_all_local!` to kill any straggler connections.
17
+ - Avoid rapid connect/disconnect loops — the client handles backoff internally.
18
+
19
+ ```ruby
20
+ # Kill all local WebSocket connections
21
+ DhanHQ::WS.disconnect_all_local!
22
+
23
+ # Wait before reconnecting
24
+ sleep(2)
25
+
26
+ # Reconnect
27
+ client = DhanHQ::WS.connect(mode: :ticker) { |tick| puts tick[:ltp] }
28
+ ```
29
+
30
+ ---
31
+
32
+ ## No Ticks After Reconnect
33
+
34
+ **Symptom:** WebSocket reconnects successfully but no market data arrives.
35
+
36
+ **Cause:** Subscriptions were not restored after the connection dropped.
37
+
38
+ **Solution:**
39
+ - The client **automatically resends** the current subscription snapshot on reconnect — this should work transparently.
40
+ - If you're managing connections manually, ensure you re-subscribe after a clean start.
41
+ - Check that your instruments are valid and the market is open.
42
+
43
+ ---
44
+
45
+ ## Binary Parse Errors
46
+
47
+ **Symptom:** Errors in logs related to binary frame parsing.
48
+
49
+ **Cause:** Malformed or unexpected binary frames from the server.
50
+
51
+ **Solution:**
52
+ - The client safely drops malformed frames and keeps the event loop alive.
53
+ - Run with `DHAN_LOG_LEVEL=DEBUG` to inspect raw frames:
54
+
55
+ ```bash
56
+ export DHAN_LOG_LEVEL=DEBUG
57
+ ```
58
+
59
+ ```ruby
60
+ DhanHQ.logger.level = Logger::DEBUG
61
+ ```
62
+
63
+ ---
64
+
65
+ ## Authentication Errors
66
+
67
+ | Error Class | Meaning |
68
+ | ------------------------------------ | ---------------------------------------------------------- |
69
+ | `DhanHQ::AuthenticationError` | Token could not be resolved (missing config, nil provider) |
70
+ | `DhanHQ::InvalidAuthenticationError` | API returned 401 or error code DH-901 |
71
+ | `DhanHQ::TokenExpiredError` | API returned error code 807 (token expired) |
72
+ | `DhanHQ::InvalidTokenError` | API returned error code 809 (invalid token) |
73
+
74
+ **Solutions:**
75
+ - Verify `DHAN_CLIENT_ID` and `DHAN_ACCESS_TOKEN` are set correctly.
76
+ - If using `access_token_provider`, ensure it returns a non-nil string.
77
+ - For 401 retries: the client retries **once** with a fresh token when `access_token_provider` is configured.
78
+ - See [AUTHENTICATION.md](AUTHENTICATION.md) for detailed token lifecycle handling.
79
+
80
+ ---
81
+
82
+ ## Connection Timeouts
83
+
84
+ **Symptom:** REST API calls hang or fail with timeout errors.
85
+
86
+ **Solution:** Adjust timeout settings via environment variables:
87
+
88
+ ```dotenv
89
+ DHAN_CONNECT_TIMEOUT=15 # default: 10 seconds
90
+ DHAN_READ_TIMEOUT=60 # default: 30 seconds
91
+ DHAN_WRITE_TIMEOUT=60 # default: 30 seconds
92
+ ```
93
+
94
+ ---
95
+
96
+ ## Debug Logging
97
+
98
+ Enable full debug output to diagnose any issue:
99
+
100
+ ```ruby
101
+ DhanHQ.logger.level = Logger::DEBUG
102
+ ```
103
+
104
+ Or via environment:
105
+
106
+ ```bash
107
+ export DHAN_LOG_LEVEL=DEBUG
108
+ ```
109
+
110
+ This logs HTTP requests/responses, WebSocket frames, and internal state transitions.
111
+
112
+ ---
113
+
114
+ ## Getting Help
115
+
116
+ - [DhanHQ GitHub Issues](https://github.com/shubhamtaywade82/dhanhq-client/issues)
117
+ - [Dhan API Documentation](https://dhanhq.co/docs/v2/)
@@ -45,10 +45,10 @@ market_client = DhanHQ::WS.connect(mode: :ticker) do |tick|
45
45
  end
46
46
 
47
47
  # Subscribe to specific indices
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
48
+ market_client.subscribe_one(segment: DhanHQ::Constants::ExchangeSegment::IDX_I, security_id: "13") # NIFTY
49
+ market_client.subscribe_one(segment: DhanHQ::Constants::ExchangeSegment::IDX_I, security_id: "25") # BANKNIFTY
50
+ market_client.subscribe_one(segment: DhanHQ::Constants::ExchangeSegment::IDX_I, security_id: "29") # NIFTYIT
51
+ market_client.subscribe_one(segment: DhanHQ::Constants::ExchangeSegment::IDX_I, security_id: "51") # SENSEX
52
52
 
53
53
  # Clean shutdown
54
54
  market_client.stop
@@ -136,20 +136,20 @@ end
136
136
  client = DhanHQ::WS.connect(mode: :ticker) { |tick| puts tick[:ltp] }
137
137
 
138
138
  # Subscribe to individual instruments
139
- client.subscribe_one(segment: "IDX_I", security_id: "13") # NIFTY
140
- client.subscribe_one(segment: "IDX_I", security_id: "25") # BANKNIFTY
141
- client.subscribe_one(segment: "NSE_EQ", security_id: "2885") # RELIANCE
139
+ client.subscribe_one(segment: DhanHQ::Constants::ExchangeSegment::IDX_I, security_id: "13") # NIFTY
140
+ client.subscribe_one(segment: DhanHQ::Constants::ExchangeSegment::IDX_I, security_id: "25") # BANKNIFTY
141
+ client.subscribe_one(segment: DhanHQ::Constants::ExchangeSegment::NSE_EQ, security_id: "2885") # RELIANCE
142
142
 
143
143
  # Subscribe to multiple instruments
144
144
  instruments = [
145
- { ExchangeSegment: "IDX_I", SecurityId: "13" },
146
- { ExchangeSegment: "IDX_I", SecurityId: "25" },
147
- { ExchangeSegment: "NSE_EQ", SecurityId: "2885" }
145
+ { ExchangeSegment: DhanHQ::Constants::ExchangeSegment::IDX_I, SecurityId: "13" },
146
+ { ExchangeSegment: DhanHQ::Constants::ExchangeSegment::IDX_I, SecurityId: "25" },
147
+ { ExchangeSegment: DhanHQ::Constants::ExchangeSegment::NSE_EQ, SecurityId: "2885" }
148
148
  ]
149
149
  client.subscribe_many(instruments)
150
150
 
151
151
  # Unsubscribe
152
- client.unsubscribe_one(segment: "IDX_I", security_id: "13")
152
+ client.unsubscribe_one(segment: DhanHQ::Constants::ExchangeSegment::IDX_I, security_id: "13")
153
153
  ```
154
154
 
155
155
  #### Finding Correct Security IDs
@@ -297,8 +297,8 @@ end
297
297
 
298
298
  # Method 2: Direct specification (legacy)
299
299
  symbols_direct = [
300
- { symbol: "RELIANCE", exchange_segment: "NSE_EQ", security_id: "2885" },
301
- { symbol: "TCS", exchange_segment: "NSE_EQ", security_id: "11536" }
300
+ { symbol: "RELIANCE", exchange_segment: DhanHQ::Constants::ExchangeSegment::NSE_EQ, security_id: "2885" },
301
+ { symbol: "TCS", exchange_segment: DhanHQ::Constants::ExchangeSegment::NSE_EQ, security_id: "11536" }
302
302
  ]
303
303
 
304
304
  depth_client = DhanHQ::WS::MarketDepth.connect(symbols: symbols_direct) do |depth_data|
@@ -333,8 +333,8 @@ client.start
333
333
 
334
334
  # Subscribe to symbols
335
335
  symbols = [
336
- { symbol: "RELIANCE", exchange_segment: "NSE_EQ", security_id: "2885" },
337
- { symbol: "TCS", exchange_segment: "NSE_EQ", security_id: "11536" }
336
+ { symbol: "RELIANCE", exchange_segment: DhanHQ::Constants::ExchangeSegment::NSE_EQ, security_id: "2885" },
337
+ { symbol: "TCS", exchange_segment: DhanHQ::Constants::ExchangeSegment::NSE_EQ, security_id: "11536" }
338
338
  ]
339
339
  client.subscribe(symbols)
340
340
  ```
@@ -587,10 +587,10 @@ class MarketDataService
587
587
  end
588
588
 
589
589
  # Subscribe to indices
590
- @market_client.subscribe_one(segment: "IDX_I", security_id: "13") # NIFTY
591
- @market_client.subscribe_one(segment: "IDX_I", security_id: "25") # BANKNIFTY
592
- @market_client.subscribe_one(segment: "IDX_I", security_id: "29") # NIFTYIT
593
- @market_client.subscribe_one(segment: "IDX_I", security_id: "51") # SENSEX
590
+ @market_client.subscribe_one(segment: DhanHQ::Constants::ExchangeSegment::IDX_I, security_id: "13") # NIFTY
591
+ @market_client.subscribe_one(segment: DhanHQ::Constants::ExchangeSegment::IDX_I, security_id: "25") # BANKNIFTY
592
+ @market_client.subscribe_one(segment: DhanHQ::Constants::ExchangeSegment::IDX_I, security_id: "29") # NIFTYIT
593
+ @market_client.subscribe_one(segment: DhanHQ::Constants::ExchangeSegment::IDX_I, security_id: "51") # SENSEX
594
594
  end
595
595
 
596
596
  def stop_market_feed