DhanHQ 3.0.0 → 3.0.1

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 (71) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop_todo.yml +7 -0
  3. data/lib/DhanHQ/agent/order_preview.rb +50 -0
  4. data/lib/DhanHQ/agent/policy.rb +51 -0
  5. data/lib/DhanHQ/agent/tool_registry.rb +250 -0
  6. data/lib/DhanHQ/agent.rb +12 -0
  7. data/lib/DhanHQ/ai/context_builder.rb +145 -0
  8. data/lib/DhanHQ/ai/prompt_helpers.rb +114 -0
  9. data/lib/DhanHQ/ai.rb +27 -0
  10. data/lib/DhanHQ/auth.rb +0 -1
  11. data/lib/DhanHQ/client.rb +1 -3
  12. data/lib/DhanHQ/constants.rb +2 -0
  13. data/lib/DhanHQ/contracts/iceberg_order_contract.rb +83 -0
  14. data/lib/DhanHQ/contracts/twap_order_contract.rb +106 -0
  15. data/lib/DhanHQ/core/auth_api.rb +0 -1
  16. data/lib/DhanHQ/errors.rb +4 -0
  17. data/lib/DhanHQ/events/base.rb +203 -0
  18. data/lib/DhanHQ/events/bus.rb +158 -0
  19. data/lib/DhanHQ/events.rb +40 -0
  20. data/lib/DhanHQ/indicators.rb +283 -0
  21. data/lib/DhanHQ/market_data/market_snapshot.rb +97 -0
  22. data/lib/DhanHQ/market_data/ohlc_series.rb +169 -0
  23. data/lib/DhanHQ/market_data/option_snapshot.rb +223 -0
  24. data/lib/DhanHQ/market_data.rb +25 -0
  25. data/lib/DhanHQ/mcp/server.rb +72 -0
  26. data/lib/DhanHQ/mcp.rb +10 -0
  27. data/lib/DhanHQ/models/funds.rb +12 -0
  28. data/lib/DhanHQ/models/holding.rb +42 -0
  29. data/lib/DhanHQ/models/iceberg_order.rb +139 -0
  30. data/lib/DhanHQ/models/instrument.rb +36 -0
  31. data/lib/DhanHQ/models/order.rb +95 -0
  32. data/lib/DhanHQ/models/position.rb +66 -0
  33. data/lib/DhanHQ/models/search_result.rb +12 -0
  34. data/lib/DhanHQ/models/trade.rb +13 -0
  35. data/lib/DhanHQ/models/twap_order.rb +136 -0
  36. data/lib/DhanHQ/option_analytics/black_scholes.rb +194 -0
  37. data/lib/DhanHQ/option_analytics/max_pain.rb +119 -0
  38. data/lib/DhanHQ/option_analytics.rb +36 -0
  39. data/lib/DhanHQ/resources/iceberg_orders.rb +61 -0
  40. data/lib/DhanHQ/resources/twap_orders.rb +61 -0
  41. data/lib/DhanHQ/risk/checks/asm_gsm.rb +17 -0
  42. data/lib/DhanHQ/risk/checks/market_hours.rb +37 -0
  43. data/lib/DhanHQ/risk/checks/options.rb +46 -0
  44. data/lib/DhanHQ/risk/checks/order_type.rb +20 -0
  45. data/lib/DhanHQ/risk/checks/product_support.rb +34 -0
  46. data/lib/DhanHQ/risk/checks/quantity.rb +32 -0
  47. data/lib/DhanHQ/risk/checks/trading_permission.rb +16 -0
  48. data/lib/DhanHQ/risk/pipeline.rb +65 -0
  49. data/lib/DhanHQ/risk.rb +250 -0
  50. data/lib/DhanHQ/skills/base.rb +132 -0
  51. data/lib/DhanHQ/skills/builtin/buy_atm_call.rb +87 -0
  52. data/lib/DhanHQ/skills/builtin/iron_condor.rb +93 -0
  53. data/lib/DhanHQ/skills/builtin/square_off_all.rb +45 -0
  54. data/lib/DhanHQ/skills/builtin/square_off_position.rb +48 -0
  55. data/lib/DhanHQ/skills/builtin/strangle.rb +93 -0
  56. data/lib/DhanHQ/skills/registry.rb +101 -0
  57. data/lib/DhanHQ/skills/workflow.rb +66 -0
  58. data/lib/DhanHQ/skills.rb +29 -0
  59. data/lib/DhanHQ/strategy/base.rb +189 -0
  60. data/lib/DhanHQ/strategy.rb +40 -0
  61. data/lib/DhanHQ/version.rb +1 -1
  62. data/lib/DhanHQ/ws/decoder.rb +57 -19
  63. data/lib/DhanHQ.rb +3 -0
  64. data/lib/dhan_hq/agent.rb +3 -0
  65. data/lib/dhan_hq/mcp.rb +3 -0
  66. data/lib/dhan_hq.rb +27 -2
  67. data/lib/ta/technical_analysis.rb +3 -1
  68. data/skills/dhanhq-ruby/SKILL.md +74 -0
  69. data/skills/dhanhq-ruby/references/market_data.md +3 -0
  70. data/skills/dhanhq-ruby/references/orders.md +7 -0
  71. metadata +59 -20
