DhanHQ 2.1.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 (113) hide show
  1. checksums.yaml +7 -0
  2. data/.rspec +3 -0
  3. data/.rubocop.yml +26 -0
  4. data/CHANGELOG.md +20 -0
  5. data/CODE_OF_CONDUCT.md +132 -0
  6. data/GUIDE.md +555 -0
  7. data/LICENSE.txt +21 -0
  8. data/README.md +463 -0
  9. data/README1.md +521 -0
  10. data/Rakefile +12 -0
  11. data/TAGS +10 -0
  12. data/TODO-1.md +14 -0
  13. data/TODO.md +127 -0
  14. data/app/services/live/order_update_guard_support.rb +75 -0
  15. data/app/services/live/order_update_hub.rb +76 -0
  16. data/app/services/live/order_update_persistence_support.rb +68 -0
  17. data/config/initializers/order_update_hub.rb +16 -0
  18. data/diagram.html +184 -0
  19. data/diagram.md +34 -0
  20. data/docs/rails_integration.md +304 -0
  21. data/exe/DhanHQ +4 -0
  22. data/lib/DhanHQ/client.rb +116 -0
  23. data/lib/DhanHQ/config.rb +32 -0
  24. data/lib/DhanHQ/configuration.rb +72 -0
  25. data/lib/DhanHQ/constants.rb +170 -0
  26. data/lib/DhanHQ/contracts/base_contract.rb +15 -0
  27. data/lib/DhanHQ/contracts/historical_data_contract.rb +28 -0
  28. data/lib/DhanHQ/contracts/margin_calculator_contract.rb +19 -0
  29. data/lib/DhanHQ/contracts/modify_order_contract copy.rb +100 -0
  30. data/lib/DhanHQ/contracts/modify_order_contract.rb +22 -0
  31. data/lib/DhanHQ/contracts/option_chain_contract.rb +31 -0
  32. data/lib/DhanHQ/contracts/order_contract.rb +102 -0
  33. data/lib/DhanHQ/contracts/place_order_contract.rb +119 -0
  34. data/lib/DhanHQ/contracts/position_conversion_contract.rb +24 -0
  35. data/lib/DhanHQ/contracts/slice_order_contract.rb +111 -0
  36. data/lib/DhanHQ/core/base_api.rb +105 -0
  37. data/lib/DhanHQ/core/base_model.rb +266 -0
  38. data/lib/DhanHQ/core/base_resource.rb +50 -0
  39. data/lib/DhanHQ/core/error_handler.rb +19 -0
  40. data/lib/DhanHQ/error_object.rb +49 -0
  41. data/lib/DhanHQ/errors.rb +45 -0
  42. data/lib/DhanHQ/helpers/api_helper.rb +17 -0
  43. data/lib/DhanHQ/helpers/attribute_helper.rb +72 -0
  44. data/lib/DhanHQ/helpers/model_helper.rb +7 -0
  45. data/lib/DhanHQ/helpers/request_helper.rb +69 -0
  46. data/lib/DhanHQ/helpers/response_helper.rb +98 -0
  47. data/lib/DhanHQ/helpers/validation_helper.rb +36 -0
  48. data/lib/DhanHQ/json_loader.rb +23 -0
  49. data/lib/DhanHQ/models/edis.rb +58 -0
  50. data/lib/DhanHQ/models/forever_order.rb +85 -0
  51. data/lib/DhanHQ/models/funds.rb +50 -0
  52. data/lib/DhanHQ/models/historical_data.rb +77 -0
  53. data/lib/DhanHQ/models/holding.rb +56 -0
  54. data/lib/DhanHQ/models/kill_switch.rb +49 -0
  55. data/lib/DhanHQ/models/ledger_entry.rb +60 -0
  56. data/lib/DhanHQ/models/margin.rb +54 -0
  57. data/lib/DhanHQ/models/market_feed.rb +41 -0
  58. data/lib/DhanHQ/models/option_chain.rb +79 -0
  59. data/lib/DhanHQ/models/order.rb +239 -0
  60. data/lib/DhanHQ/models/position.rb +60 -0
  61. data/lib/DhanHQ/models/profile.rb +44 -0
  62. data/lib/DhanHQ/models/super_order.rb +69 -0
  63. data/lib/DhanHQ/models/trade.rb +79 -0
  64. data/lib/DhanHQ/rate_limiter.rb +107 -0
  65. data/lib/DhanHQ/requests/optionchain/nifty.json +5 -0
  66. data/lib/DhanHQ/requests/optionchain/nifty_expiries.json +4 -0
  67. data/lib/DhanHQ/requests/orders/create.json +0 -0
  68. data/lib/DhanHQ/resources/edis.rb +44 -0
  69. data/lib/DhanHQ/resources/forever_orders.rb +53 -0
  70. data/lib/DhanHQ/resources/funds.rb +21 -0
  71. data/lib/DhanHQ/resources/historical_data.rb +34 -0
  72. data/lib/DhanHQ/resources/holdings.rb +21 -0
  73. data/lib/DhanHQ/resources/kill_switch.rb +21 -0
  74. data/lib/DhanHQ/resources/margin_calculator.rb +22 -0
  75. data/lib/DhanHQ/resources/market_feed.rb +56 -0
  76. data/lib/DhanHQ/resources/option_chain.rb +31 -0
  77. data/lib/DhanHQ/resources/orders.rb +70 -0
  78. data/lib/DhanHQ/resources/positions.rb +29 -0
  79. data/lib/DhanHQ/resources/profile.rb +25 -0
  80. data/lib/DhanHQ/resources/statements.rb +42 -0
  81. data/lib/DhanHQ/resources/super_orders.rb +46 -0
  82. data/lib/DhanHQ/resources/trades.rb +23 -0
  83. data/lib/DhanHQ/version.rb +6 -0
  84. data/lib/DhanHQ/ws/client.rb +182 -0
  85. data/lib/DhanHQ/ws/cmd_bus.rb +38 -0
  86. data/lib/DhanHQ/ws/connection.rb +240 -0
  87. data/lib/DhanHQ/ws/decoder.rb +83 -0
  88. data/lib/DhanHQ/ws/errors.rb +0 -0
  89. data/lib/DhanHQ/ws/orders/client.rb +59 -0
  90. data/lib/DhanHQ/ws/orders/connection.rb +148 -0
  91. data/lib/DhanHQ/ws/orders.rb +13 -0
  92. data/lib/DhanHQ/ws/packets/depth_delta_packet.rb +20 -0
  93. data/lib/DhanHQ/ws/packets/disconnect_packet.rb +15 -0
  94. data/lib/DhanHQ/ws/packets/full_packet.rb +40 -0
  95. data/lib/DhanHQ/ws/packets/header.rb +23 -0
  96. data/lib/DhanHQ/ws/packets/index_packet.rb +14 -0
  97. data/lib/DhanHQ/ws/packets/market_depth_level.rb +21 -0
  98. data/lib/DhanHQ/ws/packets/market_status_packet.rb +14 -0
  99. data/lib/DhanHQ/ws/packets/oi_packet.rb +15 -0
  100. data/lib/DhanHQ/ws/packets/prev_close_packet.rb +16 -0
  101. data/lib/DhanHQ/ws/packets/quote_packet.rb +26 -0
  102. data/lib/DhanHQ/ws/packets/ticker_packet.rb +16 -0
  103. data/lib/DhanHQ/ws/registry.rb +46 -0
  104. data/lib/DhanHQ/ws/segments.rb +75 -0
  105. data/lib/DhanHQ/ws/singleton_lock.rb +54 -0
  106. data/lib/DhanHQ/ws/sub_state.rb +59 -0
  107. data/lib/DhanHQ/ws/websocket_packet_parser.rb +165 -0
  108. data/lib/DhanHQ/ws.rb +37 -0
  109. data/lib/DhanHQ.rb +135 -0
  110. data/lib/ta/technical_analysis.rb +405 -0
  111. data/sig/DhanHQ.rbs +4 -0
  112. data/watchlist.csv +3 -0
  113. metadata +283 -0
