DhanHQ 2.1.3 → 2.1.5
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/.rubocop.yml +2 -0
- data/.rubocop_todo.yml +185 -0
- data/CHANGELOG.md +24 -0
- data/GUIDE.md +44 -44
- data/README.md +23 -3
- data/docs/rails_integration.md +1 -1
- data/docs/technical_analysis.md +1 -0
- data/lib/DhanHQ/config.rb +1 -0
- data/lib/DhanHQ/contracts/modify_order_contract.rb +1 -0
- data/lib/DhanHQ/contracts/option_chain_contract.rb +11 -1
- data/lib/DhanHQ/models/option_chain.rb +2 -0
- data/lib/DhanHQ/rate_limiter.rb +4 -2
- data/lib/DhanHQ/version.rb +1 -1
- data/lib/DhanHQ/ws/client.rb +1 -1
- data/lib/DhanHQ/ws/connection.rb +1 -1
- data/lib/DhanHQ/ws/orders/client.rb +3 -0
- data/lib/DhanHQ/ws/orders/connection.rb +5 -6
- data/lib/DhanHQ/ws/orders.rb +3 -2
- data/lib/DhanHQ/ws/registry.rb +1 -0
- data/lib/DhanHQ/ws/segments.rb +4 -4
- data/lib/DhanHQ/ws/sub_state.rb +1 -1
- data/lib/dhanhq/analysis/helpers/bias_aggregator.rb +18 -18
- data/lib/dhanhq/analysis/helpers/moneyness_helper.rb +1 -0
- data/lib/dhanhq/analysis/multi_timeframe_analyzer.rb +2 -0
- data/lib/dhanhq/analysis/options_buying_advisor.rb +4 -3
- data/lib/dhanhq/contracts/options_buying_advisor_contract.rb +1 -0
- data/lib/ta/candles.rb +1 -0
- data/lib/ta/fetcher.rb +1 -0
- data/lib/ta/indicators.rb +2 -1
- data/lib/ta/market_calendar.rb +4 -3
- data/lib/ta/technical_analysis.rb +3 -2
- metadata +4 -4
- data/lib/DhanHQ/ws/errors.rb +0 -0
- /data/lib/DhanHQ/contracts/{modify_order_contract copy.rb → modify_order_contract_copy.rb} +0 -0
- /data/lib/{DhanHQ.rb → dhan_hq.rb} +0 -0
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: cff831c4d2dc881c38a4ecc6274ad9ba8601fd6c6910815ab9ec40fde31af564
|
4
|
+
data.tar.gz: 00b892609e077902e1137b789dcd3a6d0e51fa3e042d38b05c39fc92971da157
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: f476df43f8f9500515101c85d423b91f713f384e89a01f316ed66265d69502a45b4adf7c2cd06aa6c8d466e65d5548e0337361844305e6f9103df0dd6a7564f3
|
7
|
+
data.tar.gz: aa14913dfa8590e8ba52e2f49a97b4cf475ad6d0058deac7114c1c56cb3817c70a38655afa4b30ef9741ab3da82f53c9d719dec70bef70bb9e242f7081a7392e
|
data/.rubocop.yml
CHANGED
data/.rubocop_todo.yml
ADDED
@@ -0,0 +1,185 @@
|
|
1
|
+
# This configuration was generated by
|
2
|
+
# `rubocop --auto-gen-config --auto-gen-only-exclude --exclude-limit 999`
|
3
|
+
# on 2025-10-12 11:03:03 UTC using RuboCop version 1.80.2.
|
4
|
+
# The point is for the user to remove these configuration records
|
5
|
+
# one by one as the offenses are removed from the code base.
|
6
|
+
# Note that changes in the inspected code, or installation of new
|
7
|
+
# versions of RuboCop, may require this file to be generated again.
|
8
|
+
|
9
|
+
# Offense count: 4
|
10
|
+
# This cop supports safe autocorrection (--autocorrect).
|
11
|
+
# Configuration parameters: Max, AllowHeredoc, AllowURI, AllowQualifiedName, URISchemes, IgnoreCopDirectives, AllowedPatterns, SplitStrings.
|
12
|
+
# URISchemes: http, https
|
13
|
+
Layout/LineLength:
|
14
|
+
Exclude:
|
15
|
+
- 'lib/DhanHQ/ws/decoder.rb'
|
16
|
+
|
17
|
+
# Offense count: 2
|
18
|
+
# Configuration parameters: IgnoreLiteralBranches, IgnoreConstantBranches, IgnoreDuplicateElseBranch.
|
19
|
+
Lint/DuplicateBranch:
|
20
|
+
Exclude:
|
21
|
+
- 'bin/ta_strategy.rb'
|
22
|
+
- 'lib/dhanhq/analysis/helpers/bias_aggregator.rb'
|
23
|
+
|
24
|
+
# Offense count: 3
|
25
|
+
# Configuration parameters: AllowComments, AllowNil.
|
26
|
+
Lint/SuppressedException:
|
27
|
+
Exclude:
|
28
|
+
- 'lib/DhanHQ/ws/connection.rb'
|
29
|
+
- 'lib/DhanHQ/ws/registry.rb'
|
30
|
+
|
31
|
+
# Offense count: 42
|
32
|
+
# Configuration parameters: AllowedMethods, AllowedPatterns, CountRepeatedAttributes, Max.
|
33
|
+
Metrics/AbcSize:
|
34
|
+
Exclude:
|
35
|
+
- 'bin/ta_strategy.rb'
|
36
|
+
- 'bin/ws_feed_test.rb'
|
37
|
+
- 'lib/DhanHQ/helpers/response_helper.rb'
|
38
|
+
- 'lib/DhanHQ/models/order.rb'
|
39
|
+
- 'lib/DhanHQ/rate_limiter.rb'
|
40
|
+
- 'lib/DhanHQ/ws/client.rb'
|
41
|
+
- 'lib/DhanHQ/ws/connection.rb'
|
42
|
+
- 'lib/DhanHQ/ws/decoder.rb'
|
43
|
+
- 'lib/DhanHQ/ws/orders/connection.rb'
|
44
|
+
- 'lib/DhanHQ/ws/websocket_packet_parser.rb'
|
45
|
+
- 'lib/dhan_hq.rb'
|
46
|
+
- 'lib/dhanhq/analysis/helpers/bias_aggregator.rb'
|
47
|
+
- 'lib/dhanhq/analysis/multi_timeframe_analyzer.rb'
|
48
|
+
- 'lib/dhanhq/analysis/options_buying_advisor.rb'
|
49
|
+
- 'lib/ta/candles.rb'
|
50
|
+
- 'lib/ta/fetcher.rb'
|
51
|
+
- 'lib/ta/indicators.rb'
|
52
|
+
- 'lib/ta/technical_analysis.rb'
|
53
|
+
|
54
|
+
# Offense count: 1
|
55
|
+
# Configuration parameters: CountComments, Max, CountAsOne, AllowedMethods, AllowedPatterns, inherit_mode.
|
56
|
+
# AllowedMethods: refine
|
57
|
+
Metrics/BlockLength:
|
58
|
+
Exclude:
|
59
|
+
- 'lib/DhanHQ/ws/connection.rb'
|
60
|
+
|
61
|
+
# Offense count: 8
|
62
|
+
# Configuration parameters: CountComments, Max, CountAsOne.
|
63
|
+
Metrics/ClassLength:
|
64
|
+
Exclude:
|
65
|
+
- 'lib/DhanHQ/core/base_model.rb'
|
66
|
+
- 'lib/DhanHQ/models/order.rb'
|
67
|
+
- 'lib/DhanHQ/ws/connection.rb'
|
68
|
+
- 'lib/DhanHQ/ws/orders/connection.rb'
|
69
|
+
- 'lib/DhanHQ/ws/websocket_packet_parser.rb'
|
70
|
+
- 'lib/dhanhq/analysis/multi_timeframe_analyzer.rb'
|
71
|
+
- 'lib/dhanhq/analysis/options_buying_advisor.rb'
|
72
|
+
- 'lib/ta/technical_analysis.rb'
|
73
|
+
|
74
|
+
# Offense count: 25
|
75
|
+
# Configuration parameters: AllowedMethods, AllowedPatterns, Max.
|
76
|
+
Metrics/CyclomaticComplexity:
|
77
|
+
Exclude:
|
78
|
+
- 'bin/ta_strategy.rb'
|
79
|
+
- 'bin/ws_feed_test.rb'
|
80
|
+
- 'lib/DhanHQ/helpers/response_helper.rb'
|
81
|
+
- 'lib/DhanHQ/models/order.rb'
|
82
|
+
- 'lib/DhanHQ/ws/connection.rb'
|
83
|
+
- 'lib/DhanHQ/ws/decoder.rb'
|
84
|
+
- 'lib/DhanHQ/ws/orders/connection.rb'
|
85
|
+
- 'lib/DhanHQ/ws/segments.rb'
|
86
|
+
- 'lib/DhanHQ/ws/websocket_packet_parser.rb'
|
87
|
+
- 'lib/dhanhq/analysis/helpers/bias_aggregator.rb'
|
88
|
+
- 'lib/dhanhq/analysis/multi_timeframe_analyzer.rb'
|
89
|
+
- 'lib/dhanhq/analysis/options_buying_advisor.rb'
|
90
|
+
- 'lib/ta/candles.rb'
|
91
|
+
- 'lib/ta/indicators.rb'
|
92
|
+
- 'lib/ta/technical_analysis.rb'
|
93
|
+
|
94
|
+
# Offense count: 52
|
95
|
+
# Configuration parameters: CountComments, Max, CountAsOne, AllowedMethods, AllowedPatterns.
|
96
|
+
Metrics/MethodLength:
|
97
|
+
Exclude:
|
98
|
+
- 'bin/ta_strategy.rb'
|
99
|
+
- 'bin/ws_feed_test.rb'
|
100
|
+
- 'lib/DhanHQ/core/base_model.rb'
|
101
|
+
- 'lib/DhanHQ/helpers/response_helper.rb'
|
102
|
+
- 'lib/DhanHQ/models/holding.rb'
|
103
|
+
- 'lib/DhanHQ/models/instrument.rb'
|
104
|
+
- 'lib/DhanHQ/models/ledger_entry.rb'
|
105
|
+
- 'lib/DhanHQ/models/order.rb'
|
106
|
+
- 'lib/DhanHQ/rate_limiter.rb'
|
107
|
+
- 'lib/DhanHQ/ws/connection.rb'
|
108
|
+
- 'lib/DhanHQ/ws/decoder.rb'
|
109
|
+
- 'lib/DhanHQ/ws/orders/connection.rb'
|
110
|
+
- 'lib/DhanHQ/ws/segments.rb'
|
111
|
+
- 'lib/DhanHQ/ws/singleton_lock.rb'
|
112
|
+
- 'lib/DhanHQ/ws/websocket_packet_parser.rb'
|
113
|
+
- 'lib/dhanhq/analysis/helpers/bias_aggregator.rb'
|
114
|
+
- 'lib/dhanhq/analysis/multi_timeframe_analyzer.rb'
|
115
|
+
- 'lib/dhanhq/analysis/options_buying_advisor.rb'
|
116
|
+
- 'lib/ta/candles.rb'
|
117
|
+
- 'lib/ta/fetcher.rb'
|
118
|
+
- 'lib/ta/indicators.rb'
|
119
|
+
- 'lib/ta/technical_analysis.rb'
|
120
|
+
|
121
|
+
# Offense count: 3
|
122
|
+
# Configuration parameters: CountComments, Max, CountAsOne.
|
123
|
+
Metrics/ModuleLength:
|
124
|
+
Exclude:
|
125
|
+
- 'bin/ta_strategy.rb'
|
126
|
+
- 'lib/DhanHQ/constants.rb'
|
127
|
+
- 'lib/ta/indicators.rb'
|
128
|
+
|
129
|
+
# Offense count: 1
|
130
|
+
# Configuration parameters: Max, CountKeywordArgs, MaxOptionalParameters.
|
131
|
+
Metrics/ParameterLists:
|
132
|
+
Exclude:
|
133
|
+
- 'lib/ta/technical_analysis.rb'
|
134
|
+
|
135
|
+
# Offense count: 21
|
136
|
+
# Configuration parameters: AllowedMethods, AllowedPatterns, Max.
|
137
|
+
Metrics/PerceivedComplexity:
|
138
|
+
Exclude:
|
139
|
+
- 'bin/ta_strategy.rb'
|
140
|
+
- 'bin/ws_feed_test.rb'
|
141
|
+
- 'lib/DhanHQ/helpers/response_helper.rb'
|
142
|
+
- 'lib/DhanHQ/models/order.rb'
|
143
|
+
- 'lib/DhanHQ/ws/connection.rb'
|
144
|
+
- 'lib/DhanHQ/ws/decoder.rb'
|
145
|
+
- 'lib/DhanHQ/ws/orders/connection.rb'
|
146
|
+
- 'lib/dhanhq/analysis/helpers/bias_aggregator.rb'
|
147
|
+
- 'lib/dhanhq/analysis/multi_timeframe_analyzer.rb'
|
148
|
+
- 'lib/dhanhq/analysis/options_buying_advisor.rb'
|
149
|
+
- 'lib/ta/candles.rb'
|
150
|
+
- 'lib/ta/indicators.rb'
|
151
|
+
- 'lib/ta/technical_analysis.rb'
|
152
|
+
|
153
|
+
# Offense count: 1
|
154
|
+
# Configuration parameters: MinNameLength, AllowNamesEndingInNumbers, AllowedNames, ForbiddenNames.
|
155
|
+
# AllowedNames: as, at, by, cc, db, id, if, in, io, ip, of, on, os, pp, to
|
156
|
+
Naming/MethodParameterName:
|
157
|
+
Exclude:
|
158
|
+
- 'lib/DhanHQ/ws/connection.rb'
|
159
|
+
|
160
|
+
# Offense count: 7
|
161
|
+
# Configuration parameters: Mode, AllowedMethods, AllowedPatterns, AllowBangMethods, WaywardPredicates.
|
162
|
+
# AllowedMethods: call
|
163
|
+
# WaywardPredicates: nonzero?
|
164
|
+
Naming/PredicateMethod:
|
165
|
+
Exclude:
|
166
|
+
- 'lib/DhanHQ/models/forever_order.rb'
|
167
|
+
- 'lib/DhanHQ/models/order.rb'
|
168
|
+
- 'lib/DhanHQ/models/super_order.rb'
|
169
|
+
- 'lib/DhanHQ/ws/singleton_lock.rb'
|
170
|
+
|
171
|
+
# Offense count: 5
|
172
|
+
# Configuration parameters: EnforcedStyle, CheckMethodNames, CheckSymbols, AllowedIdentifiers, AllowedPatterns.
|
173
|
+
# SupportedStyles: snake_case, normalcase, non_integer
|
174
|
+
# AllowedIdentifiers: TLS1_1, TLS1_2, capture3, iso8601, rfc1123_date, rfc822, rfc2822, rfc3339, x86_64
|
175
|
+
Naming/VariableNumber:
|
176
|
+
Exclude:
|
177
|
+
- 'lib/DhanHQ/ws/connection.rb'
|
178
|
+
- 'lib/DhanHQ/ws/orders/connection.rb'
|
179
|
+
|
180
|
+
# Offense count: 3
|
181
|
+
# Configuration parameters: MinSize.
|
182
|
+
Performance/CollectionLiteralInLoop:
|
183
|
+
Exclude:
|
184
|
+
- 'lib/dhanhq/analysis/multi_timeframe_analyzer.rb'
|
185
|
+
- 'lib/ta/fetcher.rb'
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,29 @@
|
|
1
1
|
## [Unreleased]
|
2
2
|
|
3
|
+
## [2.1.5] - 2025-01-27
|
4
|
+
|
5
|
+
### ⚠️ BREAKING CHANGES
|
6
|
+
- **Changed require statement**: `require 'DhanHQ'` → `require 'dhan_hq'`
|
7
|
+
- This affects all Ruby files that require the gem
|
8
|
+
- Update all `require 'DhanHQ'` statements to `require 'dhan_hq'` in your codebase
|
9
|
+
- The gem name remains `DhanHQ` in your Gemfile, only the require statement changes
|
10
|
+
|
11
|
+
### Added
|
12
|
+
- **OptionChain validation**: Added proper parameter validation for `OptionChain.fetch` and `OptionChain.fetch_expiry_list` methods
|
13
|
+
- `OptionChain.fetch` requires `underlying_scrip`, `underlying_seg`, and `expiry` parameters
|
14
|
+
- `OptionChain.fetch_expiry_list` requires only `underlying_scrip` and `underlying_seg` parameters
|
15
|
+
- Validates exchange segments against `%w[IDX_I NSE_FNO BSE_FNO MCX_FO]`
|
16
|
+
- Validates expiry format as `YYYY-MM-DD` and ensures it's a valid date
|
17
|
+
|
18
|
+
### Fixed
|
19
|
+
- **RuboCop compliance**: Fixed all RuboCop offenses (179 → 0 offenses)
|
20
|
+
- **Documentation**: Updated all documentation examples to use `require 'dhan_hq'`
|
21
|
+
- **Code quality**: Added comprehensive validation tests for OptionChain methods
|
22
|
+
|
23
|
+
### Changed
|
24
|
+
- **File structure**: Renamed main library file from `lib/DhanHQ.rb` to `lib/dhan_hq.rb` for better Ruby conventions
|
25
|
+
- **Require paths**: Updated all internal require statements to use snake_case naming
|
26
|
+
|
3
27
|
## [2.1.0] - 2025-09-20
|
4
28
|
|
5
29
|
- Add REST coverage for EDIS (`/edis/form`, `/edis/bulkform`, `/edis/tpin`, `/edis/inquire/{isin}`) and the account kill-switch endpoint.
|
data/GUIDE.md
CHANGED
@@ -32,7 +32,7 @@ bundle install
|
|
32
32
|
Bootstrap from environment variables:
|
33
33
|
|
34
34
|
```ruby
|
35
|
-
require '
|
35
|
+
require 'dhan_hq'
|
36
36
|
|
37
37
|
DhanHQ.configure_with_env
|
38
38
|
DhanHQ.logger.level = (ENV["DHAN_LOG_LEVEL"] || "INFO").upcase.then { |level| Logger.const_get(level) }
|
@@ -42,9 +42,9 @@ DhanHQ.logger.level = (ENV["DHAN_LOG_LEVEL"] || "INFO").upcase.then { |level| Lo
|
|
42
42
|
|
43
43
|
`configure_with_env` reads from `ENV` and raises unless both variables are set:
|
44
44
|
|
45
|
-
| Variable
|
46
|
-
|
|
47
|
-
| `CLIENT_ID`
|
45
|
+
| Variable | Description |
|
46
|
+
| -------------- | ---------------------------------------------------- |
|
47
|
+
| `CLIENT_ID` | Your Dhan trading client id. |
|
48
48
|
| `ACCESS_TOKEN` | REST/WebSocket access token generated via Dhan APIs. |
|
49
49
|
|
50
50
|
Provide them via `.env`, Rails credentials, or your secret manager of choice
|
@@ -55,14 +55,14 @@ before the initializer runs.
|
|
55
55
|
Set any of the following environment variables _before_ calling
|
56
56
|
`configure_with_env` to customise runtime behaviour:
|
57
57
|
|
58
|
-
| Variable
|
59
|
-
|
|
60
|
-
| `DHAN_LOG_LEVEL`
|
61
|
-
| `DHAN_BASE_URL`
|
62
|
-
| `DHAN_WS_VERSION`
|
63
|
-
| `DHAN_WS_ORDER_URL`
|
64
|
-
| `DHAN_WS_USER_TYPE`
|
65
|
-
| `DHAN_PARTNER_ID` / `DHAN_PARTNER_SECRET` | Required when streaming as a partner.
|
58
|
+
| Variable | Purpose |
|
59
|
+
| ----------------------------------------- | ---------------------------------------------------- |
|
60
|
+
| `DHAN_LOG_LEVEL` | Change logger verbosity (`INFO` default). |
|
61
|
+
| `DHAN_BASE_URL` | Override the REST API host. |
|
62
|
+
| `DHAN_WS_VERSION` | Target a specific WebSocket API version. |
|
63
|
+
| `DHAN_WS_ORDER_URL` | Customise the order update WebSocket endpoint. |
|
64
|
+
| `DHAN_WS_USER_TYPE` | Toggle between `SELF` and `PARTNER` streaming modes. |
|
65
|
+
| `DHAN_PARTNER_ID` / `DHAN_PARTNER_SECRET` | Required when streaming as a partner. |
|
66
66
|
|
67
67
|
---
|
68
68
|
|
@@ -106,32 +106,32 @@ order.refresh
|
|
106
106
|
|
107
107
|
Required fields validated by `DhanHQ::Contracts::PlaceOrderContract`:
|
108
108
|
|
109
|
-
| Key
|
110
|
-
|
|
111
|
-
| `transaction_type
|
112
|
-
| `exchange_segment
|
113
|
-
| `product_type`
|
114
|
-
| `order_type`
|
115
|
-
| `validity`
|
116
|
-
| `security_id`
|
117
|
-
| `quantity`
|
109
|
+
| Key | Type | Allowed Values / Notes |
|
110
|
+
| ------------------ | ------- | -------------------------------------------------- |
|
111
|
+
| `transaction_type` | String | `BUY`, `SELL` |
|
112
|
+
| `exchange_segment` | String | Use `DhanHQ::Constants::EXCHANGE_SEGMENTS` |
|
113
|
+
| `product_type` | String | `CNC`, `INTRADAY`, `MARGIN`, `MTF`, `CO`, `BO` |
|
114
|
+
| `order_type` | String | `LIMIT`, `MARKET`, `STOP_LOSS`, `STOP_LOSS_MARKET` |
|
115
|
+
| `validity` | String | `DAY`, `IOC` |
|
116
|
+
| `security_id` | String | Security identifier from the scrip master |
|
117
|
+
| `quantity` | Integer | Must be > 0 |
|
118
118
|
|
119
119
|
Optional fields and special rules:
|
120
120
|
|
121
|
-
| Key
|
122
|
-
|
|
123
|
-
| `correlation_id`
|
124
|
-
| `disclosed_quantity`
|
125
|
-
| `trading_symbol`
|
126
|
-
| `price`
|
127
|
-
| `trigger_price`
|
128
|
-
| `after_market_order`
|
129
|
-
| `amo_time`
|
130
|
-
| `bo_profit_value`
|
131
|
-
| `bo_stop_loss_value`
|
132
|
-
| `drv_expiry_date`
|
133
|
-
| `drv_option_type`
|
134
|
-
| `drv_strike_price`
|
121
|
+
| Key | Type | Notes |
|
122
|
+
| -------------------- | ------- | --------------------------------------------------------------------------------- |
|
123
|
+
| `correlation_id` | String | ≤ 25 chars; useful for idempotency |
|
124
|
+
| `disclosed_quantity` | Integer | ≥ 0 and ≤ 30% of `quantity` |
|
125
|
+
| `trading_symbol` | String | Optional label |
|
126
|
+
| `price` | Float | Mandatory for `LIMIT` |
|
127
|
+
| `trigger_price` | Float | Mandatory for SL / SLM |
|
128
|
+
| `after_market_order` | Boolean | Require `amo_time` when true |
|
129
|
+
| `amo_time` | String | `OPEN`, `OPEN_30`, `OPEN_60` (check `DhanHQ::Constants::AMO_TIMINGS` for updates) |
|
130
|
+
| `bo_profit_value` | Float | Required with `product_type: "BO"` |
|
131
|
+
| `bo_stop_loss_value` | Float | Required with `product_type: "BO"` |
|
132
|
+
| `drv_expiry_date` | String | Pass ISO `YYYY-MM-DD` for derivatives |
|
133
|
+
| `drv_option_type` | String | `CALL`, `PUT`, `NA` |
|
134
|
+
| `drv_strike_price` | Float | > 0 |
|
135
135
|
|
136
136
|
Example:
|
137
137
|
|
@@ -335,15 +335,15 @@ Both endpoints return arrays and skip validation because they represent historic
|
|
335
335
|
|
336
336
|
`DhanHQ::Models::HistoricalData` enforces `HistoricalDataContract` before delegating to `/v2/charts`.
|
337
337
|
|
338
|
-
| Key | Type
|
339
|
-
| ------------------ |
|
340
|
-
| `security_id` | String
|
341
|
-
| `exchange_segment` | String
|
342
|
-
| `instrument` | String
|
343
|
-
| `from_date` | String
|
344
|
-
| `to_date` | String
|
345
|
-
| `expiry_code` | Integer| Optional (`0`, `1`, `2`)
|
346
|
-
| `interval` | String
|
338
|
+
| Key | Type | Notes |
|
339
|
+
| ------------------ | ------- | -------------------------------------------------- |
|
340
|
+
| `security_id` | String | Required |
|
341
|
+
| `exchange_segment` | String | See `EXCHANGE_SEGMENTS` |
|
342
|
+
| `instrument` | String | Use `DhanHQ::Constants::INSTRUMENTS` |
|
343
|
+
| `from_date` | String | `YYYY-MM-DD` |
|
344
|
+
| `to_date` | String | `YYYY-MM-DD` |
|
345
|
+
| `expiry_code` | Integer | Optional (`0`, `1`, `2`) |
|
346
|
+
| `interval` | String | Optional (`1`, `5`, `15`, `25`, `60`) for intraday |
|
347
347
|
|
348
348
|
```ruby
|
349
349
|
bars = DhanHQ::Models::HistoricalData.intraday(
|
data/README.md
CHANGED
@@ -7,6 +7,26 @@ A clean Ruby client for **Dhan API v2** with ORM-like models (Orders, Positions,
|
|
7
7
|
* REST coverage: Orders, Super Orders, Forever Orders, Trades, Positions, Holdings, Funds/Margin, HistoricalData, OptionChain, MarketFeed
|
8
8
|
* **WebSocket**: subscribe/unsubscribe dynamically, auto-reconnect with backoff, 429 cool-off, idempotent subs, header+payload binary parsing, normalized ticks
|
9
9
|
|
10
|
+
## ⚠️ BREAKING CHANGE NOTICE
|
11
|
+
|
12
|
+
**IMPORTANT**: Starting from version 2.1.5, the require statement has changed:
|
13
|
+
|
14
|
+
```ruby
|
15
|
+
# OLD (deprecated)
|
16
|
+
require 'DhanHQ'
|
17
|
+
|
18
|
+
# NEW (current)
|
19
|
+
require 'dhan_hq'
|
20
|
+
```
|
21
|
+
|
22
|
+
**Migration**: Update all your `require 'DhanHQ'` statements to `require 'dhan_hq'` in your codebase. This change affects:
|
23
|
+
- All Ruby files that require the gem
|
24
|
+
- Documentation examples
|
25
|
+
- Scripts and automation tools
|
26
|
+
- Rails applications using this gem
|
27
|
+
|
28
|
+
The gem name remains `DhanHQ` in your Gemfile, only the require statement changes.
|
29
|
+
|
10
30
|
---
|
11
31
|
|
12
32
|
## Installation
|
@@ -36,7 +56,7 @@ gem install DhanHQ
|
|
36
56
|
### From ENV / .env
|
37
57
|
|
38
58
|
```ruby
|
39
|
-
require '
|
59
|
+
require 'dhan_hq'
|
40
60
|
|
41
61
|
DhanHQ.configure_with_env
|
42
62
|
DhanHQ.logger.level = (ENV["DHAN_LOG_LEVEL"] || "INFO").upcase.then { |level| Logger.const_get(level) }
|
@@ -155,7 +175,7 @@ initializers, service objects, workers, and ActionCable wiring tailored for the
|
|
155
175
|
### Start, subscribe, stop
|
156
176
|
|
157
177
|
```ruby
|
158
|
-
require '
|
178
|
+
require 'dhan_hq'
|
159
179
|
|
160
180
|
DhanHQ.configure_with_env
|
161
181
|
DhanHQ.logger.level = (ENV["DHAN_LOG_LEVEL"] || "INFO").upcase.then { |level| Logger.const_get(level) }
|
@@ -215,7 +235,7 @@ Receive live updates whenever your orders transition between states (placed →
|
|
215
235
|
### Standalone Ruby script
|
216
236
|
|
217
237
|
```ruby
|
218
|
-
require '
|
238
|
+
require 'dhan_hq'
|
219
239
|
|
220
240
|
DhanHQ.configure_with_env
|
221
241
|
DhanHQ.logger.level = (ENV["DHAN_LOG_LEVEL"] || "INFO").upcase.then { |level| Logger.const_get(level) }
|
data/docs/rails_integration.md
CHANGED
@@ -53,7 +53,7 @@ environment variables (Rails credentials can be copied into ENV on boot):
|
|
53
53
|
|
54
54
|
```ruby
|
55
55
|
# config/initializers/dhanhq.rb
|
56
|
-
require '
|
56
|
+
require 'dhan_hq'
|
57
57
|
|
58
58
|
if (creds = Rails.application.credentials.dig(:dhanhq))
|
59
59
|
ENV['CLIENT_ID'] ||= creds[:client_id]
|
data/docs/technical_analysis.md
CHANGED
data/lib/DhanHQ/config.rb
CHANGED
@@ -6,7 +6,7 @@ module DhanHQ
|
|
6
6
|
module Contracts
|
7
7
|
# **Validation contract for fetching option chain data**
|
8
8
|
#
|
9
|
-
# Validates request parameters for fetching option chains
|
9
|
+
# Validates request parameters for fetching option chains.
|
10
10
|
class OptionChainContract < BaseContract
|
11
11
|
params do
|
12
12
|
required(:underlying_scrip).filled(:integer) # Security ID
|
@@ -27,5 +27,15 @@ module DhanHQ
|
|
27
27
|
end
|
28
28
|
end
|
29
29
|
end
|
30
|
+
|
31
|
+
# **Validation contract for fetching option chain expiry list**
|
32
|
+
#
|
33
|
+
# Validates request parameters for fetching expiry lists (expiry not required).
|
34
|
+
class OptionChainExpiryListContract < BaseContract
|
35
|
+
params do
|
36
|
+
required(:underlying_scrip).filled(:integer) # Security ID
|
37
|
+
required(:underlying_seg).filled(:string, included_in?: %w[IDX_I NSE_FNO BSE_FNO MCX_FO])
|
38
|
+
end
|
39
|
+
end
|
30
40
|
end
|
31
41
|
end
|
@@ -34,6 +34,8 @@ module DhanHQ
|
|
34
34
|
# @param params [Hash] The request parameters (snake_case format)
|
35
35
|
# @return [Array<String>] The list of expiry dates
|
36
36
|
def fetch_expiry_list(params)
|
37
|
+
validate_params!(params, DhanHQ::Contracts::OptionChainExpiryListContract)
|
38
|
+
|
37
39
|
response = resource.expirylist(params)
|
38
40
|
response[:status] == "success" ? response[:data] : []
|
39
41
|
end
|
data/lib/DhanHQ/rate_limiter.rb
CHANGED
@@ -33,9 +33,11 @@ module DhanHQ
|
|
33
33
|
if @api_type == :option_chain
|
34
34
|
last_request_time = @buckets[:last_request_time]
|
35
35
|
|
36
|
-
sleep_time =
|
36
|
+
sleep_time = 3 - (Time.now - last_request_time)
|
37
37
|
if sleep_time.positive?
|
38
|
-
|
38
|
+
if ENV["DHAN_DEBUG"] == "true"
|
39
|
+
puts "Sleeping for #{sleep_time.round(2)} seconds due to option_chain rate limit"
|
40
|
+
end
|
39
41
|
sleep(sleep_time)
|
40
42
|
end
|
41
43
|
|
data/lib/DhanHQ/version.rb
CHANGED
data/lib/DhanHQ/ws/client.rb
CHANGED
@@ -161,7 +161,7 @@ module DhanHQ
|
|
161
161
|
|
162
162
|
private
|
163
163
|
|
164
|
-
def prune(
|
164
|
+
def prune(hash) = { ExchangeSegment: hash[:ExchangeSegment], SecurityId: hash[:SecurityId] }
|
165
165
|
|
166
166
|
def emit(event, payload)
|
167
167
|
begin
|
data/lib/DhanHQ/ws/connection.rb
CHANGED
@@ -94,7 +94,7 @@ module DhanHQ
|
|
94
94
|
backoff = 2.0
|
95
95
|
until @stop
|
96
96
|
failed = false
|
97
|
-
got_429 = false
|
97
|
+
got_429 = false
|
98
98
|
|
99
99
|
# respect any active cool-off window
|
100
100
|
sleep (@cooloff_until - Time.now).ceil if @cooloff_until && Time.now < @cooloff_until
|
@@ -6,6 +6,7 @@ require_relative "connection"
|
|
6
6
|
module DhanHQ
|
7
7
|
module WS
|
8
8
|
module Orders
|
9
|
+
# WebSocket client for real-time order updates
|
9
10
|
class Client
|
10
11
|
def initialize(url: nil)
|
11
12
|
@callbacks = Concurrent::Map.new { |h, k| h[k] = [] }
|
@@ -16,6 +17,7 @@ module DhanHQ
|
|
16
17
|
|
17
18
|
def start
|
18
19
|
return self if @started.true?
|
20
|
+
|
19
21
|
@started.make_true
|
20
22
|
@conn = Connection.new(url: @url) do |msg|
|
21
23
|
emit(:update, msg) if msg&.dig(:Type) == "order_alert"
|
@@ -28,6 +30,7 @@ module DhanHQ
|
|
28
30
|
|
29
31
|
def stop
|
30
32
|
return unless @started.true?
|
33
|
+
|
31
34
|
@started.make_false
|
32
35
|
@conn&.stop
|
33
36
|
emit(:close, true)
|
@@ -8,6 +8,7 @@ require "thread" # rubocop:disable Lint/RedundantRequireStatement
|
|
8
8
|
module DhanHQ
|
9
9
|
module WS
|
10
10
|
module Orders
|
11
|
+
# WebSocket connection for real-time order updates
|
11
12
|
class Connection
|
12
13
|
COOL_OFF_429 = 60
|
13
14
|
MAX_BACKOFF = 90
|
@@ -84,12 +85,10 @@ module DhanHQ
|
|
84
85
|
end
|
85
86
|
|
86
87
|
@ws.on :message do |ev|
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
DhanHQ.logger&.error("[DhanHQ::WS::Orders] bad JSON #{e.class}: #{e.message}")
|
92
|
-
end
|
88
|
+
msg = JSON.parse(ev.data, symbolize_names: true)
|
89
|
+
@on_json&.call(msg)
|
90
|
+
rescue StandardError => e
|
91
|
+
DhanHQ.logger&.error("[DhanHQ::WS::Orders] bad JSON #{e.class}: #{e.message}")
|
93
92
|
end
|
94
93
|
|
95
94
|
@ws.on :close do |ev|
|
data/lib/DhanHQ/ws/orders.rb
CHANGED
@@ -4,9 +4,10 @@ require_relative "orders/client"
|
|
4
4
|
|
5
5
|
module DhanHQ
|
6
6
|
module WS
|
7
|
+
# WebSocket orders module for real-time order updates
|
7
8
|
module Orders
|
8
|
-
def self.connect(&
|
9
|
-
Client.new.start.on(:update, &
|
9
|
+
def self.connect(&)
|
10
|
+
Client.new.start.on(:update, &)
|
10
11
|
end
|
11
12
|
end
|
12
13
|
end
|
data/lib/DhanHQ/ws/registry.rb
CHANGED
data/lib/DhanHQ/ws/segments.rb
CHANGED
@@ -47,11 +47,11 @@ module DhanHQ
|
|
47
47
|
# - ExchangeSegment is a STRING enum (e.g., "NSE_FNO")
|
48
48
|
# - SecurityId is a STRING
|
49
49
|
#
|
50
|
-
# @param
|
50
|
+
# @param hash [Hash]
|
51
51
|
# @return [Hash] Normalized instrument hash.
|
52
|
-
def self.normalize_instrument(
|
53
|
-
seg = to_request_string(
|
54
|
-
sid = (
|
52
|
+
def self.normalize_instrument(hash)
|
53
|
+
seg = to_request_string(hash[:ExchangeSegment] || hash["ExchangeSegment"])
|
54
|
+
sid = (hash[:SecurityId] || hash["SecurityId"]).to_s
|
55
55
|
{ ExchangeSegment: seg, SecurityId: sid }
|
56
56
|
end
|
57
57
|
|
data/lib/DhanHQ/ws/sub_state.rb
CHANGED
@@ -2,6 +2,7 @@
|
|
2
2
|
|
3
3
|
module DhanHQ
|
4
4
|
module Analysis
|
5
|
+
# Aggregates indicator scores across timeframes into a single bias and confidence score
|
5
6
|
class BiasAggregator
|
6
7
|
DEFAULT_WEIGHTS = { m1: 0.1, m5: 0.2, m15: 0.25, m25: 0.15, m60: 0.3 }.freeze
|
7
8
|
|
@@ -44,36 +45,35 @@ module DhanHQ
|
|
44
45
|
private
|
45
46
|
|
46
47
|
def score_tf(val)
|
47
|
-
rsi
|
48
|
+
rsi = val[:rsi]
|
48
49
|
macd = val[:macd] || {}
|
49
50
|
hist = macd[:hist]
|
50
|
-
adx
|
51
|
+
adx = val[:adx]
|
51
52
|
|
52
|
-
rsi_component =
|
53
|
-
|
53
|
+
rsi_component = if rsi.nil?
|
54
|
+
0.5
|
55
|
+
elsif rsi >= 55
|
56
|
+
0.65
|
57
|
+
elsif rsi <= 45
|
58
|
+
0.35
|
54
59
|
else
|
55
|
-
return 0.65 if rsi >= 55
|
56
|
-
return 0.35 if rsi <= 45
|
57
|
-
|
58
60
|
0.5
|
59
61
|
end
|
60
62
|
|
61
|
-
macd_component =
|
62
|
-
|
63
|
+
macd_component = if hist.nil?
|
64
|
+
0.5
|
63
65
|
else
|
64
66
|
hist >= 0 ? 0.6 : 0.4
|
65
67
|
end
|
66
68
|
|
67
|
-
adx_component =
|
68
|
-
|
69
|
+
adx_component = if adx.nil?
|
70
|
+
0.5
|
71
|
+
elsif adx >= @strong_adx
|
72
|
+
0.65
|
73
|
+
elsif adx >= @min_adx
|
74
|
+
0.55
|
69
75
|
else
|
70
|
-
|
71
|
-
0.65
|
72
|
-
elsif adx >= @min_adx
|
73
|
-
0.55
|
74
|
-
else
|
75
|
-
0.45
|
76
|
-
end
|
76
|
+
0.45
|
77
77
|
end
|
78
78
|
|
79
79
|
(rsi_component * 0.4) + (macd_component * 0.3) + (adx_component * 0.3)
|
@@ -5,7 +5,9 @@ require "dry/validation"
|
|
5
5
|
|
6
6
|
module DhanHQ
|
7
7
|
module Analysis
|
8
|
+
# Analyzes multi-timeframe indicator data to produce a consolidated bias summary
|
8
9
|
class MultiTimeframeAnalyzer
|
10
|
+
# Input validation contract for multi-timeframe analyzer
|
9
11
|
class InputContract < Dry::Validation::Contract
|
10
12
|
params do
|
11
13
|
required(:meta).filled(:hash)
|
@@ -6,6 +6,7 @@ require_relative "../contracts/options_buying_advisor_contract"
|
|
6
6
|
|
7
7
|
module DhanHQ
|
8
8
|
module Analysis
|
9
|
+
# Options buying advisor for INDEX options (NIFTY/BANKNIFTY/SENSEX)
|
9
10
|
class OptionsBuyingAdvisor
|
10
11
|
DEFAULT_CONFIG = {
|
11
12
|
timeframe_weights: { m1: 0.1, m5: 0.2, m15: 0.25, m25: 0.15, m60: 0.3 },
|
@@ -229,10 +230,10 @@ module DhanHQ
|
|
229
230
|
{ decision: :no_trade, reason: reason }
|
230
231
|
end
|
231
232
|
|
232
|
-
def deep_merge(
|
233
|
-
return
|
233
|
+
def deep_merge(first_hash, second_hash)
|
234
|
+
return first_hash unless second_hash
|
234
235
|
|
235
|
-
|
236
|
+
first_hash.merge(second_hash) { |_, av, bv| av.is_a?(Hash) && bv.is_a?(Hash) ? deep_merge(av, bv) : bv }
|
236
237
|
end
|
237
238
|
|
238
239
|
def deep_symbolize(obj)
|
data/lib/ta/candles.rb
CHANGED
data/lib/ta/fetcher.rb
CHANGED
data/lib/ta/indicators.rb
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module TA
|
4
|
+
# Technical indicator calculations with fallback implementations
|
4
5
|
module Indicators
|
5
6
|
module_function
|
6
7
|
|
@@ -9,7 +10,7 @@ module TA
|
|
9
10
|
|
10
11
|
k = 2.0 / (period + 1)
|
11
12
|
series.each_with_index.reduce(nil) do |ema_prev, (v, i)|
|
12
|
-
i
|
13
|
+
i.zero? ? v.to_f : (v.to_f * k) + ((ema_prev || v.to_f) * (1 - k))
|
13
14
|
end
|
14
15
|
end
|
15
16
|
|
data/lib/ta/market_calendar.rb
CHANGED
@@ -3,6 +3,7 @@
|
|
3
3
|
require "date"
|
4
4
|
|
5
5
|
module TA
|
6
|
+
# Market calendar utilities for trading day calculations
|
6
7
|
module MarketCalendar
|
7
8
|
MARKET_HOLIDAYS = [
|
8
9
|
Date.new(2025, 8, 15),
|
@@ -35,12 +36,12 @@ module TA
|
|
35
36
|
trading_day?(Date.today) ? Date.today : last_trading_day(from: Date.today)
|
36
37
|
end
|
37
38
|
|
38
|
-
def self.trading_days_ago(date,
|
39
|
-
raise ArgumentError, "
|
39
|
+
def self.trading_days_ago(date, days)
|
40
|
+
raise ArgumentError, "days must be >= 0" if days.to_i.negative?
|
40
41
|
|
41
42
|
d = trading_day?(date) ? date : today_or_last_trading_day
|
42
43
|
count = 0
|
43
|
-
while count <
|
44
|
+
while count < days
|
44
45
|
d = prev_trading_day(from: d)
|
45
46
|
count += 1
|
46
47
|
end
|
@@ -16,13 +16,14 @@ rescue LoadError => e
|
|
16
16
|
warn "technical-analysis not available: #{e.message}"
|
17
17
|
end
|
18
18
|
|
19
|
-
require "
|
19
|
+
require "dhan_hq"
|
20
20
|
require_relative "market_calendar"
|
21
21
|
require_relative "candles"
|
22
22
|
require_relative "indicators"
|
23
23
|
require_relative "fetcher"
|
24
24
|
|
25
25
|
module TA
|
26
|
+
# Main technical analysis orchestrator for multi-timeframe indicator computation
|
26
27
|
class TechnicalAnalysis
|
27
28
|
DEFAULTS = {
|
28
29
|
rsi_period: 14,
|
@@ -122,7 +123,7 @@ module TA
|
|
122
123
|
end
|
123
124
|
|
124
125
|
def normalize_from_date(from_date, to_date, days_back)
|
125
|
-
if (from_date.nil? || from_date.to_s.strip.empty?) && days_back
|
126
|
+
if (from_date.nil? || from_date.to_s.strip.empty?) && days_back&.to_i&.positive?
|
126
127
|
to_d = Date.parse(to_date)
|
127
128
|
n_back = [days_back.to_i - 1, 0].max
|
128
129
|
return MarketCalendar.trading_days_ago(to_d, n_back).strftime("%Y-%m-%d")
|
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.
|
4
|
+
version: 2.1.5
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Shubham Taywade
|
@@ -145,6 +145,7 @@ extra_rdoc_files: []
|
|
145
145
|
files:
|
146
146
|
- ".rspec"
|
147
147
|
- ".rubocop.yml"
|
148
|
+
- ".rubocop_todo.yml"
|
148
149
|
- CHANGELOG.md
|
149
150
|
- CODE_OF_CONDUCT.md
|
150
151
|
- GUIDE.md
|
@@ -164,7 +165,6 @@ files:
|
|
164
165
|
- docs/rails_integration.md
|
165
166
|
- docs/technical_analysis.md
|
166
167
|
- exe/DhanHQ
|
167
|
-
- lib/DhanHQ.rb
|
168
168
|
- lib/DhanHQ/client.rb
|
169
169
|
- lib/DhanHQ/config.rb
|
170
170
|
- lib/DhanHQ/configuration.rb
|
@@ -173,8 +173,8 @@ files:
|
|
173
173
|
- lib/DhanHQ/contracts/historical_data_contract.rb
|
174
174
|
- lib/DhanHQ/contracts/instrument_list_contract.rb
|
175
175
|
- lib/DhanHQ/contracts/margin_calculator_contract.rb
|
176
|
-
- lib/DhanHQ/contracts/modify_order_contract copy.rb
|
177
176
|
- lib/DhanHQ/contracts/modify_order_contract.rb
|
177
|
+
- lib/DhanHQ/contracts/modify_order_contract_copy.rb
|
178
178
|
- lib/DhanHQ/contracts/option_chain_contract.rb
|
179
179
|
- lib/DhanHQ/contracts/order_contract.rb
|
180
180
|
- lib/DhanHQ/contracts/place_order_contract.rb
|
@@ -235,7 +235,6 @@ files:
|
|
235
235
|
- lib/DhanHQ/ws/cmd_bus.rb
|
236
236
|
- lib/DhanHQ/ws/connection.rb
|
237
237
|
- lib/DhanHQ/ws/decoder.rb
|
238
|
-
- lib/DhanHQ/ws/errors.rb
|
239
238
|
- lib/DhanHQ/ws/orders.rb
|
240
239
|
- lib/DhanHQ/ws/orders/client.rb
|
241
240
|
- lib/DhanHQ/ws/orders/connection.rb
|
@@ -255,6 +254,7 @@ files:
|
|
255
254
|
- lib/DhanHQ/ws/singleton_lock.rb
|
256
255
|
- lib/DhanHQ/ws/sub_state.rb
|
257
256
|
- lib/DhanHQ/ws/websocket_packet_parser.rb
|
257
|
+
- lib/dhan_hq.rb
|
258
258
|
- lib/dhanhq/analysis/helpers/bias_aggregator.rb
|
259
259
|
- lib/dhanhq/analysis/helpers/moneyness_helper.rb
|
260
260
|
- lib/dhanhq/analysis/multi_timeframe_analyzer.rb
|
data/lib/DhanHQ/ws/errors.rb
DELETED
File without changes
|
File without changes
|
File without changes
|