DhanHQ 3.2.1 → 3.4.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 (41) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +76 -0
  3. data/GUIDE.md +16 -0
  4. data/README.md +93 -18
  5. data/docs/CONFIGURATION.md +39 -14
  6. data/docs/RAILS_INTEGRATION.md +60 -17
  7. data/docs/RELEASE_GUIDE.md +1 -1
  8. data/docs/TROUBLESHOOTING.md +15 -1
  9. data/lib/DhanHQ/backtest/result.rb +87 -0
  10. data/lib/DhanHQ/backtest/runner.rb +144 -0
  11. data/lib/DhanHQ/backtest/trade.rb +35 -0
  12. data/lib/DhanHQ/backtest.rb +24 -0
  13. data/lib/DhanHQ/concerns/bang_writes.rb +69 -0
  14. data/lib/DhanHQ/concerns/tracked_writes.rb +56 -0
  15. data/lib/DhanHQ/configuration.rb +35 -1
  16. data/lib/DhanHQ/core/base_model.rb +6 -13
  17. data/lib/DhanHQ/deprecation.rb +62 -0
  18. data/lib/DhanHQ/jobs/place_order_job.rb +47 -0
  19. data/lib/DhanHQ/models/alert_order.rb +7 -0
  20. data/lib/DhanHQ/models/forever_order.rb +9 -0
  21. data/lib/DhanHQ/models/global_stocks/order.rb +9 -0
  22. data/lib/DhanHQ/models/iceberg_order.rb +9 -0
  23. data/lib/DhanHQ/models/multi_order.rb +7 -0
  24. data/lib/DhanHQ/models/order.rb +28 -0
  25. data/lib/DhanHQ/models/pnl_exit.rb +7 -0
  26. data/lib/DhanHQ/models/super_order.rb +9 -0
  27. data/lib/DhanHQ/models/twap_order.rb +9 -0
  28. data/lib/DhanHQ/version.rb +1 -1
  29. data/lib/DhanHQ/write_result.rb +163 -0
  30. data/lib/DhanHQ/ws/base_connection.rb +1 -0
  31. data/lib/DhanHQ/ws/connection.rb +1 -0
  32. data/lib/DhanHQ/ws/orders/connection.rb +1 -0
  33. data/lib/DhanHQ/ws.rb +25 -0
  34. data/lib/dhan_hq.rb +5 -0
  35. data/lib/generators/dhanhq/install/install_generator.rb +45 -0
  36. data/lib/generators/dhanhq/install/templates/initializer.rb +18 -0
  37. data/lib/generators/dhanhq/install/templates/market_feed_channel.rb +7 -0
  38. data/lib/generators/dhanhq/install/templates/market_feed_worker.rb +27 -0
  39. data/lib/generators/dhanhq/install/templates/place_order_service.rb +24 -0
  40. data/skills/dhanhq-ruby/references/portfolio.md +15 -8
  41. metadata +30 -13
@@ -32,6 +32,13 @@ module DhanHQ
32
32
  # puts response[:pnl_exit_status] # => "DISABLED"
33
33
  #
34
34
  class PnlExit < BaseModel
35
+ extend DhanHQ::Concerns::BangWrites
36
+ extend DhanHQ::Concerns::TrackedWrites
37
+
38
+ track_class_writes :configure, :stop
39
+
40
+ bang_class_writes :configure, :stop
41
+
35
42
  HTTP_PATH = "/v2/pnlExit"
36
43
 
37
44
  attributes :pnl_exit_status, :profit, :loss, :segments, :enable_kill_switch
@@ -48,6 +48,15 @@ module DhanHQ
48
48
  # order.cancel("STOP_LOSS_LEG")
49
49
  #
50
50
  class SuperOrder < BaseModel
51
+ extend DhanHQ::Concerns::BangWrites
52
+ extend DhanHQ::Concerns::TrackedWrites
53
+
54
+ track_class_writes :create
55
+ track_writes :modify, :cancel
56
+
57
+ bang_class_writes :create
58
+ bang_writes :modify, :cancel
59
+
51
60
  include Concerns::ApiResponseHandler
52
61
 
53
62
  attributes :dhan_client_id, :order_id, :correlation_id, :order_status,
@@ -45,6 +45,15 @@ module DhanHQ
45
45
  # order.cancel
46
46
  #
47
47
  class TwapOrder < BaseModel
