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,105 @@
1
+ module TechnicalAnalysis
2
+ # Awesome Oscillator
3
+ class Ao < 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
+ "ao"
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
+ "Awesome 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(short_period long_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(long_period: 34, **params)
42
+ long_period.to_i
43
+ end
44
+
45
+ # Calculates the awesome oscillator for the data over the given period
46
+ # https://www.tradingview.com/wiki/Awesome_Oscillator_(AO)
47
+ #
48
+ # @param data [Array] Array of hashes with keys (:date_time, :high, :low)
49
+ # @param short_period [Integer] The given period to calculate the short period SMA
50
+ # @param long_period [Integer] The given period to calculate the long period SMA
51
+ #
52
+ # @return [Array<AoValue>] An array of AoValue instances
53
+ def self.calculate(data, short_period: 5, long_period: 34)
54
+ short_period = short_period.to_i
55
+ long_period = long_period.to_i
56
+ Validation.validate_numeric_data(data, :high, :low)
57
+ Validation.validate_length(data, min_data_size(long_period: long_period))
58
+ Validation.validate_date_time_key(data)
59
+
60
+ data = data.sort_by { |row| row[:date_time] }
61
+
62
+ midpoint_values = []
63
+ output = []
64
+
65
+ data.each do |v|
66
+ midpoint = (v[:high] + v[:low]) / 2
67
+ midpoint_values << midpoint
68
+
69
+ if midpoint_values.size == long_period
70
+ short_period_sma = ArrayHelper.average(midpoint_values.last(short_period))
71
+ long_period_sma = ArrayHelper.average(midpoint_values)
72
+ value = short_period_sma - long_period_sma
73
+
74
+ output << AoValue.new(date_time: v[:date_time], ao: value)
75
+
76
+ midpoint_values.shift
77
+ end
78
+ end
79
+
80
+ output.sort_by(&:date_time).reverse
81
+ end
82
+
83
+ end
84
+
85
+ # The value class to be returned by calculations
86
+ class AoValue
87
+
88
+ # @return [String] the date_time of the obversation as it was provided
89
+ attr_accessor :date_time
90
+
91
+ # @return [Float] the ao calculation value
92
+ attr_accessor :ao
93
+
94
+ def initialize(date_time: nil, ao: nil)
95
+ @date_time = date_time
96
+ @ao = ao
97
+ end
98
+
99
+ # @return [Hash] the attributes as a hash
100
+ def to_hash
101
+ { date_time: @date_time, ao: @ao }
102
+ end
103
+
104
+ end
105
+ end
@@ -0,0 +1,109 @@
1
+ module TechnicalAnalysis
2
+ # Average True Range
3
+ class Atr < 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
+ "atr"
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
+ "Average True Range"
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 average true range (ATR) for the data over the given period
46
+ # https://en.wikipedia.org/wiki/Average_true_range
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 ATR
50
+ #
51
+ # @return [Array<AtrValue>] An array of AtrValue instances
52
+ def self.calculate(data, period: 14)
53
+ period = period.to_i
54
+ Validation.validate_numeric_data(data, :high, :low, :close)
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
+ period_values = []
62
+ prev_price = data.shift
63
+
64
+ data.each do |v|
65
+ tr = StockCalculation.true_range(v[:high], v[:low], prev_price[:close])
66
+
67
+ period_values << tr
68
+
69
+ if period_values.size == period
70
+ if output.empty?
71
+ atr = ArrayHelper.average(period_values)
72
+ else
73
+ atr = (output.last.atr * (period - 1.0) + tr) / period.to_f
74
+ end
75
+
76
+ output << AtrValue.new(date_time: v[:date_time], atr: atr)
77
+
78
+ period_values.shift
79
+ end
80
+
81
+ prev_price = v
82
+ end
83
+
84
+ output.sort_by(&:date_time).reverse
85
+ end
86
+
87
+ end
88
+
89
+ # The value class to be returned by calculations
90
+ class AtrValue
91
+
92
+ # @return [String] the date_time of the obversation as it was provided
93
+ attr_accessor :date_time
94
+
95
+ # @return [Float] the atr calculation value
96
+ attr_accessor :atr
97
+
98
+ def initialize(date_time: nil, atr: nil)
99
+ @date_time = date_time
100
+ @atr = atr
101
+ end
102
+
103
+ # @return [Hash] the attributes as a hash
104
+ def to_hash
105
+ { date_time: @date_time, atr: @atr }
106
+ end
107
+
108
+ end
109
+ end
@@ -0,0 +1,126 @@
1
+ module TechnicalAnalysis
2
+ # Bollinger Bands
3
+ class Bb < 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
+ "bb"
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
+ "Bollinger Bands"
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 standard_deviations 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: 20, **params)
42
+ period.to_i
43
+ end
44
+
45
+ # Calculates the bollinger bands (BB) for the data over the given period
46
+ # https://en.wikipedia.org/wiki/Bollinger_Bands
47
+ #
48
+ # @param data [Array] Array of hashes with keys (:date_time, :value)
49
+ # @param period [Integer] The given period to calculate the BB
50
+ # @param standard_deviations [Float] The given standard deviations to calculate the upper and
51
+ # lower bands of the BB
52
+ # @param price_key [Symbol] The hash key for the price data. Default :value
53
+ #
54
+ # @return [Array<BbValue>] An array of BbValue instances
55
+ def self.calculate(data, period: 20, standard_deviations: 2, price_key: :value)
56
+ period = period.to_i
57
+ standard_deviations = standard_deviations.to_f
58
+ price_key = price_key.to_sym
59
+ Validation.validate_numeric_data(data, price_key)
60
+ Validation.validate_length(data, min_data_size(period: period))
61
+ Validation.validate_date_time_key(data)
62
+
63
+ data = data.sort_by { |row| row[:date_time] }
64
+
65
+ output = []
66
+ period_values = []
67
+
68
+ data.each do |v|
69
+ period_values << v[price_key]
70
+
71
+ if period_values.size == period
72
+ mb = ArrayHelper.average(period_values)
73
+ sd = ArrayHelper.standard_deviation(period_values)
74
+ ub = mb + standard_deviations * sd
75
+ lb = mb - standard_deviations * sd
76
+
77
+ output << BbValue.new(
78
+ date_time: v[:date_time],
79
+ lower_band: lb,
80
+ middle_band: mb,
81
+ upper_band: ub
82
+ )
83
+
84
+ period_values.shift
85
+ end
86
+ end
87
+
88
+ output.sort_by(&:date_time).reverse
89
+ end
90
+
91
+ end
92
+
93
+ # The value class to be returned by calculations
94
+ class BbValue
95
+
96
+ # @return [String] the date_time of the obversation as it was provided
97
+ attr_accessor :date_time
98
+
99
+ # @return [Float] the lower_band calculation value
100
+ attr_accessor :lower_band
101
+
102
+ # @return [Float] the middle_band calculation value
103
+ attr_accessor :middle_band
104
+
105
+ # @return [Float] the upper_band calculation value
106
+ attr_accessor :upper_band
107
+
108
+ def initialize(date_time: nil, lower_band: nil, middle_band: nil, upper_band: nil)
109
+ @date_time = date_time
110
+ @lower_band = lower_band
111
+ @middle_band = middle_band
112
+ @upper_band = upper_band
113
+ end
114
+
115
+ # @return [Hash] the attributes as a hash
116
+ def to_hash
117
+ {
118
+ date_time: @date_time,
119
+ lower_band: @lower_band,
120
+ middle_band: @middle_band,
121
+ upper_band: @upper_band
122
+ }
123
+ end
124
+
125
+ end
126
+ end
@@ -0,0 +1,105 @@
1
+ module TechnicalAnalysis
2
+ class Cci < Indicator
3
+
4
+ # Returns the symbol of the technical indicator
5
+ #
6
+ # @return [String] A string of the symbol of the technical indicator
7
+ def self.indicator_symbol
8
+ "cci"
9
+ end
10
+
11
+ # Returns the name of the technical indicator
12
+ #
13
+ # @return [String] A string of the name of the technical indicator
14
+ def self.indicator_name
15
+ "Commodity Channel Index"
16
+ end
17
+
18
+ # Returns an array of valid keys for options for this technical indicator
19
+ #
20
+ # @return [Array] An array of keys as symbols for valid options for this technical indicator
21
+ def self.valid_options
22
+ %i(period constant)
23
+ end
24
+
25
+ # Validates the provided options for this technical indicator
26
+ #
27
+ # @param options [Hash] The options for the technical indicator to be validated
28
+ #
29
+ # @return [Boolean] Returns true if options are valid or raises a ValidationError if they're not
30
+ def self.validate_options(options)
31
+ Validation.validate_options(options, valid_options)
32
+ end
33
+
34
+ # Calculates the minimum number of observations needed to calculate the technical indicator
35
+ #
36
+ # @param options [Hash] The options for the technical indicator
37
+ #
38
+ # @return [Integer] Returns the minimum number of observations needed to calculate the technical
39
+ # indicator based on the options provided
40
+ def self.min_data_size(period: 20, **params)
41
+ period.to_i
42
+ end
43
+
44
+ # Calculates the commodity channel index (CCI) for the data over the given period
45
+ # https://en.wikipedia.org/wiki/Commodity_channel_index
46
+ #
47
+ # @param data [Array] Array of hashes with keys (:date_time, :high, :low, :close)
48
+ # @param period [Integer] The given period to calculate the CCI
49
+ # @param constant [Float] The given constant to ensure that approximately 70 to 80 percent of
50
+ # CCI values would fall between −100 and +100
51
+ #
52
+ # @return [Array<CciValue>] An array of CciValue instances
53
+ def self.calculate(data, period: 20, constant: 0.015)
54
+ period = period.to_i
55
+ constant = constant.to_f
56
+ Validation.validate_numeric_data(data, :high, :low, :close)
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
+ typical_prices = []
64
+
65
+ data.each do |v|
66
+ typical_price = StockCalculation.typical_price(v)
67
+ typical_prices << typical_price
68
+
69
+ if typical_prices.size == period
70
+ period_sma = ArrayHelper.average(typical_prices)
71
+ mean_deviation = ArrayHelper.mean(typical_prices.map { |tp| (tp - period_sma).abs })
72
+ cci = (typical_price - period_sma) / (constant * mean_deviation)
73
+
74
+ output << CciValue.new(date_time: v[:date_time], cci: cci)
75
+
76
+ typical_prices.shift
77
+ end
78
+ end
79
+
80
+ output.sort_by(&:date_time).reverse
81
+ end
82
+
83
+ end
84
+
85
+ # The value class to be returned by calculations
86
+ class CciValue
87
+
88
+ # @return [String] the date_time of the obversation as it was provided
89
+ attr_accessor :date_time
90
+
91
+ # @return [Float] the cci calculation value
92
+ attr_accessor :cci
93
+
94
+ def initialize(date_time: nil, cci: nil)
95
+ @date_time = date_time
96
+ @cci = cci
97
+ end
98
+
99
+ # @return [Hash] the attributes as a hash
100
+ def to_hash
101
+ { date_time: @date_time, cci: @cci }
102
+ end
103
+
104
+ end
105
+ end