DhanHQ 2.1.8 → 2.1.10

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.
@@ -3,72 +3,189 @@
3
3
  module DhanHQ
4
4
  module Models
5
5
  ##
6
- # Model class for fetching Daily & Intraday data
7
- # The default response is a Hash with arrays of "open", "high", "low", etc.
6
+ # Model for fetching historical candle data (OHLC) for desired instruments across segments and exchanges.
7
+ #
8
+ # This API provides historical price data in the form of candlestick data with timestamp, open, high, low,
9
+ # close, and volume information. Data is available in two formats:
10
+ # - **Daily**: Daily candle data available back to the date of instrument inception
11
+ # - **Intraday**: Minute-level candle data (1, 5, 15, 25, 60 minutes) available for the last 5 years
12
+ #
13
+ # @example Fetch daily historical data
14
+ # data = DhanHQ::Models::HistoricalData.daily(
15
+ # security_id: "1333",
16
+ # exchange_segment: "NSE_EQ",
17
+ # instrument: "EQUITY",
18
+ # from_date: "2022-01-08",
19
+ # to_date: "2022-02-08"
20
+ # )
21
+ # puts "First day close: #{data[:close].first}"
22
+ #
23
+ # @example Fetch intraday historical data
24
+ # data = DhanHQ::Models::HistoricalData.intraday(
25
+ # security_id: "1333",
26
+ # exchange_segment: "NSE_EQ",
27
+ # instrument: "EQUITY",
28
+ # interval: "15",
29
+ # from_date: "2024-09-11",
30
+ # to_date: "2024-09-15"
31
+ # )
32
+ # puts "Total candles: #{data[:open].size}"
33
+ #
34
+ # @note For intraday data, only 90 days of data can be polled at once for any time interval.
35
+ # It is recommended to store this data locally for day-to-day analysis.
8
36
  #
9
37
  class HistoricalData < BaseModel
10
- # Typically, we won't define a single resource path,
11
- # because we call "daily" or "intraday" endpoints specifically.
12
- # So let's rely on the resource call directly.
38
+ # Base path for historical data endpoints.
13
39
  HTTP_PATH = "/v2/charts"
14
40
 
15
- # If you want typed attributes, you could define them,
16
- # but the endpoints return arrays. We'll keep it raw.
17
- # e.g. attributes :open, :high, :low, :close, :volume, :timestamp
18
-
19
41
  class << self
20
42
  ##
21
- # Provide a **shared instance** of the `HistoricalData` resource
43
+ # Provides a shared instance of the HistoricalData resource.
22
44
  #
23
- # @return [DhanHQ::Resources::HistoricalData]
45
+ # @return [DhanHQ::Resources::HistoricalData] The HistoricalData resource client instance
24
46
  def resource
25
47
  @resource ||= DhanHQ::Resources::HistoricalData.new
26
48
  end
27
49
 
28
50
  ##
29
- # Daily historical data
30
- # @param params [Hash] The request parameters, e.g.:
31
- # {
51
+ # Fetches daily OHLC (Open, High, Low, Close) and volume data for the desired instrument.
52
+ #
53
+ # Retrieves daily candle data for any scrip available back to the date of its inception.
54
+ # The data is returned as arrays where each index corresponds to a single trading day.
55
+ #
56
+ # @param params [Hash{Symbol => String, Integer, Boolean}] Request parameters
57
+ # @option params [String] :security_id (required) Exchange standard ID for each scrip
58
+ # @option params [String] :exchange_segment (required) Exchange and segment for which data is to be fetched.
59
+ # Valid values: See {DhanHQ::Constants::EXCHANGE_SEGMENTS}
60
+ # @option params [String] :instrument (required) Instrument type of the scrip.
61
+ # Valid values: See {DhanHQ::Constants::INSTRUMENTS}
62
+ # @option params [Integer] :expiry_code (optional) Expiry of the instruments in case of derivatives.
63
+ # Valid values: 0, 1, 2
64
+ # @option params [Boolean] :oi (optional) Include Open Interest data for Futures & Options.
65
+ # Default: false
66
+ # @option params [String] :from_date (required) Start date of the desired range in YYYY-MM-DD format
67
+ # @option params [String] :to_date (required) End date of the desired range (non-inclusive) in YYYY-MM-DD format
68
+ #
69
+ # @return [HashWithIndifferentAccess{Symbol => Array<Float, Integer>}] Historical data hash containing:
70
+ # - **:open** [Array<Float>] Open prices for each trading day
71
+ # - **:high** [Array<Float>] High prices for each trading day
72
+ # - **:low** [Array<Float>] Low prices for each trading day
73
+ # - **:close** [Array<Float>] Close prices for each trading day
74
+ # - **:volume** [Array<Integer>] Volume traded for each trading day
75
+ # - **:timestamp** [Array<Integer>] Epoch timestamps (Unix time in seconds) for each trading day
76
+ # - **:open_interest** [Array<Float>] Open interest values (only included if `oi: true` was specified)
77
+ #
78
+ # @example Fetch daily data for equity
79
+ # data = DhanHQ::Models::HistoricalData.daily(
32
80
  # security_id: "1333",
