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.
Files changed (79) hide show
  1. checksums.yaml +7 -0
  2. data/lib/technical-analysis.rb +3 -0
  3. data/lib/technical_analysis.rb +41 -0
  4. data/lib/technical_analysis/helpers/array_helper.rb +27 -0
  5. data/lib/technical_analysis/helpers/stock_calculation.rb +25 -0
  6. data/lib/technical_analysis/helpers/validation.rb +33 -0
  7. data/lib/technical_analysis/indicators/adi.rb +101 -0
  8. data/lib/technical_analysis/indicators/adtv.rb +98 -0
  9. data/lib/technical_analysis/indicators/adx.rb +168 -0
  10. data/lib/technical_analysis/indicators/ao.rb +105 -0
  11. data/lib/technical_analysis/indicators/atr.rb +109 -0
  12. data/lib/technical_analysis/indicators/bb.rb +126 -0
  13. data/lib/technical_analysis/indicators/cci.rb +105 -0
  14. data/lib/technical_analysis/indicators/cmf.rb +105 -0
  15. data/lib/technical_analysis/indicators/cr.rb +95 -0
  16. data/lib/technical_analysis/indicators/dc.rb +108 -0
  17. data/lib/technical_analysis/indicators/dlr.rb +97 -0
  18. data/lib/technical_analysis/indicators/dpo.rb +106 -0
  19. data/lib/technical_analysis/indicators/dr.rb +96 -0
  20. data/lib/technical_analysis/indicators/eom.rb +104 -0
  21. data/lib/technical_analysis/indicators/fi.rb +95 -0
  22. data/lib/technical_analysis/indicators/ichimoku.rb +179 -0
  23. data/lib/technical_analysis/indicators/indicator.rb +138 -0
  24. data/lib/technical_analysis/indicators/kc.rb +124 -0
  25. data/lib/technical_analysis/indicators/kst.rb +132 -0
  26. data/lib/technical_analysis/indicators/macd.rb +144 -0
  27. data/lib/technical_analysis/indicators/mfi.rb +119 -0
  28. data/lib/technical_analysis/indicators/mi.rb +121 -0
  29. data/lib/technical_analysis/indicators/nvi.rb +102 -0
  30. data/lib/technical_analysis/indicators/obv.rb +104 -0
  31. data/lib/technical_analysis/indicators/obv_mean.rb +110 -0
  32. data/lib/technical_analysis/indicators/rsi.rb +133 -0
  33. data/lib/technical_analysis/indicators/sma.rb +98 -0
  34. data/lib/technical_analysis/indicators/sr.rb +122 -0
  35. data/lib/technical_analysis/indicators/trix.rb +127 -0
  36. data/lib/technical_analysis/indicators/tsi.rb +139 -0
  37. data/lib/technical_analysis/indicators/uo.rb +130 -0
  38. data/lib/technical_analysis/indicators/vi.rb +117 -0
  39. data/lib/technical_analysis/indicators/vpt.rb +95 -0
  40. data/lib/technical_analysis/indicators/wr.rb +103 -0
  41. data/spec/helpers/array_helper_spec.rb +31 -0
  42. data/spec/helpers/validaton_spec.rb +22 -0
  43. data/spec/spec_helper.rb +26 -0
  44. data/spec/ta_test_data.csv +64 -0
  45. data/spec/technical_analysis/indicators/adi_spec.rb +116 -0
  46. data/spec/technical_analysis/indicators/adtv_spec.rb +98 -0
  47. data/spec/technical_analysis/indicators/adx_spec.rb +92 -0
  48. data/spec/technical_analysis/indicators/ao_spec.rb +86 -0
  49. data/spec/technical_analysis/indicators/atr_spec.rb +105 -0
  50. data/spec/technical_analysis/indicators/bb_spec.rb +100 -0
  51. data/spec/technical_analysis/indicators/cci_spec.rb +100 -0
  52. data/spec/technical_analysis/indicators/cmf_spec.rb +100 -0
  53. data/spec/technical_analysis/indicators/cr_spec.rb +119 -0
  54. data/spec/technical_analysis/indicators/dc_spec.rb +100 -0
  55. data/spec/technical_analysis/indicators/dlr_spec.rb +119 -0
  56. data/spec/technical_analysis/indicators/dpo_spec.rb +90 -0
  57. data/spec/technical_analysis/indicators/dr_spec.rb +119 -0
  58. data/spec/technical_analysis/indicators/eom_spec.rb +105 -0
  59. data/spec/technical_analysis/indicators/fi_spec.rb +118 -0
  60. data/spec/technical_analysis/indicators/ichimoku_spec.rb +95 -0
  61. data/spec/technical_analysis/indicators/indicator_spec.rb +120 -0
  62. data/spec/technical_analysis/indicators/kc_spec.rb +110 -0
  63. data/spec/technical_analysis/indicators/kst_spec.rb +78 -0
  64. data/spec/technical_analysis/indicators/macd_spec.rb +86 -0
  65. data/spec/technical_analysis/indicators/mfi_spec.rb +105 -0
  66. data/spec/technical_analysis/indicators/mi_spec.rb +79 -0
  67. data/spec/technical_analysis/indicators/nvi_spec.rb +119 -0
  68. data/spec/technical_analysis/indicators/obv_mean_spec.rb +109 -0
  69. data/spec/technical_analysis/indicators/obv_spec.rb +119 -0
  70. data/spec/technical_analysis/indicators/rsi_spec.rb +105 -0
  71. data/spec/technical_analysis/indicators/sma_spec.rb +115 -0
  72. data/spec/technical_analysis/indicators/sr_spec.rb +104 -0
  73. data/spec/technical_analysis/indicators/trix_spec.rb +76 -0
  74. data/spec/technical_analysis/indicators/tsi_spec.rb +87 -0
  75. data/spec/technical_analysis/indicators/uo_spec.rb +91 -0
  76. data/spec/technical_analysis/indicators/vi_spec.rb +105 -0
  77. data/spec/technical_analysis/indicators/vpt_spec.rb +118 -0
  78. data/spec/technical_analysis/indicators/wr_spec.rb +106 -0
  79. 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