technical-analysis 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
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