ruby-technical-analysis 0.1.1 → 1.0.3

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 +4 -4
  2. data/lib/ruby-technical-analysis.rb +1 -0
  3. data/lib/ruby_technical_analysis/indicator.rb +41 -0
  4. data/lib/ruby_technical_analysis/indicators/bollinger_bands.rb +50 -0
  5. data/lib/ruby_technical_analysis/indicators/chaikin_money_flow.rb +47 -0
  6. data/lib/ruby_technical_analysis/indicators/chande_momentum_oscillator.rb +56 -0
  7. data/lib/ruby_technical_analysis/indicators/commodity_channel_index.rb +70 -0
  8. data/lib/ruby_technical_analysis/indicators/envelopes_ema.rb +48 -0
  9. data/lib/ruby_technical_analysis/indicators/intraday_momentum_index.rb +45 -0
  10. data/lib/ruby_technical_analysis/indicators/macd.rb +86 -0
  11. data/lib/ruby_technical_analysis/indicators/mass_index.rb +70 -0
  12. data/lib/ruby_technical_analysis/indicators/moving_averages.rb +71 -0
  13. data/lib/ruby_technical_analysis/indicators/pivot_points.rb +75 -0
  14. data/lib/ruby_technical_analysis/indicators/price_channel.rb +43 -0
  15. data/lib/ruby_technical_analysis/indicators/qstick.rb +44 -0
  16. data/lib/ruby_technical_analysis/indicators/rate_of_change.rb +40 -0
  17. data/lib/ruby_technical_analysis/indicators/relative_momentum_index.rb +79 -0
  18. data/lib/ruby_technical_analysis/indicators/relative_strength_index.rb +74 -0
  19. data/lib/ruby_technical_analysis/indicators/statistical_methods.rb +40 -0
  20. data/lib/ruby_technical_analysis/indicators/stochastic_oscillator.rb +99 -0
  21. data/lib/ruby_technical_analysis/indicators/volume_oscillator.rb +50 -0
  22. data/lib/ruby_technical_analysis/indicators/volume_rate_of_change.rb +48 -0
  23. data/lib/ruby_technical_analysis/indicators/wilders_smoothing.rb +47 -0
  24. data/lib/ruby_technical_analysis/indicators/williams_percent_r.rb +69 -0
  25. data/lib/ruby_technical_analysis.rb +24 -7
  26. data/spec/ruby_technical_analysis/indicator_spec.rb +76 -0
  27. data/spec/ruby_technical_analysis/indicators/bollinger_bands_spec.rb +67 -0
  28. data/spec/ruby_technical_analysis/indicators/chaikin_money_flow_spec.rb +63 -0
  29. data/spec/ruby_technical_analysis/indicators/chande_momentum_oscillator_spec.rb +59 -0
  30. data/spec/ruby_technical_analysis/indicators/commodity_channel_index_spec.rb +66 -0
  31. data/spec/ruby_technical_analysis/indicators/envelopes_ema_spec.rb +69 -0
  32. data/spec/ruby_technical_analysis/indicators/intraday_momentum_spec.rb +63 -0
  33. data/spec/ruby_technical_analysis/indicators/macd_spec.rb +61 -0
  34. data/spec/ruby_technical_analysis/indicators/mass_index_spec.rb +67 -0
  35. data/spec/ruby_technical_analysis/indicators/moving_averages_spec.rb +81 -0
  36. data/spec/ruby_technical_analysis/indicators/pivot_points_spec.rb +43 -0
  37. data/spec/ruby_technical_analysis/indicators/price_channel_spec.rb +54 -0
  38. data/spec/ruby_technical_analysis/indicators/qstick_spec.rb +59 -0
  39. data/spec/ruby_technical_analysis/indicators/rate_of_change_spec.rb +59 -0
  40. data/spec/ruby_technical_analysis/indicators/relative_momentum_index_spec.rb +67 -0
  41. data/spec/ruby_technical_analysis/indicators/relative_strength_index_spec.rb +59 -0
  42. data/spec/ruby_technical_analysis/indicators/statistical_methods_spec.rb +91 -0
  43. data/spec/ruby_technical_analysis/indicators/stochastic_oscillator_spec.rb +106 -0
  44. data/spec/ruby_technical_analysis/indicators/volume_oscillator_spec.rb +98 -0
  45. data/spec/ruby_technical_analysis/indicators/volume_rate_of_change_spec.rb +67 -0
  46. data/spec/ruby_technical_analysis/indicators/wiilders_smoothing_spec.rb +67 -0
  47. data/spec/ruby_technical_analysis/indicators/williams_percent_r_spec.rb +71 -0
  48. data/spec/spec_helper.rb +1 -0
  49. metadata +100 -40
  50. data/.rubocop.yml +0 -34
  51. data/CHANGELOG.md +0 -5
  52. data/CODE_OF_CONDUCT.md +0 -84
  53. data/Gemfile +0 -12
  54. data/LICENSE.txt +0 -21
  55. data/README.md +0 -36
  56. data/Rakefile +0 -16
  57. data/lib/ruby-technical-analysis/indicators/bollinger_bands.rb +0 -25
  58. data/lib/ruby-technical-analysis/indicators/chaikin_money_flow.rb +0 -70
  59. data/lib/ruby-technical-analysis/indicators/chande_momentum_oscillator.rb +0 -34
  60. data/lib/ruby-technical-analysis/indicators/commodity_channel_index.rb +0 -64
  61. data/lib/ruby-technical-analysis/indicators/envelopes_ema.rb +0 -24
  62. data/lib/ruby-technical-analysis/indicators/intraday_momentum_index.rb +0 -48
  63. data/lib/ruby-technical-analysis/indicators/macd.rb +0 -47
  64. data/lib/ruby-technical-analysis/indicators/mass_index.rb +0 -73
  65. data/lib/ruby-technical-analysis/indicators/pivot_points.rb +0 -23
  66. data/lib/ruby-technical-analysis/indicators/price_channel.rb +0 -37
  67. data/lib/ruby-technical-analysis/indicators/qstick.rb +0 -40
  68. data/lib/ruby-technical-analysis/indicators/rate_of_change.rb +0 -18
  69. data/lib/ruby-technical-analysis/indicators/relative_momentum_index.rb +0 -66
  70. data/lib/ruby-technical-analysis/indicators/relative_strength_index.rb +0 -63
  71. data/lib/ruby-technical-analysis/indicators/stochastic_oscillator.rb +0 -65
  72. data/lib/ruby-technical-analysis/indicators/volume_oscillator.rb +0 -38
  73. data/lib/ruby-technical-analysis/indicators/volume_rate_of_change.rb +0 -26
  74. data/lib/ruby-technical-analysis/indicators/wilders_smoothing.rb +0 -27
  75. data/lib/ruby-technical-analysis/indicators/williams_percent_r.rb +0 -52
  76. data/lib/ruby-technical-analysis/moving_averages.rb +0 -85
  77. data/lib/ruby-technical-analysis/statistical_methods.rb +0 -24
  78. data/lib/ruby-technical-analysis/version.rb +0 -5
  79. data/sig/ruby-technical-analysis.rbs +0 -4
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 5daf0842421c382edf92ab3091c0dd0bb07182b78133724bdb5f3a0c602194bb
4
- data.tar.gz: 8d7e60360f663a0156dec18fd28112e0f1ab5db87d3d8c1786ffc6d3a443a168
3
+ metadata.gz: bf02e284fb57e98dce92fcd701de76ea3a51673422f5c05abac68eff952ddb4b
4
+ data.tar.gz: 62d5a60ec7b41cacfa5efedd2076252ad86c161b320b6552d7610992844f1976
5
5
  SHA512:
