DhanHQ 3.3.0 → 4.0.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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +49 -0
- data/GUIDE.md +16 -0
- data/README.md +67 -17
- data/docs/CONFIGURATION.md +26 -0
- data/docs/RAILS_INTEGRATION.md +60 -17
- data/docs/TROUBLESHOOTING.md +15 -1
- data/lib/DhanHQ/backtest/result.rb +87 -0
- data/lib/DhanHQ/backtest/runner.rb +144 -0
- data/lib/DhanHQ/backtest/trade.rb +35 -0
- data/lib/DhanHQ/backtest.rb +24 -0
- data/lib/DhanHQ/client.rb +76 -26
- data/lib/DhanHQ/configuration.rb +40 -23
- data/lib/DhanHQ/core/base_api.rb +3 -2
- data/lib/DhanHQ/dry_run/simulator.rb +3 -2
- data/lib/DhanHQ/helpers/request_helper.rb +13 -3
- data/lib/DhanHQ/jobs/place_order_job.rb +47 -0
- data/lib/DhanHQ/resources/market_feed.rb +1 -1
- data/lib/DhanHQ/version.rb +1 -1
- data/lib/DhanHQ/ws/base_connection.rb +3 -1
- data/lib/DhanHQ/ws/client.rb +7 -5
- data/lib/DhanHQ/ws/connection.rb +1 -0
- data/lib/DhanHQ/ws/market_depth/client.rb +4 -3
- data/lib/DhanHQ/ws/orders/client.rb +4 -4
- data/lib/DhanHQ/ws/orders/connection.rb +2 -1
- data/lib/DhanHQ/ws.rb +25 -0
- data/lib/dhan_hq.rb +74 -46
- data/lib/generators/dhanhq/install/install_generator.rb +45 -0
- data/lib/generators/dhanhq/install/templates/initializer.rb +18 -0
- data/lib/generators/dhanhq/install/templates/market_feed_channel.rb +7 -0
- data/lib/generators/dhanhq/install/templates/market_feed_worker.rb +27 -0
- data/lib/generators/dhanhq/install/templates/place_order_service.rb +24 -0
- metadata +44 -31
data/lib/dhan_hq.rb
CHANGED
|
@@ -3,7 +3,11 @@
|
|
|
3
3
|
require "json"
|
|
4
4
|
require "logger"
|
|
5
5
|
require "zeitwerk"
|
|
6
|
-
|
|
6
|
+
begin
|
|
7
|
+
require "dotenv/load"
|
|
8
|
+
rescue LoadError
|
|
9
|
+
# Dotenv is optional in host applications
|
|
10
|
+
end
|
|
7
11
|
require "faraday"
|
|
8
12
|
# Minimal eager requires for backward-compatible constants.
|
|
9
13
|
# These are widely referenced (e.g. `DhanHQ::BaseAPI`) and should not depend on
|
|
@@ -88,9 +92,13 @@ module DhanHQ
|
|
|
88
92
|
# @return [String]
|
|
89
93
|
BASE_URL = Constants::Urls::REST_API_BASE
|
|
90
94
|
# The current configuration instance.
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
95
|
+
attr_reader :configuration
|
|
96
|
+
|
|
97
|
+
def configuration=(config)
|
|
98
|
+
@configuration = config
|
|
99
|
+
@default_client = nil
|
|
100
|
+
reset_model_resources!
|
|
101
|
+
end
|
|
94
102
|
|
|
95
103
|
attr_writer :logger
|
|
96
104
|
|
|
@@ -106,8 +114,10 @@ module DhanHQ
|
|
|
106
114
|
# @return [void]
|
|
107
115
|
def configure
|
|
108
116
|
self.configuration ||= Configuration.new
|
|
109
|
-
yield(configuration)
|
|
117
|
+
yield(configuration) if block_given?
|
|
110
118
|
self.logger ||= Logger.new($stdout, level: Logger::INFO)
|
|
119
|
+
@default_client = Client.new(config: configuration)
|
|
120
|
+
reset_model_resources!
|
|
111
121
|
end
|
|
112
122
|
|
|
113
123
|
# default logger so calls like DhanHQ.logger&.info never explode
|
|
@@ -118,6 +128,18 @@ module DhanHQ
|
|
|
118
128
|
@logger ||= Logger.new($stdout, level: Logger::INFO)
|
|
119
129
|
end
|
|
120
130
|
|
|
131
|
+
# Global default client (used for backward compatibility with class-level APIs)
|
|
132
|
+
#
|
|
133
|
+
# @return [DhanHQ::Client]
|
|
134
|
+
def default_client
|
|
135
|
+
@default_client ||= Client.new(config: ensure_configuration!)
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
# Resets the global default client
|
|
139
|
+
def reset_default_client!
|
|
140
|
+
@default_client = nil
|
|
141
|
+
end
|
|
142
|
+
|
|
121
143
|
# Configures the DhanHQ client using environment variables.
|
|
122
144
|
#
|
|
123
145
|
# When credentials are injected via `DHAN_ACCESS_TOKEN` and `DHAN_CLIENT_ID` this helper
|
|
@@ -129,6 +151,8 @@ module DhanHQ
|
|
|
129
151
|
# @return [void]
|
|
130
152
|
def configure_with_env
|
|
131
153
|
self.configuration = Configuration.new
|
|
154
|
+
@default_client = Client.new(config: configuration)
|
|
155
|
+
reset_model_resources!
|
|
132
156
|
end
|
|
133
157
|
|
|
134
158
|
# Ensures configuration exists, bootstrapping from ENV when nil.
|
|
@@ -140,9 +164,32 @@ module DhanHQ
|
|
|
140
164
|
self.configuration ||= Configuration.new
|
|
141
165
|
end
|
|
142
166
|
|
|
143
|
-
# Resets the configuration to nil.
|
|
167
|
+
# Resets the configuration and default client to nil.
|
|
144
168
|
def reset_configuration!
|
|
145
169
|
self.configuration = nil
|
|
170
|
+
@default_client = nil
|
|
171
|
+
reset_model_resources!
|
|
172
|
+
end
|
|
173
|
+
|
|
174
|
+
# Clears memoized class-level resource and API instances across all models.
|
|
175
|
+
def reset_model_resources!
|
|
176
|
+
models = [
|
|
177
|
+
BaseModel,
|
|
178
|
+
Models::Order, Models::Holding, Models::Position, Models::HistoricalData,
|
|
179
|
+
Models::Trade, Models::OptionChain, Models::Funds, Models::AlertOrder,
|
|
180
|
+
Models::ForeverOrder, Models::IcebergOrder, Models::SuperOrder, Models::TwapOrder,
|
|
181
|
+
Models::LedgerEntry, Models::Instrument, Models::Edis, Models::Profile,
|
|
182
|
+
Models::Margin, Models::MultiOrder, Models::KillSwitch, Models::PnlExit,
|
|
183
|
+
Models::MarketFeed, Models::ExpiredOptionsData
|
|
184
|
+
]
|
|
185
|
+
|
|
186
|
+
models.each do |klass|
|
|
187
|
+
next unless klass.is_a?(Class)
|
|
188
|
+
|
|
189
|
+
klass.instance_variables.each do |var|
|
|
190
|
+
klass.instance_variable_set(var, nil) if var.to_s.end_with?("resource", "api")
|
|
191
|
+
end
|
|
192
|
+
end
|
|
146
193
|
end
|
|
147
194
|
|
|
148
195
|
# Configures the DhanHQ client by fetching credentials from a token endpoint.
|
|
@@ -164,39 +211,14 @@ module DhanHQ
|
|
|
164
211
|
def configure_from_token_endpoint(base_url: nil, bearer_token: nil)
|
|
165
212
|
base_url ||= ENV.fetch("DHAN_TOKEN_ENDPOINT_BASE_URL", nil)
|
|
166
213
|
bearer_token ||= ENV.fetch("DHAN_TOKEN_ENDPOINT_BEARER", nil)
|
|
167
|
-
|
|
168
214
|
raise DhanHQ::TokenEndpointError, "base_url and bearer_token (or ENV DHAN_TOKEN_ENDPOINT_*) are required" if base_url.to_s.empty? || bearer_token.to_s.empty?
|
|
169
215
|
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
c.adapter ::Faraday.default_adapter
|
|
174
|
-
end
|
|
175
|
-
|
|
176
|
-
response = conn.get("") do |req|
|
|
177
|
-
req.headers["Authorization"] = "Bearer #{bearer_token}"
|
|
178
|
-
req.headers["Accept"] = "application/json"
|
|
179
|
-
end
|
|
180
|
-
|
|
181
|
-
unless response.success?
|
|
182
|
-
body = parse_json_body(response.body)
|
|
183
|
-
msg = body["error"] || body["message"] || body["errorMessage"] || response.body.to_s
|
|
184
|
-
raise DhanHQ::TokenEndpointError, "Token endpoint returned #{response.status}: #{msg}"
|
|
185
|
-
end
|
|
186
|
-
|
|
187
|
-
data = parse_json_body(response.body)
|
|
188
|
-
data = data.transform_keys(&:to_s) if data.is_a?(Hash)
|
|
189
|
-
|
|
190
|
-
access_token = data["access_token"] || data[:access_token]
|
|
191
|
-
client_id = data["client_id"] || data[:client_id]
|
|
216
|
+
data = fetch_token_endpoint("#{base_url.to_s.chomp("/")}/auth/dhan/token", bearer_token, "Token endpoint")
|
|
217
|
+
access_token = data["access_token"]
|
|
218
|
+
client_id = data["client_id"]
|
|
192
219
|
raise DhanHQ::TokenEndpointError, "Token endpoint response missing access_token or client_id" if access_token.to_s.empty? || client_id.to_s.empty?
|
|
193
220
|
|
|
194
|
-
|
|
195
|
-
configuration.access_token = access_token.to_s
|
|
196
|
-
configuration.client_id = client_id.to_s
|
|
197
|
-
dhan_base = data["base_url"] || data[:base_url]
|
|
198
|
-
configuration.base_url = dhan_base.to_s if dhan_base.to_s != ""
|
|
199
|
-
configuration
|
|
221
|
+
apply_token_config(access_token, client_id, data["base_url"])
|
|
200
222
|
end
|
|
201
223
|
|
|
202
224
|
# Configures the DhanHQ client by fetching credentials from a dashboard API.
|
|
@@ -208,6 +230,18 @@ module DhanHQ
|
|
|
208
230
|
def configure_from_dashboard(bearer_token:, url: "http://localhost:3011/api/dhan_access_token")
|
|
209
231
|
raise DhanHQ::TokenEndpointError, "bearer_token is required" if bearer_token.to_s.empty?
|
|
210
232
|
|
|
233
|
+
data = fetch_token_endpoint(url, bearer_token, "Dashboard")
|
|
234
|
+
access_token = data["access_token"] || data["accessToken"] || data["dhan_access_token"] || data["dhanaccesstoken"]
|
|
235
|
+
client_id = data["client_id"] || data["clientId"] || data["dhan_client_id"] || data["dhanClientId"]
|
|
236
|
+
client_id ||= configuration&.client_id || ENV.fetch("DHAN_CLIENT_ID", nil)
|
|
237
|
+
|
|
238
|
+
raise DhanHQ::TokenEndpointError, "Dashboard response missing access_token (tried access_token, dhan_access_token, dhanaccesstoken)" if access_token.to_s.empty?
|
|
239
|
+
raise DhanHQ::TokenEndpointError, "Dashboard response missing client_id, and no fallback client_id was found in config or ENV['DHAN_CLIENT_ID']" if client_id.to_s.empty?
|
|
240
|
+
|
|
241
|
+
apply_token_config(access_token, client_id, data["base_url"] || data["baseUrl"])
|
|
242
|
+
end
|
|
243
|
+
|
|
244
|
+
def fetch_token_endpoint(url, bearer_token, label)
|
|
211
245
|
conn = ::Faraday.new(url: url) do |c|
|
|
212
246
|
c.request :url_encoded
|
|
213
247
|
c.adapter ::Faraday.default_adapter
|
|
@@ -221,24 +255,18 @@ module DhanHQ
|
|
|
221
255
|
unless response.success?
|
|
222
256
|
body = parse_json_body(response.body)
|
|
223
257
|
msg = body["error"] || body["message"] || body["errorMessage"] || response.body.to_s
|
|
224
|
-
raise DhanHQ::TokenEndpointError, "
|
|
258
|
+
raise DhanHQ::TokenEndpointError, "#{label} returned #{response.status}: #{msg}"
|
|
225
259
|
end
|
|
226
260
|
|
|
227
261
|
data = parse_json_body(response.body)
|
|
228
|
-
data
|
|
229
|
-
|
|
230
|
-
access_token = data["access_token"] || data["accessToken"] || data["dhan_access_token"] || data["dhanaccesstoken"]
|
|
231
|
-
client_id = data["client_id"] || data["clientId"] || data["dhan_client_id"] || data["dhanClientId"]
|
|
232
|
-
client_id ||= self.configuration&.client_id || ENV.fetch("DHAN_CLIENT_ID", nil)
|
|
233
|
-
|
|
234
|
-
raise DhanHQ::TokenEndpointError, "Dashboard response missing access_token (tried access_token, dhan_access_token, dhanaccesstoken)" if access_token.to_s.empty?
|
|
235
|
-
raise DhanHQ::TokenEndpointError, "Dashboard response missing client_id, and no fallback client_id was found in config or ENV['DHAN_CLIENT_ID']" if client_id.to_s.empty?
|
|
262
|
+
data.is_a?(Hash) ? data.transform_keys(&:to_s) : {}
|
|
263
|
+
end
|
|
236
264
|
|
|
265
|
+
def apply_token_config(access_token, client_id, base_url)
|
|
237
266
|
self.configuration ||= Configuration.new
|
|
238
267
|
configuration.access_token = access_token.to_s
|
|
239
268
|
configuration.client_id = client_id.to_s
|
|
240
|
-
|
|
241
|
-
configuration.base_url = dhan_base.to_s if dhan_base.to_s != ""
|
|
269
|
+
configuration.base_url = base_url.to_s if base_url.to_s != ""
|
|
242
270
|
configuration
|
|
243
271
|
end
|
|
244
272
|
|
|
@@ -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,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
|
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:
|
|
4
|
+
version: 4.0.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-
|
|
11
|
+
date: 2026-10-10 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: activesupport
|
|
@@ -16,98 +16,98 @@ dependencies:
|
|
|
16
16
|
requirements:
|
|
17
17
|
- - ">="
|
|
18
18
|
- !ruby/object:Gem::Version
|
|
19
|
-
version: '0'
|
|
19
|
+
version: '8.0'
|
|
20
20
|
type: :runtime
|
|
21
21
|
prerelease: false
|
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
|
23
23
|
requirements:
|
|
24
24
|
- - ">="
|
|
25
25
|
- !ruby/object:Gem::Version
|
|
26
|
-
version: '0'
|
|
26
|
+
version: '8.0'
|
|
27
27
|
- !ruby/object:Gem::Dependency
|
|
28
28
|
name: base64
|
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
|
30
30
|
requirements:
|
|
31
31
|
- - ">="
|
|
32
32
|
- !ruby/object:Gem::Version
|
|
33
|
-
version: '0'
|
|
33
|
+
version: '0.1'
|
|
34
34
|
type: :runtime
|
|
35
35
|
prerelease: false
|
|
36
36
|
version_requirements: !ruby/object:Gem::Requirement
|
|
37
37
|
requirements:
|
|
38
38
|
- - ">="
|
|
39
39
|
- !ruby/object:Gem::Version
|
|
40
|
-
version: '0'
|
|
40
|
+
version: '0.1'
|
|
41
41
|
- !ruby/object:Gem::Dependency
|
|
42
42
|
name: bindata
|
|
43
43
|
requirement: !ruby/object:Gem::Requirement
|
|
44
44
|
requirements:
|
|
45
45
|
- - ">="
|
|
46
46
|
- !ruby/object:Gem::Version
|
|
47
|
-
version: '
|
|
47
|
+
version: '2.4'
|
|
48
48
|
type: :runtime
|
|
49
49
|
prerelease: false
|
|
50
50
|
version_requirements: !ruby/object:Gem::Requirement
|
|
51
51
|
requirements:
|
|
52
52
|
- - ">="
|
|
53
53
|
- !ruby/object:Gem::Version
|
|
54
|
-
version: '
|
|
54
|
+
version: '2.4'
|
|
55
55
|
- !ruby/object:Gem::Dependency
|
|
56
56
|
name: concurrent-ruby
|
|
57
57
|
requirement: !ruby/object:Gem::Requirement
|
|
58
58
|
requirements:
|
|
59
59
|
- - ">="
|
|
60
60
|
- !ruby/object:Gem::Version
|
|
61
|
-
version: '
|
|
61
|
+
version: '1.3'
|
|
62
62
|
type: :runtime
|
|
63
63
|
prerelease: false
|
|
64
64
|
version_requirements: !ruby/object:Gem::Requirement
|
|
65
65
|
requirements:
|
|
66
66
|
- - ">="
|
|
67
67
|
- !ruby/object:Gem::Version
|
|
68
|
-
version: '
|
|
68
|
+
version: '1.3'
|
|
69
69
|
- !ruby/object:Gem::Dependency
|
|
70
70
|
name: csv
|
|
71
71
|
requirement: !ruby/object:Gem::Requirement
|
|
72
72
|
requirements:
|
|
73
73
|
- - ">="
|
|
74
74
|
- !ruby/object:Gem::Version
|
|
75
|
-
version: '0'
|
|
75
|
+
version: '3.0'
|
|
76
76
|
type: :runtime
|
|
77
77
|
prerelease: false
|
|
78
78
|
version_requirements: !ruby/object:Gem::Requirement
|
|
79
79
|
requirements:
|
|
80
80
|
- - ">="
|
|
81
81
|
- !ruby/object:Gem::Version
|
|
82
|
-
version: '0'
|
|
82
|
+
version: '3.0'
|
|
83
83
|
- !ruby/object:Gem::Dependency
|
|
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.0'
|
|
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.0'
|
|
97
97
|
- !ruby/object:Gem::Dependency
|
|
98
98
|
name: eventmachine
|
|
99
99
|
requirement: !ruby/object:Gem::Requirement
|
|
100
100
|
requirements:
|
|
101
101
|
- - ">="
|
|
102
102
|
- !ruby/object:Gem::Version
|
|
103
|
-
version: '
|
|
103
|
+
version: '1.2'
|
|
104
104
|
type: :runtime
|
|
105
105
|
prerelease: false
|
|
106
106
|
version_requirements: !ruby/object:Gem::Requirement
|
|
107
107
|
requirements:
|
|
108
108
|
- - ">="
|
|
109
109
|
- !ruby/object:Gem::Version
|
|
110
|
-
version: '
|
|
110
|
+
version: '1.2'
|
|
111
111
|
- !ruby/object:Gem::Dependency
|
|
112
112
|
name: faraday
|
|
113
113
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -128,46 +128,47 @@ dependencies:
|
|
|
128
128
|
requirements:
|
|
129
129
|
- - ">="
|
|
130
130
|
- !ruby/object:Gem::Version
|
|
131
|
-
version: '0'
|
|
131
|
+
version: '0.11'
|
|
132
132
|
type: :runtime
|
|
133
133
|
prerelease: false
|
|
134
134
|
version_requirements: !ruby/object:Gem::Requirement
|
|
135
135
|
requirements:
|
|
136
136
|
- - ">="
|
|
137
137
|
- !ruby/object:Gem::Version
|
|
138
|
-
version: '0'
|
|
138
|
+
version: '0.11'
|
|
139
139
|
- !ruby/object:Gem::Dependency
|
|
140
140
|
name: rotp
|
|
141
141
|
requirement: !ruby/object:Gem::Requirement
|
|
142
142
|
requirements:
|
|
143
143
|
- - ">="
|
|
144
144
|
- !ruby/object:Gem::Version
|
|
145
|
-
version: '0'
|
|
145
|
+
version: '6.0'
|
|
146
146
|
type: :runtime
|
|
147
147
|
prerelease: false
|
|
148
148
|
version_requirements: !ruby/object:Gem::Requirement
|
|
149
149
|
requirements:
|
|
150
150
|
- - ">="
|
|
151
151
|
- !ruby/object:Gem::Version
|
|
152
|
-
version: '0'
|
|
152
|
+
version: '6.0'
|
|
153
153
|
- !ruby/object:Gem::Dependency
|
|
154
154
|
name: zeitwerk
|
|
155
155
|
requirement: !ruby/object:Gem::Requirement
|
|
156
156
|
requirements:
|
|
157
157
|
- - ">="
|
|
158
158
|
- !ruby/object:Gem::Version
|
|
159
|
-
version: '
|
|
159
|
+
version: '2.6'
|
|
160
160
|
type: :runtime
|
|
161
161
|
prerelease: false
|
|
162
162
|
version_requirements: !ruby/object:Gem::Requirement
|
|
163
163
|
requirements:
|
|
164
164
|
- - ">="
|
|
165
165
|
- !ruby/object:Gem::Version
|
|
166
|
-
version: '
|
|
167
|
-
description: A production-grade Ruby SDK for Dhan API v2 built for
|
|
168
|
-
monitoring, and live trading systems
|
|
169
|
-
|
|
170
|
-
|
|
166
|
+
version: '2.6'
|
|
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,6 +229,10 @@ 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
|
|
232
237
|
- lib/DhanHQ/concerns/bang_writes.rb
|
|
233
238
|
- lib/DhanHQ/concerns/order_audit.rb
|
|
@@ -283,6 +288,7 @@ files:
|
|
|
283
288
|
- lib/DhanHQ/helpers/response_helper.rb
|
|
284
289
|
- lib/DhanHQ/helpers/validation_helper.rb
|
|
285
290
|
- lib/DhanHQ/indicators.rb
|
|
291
|
+
- lib/DhanHQ/jobs/place_order_job.rb
|
|
286
292
|
- lib/DhanHQ/json_loader.rb
|
|
287
293
|
- lib/DhanHQ/market_data.rb
|
|
288
294
|
- lib/DhanHQ/market_data/market_snapshot.rb
|
|
@@ -433,6 +439,11 @@ files:
|
|
|
433
439
|
- lib/dhanhq/analysis/multi_timeframe_analyzer.rb
|
|
434
440
|
- lib/dhanhq/analysis/options_buying_advisor.rb
|
|
435
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
|
|
436
447
|
- lib/rubocop/cop/dhanhq/use_constants.rb
|
|
437
448
|
- lib/ta.rb
|
|
438
449
|
- lib/ta/candles.rb
|
|
@@ -469,14 +480,15 @@ files:
|
|
|
469
480
|
- skills/dhanhq-ruby/scripts/resolve_security.rb
|
|
470
481
|
- skills/dhanhq-ruby/scripts/trade_logger.rb
|
|
471
482
|
- skills/dhanhq-ruby/scripts/validate_order.rb
|
|
472
|
-
homepage: https://github.
|
|
483
|
+
homepage: https://shubhamtaywade82.github.io/dhanhq-client/
|
|
473
484
|
licenses:
|
|
474
485
|
- MIT
|
|
475
486
|
metadata:
|
|
476
487
|
allowed_push_host: https://rubygems.org
|
|
477
|
-
homepage_uri: https://github.
|
|
488
|
+
homepage_uri: https://shubhamtaywade82.github.io/dhanhq-client/
|
|
478
489
|
source_code_uri: https://github.com/shubhamtaywade82/dhanhq-client
|
|
479
490
|
changelog_uri: https://github.com/shubhamtaywade82/dhanhq-client/blob/main/CHANGELOG.md
|
|
491
|
+
documentation_uri: https://shubhamtaywade82.github.io/dhanhq-client/
|
|
480
492
|
rubygems_mfa_required: 'true'
|
|
481
493
|
post_install_message:
|
|
482
494
|
rdoc_options: []
|
|
@@ -496,5 +508,6 @@ requirements: []
|
|
|
496
508
|
rubygems_version: 3.5.11
|
|
497
509
|
signing_key:
|
|
498
510
|
specification_version: 4
|
|
499
|
-
summary:
|
|
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.
|
|
500
513
|
test_files: []
|