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
@@ -1,130 +1,412 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require_relative "errors"
4
+
3
5
  module DhanHQ
4
6
  # Enumerations and helper lookups used across the REST and WebSocket clients.
5
7
  module Constants
6
- # Valid transaction directions accepted by order placement APIs.
7
- TRANSACTION_TYPES = %w[BUY SELL].freeze
8
-
9
- # Supported exchange segments for security lookups and subscription APIs.
10
- EXCHANGE_SEGMENTS = %w[
11
- NSE_EQ
12
- NSE_FNO
13
- NSE_CURRENCY
14
- BSE_EQ
15
- BSE_FNO
16
- BSE_CURRENCY
17
- MCX_COMM
18
- IDX_I
19
- ].freeze
8
+ # Exchange segments for different markets and instruments.
9
+ module ExchangeSegment
10
+ IDX_I = "IDX_I"
11
+ NSE_EQ = "NSE_EQ"
12
+ NSE_FNO = "NSE_FNO"
13
+ NSE_CURRENCY = "NSE_CURRENCY"
14
+ NSE_COMM = "NSE_COMM"
15
+ BSE_EQ = "BSE_EQ"
16
+ MCX_COMM = "MCX_COMM"
17
+ BSE_CURRENCY = "BSE_CURRENCY"
18
+ BSE_FNO = "BSE_FNO"
20
19
 
21
- # Security instrument kinds returned in instrument master downloads.
22
- INSTRUMENTS = %w[
23
- INDEX
24
- FUTIDX
25
- OPTIDX
26
- EQUITY
27
- FUTSTK
28
- OPTSTK
29
- FUTCOM
30
- OPTFUT
31
- FUTCUR
32
- OPTCUR
33
- ].freeze
20
+ ALL = [IDX_I, NSE_EQ, NSE_FNO, NSE_CURRENCY, NSE_COMM, BSE_EQ, MCX_COMM, BSE_CURRENCY, BSE_FNO].freeze
21
+ end
34
22
 
35
- # Product types that can be used while placing or modifying orders.
36
- PRODUCT_TYPES = %w[
37
- CNC
38
- INTRADAY
39
- MARGIN
40
- MTF
41
- CO
42
- BO
43
- ].freeze
23
+ # Product types for order placement.
24
+ module ProductType
25
+ CNC = "CNC"
26
+ INTRADAY = "INTRADAY"
27
+ MARGIN = "MARGIN"
28
+ MTF = "MTF"
29
+ CO = "CO"
30
+ BO = "BO"
44
31
 
45
- # Order execution types supported by the platform.
46
- ORDER_TYPES = %w[
47
- LIMIT
48
- MARKET
49
- STOP_LOSS
50
- STOP_LOSS_MARKET
51
- ].freeze
32
+ ALL = [CNC, INTRADAY, MARGIN, MTF, CO, BO].freeze
33
+ end
52
34
 
53
- # Order validity flags supported by the trading APIs.
54
- VALIDITY_TYPES = %w[DAY IOC].freeze
35
+ # Buy/Sell transaction types.
36
+ module TransactionType
37
+ BUY = "BUY"
38
+ SELL = "SELL"
55
39
 
56
- # Permitted after-market order submission windows.
57
- AMO_TIMINGS = %w[
58
- OPEN
59
- OPEN_30
60
- OPEN_60
61
- PRE_OPEN
62
- ].freeze
40
+ ALL = [BUY, SELL].freeze
41
+ end
63
42
 
