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,105 @@
1
+ module TechnicalAnalysis
2
+ # Chaikin Money Flow
3
+ class Cmf < 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
+ "cmf"
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
+ "Chaikin Money Flow"
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: 20)
42
+ period.to_i
43
+ end
44
+
45
+ # Calculates the chaikin money flow (CMF) for the data over the given period
46
+ # https://stockcharts.com/school/doku.php?id=chart_school:technical_indicators:chaikin_money_flow_cmf
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 CMF
50
+ #
51
+ # @return [Array<CmfValue>] An array of CmfValue instances
52
+ def self.calculate(data, period: 20)
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
+ period_values = []
62
+
63
+ data.each do |v|
64
+ multiplier = ((v[:close] - v[:low]) - (v[:high] - v[:close])) / (v[:high] - v[:low])
65
+ mf_volume = multiplier * v[:volume]
66
+
67
+ period_values << { volume: v[:volume], mf_volume: mf_volume }
68
+
69
+ if period_values.size == period
70
+ volume_sum = ArrayHelper.sum(period_values.map { |pv| pv[:volume] })
71
+ mf_volume_sum = ArrayHelper.sum(period_values.map { |pv| pv[:mf_volume] })
72
+ cmf = mf_volume_sum / volume_sum
73
+
74
+ output << CmfValue.new(date_time: v[:date_time], cmf: cmf)
75
+
76
+ period_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 CmfValue
87
+
88
+ # @return [String] the date_time of the obversation as it was provided
89
+ attr_accessor :date_time
90
+
91
+ # @return [Float] the cmf calculation value
92
+ attr_accessor :cmf
93
+
94
+ def initialize(date_time: nil, cmf: nil)
95
+ @date_time = date_time
96
+ @cmf = cmf
97
+ end
98
+
99
+ # @return [Hash] the attributes as a hash
100
+ def to_hash
101
+ { date_time: @date_time, cmf: @cmf }
102
+ end
103
+
104
+ end
105
+ end
@@ -0,0 +1,95 @@
1
+ module TechnicalAnalysis
2
+ # Cumulative Return
3
+ class Cr < 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
+ "cr"
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
+ "Cumulative Return"
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(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(**params)
42
+ 1
43
+ end
44
+
45
+ # Calculates the cumulative return (CR) for the data over the given period
46
+ # https://www.investopedia.com/terms/c/cumulativereturn.asp
47
+ #
48
+ # @param data [Array] Array of hashes with keys (:date_time, :value)
49
+ # @param price_key [Symbol] The hash key for the price data. Default :value
50
+ #
51
+ # @return [Array<CrValue>] An array of CrValue instances
52
+ def self.calculate(data, price_key: :value)
53
+ price_key = price_key.to_sym
54
+ Validation.validate_numeric_data(data, price_key)
55
+ Validation.validate_length(data, min_data_size({}))
56
+ Validation.validate_date_time_key(data)
57
+
58
+ data = data.sort_by { |row| row[:date_time] }
59
+
60
+ output = []
61
+ start_price = data.first[price_key]
62
+
63
+ data.each do |v|
64
+ output << CrValue.new(
65
+ date_time: v[:date_time],
66
+ cr: ((v[price_key] - start_price) / start_price)
67
+ )
68
+ end
69
+
70
+ output.sort_by(&:date_time).reverse
71
+ end
72
+
73
+ end
74
+
75
+ # The value class to be returned by calculations
76
+ class CrValue
77
+
78
+ # @return [String] the date_time of the obversation as it was provided
79
+ attr_accessor :date_time
80
+
81
+ # @return [Float] the cr calculation value
82
+ attr_accessor :cr
83
+
84
+ def initialize(date_time: nil, cr: nil)
85
+ @date_time = date_time
86
+ @cr = cr
87
+ end
88
+
89
+ # @return [Hash] the attributes as a hash
90
+ def to_hash
91
+ { date_time: @date_time, cr: @cr }
92
+ end
93
+
94
+ end
95
+ end
@@ -0,0 +1,108 @@
1
+ module TechnicalAnalysis
2
+ # Donchian Channel
3
+ class Dc < 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
+ "dc"
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
+ "Donchian Channel"
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: 20, **params)
42
+ period.to_i
43
+ end
44
+
45
+ # Calculates the donchian channel (DC) for the data over the given period
46
+ # https://en.wikipedia.org/wiki/Donchian_channel
47
+ #
48
+ # @param data [Array] Array of hashes with keys (:date_time, :value)
49
+ # @param period [Integer] The given period to calculate the DC
50
+ # @param price_key [Symbol] The hash key for the price data. Default :value
51
+ #
52
+ # @return [Array<DcValue>] An array of DcValue instances
53
+ def self.calculate(data, period: 20, 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
+
68
+ if period_values.size == period
69
+ output << DcValue.new(
70
+ date_time: v[:date_time],
71
+ upper_bound: period_values.max,
72
+ lower_bound: period_values.min
73
+ )
74
+
75
+ period_values.shift
76
+ end
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 DcValue
86
+
87
+ # @return [String] the date_time of the obversation as it was provided
88
+ attr_accessor :date_time
89
+
90
+ # @return [Float] the upper_bound calculation value
91
+ attr_accessor :upper_bound
92
+
93
+ # @return [Float] the lower_bound calculation value
94
+ attr_accessor :lower_bound
95
+
96
+ def initialize(date_time: nil, upper_bound: nil, lower_bound: ninl)
97
+ @date_time = date_time
98
+ @upper_bound = upper_bound
99
+ @lower_bound = lower_bound
100
+ end
101
+
102
+ # @return [Hash] the attributes as a hash
103
+ def to_hash
104
+ { date_time: @date_time, upper_bound: @upper_bound, lower_bound: @lower_bound }
105
+ end
106
+
107
+ end
108
+ end
@@ -0,0 +1,97 @@
1
+ module TechnicalAnalysis
2
+ # Daily Log Return
3
+ class Dlr < 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
+ "dlr"
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
+ "Daily Log Return"
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(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(**params)
42
+ 1
43
+ end
44
+
45
+ # Calculates the daily log return (percent expressed as a decimal) for the data over the given period
46
+ # https://www.quora.com/What-are-daily-log-returns-of-an-equity
47
+ # https://en.wikipedia.org/wiki/Rate_of_return#Logarithmic_or_continuously_compounded_return
48
+ #
49
+ # @param data [Array] Array of hashes with keys (:date_time, :value)
50
+ # @param price_key [Symbol] The hash key for the price data. Default :value
51
+ #
52
+ # @return [Array<DlrValue>] An array of DlrValue instances
53
+ def self.calculate(data, price_key: :value)
54
+ price_key = price_key.to_sym
55
+ Validation.validate_numeric_data(data, price_key)
56
+ Validation.validate_length(data, min_data_size({}))
57
+ Validation.validate_date_time_key(data)
58
+
59
+ data = data.sort_by { |row| row[:date_time] }
60
+
61
+ output = []
62
+ prev_price = data.first[price_key].to_f
63
+
64
+ data.each do |v|
65
+ current_price = v[:close].to_f
66
+
67
+ output << DlrValue.new(date_time: v[:date_time], dlr: Math.log(current_price / prev_price))
68
+
69
+ prev_price = current_price
70
+ end
71
+
72
+ output.sort_by(&:date_time).reverse
73
+ end
74
+
75
+ end
76
+
77
+ # The value class to be returned by calculations
78
+ class DlrValue
79
+
80
+ # @return [String] the date_time of the obversation as it was provided
81
+ attr_accessor :date_time
82
+
83
+ # @return [Float] the dlr calculation value
84
+ attr_accessor :dlr
85
+
86
+ def initialize(date_time: nil, dlr: nil)
87
+ @date_time = date_time
88
+ @dlr = dlr
89
+ end
90
+
91
+ # @return [Hash] the attributes as a hash
92
+ def to_hash
93
+ { date_time: @date_time, dlr: @dlr }
94
+ end
95
+
96
+ end
97
+ end
@@ -0,0 +1,106 @@
1
+ module TechnicalAnalysis
2
+ # Detrended Price Oscillator
3
+ class Dpo < 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
+ "dpo"
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
+ "Detrended Price 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 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 + (period.to_i / 2)
43
+ end
44
+
45
+ # Calculates the detrended price oscillator for the data over the given period
46
+ # https://en.wikipedia.org/wiki/Detrended_price_oscillator
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<DpoValue>]
53
+ def self.calculate(data, period: 20, 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
+ index = period + (period / 2) - 1
63
+ midpoint_index = (period / 2) + 1
64
+ output = []
65
+
66
+ while index < data.size
67
+ current_record = data[index]
68
+ date_time = current_record[:date_time]
69
+ current_price = current_record[price_key]
70
+
71
+ sma_range = (index - midpoint_index - period + 2)..(index - midpoint_index + 1)
72
+ midpoint_period_sma = ArrayHelper.average(data[sma_range].map { |v| v[price_key] })
73
+
74
+ dpo = (current_price - midpoint_period_sma)
75
+
76
+ output << DpoValue.new(date_time: date_time, dpo: dpo)
77
+
78
+ index += 1
79
+ end
80
+
81
+ output.sort_by(&:date_time).reverse
82
+ end
83
+
84
+ end
85
+
86
+ # The value class to be returned by calculations
87
+ class DpoValue
88
+
89
+ # @return [String] the date_time of the obversation as it was provided
90
+ attr_accessor :date_time
91
+
92
+ # @return [Float] the dpo calculation value
93
+ attr_accessor :dpo
94
+
95
+ def initialize(date_time: nil, dpo: nil)
96
+ @date_time = date_time
97
+ @dpo = dpo
98
+ end
99
+
100
+ # @return [Hash] the attributes as a hash
101
+ def to_hash
102
+ { date_time: @date_time, dpo: @dpo }
103
+ end
104
+
105
+ end
106
+ end