48
+ extend DhanHQ::Concerns::BangWrites
49
+ extend DhanHQ::Concerns::TrackedWrites
50
+
51
+ track_class_writes :create
52
+ track_writes :modify, :cancel
53
+
54
+ bang_class_writes :create
55
+ bang_writes :modify, :cancel
56
+
48
57
  include Concerns::ApiResponseHandler
49
58
 
50
59
  attributes :dhan_client_id, :order_id, :correlation_id, :order_status,
@@ -2,5 +2,5 @@
2
2
 
3
3
  module DhanHQ
4
4
  # Semantic version of the DhanHQ client gem.
5
- VERSION = "3.2.1"
5
+ VERSION = "3.4.0"
6
6
  end
@@ -0,0 +1,163 @@
1
+ # frozen_string_literal: true
2
+
3
+ module DhanHQ
4
+ # Interprets the several shapes a write currently uses to signal failure.
5
+ #
6
+ # The model write methods do not share a return contract. Depending on which class
7
+ # and which failure you hit, a rejected write comes back as +nil+, as +false+, or as
8
+ # a {DhanHQ::ErrorObject} — and {DhanHQ::Models::AlertOrder.modify} can return either
9
+ # of the first two from the same method. A caller cannot write one error branch.
10
+ #
11
+ # Unifying those contracts is a breaking change for the applications that depend on
12
+ # this gem, so it is staged (see CHANGELOG). This module is step one: it puts the
13
+ # knowledge of "what does failure look like" in exactly one place, and backs the bang
14
+ # variants (+place!+, +modify!+, +cancel!+, …) that give callers a single, explicit
15
+ # failure mode to rescue today.
16
+ #
17
+ # @example Opting a call site into explicit failures
18
+ # order = DhanHQ::Models::Order.place!(params) # raises DhanHQ::OrderError
19
+ # # instead of
20
+ # order = DhanHQ::Models::Order.place(params) # nil on failure
21
+ module WriteResult
22
+ # Thread-local flag set while a bang variant is calling through.
23
+ SUPPRESSION_KEY = :dhanhq_suppress_write_deprecation
24
+
25
+ module_function
26
+
27
+ # Whether a write result represents a rejected or failed operation.
28
+ #
29
+ # @param result [Object] Return value of a write method.
30
+ # @return [Boolean]
31
+ def failure?(result)
32
+ result.nil? || result == false || result.is_a?(DhanHQ::ErrorObject)
33
+ end
34
+
35
+ # @param result [Object] Return value of a write method.
36
+ # @return [Boolean]
37
+ def success?(result)
38
+ !failure?(result)
39
+ end
40
+
41
+ # Returns the result, or raises carrying whatever diagnostics the failure held.
42
+ #
43
+ # @param result [Object] Return value of a write method.
44
+ # @param operation [String] Human-readable operation name for the message,
45
+ # e.g. +"DhanHQ::Models::Order.place"+.
46
+ # @param error_class [Class] Exception to raise. Defaults to {DhanHQ::OrderError},
47
+ # which descends from {DhanHQ::Error}, so existing `rescue DhanHQ::Error`
48
+ # handlers still catch it.
49
+ # @param errors [Hash, nil] Validation errors to report when the result itself
50
+ # carries none — typically a model's +errors+ hash.
51
+ # @return [Object] The result, when it represents success.
52
+ # @raise [DhanHQ::Error] When the result represents failure.
53
+ def unwrap!(result, operation:, error_class: DhanHQ::OrderError, errors: nil)
54
+ return result if success?(result)
55
+
56
+ raise error_class, "#{operation} failed: #{describe(result, errors)}"
57
+ end
58
+
59
+ # Reports, once per call site, that a non-bang write signalled failure through a
60
+ # value whose shape is going to change in 4.0.0.
61
+ #
62
+ # Step two of the migration: the notice tells a maintainer which of their call
63
+ # sites still branch on the old return value, so they can move to the bang variant
64
+ # before the non-bang contract unifies on {DhanHQ::ErrorObject}. Returns the result
65
+ # untouched — this observes, it never alters behaviour.
66
+ #
67
+ # Silent when the write succeeded, when the caller opted out via
68
+ # +config.warn_on_ambiguous_write_failure = false+, or when reached through a bang
69
+ # variant (those callers have already migrated — see {.suppressing_deprecation}).
70
+ #
71
+ # @param result [Object] Return value of a non-bang write method.
72
+ # @param operation [String] Operation label from {.operation_label}.
73
+ # @return [Object] The result, unchanged.
74
+ def report_ambiguous_failure(result, operation:)
75
+ return result unless failure?(result)
76
+ return result if suppressed?
77
+ return result unless DhanHQ.configuration&.warn_on_ambiguous_write_failure?
78
+
79
+ DhanHQ::Deprecation.warn_once(
80
+ operation,
81
+ "#{operation} reported failure as #{shape_of(result)}. Write methods return " \
82
+ "nil, false or a DhanHQ::ErrorObject inconsistently today and will all return " \
83
+ "DhanHQ::ErrorObject in 4.0.0, which is truthy — an `if result` failure branch " \
84
+ "will invert. Use #{operation}! to get a DhanHQ::OrderError instead, or set " \
85
+ "config.warn_on_ambiguous_write_failure = false to silence this."
86
+ )
87
+
88
+ result
89
+ end
90
+
91
+ # Runs the block with {.report_ambiguous_failure} disabled on this thread.
92
+ #
93
+ # Used by the bang variants: they call the non-bang method to get its result, and a
94
+ # caller who has already moved to `place!` does not need telling to move to
95
+ # `place!`. Thread-local so a concurrent thread still gets its own notices.
96
+ #
97
+ # @return [Object] The block's value.
98
+ def suppressing_deprecation
99
+ previous = Thread.current[SUPPRESSION_KEY]
100
+ Thread.current[SUPPRESSION_KEY] = true
101
+ yield
102
+ ensure
103
+ Thread.current[SUPPRESSION_KEY] = previous
104
+ end
105
+
106
+ # @return [Boolean]
107
+ def suppressed?
108
+ Thread.current[SUPPRESSION_KEY] == true
109
+ end
110
+
111
+ # Names the shape a failure arrived in, for the notice.
112
+ #
113
+ # @return [String]
114
+ def shape_of(result)
115
+ return "nil" if result.nil?
116
+ return "false" if result == false
117
+
118
+ "a DhanHQ::ErrorObject"
119
+ end
120
+
121
+ # Label identifying the operation that failed, for the exception message.
122
+ #
123
+ # @param receiver [Object] The class (for a class method) or instance.
124
+ # @param name [Symbol] Method name.
125
+ # @return [String] e.g. +"DhanHQ::Models::Order.place"+ or +"…Order#modify"+.
126
+ def operation_label(receiver, name)
127
+ return "#{module_label(receiver)}.#{name}" if receiver.is_a?(Module)
128
+
129
+ "#{module_label(receiver.class)}##{name}"
130
+ end
131
+
132
+ # Reads a module's declared name, falling back to +to_s+ so an anonymous class
133
+ # still yields something readable rather than an object address.
134
+ #
135
+ # @return [String]
136
+ def module_label(mod)
137
+ mod.name || mod.to_s
138
+ end
139
+
140
+ # Validation errors carried by a model instance, when it has any.
141
+ #
142
+ # @param receiver [Object]
143
+ # @return [Hash, nil]
144
+ def errors_from(receiver)
145
+ return nil if receiver.is_a?(Module)
146
+ return nil unless receiver.respond_to?(:errors)
147
+
148
+ receiver.errors
149
+ end
150
+
151
+ # Best available explanation for a failure.
152
+ #
153
+ # @return [String]
154
+ def describe(result, errors = nil)
155
+ return result.errors.to_s if result.is_a?(DhanHQ::ErrorObject)
156
+ return errors.to_s if errors && !errors.empty?
157
+
158
+ # `nil` and `false` carry nothing, so say which of the two it was rather than
159
+ # inventing a cause.
160
+ result.nil? ? "the API returned no record" : "the API rejected the request"
161
+ end
162
+ end
163
+ end
@@ -212,6 +212,7 @@ module DhanHQ
212
212
  # Handle WebSocket message event