64
- # Status values returned when querying order lifecycle events.
65
- ORDER_STATUSES = %w[
66
- TRANSIT
67
- PENDING
68
- REJECTED
69
- CANCELLED
70
- PART_TRADED
71
- TRADED
72
- EXPIRED
73
- MODIFIED
74
- TRIGGERED
75
- ].freeze
43
+ # Order types for placement and modification.
44
+ module OrderType
45
+ LIMIT = "LIMIT"
46
+ MARKET = "MARKET"
47
+ STOP_LOSS = "STOP_LOSS"
48
+ STOP_LOSS_MARKET = "STOP_LOSS_MARKET"
49
+
50
+ ALL = [LIMIT, MARKET, STOP_LOSS, STOP_LOSS_MARKET].freeze
51
+ end
52
+
53
+ # Order validity types.
54
+ module Validity
55
+ DAY = "DAY"
56
+ IOC = "IOC"
57
+
58
+ ALL = [DAY, IOC].freeze
59
+ end
60
+
61
+ # Order lifecycle status values.
62
+ module OrderStatus
63
+ TRANSIT = "TRANSIT"
64
+ PENDING = "PENDING"
65
+ CLOSED = "CLOSED"
66
+ TRIGGERED = "TRIGGERED"
67
+ REJECTED = "REJECTED"
68
+ CANCELLED = "CANCELLED"
69
+ PART_TRADED = "PART_TRADED"
70
+ TRADED = "TRADED"
71
+ EXPIRED = "EXPIRED"
72
+ MODIFIED = "MODIFIED"
73
+
74
+ ALL = [TRANSIT, PENDING, CLOSED, TRIGGERED, REJECTED, CANCELLED, PART_TRADED, TRADED, EXPIRED, MODIFIED].freeze
75
+ end
76
+
77
+ # AMO (after market order) timing options.
78
+ module AmoTime
79
+ PRE_OPEN = "PRE_OPEN"
80
+ OPEN = "OPEN"
81
+ OPEN_30 = "OPEN_30"
82
+ OPEN_60 = "OPEN_60"
83
+
84
+ ALL = [PRE_OPEN, OPEN, OPEN_30, OPEN_60].freeze
85
+ end
86
+
87
+ # Expiry codes for futures and options.
88
+ module ExpiryCode
89
+ CURRENT = 0
90
+ NEXT = 1
91
+ FAR = 2
92
+
93
+ ALL = [CURRENT, NEXT, FAR].freeze
94
+ end
95
+
96
+ # Instrument types across exchanges.
97
+ module InstrumentType
98
+ INDEX = "INDEX"
99
+ FUTIDX = "FUTIDX"
100
+ OPTIDX = "OPTIDX"
101
+ EQUITY = "EQUITY"
102
+ FUTSTK = "FUTSTK"
103
+ OPTSTK = "OPTSTK"
104
+ FUTCOM = "FUTCOM"
105
+ OPTFUT = "OPTFUT"
106
+ FUTCUR = "FUTCUR"
107
+ OPTCUR = "OPTCUR"
108
+
109
+ ALL = [INDEX, FUTIDX, OPTIDX, EQUITY, FUTSTK, OPTSTK, FUTCOM, OPTFUT, FUTCUR, OPTCUR].freeze
110
+ end
111
+
112
+ # Backward-compatible alias kept for existing SDK usage.
113
+ Instrument = InstrumentType
114
+
115
+ # Option types for derivatives trading.
116
+ module OptionType
117
+ CALL = "CALL"
118
+ PUT = "PUT"
119
+
120
+ ALL = [CALL, PUT].freeze
121
+ end
122
+
123
+ # Leg names used in BO/CO/Super/Forever orders.
124
+ module LegName
125
+ ENTRY_LEG = "ENTRY_LEG"
126
+ TARGET_LEG = "TARGET_LEG"
127
+ STOP_LOSS_LEG = "STOP_LOSS_LEG"
128
+
129
+ ALL = [ENTRY_LEG, TARGET_LEG, STOP_LOSS_LEG].freeze
130
+ end
131
+
132
+ # Order flags for Forever Orders.
133
+ module OrderFlag
134
+ SINGLE = "SINGLE"
135
+ OCO = "OCO"
136
+
137
+ ALL = [SINGLE, OCO].freeze
138
+ end
139
+
140
+ # Position types for position conversion.
141
+ module PositionType
142
+ LONG = "LONG"
143
+ SHORT = "SHORT"
144
+
145
+ ALL = [LONG, SHORT].freeze
146
+ end
147
+
148
+ # Feed request codes for Live Market Feed WebSocket.
149
+ module FeedRequest
150
+ CONNECT = 11
151
+ DISCONNECT = 12
152
+ SUBSCRIBE_TICKER = 15
153
+ UNSUBSCRIBE_TICKER = 16
154
+ SUBSCRIBE_QUOTE = 17
155
+ UNSUBSCRIBE_QUOTE = 18
156
+ SUBSCRIBE_FULL = 21
157
+ UNSUBSCRIBE_FULL = 22
158
+ SUBSCRIBE_DEPTH = 23
159
+ UNSUBSCRIBE_DEPTH = 24
160
+
161
+ ALL = [
162
+ CONNECT,
163
+ DISCONNECT,
164
+ SUBSCRIBE_TICKER,
165
+ UNSUBSCRIBE_TICKER,
166
+ SUBSCRIBE_QUOTE,
167
+ UNSUBSCRIBE_QUOTE,
168
+ SUBSCRIBE_FULL,
169
+ UNSUBSCRIBE_FULL,
170
+ SUBSCRIBE_DEPTH,
171
+ UNSUBSCRIBE_DEPTH
172
+ ].freeze
173
+ end
174
+
175
+ # Feed response codes for Live Market Feed WebSocket.
176
+ module FeedResponse
177
+ INDEX_PACKET = 1
178
+ TICKER_PACKET = 2
179
+ QUOTE_PACKET = 4
180
+ OI_PACKET = 5
181
+ PREV_CLOSE_PACKET = 6
182
+ MARKET_STATUS_PACKET = 7
183
+ FULL_PACKET = 8
184
+ FEED_DISCONNECT = 50
185
+
186
+ ALL = [
187
+ INDEX_PACKET,
188
+ TICKER_PACKET,
189
+ QUOTE_PACKET,
190
+ OI_PACKET,
191
+ PREV_CLOSE_PACKET,
192
+ MARKET_STATUS_PACKET,
193
+ FULL_PACKET,
194
+ FEED_DISCONNECT
195
+ ].freeze
196
+ end
197
+
198
+ # Comparison types for conditional trigger alerts.
199
+ module ComparisonType
200
+ TECHNICAL_WITH_VALUE = "TECHNICAL_WITH_VALUE"
201
+ TECHNICAL_WITH_INDICATOR = "TECHNICAL_WITH_INDICATOR"
202
+ TECHNICAL_WITH_CLOSE = "TECHNICAL_WITH_CLOSE"
203
+ PRICE_WITH_VALUE = "PRICE_WITH_VALUE"
204
+
205
+ ALL = [
206
+ TECHNICAL_WITH_VALUE,
207
+ TECHNICAL_WITH_INDICATOR,
208
+ TECHNICAL_WITH_CLOSE,
209
+ PRICE_WITH_VALUE
210
+ ].freeze
211
+ end
212
+
213
+ # Technical indicators for conditional triggers.
214
+ module IndicatorName
215
+ SMA_5 = "SMA_5"
216
+ SMA_10 = "SMA_10"
217
+ SMA_20 = "SMA_20"
218
+ SMA_50 = "SMA_50"
219
+ SMA_100 = "SMA_100"
220
+ SMA_200 = "SMA_200"
221
+
222
+ EMA_5 = "EMA_5"
223
+ EMA_10 = "EMA_10"
224
+ EMA_20 = "EMA_20"
225
+ EMA_50 = "EMA_50"
226
+ EMA_100 = "EMA_100"
227
+ EMA_200 = "EMA_200"
228
+
229
+ BB_UPPER = "BB_UPPER"
230
+ BB_LOWER = "BB_LOWER"
231
+ RSI_14 = "RSI_14"
232
+ ATR_14 = "ATR_14"
233
+ STOCHASTIC = "STOCHASTIC"
234
+ STOCHRSI_14 = "STOCHRSI_14"
235
+ MACD_26 = "MACD_26"
236
+ MACD_12 = "MACD_12"
237
+ MACD_HIST = "MACD_HIST"
238
+
239
+ ALL = [
240
+ SMA_5,
241
+ SMA_10,
242
+ SMA_20,
243
+ SMA_50,
244
+ SMA_100,
245
+ SMA_200,
246
+ EMA_5,
247
+ EMA_10,
248
+ EMA_20,
249
+ EMA_50,
250
+ EMA_100,
251
+ EMA_200,
252
+ BB_UPPER,
253
+ BB_LOWER,
254
+ RSI_14,
255
+ ATR_14,
256
+ STOCHASTIC,
257
+ STOCHRSI_14,
258
+ MACD_26,
259
+ MACD_12,
260
+ MACD_HIST
261
+ ].freeze
262
+ end
263
+
264
+ # Operators for conditional trigger comparisons.
265
+ module Operator
266
+ CROSSING_UP = "CROSSING_UP"
267
+ CROSSING_DOWN = "CROSSING_DOWN"
268
+ CROSSING_ANY_SIDE = "CROSSING_ANY_SIDE"
269
+ GREATER_THAN = "GREATER_THAN"
270
+ LESS_THAN = "LESS_THAN"
271
+ GREATER_THAN_EQUAL = "GREATER_THAN_EQUAL"
272
+ LESS_THAN_EQUAL = "LESS_THAN_EQUAL"
273
+ EQUAL = "EQUAL"
274
+ NOT_EQUAL = "NOT_EQUAL"
275
+
276
+ ALL = [
277
+ CROSSING_UP,
278
+ CROSSING_DOWN,
279
+ CROSSING_ANY_SIDE,
280
+ GREATER_THAN,
281
+ LESS_THAN,
282
+ GREATER_THAN_EQUAL,
283
+ LESS_THAN_EQUAL,
284
+ EQUAL,
285
+ NOT_EQUAL
286
+ ].freeze
287
+ end
288
+
289
+ # Status values for conditional trigger alerts.
290
+ module TriggerStatus
291
+ ACTIVE = "ACTIVE"
292
+ TRIGGERED = "TRIGGERED"
293
+ EXPIRED = "EXPIRED"
294
+ CANCELLED = "CANCELLED"
295
+
296
+ ALL = [ACTIVE, TRIGGERED, EXPIRED, CANCELLED].freeze
297
+ end
298
+
299
+ # Trading API error codes (DH-900 series).
300
+ module TradingErrorCode
301
+ INVALID_AUTHENTICATION = "DH-901"
302
+ INVALID_ACCESS = "DH-902"
303
+ USER_ACCOUNT = "DH-903"
304
+ RATE_LIMIT = "DH-904"
305
+ INPUT_EXCEPTION = "DH-905"
306
+ ORDER_ERROR = "DH-906"
307
+ DATA_ERROR = "DH-907"
308
+ INTERNAL_SERVER_ERROR = "DH-908"
309
+ NETWORK_ERROR = "DH-909"
310
+ OTHERS = "DH-910"
311
+ NO_HOLDINGS = "DH-1111"
312
+
313
+ ALL = [
314
+ INVALID_AUTHENTICATION,
315
+ INVALID_ACCESS,
316
+ USER_ACCOUNT,
317
+ RATE_LIMIT,
318
+ INPUT_EXCEPTION,
319
+ ORDER_ERROR,
320
+ DATA_ERROR,
321
+ INTERNAL_SERVER_ERROR,
322
+ NETWORK_ERROR,
323
+ OTHERS,
324
+ NO_HOLDINGS
325
+ ].freeze
326
+ end
327
+
328
+ # Data API error codes (800 series).
329
+ module DataErrorCode
330
+ INTERNAL_SERVER_ERROR = 800
331
+ INSTRUMENTS_LIMIT = 804
332
+ TOO_MANY_REQUESTS = 805
333
+ NOT_SUBSCRIBED = 806
334
+ TOKEN_EXPIRED = 807
335
+ AUTH_FAILED = 808
336
+ INVALID_TOKEN = 809
337
+ INVALID_CLIENT_ID = 810
338
+ INVALID_EXPIRY_DATE = 811
339
+ INVALID_DATE_FORMAT = 812
340
+ INVALID_SECURITY_ID = 813
341
+ INVALID_REQUEST = 814
342
+
343
+ ALL = [
344
+ INTERNAL_SERVER_ERROR,
345
+ INSTRUMENTS_LIMIT,
346
+ TOO_MANY_REQUESTS,
347
+ NOT_SUBSCRIBED,
348
+ TOKEN_EXPIRED,
349
+ AUTH_FAILED,
350
+ INVALID_TOKEN,
351
+ INVALID_CLIENT_ID,
352
+ INVALID_EXPIRY_DATE,
353
+ INVALID_DATE_FORMAT,
354
+ INVALID_SECURITY_ID,
355
+ INVALID_REQUEST
356
+ ].freeze
357
+ end
358
+
359
+ # Public rate limits published in DhanHQ API documentation.
360
+ module RateLimit
361
+ ORDER_API = { per_second: 10, per_minute: 250, per_hour: 1000, per_day: 7000 }.freeze
362
+ DATA_API = { per_second: 5, per_day: 100_000 }.freeze
363
+ QUOTE_API = { per_second: 1 }.freeze
364
+ NON_TRADING_API = { per_second: 20 }.freeze
365
+ ORDER_MODIFICATIONS_PER_ORDER = 25
366
+ end
367
+
368
+ # Backward-compatible arrays used across existing validations.
369
+ TRANSACTION_TYPES = TransactionType::ALL
370
+ EXCHANGE_SEGMENTS = ExchangeSegment::ALL
371
+ INSTRUMENTS = InstrumentType::ALL
372
+ PRODUCT_TYPES = ProductType::ALL
373
+ ORDER_TYPES = OrderType::ALL
374
+ VALIDITY_TYPES = Validity::ALL
375
+ AMO_TIMINGS = AmoTime::ALL
376
+ ORDER_STATUSES = OrderStatus::ALL
377
+ COMPARISON_TYPES = ComparisonType::ALL
378
+ OPERATORS = Operator::ALL
76
379
 