@@ -0,0 +1,79 @@
1
+ # frozen_string_literal: true
2
+
3
+ module DhanHQ
4
+ module Models
5
+ ##
6
+ # Represents a single trade.
7
+ # The API docs show an array of trades from GET /v2/trades/{from-date}/{to-date}/{page}
8
+ class Trade < BaseModel
9
+ # No explicit HTTP_PATH if we rely on the statements resource
10
+ # but we can define it if needed
11
+ HTTP_PATH = "/v2/trades"
12
+
13
+ attributes :dhan_client_id, :order_id, :exchange_order_id, :exchange_trade_id,
14
+ :transaction_type, :exchange_segment, :product_type, :order_type,
15
+ :trading_symbol, :custom_symbol, :security_id, :traded_quantity,
16
+ :traded_price, :isin, :instrument, :sebi_tax, :stt, :brokerage_charges,
17
+ :service_tax, :exchange_transaction_charges, :stamp_duty,
18
+ :create_time, :update_time, :exchange_time, :drv_expiry_date,
19
+ :drv_option_type, :drv_strike_price
20
+
21
+ class << self
22
+ ##
23
+ # Provide a **shared instance** of the `Statements` resource,
24
+ # where we have `trade_history(from_date:, to_date:, page:)`.
25
+ # used for fetching historical trades.
26
+ def resource
27
+ @resource ||= DhanHQ::Resources::Statements.new
28
+ end
29
+
30
+ ##
31
+ # Resource for current day tradebook APIs
32
+ def tradebook_resource
33
+ @tradebook_resource ||= DhanHQ::Resources::Trades.new
34
+ end
35
+
36
+ ##
37
+ # Fetch trades within the given date range and page.
38
+ # GET /v2/trades/{from-date}/{to-date}/{page}
39
+ #
40
+ # @param from_date [String]
41
+ # @param to_date [String]
42
+ # @param page [Integer] Default 0
43
+ # @return [Array<Trade>]
44
+ # Retrieve historical trades
45
+ def history(from_date:, to_date:, page: 0)
46
+ # The resource call returns an Array<Hash>.
47
+ response = resource.trade_history(from_date: from_date, to_date: to_date, page: page)
48
+ return [] unless response.is_a?(Array)
49
+
50
+ response.map { |t| new(t, skip_validation: true) }
51
+ end
52
+
53
+ alias all history
54
+
55
+ # Retrieve current day trades
56
+ def today
57
+ response = tradebook_resource.all
58
+ return [] unless response.is_a?(Array)
59
+
60
+ response.map { |t| new(t, skip_validation: true) }
61
+ end
62
+
63
+ # Fetch trades for a specific order id for the current day
64
+ def find_by_order_id(order_id)
65
+ response = tradebook_resource.find(order_id)
66
+ return nil unless response.is_a?(Hash) || (response.is_a?(Array) && response.any?)
67
+
68
+ data = response.is_a?(Array) ? response.first : response
69
+ new(data, skip_validation: true)
70
+ end
71
+ end
72
+
73
+ # If you want custom validations, you'd set a contract or skip for read-only
74
+ def validation_contract
75
+ nil
76
+ end
77
+ end
78
+ end
79
+ end
@@ -0,0 +1,107 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "concurrent"
4
+
5
+ module DhanHQ
6
+ # Coarse-grained in-memory throttler matching the platform rate limits.
7
+ class RateLimiter
8
+ # Per-interval thresholds keyed by API type.
9
+ RATE_LIMITS = {
10
+ order_api: { per_second: 25, per_minute: 250, per_hour: 1000, per_day: 7000 },
11
+ data_api: { per_second: 5, per_minute: Float::INFINITY, per_hour: Float::INFINITY, per_day: 100_000 },
12
+ quote_api: { per_second: 1, per_minute: Float::INFINITY, per_hour: Float::INFINITY, per_day: Float::INFINITY },
13
+ option_chain: { per_second: 1.0 / 3, per_minute: 20, per_hour: 600, per_day: 4800 },
14
+ non_trading_api: { per_second: 20, per_minute: Float::INFINITY, per_hour: Float::INFINITY,
15
+ per_day: Float::INFINITY }
16
+ }.freeze
17
+
18
+ # Creates a rate limiter for a given API type.
19
+ #
20
+ # @param api_type [Symbol] One of the keys from {RATE_LIMITS}.
21
+ def initialize(api_type)
22
+ @api_type = api_type
23
+ @buckets = Concurrent::Hash.new
24
+ @buckets[:last_request_time] = Time.at(0) if api_type == :option_chain
25
+ initialize_buckets
26
+ start_cleanup_threads
27
+ end
28
+
29
+ # Blocks until the current request is allowed by the configured limits.
30
+ #
31
+ # @return [void]
32
+ def throttle!
33
+ if @api_type == :option_chain
34
+ last_request_time = @buckets[:last_request_time]
35
+
36
+ sleep_time = 4 - (Time.now - last_request_time)
37
+ if sleep_time.positive?
38
+ puts "Sleeping for #{sleep_time.round(2)} seconds due to option_chain rate limit"
39
+ sleep(sleep_time)
40
+ end
41
+
42
+ @buckets[:last_request_time] = Time.now
43
+ return
44
+ end
45
+
46
+ loop do
47
+ break if allow_request?
48
+
49
+ sleep(0.1)
50
+ end
51
+ record_request
52
+ end
53
+
54
+ private
55
+
56
+ # Prepares the counters used for each interval in {RATE_LIMITS}.
57
+ def initialize_buckets
58
+ RATE_LIMITS[@api_type].each_key do |interval|
59
+ @buckets[interval] = Concurrent::AtomicFixnum.new(0)
60
+ end
61
+ end
62
+
63
+ # Determines whether a request can be made without exceeding limits.
64
+ #
65
+ # @return [Boolean]
66
+ def allow_request?
67
+ RATE_LIMITS[@api_type].all? do |interval, limit|
68
+ @buckets[interval].value < limit
69
+ end
70
+ end
71
+
72
+ # Increments the counters for each time window once a request is made.
73
+ def record_request
74
+ RATE_LIMITS[@api_type].each_key do |interval|
75
+ @buckets[interval].increment
76
+ end
77
+ end
78
+
79
+ # Spawns background threads to reset counters after each interval elapses.
80
+ def start_cleanup_threads
81
+ Thread.new do
82
+ loop do
83
+ sleep(1)
84
+ @buckets[:per_second].value = 0
85
+ end
86
+ end
87
+ Thread.new do
88
+ loop do
89
+ sleep(60)
90
+ @buckets[:per_minute].value = 0
91
+ end
92
+ end
93
+ Thread.new do
94
+ loop do
95
+ sleep(3600)
96
+ @buckets[:per_hour].value = 0
97
+ end
98
+ end
99
+ Thread.new do
100
+ loop do
101
+ sleep(86_400)
102
+ @buckets[:per_day].value = 0
103
+ end
104
+ end
105
+ end
106
+ end
107
+ end
@@ -0,0 +1,5 @@
1
+ {
2
+ "UnderlyingScrip": 13,
3
+ "UnderlyingSeg": "IDX_I",
4
+ "Expiry": "2025-02-27"
5
+ }
@@ -0,0 +1,4 @@
1
+ {
2
+ "UnderlyingScrip": 13,
3
+ "UnderlyingSeg": "IDX_I"
4
+ }
File without changes
@@ -0,0 +1,44 @@
1
+ # frozen_string_literal: true
2
+
3
+ module DhanHQ
4
+ module Resources
5
+ # Resource client for electronic DIS flows.
6
+ class Edis < BaseAPI
7
+ # EDIS endpoints are served from the trading API.
8
+ API_TYPE = :order_api
9
+ # Base path for EDIS endpoints.
10
+ HTTP_PATH = "/v2/edis"
11
+
12
+ # Creates a TPIN request form.
13
+ #
14
+ # @param params [Hash]
15
+ # @return [Hash]
16
+ def form(params)
17
+ post("/form", params: params)
18
+ end
19
+
20
+ # Bulk EDIS form submission.
21
+ #
22
+ # @param params [Hash]
23
+ # @return [Hash]
24
+ def bulk_form(params)
25
+ post("/bulkform", params: params)
26
+ end
27
+
28
+ # Generates a TPIN for the client.
29
+ #
30
+ # @return [Hash]
31
+ def tpin
32
+ get("/tpin")
33
+ end
34
+
35
+ # Checks the EDIS status for a given ISIN.
36
+ #
37
+ # @param isin [String]
38
+ # @return [Hash]
39
+ def inquire(isin)
40
+ get("/inquire/#{isin}")
41
+ end
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,53 @@
1
+ # frozen_string_literal: true
2
+
3
+ module DhanHQ
4
+ module Resources
5
+ # Resource client for GTT/forever order management.
6
+ class ForeverOrders < BaseAPI
7
+ # Uses the trading API tier.
8
+ API_TYPE = :order_api
9
+ # Root path for forever order operations.
10
+ HTTP_PATH = "/v2/forever"
11
+
12
+ # Lists all forever orders for the account.
13
+ #
14
+ # @return [Array<Hash>]
15
+ def all
16
+ get("/orders")
17
+ end
18
+
19
+ # Creates a new forever order configuration.
20
+ #
21
+ # @param params [Hash]
22
+ # @return [Hash]
23
+ def create(params)
24
+ post("/orders", params: params)
25
+ end
26
+
27
+ # Fetches a forever order by identifier.
28
+ #
29
+ # @param order_id [String]
30
+ # @return [Hash]
31
+ def find(order_id)
32
+ get("/orders/#{order_id}")
33
+ end
34
+
35
+ # Updates a forever order.
36
+ #
37
+ # @param order_id [String]
38
+ # @param params [Hash]
39
+ # @return [Hash]
40
+ def update(order_id, params)
41
+ put("/orders/#{order_id}", params: params)
42
+ end
43
+
44
+ # Cancels a forever order.
45
+ #
46
+ # @param order_id [String]
47
+ # @return [Hash]
48
+ def cancel(order_id)
49
+ delete("/orders/#{order_id}")
50
+ end
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ module DhanHQ
4
+ module Resources
5
+ # Resource client that exposes funds and limits endpoints.
6
+ class Funds < BaseAPI
7
+ # Funds data comes from the trading API tier.
8
+ API_TYPE = :order_api
9
+ # Base path for fund limit endpoints.
10
+ HTTP_PATH = "/v2/fundlimit"
11
+
12
+ ##
13
+ # Fetch fund limit details.
14
+ #
15
+ # @return [Hash] API response containing fund details.
16
+ def fetch
17
+ get("")
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,34 @@
1
+ # frozen_string_literal: true
2
+
3
+ module DhanHQ
4
+ module Resources
5
+ ##
6
+ # Resource class for fetching Daily & Intraday historical data.
7
+ # Based on the official docs:
8
+ # - POST /v2/charts/historical (daily timeframe)
9
+ # - POST /v2/charts/intraday (minute timeframe)
10
+ #
11
+ class HistoricalData < BaseAPI
12
+ API_TYPE = :data_api # Because we are fetching market data
13
+ HTTP_PATH = "/v2/charts" # The base path for historical endpoints
14
+
15
+ ##
16
+ # POST /v2/charts/historical
17
+ # "Daily Historical Data"
18
+ # @param params [Hash]
19
+ # @return [Hash] The parsed API response containing arrays of open, high, low, close, volume, timestamp
20
+ def daily(params)
21
+ post("/historical", params: params)
22
+ end
23
+
24
+ ##
25
+ # POST /v2/charts/intraday
26
+ # "Intraday Historical Data"
27
+ # @param params [Hash]
28
+ # @return [Hash] The parsed API response containing arrays for candle data
29
+ def intraday(params)
30
+ post("/intraday", params: params)
31
+ end
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ module DhanHQ
4
+ module Resources
5
+ # Resource client exposing portfolio holdings.
6
+ class Holdings < BaseAPI
7
+ # Holdings are exposed via the trading API.
8
+ API_TYPE = :order_api
9
+ # Base path for holdings queries.
10
+ HTTP_PATH = "/v2/holdings"
11
+
12
+ ##
13
+ # Fetch all holdings.
14
+ #
15
+ # @return [Array<Hash>] API response containing holdings data.
16
+ def all
17
+ get("")
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ module DhanHQ
4
+ module Resources
5
+ # Resource client to control the trading kill switch feature.
6
+ class KillSwitch < BaseAPI
7
+ # Kill switch operations execute on the trading API tier.
8
+ API_TYPE = :order_api
9
+ # Base path for kill switch operations.
10
+ HTTP_PATH = "/v2/killswitch"
11
+
12
+ # Enables or disables the kill switch.
13
+ #
14
+ # @param params [Hash]
15
+ # @return [Hash]
16
+ def update(params)
17
+ post("", params: params)
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ module DhanHQ
4
+ module Resources
5
+ # Resource client for invoking the margin calculator endpoint.
6
+ class MarginCalculator < BaseAPI
7
+ # Calculator results are served via the trading API.
8
+ API_TYPE = :order_api
9
+ # Base path for the calculator endpoint.
10
+ HTTP_PATH = "/v2/margincalculator"
11
+
12
+ ##
13
+ # Calculate margin requirements for an order.
14
+ #
15
+ # @param params [Hash] Request parameters for margin calculation.
16
+ # @return [Hash] API response containing margin details.
17
+ def calculate(params)
18
+ post("", params: params)
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,56 @@
1
+ # frozen_string_literal: true
2
+
3
+ module DhanHQ
4
+ module Resources
5
+ # Resource client for fetching on-demand market data snapshots.
6
+ class MarketFeed < BaseAPI
7
+ # Market feed requests hit the data API tier.
8
+ API_TYPE = :data_api
9
+ # Root path for market feed endpoints.
10
+ HTTP_PATH = "/v2"
11
+
12
+ ##
13
+ # POST /v2/marketfeed/ltp
14
+ # Returns the LTP (Last Traded Price) of up to 1000 instruments.
15
+ #
16
+ # @param params [Hash] Example:
17
+ # {
18
+ # "NSE_EQ": [11536, 3456],
19
+ # "NSE_FNO": [49081, 49082]
20
+ # }
21
+ # @return [HashWithIndifferentAccess]
22
+ def ltp(params)
23
+ post("/marketfeed/ltp", params: params)
24
+ end
25
+
26
+ ##
27
+ # POST /v2/marketfeed/ohlc
28
+ # Returns open-high-low-close for up to 1000 instruments.
29
+ #
30
+ # @param params [Hash]
31
+ # @return [HashWithIndifferentAccess]
32
+ def ohlc(params)
33
+ post("/marketfeed/ohlc", params: params)
34
+ end
35
+
36
+ ##
37
+ # POST /v2/marketfeed/quote
38
+ # Returns market depth, OI, and other details for up to 1000 instruments.
39
+ #
40
+ # @param params [Hash]
41
+ # @return [HashWithIndifferentAccess]
42
+ def quote(params)
43
+ quote_resource.post("/marketfeed/quote", params: params)
44
+ end
45
+
46
+ private
47
+
48
+ # Lazily builds a `:quote_api` scoped client for quote depth requests.
49
+ #
50
+ # @return [MarketFeed]
51
+ def quote_resource
52
+ @quote_resource ||= self.class.new(api_type: :quote_api)
53
+ end
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ module DhanHQ
4
+ module Resources
5
+ # Resource client for querying option chain data APIs.
6
+ class OptionChain < BaseAPI
7
+ # Option chain queries have bespoke rate limits, hence their own API type.
8
+ API_TYPE = :option_chain
9
+ # Base path for option chain endpoints.
10
+ HTTP_PATH = "/v2/optionchain"
11
+
12
+ ##
13
+ # Fetch option chain data based on provided parameters.
14
+ #
15
+ # @param params [Hash] Query parameters for the request.
16
+ # @return [Hash] API response containing option chain data.
17
+ def fetch(params)
18
+ post("", params: params)
19
+ end
20
+
21
+ ##
22
+ # Fetch expiry dates list based on provided parameters.
23
+ #
24
+ # @param params [Hash] Query parameters for the request.
25
+ # @return [Hash] API response containing option chain data.
26
+ def expirylist(params)
27
+ post("/expirylist", params: params)
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,70 @@
1
+ # frozen_string_literal: true
2
+
3
+ module DhanHQ
4
+ # REST API wrappers grouped by resource type.
5
+ module Resources
6
+ # Resource client for managing equity and F&O orders.
7
+ class Orders < BaseAPI
8
+ # Orders are routed through the trading API tier.
9
+ API_TYPE = :order_api
10
+ # Base path for order endpoints.
11
+ HTTP_PATH = "/v2/orders"
12
+
13
+ # Retrieve all orders for the current trading day.
14
+ #
15
+ # @return [Array<Hash>]
16
+ def all
17
+ get("")
18
+ end
19
+
20
+ # Places a new order using the provided payload.
21
+ #
22
+ # @param params [Hash]
23
+ # @return [Hash]
24
+ def create(params)
25
+ post("", params: params)
26
+ end
27
+
28
+ # Fetches a single order by broker order id.
29
+ #
30
+ # @param order_id [String]
31
+ # @return [Hash]
32
+ def find(order_id)
33
+ get("/#{order_id}")
34
+ end
35
+
36
+ # Modifies an existing order.
37
+ #
38
+ # @param order_id [String]
39
+ # @param params [Hash]
40
+ # @return [Hash]
41
+ def update(order_id, params)
42
+ put("/#{order_id}", params: params)
43
+ end
44
+
45
+ # Cancels an existing order.
46
+ #
47
+ # @param order_id [String]
48
+ # @return [Hash]
49
+ def cancel(order_id)
50
+ delete("/#{order_id}")
51
+ end
52
+
53
+ # Places a slicing order request.
54
+ #
55
+ # @param params [Hash]
56
+ # @return [Hash]
57
+ def slicing(params)
58
+ post("/slicing", params: params)
59
+ end
60
+
61
+ # Retrieve an order by client-supplied correlation id.
62
+ #
63
+ # @param correlation_id [String]
64
+ # @return [Hash]
65
+ def by_correlation(correlation_id)
66
+ get("/external/#{correlation_id}")
67
+ end
68
+ end
69
+ end
70
+ end
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ module DhanHQ
4
+ module Resources
5
+ # Resource client managing intraday and carry-forward positions.
6
+ class Positions < BaseAPI
7
+ # Position endpoints are exposed via the trading API.
8
+ API_TYPE = :order_api
9
+ # Base path for position management endpoints.
10
+ HTTP_PATH = "/v2/positions"
11
+
12
+ ##
13
+ # Fetch all open positions for the day.
14
+ #
15
+ # @return [Array<Hash>] API response containing position data.
16
+ def all
17
+ get("")
18
+ end
19
+
20
+ # Converts a position between eligible product types.
21
+ #
22
+ # @param params [Hash]
23
+ # @return [Hash]
24
+ def convert(params)
25
+ post("/convert", params: params)
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ module DhanHQ
4
+ module Resources
5
+ ##
6
+ # Provides access to the user profile endpoint.
7
+ #
8
+ # The endpoint is a simple GET request to `/v2/profile` that returns
9
+ # account level metadata (token validity, active segments, etc.).
10
+ class Profile < BaseAPI
11
+ # Profile metadata is served from the non-trading API tier.
12
+ API_TYPE = :non_trading_api
13
+ # Base path for profile lookups.
14
+ HTTP_PATH = "/v2/profile"
15
+
16
+ ##
17
+ # Fetch the authenticated user's profile information.
18
+ #
19
+ # @return [Hash] Parsed response from the profile endpoint.
20
+ def fetch
21
+ get("")
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,42 @@
1
+ # frozen_string_literal: true
2
+
3
+ module DhanHQ
4
+ module Resources
5
+ ##
6
+ # Provides methods to retrieve Ledger and Trade History.
7
+ #
8
+ # GET /v2/ledger?from-date=YYYY-MM-DD&to-date=YYYY-MM-DD
9
+ # GET /v2/trades/{from-date}/{to-date}/{page}
10
+ #
11
+ class Statements < BaseAPI
12
+ # Statement history is fetched from the non-trading API tier.
13
+ API_TYPE = :non_trading_api
14
+ # Base path for ledger and trade history.
15
+ HTTP_PATH = "/v2"
16
+
17
+ ##
18
+ # GET /v2/ledger?from-date=YYYY-MM-DD&to-date=YYYY-MM-DD
19
+ # @param from_date [String] e.g. "2023-01-01"
20
+ # @param to_date [String] e.g. "2023-01-31"
21
+ # @return [Array<Hash>] An array of ledger entries
22
+ def ledger(from_date:, to_date:)
23
+ # Because the docs say "from-date" & "to-date" (with dashes),
24
+ # pass them as snake case or match them exactly:
25
+ get("/ledger", params: { "from-date": from_date, "to-date": to_date })
26
+ end
27
+
28
+ ##
29
+ # GET /v2/trades/{from-date}/{to-date}/{page}
30
+ # @param from_date [String]
31
+ # @param to_date [String]
32
+ # @param page [Integer] Defaults to 0
33
+ # @return [Array<Hash>] An array of trades
34
+ #
35
+ def trade_history(from_date:, to_date:, page: 0)
36
+ # docs show this path style:
37
+ # /v2/trades/{from-date}/{to-date}/{page}
38
+ get("/trades/#{from_date}/#{to_date}/#{page}")
39
+ end
40
+ end
41
+ end
42
+ end