213
213
  # @param ev [Event] WebSocket message event
214
214
  def handle_message(ev)
215
+ WS.debug_frame(self.class.name, ev.data)
215
216
  emit(:raw, ev.data)
216
217
  process_message(ev.data) if respond_to?(:process_message, true)
217
218
  end
@@ -141,6 +141,7 @@ module DhanHQ
141
141
  @ws.on(:open) { |_| handle_open(sessions) }
142
142
 
143
143
  @ws.on :message do |ev|
144
+ WS.debug_frame(self.class.name, ev.data)
144
145
  notify(:message, nil)
145
146
  @on_binary&.call(ev.data) # raw frames to decoder
146
147
  end
@@ -63,6 +63,7 @@ module DhanHQ
63
63
  # Process incoming WebSocket message
64
64
  # @param ev [Event] WebSocket message event
65
65
  def handle_message(ev)
66
+ WS.debug_frame(self.class.name, ev.data)
66
67
  msg = JSON.parse(ev.data, symbolize_names: true)
67
68
  emit(:raw, msg)
68
69
  emit(:message, msg)
data/lib/DhanHQ/ws.rb CHANGED
@@ -34,5 +34,30 @@ module DhanHQ
34
34
  def self.disconnect_all_local!
35
35
  Registry.stop_all