77
380
  # Exchange aliases used when building subscription payloads.
78
- NSE = "NSE_EQ"
79
- # Bombay Stock Exchange equities segment alias.
80
- BSE = "BSE_EQ"
81
- # Currency segment alias.
82
- CUR = "NSE_CURRENCY"
83
- # Multi Commodity Exchange segment alias.
84
- MCX = "MCX_COMM"
85
- # F&O segment alias.
86
- FNO = "NSE_FNO"
87
- # National Stock Exchange futures & options segment alias.
88
- NSE_FNO = "NSE_FNO"
89
- # Bombay Stock Exchange futures & options segment alias.
90
- BSE_FNO = "BSE_FNO"
91
- # Broad index segment alias.
92
- INDEX = "IDX_I"
93
-
94
- # Segments that support option instruments.
381
+ NSE = ExchangeSegment::NSE_EQ
382
+ BSE = ExchangeSegment::BSE_EQ
383
+ CUR = ExchangeSegment::NSE_CURRENCY
384
+ MCX = ExchangeSegment::MCX_COMM
385
+ FNO = ExchangeSegment::NSE_FNO
386
+ NSE_FNO = ExchangeSegment::NSE_FNO
387
+ BSE_FNO = ExchangeSegment::BSE_FNO
388
+ INDEX = ExchangeSegment::IDX_I
389
+
95
390
  OPTION_SEGMENTS = [NSE, BSE, CUR, MCX, FNO, NSE_FNO, BSE_FNO, INDEX].freeze