33
81
  # exchange_segment: "NSE_EQ",
34
82
  # instrument: "EQUITY",
35
- # expiry_code: 0,
36
83
  # from_date: "2022-01-08",
37
84
  # to_date: "2022-02-08"
38
- # }
39
- # @return [HashWithIndifferentAccess]
40
- # {
41
- # open: [...], high: [...], low: [...], close: [...],
42
- # volume: [...], timestamp: [...]
43
- # }
85
+ # )
86
+ # data[:open].size # => Number of trading days
87
+ # data[:close].first # => First day's close price
88
+ #
89
+ # @example Fetch daily data with open interest for futures
90
+ # data = DhanHQ::Models::HistoricalData.daily(
91
+ # security_id: "13",
92
+ # exchange_segment: "NSE_FNO",
93
+ # instrument: "FUTIDX",
94
+ # expiry_code: 0,
95
+ # oi: true,
96
+ # from_date: "2024-01-01",
97
+ # to_date: "2024-01-31"
98
+ # )
99
+ # puts "OI data available: #{data.key?(:open_interest)}"
100
+ #
101
+ # @raise [DhanHQ::ValidationError] If validation fails for any parameter
44
102
  def daily(params)
45
103
  validate_params!(params, DhanHQ::Contracts::HistoricalDataContract)
46
- # You can rename the keys from snake_case to something if needed
47
104
  resource.daily(params)
48
- # return as a raw hash or transform further
49
105
  end
50
106
 
51
107
  ##