36
36
  end
37
+
38
+ # Cap on how many bytes of a frame get hex-dumped by {debug_frame}. A full
39
+ # depth packet can run several KB; at tick frequency that floods the log
40
+ # long before it adds diagnostic value beyond the first couple hundred
41
+ # bytes (header + the first few fields is normally enough to spot a
42
+ # parsing bug). The full frame size is always logged regardless of the cap.
43
+ DEBUG_FRAME_MAX_BYTES = 256
44
+
45
+ # Logs a raw inbound WebSocket frame as a hex dump when
46
+ # +config.ws_debug+ (+DHAN_WS_DEBUG=true+) is enabled. A no-op otherwise --
47
+ # the flag is checked before any hex encoding work, so there's no cost
48
+ # when debug logging is off. Frames longer than {DEBUG_FRAME_MAX_BYTES}
49
+ # are truncated in the dump, but the logged byte count is always the full
50
+ # frame size.
51
+ #
52
+ # @param source [String] short tag identifying which connection the frame came from
53
+ # @param data [String] raw frame bytes
54
+ # @return [void]
55
+ def self.debug_frame(source, data)
56
+ return unless DhanHQ.configuration&.ws_debug?
57
+
58
+ hex = data.byteslice(0, DEBUG_FRAME_MAX_BYTES).unpack1("H*")
59
+ hex += "...truncated" if data.bytesize > DEBUG_FRAME_MAX_BYTES
60
+ DhanHQ.logger&.debug("[DhanHQ::WS::#{source}] frame (#{data.bytesize} bytes): #{hex}")
61
+ end
37
62
  end
38
63
  end
data/lib/dhan_hq.rb CHANGED
@@ -35,6 +35,11 @@ module DhanHQ
35
35
  "base_api" => "BaseAPI",
36
36
  "ip_setup" => "IPSetup",
37
37
  "json_loader" => "JSONLoader",
38
+ # Same failure as "ai" above: mcp.rb defines DhanHQ::MCP while Zeitwerk expected
39
+ # DhanHQ::Mcp, so DhanHQ::MCP::Server raised NameError after a bare `require
40
+ # "dhan_hq"` — it only worked via exe/dhanhq-mcp and lib/dhan_hq/mcp.rb, which
41
+ # require_relative the file directly instead of going through the autoloader.
42
+ "mcp" => "MCP",
38
43
  "ws" => "WS"
39
44
  )
40
45
  LOADER.push_dir(File.join(__dir__, "DhanHQ"), namespace: self)
