technical-analysis 0.1.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 +7 -0
- data/lib/technical-analysis.rb +3 -0
- data/lib/technical_analysis.rb +41 -0
- data/lib/technical_analysis/helpers/array_helper.rb +27 -0
- data/lib/technical_analysis/helpers/stock_calculation.rb +25 -0
- data/lib/technical_analysis/helpers/validation.rb +33 -0
- data/lib/technical_analysis/indicators/adi.rb +101 -0
- data/lib/technical_analysis/indicators/adtv.rb +98 -0
- data/lib/technical_analysis/indicators/adx.rb +168 -0
- data/lib/technical_analysis/indicators/ao.rb +105 -0
- data/lib/technical_analysis/indicators/atr.rb +109 -0
- data/lib/technical_analysis/indicators/bb.rb +126 -0
- data/lib/technical_analysis/indicators/cci.rb +105 -0
- data/lib/technical_analysis/indicators/cmf.rb +105 -0
- data/lib/technical_analysis/indicators/cr.rb +95 -0
- data/lib/technical_analysis/indicators/dc.rb +108 -0
- data/lib/technical_analysis/indicators/dlr.rb +97 -0
- data/lib/technical_analysis/indicators/dpo.rb +106 -0
- data/lib/technical_analysis/indicators/dr.rb +96 -0
- data/lib/technical_analysis/indicators/eom.rb +104 -0
- data/lib/technical_analysis/indicators/fi.rb +95 -0
- data/lib/technical_analysis/indicators/ichimoku.rb +179 -0
- data/lib/technical_analysis/indicators/indicator.rb +138 -0
- data/lib/technical_analysis/indicators/kc.rb +124 -0
- data/lib/technical_analysis/indicators/kst.rb +132 -0
- data/lib/technical_analysis/indicators/macd.rb +144 -0
- data/lib/technical_analysis/indicators/mfi.rb +119 -0
- data/lib/technical_analysis/indicators/mi.rb +121 -0
- data/lib/technical_analysis/indicators/nvi.rb +102 -0
- data/lib/technical_analysis/indicators/obv.rb +104 -0
- data/lib/technical_analysis/indicators/obv_mean.rb +110 -0
- data/lib/technical_analysis/indicators/rsi.rb +133 -0
- data/lib/technical_analysis/indicators/sma.rb +98 -0
- data/lib/technical_analysis/indicators/sr.rb +122 -0
- data/lib/technical_analysis/indicators/trix.rb +127 -0
- data/lib/technical_analysis/indicators/tsi.rb +139 -0
- data/lib/technical_analysis/indicators/uo.rb +130 -0
- data/lib/technical_analysis/indicators/vi.rb +117 -0
- data/lib/technical_analysis/indicators/vpt.rb +95 -0
- data/lib/technical_analysis/indicators/wr.rb +103 -0
- data/spec/helpers/array_helper_spec.rb +31 -0
- data/spec/helpers/validaton_spec.rb +22 -0
- data/spec/spec_helper.rb +26 -0
- data/spec/ta_test_data.csv +64 -0
- data/spec/technical_analysis/indicators/adi_spec.rb +116 -0
- data/spec/technical_analysis/indicators/adtv_spec.rb +98 -0
- data/spec/technical_analysis/indicators/adx_spec.rb +92 -0
- data/spec/technical_analysis/indicators/ao_spec.rb +86 -0
- data/spec/technical_analysis/indicators/atr_spec.rb +105 -0
- data/spec/technical_analysis/indicators/bb_spec.rb +100 -0
- data/spec/technical_analysis/indicators/cci_spec.rb +100 -0
- data/spec/technical_analysis/indicators/cmf_spec.rb +100 -0
- data/spec/technical_analysis/indicators/cr_spec.rb +119 -0
- data/spec/technical_analysis/indicators/dc_spec.rb +100 -0
- data/spec/technical_analysis/indicators/dlr_spec.rb +119 -0
- data/spec/technical_analysis/indicators/dpo_spec.rb +90 -0
- data/spec/technical_analysis/indicators/dr_spec.rb +119 -0
- data/spec/technical_analysis/indicators/eom_spec.rb +105 -0
- data/spec/technical_analysis/indicators/fi_spec.rb +118 -0
- data/spec/technical_analysis/indicators/ichimoku_spec.rb +95 -0
- data/spec/technical_analysis/indicators/indicator_spec.rb +120 -0
- data/spec/technical_analysis/indicators/kc_spec.rb +110 -0
- data/spec/technical_analysis/indicators/kst_spec.rb +78 -0
- data/spec/technical_analysis/indicators/macd_spec.rb +86 -0
- data/spec/technical_analysis/indicators/mfi_spec.rb +105 -0
- data/spec/technical_analysis/indicators/mi_spec.rb +79 -0
- data/spec/technical_analysis/indicators/nvi_spec.rb +119 -0
- data/spec/technical_analysis/indicators/obv_mean_spec.rb +109 -0
- data/spec/technical_analysis/indicators/obv_spec.rb +119 -0
- data/spec/technical_analysis/indicators/rsi_spec.rb +105 -0
- data/spec/technical_analysis/indicators/sma_spec.rb +115 -0
- data/spec/technical_analysis/indicators/sr_spec.rb +104 -0
- data/spec/technical_analysis/indicators/trix_spec.rb +76 -0
- data/spec/technical_analysis/indicators/tsi_spec.rb +87 -0
- data/spec/technical_analysis/indicators/uo_spec.rb +91 -0
- data/spec/technical_analysis/indicators/vi_spec.rb +105 -0
- data/spec/technical_analysis/indicators/vpt_spec.rb +118 -0
- data/spec/technical_analysis/indicators/wr_spec.rb +106 -0
- metadata +177 -0
@@ -0,0 +1,110 @@
|
|
1
|
+
module TechnicalAnalysis
|
2
|
+
# On-balance Volume Mean
|
3
|
+
class ObvMean < Indicator
|
4
|
+
|
5
|
+
# Returns the symbol of the technical indicator
|
6
|
+
#
|
7
|
+
# @return [String] A string of the symbol of the technical indicator
|
8
|
+
def self.indicator_symbol
|
9
|
+
"obv_mean"
|
10
|
+
end
|
11
|
+
|
12
|
+
# Returns the name of the technical indicator
|
13
|
+
#
|
14
|
+
# @return [String] A string of the name of the technical indicator
|
15
|
+
def self.indicator_name
|
16
|
+
"On-balance Volume Mean"
|
17
|
+
end
|
18
|
+
|
19
|
+
# Returns an array of valid keys for options for this technical indicator
|
20
|
+
#
|
21
|
+
# @return [Array] An array of keys as symbols for valid options for this technical indicator
|
22
|
+
def self.valid_options
|
23
|
+
%i(period)
|
24
|
+
end
|
25
|
+
|
26
|
+
# Validates the provided options for this technical indicator
|
27
|
+
#
|
28
|
+
# @param options [Hash] The options for the technical indicator to be validated
|
29
|
+
#
|
30
|
+
# @return [Boolean] Returns true if options are valid or raises a ValidationError if they're not
|
31
|
+
def self.validate_options(options)
|
32
|
+
Validation.validate_options(options, valid_options)
|
33
|
+
end
|
34
|
+
|
35
|
+
# Calculates the minimum number of observations needed to calculate the technical indicator
|
36
|
+
#
|
37
|
+
# @param options [Hash] The options for the technical indicator
|
38
|
+
#
|
39
|
+
# @return [Integer] Returns the minimum number of observations needed to calculate the technical
|
40
|
+
# indicator based on the options provided
|
41
|
+
def self.min_data_size(period: 10)
|
42
|
+
period.to_i + 1
|
43
|
+
end
|
44
|
+
|
45
|
+
# Calculates the on-balance volume mean (OBV mean) for the data over the given period
|
46
|
+
# https://en.wikipedia.org/wiki/On-balance_volume
|
47
|
+
#
|
48
|
+
# @param data [Array] Array of hashes with keys (:date_time, :close, :volume)
|
49
|
+
# @param period [Integer] The given period to calculate the OBV mean
|
50
|
+
#
|
51
|
+
# @return [Array<ObvMeanValue>] An array of ObvMeanValue instances
|
52
|
+
def self.calculate(data, period: 10)
|
53
|
+
period = period.to_i
|
54
|
+
Validation.validate_numeric_data(data, :close, :volume)
|
55
|
+
Validation.validate_length(data, min_data_size(period: period))
|
56
|
+
Validation.validate_date_time_key(data)
|
57
|
+
|
58
|
+
data = data.sort_by { |row| row[:date_time] }
|
59
|
+
|
60
|
+
current_obv = 0
|
61
|
+
obvs = []
|
62
|
+
output = []
|
63
|
+
prior_close = nil
|
64
|
+
prior_volume = nil
|
65
|
+
|
66
|
+
data.each do |v|
|
67
|
+
volume = v[:volume]
|
68
|
+
close = v[:close]
|
69
|
+
|
70
|
+
unless prior_close.nil?
|
71
|
+
current_obv += volume if close > prior_close
|
72
|
+
current_obv -= volume if close < prior_close
|
73
|
+
obvs << current_obv
|
74
|
+
end
|
75
|
+
|
76
|
+
prior_volume = volume
|
77
|
+
prior_close = close
|
78
|
+
|
79
|
+
if obvs.size == period
|
80
|
+
output << ObvMeanValue.new(date_time: v[:date_time], obv_mean: ArrayHelper.average(obvs))
|
81
|
+
obvs.shift
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
output.sort_by(&:date_time).reverse
|
86
|
+
end
|
87
|
+
|
88
|
+
end
|
89
|
+
|
90
|
+
# The value class to be returned by calculations
|
91
|
+
class ObvMeanValue
|
92
|
+
|
93
|
+
# @return [String] the date_time of the obversation as it was provided
|
94
|
+
attr_accessor :date_time
|
95
|
+
|
96
|
+
# @return [Float] the obv_mean calculation value
|
97
|
+
attr_accessor :obv_mean
|
98
|
+
|
99
|
+
def initialize(date_time: nil, obv_mean: nil)
|
100
|
+
@date_time = date_time
|
101
|
+
@obv_mean = obv_mean
|
102
|
+
end
|
103
|
+
|
104
|
+
# @return [Hash] the attributes as a hash
|
105
|
+
def to_hash
|
106
|
+
{ date_time: @date_time, obv_mean: @obv_mean }
|
107
|
+
end
|
108
|
+
|
109
|
+
end
|
110
|
+
end
|
@@ -0,0 +1,133 @@
|
|
1
|
+
module TechnicalAnalysis
|
2
|
+
# Relative Strength Index
|
3
|
+
class Rsi < Indicator
|
4
|
+
|
5
|
+
# Returns the symbol of the technical indicator
|
6
|
+
#
|
7
|
+
# @return [String] A string of the symbol of the technical indicator
|
8
|
+
def self.indicator_symbol
|
9
|
+
"rsi"
|
10
|
+
end
|
11
|
+
|
12
|
+
# Returns the name of the technical indicator
|
13
|
+
#
|
14
|
+
# @return [String] A string of the name of the technical indicator
|
15
|
+
def self.indicator_name
|
16
|
+
"Relative Strength Index"
|
17
|
+
end
|
18
|
+
|
19
|
+
# Returns an array of valid keys for options for this technical indicator
|
20
|
+
#
|
21
|
+
# @return [Array] An array of keys as symbols for valid options for this technical indicator
|
22
|
+
def self.valid_options
|
23
|
+
%i(period price_key)
|
24
|
+
end
|
25
|
+
|
26
|
+
# Validates the provided options for this technical indicator
|
27
|
+
#
|
28
|
+
# @param options [Hash] The options for the technical indicator to be validated
|
29
|
+
#
|
30
|
+
# @return [Boolean] Returns true if options are valid or raises a ValidationError if they're not
|
31
|
+
def self.validate_options(options)
|
32
|
+
Validation.validate_options(options, valid_options)
|
33
|
+
end
|
34
|
+
|
35
|
+
# Calculates the minimum number of observations needed to calculate the technical indicator
|
36
|
+
#
|
37
|
+
# @param options [Hash] The options for the technical indicator
|
38
|
+
#
|
39
|
+
# @return [Integer] Returns the minimum number of observations needed to calculate the technical
|
40
|
+
# indicator based on the options provided
|
41
|
+
def self.min_data_size(period: 14, **params)
|
42
|
+
period.to_i + 1
|
43
|
+
end
|
44
|
+
|
45
|
+
# Calculates the relative strength index for the data over the given period
|
46
|
+
# https://en.wikipedia.org/wiki/Relative_strength_index
|
47
|
+
#
|
48
|
+
# @param data [Array] Array of hashes with keys (:date_time, :value)
|
49
|
+
# @param period [Integer] The given period to calculate the RSI
|
50
|
+
# @param price_key [Symbol] The hash key for the price data. Default :value
|
51
|
+
#
|
52
|
+
# @return [Array<RsiValue>] An array of RsiValue instances
|
53
|
+
def self.calculate(data, period: 14, price_key: :value)
|
54
|
+
period = period.to_i
|
55
|
+
price_key = price_key.to_sym
|
56
|
+
Validation.validate_numeric_data(data, price_key)
|
57
|
+
Validation.validate_length(data, min_data_size(period: period))
|
58
|
+
Validation.validate_date_time_key(data)
|
59
|
+
|
60
|
+
data = data.sort_by { |row| row[:date_time] }
|
61
|
+
|
62
|
+
output = []
|
63
|
+
prev_price = data.shift[price_key]
|
64
|
+
prev_avg = nil
|
65
|
+
price_changes = []
|
66
|
+
smoothing_period = period - 1
|
67
|
+
|
68
|
+
data.each do |v|
|
69
|
+
price_change = (v[price_key] - prev_price)
|
70
|
+
price_changes << price_change
|
71
|
+
|
72
|
+
if price_changes.size == period
|
73
|
+
if prev_avg.nil?
|
74
|
+
avg_gain = ArrayHelper.average(price_changes.map { |pc| pc.positive? ? pc : 0 })
|
75
|
+
avg_loss = ArrayHelper.average(price_changes.map { |pc| pc.negative? ? pc.abs : 0 })
|
76
|
+
else
|
77
|
+
if price_change > 0
|
78
|
+
current_loss = 0
|
79
|
+
current_gain = price_change
|
80
|
+
elsif price_change < 0
|
81
|
+
current_loss = price_change.abs
|
82
|
+
current_gain = 0
|
83
|
+
else
|
84
|
+
current_loss = 0
|
85
|
+
current_gain = 0
|
86
|
+
end
|
87
|
+
|
88
|
+
avg_gain = (((prev_avg[:gain] * smoothing_period) + current_gain) / period.to_f)
|
89
|
+
avg_loss = (((prev_avg[:loss] * smoothing_period) + current_loss) / period.to_f)
|
90
|
+
end
|
91
|
+
|
92
|
+
if avg_loss == 0
|
93
|
+
rsi = 100
|
94
|
+
else
|
95
|
+
rs = avg_gain / avg_loss
|
96
|
+
rsi = (100.00 - (100.00 / (1.00 + rs)))
|
97
|
+
end
|
98
|
+
|
99
|
+
output << RsiValue.new(date_time: v[:date_time], rsi: rsi)
|
100
|
+
|
101
|
+
prev_avg = { gain: avg_gain, loss: avg_loss }
|
102
|
+
price_changes.shift
|
103
|
+
end
|
104
|
+
|
105
|
+
prev_price = v[price_key]
|
106
|
+
end
|
107
|
+
|
108
|
+
output.sort_by(&:date_time).reverse
|
109
|
+
end
|
110
|
+
|
111
|
+
end
|
112
|
+
|
113
|
+
# The value class to be returned by calculations
|
114
|
+
class RsiValue
|
115
|
+
|
116
|
+
# @return [String] the date_time of the obversation as it was provided
|
117
|
+
attr_accessor :date_time
|
118
|
+
|
119
|
+
# @return [Float] the rsi calculation value
|
120
|
+
attr_accessor :rsi
|
121
|
+
|
122
|
+
def initialize(date_time: nil, rsi: nil)
|
123
|
+
@date_time = date_time
|
124
|
+
@rsi = rsi
|
125
|
+
end
|
126
|
+
|
127
|
+
# @return [Hash] the attributes as a hash
|
128
|
+
def to_hash
|
129
|
+
{ date_time: @date_time, rsi: @rsi }
|
130
|
+
end
|
131
|
+
|
132
|
+
end
|
133
|
+
end
|
@@ -0,0 +1,98 @@
|
|
1
|
+
module TechnicalAnalysis
|
2
|
+
# Simple Moving Average
|
3
|
+
class Sma < Indicator
|
4
|
+
|
5
|
+
# Returns the symbol of the technical indicator
|
6
|
+
#
|
7
|
+
# @return [String] A string of the symbol of the technical indicator
|
8
|
+
def self.indicator_symbol
|
9
|
+
"sma"
|
10
|
+
end
|
11
|
+
|
12
|
+
# Returns the name of the technical indicator
|
13
|
+
#
|
14
|
+
# @return [String] A string of the name of the technical indicator
|
15
|
+
def self.indicator_name
|
16
|
+
"Simple Moving Average"
|
17
|
+
end
|
18
|
+
|
19
|
+
# Returns an array of valid keys for options for this technical indicator
|
20
|
+
#
|
21
|
+
# @return [Array] An array of keys as symbols for valid options for this technical indicator
|
22
|
+
def self.valid_options
|
23
|
+
%i(period price_key)
|
24
|
+
end
|
25
|
+
|
26
|
+
# Validates the provided options for this technical indicator
|
27
|
+
#
|
28
|
+
# @param options [Hash] The options for the technical indicator to be validated
|
29
|
+
#
|
30
|
+
# @return [Boolean] Returns true if options are valid or raises a ValidationError if they're not
|
31
|
+
def self.validate_options(options)
|
32
|
+
Validation.validate_options(options, valid_options)
|
33
|
+
end
|
34
|
+
|
35
|
+
# Calculates the minimum number of observations needed to calculate the technical indicator
|
36
|
+
#
|
37
|
+
# @param options [Hash] The options for the technical indicator
|
38
|
+
#
|
39
|
+
# @return [Integer] Returns the minimum number of observations needed to calculate the technical
|
40
|
+
# indicator based on the options provided
|
41
|
+
def self.min_data_size(period: 30, **params)
|
42
|
+
period.to_i
|
43
|
+
end
|
44
|
+
|
45
|
+
# Calculates the simple moving average (SMA) for the data over the given period
|
46
|
+
# https://en.wikipedia.org/wiki/Moving_average#Simple_moving_average
|
47
|
+
#
|
48
|
+
# @param data [Array] Array of hashes with keys (:date_time, :value)
|
49
|
+
# @param period [Integer] The given period to calculate the SMA
|
50
|
+
# @param price_key [Symbol] The hash key for the price data. Default :value
|
51
|
+
#
|
52
|
+
# @return [Array<SmaValue>] An array of SmaValue instances
|
53
|
+
def self.calculate(data, period: 30, price_key: :value)
|
54
|
+
period = period.to_i
|
55
|
+
price_key = price_key.to_sym
|
56
|
+
Validation.validate_numeric_data(data, price_key)
|
57
|
+
Validation.validate_length(data, min_data_size(period: period))
|
58
|
+
Validation.validate_date_time_key(data)
|
59
|
+
|
60
|
+
data = data.sort_by { |row| row[:date_time] }
|
61
|
+
|
62
|
+
output = []
|
63
|
+
period_values = []
|
64
|
+
|
65
|
+
data.each do |v|
|
66
|
+
period_values << v[price_key]
|
67
|
+
if period_values.size == period
|
68
|
+
output << SmaValue.new(date_time: v[:date_time], sma: ArrayHelper.average(period_values))
|
69
|
+
period_values.shift
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
output.sort_by(&:date_time).reverse
|
74
|
+
end
|
75
|
+
|
76
|
+
end
|
77
|
+
|
78
|
+
# The value class to be returned by calculations
|
79
|
+
class SmaValue
|
80
|
+
|
81
|
+
# @return [String] the date_time of the obversation as it was provided
|
82
|
+
attr_accessor :date_time
|
83
|
+
|
84
|
+
# @return [Float] the sma calculation value
|
85
|
+
attr_accessor :sma
|
86
|
+
|
87
|
+
def initialize(date_time: nil, sma: nil)
|
88
|
+
@date_time = date_time
|
89
|
+
@sma = sma
|
90
|
+
end
|
91
|
+
|
92
|
+
# @return [Hash] the attributes as a hash
|
93
|
+
def to_hash
|
94
|
+
{ date_time: @date_time, sma: @sma }
|
95
|
+
end
|
96
|
+
|
97
|
+
end
|
98
|
+
end
|
@@ -0,0 +1,122 @@
|
|
1
|
+
module TechnicalAnalysis
|
2
|
+
# Stochastic Oscillator
|
3
|
+
class Sr < Indicator
|
4
|
+
|
5
|
+
# Returns the symbol of the technical indicator
|
6
|
+
#
|
7
|
+
# @return [String] A string of the symbol of the technical indicator
|
8
|
+
def self.indicator_symbol
|
9
|
+
"sr"
|
10
|
+
end
|
11
|
+
|
12
|
+
# Returns the name of the technical indicator
|
13
|
+
#
|
14
|
+
# @return [String] A string of the name of the technical indicator
|
15
|
+
def self.indicator_name
|
16
|
+
"Stochastic Oscillator"
|
17
|
+
end
|
18
|
+
|
19
|
+
# Returns an array of valid keys for options for this technical indicator
|
20
|
+
#
|
21
|
+
# @return [Array] An array of keys as symbols for valid options for this technical indicator
|
22
|
+
def self.valid_options
|
23
|
+
%i(period signal_period)
|
24
|
+
end
|
25
|
+
|
26
|
+
# Validates the provided options for this technical indicator
|
27
|
+
#
|
28
|
+
# @param options [Hash] The options for the technical indicator to be validated
|
29
|
+
#
|
30
|
+
# @return [Boolean] Returns true if options are valid or raises a ValidationError if they're not
|
31
|
+
def self.validate_options(options)
|
32
|
+
Validation.validate_options(options, valid_options)
|
33
|
+
end
|
34
|
+
|
35
|
+
# Calculates the minimum number of observations needed to calculate the technical indicator
|
36
|
+
#
|
37
|
+
# @param options [Hash] The options for the technical indicator
|
38
|
+
#
|
39
|
+
# @return [Integer] Returns the minimum number of observations needed to calculate the technical
|
40
|
+
# indicator based on the options provided
|
41
|
+
def self.min_data_size(period: 14, signal_period: 3)
|
42
|
+
period.to_i + signal_period.to_i - 1
|
43
|
+
end
|
44
|
+
|
45
|
+
# Calculates the stochastic oscillator (%K) for the data over the given period
|
46
|
+
# https://en.wikipedia.org/wiki/Stochastic_oscillator
|
47
|
+
#
|
48
|
+
# @param data [Array] Array of hashes with keys (:date_time, :high, :low, :close)
|
49
|
+
# @param period [Integer] The given period to calculate the SR
|
50
|
+
# @param signal_period [Integer] The given period to calculate the SMA as a signal line for SR
|
51
|
+
#
|
52
|
+
# @return [Array<SrValue>] An array of SrValue instances
|
53
|
+
def self.calculate(data, period: 14, signal_period: 3)
|
54
|
+
period = period.to_i
|
55
|
+
signal_period = signal_period.to_i
|
56
|
+
Validation.validate_numeric_data(data, :high, :low, :close)
|
57
|
+
Validation.validate_length(data, min_data_size(period: period, signal_period: signal_period))
|
58
|
+
Validation.validate_date_time_key(data)
|
59
|
+
|
60
|
+
data = data.sort_by { |row| row[:date_time] }
|
61
|
+
|
62
|
+
high_low_values = []
|
63
|
+
output = []
|
64
|
+
sr_values = []
|
65
|
+
|
66
|
+
data.each do |v|
|
67
|
+
high_low_values << { high: v[:high], low: v[:low] }
|
68
|
+
|
69
|
+
if high_low_values.size == period
|
70
|
+
lowest_low = high_low_values.map { |hlv| hlv[:low] }.min
|
71
|
+
highest_high = high_low_values.map { |hlv| hlv[:high] }.max
|
72
|
+
|
73
|
+
sr = (v[:close] - lowest_low) / (highest_high - lowest_low) * 100.00
|
74
|
+
|
75
|
+
sr_values << sr
|
76
|
+
|
77
|
+
if sr_values.size == signal_period
|
78
|
+
signal = ArrayHelper.average(sr_values)
|
79
|
+
|
80
|
+
output << SrValue.new(
|
81
|
+
date_time: v[:date_time],
|
82
|
+
sr: sr,
|
83
|
+
sr_signal: signal
|
84
|
+
)
|
85
|
+
|
86
|
+
sr_values.shift
|
87
|
+
end
|
88
|
+
|
89
|
+
high_low_values.shift
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
output.sort_by(&:date_time).reverse
|
94
|
+
end
|
95
|
+
|
96
|
+
end
|
97
|
+
|
98
|
+
# The value class to be returned by calculations
|
99
|
+
class SrValue
|
100
|
+
|
101
|
+
# @return [String] the date_time of the obversation as it was provided
|
102
|
+
attr_accessor :date_time
|
103
|
+
|
104
|
+
# @return [Float] the sr calculation value
|
105
|
+
attr_accessor :sr
|
106
|
+
|
107
|
+
# @return [Float] the sr_signal calculation value
|
108
|
+
attr_accessor :sr_signal
|
109
|
+
|
110
|
+
def initialize(date_time: nil, sr: nil, sr_signal: nil)
|
111
|
+
@date_time = date_time
|
112
|
+
@sr = sr
|
113
|
+
@sr_signal = sr_signal
|
114
|
+
end
|
115
|
+
|
116
|
+
# @return [Hash] the attributes as a hash
|
117
|
+
def to_hash
|
118
|
+
{ date_time: @date_time, sr: @sr, sr_signal: @sr_signal }
|
119
|
+
end
|
120
|
+
|
121
|
+
end
|
122
|
+
end
|