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,11 +3,35 @@
3
3
  module DhanHQ
4
4
  module Models
5
5
  ##
6
- # Represents a single trade.
7
- # Supports three main API endpoints:
8
- # 1. GET /v2/trades - Current day trades
9
- # 2. GET /v2/trades/{order-id} - Trades for specific order
10
- # 3. GET /v2/trades/{from-date}/{to-date}/{page} - Historical trades
6
+ # Model for retrieving trade execution details.
7
+ #
8
+ # The Trade Book API lets you retrieve an array of all trades executed in a day.
9
+ # You can also fetch trade details for a specific order ID, which is useful during
10
+ # partial trades or Bracket/Cover Orders. Additionally, you can retrieve detailed
11
+ # trade history for all orders within a particular time frame.
12
+ #
13
+ # @example Fetch all trades for today
14
+ # trades = DhanHQ::Models::Trade.today
15
+ # trades.each do |trade|
16
+ # puts "#{trade.trading_symbol}: #{trade.traded_quantity} @ ₹#{trade.traded_price}"
17
+ # puts "Total Value: ₹#{trade.total_value}, Charges: ₹#{trade.total_charges}"
18
+ # end
19
+ #
20
+ # @example Fetch trades for a specific order
21
+ # trade = DhanHQ::Models::Trade.find_by_order_id("112111182045")
22
+ # if trade
23
+ # puts "Traded: #{trade.traded_quantity} @ ₹#{trade.traded_price}"
24
+ # end
25
+ #
26
+ # @example Fetch historical trades
27
+ # trades = DhanHQ::Models::Trade.history(
28
+ # from_date: "2022-12-01",
29
+ # to_date: "2022-12-31",
30
+ # page: 0
31
+ # )
32
+ # total_value = trades.sum(&:total_value)
33
+ # puts "Total traded value: ₹#{total_value}"
34
+ #
11
35
  class Trade < BaseModel
12
36
  HTTP_PATH = "/v2/trades"
13
37
 
@@ -22,22 +46,66 @@ module DhanHQ
22
46
 
23
47
  class << self
24
48
  ##
25
- # Resource for current day tradebook APIs
49
+ # Provides a shared instance of the Trades resource for current day tradebook APIs.
50
+ #
51
+ # @return [DhanHQ::Resources::Trades] The Trades resource client instance
26
52
  def tradebook_resource
27
53
  @tradebook_resource ||= DhanHQ::Resources::Trades.new
28
54
  end
29
55
 
30
56
  ##
31
- # Resource for historical trade data
57
+ # Provides a shared instance of the Statements resource for historical trade data.
58
+ #
59
+ # @return [DhanHQ::Resources::Statements] The Statements resource client instance
32
60
  def statements_resource
33
61
  @statements_resource ||= DhanHQ::Resources::Statements.new
34
62
  end
35
63
 
36
64
  ##
