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,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