52
- # Intraday historical data
53
- # @param params [Hash], e.g.:
54
- # {
108
+ # Fetches intraday OHLC (Open, High, Low, Close) and volume data for minute-level timeframes.
109
+ #
110
+ # Retrieves minute-level candle data (1, 5, 15, 25, or 60 minutes) for desired instruments.
111
+ # Data is available for the last 5 years for all exchanges and segments for all active instruments.
112
+ #
113
+ # **Important**: Only 90 days of data can be polled at once for any of the time intervals.
114
+ # It is recommended that you store this data locally for day-to-day analysis.
115
+ #
116
+ # @param params [Hash{Symbol => String, Integer, Boolean}] Request parameters
117
+ # @option params [String] :security_id (required) Exchange standard ID for each scrip
118
+ # @option params [String] :exchange_segment (required) Exchange and segment for which data is to be fetched.
119
+ # Valid values: See {DhanHQ::Constants::EXCHANGE_SEGMENTS}
120
+ # @option params [String] :instrument (required) Instrument type of the scrip.
121
+ # Valid values: See {DhanHQ::Constants::INSTRUMENTS}
122
+ # @option params [String] :interval (required) Minute intervals for the timeframe.
123
+ # Valid values: "1", "5", "15", "25", "60"
124
+ # @option params [Integer] :expiry_code (optional) Expiry of the instruments in case of derivatives.
125
+ # Valid values: 0, 1, 2
126
+ # @option params [Boolean] :oi (optional) Include Open Interest data for Futures & Options.
127
+ # Default: false
128
+ # @option params [String] :from_date (required) Start date of the desired range.
129
+ # Format: YYYY-MM-DD or YYYY-MM-DD HH:MM:SS (e.g., "2024-09-11" or "2024-09-11 09:30:00")
130
+ # @option params [String] :to_date (required) End date of the desired range.
131
+ # Format: YYYY-MM-DD or YYYY-MM-DD HH:MM:SS (e.g., "2024-09-15" or "2024-09-15 13:00:00")
132
+ #
133
+ # @return [HashWithIndifferentAccess{Symbol => Array<Float, Integer>}] Historical data hash containing:
134
+ # - **:open** [Array<Float>] Open prices for each timeframe
135
+ # - **:high** [Array<Float>] High prices for each timeframe
136
+ # - **:low** [Array<Float>] Low prices for each timeframe
137
+ # - **:close** [Array<Float>] Close prices for each timeframe
138
+ # - **:volume** [Array<Integer>] Volume traded for each timeframe
139
+ # - **:timestamp** [Array<Integer>] Epoch timestamps (Unix time in seconds) for each timeframe
140
+ # - **:open_interest** [Array<Float>] Open interest values (only included if `oi: true` was specified)
141
+ #
142
+ # @example Fetch 15-minute intraday data
143
+ # data = DhanHQ::Models::HistoricalData.intraday(
55
144
  # security_id: "1333",
56
145
  # exchange_segment: "NSE_EQ",
57
146
  # instrument: "EQUITY",
58
147
  # interval: "15",
59
148
  # from_date: "2024-09-11",
60
149
  # to_date: "2024-09-15"
61
- # }
62
- # @return [HashWithIndifferentAccess]
63
- # { open: [...], high: [...], low: [...], close: [...],
64
- # volume: [...], timestamp: [...] }
150
+ # )
151
+ # puts "Total 15-min candles: #{data[:open].size}"
152
+ #
153
+ # @example Fetch 1-minute data with specific time range
154
+ # data = DhanHQ::Models::HistoricalData.intraday(
155
+ # security_id: "1333",
156
+ # exchange_segment: "NSE_EQ",
157
+ # instrument: "EQUITY",
158
+ # interval: "1",
159
+ # from_date: "2024-09-11 09:30:00",
160
+ # to_date: "2024-09-11 15:30:00"
161
+ # )
162
+ # # Returns 1-minute candles for the specified time range
163
+ #
164
+ # @example Fetch intraday data for futures with open interest
165
+ # data = DhanHQ::Models::HistoricalData.intraday(
166
+ # security_id: "13",
167
+ # exchange_segment: "NSE_FNO",
168
+ # instrument: "FUTIDX",
169
+ # interval: "5",
170
+ # expiry_code: 0,
171
+ # oi: true,
172
+ # from_date: "2024-01-01",
173
+ # to_date: "2024-01-31"
174
+ # )
175
+ #
176
+ # @note Maximum 90 days of data can be fetched in a single request. For longer periods,
177
+ # make multiple requests or store data locally for analysis.
178
+ # @raise [DhanHQ::ValidationError] If validation fails for any parameter
65
179
  def intraday(params)
66
180
  validate_params!(params, DhanHQ::Contracts::HistoricalDataContract)
67
181
  resource.intraday(params)
68
182
  end
69
183
  end
70
184
 
71
- # For a read-only type of data, we might skip validations or specify a contract if needed
185
+ ##
186
+ # HistoricalData objects are read-only, so no validation contract is applied.
187
+ #
188
+ # @return [nil] No validation contract needed for read-only data
72
189
  def validation_contract
73
190
  nil
74
191
  end
@@ -2,7 +2,30 @@
2
2
 
3
3
  module DhanHQ
4
4
  module Models
5
+ ##
5
6
  # Model representing a single portfolio holding.