@@ -0,0 +1,283 @@
1
+ # frozen_string_literal: true
2
+
3
+ module DhanHQ
4
+ # Technical analysis indicators for market data analysis.
5
+ module Indicators
6
+ # Simple Moving Average (SMA)
7
+ #
8
+ # Calculates the average of a specified number of data points.
9
+ #
10
+ # @example Calculate 20-period SMA
11
+ # closes = [100, 102, 101, 103, 105, 104, 106, 108, 107, 109]
12
+ # sma = DhanHQ::Indicators::SMA.calculate(closes, period: 5)
13
+ # #=> [102.2, 103.0, 103.8, 104.6, 105.4, 106.8, 108.2, 107.8]
14
+ #
15
+ class SMA
16
+ # Calculate SMA for the given data.
17
+ #
18
+ # @param data [Array<Numeric>] Input price data
19
+ # @param period [Integer] Number of periods (default: 20)
20
+ # @return [Array<Float>] SMA values (nil for insufficient data points)
21
+ def self.calculate(data, period: 20)
22
+ return [] if data.nil? || data.empty? || period < 1
23
+
24
+ data.each_index.map do |i|
25
+ next nil if i < period - 1
26
+
27
+ window = data[(i - period + 1)..i]
28
+ window.sum.to_f / period
29
+ end
30
+ end
31
+ end
32
+
33
+ # Exponential Moving Average (EMA)
34
+ #
35
+ # Weighted average that gives more importance to recent prices.
36
+ #
37
+ # @example Calculate 12-period EMA
38
+ # closes = [100, 102, 101, 103, 105, 104, 106, 108, 107, 109]
39
+ # ema = DhanHQ::Indicators::EMA.calculate(closes, period: 5)
40
+ #
41
+ class EMA
42
+ # Calculate EMA for the given data.
43
+ #
44
+ # @param data [Array<Numeric>] Input price data
45
+ # @param period [Integer] Number of periods (default: 20)
46
+ # @return [Array<Float>] EMA values (nil for insufficient data points)
47
+ def self.calculate(data, period: 20)
48
+ return [] if data.nil? || data.empty? || period < 1
49
+
50
+ multiplier = 2.0 / (period + 1)
51
+ ema_values = []
52
+
53
+ data.each_with_index do |price, i|
54
+ if i < period - 1
55
+ ema_values << nil
56
+ elsif i == period - 1
57
+ # First EMA is SMA
58
+ window = data[0..i]
59
+ ema_values << (window.sum.to_f / period)
60
+ else
61
+ # EMA = (Price - Previous EMA) * Multiplier + Previous EMA
62
+ previous_ema = ema_values.last
63
+ ema_values << (((price - previous_ema) * multiplier) + previous_ema)
64
+ end
65
+ end
66
+
67
+ ema_values
68
+ end
69
+ end
70
+
71
+ # Relative Strength Index (RSI)
72
+ #
73
+ # Momentum oscillator measuring speed and magnitude of price changes.
74
+ #
75
+ # @example Calculate 14-period RSI
76
+ # closes = [100, 102, 101, 103, 105, 104, 106, 108, 107, 109, 111, 110, 112, 114, 113]
77
+ # rsi = DhanHQ::Indicators::RSI.calculate(closes, period: 14)
78
+ #
79
+ class RSI
80
+ # Calculate RSI for the given data.
81
+ #
82
+ # @param data [Array<Numeric>] Input price data
83
+ # @param period [Integer] Number of periods (default: 14)
84
+ # @return [Array<Float>] RSI values (nil for insufficient data points)
85
+ def self.calculate(data, period: 14)
86
+ return [] if data.nil? || data.empty? || period < 1
87
+ return [] if data.length < period + 1
88
+
89
+ # Calculate price changes
90
+ changes = data.each_cons(2).map { |a, b| b - a }
91
+
92
+ # Calculate initial average gains and losses
93
+ gains = changes[0...period].select(&:positive?)
94
+ losses = changes[0...period].select(&:negative?).map(&:abs)
95
+
96
+ avg_gain = gains.sum.to_f / period
97
+ avg_loss = losses.sum.to_f / period
98
+
99
+ rsi_values = Array.new(period, nil)
100
+
101
+ # Calculate RSI for first period
102
+ rsi_values << calculate_rsi(avg_gain, avg_loss)
103
+
104
+ # Calculate subsequent RSI values
105
+ changes[period..].each do |change|
106
+ gain = change.positive? ? change : 0
107
+ loss = change.negative? ? change.abs : 0
108
+
109
+ avg_gain = ((avg_gain * (period - 1)) + gain) / period
110
+ avg_loss = ((avg_loss * (period - 1)) + loss) / period
111
+
112
+ rsi_values << calculate_rsi(avg_gain, avg_loss)
113
+ end
114
+
115
+ rsi_values
116
+ end
117
+
118
+ def self.calculate_rsi(avg_gain, avg_loss)
119
+ return 100.0 if avg_loss.zero?
120
+ return 0.0 if avg_gain.zero?
121
+
122
+ rs = avg_gain / avg_loss
123
+ 100 - (100 / (1 + rs))
124
+ end
125
+ private_class_method :calculate_rsi
126
+ end
127
+
128
+ # Moving Average Convergence Divergence (MACD)
129
+ #
130
+ # Trend-following momentum indicator showing relationship between two EMAs.
131
+ #
132
+ # @example Calculate MACD
133
+ # closes = (0..99).map { |i| 100 + Math.sin(i * 0.1) * 10 }
134
+ # macd = DhanHQ::Indicators::MACD.calculate(closes)
135
+ # #=> { macd_line: [...], signal_line: [...], histogram: [...] }
136
+ #
137
+ class MACD
138
+ # Calculate MACD for the given data.
139
+ #
140
+ # @param data [Array<Numeric>] Input price data
141
+ # @param fast_period [Integer] Fast EMA period (default: 12)
142
+ # @param slow_period [Integer] Slow EMA period (default: 26)
143
+ # @param signal_period [Integer] Signal line period (default: 9)
144
+ # @return [Hash] Hash with :macd_line, :signal_line, and :histogram arrays
145
+ def self.calculate(data, fast_period: 12, slow_period: 26, signal_period: 9)
146
+ return { macd_line: [], signal_line: [], histogram: [] } if data.nil? || data.empty?
147
+
148
+ # Calculate fast and slow EMAs
149
+ fast_ema = EMA.calculate(data, period: fast_period)
150
+ slow_ema = EMA.calculate(data, period: slow_period)
151
+
152
+ # Calculate MACD line (fast EMA - slow EMA)
153
+ macd_line = fast_ema.zip(slow_ema).map do |fast, slow|
154
+ next nil if fast.nil? || slow.nil?
155
+
156
+ fast - slow
157
+ end
158
+
159
+ # Calculate signal line (EMA of MACD line)
160
+ valid_macd = macd_line.compact
161
+ signal_line_raw = valid_macd.empty? ? [] : EMA.calculate(valid_macd, period: signal_period)
162
+
163
+ # Align signal line with MACD line
164
+ signal_line = Array.new(macd_line.length - signal_line_raw.length, nil) + signal_line_raw
165
+
166
+ # Calculate histogram (MACD line - signal line)
167
+ histogram = macd_line.zip(signal_line).map do |macd, signal|
168
+ next nil if macd.nil? || signal.nil?
169
+
170
+ macd - signal
171
+ end
172
+
173
+ {
174
+ macd_line: macd_line,
175
+ signal_line: signal_line,
176
+ histogram: histogram
177
+ }
178
+ end
179
+ end
180
+
181
+ # Bollinger Bands
182
+ #
183
+ # Volatility indicator consisting of three lines: middle band (SMA), upper band, lower band.
184
+ #
185
+ # @example Calculate Bollinger Bands
186
+ # closes = (0..99).map { |i| 100 + Math.sin(i * 0.1) * 10 }
187
+ # bb = DhanHQ::Indicators::BollingerBands.calculate(closes)
188
+ # #=> { upper: [...], middle: [...], lower: [...] }
189
+ #
190
+ class BollingerBands
191
+ # Calculate Bollinger Bands for the given data.
192
+ #
193
+ # @param data [Array<Numeric>] Input price data
194
+ # @param period [Integer] Number of periods (default: 20)
195
+ # @param std_dev [Float] Number of standard deviations (default: 2.0)
196
+ # @return [Hash] Hash with :upper, :middle, and :lower arrays
197
+ def self.calculate(data, period: 20, std_dev: 2.0)
198
+ return { upper: [], middle: [], lower: [] } if data.nil? || data.empty?
199
+
200
+ # Calculate middle band (SMA)
201
+ middle = SMA.calculate(data, period: period)
202
+
203
+ # Calculate upper and lower bands
204
+ upper = []
205
+ lower = []
206
+
207
+ data.each_with_index do |_, i|
208
+ if i < period - 1
209
+ upper << nil
210
+ lower << nil
211
+ else
212
+ window = data[(i - period + 1)..i]
213
+ mean = middle[i]
214
+ variance = window.sum { |x| (x - mean)**2 } / period
215
+ std = Math.sqrt(variance)
216
+
217
+ upper << (mean + (std_dev * std))
218
+ lower << (mean - (std_dev * std))
219
+ end
220
+ end
221
+
222
+ {
223
+ upper: upper,
224
+ middle: middle,
225
+ lower: lower
226
+ }
227
+ end
228
+ end
229
+
230
+ # Average True Range (ATR)
231
+ #
232
+ # Volatility indicator measuring market volatility.
233
+ #
234
+ # @example Calculate ATR
235
+ # ohlc = [
236
+ # { open: 100, high: 105, low: 98, close: 103 },
237
+ # { open: 103, high: 108, low: 101, close: 106 },
238
+ # ...
239
+ # ]
240
+ # atr = DhanHQ::Indicators::ATR.calculate(ohlc, period: 14)
241
+ #
242
+ class ATR
243
+ # Calculate ATR for the given OHLC data.
244
+ #
245
+ # @param data [Array<Hash>] Array of OHLC hashes with :open, :high, :low, :close
246
+ # @param period [Integer] Number of periods (default: 14)
247
+ # @return [Array<Float>] ATR values (nil for insufficient data points)
248
+ def self.calculate(data, period: 14)
249
+ return [] if data.nil? || data.empty? || period < 1
250
+ return [] if data.length < 2
251
+
252
+ # Calculate true ranges
253
+ true_ranges = data.each_cons(2).map do |prev, curr|
254
+ high = curr[:high] || curr["high"]
255
+ low = curr[:low] || curr["low"]
256
+ prev_close = prev[:close] || prev["close"]
257
+
258
+ [high - low, (high - prev_close).abs, (low - prev_close).abs].max
259
+ end
260
+
261
+ # Calculate ATR using smoothed average
262
+ atr_values = [nil] # First value has no true range
263
+
264
+ # First ATR is simple average
265
+ if true_ranges.length >= period
266
+ first_atr = true_ranges[0...period].sum.to_f / period
267
+ atr_values.concat(Array.new(period - 1, nil))
268
+ atr_values << first_atr
269
+
270
+ # Subsequent ATRs use smoothing
271
+ true_ranges[period..].each do |tr|
272
+ previous_atr = atr_values.last
273
+ atr_values << (((previous_atr * (period - 1)) + tr) / period)
274
+ end
275
+ else
276
+ atr_values.concat(Array.new(true_ranges.length, nil))
277
+ end
278
+
279
+ atr_values
280
+ end
281
+ end
282
+ end
283
+ end
@@ -0,0 +1,97 @@
1
+ # frozen_string_literal: true
2
+
3
+ module DhanHQ
4
+ # Higher-level abstractions for market data consumption.
5
+ module MarketData
6
+ # A snapshot of market data for multiple instruments at a point in time.
7
+ #
8
+ # Wraps the raw MarketFeed response into a more convenient structure
9
+ # with typed accessors and helper methods.
10
+ #
11
+ # @example Build a snapshot from MarketFeed response
12
+ # response = DhanHQ::Models::MarketFeed.ltp("NSE_EQ" => [11536, 3456])
13
+ # snapshot = DhanHQ::MarketData::MarketSnapshot.from_response(response)
14
+ # snapshot.ltp("NSE_EQ", "11536") #=> 2850.50
15
+ #
16
+ class MarketSnapshot
17
+ attr_reader :data, :fetched_at
18
+
19
+ def initialize(data = {})
20
+ @data = data
21
+ @fetched_at = Time.now
22
+ end
23
+
24
+ # Build a MarketSnapshot from a raw MarketFeed API response.
25
+ def self.from_response(response)
26
+ raw_data = response.is_a?(Hash) ? (response[:data] || response["data"] || {}) : {}
27
+ new(normalize_data(raw_data))
28
+ end
29
+
30
+ # Get LTP for a specific instrument.
31
+ def ltp(exchange_segment, security_id)
32
+ instrument_data(exchange_segment, security_id)&.dig(:last_price)
33
+ end
34
+
35
+ # Get OHLC for a specific instrument.
36
+ def ohlc(exchange_segment, security_id)
37
+ instrument_data(exchange_segment, security_id)&.dig(:ohlc)
38
+ end
39
+
40
+ # Get full quote (market depth) for a specific instrument.
41
+ def quote(exchange_segment, security_id)
42
+ instrument_data(exchange_segment, security_id)
43
+ end
44
+
45
+ # Get all instruments for a specific exchange segment.
46
+ def for_segment(exchange_segment)
47
+ @data[exchange_segment.to_s] || {}
48
+ end
49
+
50
+ # Get all security IDs across all segments.
51
+ def security_ids
52
+ @data.each_with_object([]) do |(_segment, instruments), ids|
53
+ ids.concat(instruments.keys)
54
+ end
55
+ end
56
+
57
+ # Get total number of instruments in the snapshot.
58
+ def size
59
+ @data.values.sum(&:size)
60
+ end
61
+
62
+ # Check if the snapshot is empty.
63
+ def empty?
64
+ @data.empty? || @data.values.all?(&:empty?)
65
+ end
66
+
67
+ private
68
+
69
+ def instrument_data(exchange_segment, security_id)
70
+ @data[exchange_segment.to_s]&.dig(security_id.to_s)
71
+ end
72
+
73
+ def self.normalize_data(data)
74
+ result = {}
75
+ data.each do |segment, instruments|
76
+ result[segment.to_s] = {}
77
+ next unless instruments.is_a?(Hash)
78
+
79
+ instruments.each do |sec_id, info|
80
+ result[segment.to_s][sec_id.to_s] = normalize_instrument(info)
81
+ end
82
+ end
83
+ result
84
+ end
85
+ private_class_method :normalize_data
86
+
87
+ def self.normalize_instrument(info)
88
+ return info unless info.is_a?(Hash)
89
+
90
+ info.each_with_object({}) do |(key, value), hash|
91
+ hash[key.to_sym] = value
92
+ end
93
+ end
94
+ private_class_method :normalize_instrument
95
+ end
96
+ end
97
+ end
@@ -0,0 +1,169 @@
1
+ # frozen_string_literal: true
2
+
3
+ module DhanHQ
4
+ module MarketData
5
+ # A time series of OHLCV candles for a single instrument.
6
+ #
7
+ # Wraps historical data responses into a convenient array-like structure
8
+ # with helper methods for analysis.
9
+ #
10
+ # @example Build a series from historical data response
11
+ # response = DhanHQ::Models::HistoricalData.daily(
12
+ # security_id: "11536",
13
+ # exchange_segment: "NSE_EQ",
14
+ # instrument: "EQUITY",
15
+ # from_date: "2024-01-01",
16
+ # to_date: "2024-12-31"
17
+ # )
18
+ # series = DhanHQ::MarketData::OHLCSeries.from_response(response)
19
+ # series.closes #=> [2800.0, 2810.5, ...]
20
+ # series.volumes #=> [123456, 234567, ...]
21
+ #
22
+ class OHLCSeries
23
+ include Enumerable
24
+
25
+ Candle = Struct.new(:timestamp, :open, :high, :low, :close, :volume, :open_interest) do
26
+ def body_size
27
+ (close - open).abs
28
+ end
29
+
30
+ def upper_shadow
31
+ high - [open, close].max
32
+ end
33
+
34
+ def lower_shadow
35
+ [open, close].min - low
36
+ end
37
+
38
+ def bullish?
39
+ close > open
40
+ end
41
+
42
+ def bearish?
43
+ close < open
44
+ end
45
+
46
+ def doji?
47
+ (close - open).abs < (high - low) * 0.1
48
+ end
49
+ end
50
+
51
+ attr_reader :candles, :security_id, :exchange_segment
52
+
53
+ def initialize(candles = [], metadata = {})
54
+ @candles = candles
55
+ @security_id = metadata[:security_id]
56
+ @exchange_segment = metadata[:exchange_segment]
57
+ end
58
+
59
+ # Build an OHLCSeries from a raw historical data API response.
60
+ def self.from_response(response)
61
+ data = response.is_a?(Hash) ? (response[:data] || response["data"] || response) : response
62
+ data = [data] unless data.is_a?(Array)
63
+
64
+ candles = data.map do |candle|
65
+ Candle.new(
66
+ timestamp: candle[:timestamp] || candle["timestamp"],
67
+ open: (candle[:open] || candle["open"]).to_f,
68
+ high: (candle[:high] || candle["high"]).to_f,
69
+ low: (candle[:low] || candle["low"]).to_f,
70
+ close: (candle[:close] || candle["close"]).to_f,
71
+ volume: (candle[:volume] || candle["volume"]).to_i,
72
+ open_interest: candle[:open_interest] || candle["open_interest"]
73
+ )
74
+ end
75
+
76
+ new(candles)
77
+ end
78
+
79
+ def each(&)
80
+ @candles.each(&)
81
+ end
82
+
83
+ def size
84
+ @candles.size
85
+ end
86
+
87
+ def empty?
88
+ @candles.empty?
89
+ end
90
+
91
+ # Get all close prices.
92
+ def closes
93
+ @candles.map(&:close)
94
+ end
95
+
96
+ # Get all open prices.
97
+ def opens
98
+ @candles.map(&:open)
99
+ end
100
+
101
+ # Get all high prices.
102
+ def highs
103
+ @candles.map(&:high)
104
+ end
105
+
106
+ # Get all low prices.
107
+ def lows
108
+ @candles.map(&:low)
109
+ end
110
+
111
+ # Get all volumes.
112
+ def volumes
113
+ @candles.map(&:volume)
114
+ end
115
+
116
+ # Get the most recent candle.
117
+ def last
118
+ @candles.last
119
+ end
120
+
121
+ # Get the oldest candle.
122
+ def first
123
+ @candles.first
124
+ end
125
+
126
+ # Get the date range of the series.
127
+ def date_range
128
+ return nil if empty?
129
+
130
+ [first.timestamp, last.timestamp]
131
+ end
132
+
133
+ # Calculate the total volume across all candles.
134
+ def total_volume
135
+ @candles.sum(&:volume)
136
+ end
137
+
138
+ # Calculate the average close price.
139
+ def average_close
140
+ return nil if empty?
141
+
142
+ closes.sum / size
143
+ end
144
+
145
+ # Calculate the price range (highest high - lowest low).
146
+ def price_range
147
+ return nil if empty?
148
+
149
+ highs.max - lows.min
150
+ end
151
+
152
+ # Slice the series by date range (requires timestamps).
153
+ def slice_range(from_timestamp, to_timestamp)
154
+ self.class.new(
155
+ @candles.select { |c| c.timestamp.between?(from_timestamp, to_timestamp) },
156
+ { security_id: @security_id, exchange_segment: @exchange_segment }
157
+ )
158
+ end
159
+
160
+ # Take the last N candles.
161
+ def tail(count)
162
+ self.class.new(
163
+ @candles.last(count),
164
+ { security_id: @security_id, exchange_segment: @exchange_segment }
165
+ )
166
+ end
167
+ end
168
+ end
169
+ end