@@ -0,0 +1,45 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "rails/generators"
4
+
5
+ module Dhanhq
6
+ # Scaffolds a DhanHQ initializer, a sample order-placing service object, and
7
+ # a Sidekiq worker + ActionCable channel for streaming market data.
8
+ #
9
+ # rails generate dhanhq:install
10
+ #
11
+ class InstallGenerator < Rails::Generators::Base
12
+ source_root File.expand_path("templates", __dir__)
13
+
14
+ def create_initializer
15
+ template "initializer.rb", "config/initializers/dhanhq.rb"
16
+ end
17
+
18
+ def create_order_service
19
+ template "place_order_service.rb", "app/services/dhan/orders/place_order.rb"
20
+ end
21
+
22
+ def create_market_feed_worker
23
+ template "market_feed_worker.rb", "app/workers/dhan_market_feed_worker.rb"
24
+ end
25
+
26
+ def create_market_feed_channel
27
+ template "market_feed_channel.rb", "app/channels/dhan_market_feed_channel.rb"
28
+ end
29
+
30
+ def show_post_install_message
31
+ say ""
32
+ say "DhanHQ installed! Next steps:", :green
33
+ say " 1. Add your credentials:"
34
+ say " rails credentials:edit"
35
+ say " dhanhq:"
36
+ say " client_id: \"your_client_id\""
37
+ say " access_token: \"your_access_token\""
38
+ say " 2. Set LIVE_TRADING=true before placing real orders (see docs/CONFIGURATION.md)"
39
+ say " 3. Start the market feed: DhanMarketFeedWorker.perform_async"
40
+ say ""
41
+ say "Full reference: https://github.com/shubhamtaywade82/dhanhq-client/blob/main/docs/RAILS_INTEGRATION.md"
42
+ say ""
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "dhan_hq"
4
+
5
+ if (creds = Rails.application.credentials.dig(:dhanhq))
6
+ ENV["DHAN_CLIENT_ID"] ||= creds[:client_id]
7
+ ENV["DHAN_ACCESS_TOKEN"] ||= creds[:access_token]
8
+ end
9
+
10
+ DhanHQ.configure_with_env
11
+
12
+ log_level = (ENV["DHAN_LOG_LEVEL"] || "INFO").upcase
13
+ DhanHQ.logger.level = Logger.const_get(log_level)
14
+
15
+ # Full optional configuration (base_url, ws_order_url, partner auth, timeouts,
16
+ # DHAN_WS_DEBUG, ...) is documented in:
17
+ # https://github.com/shubhamtaywade82/dhanhq-client/blob/main/docs/CONFIGURATION.md
18
+ # https://github.com/shubhamtaywade82/dhanhq-client/blob/main/docs/RAILS_INTEGRATION.md
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ class DhanMarketFeedChannel < ApplicationCable::Channel
4
+ def subscribed
5
+ stream_from "dhan_market_feed"
6
+ end
7
+ end
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Keeps a DhanHQ market feed connection open and broadcasts ticks over
4
+ # ActionCable. Start it with DhanMarketFeedWorker.perform_async -- the job
5
+ # blocks for the life of the connection rather than completing immediately,
6
+ # so Sidekiq's dashboard reflects whether the feed is actually running.
7
+ #
8
+ # retry: false because there is nothing to retry: the underlying
9
+ # DhanHQ::WS::Client already reconnects and re-subscribes on its own.
10
+ class DhanMarketFeedWorker
11
+ include Sidekiq::Worker
12
+ sidekiq_options retry: false
13
+
14
+ def perform(mode = "quote")
15
+ client = DhanHQ::WS.connect(mode: mode.to_sym) do |tick|
16
+ ActionCable.server.broadcast("dhan_market_feed", tick)
17
+ end
18
+
19
+ client.on(:reconnect) { |info| Rails.logger.warn("[DhanMarketFeedWorker] reconnect ##{info[:attempt]}") }
20
+ client.on(:error) { |message| Rails.logger.error("[DhanMarketFeedWorker] #{message}") }
21
+
22
+ loop do
23
+ sleep 30
24
+ break unless client.connected?
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Dhan
4
+ module Orders
5
+ # Places a Dhan order via the bang variant, so a rejection raises a
6
+ # specific, catchable error instead of Order.place's ambiguous
7
+ # nil/false/ErrorObject return.
8
+ class PlaceOrder
9
+ def initialize(params)
10
+ @params = params
11
+ end
12
+
13
+ def call
14
+ DhanHQ::Models::Order.place!(@params)
15
+ rescue DhanHQ::OrderError => e
16
+ Rails.logger.error("Dhan order rejected: #{e.message}")
17
+ raise
18
+ rescue DhanHQ::RiskViolation => e
19
+ Rails.logger.warn("Dhan risk check blocked the order: #{e.message}")
20
+ raise
21
+ end
22
+ end
23
+ end
24
+ end
@@ -70,24 +70,31 @@ position.convert(
70
70
 
71
71
  ## eDIS Authorization
72
72
 
73
- For selling delivery holdings, authorization is handled via `DhanHQ::Models::EDIS`:
73
+ For selling delivery holdings, authorization is handled via `DhanHQ::Models::Edis`:
74
74
 
75
75
  ### Step 1: Generate TPIN
76
76
  ```ruby
77
- DhanHQ::Models::EDIS.generate_tpin
77
+ DhanHQ::Models::Edis.generate_tpin
78
78
  ```
79
79
 
80
- ### Step 2: Open Browser Authorization
80
+ Triggers a TPIN to the user's registered mobile/email. Returns `{status: "accepted"}` the API responds `202` for this async operation.
81
+
82
+ ### Step 2: Generate and Render the Authorization Form
81
83
  ```ruby
82
- DhanHQ::Models::EDIS.open_browser_for_tpin(
84
+ form = DhanHQ::Models::Edis.generate_form(
83
85
  isin: "INE002A01018",
84
86
  qty: 5,
85
- exchange: "NSE"
87
+ exchange: "NSE",
88
+ segment: "EQ"
86
89
  )
90
+ # form[:edisFormHtml] is a browser-postable HTML form; render or POST it so the
91
+ # user can complete authorization on Dhan's eDIS page.
87
92
  ```
88
93
 
89
- ### Step 3: Inquiry eDIS Approval
94
+ ### Step 3: Inquire eDIS Approval
90
95
  ```ruby
91
- inquiry = DhanHQ::Models::EDIS.inquiry(isin: "INE002A01018")
92
- puts "Approved Qty: #{inquiry.aprvd_qty}, Status: #{inquiry.status}"
96
+ status = DhanHQ::Models::Edis.inquire(isin: "INE002A01018") # or isin: "ALL"
97
+ puts "Approved Qty: #{status[:aprvdQty]}, Status: #{status[:status]}"
93
98
  ```
99
+
100
+ `inquire` returns the raw API response (a `HashWithIndifferentAccess`), not a model instance — key names match the API's camelCase (`aprvdQty`, `totalQty`), not the snake_case used elsewhere in this gem.
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: DhanHQ
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.2.1
4
+ version: 3.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Shubham Taywade
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2026-07-26 00:00:00.000000000 Z
11
+ date: 2026-08-15 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport
@@ -84,16 +84,16 @@ dependencies:
84
84
  name: dry-validation
85
85
  requirement: !ruby/object:Gem::Requirement
86
86
  requirements:
87
- - - ">="
87
+ - - "~>"
88
88
  - !ruby/object:Gem::Version
89
- version: '0'
89
+ version: '1.11'
90
90
  type: :runtime
91
91
  prerelease: false
92
92
  version_requirements: !ruby/object:Gem::Requirement
93
93
  requirements:
94
- - - ">="
94
+ - - "~>"
95
95
  - !ruby/object:Gem::Version
96
- version: '0'
96
+ version: '1.11'
97
97
  - !ruby/object:Gem::Dependency
98
98
  name: eventmachine
99
99
  requirement: !ruby/object:Gem::Requirement
@@ -164,10 +164,11 @@ dependencies:
164
164
  - - ">="
165
165
  - !ruby/object:Gem::Version
166
166
  version: '0'
167
- description: A production-grade Ruby SDK for Dhan API v2 built for algo trading, portfolio
168
- monitoring, and live trading systems. Provides typed models, token lifecycle management,
169
- dry-validation contracts, resilient WebSocket streaming, and safety-focused order
170
- workflows for Ruby applications.
167
+ description: A production-grade Ruby SDK and Ruby client for Dhan API v2 built for
168
+ algorithmic trading, portfolio monitoring, and live trading systems on NSE, BSE
169
+ and MCX. Provides typed models, token lifecycle management, dry-validation contracts,
170
+ resilient WebSocket streaming with auto-reconnect, and safety-focused order workflows
171
+ for Ruby on Rails and standalone Ruby applications.
171
172
  email:
172
173
  - shubhamtaywade82@gmail.com
173
174
  executables:
@@ -228,8 +229,14 @@ files:
228
229
  - lib/DhanHQ/auth/token_generator.rb
229
230
  - lib/DhanHQ/auth/token_manager.rb
230
231
  - lib/DhanHQ/auth/token_renewal.rb
232
+ - lib/DhanHQ/backtest.rb
233
+ - lib/DhanHQ/backtest/result.rb
234
+ - lib/DhanHQ/backtest/runner.rb
235
+ - lib/DhanHQ/backtest/trade.rb
231
236
  - lib/DhanHQ/client.rb
237
+ - lib/DhanHQ/concerns/bang_writes.rb
232
238
  - lib/DhanHQ/concerns/order_audit.rb
239
+ - lib/DhanHQ/concerns/tracked_writes.rb
233
240
  - lib/DhanHQ/configuration.rb
234
241
  - lib/DhanHQ/constants.rb
235
242
  - lib/DhanHQ/contracts/alert_order_contract.rb
@@ -266,6 +273,7 @@ files:
266
273
  - lib/DhanHQ/core/base_model.rb
267
274
  - lib/DhanHQ/core/base_resource.rb
268
275
  - lib/DhanHQ/core/error_handler.rb
276
+ - lib/DhanHQ/deprecation.rb
269
277
  - lib/DhanHQ/dry_run/ledger.rb
270
278
  - lib/DhanHQ/dry_run/simulator.rb
271
279
  - lib/DhanHQ/error_object.rb
@@ -280,6 +288,7 @@ files:
280
288
  - lib/DhanHQ/helpers/response_helper.rb
281
289
  - lib/DhanHQ/helpers/validation_helper.rb
282
290
  - lib/DhanHQ/indicators.rb
291
+ - lib/DhanHQ/jobs/place_order_job.rb
283
292
  - lib/DhanHQ/json_loader.rb
284
293
  - lib/DhanHQ/market_data.rb
285
294
  - lib/DhanHQ/market_data/market_snapshot.rb
@@ -391,6 +400,7 @@ files:
391
400
  - lib/DhanHQ/utils/network_inspector.rb
392
401
  - lib/DhanHQ/version.rb
393
402
  - lib/DhanHQ/write_paths.rb
403
+ - lib/DhanHQ/write_result.rb
394
404
  - lib/DhanHQ/ws.rb
395
405
  - lib/DhanHQ/ws/base_connection.rb
396
406
  - lib/DhanHQ/ws/client.rb
@@ -429,6 +439,11 @@ files:
429
439
  - lib/dhanhq/analysis/multi_timeframe_analyzer.rb
430
440
  - lib/dhanhq/analysis/options_buying_advisor.rb
431
441
  - lib/dhanhq/contracts/options_buying_advisor_contract.rb
442
+ - lib/generators/dhanhq/install/install_generator.rb
443
+ - lib/generators/dhanhq/install/templates/initializer.rb
444
+ - lib/generators/dhanhq/install/templates/market_feed_channel.rb
445
+ - lib/generators/dhanhq/install/templates/market_feed_worker.rb
446
+ - lib/generators/dhanhq/install/templates/place_order_service.rb
432
447
  - lib/rubocop/cop/dhanhq/use_constants.rb
433
448
  - lib/ta.rb
434
449
  - lib/ta/candles.rb
@@ -465,14 +480,15 @@ files:
465
480
  - skills/dhanhq-ruby/scripts/resolve_security.rb
466
481
  - skills/dhanhq-ruby/scripts/trade_logger.rb
467
482
  - skills/dhanhq-ruby/scripts/validate_order.rb
468
- homepage: https://github.com/shubhamtaywade82/dhanhq-client
483
+ homepage: https://shubhamtaywade82.github.io/dhanhq-client/
469
484
  licenses:
470
485
  - MIT
471
486
  metadata:
472
487
  allowed_push_host: https://rubygems.org
473
- homepage_uri: https://github.com/shubhamtaywade82/dhanhq-client
488
+ homepage_uri: https://shubhamtaywade82.github.io/dhanhq-client/
474
489
  source_code_uri: https://github.com/shubhamtaywade82/dhanhq-client
475
490
  changelog_uri: https://github.com/shubhamtaywade82/dhanhq-client/blob/main/CHANGELOG.md
491
+ documentation_uri: https://shubhamtaywade82.github.io/dhanhq-client/
476
492
  rubygems_mfa_required: 'true'
477
493
  post_install_message:
478
494
  rdoc_options: []
@@ -492,5 +508,6 @@ requirements: []
492
508
  rubygems_version: 3.5.11
493
509
  signing_key:
494
510
  specification_version: 4
495
- summary: The Ruby SDK for Dhan API v2 with REST, WebSocket, and trading workflows.
511
+ summary: Production-grade Ruby SDK for Dhan API v2 with REST APIs, WebSocket market
512
+ data, token lifecycle management, dry-validation contracts and trading workflows.
496
513
  test_files: []