7
+ #
8
+ # Holdings represent all stocks/securities bought or sold in previous trading sessions.
9
+ # This includes both T1 (pending delivery) and delivered quantities in your demat account.
10
+ # Each holding provides information about quantities, average cost price, and availability
11
+ # for transactions.
12
+ #
13
+ # @example Fetch all holdings
14
+ # holdings = DhanHQ::Models::Holding.all
15
+ # holdings.each do |holding|
16
+ # puts "#{holding.trading_symbol}: #{holding.total_qty} shares @ ₹#{holding.avg_cost_price}"
17
+ # end
18
+ #
19
+ # @example Find a specific holding by symbol
20
+ # holdings = DhanHQ::Models::Holding.all
21
+ # hdfc = holdings.find { |h| h.trading_symbol == "HDFC" }
22
+ # puts "Available quantity: #{hdfc.available_qty}" if hdfc
23
+ #
24
+ # @example Calculate portfolio value
25
+ # holdings = DhanHQ::Models::Holding.all
26
+ # total_value = holdings.sum { |h| h.total_qty * h.avg_cost_price }
27
+ # puts "Portfolio value: ₹#{total_value}"
28
+ #
6
29
  class Holding < BaseModel
7
30
  # Base path used when retrieving holdings.
8
31
  HTTP_PATH = "/v2/holdings"
@@ -12,17 +35,51 @@ module DhanHQ
12
35
 
13
36
  class << self
14
37
  ##
15
- # Provides a **shared instance** of the `Holdings` resource.
38
+ # Provides a shared instance of the Holdings resource.
16
39
  #
17
- # @return [DhanHQ::Resources::Holdings]
40
+ # @return [DhanHQ::Resources::Holdings] The Holdings resource client instance
18
41
  def resource
19
42
  @resource ||= DhanHQ::Resources::Holdings.new
20
43
  end
21
44
 
22
45
  ##
23
- # Fetch all holdings.
46
+ # Retrieves all holdings bought/sold in previous trading sessions.
24
47
  #
25
- # @return [Array<Holding>]
48
+ # Fetches all T1 (pending delivery) and delivered quantities from your portfolio.
49
+ # Includes information about available quantities for transaction, collateral quantities,
50
+ # and average cost prices for each holding.
51
+ #
52
+ # @return [Array<Holding>] Array of Holding objects. Returns empty array if no holdings exist.
53
+ # Each Holding object contains (keys normalized to snake_case):
54
+ # - **:exchange** [String] Exchange identifier (e.g., "ALL", "NSE", "BSE")
55
+ # - **:trading_symbol** [String] Trading symbol of the security
56
+ # - **:security_id** [String] Exchange standard ID for each scrip
57
+ # - **:isin** [String] Universal standard ID for each scrip (International Securities Identification Number)
58
+ # - **:total_qty** [Integer] Total quantity of the holding
59
+ # - **:dp_qty** [Integer] Quantity delivered in demat account
60
+ # - **:t1_qty** [Integer] Quantity pending delivery in demat account (T+1 settlement)
61
+ # - **:available_qty** [Integer] Quantity available for transaction
62
+ # - **:collateral_qty** [Integer] Quantity placed as collateral with broker
63
+ # - **:avg_cost_price** [Float] Average buy price of total quantity
64
+ #
65
+ # @example Fetch all holdings
66
+ # holdings = DhanHQ::Models::Holding.all
67
+ # holdings.each do |holding|
68
+ # puts "#{holding.trading_symbol}: #{holding.total_qty} @ ₹#{holding.avg_cost_price}"
69
+ # end
70
+ #
71
+ # @example Filter holdings by available quantity
72
+ # holdings = DhanHQ::Models::Holding.all
73
+ # sellable = holdings.select { |h| h.available_qty > 0 }
74
+ # puts "You can sell #{sellable.size} holdings"
75
+ #
76
+ # @example Calculate total investment
77
+ # holdings = DhanHQ::Models::Holding.all
78
+ # total_investment = holdings.sum { |h| h.total_qty * h.avg_cost_price }
79
+ # puts "Total investment: ₹#{total_investment}"
80
+ #
81
+ # @note This is a GET request with no body parameters required
82
+ # @note Returns empty array if no holdings exist or if {DhanHQ::NoHoldingsError} is raised
26
83
  def all