6
- metadata.gz: 47fbd9e1e23df84ded3562c94379e506eb64170407f3aaa104c584fbf8dfb518b1ae0648ae8cc1f6e50bf27674e6cc8faffea056ca2155bf33557b572f98f6ea
7
- data.tar.gz: bac115dfd0edf9518aaf6ccd78a017e877ef7085472202b972deb8cbca6984ac045d03ced9c668570cd18bd1ca2ed7f2a08e9026f76d89af5f5cddedbc50735b
6
+ metadata.gz: 254748860aee736da7e4d87179cbfb23b06da3417eea76e2dd635220ff7580145e4291dccae82f528f0fd0597eec9bb7a30af063ae020442618981429846db36
7
+ data.tar.gz: 62bbe4a770fbc0666cb360cc93d753eb4a57516bf8e04bc2ac71d25cad30fd3666b8a5d55c96ec98db5543a6ff16e32ed1f1db57adb98d5a0f1c69a0beabb3e3
@@ -0,0 +1 @@
1
+ require "ruby_technical_analysis"
@@ -0,0 +1,41 @@
1
+ module RubyTechnicalAnalysis
2
+ # Base class for indicators
3
+ class Indicator
4
+ attr_reader :series
5
+
6
+ def self.call(**kwargs) # standard:disable Style/ArgumentsForwarding
7
+ new(**kwargs).call # standard:disable Style/ArgumentsForwarding
8
+ end
9
+
10
+ # @param series [Array] An array of prices
11
+ def initialize(series: [])
12
+ @series = series
13
+ end
14
+
15
+ private
16
+
17
+ def extract_series(subset_length: nil)
18
+ subset_length.nil? ? series : series.last(subset_length)
19
+ end
20
+
21
+ def extract_highs_lows_closes_volumes(subset_length: nil)
22
+ highs, lows, closes, volumes = extract_series(subset_length: subset_length).transpose
23
+
24
+ [highs, lows, closes, volumes]
25
+ end
26
+
27
+ def extract_highs_lows_closes(subset_length: nil)
28
+ highs, lows, closes = extract_series(subset_length: subset_length).transpose
29
+
30
+ [highs, lows, closes]
31
+ end
32
+
33
+ def moving_averages(subset_length: nil, period: nil)
34
+ RubyTechnicalAnalysis::MovingAverages.new(series: extract_series(subset_length: subset_length), period: period)
35
+ end
36
+
37
+ def statistical_methods(subset_length: nil)
38
+ RubyTechnicalAnalysis::StatisticalMethods.new(series: extract_series(subset_length: subset_length))
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,50 @@
1
+ module RubyTechnicalAnalysis
2
+ # Bollinger Bands
3
+ #
4
+ # Find more information at: https://www.fidelity.com/learning-center/trading-investing/technical-analysis/technical-indicator-guide/bollinger-bands
5
+ class BollingerBands < Indicator
6
+ attr_reader :period, :standard_deviations
7
+
8
+ # @param series [Array] An array of prices, typically closing prices
9
+ # @param period [Integer] The number of periods to use in the calculation
10
+ # @param standard_deviations [Integer] The number of standard deviations to use in the calculation
11
+ def initialize(series: [], period: 20, standard_deviations: 2)
12
+ @period = period
13
+ @standard_deviations = standard_deviations
14
+
15
+ super(series: series)
16
+ end
17
+
18
+ # @return [Array] An array containing the current upper, middle, and lower bands of the series
19
+ def call
20
+ calculate_bollinger_bands
21
+ end
22
+
23
+ # @return [Boolean] Whether or not the object is valid
24
+ def valid?
25
+ period <= series.length && standard_deviations > 0
26
+ end
27
+
28
+ private
29
+
30
+ def _middle_price
31
+ @_middle_price ||= moving_averages(period: period).sma
32
+ end
33
+
34
+ def _twice_sd
35
+ @_twice_sd ||= standard_deviations * statistical_methods.standard_deviation
36
+ end
37
+
38
+ def upper_band
39
+ _middle_price + _twice_sd
40
+ end
41
+
42
+ def lower_band
43
+ _middle_price - _twice_sd
44
+ end
45
+
46
+ def calculate_bollinger_bands
47
+ [upper_band, _middle_price, lower_band].map { |band| band.truncate(3) }
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,47 @@
1
+ module RubyTechnicalAnalysis
2
+ # Chaikin Money Flow
3
+ #
4
+ # Find more information at: https://www.fidelity.com/learning-center/trading-investing/technical-analysis/technical-indicator-guide/cmf
5
+ class ChaikinMoneyFlow < Indicator
6
+ attr_reader :period
7
+
8
+ # @param series [Array] An array of arrays containing high, low, close, and volume information, e.g. [[high, low, close, volume], [high, low, close, volume]]
9
+ # @param period [Integer] The number of periods to use in the calculation
10
+ def initialize(series: [], period: 21)
11
+ @period = period
12
+ @cmf_sum = 0
13
+ @vol_sum = 0
14
+
15
+ super(series: series)
16
+ end
17
+
18
+ # @return [Float] The current Chaikin Money Flow value
19
+ def call
20
+ calculate_cmf
21
+ end
22
+
23
+ # @return [Boolean] Whether or not the object is valid
24
+ def valid?
25
+ period <= series.length
26
+ end
27
+
28
+ private
29
+
30
+ def calculate_cmf_sum
31
+ highs, lows, closes, volumes = extract_highs_lows_closes_volumes(subset_length: period)
32
+
33
+ period.times do |index|
34
+ high, low, close, volume = highs.at(index), lows.at(index), closes.at(index), volumes.at(index)
35
+
36
+ @vol_sum += volume
37
+ @cmf_sum += (((close - low) - (high - close)).to_f / (high - low)) * volume
38
+ end
39
+ end
40
+
41
+ def calculate_cmf
42
+ calculate_cmf_sum
43
+
44
+ (@vol_sum.zero? ? 0 : @cmf_sum.to_f / @vol_sum).round(5)
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,56 @@
1
+ module RubyTechnicalAnalysis
2
+ # Chande Momentum Oscillator
3
+ #
4
+ # Find more information at: https://www.fidelity.com/learning-center/trading-investing/technical-analysis/technical-indicator-guide/cmo
5
+ class ChandeMomentumOscillator < Indicator
6
+ attr_reader :period
7
+
8
+ # @param series [Array] An array of prices, typically closing prices
9
+ # @param period [Integer] The number of periods to use in the calculation
10
+ def initialize(series: [], period: 20)
11
+ @period = period
12
+ @up_change_sum = 0
13
+ @down_change_sum = 0
14
+
15
+ super(series: series)
16
+ end
17
+
18
+ # @return [Float] The current Chande Momentum Oscillator value
19
+ def call
20
+ calculate_cmo
21
+ end
22
+
23
+ # @return [Boolean] Whether or not the object is valid
24
+ def valid?
25
+ period + 1 <= series.length
26
+ end
27
+
28
+ private
29
+
30
+ def _closes
31
+ @_closes ||= extract_series(subset_length: period + 1)
32
+ end
33
+
34
+ def calculate_change_sums
35
+ (1..period).each do |index|
36
+ price_diff = _closes.at(index) - _closes.at(index - 1)
37
+ @up_change_sum += price_diff if price_diff.positive?
38
+ @down_change_sum -= price_diff if price_diff.negative?
39
+ end
40
+
41
+ @up_change_sum + @down_change_sum
42
+ end
43
+
44
+ def _up_sum_plus_down_sum
45
+ @_up_sum_plus_down_sum ||= calculate_change_sums
46
+ end
47
+
48
+ def calculate_oscillator_value
49
+ (@up_change_sum - @down_change_sum).to_f / _up_sum_plus_down_sum * 100
50
+ end
51
+
52
+ def calculate_cmo
53
+ _up_sum_plus_down_sum.zero? ? 0 : calculate_oscillator_value.round(4)
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,70 @@
1
+ module RubyTechnicalAnalysis
2
+ # Commodity Channel Index
3
+ #
4
+ # Find more information at: https://www.fidelity.com/learning-center/trading-investing/technical-analysis/technical-indicator-guide/cci
5
+ class CommodityChannelIndex < Indicator
6
+ attr_reader :period
7
+
8
+ # @param series [Array] An array of arrays containing high, low, close information, e.g. [[high, low, close], [high, low, close]]
9
+ # @param period [Integer] The number of periods to use in the calculation
10
+ def initialize(series: [], period: 20)
11
+ @period = period
12
+
13
+ super(series: series)
14
+ end
15
+
16
+ # @return [Float] The current Commodity Channel Index value
17
+ def call
18
+ calculate_cci
19
+ end
20
+
21
+ # @return [Boolean] Whether or not the object is valid
22
+ def valid?
23
+ min_size <= series.length
24
+ end
25
+
26
+ private
27
+
28
+ def min_size
29
+ (period * 2 - 1)
30
+ end
31
+
32
+ def calculate_typical_prices
33
+ highs, lows, closes = extract_highs_lows_closes(subset_length: min_size)
34
+
35
+ highs.zip(lows, closes).map { |high, low, close| (high + low + close) / 3 }
36
+ end
37
+
38
+ def _typical_prices
39
+ @_typical_prices ||= calculate_typical_prices
40
+ end
41
+
42
+ def _typical_prices_sma
43
+ @_typical_prices_sma ||=
44
+ _typical_prices.each_cons(period).map do |tp|
45
+ RubyTechnicalAnalysis::MovingAverages.new(series: tp, period: period).sma
46
+ end
47
+ end
48
+
49
+ def _period_sum
50
+ @_period_sum ||=
51
+ _typical_prices.last(period).sum do |tp|
52
+ (_typical_prices_sma.last - tp).abs
53
+ end
54
+ end
55
+
56
+ def _period_sum_next_period
57
+ @_period_sum_next_period ||=
58
+ (_period_sum.to_f / period) * 0.015
59
+ end
60
+
61
+ def _typical_prices_sma_minus_typical_prices
62
+ @_typical_prices_sma_minus_typical_prices ||=
63
+ _typical_prices.last(period).last - _typical_prices_sma.last
64
+ end
65
+
66
+ def calculate_cci
67
+ (_typical_prices_sma_minus_typical_prices.to_f / _period_sum_next_period)
68
+ end
69
+ end
70
+ end
@@ -0,0 +1,48 @@
1
+ module RubyTechnicalAnalysis
2
+ # Envelopes EMA
3
+ #
4
+ # Find more information at: https://www.fidelity.com/learning-center/trading-investing/technical-analysis/technical-indicator-guide/mae
5
+ #
6
+ # Note that this indicator is similar but not the exact same as the one in the link above. This indicator is based on the EMA, not the SMA.
7
+ class EnvelopesEma < Indicator
8
+ attr_reader :period, :percent
9
+
10
+ # @param series [Array] An array of prices, typically closing prices
11
+ # @param period [Integer] The number of periods to use in the calculation
12
+ # @param percent [Integer] The percent to use in the calculation
13
+ def initialize(series: [], period: 20, percent: 5)
14
+ @period = period
15
+ @percent = percent
16
+
17
+ super(series: series)
18
+ end
19
+
20
+ # @return [Array] An array containing the current upper, middle, and lower bands of the series
21
+ def call
22
+ caluculate_envelopes_ema
23
+ end
24
+
25
+ # @return [Boolean] Whether or not the object is valid
26
+ def valid?
27
+ period <= series.length && percent <= 100
28
+ end
29
+
30
+ private
31
+
32
+ def _eema
33
+ @_eema ||= moving_averages(period: period).ema
34
+ end
35
+
36
+ def eema_up
37
+ (_eema * (100 + percent)) / 100
38
+ end
39
+
40
+ def eema_down
41
+ (_eema * (100 - percent)) / 100
42
+ end
43
+
44
+ def caluculate_envelopes_ema
45
+ [eema_up, _eema, eema_down].map { |val| val.truncate(3) }
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,45 @@
1
+ module RubyTechnicalAnalysis
2
+ # Intraday Momentum Index
3
+ #
4
+ # Find more information at: https://www.investopedia.com/terms/i/intraday-momentum-index-imi.asp
5
+ class IntradayMomentumIndex < Indicator
6
+ attr_reader :period
7
+
8
+ # @param series [Array] An array of arrays containing open, close prices, e.g. [[open, close], [open, close]]
9
+ # @param period [Integer] The number of periods to use in the calculation
10
+ def initialize(series: [], period: 14)
11
+ @period = period
12
+ @gsum = 0
13
+ @lsum = 0
14
+
15
+ super(series: series)
16
+ end
17
+
18
+ # @return [Float] The current Intraday Momentum Index value
19
+ def call
20
+ calculate_imi
21
+ end
22
+
23
+ # @return [Boolean] Whether or not the object is valid
24
+ def valid?
25
+ period <= series.length
26
+ end
27
+
28
+ private
29
+
30
+ def calculate_gsum_plus_lsum
31
+ series.last(period).each do |open, close|
32
+ cmo = (close - open).abs
33
+ (close > open) ? @gsum += cmo : @lsum += cmo
34
+ end
35
+
36
+ @gsum + @lsum
37
+ end
38
+
39
+ def calculate_imi
40
+ gsum_plus_lsum = calculate_gsum_plus_lsum
41
+
42
+ (gsum_plus_lsum.zero? ? 0 : (@gsum / gsum_plus_lsum) * 100).round(4)
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,86 @@
1
+ module RubyTechnicalAnalysis
2
+ # Moving Average Convergence Divergence (MACD)
3
+ #
4
+ # Find more information at: https://www.fidelity.com/learning-center/trading-investing/technical-analysis/technical-indicator-guide/macd
5
+ class Macd < Indicator
6
+ attr_reader :fast_period, :slow_period, :signal_period
7
+
8
+ # @param series [Array] An array of prices, typically closing prices
9
+ # @param fast_period [Integer] The number of periods to use in the fast calculation
10
+ # @param slow_period [Integer] The number of periods to use in the slow calculation
11
+ # @param signal_period [Integer] The number of periods to use in the signal calculation
12
+ def initialize(series: [], fast_period: 12, slow_period: 26, signal_period: 9)
13
+ @fast_period = fast_period
14
+ @slow_period = slow_period
15
+ @signal_period = signal_period
16
+
17
+ super(series: series)
18
+ end
19
+
20
+ # @return [Array] An array containing the current MACD line, signal line, and histogram values
21
+ def call
22
+ calculate_macd
23
+ end
24
+
25
+ # @return [Boolean] Whether or not the object is valid
26
+ def valid?
27
+ series.length >= slow_period + signal_period
28
+ end
29
+
30
+ private
31
+
32
+ def fast_pct
33
+ (2.0 / (fast_period + 1)).truncate(6)
34
+ end
35
+
36
+ def slow_pct
37
+ (2.0 / (slow_period + 1)).truncate(6)
38
+ end
39
+
40
+ def period_array(percent)
41
+ period_values = Array(series.first)
42
+
43
+ series.drop(1).each_with_index do |value, index|
44
+ period_values << ((value * percent) + (period_values.last * (1 - percent))).round(3)
45
+ end
46
+
47
+ period_values
48
+ end
49
+
50
+ def _fast_period_array
51
+ @_fast_period_array ||= period_array(fast_pct)
52
+ end
53
+
54
+ def _slow_period_array
55
+ @_slow_period_array ||= period_array(slow_pct)
56
+ end
57
+
58
+ def _signal_array
59
+ @_signal_array ||= (0..signal_period - 1).map do |index|
60
+ calculated_index = slow_period + index - 1
61
+
62
+ (_fast_period_array.at(calculated_index) - _slow_period_array.at(calculated_index)).round(3)
63
+ end
64
+ end
65
+
66
+ def _signal_pct
67
+ @_signal_pct ||= (2.0 / (signal_period + 1)).truncate(6)
68
+ end
69
+
70
+ def _signal
71
+ @_signal ||= ((_signal_array.last * _signal_pct) + (_signal_array.at(-2) * (1 - _signal_pct))).round(3)
72
+ end
73
+
74
+ def _macd_line
75
+ @_macd_line ||= (_fast_period_array.last - _slow_period_array.last).round(4)
76
+ end
77
+
78
+ def histogram
79
+ (_macd_line - _signal).round(3)
80
+ end
81
+
82
+ def calculate_macd
83
+ [_macd_line, _signal, histogram]
84
+ end
85
+ end
86
+ end
@@ -0,0 +1,70 @@
1
+ module RubyTechnicalAnalysis
2
+ # Mass Index
3
+ #
4
+ # Find more information at: https://www.investopedia.com/terms/m/mass-index.asp
5
+ class MassIndex < Indicator
6
+ attr_reader :period
7
+
8
+ # @param series [Array] An array of arrays containing high and low prices, e.g. [[high, low], [high, low]]
9
+ # @param period [Integer] The number of periods to use in the calculation
10
+ def initialize(series: [], period: 9)
11
+ @period = period
12
+
13
+ super(series: series)
14
+ end
15
+
16
+ # @return [Float] The current Mass Index value
17
+ def call
18
+ calculate_mass_index
19
+ end
20
+
21
+ # @return [Boolean] Whether or not the object is valid
22
+ def valid?
23
+ series.length >= _full_period
24
+ end
25
+
26
+ private
27
+
28
+ def _highs
29
+ @_highs ||= series.map(&:first).last(_full_period)
30
+ end
31
+
32
+ def lows
33
+ series.map(&:last).last(_full_period)
34
+ end
35
+
36
+ def _full_period
37
+ @_full_period ||= (2 * period + 1)
38
+ end
39
+
40
+ def _low_multiple
41
+ @_low_multiple ||= (2.0 / (period + 1)).truncate(4)
42
+ end
43
+
44
+ def high_multiple
45
+ 1 - _low_multiple
46
+ end
47
+
48
+ def high_minus_low_array
49
+ (0..(_highs.size - 1)).map { |index| _highs.at(index) - lows.at(index) }
50
+ end
51
+
52
+ def _high_minus_low_ema_array
53
+ @_high_minus_low_ema_array ||= high_minus_low_array.each_with_index.reduce([]) do |arr, (value, index)|
54
+ arr << (index.zero? ? value.truncate(4) : ((value * _low_multiple) + (arr.at(index - 1) * high_multiple)).truncate(4))
55
+ end
56
+ end
57
+
58
+ def high_minus_low_ema_ema_array
59
+ [*0..period + 1].each.reduce([]) do |arr, index|
60
+ arr << (index.zero? ? _high_minus_low_ema_array.at(period - index - 1) : ((_high_minus_low_ema_array.at(period + index - 1) * 0.2) + (arr.last * 0.8)).round(4))
61
+ end
62
+ end
63
+
64
+ def calculate_mass_index
65
+ [*0..2].each.sum do |index|
66
+ (_high_minus_low_ema_array.at((period * 2) + index - 2) / high_minus_low_ema_ema_array.at(period + index - 1))
67
+ end.round(4)
68
+ end
69
+ end
70
+ end
@@ -0,0 +1,71 @@
1
+ module RubyTechnicalAnalysis
2
+ # Moving Averages
3
+ #
4
+ # Find more information at:
5
+ #
6
+ # Simple Moving Average (SMA): https://www.fidelity.com/learning-center/trading-investing/technical-analysis/technical-indicator-guide/sma
7
+ #
8
+ # Exponential Moving Average (EMA): https://www.fidelity.com/learning-center/trading-investing/technical-analysis/technical-indicator-guide/ema
9
+ #
10
+ # Weighted Moving Average (WMA): https://www.fidelity.com/learning-center/trading-investing/technical-analysis/technical-indicator-guide/wma
11
+ class MovingAverages < Indicator
12
+ attr_reader :period
13
+
14
+ # @param series [Array] An array of prices, typically closing prices
15
+ # @param period [Integer] The number of periods to use in the calculation
16
+ def initialize(series: [], period: 20)
17
+ @period = period
18
+
19
+ super(series: series)
20
+ end
21
+
22
+ # Simple Moving Average
23
+ # @return [Float] The current SMA value
24
+ def sma
25
+ series.last(period).sum.to_f / period
26
+ end
27
+
28
+ # Exponential Moving Average
29
+ # @return [Float] The current EMA value
30
+ def ema
31
+ return series.last if period == 1
32
+
33
+ series.last(period).each_with_object([]) do |num, result|
34
+ result << if result.empty?
35
+ num
36
+ else
37
+ (num * _ema_percentages.first) + (result.last * _ema_percentages.last)
38
+ end
39
+ end.last
40
+ end
41
+
42
+ # Weighted Moving Average
43
+ # @return [Float] The current WMA value
44
+ def wma
45
+ true_periods = (1..period).sum
46
+
47
+ sigma_periods = series.last(period).each_with_index.sum { |num, index| (index + 1) * num }
48
+
49
+ sigma_periods.to_f / true_periods
50
+ end
51
+
52
+ # @return [Boolean] Whether or not the object is valid
53
+ def valid?
54
+ period <= series.length
55
+ end
56
+
57
+ private
58
+
59
+ def _ema_percentages
60
+ @_ema_percentages ||=
61
+ case period
62
+ when 12 then [0.846154, 0.153846]
63
+ when 26 then [0.925926, 0.074074]
64
+ else
65
+ last_obs_pct = 2.0 / (period + 1)
66
+
67
+ [last_obs_pct, 1 - last_obs_pct]
68
+ end
69
+ end
70
+ end
71
+ end
@@ -0,0 +1,75 @@
1
+ module RubyTechnicalAnalysis
2
+ # Pivot Points
3
+ #
4
+ # Find more information at: https://www.fidelity.com/learning-center/trading-investing/technical-analysis/technical-indicator-guide/pivot-points
5
+ class PivotPoints < Indicator
6
+ # @param series [Array] An array of high, low, and closing prices for a single period
7
+ # def initialize(series: [])
8
+ # super(series: series)
9
+ # end
10
+
11
+ # @return [Array] An array containing the current S3, S2, S1, pivot, R1, R2, and R3 values
12
+ def call
13
+ calculate_pivot_points
14
+ end
15
+
16
+ # @return [Boolean] Whether or not the object is valid
17
+ def valid?
18
+ series.length == 3
19
+ end
20
+
21
+ private
22
+
23
+ def _high
24
+ @_high ||= series.first
25
+ end
26
+
27
+ def _low
28
+ @_low ||= series.at(1)
29
+ end
30
+
31
+ def _close
32
+ @_close ||= series.last
33
+ end
34
+
35
+ def _pivot
36
+ @_pivot ||= ((_high + _low + _close) / 3.0).round(2)
37
+ end
38
+
39
+ def support_one
40
+ ((2 * _pivot) - _high).round(2)
41
+ end
42
+
43
+ def support_two
44
+ (_pivot - (_high - _low)).round(2)
45
+ end
46
+
47
+ def support_three
48
+ (_low - (2 * (_high - _pivot))).round(2)
49
+ end
50
+
51
+ def resistance_one
52
+ ((2 * _pivot) - _low).round(2)
53
+ end
54
+
55
+ def resistance_two
56
+ (_pivot + (_high - _low)).round(2)
57
+ end
58
+
59
+ def resistance_three
60
+ (_high + (2 * (_pivot - _low))).round(2)
61
+ end
62
+
63
+ def calculate_pivot_points
64
+ [
65
+ support_three,
66
+ support_two,
67
+ support_one,
68
+ _pivot,
69
+ resistance_one,
70
+ resistance_two,
71
+ resistance_three
72
+ ]
73
+ end
74
+ end
75
+ end