37
- # Fetch current day trades
38
- # GET /v2/trades
65
+ # Retrieves all trades executed during the current trading day.
66
+ #
67
+ # Fetches an array of all trades executed in the day. This is useful for
68
+ # tracking daily execution activity and analyzing trade performance.
69
+ #
70
+ # @return [Array<Trade>] Array of Trade objects. Returns empty array if no trades exist.
71
+ # Each Trade object contains (keys normalized to snake_case):
72
+ # - **:dhan_client_id** [String] User-specific identification generated by Dhan
73
+ # - **:order_id** [String] Order-specific identification generated by Dhan
74
+ # - **:exchange_order_id** [String] Order-specific identification generated by exchange
75
+ # - **:exchange_trade_id** [String] Trade-specific identification generated by exchange
76
+ # - **:transaction_type** [String] The trading side of transaction. "BUY" or "SELL"
77
+ # - **:exchange_segment** [String] Exchange segment of instrument
78
+ # - **:product_type** [String] Product type. Valid values: "CNC", "INTRADAY",
79
+ # "MARGIN", "MTF", "CO", "BO"
80
+ # - **:order_type** [String] Order type. Valid values: "LIMIT", "MARKET",
81
+ # "STOP_LOSS", "STOP_LOSS_MARKET"
82
+ # - **:trading_symbol** [String] Trading symbol of the instrument
83
+ # - **:security_id** [String] Exchange standard ID for each scrip
84
+ # - **:traded_quantity** [Integer] Number of shares executed
85
+ # - **:traded_price** [Float] Price at which trade is executed
86
+ # - **:create_time** [String] Time at which the order is created
87
+ # - **:update_time** [String] Time at which the last activity happened
88
+ # - **:exchange_time** [String] Time at which order reached at exchange
89
+ # - **:drv_expiry_date** [String, nil] For F&O, expiry date of contract
90
+ # - **:drv_option_type** [String, nil] Type of Option. "CALL" or "PUT"
91
+ # - **:drv_strike_price** [Float] For Options, Strike Price
92
+ #
93
+ # @example Fetch and analyze today's trades
94
+ # trades = DhanHQ::Models::Trade.today
95
+ # buy_trades = trades.select(&:buy?)
96
+ # sell_trades = trades.select(&:sell?)
97
+ # puts "Buy trades: #{buy_trades.count}, Sell trades: #{sell_trades.count}"
98
+ # total_value = trades.sum(&:total_value)
99
+ # puts "Total traded value: ₹#{total_value}"
100
+ #
101
+ # @example Calculate P&L for today
102
+ # trades = DhanHQ::Models::Trade.today
103
+ # buy_value = trades.select(&:buy?).sum(&:total_value)
104
+ # sell_value = trades.select(&:sell?).sum(&:total_value)
105
+ # total_charges = trades.sum(&:total_charges)
106
+ # pnl = sell_value - buy_value - total_charges
107
+ # puts "P&L: ₹#{pnl}"
39
108
  #
40
- # @return [Array<Trade>] Array of trades executed today
41
109
  def today
42
110
  response = tradebook_resource.all
43
111
  return [] unless response.is_a?(Array)
@@ -46,11 +114,28 @@ module DhanHQ
46
114
  end
47
115
 
48
116
  ##
49
- # Fetch trades for a specific order ID (current day)
50
- # GET /v2/trades/{order-id}
117
+ # Retrieves trade details for a specific order ID (current day).
118
+ #
119
+ # Fetches all trades generated for a particular order ID. This is especially useful
120
+ # during partial trades or Bracket/Cover Orders where traders may get confused
121
+ # reading trades from the tradebook. The response includes all trades generated
122
+ # for the specified order ID.
123
+ #
124
+ # @param order_id [String] Order-specific identification generated by Dhan
51
125
  #
52
- # @param order_id [String] The order ID to fetch trades for
53
- # @return [Trade, nil] Trade object or nil if not found
126
+ # @return [Trade, nil] Trade object with trade details if found, nil otherwise.
127
+ # Response structure is the same as {today}.
128
+ #
129
+ # @example Fetch trade for an order
130
+ # trade = DhanHQ::Models::Trade.find_by_order_id("112111182045")
131
+ # if trade
132
+ # puts "Order: #{trade.order_id}"
133
+ # puts "Traded: #{trade.traded_quantity} @ ₹#{trade.traded_price}"
134
+ # puts "Symbol: #{trade.trading_symbol}"
135
+ # puts "Time: #{trade.exchange_time}"
136
+ # end
137
+ #
138
+ # @raise [DhanHQ::ValidationError] If order_id validation fails
54
139
  def find_by_order_id(order_id)
55
140
  # Validate input
56
141
  contract = DhanHQ::Contracts::TradeByOrderIdContract.new
@@ -68,13 +153,61 @@ module DhanHQ
68
153
  end
69
154
 
70
155
  ##
71
- # Fetch historical trades within the given date range and page
72
- # GET /v2/trades/{from-date}/{to-date}/{page}
156
+ # Retrieves detailed trade history for all orders within a specified time frame.
73
157
  #