27
84
  response = resource.all
28
85
  return [] unless response.is_a?(Array)
@@ -34,9 +91,28 @@ module DhanHQ
34
91
  end
35
92
 
36
93
  ##
37
- # Convert model attributes to a hash.
94
+ # Converts the Holding model attributes to a hash representation.
95
+ #
96
+ # Useful for serialization, logging, or passing holding data to other methods.
97
+ #
98
+ # @return [Hash{Symbol => String, Integer, Float}] Hash representation of the Holding model containing:
99
+ # - **:exchange** [String] Exchange identifier
100
+ # - **:trading_symbol** [String] Trading symbol
101
+ # - **:security_id** [String] Security ID
102
+ # - **:isin** [String] ISIN code
103
+ # - **:total_qty** [Integer] Total quantity
104
+ # - **:dp_qty** [Integer] Delivered quantity in demat
105
+ # - **:t1_qty** [Integer] T+1 pending delivery quantity
106
+ # - **:available_qty** [Integer] Available quantity for transaction
107
+ # - **:collateral_qty** [Integer] Quantity placed as collateral
108
+ # - **:avg_cost_price** [Float] Average cost price
109
+ #
110
+ # @example Convert holding to hash
111
+ # holding = DhanHQ::Models::Holding.all.first
112
+ # holding_hash = holding.to_h
113
+ # puts holding_hash[:trading_symbol] # => "HDFC"
114
+ # puts holding_hash[:avg_cost_price] # => 2655.0
38
115
  #
39
- # @return [Hash] Hash representation of the Holding model.
40
116
  def to_h
