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,119 @@
|
|
1
|
+
module TechnicalAnalysis
|
2
|
+
# Monoey Flow Index
|
3
|
+
class Mfi < 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
|
+
"mfi"
|
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
|
+
"Money Flow 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)
|
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)
|
42
|
+
period.to_i + 1
|
43
|
+
end
|
44
|
+
|
45
|
+
# Calculates the money flow index (MFI) for the data over the given period
|
46
|
+
# https://en.wikipedia.org/wiki/Money_flow_index
|
47
|
+
#
|
48
|
+
# @param data [Array] Array of hashes with keys (:date_time, :high, :low, :close, :volume)
|
49
|
+
# @param period [Integer] The given period to calculate the MFI
|
50
|
+
#
|
51
|
+
# @return [Array<MfiValue>] An array of MfiValue instances
|
52
|
+
def self.calculate(data, period: 14)
|
53
|
+
period = period.to_i
|
54
|
+
Validation.validate_numeric_data(data, :high, :low, :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
|
+
output = []
|
61
|
+
prev_typical_price = StockCalculation.typical_price(data.first)
|
62
|
+
raw_money_flows = []
|
63
|
+
|
64
|
+
data.shift
|
65
|
+
|
66
|
+
data.each do |v|
|
67
|
+
typical_price = StockCalculation.typical_price(v)
|
68
|
+
|
69
|
+
if typical_price < prev_typical_price
|
70
|
+
money_flow = (-1.0 * typical_price * v[:volume])
|
71
|
+
elsif typical_price > prev_typical_price
|
72
|
+
money_flow = (typical_price * v[:volume])
|
73
|
+
else
|
74
|
+
money_flow = 0.0
|
75
|
+
end
|
76
|
+
|
77
|
+
raw_money_flows << money_flow
|
78
|
+
|
79
|
+
if raw_money_flows.size == period
|
80
|
+
positive_period_flows = ArrayHelper.sum(raw_money_flows.map { |rmf| rmf.positive? ? rmf : 0 })
|
81
|
+
negative_period_flows = ArrayHelper.sum(raw_money_flows.map { |rmf| rmf.negative? ? rmf.abs : 0 })
|
82
|
+
|
83
|
+
money_flow_ratio = (positive_period_flows / negative_period_flows)
|
84
|
+
mfi = (100.00 - (100.00 / (1.0 + money_flow_ratio)))
|
85
|
+
|
86
|
+
output << MfiValue.new(date_time: v[:date_time], mfi: mfi)
|
87
|
+
|
88
|
+
raw_money_flows.shift
|
89
|
+
end
|
90
|
+
|
91
|
+
prev_typical_price = typical_price
|
92
|
+
end
|
93
|
+
|
94
|
+
output.sort_by(&:date_time).reverse
|
95
|
+
end
|
96
|
+
|
97
|
+
end
|
98
|
+
|
99
|
+
# The value class to be returned by calculations
|
100
|
+
class MfiValue
|
101
|
+
|
102
|
+
# @return [String] the date_time of the obversation as it was provided
|
103
|
+
attr_accessor :date_time
|
104
|
+
|
105
|
+
# @return [Float] the mfi calculation value
|
106
|
+
attr_accessor :mfi
|
107
|
+
|
108
|
+
def initialize(date_time: nil, mfi: nil)
|
109
|
+
@date_time = date_time
|
110
|
+
@mfi = mfi
|
111
|
+
end
|
112
|
+
|
113
|
+
# @return [Hash] the attributes as a hash
|
114
|
+
def to_hash
|
115
|
+
{ date_time: @date_time, mfi: @mfi }
|
116
|
+
end
|
117
|
+
|
118
|
+
end
|
119
|
+
end
|
@@ -0,0 +1,121 @@
|
|
1
|
+
module TechnicalAnalysis
|
2
|
+
# Mass Index
|
3
|
+
class Mi < 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
|
+
"mi"
|
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
|
+
"Mass 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(ema_period sum_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(ema_period: 9, sum_period: 25)
|
42
|
+
(ema_period.to_i * 2) + sum_period.to_i - 2
|
43
|
+
end
|
44
|
+
|
45
|
+
# Calculates the mass index (MI) for the data over the given period
|
46
|
+
# https://en.wikipedia.org/wiki/Mass_index
|
47
|
+
#
|
48
|
+
# @param data [Array] Array of hashes with keys (:date_time, :high, :low)
|
49
|
+
# @param ema_period [Integer] The given period to calculate the EMA and EMA of EMA
|
50
|
+
# @param sum_period [Integer] The given period to calculate the sum of EMA ratios
|
51
|
+
#
|
52
|
+
# @return [Array<MiValue>] An array of MiValue instances
|
53
|
+
def self.calculate(data, ema_period: 9, sum_period: 25)
|
54
|
+
ema_period = ema_period.to_i
|
55
|
+
sum_period = sum_period.to_i
|
56
|
+
Validation.validate_numeric_data(data, :high, :low)
|
57
|
+
Validation.validate_length(data, min_data_size(ema_period: ema_period, sum_period: sum_period))
|
58
|
+
Validation.validate_date_time_key(data)
|
59
|
+
|
60
|
+
data = data.sort_by { |row| row[:date_time] }
|
61
|
+
|
62
|
+
double_emas = []
|
63
|
+
high_low_diffs = []
|
64
|
+
output = []
|
65
|
+
ratio_of_emas = []
|
66
|
+
single_emas = []
|
67
|
+
|
68
|
+
data.each do |v|
|
69
|
+
high_low_diff = v[:high] - v[:low]
|
70
|
+
high_low_diffs << high_low_diff
|
71
|
+
|
72
|
+
if high_low_diffs.size == ema_period
|
73
|
+
single_ema = StockCalculation.ema(high_low_diff, high_low_diffs, ema_period, single_emas.last)
|
74
|
+
single_emas << single_ema
|
75
|
+
|
76
|
+
if single_emas.size == ema_period
|
77
|
+
double_ema = StockCalculation.ema(single_emas.last, single_emas, ema_period, double_emas.last)
|
78
|
+
double_emas << double_ema
|
79
|
+
|
80
|
+
ratio_of_emas << (single_ema / double_ema)
|
81
|
+
|
82
|
+
if ratio_of_emas.size == sum_period
|
83
|
+
output << MiValue.new(date_time: v[:date_time], mi: ArrayHelper.sum(ratio_of_emas))
|
84
|
+
|
85
|
+
double_emas.shift
|
86
|
+
ratio_of_emas.shift
|
87
|
+
end
|
88
|
+
|
89
|
+
single_emas.shift
|
90
|
+
end
|
91
|
+
|
92
|
+
high_low_diffs.shift
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
output.sort_by(&:date_time).reverse
|
97
|
+
end
|
98
|
+
|
99
|
+
end
|
100
|
+
|
101
|
+
# The value class to be returned by calculations
|
102
|
+
class MiValue
|
103
|
+
|
104
|
+
# @return [String] the date_time of the obversation as it was provided
|
105
|
+
attr_accessor :date_time
|
106
|
+
|
107
|
+
# @return [Float] the mi calculation value
|
108
|
+
attr_accessor :mi
|
109
|
+
|
110
|
+
def initialize(date_time: nil, mi: nil)
|
111
|
+
@date_time = date_time
|
112
|
+
@mi = mi
|
113
|
+
end
|
114
|
+
|
115
|
+
# @return [Hash] the attributes as a hash
|
116
|
+
def to_hash
|
117
|
+
{ date_time: @date_time, mi: @mi }
|
118
|
+
end
|
119
|
+
|
120
|
+
end
|
121
|
+
end
|
@@ -0,0 +1,102 @@
|
|
1
|
+
module TechnicalAnalysis
|
2
|
+
# Negative Volume Index
|
3
|
+
class Nvi < 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
|
+
"nvi"
|
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
|
+
"Negative Volume 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
|
+
[]
|
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
|
+
return true if options == {}
|
33
|
+
raise Validation::ValidationError.new "This indicator doesn't accept any options."
|
34
|
+
end
|
35
|
+
|
36
|
+
# Calculates the minimum number of observations needed to calculate the technical indicator
|
37
|
+
#
|
38
|
+
# @param options [Hash] The options for the technical indicator
|
39
|
+
#
|
40
|
+
# @return [Integer] Returns the minimum number of observations needed to calculate the technical
|
41
|
+
# indicator based on the options provided
|
42
|
+
def self.min_data_size(**params)
|
43
|
+
1
|
44
|
+
end
|
45
|
+
|
46
|
+
# Calculates the negative volume index (NVI) for the data
|
47
|
+
# https://en.wikipedia.org/wiki/Negative_volume_index
|
48
|
+
#
|
49
|
+
# @param data [Array] Array of hashes with keys (:date_time, :close, :volume)
|
50
|
+
#
|
51
|
+
# @return [Array<NviValue>] An array of NviValue instances
|
52
|
+
def self.calculate(data)
|
53
|
+
Validation.validate_numeric_data(data, :close, :volume)
|
54
|
+
Validation.validate_length(data, min_data_size({}))
|
55
|
+
Validation.validate_date_time_key(data)
|
56
|
+
|
57
|
+
data = data.sort_by { |row| row[:date_time] }
|
58
|
+
|
59
|
+
nvi_cumulative = 1_000.00
|
60
|
+
output = []
|
61
|
+
prev_price = data.shift
|
62
|
+
|
63
|
+
output << NviValue.new(date_time: prev_price[:date_time], nvi: nvi_cumulative) # Start with default of 1_000
|
64
|
+
|
65
|
+
data.each do |v|
|
66
|
+
volume_change = ((v[:volume] - prev_price[:volume]) / prev_price[:volume])
|
67
|
+
|
68
|
+
if volume_change < 0
|
69
|
+
price_change = ((v[:close] - prev_price[:close]) / prev_price[:close]) * 100.00
|
70
|
+
nvi_cumulative += price_change
|
71
|
+
end
|
72
|
+
|
73
|
+
output << NviValue.new(date_time: v[:date_time], nvi: nvi_cumulative)
|
74
|
+
prev_price = v
|
75
|
+
end
|
76
|
+
|
77
|
+
output.sort_by(&:date_time).reverse
|
78
|
+
end
|
79
|
+
|
80
|
+
end
|
81
|
+
|
82
|
+
# The value class to be returned by calculations
|
83
|
+
class NviValue
|
84
|
+
|
85
|
+
# @return [String] the date_time of the obversation as it was provided
|
86
|
+
attr_accessor :date_time
|
87
|
+
|
88
|
+
# @return [Float] the nvi calculation value
|
89
|
+
attr_accessor :nvi
|
90
|
+
|
91
|
+
def initialize(date_time: nil, nvi: nil)
|
92
|
+
@date_time = date_time
|
93
|
+
@nvi = nvi
|
94
|
+
end
|
95
|
+
|
96
|
+
# @return [Hash] the attributes as a hash
|
97
|
+
def to_hash
|
98
|
+
{ date_time: @date_time, nvi: @nvi }
|
99
|
+
end
|
100
|
+
|
101
|
+
end
|
102
|
+
end
|
@@ -0,0 +1,104 @@
|
|
1
|
+
module TechnicalAnalysis
|
2
|
+
# On-balance Volume
|
3
|
+
class Obv < 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"
|
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"
|
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
|
+
[]
|
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
|
+
return true if options == {}
|
33
|
+
raise Validation::ValidationError.new "This indicator doesn't accept any options."
|
34
|
+
end
|
35
|
+
|
36
|
+
# Calculates the minimum number of observations needed to calculate the technical indicator
|
37
|
+
#
|
38
|
+
# @param options [Hash] The options for the technical indicator
|
39
|
+
#
|
40
|
+
# @return [Integer] Returns the minimum number of observations needed to calculate the technical
|
41
|
+
# indicator based on the options provided
|
42
|
+
def self.min_data_size(**params)
|
43
|
+
1
|
44
|
+
end
|
45
|
+
|
46
|
+
# Calculates the on-balance volume (OBV) for the data over the given period
|
47
|
+
# https://en.wikipedia.org/wiki/On-balance_volume
|
48
|
+
#
|
49
|
+
# @param data [Array] Array of hashes with keys (:date_time, :close, :volume)
|
50
|
+
#
|
51
|
+
# @return [Array<ObvValue>] An array of ObvValue instances
|
52
|
+
def self.calculate(data)
|
53
|
+
Validation.validate_numeric_data(data, :close, :volume)
|
54
|
+
Validation.validate_length(data, min_data_size({}))
|
55
|
+
Validation.validate_date_time_key(data)
|
56
|
+
|
57
|
+
data = data.sort_by { |row| row[:date_time] }
|
58
|
+
|
59
|
+
current_obv = 0
|
60
|
+
output = []
|
61
|
+
prior_close = nil
|
62
|
+
prior_volume = nil
|
63
|
+
|
64
|
+
data.each do |v|
|
65
|
+
volume = v[:volume]
|
66
|
+
close = v[:close]
|
67
|
+
|
68
|
+
unless prior_close.nil?
|
69
|
+
current_obv += volume if close > prior_close
|
70
|
+
current_obv -= volume if close < prior_close
|
71
|
+
end
|
72
|
+
|
73
|
+
output << ObvValue.new(date_time: v[:date_time], obv: current_obv)
|
74
|
+
|
75
|
+
prior_volume = volume
|
76
|
+
prior_close = close
|
77
|
+
end
|
78
|
+
|
79
|
+
output.sort_by(&:date_time).reverse
|
80
|
+
end
|
81
|
+
|
82
|
+
end
|
83
|
+
|
84
|
+
# The value class to be returned by calculations
|
85
|
+
class ObvValue
|
86
|
+
|
87
|
+
# @return [String] the date_time of the obversation as it was provided
|
88
|
+
attr_accessor :date_time
|
89
|
+
|
90
|
+
# @return [Float] the obv calculation value
|
91
|
+
attr_accessor :obv
|
92
|
+
|
93
|
+
def initialize(date_time: nil, obv: nil)
|
94
|
+
@date_time = date_time
|
95
|
+
@obv = obv
|
96
|
+
end
|
97
|
+
|
98
|
+
# @return [Hash] the attributes as a hash
|
99
|
+
def to_hash
|
100
|
+
{ date_time: @date_time, obv: @obv }
|
101
|
+
end
|
102
|
+
|
103
|
+
end
|
104
|
+
end
|