74
- # @param from_date [String] Start date in YYYY-MM-DD format
75
- # @param to_date [String] End date in YYYY-MM-DD format
76
- # @param page [Integer] Page number (default: 0)
77
- # @return [Array<Trade>] Array of historical trades
158
+ # Fetches paginated trade history data with comprehensive charge breakdowns.
159
+ # The response includes detailed information about SEBI tax, STT, brokerage charges,
160
+ # service tax, exchange transaction charges, and stamp duty for each trade.
161
+ #
162
+ # @param from_date [String] (required) Start date in "YYYY-MM-DD" format
163
+ # @param to_date [String] (required) End date in "YYYY-MM-DD" format
164
+ # @param page [Integer] (default: 0) Page number for paginated results. Pass 0 for first page
165
+ #
166
+ # @return [Array<Trade>] Array of historical Trade objects. Returns empty array if no trades found.
167
+ # Each Trade object contains all fields from {today} plus additional charge details:
168
+ # - **:custom_symbol** [String] Trading Symbol as per Dhan
169
+ # - **:isin** [String] Universal standard ID for each scrip (International Securities Identification Number)
170
+ # - **:instrument** [String] Type of Instrument. "EQUITY" or "DERIVATIVES"
171
+ # - **:sebi_tax** [String] SEBI Turnover Charges
172
+ # - **:stt** [String] Securities Transactions Tax
173
+ # - **:brokerage_charges** [String] Brokerage charges by Dhan
174
+ # - **:service_tax** [String] Applicable Service Tax
175
+ # - **:exchange_transaction_charges** [String] Exchange Transaction Charge
176
+ # - **:stamp_duty** [String] Stamp Duty Charges
177
+ #
178
+ # @example Fetch historical trades for a month
179
+ # trades = DhanHQ::Models::Trade.history(
180
+ # from_date: "2022-12-01",
181
+ # to_date: "2022-12-31",
182
+ # page: 0
183
+ # )
184
+ # puts "Total trades: #{trades.count}"
185
+ #
186
+ # @example Calculate total charges for historical period
187
+ # trades = DhanHQ::Models::Trade.history(
188
+ # from_date: "2022-12-01",
189
+ # to_date: "2022-12-31"
190
+ # )
191
+ # total_charges = trades.sum(&:total_charges)
192
+ # total_brokerage = trades.sum { |t| t.brokerage_charges.to_f }
193
+ # puts "Total Charges: ₹#{total_charges}"
194
+ # puts "Total Brokerage: ₹#{total_brokerage}"
195
+ #
196
+ # @example Paginate through historical trades
197
+ # page = 0
198
+ # loop do
199
+ # trades = DhanHQ::Models::Trade.history(
200
+ # from_date: "2022-12-01",
201
+ # to_date: "2022-12-31",
202
+ # page: page
203
+ # )
204
+ # break if trades.empty?
205
+ #
206
+ # puts "Page #{page}: #{trades.count} trades"
207
+ # page += 1
208
+ # end
209
+ #
210
+ # @raise [DhanHQ::ValidationError] If date format or page validation fails
78
211
  def history(from_date:, to_date:, page: 0)
79
212
  validate_history_params(from_date, to_date, page)
80
213
 
@@ -91,6 +224,16 @@ module DhanHQ
91
224
 
92
225
  private
93
226
 
227
+ ##
228
+ # Validates parameters for historical trade queries.
229
+ #
230
+ # @param from_date [String] Start date in YYYY-MM-DD format
231
+ # @param to_date [String] End date in YYYY-MM-DD format
232
+ # @param page [Integer] Page number
233
+ #
234
+ # @raise [DhanHQ::ValidationError] If validation fails
235
+ #
236
+ # @api private
94
237
  def validate_history_params(from_date, to_date, page)
95
238
  contract = DhanHQ::Contracts::TradeHistoryContract.new
96
239
  validation_result = contract.call(from_date: from_date, to_date: to_date, page: page)
@@ -100,48 +243,131 @@ module DhanHQ
100
243
  raise DhanHQ::ValidationError, "Invalid parameters: #{validation_result.errors.to_h}"
101
244
  end
102
245
 
103
- # Alias for backward compatibility
246
+ # Alias for backward compatibility with historical trades
247
+ # @api private
104
248
  alias all history