96
391
 
97
- # Canonical buy transaction label.
98
- BUY = "BUY"
99
- # Canonical sell transaction label.
100
- SELL = "SELL"
101
-
102
- # Cash-and-carry product identifier.
103
- CNC = "CNC"
104
- # Intraday margin product identifier.
105
- INTRA = "INTRADAY"
106
- # Carry-forward margin product identifier.
107
- MARGIN = "MARGIN"
108
- # Cover order product identifier.
109
- CO = "CO"
110
- # Bracket order product identifier.
111
- BO = "BO"
112
- # Margin trading funding identifier.
113
- MTF = "MTF"
114
-
115
- # Limit price order type.
116
- LIMIT = "LIMIT"
117
- # Market order type.
118
- MARKET = "MARKET"
119
- # Stop-loss limit order type.
120
- SL = "STOP_LOSS"
121
- # Stop-loss market order type.
122
- SLM = "STOP_LOSS_MARKET"
123
-
124
- # Good-for-day validity flag.
125
- DAY = "DAY"
126
- # Immediate-or-cancel validity flag.
127
- IOC = "IOC"
392
+ # Canonical labels kept for compatibility with previous SDK versions.
393
+ BUY = TransactionType::BUY
394
+ SELL = TransactionType::SELL
395
+
396
+ CNC = ProductType::CNC
397
+ INTRA = ProductType::INTRADAY
398
+ MARGIN = ProductType::MARGIN
399
+ CO = ProductType::CO
400
+ BO = ProductType::BO
401
+ MTF = ProductType::MTF
402
+
403
+ LIMIT = OrderType::LIMIT
404
+ MARKET = OrderType::MARKET
405
+ SL = OrderType::STOP_LOSS
406
+ SLM = OrderType::STOP_LOSS_MARKET
407
+
408
+ DAY = Validity::DAY
409
+ IOC = Validity::IOC
128
410
 