41
117
  {
42
118
  exchange: exchange,
@@ -2,45 +2,147 @@
2
2
 
3
3
  module DhanHQ
4
4
  module Models
5
- # Model helper to toggle the trading kill switch.
5
+ ##
6
+ # Model for managing the trading kill switch feature.
7
+ #
8
+ # The Kill Switch API lets you activate or deactivate the kill switch for your account,
9
+ # which will disable trading for the current trading day. This is a safety feature that
10
+ # can be used to prevent further trading activity when needed.
11
+ #
12
+ # @note **Important**: Before activating the kill switch, you must ensure that all your
13
+ # positions are closed and there are no pending orders in your account. The kill switch
14
+ # will not activate if there are open positions or pending orders.
15
+ #
16
+ # @example Activate kill switch
17
+ # response = DhanHQ::Models::KillSwitch.activate
18
+ # puts response[:kill_switch_status]
19
+ # # => "Kill Switch has been successfully activated"
20
+ #
21
+ # @example Deactivate kill switch
22
+ # response = DhanHQ::Models::KillSwitch.deactivate
23
+ # puts response[:kill_switch_status]
24
+ #
25
+ # @example Update kill switch with custom status
26
+ # response = DhanHQ::Models::KillSwitch.update("ACTIVATE")
27
+ # if response[:kill_switch_status].include?("successfully")
28
+ # puts "Kill switch activated"
29
+ # end
30
+ #
6
31
  class KillSwitch < BaseModel
7
32
  # Base path used by the kill switch resource.
8
33
  HTTP_PATH = "/v2/killswitch"
9
34
 
10
35
  class << self
11
- # Shared resource for kill switch operations.
36
+ ##
37
+ # Provides a shared instance of the KillSwitch resource.
12
38
  #
13
- # @return [DhanHQ::Resources::KillSwitch]
39
+ # @return [DhanHQ::Resources::KillSwitch] The KillSwitch resource client instance
14
40
  def resource
15
41
  @resource ||= DhanHQ::Resources::KillSwitch.new
16
42
  end
17
43
 
18
- # Updates the kill switch status.
44
+ ##
45
+ # Updates the kill switch status with the specified action.
46
+ #
47
+ # Allows you to set the kill switch to either "ACTIVATE" or "DEACTIVATE" state.
48
+ # The status is passed as a query parameter to the API endpoint.
49
+ #
50
+ # @param status [String] Kill switch action to perform.
51
+ # Valid values: "ACTIVATE", "DEACTIVATE"
52
+ #
53
+ # @return [Hash{Symbol => String}] Response hash containing kill switch operation result.
54
+ # Response structure (keys normalized to snake_case):
55
+ # - **:dhan_client_id** [String] User-specific identification generated by Dhan
56
+ # - **:kill_switch_status** [String] Status message indicating the result of the operation.
57
+ # For activation: "Kill Switch has been successfully activated"
58
+ # For deactivation: Status message for deactivation
59
+ #
60
+ # @example Update kill switch status
61
+ # response = DhanHQ::Models::KillSwitch.update("ACTIVATE")
62
+ # puts response[:kill_switch_status]
63
+ #
64
+ # @example Check if activation was successful
65
+ # response = DhanHQ::Models::KillSwitch.update("ACTIVATE")
66
+ # if response[:kill_switch_status].include?("successfully")
67
+ # puts "Kill switch is now active"
68
+ # end
19
69
  #
20
- # @param status [String]
21
- # @return [Hash]
22
70
  def update(status)
23
71
  resource.update(kill_switch_status: status)
24
72
  end
25
73
 
26
- # Activates the kill switch for the account.
74
+ ##
75
+ # Activates the kill switch for your trading account.
76
+ #
77
+ # Disables trading for the current trading day. All trading operations will be blocked
78
+ # until the kill switch is deactivated. This is a safety feature to prevent further
79
+ # trading activity.
80
+ #
81
+ # @note **Prerequisites**: All positions must be closed and there must be no pending
82
+ # orders in your account before activation. The API will reject the activation request
83
+ # if these conditions are not met.
84
+ #
85
+ # @return [Hash{Symbol => String}] Response hash containing kill switch activation result.
86
+ # Response structure (keys normalized to snake_case):
87
+ # - **:dhan_client_id** [String] User-specific identification generated by Dhan
88
+ # - **:kill_switch_status** [String] Status message, typically:
89
+ # "Kill Switch has been successfully activated"
90
+ #
91
+ # @example Activate kill switch
92
+ # response = DhanHQ::Models::KillSwitch.activate
93
+ # puts response[:kill_switch_status]
94
+ # # => "Kill Switch has been successfully activated"
95
+ #
96
+ # @example Activate with error handling
97
+ # begin
98
+ # response = DhanHQ::Models::KillSwitch.activate
99
+ # if response[:kill_switch_status].include?("successfully")
100
+ # puts "✓ Kill switch activated - trading disabled"
101
+ # end
102
+ # rescue => e
103
+ # puts "Failed to activate kill switch: #{e.message}"
104
+ # puts "Ensure all positions are closed and no orders are pending"
105
+ # end
27
106
  #
28
- # @return [Hash]
29
107
  def activate
30
108
  update("ACTIVATE")
31
109
  end
32
110
 
33
- # Deactivates the kill switch for the account.
111
+ ##
112
+ # Deactivates the kill switch for your trading account.
113
+ #
114
+ # Re-enables trading for your account. After deactivation, you can resume placing
115
+ # orders and executing trades normally.
116
+ #
117
+ # @return [Hash{Symbol => String}] Response hash containing kill switch deactivation result.
118
+ # Response structure (keys normalized to snake_case):
119
+ # - **:dhan_client_id** [String] User-specific identification generated by Dhan
120
+ # - **:kill_switch_status** [String] Status message indicating deactivation result
121
+ #
122
+ # @example Deactivate kill switch
123
+ # response = DhanHQ::Models::KillSwitch.deactivate
124
+ # puts response[:kill_switch_status]
125
+ #
126
+ # @example Re-enable trading
127
+ # response = DhanHQ::Models::KillSwitch.deactivate
128
+ # if response[:kill_switch_status].include?("successfully")
129
+ # puts "✓ Trading re-enabled"
130
+ # end
34
131
  #
35
- # @return [Hash]
36
132
  def deactivate
37
133
  update("DEACTIVATE")
38
134
  end
39
135
  end
40
136
 
137
+ ##
41
138
  # No explicit validation contract is required for kill switch updates.
42
139
  #
43
- # @return [nil]
140
+ # Kill switch operations are simple status updates that don't require complex validation.
141
+ # The API handles validation server-side.
142
+ #
143
+ # @return [nil] Always returns nil as no validation contract is needed
144
+ #
145
+ # @api private
44
146
  def validation_contract
45
147
  nil
46
148
  end
@@ -3,34 +3,98 @@
3
3
  module DhanHQ
4
4
  module Models
5
5
  ##
6
- # Represents a single row/entry in the Ledger.
7
- # Ledger data typically returns an array of these objects.
6
+ # Model representing a single ledger entry in the Trading Account Ledger Report.
7
+ #
8
+ # The Ledger Report contains all credit and debit transaction details for a particular time interval.
9
+ # Each entry represents a single transaction with details such as narration, voucher information,
10
+ # debit/credit amounts, and running balance.
11
+ #
12
+ # @example Fetch ledger entries for a date range
13
+ # entries = DhanHQ::Models::LedgerEntry.all(
14
+ # from_date: "2024-04-01",
15
+ # to_date: "2024-04-30"
16
+ # )
17
+ # entries.each do |entry|
18
+ # puts "#{entry.voucherdate}: #{entry.narration} - #{entry.debit} / #{entry.credit}"
19
+ # end
20
+ #
21
+ # @example Calculate total credits and debits
22
+ # entries = DhanHQ::Models::LedgerEntry.all(
23
+ # from_date: "2024-04-01",
24
+ # to_date: "2024-04-30"
25
+ # )
26
+ # total_debits = entries.sum { |e| e.debit.to_f }
27
+ # total_credits = entries.sum { |e| e.credit.to_f }
28
+ #
8
29
  class LedgerEntry < BaseModel
9
- # The endpoint is /v2/ledger?from-date=...&to-date=...
10
- # So we may define a resource path or rely on the Statements resource.
30
+ # Base path for ledger endpoint.
11
31
  HTTP_PATH = "/v2/ledger"
12
32
 
13
- # Typical fields from API docs
14
33
  attributes :dhan_client_id, :narration, :voucherdate, :exchange,
15
34
  :voucherdesc, :vouchernumber, :debit, :credit, :runbal
16
35
 
17
36
  class << self
18
37
  ##
19
- # Provides a **shared instance** of the `Statements` resource.
38
+ # Provides a shared instance of the Statements resource.
20
39
  #
21
- # @return [DhanHQ::Resources::Statements]
40
+ # @return [DhanHQ::Resources::Statements] The Statements resource client instance
22
41
  def resource
23
42
  @resource ||= DhanHQ::Resources::Statements.new
24
43
  end
25
44
 
26
45
  ##
27
- # Fetch ledger entries for the given date range.
46
+ # Retrieves Trading Account Ledger Report entries for the specified date range.
28
47
  #
29
- # @param from_date [String] e.g. "2023-01-01"
30
- # @param to_date [String] e.g. "2023-01-31"
31
- # @return [Array<LedgerEntry>]
48
+ # Fetches all credit and debit transaction details for the given time interval.
49
+ # The ledger entries include transaction descriptions, voucher information, amounts,
50
+ # and running balances.
51
+ #
52
+ # @param from_date [String] Start date of the ledger report in YYYY-MM-DD format.
53
+ # Example: "2024-04-01"
54
+ # @param to_date [String] End date of the ledger report in YYYY-MM-DD format.
55
+ # Example: "2024-04-30"
56
+ #
57
+ # @return [Array<LedgerEntry>] Array of LedgerEntry objects. Returns empty array if no entries exist.
58
+ # Each LedgerEntry object contains (keys normalized to snake_case):
59
+ # - **:dhan_client_id** [String] User-specific identification generated by Dhan
60
+ # - **:narration** [String] Description of the ledger transaction (e.g., "FUNDS WITHDRAWAL", "CLOSING BALANCE")
61
+ # - **:voucherdate** [String] Transaction date in format "Mon DD, YYYY" (e.g., "Jun 22, 2022")
62
+ # - **:exchange** [String] Exchange information for the transaction (e.g., "NSE-CAPITAL", "NSE_CASH")
63
+ # - **:voucherdesc** [String] Nature of transaction (e.g., "PAYBNK", "CLOSING BALANCE", "OPENING BALANCE")
64
+ # - **:vouchernumber** [String] System generated transaction number. May be empty string for balance entries
65
+ # - **:debit** [String] Debit amount as string. Only populated when credit returns "0.00"
66
+ # - **:credit** [String] Credit amount as string. Only populated when debit returns "0.00"
67
+ # - **:runbal** [String] Running balance post transaction as string
68
+ #
69
+ # @example Fetch ledger entries for a month
70
+ # entries = DhanHQ::Models::LedgerEntry.all(
71
+ # from_date: "2024-04-01",
72
+ # to_date: "2024-04-30"
73
+ # )
74
+ # entries.each do |entry|
75
+ # puts "#{entry.voucherdate}: #{entry.narration} - Balance: #{entry.runbal}"
76
+ # end
77
+ #
78
+ # @example Filter entries by transaction type
79
+ # entries = DhanHQ::Models::LedgerEntry.all(
80
+ # from_date: "2024-04-01",
81
+ # to_date: "2024-04-30"
82
+ # )
83
+ # withdrawals = entries.select { |e| e.voucherdesc == "PAYBNK" }
84
+ # puts "Total withdrawals: #{withdrawals.size}"
85
+ #
86
+ # @example Calculate net balance change
87
+ # entries = DhanHQ::Models::LedgerEntry.all(
88
+ # from_date: "2024-04-01",
89
+ # to_date: "2024-04-30"
90
+ # )
91
+ # net_change = entries.sum { |e| e.credit.to_f - e.debit.to_f }
92
+ # puts "Net change: ₹#{net_change}"
93
+ #
94
+ # @note This is a GET request with query parameters. No body required.
95
+ # @note The ledger report represents historical data dumps and entries are returned as-is
96
+ # without additional validation.
32
97
  def all(from_date:, to_date:)
33
- # The resource call returns an Array<Hash>, according to the docs.
34
98
  response = resource.ledger(from_date: from_date, to_date: to_date)
35
99
 
36
100
  return [] unless response.is_a?(Array)
@@ -41,7 +105,31 @@ module DhanHQ
41
105
  end
42
106
  end
43
107
 
44
- # Optional: you can override #to_h or #inspect if you want a custom representation
108
+ ##
109
+ # Converts the LedgerEntry model attributes to a hash representation.
110
+ #
111
+ # Useful for serialization, logging, or passing ledger entry data to other methods.
112
+ #
113
+ # @return [Hash{Symbol => String}] Hash representation of the LedgerEntry model containing:
114
+ # - **:dhan_client_id** [String] User-specific identification
115
+ # - **:narration** [String] Transaction description
116
+ # - **:voucherdate** [String] Transaction date
117
+ # - **:exchange** [String] Exchange information
118
+ # - **:voucherdesc** [String] Nature of transaction
119
+ # - **:vouchernumber** [String] Transaction number
120
+ # - **:debit** [String] Debit amount
121
+ # - **:credit** [String] Credit amount
122
+ # - **:runbal** [String] Running balance
123
+ #
124
+ # @example Convert entry to hash
125
+ # entry = DhanHQ::Models::LedgerEntry.all(
126
+ # from_date: "2024-04-01",
127
+ # to_date: "2024-04-30"
128
+ # ).first
129
+ # entry_hash = entry.to_h
130
+ # puts entry_hash[:narration] # => "FUNDS WITHDRAWAL"
131
+ # puts entry_hash[:runbal] # => "957.29"
132
+ #
45
133
  def to_h
46
134
  {
47
135
  dhan_client_id: dhan_client_id,