105
249
  end
106
250
 
107
251
  ##
108
- # Trade objects are read-only, so no validation contract needed
252
+ # Trade objects are read-only, so no validation contract needed.
253
+ #
254
+ # @return [nil] Always returns nil as trades are read-only
255
+ #
256
+ # @api private
109
257
  def validation_contract
110
258
  nil
111
259
  end
112
260
 
113
261
  ##
114
- # Helper methods for trade data
262
+ # Checks if the trade is a BUY transaction.
263
+ #
264
+ # @return [Boolean] true if transaction_type is "BUY", false otherwise
265
+ #
266
+ # @example
267
+ # trade = DhanHQ::Models::Trade.today.first
268
+ # if trade.buy?
269
+ # puts "This is a buy trade"
270
+ # end
271
+ #
115
272
  def buy?
116
273
  transaction_type == "BUY"
117
274
  end
118
275
 
276
+ ##
277
+ # Checks if the trade is a SELL transaction.
278
+ #
279
+ # @return [Boolean] true if transaction_type is "SELL", false otherwise
280
+ #
281
+ # @example
282
+ # trade = DhanHQ::Models::Trade.today.first
283
+ # if trade.sell?
284
+ # puts "This is a sell trade"
285
+ # end
286
+ #
119
287
  def sell?
120
288
  transaction_type == "SELL"
121
289
  end
122
290
 
291
+ ##
292
+ # Checks if the trade instrument is EQUITY.
293
+ #
294
+ # @return [Boolean] true if instrument is "EQUITY", false otherwise
295
+ #
296
+ # @example
297
+ # trades = DhanHQ::Models::Trade.today
298
+ # equity_trades = trades.select(&:equity?)
299
+ # puts "Equity trades: #{equity_trades.count}"
300
+ #
123
301
  def equity?
124
302
  instrument == "EQUITY"
125
303
  end
126
304
 
305
+ ##
306
+ # Checks if the trade instrument is DERIVATIVES.
307
+ #
308
+ # @return [Boolean] true if instrument is "DERIVATIVES", false otherwise
309
+ #
310
+ # @example
311
+ # trades = DhanHQ::Models::Trade.today
312
+ # derivative_trades = trades.select(&:derivative?)
313
+ # puts "Derivative trades: #{derivative_trades.count}"
314
+ #
127
315
  def derivative?
128
316
  instrument == "DERIVATIVES"
129
317
  end
130
318
 
319
+ ##
320
+ # Checks if the trade is an option (CALL or PUT).
321
+ #
322
+ # @return [Boolean] true if drv_option_type is "CALL" or "PUT", false otherwise
323
+ #
324
+ # @example
325
+ # trades = DhanHQ::Models::Trade.today
326
+ # option_trades = trades.select(&:option?)
327
+ # puts "Option trades: #{option_trades.count}"
328
+ #
131
329
  def option?
132
330
  %w[CALL PUT].include?(drv_option_type)
133
331
  end
134
332
 
333
+ ##
334
+ # Checks if the trade is a CALL option.
335
+ #
336
+ # @return [Boolean] true if drv_option_type is "CALL", false otherwise
337
+ #
338
+ # @example
339
+ # trades = DhanHQ::Models::Trade.today
340
+ # call_trades = trades.select(&:call_option?)
341
+ # puts "Call option trades: #{call_trades.count}"
342
+ #
135
343
  def call_option?
136
344
  drv_option_type == "CALL"
137
345
  end
138
346
 
347
+ ##
348
+ # Checks if the trade is a PUT option.
349
+ #
350
+ # @return [Boolean] true if drv_option_type is "PUT", false otherwise
351
+ #
352
+ # @example
353
+ # trades = DhanHQ::Models::Trade.today
354
+ # put_trades = trades.select(&:put_option?)
355
+ # puts "Put option trades: #{put_trades.count}"
356
+ #
139
357
  def put_option?
140
358
  drv_option_type == "PUT"
141
359
  end
142
360
 
143
361
  ##