129
411
  # Download URL for the compact instrument master CSV.
130
412
  COMPACT_CSV_URL = "https://images.dhan.co/api-data/api-scrip-master.csv"
@@ -132,53 +414,76 @@ module DhanHQ
132
414
  DETAILED_CSV_URL = "https://images.dhan.co/api-data/api-scrip-master-detailed.csv"
133
415
 
134
416
  # API route prefixes that require a `client-id` header in addition to the access token.
135
- DATA_API_PREFIXES = %w[
136
- /v2/marketfeed/
137
- /v2/optionchain
138
- /v2/instrument/
417
+ DATA_API_PREFIXES = [
418
+ "/v2/marketfeed/",
419
+ "/v2/optionchain",
420
+ "/v2/instrument/"
139
421
  ].freeze
140
422
 
141
423
  # Mapping of exchange and segment combinations to canonical exchange segment names.
142
- # Used for translating between CSV column values (EXCH_ID, SEGMENT) and API segment names.
143
- # Key format: [exchange, segment] => exchange_segment
144
- # Example: ["NSE", "E"] => "NSE_EQ"
145
424
  SEGMENT_MAP = {
146
- %w[NSE E] => "NSE_EQ",
147
- %w[BSE E] => "BSE_EQ",
148
- %w[NSE D] => "NSE_FNO",
149
- %w[BSE D] => "BSE_FNO",
150
- %w[NSE C] => "NSE_CURRENCY",
151
- %w[BSE C] => "BSE_CURRENCY",
152
- %w[MCX M] => "MCX_COMM",
153
- %w[NSE I] => "IDX_I",
154
- %w[BSE I] => "IDX_I"
425
+ %w[NSE E] => ExchangeSegment::NSE_EQ,
426
+ %w[BSE E] => ExchangeSegment::BSE_EQ,
427
+ %w[NSE D] => ExchangeSegment::NSE_FNO,
428
+ %w[BSE D] => ExchangeSegment::BSE_FNO,
429
+ %w[NSE C] => ExchangeSegment::NSE_CURRENCY,
430
+ %w[BSE C] => ExchangeSegment::BSE_CURRENCY,
431
+ %w[MCX M] => ExchangeSegment::MCX_COMM,
432
+ %w[NSE I] => ExchangeSegment::IDX_I,
433
+ %w[BSE I] => ExchangeSegment::IDX_I
155
434
  }.freeze
156
435
 
157
436
  # Mapping of DhanHQ error codes to SDK error classes for consistent exception handling.
158
437
  DHAN_ERROR_MAPPING = {
159
- "DH-901" => DhanHQ::InvalidAuthenticationError,
160
- "DH-902" => DhanHQ::InvalidAccessError,
161
- "DH-903" => DhanHQ::UserAccountError,
162
- "DH-904" => DhanHQ::RateLimitError,
163
- "DH-905" => DhanHQ::InputExceptionError,
164
- "DH-906" => DhanHQ::OrderError,
165
- "DH-907" => DhanHQ::DataError,
166
- "DH-908" => DhanHQ::InternalServerError,
167
- "DH-1111" => DhanHQ::NoHoldingsError,
168
- "DH-909" => DhanHQ::NetworkError,
169
- "DH-910" => DhanHQ::OtherError,
170
- "800" => DhanHQ::InternalServerError,
171
- "804" => DhanHQ::Error, # Too many instruments
172
- "805" => DhanHQ::RateLimitError, # Too many requests
173
- "806" => DhanHQ::DataError, # Data API not subscribed
174
- "807" => DhanHQ::TokenExpiredError, # Token expired
175
- "808" => DhanHQ::AuthenticationFailedError, # Auth failed
176
- "809" => DhanHQ::InvalidTokenError, # Invalid token
177
- "810" => DhanHQ::InvalidClientIDError, # Invalid Client ID
178
- "811" => DhanHQ::InvalidRequestError, # Invalid expiry date
179
- "812" => DhanHQ::InvalidRequestError, # Invalid date format
180
- "813" => DhanHQ::InvalidRequestError, # Invalid security ID
181
- "814" => DhanHQ::InvalidRequestError # Invalid request
438
+ TradingErrorCode::INVALID_AUTHENTICATION => DhanHQ::InvalidAuthenticationError,
439
+ TradingErrorCode::INVALID_ACCESS => DhanHQ::InvalidAccessError,
440
+ TradingErrorCode::USER_ACCOUNT => DhanHQ::UserAccountError,
441
+ TradingErrorCode::RATE_LIMIT => DhanHQ::RateLimitError,
442
+ TradingErrorCode::INPUT_EXCEPTION => DhanHQ::InputExceptionError,
443
+ TradingErrorCode::ORDER_ERROR => DhanHQ::OrderError,
444
+ TradingErrorCode::DATA_ERROR => DhanHQ::DataError,
445
+ TradingErrorCode::INTERNAL_SERVER_ERROR => DhanHQ::InternalServerError,
446
+ TradingErrorCode::NO_HOLDINGS => DhanHQ::NoHoldingsError,
447
+ TradingErrorCode::NETWORK_ERROR => DhanHQ::NetworkError,
448
+ TradingErrorCode::OTHERS => DhanHQ::OtherError,
449
+ DataErrorCode::INTERNAL_SERVER_ERROR.to_s => DhanHQ::InternalServerError,
450
+ DataErrorCode::INSTRUMENTS_LIMIT.to_s => DhanHQ::Error,
451
+ DataErrorCode::TOO_MANY_REQUESTS.to_s => DhanHQ::RateLimitError,
452
+ DataErrorCode::NOT_SUBSCRIBED.to_s => DhanHQ::DataError,
453
+ DataErrorCode::TOKEN_EXPIRED.to_s => DhanHQ::TokenExpiredError,
454
+ DataErrorCode::AUTH_FAILED.to_s => DhanHQ::AuthenticationFailedError,
455
+ DataErrorCode::INVALID_TOKEN.to_s => DhanHQ::InvalidTokenError,
456
+ DataErrorCode::INVALID_CLIENT_ID.to_s => DhanHQ::InvalidClientIDError,
457
+ DataErrorCode::INVALID_EXPIRY_DATE.to_s => DhanHQ::InvalidRequestError,
458
+ DataErrorCode::INVALID_DATE_FORMAT.to_s => DhanHQ::InvalidRequestError,
459
+ DataErrorCode::INVALID_SECURITY_ID.to_s => DhanHQ::InvalidRequestError,
460
+ DataErrorCode::INVALID_REQUEST.to_s => DhanHQ::InvalidRequestError
182
461
  }.freeze
462
+
463
+ # Validate if a value belongs to a constants module that exposes ALL.
464
+ def self.valid?(module_name, value)
465
+ mod = resolve_module(module_name)
466
+ mod.const_defined?(:ALL) && mod::ALL.include?(value)
467
+ rescue NameError
468
+ false
469
+ end
470
+
471
+ # Get all values for a constants module that exposes ALL.
472
+ def self.all_for(module_name)
473
+ mod = resolve_module(module_name)
474
+ return [] unless mod.const_defined?(:ALL)
475
+
476
+ mod::ALL
477
+ rescue NameError
478
+ []
479
+ end
480
+
481
+ # Resolve symbols, strings, or direct modules under DhanHQ::Constants.
482
+ def self.resolve_module(module_name)
483
+ return module_name if module_name.is_a?(Module)
484
+
485
+ const_get(module_name.to_s)
486
+ end
487
+ private_class_method :resolve_module
183
488
  end
184
489
  end