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
+ # 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