144
- # Calculate total trade value
362
+ # Calculates the total trade value (quantity × price).
363
+ #
364
+ # @return [Float] Total trade value. Returns 0 if traded_quantity or traded_price is missing
365
+ #
366
+ # @example
367
+ # trade = DhanHQ::Models::Trade.today.first
368
+ # puts "Trade Value: ₹#{trade.total_value}"
369
+ # # => Trade Value: ₹133832.0
370
+ #
145
371
  def total_value
146
372
  return 0 unless traded_quantity && traded_price
147
373
 
@@ -149,7 +375,22 @@ module DhanHQ
149
375
  end
150
376
 
151
377
  ##
152
- # Calculate total charges
378
+ # Calculates the total charges for the trade.
379
+ #
380
+ # Sums all applicable charges including SEBI tax, STT, brokerage charges,
381
+ # service tax, exchange transaction charges, and stamp duty.
382
+ #
383
+ # @return [Float] Total charges amount. Returns 0 if no charges are present
384
+ #
385
+ # @example
386
+ # trade = DhanHQ::Models::Trade.history(
387
+ # from_date: "2022-12-01",
388
+ # to_date: "2022-12-31"
389
+ # ).first
390
+ # puts "Total Charges: ₹#{trade.total_charges}"
391
+ # puts "Brokerage: ₹#{trade.brokerage_charges}"
392
+ # puts "STT: ₹#{trade.stt}"
393
+ #
153
394
  def total_charges
154
395
  charges = [sebi_tax, stt, brokerage_charges, service_tax,
155
396
  exchange_transaction_charges, stamp_duty].compact
@@ -157,7 +398,19 @@ module DhanHQ
157
398
  end
158
399
 
159
400
  ##
160
- # Net trade value after charges
401
+ # Calculates the net trade value after deducting all charges.
402
+ #
403
+ # @return [Float] Net trade value (total_value - total_charges)
404
+ #
405
+ # @example
406
+ # trade = DhanHQ::Models::Trade.history(
407
+ # from_date: "2022-12-01",
408
+ # to_date: "2022-12-31"
409
+ # ).first
410
+ # puts "Gross Value: ₹#{trade.total_value}"
411
+ # puts "Charges: ₹#{trade.total_charges}"
412
+ # puts "Net Value: ₹#{trade.net_value}"
413
+ #
161
414
  def net_value
162
415
  total_value - total_charges
163
416
  end
@@ -6,7 +6,7 @@ module DhanHQ
6
6
  # Resource for expired options data API endpoints
7
7
  class ExpiredOptionsData < BaseAPI
8
8
  API_TYPE = :data_api
9
- HTTP_PATH = "/charts"
9
+ HTTP_PATH = "/v2/charts"
10
10
 
11
11
  ##
12
12
  # Fetch expired options data for rolling contracts
@@ -2,5 +2,5 @@
2
2
 
3
3
  module DhanHQ
4
4
  # Semantic version of the DhanHQ client gem.
5
- VERSION = "2.1.8"
5
+ VERSION = "2.1.10"
6
6
  end
@@ -184,8 +184,9 @@ module DhanHQ
184
184
 
185
185
  ##
186
186
  # Emit status-specific events
187
+ #
187
188
  # @param order_update [OrderUpdate] Current order update
188
- # @param previous_state [OrderUpdate, nil] Previous order state
189
+ # @param _previous_state [OrderUpdate, nil] Previous order state (unused parameter)
189
190
  # rubocop:disable Metrics/MethodLength
190
191
  def emit_status_specific_events(order_update, _previous_state)
191
192
  case order_update.status
@@ -10,7 +10,8 @@ module DhanHQ
10
10
  module Orders
11
11
  ##
12
12
  # Connect to order updates WebSocket with a simple callback
13
- # @param block [Proc] Callback for order updates
13
+ #
14
+ # @yield [OrderUpdate] Yields order update objects when received
14
15
  # @return [Client] WebSocket client instance
15
16
  def self.connect(&)
16
17
  Client.new.start.on(:update, &)
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: DhanHQ
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.1.8
4
+ version: 2.1.10
5
5
  platform: ruby
6
6
  authors:
7
7
  - Shubham Taywade