DhanHQ 2.1.0 → 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 +40 -14
- data/docs/rails_integration.md +1 -1
- data/docs/technical_analysis.md +144 -0
- data/lib/DhanHQ/config.rb +1 -0
- data/lib/DhanHQ/constants.rb +4 -6
- data/lib/DhanHQ/contracts/instrument_list_contract.rb +12 -0
- data/lib/DhanHQ/contracts/modify_order_contract.rb +1 -0
- data/lib/DhanHQ/contracts/option_chain_contract.rb +11 -1
- data/lib/DhanHQ/helpers/request_helper.rb +5 -1
- data/lib/DhanHQ/models/instrument.rb +56 -0
- data/lib/DhanHQ/models/option_chain.rb +2 -0
- data/lib/DhanHQ/rate_limiter.rb +4 -2
- data/lib/DhanHQ/resources/instruments.rb +28 -0
- 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.rb → dhan_hq.rb} +8 -0
- data/lib/dhanhq/analysis/helpers/bias_aggregator.rb +83 -0
- data/lib/dhanhq/analysis/helpers/moneyness_helper.rb +24 -0
- data/lib/dhanhq/analysis/multi_timeframe_analyzer.rb +232 -0
- data/lib/dhanhq/analysis/options_buying_advisor.rb +251 -0
- data/lib/dhanhq/contracts/options_buying_advisor_contract.rb +24 -0
- data/lib/ta/candles.rb +52 -0
- data/lib/ta/fetcher.rb +70 -0
- data/lib/ta/indicators.rb +169 -0
- data/lib/ta/market_calendar.rb +51 -0
- data/lib/ta/technical_analysis.rb +94 -303
- data/lib/ta.rb +7 -0
- metadata +18 -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
@@ -16,53 +16,14 @@ rescue LoadError => e
|
|
16
16
|
warn "technical-analysis not available: #{e.message}"
|
17
17
|
end
|
18
18
|
|
19
|
-
require "
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
Date.new(2025, 8, 15),
|
25
|
-
Date.new(2025, 10, 2),
|
26
|
-
Date.new(2025, 8, 27)
|
27
|
-
].freeze
|
28
|
-
|
29
|
-
def self.weekday?(date)
|
30
|
-
w = date.wday
|
31
|
-
w >= 1 && w <= 5
|
32
|
-
end
|
33
|
-
|
34
|
-
def self.trading_day?(date)
|
35
|
-
weekday?(date) && !MARKET_HOLIDAYS.include?(date)
|
36
|
-
end
|
37
|
-
|
38
|
-
def self.last_trading_day(from: Date.today)
|
39
|
-
d = from
|
40
|
-
d -= 1 until trading_day?(d)
|
41
|
-
d
|
42
|
-
end
|
43
|
-
|
44
|
-
def self.today_or_last_trading_day
|
45
|
-
trading_day?(Date.today) ? Date.today : last_trading_day(from: Date.today)
|
46
|
-
end
|
47
|
-
|
48
|
-
# Returns the trading day N days back from the given trading day.
|
49
|
-
# Example: trading_days_ago(2025-10-07, 0) -> 2025-10-07 (if trading day)
|
50
|
-
# trading_days_ago(2025-10-07, 1) -> previous trading day
|
51
|
-
def self.trading_days_ago(date, n)
|
52
|
-
raise ArgumentError, "n must be >= 0" if n.to_i.negative?
|
53
|
-
|
54
|
-
d = trading_day?(date) ? date : today_or_last_trading_day
|
55
|
-
count = 0
|
56
|
-
while count < n
|
57
|
-
d = last_trading_day(from: d)
|
58
|
-
count += 1
|
59
|
-
end
|
60
|
-
d
|
61
|
-
end
|
62
|
-
end
|
63
|
-
end
|
19
|
+
require "dhan_hq"
|
20
|
+
require_relative "market_calendar"
|
21
|
+
require_relative "candles"
|
22
|
+
require_relative "indicators"
|
23
|
+
require_relative "fetcher"
|
64
24
|
|
65
25
|
module TA
|
26
|
+
# Main technical analysis orchestrator for multi-timeframe indicator computation
|
66
27
|
class TechnicalAnalysis
|
67
28
|
DEFAULTS = {
|
68
29
|
rsi_period: 14,
|
@@ -70,24 +31,27 @@ module TA
|
|
70
31
|
adx_period: 14,
|
71
32
|
macd_fast: 12,
|
72
33
|
macd_slow: 26,
|
73
|
-
macd_signal: 9
|
34
|
+
macd_signal: 9,
|
35
|
+
throttle_seconds: 1.0,
|
36
|
+
max_retries: 3
|
74
37
|
}.freeze
|
75
38
|
|
76
39
|
def initialize(options = {})
|
77
40
|
@opts = DEFAULTS.merge(options.transform_keys(&:to_sym))
|
41
|
+
@fetcher = Fetcher.new(throttle_seconds: @opts[:throttle_seconds], max_retries: @opts[:max_retries])
|
78
42
|
end
|
79
43
|
|
80
44
|
def compute(exchange_segment:, instrument:, security_id:, from_date: nil, to_date: nil, days_back: nil,
|
81
45
|
intervals: [1, 5, 15, 25, 60])
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
46
|
+
# Normalize to_date: default to last trading day; if provided and non-trading, roll back
|
47
|
+
to_date = normalize_to_date(to_date)
|
48
|
+
|
49
|
+
# Auto-calculate required trading days if not provided
|
50
|
+
days_back = auto_days_needed(intervals) if days_back.nil? || days_back.to_i <= 0
|
51
|
+
|
52
|
+
# Derive/normalize from_date
|
53
|
+
from_date = normalize_from_date(from_date, to_date, days_back)
|
54
|
+
|
91
55
|
base_params = {
|
92
56
|
exchange_segment: exchange_segment,
|
93
57
|
instrument: instrument,
|
@@ -96,17 +60,15 @@ module TA
|
|
96
60
|
to_date: to_date
|
97
61
|
}
|
98
62
|
|
99
|
-
one_min_candles = candles(fetch_intraday_windowed(base_params, 1))
|
100
|
-
|
101
63
|
frames = {}
|
64
|
+
interval_key = { 1 => :m1, 5 => :m5, 15 => :m15, 25 => :m25, 60 => :m60 }
|
102
65
|
intervals.each do |ivl|
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
end
|
66
|
+
key = interval_key[ivl.to_i]
|
67
|
+
next unless key
|
68
|
+
|
69
|
+
raw = @fetcher.intraday_windowed(base_params, ivl.to_i)
|
70
|
+
frames[key] = Candles.from_series(raw)
|
71
|
+
sleep_with_jitter # throttle between intervals
|
110
72
|
end
|
111
73
|
|
112
74
|
{
|
@@ -123,15 +85,15 @@ module TA
|
|
123
85
|
|
124
86
|
def compute_from_file(path:, base_interval: 1, intervals: [1, 5, 15, 25, 60])
|
125
87
|
raw = JSON.parse(File.read(path))
|
126
|
-
base =
|
88
|
+
base = Candles.from_series(raw)
|
127
89
|
frames = {}
|
128
90
|
intervals.each do |ivl|
|
129
91
|
case ivl.to_i
|
130
|
-
when 1 then frames[:m1] = (base_interval == 1 ? base : resample(base, 1))
|
131
|
-
when 5 then frames[:m5] = resample(base, 5)
|
132
|
-
when 15 then frames[:m15] = resample(base, 15)
|
133
|
-
when 25 then frames[:m25] = resample(base, 25)
|
134
|
-
when 60 then frames[:m60] = resample(base, 60)
|
92
|
+
when 1 then frames[:m1] = (base_interval == 1 ? base : Candles.resample(base, 1))
|
93
|
+
when 5 then frames[:m5] = Candles.resample(base, 5)
|
94
|
+
when 15 then frames[:m15] = Candles.resample(base, 15)
|
95
|
+
when 25 then frames[:m25] = Candles.resample(base, 25)
|
96
|
+
when 60 then frames[:m60] = Candles.resample(base, 60)
|
135
97
|
end
|
136
98
|
end
|
137
99
|
{ indicators: frames.transform_values { |candles| compute_for(candles) } }
|
@@ -139,95 +101,87 @@ module TA
|
|
139
101
|
|
140
102
|
private
|
141
103
|
|
142
|
-
def
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
instrument: params[:instrument],
|
147
|
-
interval: interval.to_s,
|
148
|
-
from_date: params[:from_date],
|
149
|
-
to_date: params[:to_date]
|
150
|
-
)
|
104
|
+
def sleep_with_jitter(multiplier = 1.0)
|
105
|
+
base = (@opts[:throttle_seconds] || 3.0).to_f * multiplier
|
106
|
+
jitter = rand * 0.3
|
107
|
+
sleep(base + jitter)
|
151
108
|
end
|
152
109
|
|
153
|
-
def
|
154
|
-
|
155
|
-
to_d = Date.parse(params[:to_date])
|
156
|
-
max_span = 90
|
157
|
-
return fetch_intraday(params, interval) if (to_d - from_d).to_i <= max_span
|
110
|
+
def normalize_to_date(to_date)
|
111
|
+
return MarketCalendar.today_or_last_trading_day.strftime("%Y-%m-%d") if to_date.nil? || to_date.to_s.strip.empty?
|
158
112
|
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
end
|
169
|
-
cursor = chunk_to + 1
|
113
|
+
to_d_raw = begin
|
114
|
+
Date.parse(to_date)
|
115
|
+
rescue StandardError
|
116
|
+
nil
|
117
|
+
end
|
118
|
+
if to_d_raw && !MarketCalendar.trading_day?(to_d_raw)
|
119
|
+
MarketCalendar.last_trading_day(from: to_d_raw).strftime("%Y-%m-%d")
|
120
|
+
else
|
121
|
+
to_date
|
170
122
|
end
|
171
|
-
merged
|
172
123
|
end
|
173
124
|
|
174
|
-
def
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
125
|
+
def normalize_from_date(from_date, to_date, days_back)
|
126
|
+
if (from_date.nil? || from_date.to_s.strip.empty?) && days_back&.to_i&.positive?
|
127
|
+
to_d = Date.parse(to_date)
|
128
|
+
n_back = [days_back.to_i - 1, 0].max
|
129
|
+
return MarketCalendar.trading_days_ago(to_d, n_back).strftime("%Y-%m-%d")
|
130
|
+
end
|
131
|
+
if from_date && !from_date.to_s.strip.empty?
|
132
|
+
f_d_raw = begin
|
133
|
+
Date.parse(from_date)
|
134
|
+
rescue StandardError
|
135
|
+
nil
|
136
|
+
end
|
137
|
+
if f_d_raw && !MarketCalendar.trading_day?(f_d_raw)
|
138
|
+
fd = f_d_raw
|
139
|
+
fd += 1 until MarketCalendar.trading_day?(fd)
|
140
|
+
to_d = Date.parse(to_date)
|
141
|
+
return [fd, to_d].min.strftime("%Y-%m-%d")
|
142
|
+
end
|
143
|
+
return from_date
|
144
|
+
end
|
145
|
+
to_date
|
146
|
+
end
|
179
147
|
|
180
|
-
|
148
|
+
# Calculate how many bars we need based on indicator periods
|
149
|
+
def required_bars_for_indicators
|
150
|
+
rsi_need = (@opts[:rsi_period] || 14).to_i + 1
|
151
|
+
atr_need = (@opts[:atr_period] || 14).to_i + 1
|
152
|
+
adx_need = (@opts[:adx_period] || 14).to_i * 2
|
153
|
+
macd_need = (@opts[:macd_slow] || 26).to_i
|
154
|
+
[rsi_need, atr_need, adx_need, macd_need].max
|
181
155
|
end
|
182
156
|
|
183
|
-
def
|
184
|
-
|
185
|
-
|
186
|
-
high = series["high"] || series[:high]
|
187
|
-
low = series["low"] || series[:low]
|
188
|
-
close = series["close"] || series[:close]
|
189
|
-
vol = series["volume"] || series[:volume]
|
190
|
-
return [] unless ts && open && high && low && close && vol
|
191
|
-
return [] if close.empty?
|
157
|
+
def bars_per_trading_day(interval_minutes)
|
158
|
+
minutes = interval_minutes.to_i
|
159
|
+
return 1 if minutes <= 0
|
192
160
|
|
193
|
-
(0
|
194
|
-
{ t: parse_time_like(ts[i]), o: open[i].to_f, h: high[i].to_f, l: low[i].to_f, c: close[i].to_f,
|
195
|
-
v: vol[i].to_f }
|
196
|
-
end
|
197
|
-
rescue StandardError
|
198
|
-
[]
|
161
|
+
(375.0 / minutes).floor
|
199
162
|
end
|
200
163
|
|
201
|
-
def
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
candles.each do |c|
|
206
|
-
key = Time.at((c[:t].to_i / 60) / minutes * minutes * 60)
|
207
|
-
b = (grouped[key] ||= { t: key, o: c[:o], h: c[:h], l: c[:l], c: c[:c], v: 0.0 })
|
208
|
-
b[:h] = [b[:h], c[:h]].max
|
209
|
-
b[:l] = [b[:l], c[:l]].min
|
210
|
-
b[:c] = c[:c]
|
211
|
-
b[:v] += c[:v]
|
212
|
-
end
|
213
|
-
grouped.keys.sort.map { |k| grouped[k] }
|
164
|
+
def days_needed_for_interval(interval_minutes)
|
165
|
+
need = required_bars_for_indicators
|
166
|
+
per_day = [bars_per_trading_day(interval_minutes), 1].max
|
167
|
+
((need + per_day - 1) / per_day)
|
214
168
|
end
|
215
169
|
|
216
|
-
def
|
217
|
-
|
218
|
-
|
170
|
+
def auto_days_needed(intervals)
|
171
|
+
Array(intervals).map { |ivl| days_needed_for_interval(ivl.to_i) }.max || 1
|
172
|
+
end
|
219
173
|
|
220
174
|
def compute_for(candles)
|
221
|
-
c =
|
222
|
-
h =
|
223
|
-
l =
|
175
|
+
c = candles.map { |k| k[:c] }
|
176
|
+
h = candles.map { |k| k[:h] }
|
177
|
+
l = candles.map { |k| k[:l] }
|
224
178
|
return { rsi: nil, macd: { macd: nil, signal: nil, hist: nil }, adx: nil, atr: nil } if c.empty?
|
225
179
|
|
226
180
|
{
|
227
|
-
rsi: safe_last(rsi(c, @opts[:rsi_period])),
|
228
|
-
macd: macd(c, @opts[:macd_fast], @opts[:macd_slow], @opts[:macd_signal]),
|
229
|
-
adx: safe_last(adx(h, l, c, @opts[:adx_period])),
|
230
|
-
atr: safe_last(atr(h, l, c, @opts[:atr_period]))
|
181
|
+
rsi: safe_last(Indicators.rsi(c, @opts[:rsi_period])),
|
182
|
+
macd: Indicators.macd(c, @opts[:macd_fast], @opts[:macd_slow], @opts[:macd_signal]),
|
183
|
+
adx: safe_last(Indicators.adx(h, l, c, @opts[:adx_period])),
|
184
|
+
atr: safe_last(Indicators.atr(h, l, c, @opts[:atr_period]))
|
231
185
|
}
|
232
186
|
end
|
233
187
|
|
@@ -238,168 +192,5 @@ module TA
|
|
238
192
|
rescue StandardError
|
239
193
|
nil
|
240
194
|
end
|
241
|
-
|
242
|
-
# ---- Indicator adapters (mirror bin script) ----
|
243
|
-
def rsi(series, period)
|
244
|
-
if defined?(RubyTechnicalAnalysis) && RubyTechnicalAnalysis.const_defined?(:RSI)
|
245
|
-
return RubyTechnicalAnalysis::RSI.new(series: series, period: period).call
|
246
|
-
end
|
247
|
-
if defined?(TechnicalAnalysis) && TechnicalAnalysis.respond_to?(:rsi)
|
248
|
-
return TechnicalAnalysis.rsi(series, period: period)
|
249
|
-
end
|
250
|
-
|
251
|
-
simple_rsi(series, period)
|
252
|
-
end
|
253
|
-
|
254
|
-
def macd(series, fast, slow, signal)
|
255
|
-
if defined?(RubyTechnicalAnalysis) && RubyTechnicalAnalysis.const_defined?(:MACD)
|
256
|
-
out = RubyTechnicalAnalysis::MACD.new(series: series, fast_period: fast, slow_period: slow,
|
257
|
-
signal_period: signal).call
|
258
|
-
if out.is_a?(Hash)
|
259
|
-
m = out[:macd]
|
260
|
-
s = out[:signal]
|
261
|
-
h = out[:histogram] || out[:hist]
|
262
|
-
m = m.last if m.is_a?(Array)
|
263
|
-
s = s.last if s.is_a?(Array)
|
264
|
-
h = h.last if h.is_a?(Array)
|
265
|
-
return { macd: m, signal: s, hist: h }
|
266
|
-
end
|
267
|
-
end
|
268
|
-
if defined?(TechnicalAnalysis) && TechnicalAnalysis.respond_to?(:macd)
|
269
|
-
out = TechnicalAnalysis.macd(series, fast: fast, slow: slow, signal: signal)
|
270
|
-
if out.is_a?(Hash)
|
271
|
-
m = out[:macd]
|
272
|
-
s = out[:signal]
|
273
|
-
h = out[:hist]
|
274
|
-
m = m.last if m.is_a?(Array)
|
275
|
-
s = s.last if s.is_a?(Array)
|
276
|
-
h = h.last if h.is_a?(Array)
|
277
|
-
return { macd: m, signal: s, hist: h }
|
278
|
-
end
|
279
|
-
end
|
280
|
-
simple_macd(series, fast, slow, signal)
|
281
|
-
end
|
282
|
-
|
283
|
-
def adx(high, low, close, period)
|
284
|
-
if defined?(RubyTechnicalAnalysis) && RubyTechnicalAnalysis.const_defined?(:ADX)
|
285
|
-
return RubyTechnicalAnalysis::ADX.new(high: high, low: low, close: close, period: period).call
|
286
|
-
end
|
287
|
-
if defined?(TechnicalAnalysis) && TechnicalAnalysis.respond_to?(:adx)
|
288
|
-
return TechnicalAnalysis.adx(high: high, low: low, close: close, period: period)
|
289
|
-
end
|
290
|
-
|
291
|
-
simple_adx(high, low, close, period)
|
292
|
-
end
|
293
|
-
|
294
|
-
def atr(high, low, close, period)
|
295
|
-
if defined?(RubyTechnicalAnalysis) && RubyTechnicalAnalysis.const_defined?(:ATR)
|
296
|
-
return RubyTechnicalAnalysis::ATR.new(high: high, low: low, close: close, period: period).call
|
297
|
-
end
|
298
|
-
if defined?(TechnicalAnalysis) && TechnicalAnalysis.respond_to?(:atr)
|
299
|
-
return TechnicalAnalysis.atr(high: high, low: low, close: close, period: period)
|
300
|
-
end
|
301
|
-
|
302
|
-
simple_atr(high, low, close, period)
|
303
|
-
end
|
304
|
-
|
305
|
-
# ---- Simple fallbacks ----
|
306
|
-
def ema(series, period)
|
307
|
-
return nil if series.nil? || series.empty?
|
308
|
-
|
309
|
-
k = 2.0 / (period + 1)
|
310
|
-
series.each_with_index.reduce(nil) do |ema_prev, (v, i)|
|
311
|
-
i == 0 ? v.to_f : (v.to_f * k) + ((ema_prev || v.to_f) * (1 - k))
|
312
|
-
end
|
313
|
-
end
|
314
|
-
|
315
|
-
def simple_rsi(series, period)
|
316
|
-
gains = []
|
317
|
-
losses = []
|
318
|
-
series.each_cons(2) do |a, b|
|
319
|
-
ch = b - a
|
320
|
-
gains << [ch, 0].max
|
321
|
-
losses << [(-ch), 0].max
|
322
|
-
end
|
323
|
-
avg_gain = gains.first(period).sum / period.to_f
|
324
|
-
avg_loss = losses.first(period).sum / period.to_f
|
325
|
-
rsi_vals = Array.new(series.size, nil)
|
326
|
-
gains.drop(period).each_with_index do |g, idx|
|
327
|
-
l = losses[period + idx]
|
328
|
-
avg_gain = ((avg_gain * (period - 1)) + g) / period
|
329
|
-
avg_loss = ((avg_loss * (period - 1)) + l) / period
|
330
|
-
rs = avg_loss.zero? ? 100.0 : (avg_gain / avg_loss)
|
331
|
-
rsi_vals[period + 1 + idx] = 100.0 - (100.0 / (1 + rs))
|
332
|
-
end
|
333
|
-
rsi_vals
|
334
|
-
end
|
335
|
-
|
336
|
-
def simple_macd(series, fast, slow, signal)
|
337
|
-
e_fast = ema(series, fast)
|
338
|
-
e_slow = ema(series, slow)
|
339
|
-
e_sig = ema(series, signal)
|
340
|
-
return { macd: nil, signal: nil, hist: nil } if [e_fast, e_slow, e_sig].any?(&:nil?)
|
341
|
-
|
342
|
-
macd_line = e_fast - e_slow
|
343
|
-
signal_line = e_sig
|
344
|
-
{ macd: macd_line, signal: signal_line, hist: macd_line - signal_line }
|
345
|
-
end
|
346
|
-
|
347
|
-
def true_ranges(high, low, close)
|
348
|
-
trs = []
|
349
|
-
close.each_with_index do |_c, i|
|
350
|
-
if i.zero?
|
351
|
-
trs << (high[i] - low[i]).abs
|
352
|
-
else
|
353
|
-
tr = [(high[i] - low[i]).abs, (high[i] - close[i - 1]).abs, (low[i] - close[i - 1]).abs].max
|
354
|
-
trs << tr
|
355
|
-
end
|
356
|
-
end
|
357
|
-
trs
|
358
|
-
end
|
359
|
-
|
360
|
-
def simple_atr(high, low, close, period)
|
361
|
-
trs = true_ranges(high, low, close)
|
362
|
-
out = []
|
363
|
-
atr_prev = trs.first(period).sum / period.to_f
|
364
|
-
trs.each_with_index do |tr, i|
|
365
|
-
if i < period
|
366
|
-
out << nil
|
367
|
-
elsif i == period
|
368
|
-
out << atr_prev
|
369
|
-
else
|
370
|
-
atr_prev = ((atr_prev * (period - 1)) + tr) / period.to_f
|
371
|
-
out << atr_prev
|
372
|
-
end
|
373
|
-
end
|
374
|
-
out
|
375
|
-
end
|
376
|
-
|
377
|
-
def simple_adx(high, low, close, period)
|
378
|
-
plus_dm = [0]
|
379
|
-
minus_dm = [0]
|
380
|
-
(1...high.size).each do |i|
|
381
|
-
up_move = high[i] - high[i - 1]
|
382
|
-
down_move = low[i - 1] - low[i]
|
383
|
-
plus_dm << (up_move > down_move && up_move.positive? ? up_move : 0)
|
384
|
-
minus_dm << (down_move > up_move && down_move.positive? ? down_move : 0)
|
385
|
-
end
|
386
|
-
trs = true_ranges(high, low, close)
|
387
|
-
smooth_tr = trs.first(period).sum
|
388
|
-
smooth_plus_dm = plus_dm.first(period).sum
|
389
|
-
smooth_minus_dm = minus_dm.first(period).sum
|
390
|
-
adx_vals = Array.new(high.size, nil)
|
391
|
-
di_vals = []
|
392
|
-
(period...high.size).each do |i|
|
393
|
-
smooth_tr = smooth_tr - (smooth_tr / period) + trs[i]
|
394
|
-
smooth_plus_dm = smooth_plus_dm - (smooth_plus_dm / period) + plus_dm[i]
|
395
|
-
smooth_minus_dm = smooth_minus_dm - (smooth_minus_dm / period) + minus_dm[i]
|
396
|
-
plus_di = 100.0 * (smooth_plus_dm / smooth_tr)
|
397
|
-
minus_di = 100.0 * (smooth_minus_dm / smooth_tr)
|
398
|
-
dx = 100.0 * ((plus_di - minus_di).abs / (plus_di + minus_di))
|
399
|
-
di_vals << dx
|
400
|
-
adx_vals[i] = di_vals.last(period).sum / period.to_f if di_vals.size >= period
|
401
|
-
end
|
402
|
-
adx_vals
|
403
|
-
end
|
404
195
|
end
|
405
196
|
end
|
data/lib/ta.rb
ADDED
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
|
@@ -162,17 +163,18 @@ files:
|
|
162
163
|
- diagram.html
|
163
164
|
- diagram.md
|
164
165
|
- docs/rails_integration.md
|
166
|
+
- docs/technical_analysis.md
|
165
167
|
- exe/DhanHQ
|
166
|
-
- lib/DhanHQ.rb
|
167
168
|
- lib/DhanHQ/client.rb
|
168
169
|
- lib/DhanHQ/config.rb
|
169
170
|
- lib/DhanHQ/configuration.rb
|
170
171
|
- lib/DhanHQ/constants.rb
|
171
172
|
- lib/DhanHQ/contracts/base_contract.rb
|
172
173
|
- lib/DhanHQ/contracts/historical_data_contract.rb
|
174
|
+
- lib/DhanHQ/contracts/instrument_list_contract.rb
|
173
175
|
- lib/DhanHQ/contracts/margin_calculator_contract.rb
|
174
|
-
- lib/DhanHQ/contracts/modify_order_contract copy.rb
|
175
176
|
- lib/DhanHQ/contracts/modify_order_contract.rb
|
177
|
+
- lib/DhanHQ/contracts/modify_order_contract_copy.rb
|
176
178
|
- lib/DhanHQ/contracts/option_chain_contract.rb
|
177
179
|
- lib/DhanHQ/contracts/order_contract.rb
|
178
180
|
- lib/DhanHQ/contracts/place_order_contract.rb
|
@@ -196,6 +198,7 @@ files:
|
|
196
198
|
- lib/DhanHQ/models/funds.rb
|
197
199
|
- lib/DhanHQ/models/historical_data.rb
|
198
200
|
- lib/DhanHQ/models/holding.rb
|
201
|
+
- lib/DhanHQ/models/instrument.rb
|
199
202
|
- lib/DhanHQ/models/kill_switch.rb
|
200
203
|
- lib/DhanHQ/models/ledger_entry.rb
|
201
204
|
- lib/DhanHQ/models/margin.rb
|
@@ -215,6 +218,7 @@ files:
|
|
215
218
|
- lib/DhanHQ/resources/funds.rb
|
216
219
|
- lib/DhanHQ/resources/historical_data.rb
|
217
220
|
- lib/DhanHQ/resources/holdings.rb
|
221
|
+
- lib/DhanHQ/resources/instruments.rb
|
218
222
|
- lib/DhanHQ/resources/kill_switch.rb
|
219
223
|
- lib/DhanHQ/resources/margin_calculator.rb
|
220
224
|
- lib/DhanHQ/resources/market_feed.rb
|
@@ -231,7 +235,6 @@ files:
|
|
231
235
|
- lib/DhanHQ/ws/cmd_bus.rb
|
232
236
|
- lib/DhanHQ/ws/connection.rb
|
233
237
|
- lib/DhanHQ/ws/decoder.rb
|
234
|
-
- lib/DhanHQ/ws/errors.rb
|
235
238
|
- lib/DhanHQ/ws/orders.rb
|
236
239
|
- lib/DhanHQ/ws/orders/client.rb
|
237
240
|
- lib/DhanHQ/ws/orders/connection.rb
|
@@ -251,6 +254,17 @@ files:
|
|
251
254
|
- lib/DhanHQ/ws/singleton_lock.rb
|
252
255
|
- lib/DhanHQ/ws/sub_state.rb
|
253
256
|
- lib/DhanHQ/ws/websocket_packet_parser.rb
|
257
|
+
- lib/dhan_hq.rb
|
258
|
+
- lib/dhanhq/analysis/helpers/bias_aggregator.rb
|
259
|
+
- lib/dhanhq/analysis/helpers/moneyness_helper.rb
|
260
|
+
- lib/dhanhq/analysis/multi_timeframe_analyzer.rb
|
261
|
+
- lib/dhanhq/analysis/options_buying_advisor.rb
|
262
|
+
- lib/dhanhq/contracts/options_buying_advisor_contract.rb
|
263
|
+
- lib/ta.rb
|
264
|
+
- lib/ta/candles.rb
|
265
|
+
- lib/ta/fetcher.rb
|
266
|
+
- lib/ta/indicators.rb
|
267
|
+
- lib/ta/market_calendar.rb
|
254
268
|
- lib/ta/technical_analysis.rb
|
255
269
|
- sig/DhanHQ.rbs
|
256
270
|
- watchlist.csv
|
data/lib/DhanHQ/ws/errors.rb
DELETED
File without changes
|
File without changes
|