ruby-technical-analysis 0.1.1 → 1.0.4
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.
- checksums.yaml +4 -4
- data/lib/ruby-technical-analysis.rb +1 -0
- data/lib/ruby_technical_analysis/indicator.rb +41 -0
- data/lib/ruby_technical_analysis/indicators/bollinger_bands.rb +50 -0
- data/lib/ruby_technical_analysis/indicators/chaikin_money_flow.rb +47 -0
- data/lib/ruby_technical_analysis/indicators/chande_momentum_oscillator.rb +56 -0
- data/lib/ruby_technical_analysis/indicators/commodity_channel_index.rb +70 -0
- data/lib/ruby_technical_analysis/indicators/envelopes_ema.rb +48 -0
- data/lib/ruby_technical_analysis/indicators/intraday_momentum_index.rb +45 -0
- data/lib/ruby_technical_analysis/indicators/macd.rb +86 -0
- data/lib/ruby_technical_analysis/indicators/mass_index.rb +70 -0
- data/lib/ruby_technical_analysis/indicators/moving_averages.rb +71 -0
- data/lib/ruby_technical_analysis/indicators/pivot_points.rb +75 -0
- data/lib/ruby_technical_analysis/indicators/price_channel.rb +48 -0
- data/lib/ruby_technical_analysis/indicators/qstick.rb +44 -0
- data/lib/ruby_technical_analysis/indicators/rate_of_change.rb +40 -0
- data/lib/ruby_technical_analysis/indicators/relative_momentum_index.rb +79 -0
- data/lib/ruby_technical_analysis/indicators/relative_strength_index.rb +74 -0
- data/lib/ruby_technical_analysis/indicators/statistical_methods.rb +40 -0
- data/lib/ruby_technical_analysis/indicators/stochastic_oscillator.rb +99 -0
- data/lib/ruby_technical_analysis/indicators/volume_oscillator.rb +50 -0
- data/lib/ruby_technical_analysis/indicators/volume_rate_of_change.rb +48 -0
- data/lib/ruby_technical_analysis/indicators/wilders_smoothing.rb +47 -0
- data/lib/ruby_technical_analysis/indicators/williams_percent_r.rb +69 -0
- data/lib/ruby_technical_analysis.rb +24 -7
- data/spec/ruby_technical_analysis/indicator_spec.rb +76 -0
- data/spec/ruby_technical_analysis/indicators/bollinger_bands_spec.rb +67 -0
- data/spec/ruby_technical_analysis/indicators/chaikin_money_flow_spec.rb +63 -0
- data/spec/ruby_technical_analysis/indicators/chande_momentum_oscillator_spec.rb +59 -0
- data/spec/ruby_technical_analysis/indicators/commodity_channel_index_spec.rb +66 -0
- data/spec/ruby_technical_analysis/indicators/envelopes_ema_spec.rb +69 -0
- data/spec/ruby_technical_analysis/indicators/intraday_momentum_spec.rb +63 -0
- data/spec/ruby_technical_analysis/indicators/macd_spec.rb +61 -0
- data/spec/ruby_technical_analysis/indicators/mass_index_spec.rb +67 -0
- data/spec/ruby_technical_analysis/indicators/moving_averages_spec.rb +81 -0
- data/spec/ruby_technical_analysis/indicators/pivot_points_spec.rb +43 -0
- data/spec/ruby_technical_analysis/indicators/price_channel_spec.rb +64 -0
- data/spec/ruby_technical_analysis/indicators/qstick_spec.rb +59 -0
- data/spec/ruby_technical_analysis/indicators/rate_of_change_spec.rb +59 -0
- data/spec/ruby_technical_analysis/indicators/relative_momentum_index_spec.rb +67 -0
- data/spec/ruby_technical_analysis/indicators/relative_strength_index_spec.rb +59 -0
- data/spec/ruby_technical_analysis/indicators/statistical_methods_spec.rb +91 -0
- data/spec/ruby_technical_analysis/indicators/stochastic_oscillator_spec.rb +106 -0
- data/spec/ruby_technical_analysis/indicators/volume_oscillator_spec.rb +98 -0
- data/spec/ruby_technical_analysis/indicators/volume_rate_of_change_spec.rb +67 -0
- data/spec/ruby_technical_analysis/indicators/wiilders_smoothing_spec.rb +67 -0
- data/spec/ruby_technical_analysis/indicators/williams_percent_r_spec.rb +71 -0
- data/spec/spec_helper.rb +1 -0
- metadata +100 -40
- data/.rubocop.yml +0 -34
- data/CHANGELOG.md +0 -5
- data/CODE_OF_CONDUCT.md +0 -84
- data/Gemfile +0 -12
- data/LICENSE.txt +0 -21
- data/README.md +0 -36
- data/Rakefile +0 -16
- data/lib/ruby-technical-analysis/indicators/bollinger_bands.rb +0 -25
- data/lib/ruby-technical-analysis/indicators/chaikin_money_flow.rb +0 -70
- data/lib/ruby-technical-analysis/indicators/chande_momentum_oscillator.rb +0 -34
- data/lib/ruby-technical-analysis/indicators/commodity_channel_index.rb +0 -64
- data/lib/ruby-technical-analysis/indicators/envelopes_ema.rb +0 -24
- data/lib/ruby-technical-analysis/indicators/intraday_momentum_index.rb +0 -48
- data/lib/ruby-technical-analysis/indicators/macd.rb +0 -47
- data/lib/ruby-technical-analysis/indicators/mass_index.rb +0 -73
- data/lib/ruby-technical-analysis/indicators/pivot_points.rb +0 -23
- data/lib/ruby-technical-analysis/indicators/price_channel.rb +0 -37
- data/lib/ruby-technical-analysis/indicators/qstick.rb +0 -40
- data/lib/ruby-technical-analysis/indicators/rate_of_change.rb +0 -18
- data/lib/ruby-technical-analysis/indicators/relative_momentum_index.rb +0 -66
- data/lib/ruby-technical-analysis/indicators/relative_strength_index.rb +0 -63
- data/lib/ruby-technical-analysis/indicators/stochastic_oscillator.rb +0 -65
- data/lib/ruby-technical-analysis/indicators/volume_oscillator.rb +0 -38
- data/lib/ruby-technical-analysis/indicators/volume_rate_of_change.rb +0 -26
- data/lib/ruby-technical-analysis/indicators/wilders_smoothing.rb +0 -27
- data/lib/ruby-technical-analysis/indicators/williams_percent_r.rb +0 -52
- data/lib/ruby-technical-analysis/moving_averages.rb +0 -85
- data/lib/ruby-technical-analysis/statistical_methods.rb +0 -24
- data/lib/ruby-technical-analysis/version.rb +0 -5
- data/sig/ruby-technical-analysis.rbs +0 -4
@@ -0,0 +1,48 @@
|
|
1
|
+
module RubyTechnicalAnalysis
|
2
|
+
# Price Channel
|
3
|
+
#
|
4
|
+
# Find more information at: https://www.investopedia.com/terms/p/price-channel.asp
|
5
|
+
class PriceChannel < Indicator
|
6
|
+
attr_reader :period
|
7
|
+
|
8
|
+
# @param series [Array] An array of arrays containing high, 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: 20)
|
11
|
+
@period = period
|
12
|
+
|
13
|
+
super(series: series)
|
14
|
+
end
|
15
|
+
|
16
|
+
# @return [Array] An array containing the current upper and lower price channel values
|
17
|
+
def call
|
18
|
+
calculate_price_channel
|
19
|
+
end
|
20
|
+
|
21
|
+
# @return [Boolean] Whether the object is valid
|
22
|
+
def valid?
|
23
|
+
series.length >= period
|
24
|
+
end
|
25
|
+
|
26
|
+
private
|
27
|
+
|
28
|
+
def _highs
|
29
|
+
@_highs ||= series.last(period + 1).map { |value| value.at(0) }
|
30
|
+
end
|
31
|
+
|
32
|
+
def _lows
|
33
|
+
@_lows ||= series.last(period + 1).map { |value| value.at(1) }
|
34
|
+
end
|
35
|
+
|
36
|
+
def upper_price_channel
|
37
|
+
_highs[0..period - 1].max
|
38
|
+
end
|
39
|
+
|
40
|
+
def lower_price_channel
|
41
|
+
_lows[0..period - 1].min
|
42
|
+
end
|
43
|
+
|
44
|
+
def calculate_price_channel
|
45
|
+
[upper_price_channel, lower_price_channel]
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
module RubyTechnicalAnalysis
|
2
|
+
# Qstick
|
3
|
+
#
|
4
|
+
# Find more information at: https://www.investopedia.com/terms/q/qstick.asp
|
5
|
+
class QStick < 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: 20)
|
11
|
+
@period = period
|
12
|
+
|
13
|
+
super(series: series)
|
14
|
+
end
|
15
|
+
|
16
|
+
# @return [Float] The current Qstick value
|
17
|
+
def call
|
18
|
+
calculate_qstick
|
19
|
+
end
|
20
|
+
|
21
|
+
# @return [Boolean] Whether or not the object is valid
|
22
|
+
def valid?
|
23
|
+
period <= series.length
|
24
|
+
end
|
25
|
+
|
26
|
+
private
|
27
|
+
|
28
|
+
def _opens
|
29
|
+
@_opens ||= series.last(period).map { |value| value.at(0) }
|
30
|
+
end
|
31
|
+
|
32
|
+
def _closes
|
33
|
+
@_closes ||= series.last(period).map { |value| value.at(1) }
|
34
|
+
end
|
35
|
+
|
36
|
+
def cmo_sum
|
37
|
+
_closes.zip(_opens).sum { |close, open| close - open }
|
38
|
+
end
|
39
|
+
|
40
|
+
def calculate_qstick
|
41
|
+
(cmo_sum.to_f / period).round(4)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
module RubyTechnicalAnalysis
|
2
|
+
# Rate Of Change
|
3
|
+
#
|
4
|
+
# Find more information at: https://www.fidelity.com/learning-center/trading-investing/technical-analysis/technical-indicator-guide/roc
|
5
|
+
class RateOfChange < 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: 30)
|
11
|
+
@period = period
|
12
|
+
|
13
|
+
super(series: series)
|
14
|
+
end
|
15
|
+
|
16
|
+
# @return [Float] The current ROC value
|
17
|
+
def call
|
18
|
+
calculate_roc
|
19
|
+
end
|
20
|
+
|
21
|
+
# @return [Boolean] Whether or not the object is valid
|
22
|
+
def valid?
|
23
|
+
period + 1 <= series.length
|
24
|
+
end
|
25
|
+
|
26
|
+
private
|
27
|
+
|
28
|
+
def calculate_roc
|
29
|
+
(((current_price - lookback_price).to_f / lookback_price) * 100).round(2)
|
30
|
+
end
|
31
|
+
|
32
|
+
def current_price
|
33
|
+
series.last
|
34
|
+
end
|
35
|
+
|
36
|
+
def lookback_price
|
37
|
+
series.last(period + 1).first
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,79 @@
|
|
1
|
+
module RubyTechnicalAnalysis
|
2
|
+
# Relative Momentum Index
|
3
|
+
class RelativeMomentumIndex < Indicator
|
4
|
+
attr_reader :period_mom, :period_rmi
|
5
|
+
|
6
|
+
# @param series [Array] An array of prices, typically closing prices
|
7
|
+
# @param period_mom [Integer] The number of periods to use in the momentum calculation
|
8
|
+
# @param period_rmi [Integer] The number of periods to use in the RMI calculation
|
9
|
+
def initialize(series: [], period_mom: 14, period_rmi: 20)
|
10
|
+
@period_mom = period_mom
|
11
|
+
@period_rmi = period_rmi
|
12
|
+
@rmi = []
|
13
|
+
@rmi_intermediate = []
|
14
|
+
@smooth_up = []
|
15
|
+
@smooth_down = []
|
16
|
+
@wilders_is_set = false
|
17
|
+
|
18
|
+
super(series: series)
|
19
|
+
end
|
20
|
+
|
21
|
+
# @return [Float] The current RMI value
|
22
|
+
def call
|
23
|
+
calculate_rmi
|
24
|
+
end
|
25
|
+
|
26
|
+
# @return [Boolean] Whether or not the object is valid
|
27
|
+
def valid?
|
28
|
+
period_mom + period_rmi <= series.length
|
29
|
+
end
|
30
|
+
|
31
|
+
private
|
32
|
+
|
33
|
+
def _pmpr
|
34
|
+
@_pmpr ||= (period_mom + period_rmi)
|
35
|
+
end
|
36
|
+
|
37
|
+
def _smooth_coef_one
|
38
|
+
@_smooth_coef_one ||= (1.0 / period_rmi).round(4)
|
39
|
+
end
|
40
|
+
|
41
|
+
def _smooth_coef_two
|
42
|
+
@_smooth_coef_two ||= (1 - _smooth_coef_one)
|
43
|
+
end
|
44
|
+
|
45
|
+
def calculate_channels(cla)
|
46
|
+
period_rmi.times.map do |index|
|
47
|
+
diff = (cla.at(index) - cla.at(period_mom + index)).round(4)
|
48
|
+
[diff.negative? ? diff.abs : 0, diff.positive? ? diff : 0]
|
49
|
+
end.transpose
|
50
|
+
end
|
51
|
+
|
52
|
+
def calculate_initial_smoothing(up_ch, down_ch)
|
53
|
+
@smooth_up << RubyTechnicalAnalysis::WildersSmoothing.call(series: up_ch, period: period_rmi)
|
54
|
+
@smooth_down << RubyTechnicalAnalysis::WildersSmoothing.call(series: down_ch, period: period_rmi)
|
55
|
+
@wilders_is_set = true
|
56
|
+
end
|
57
|
+
|
58
|
+
def calculate_subsequent_smoothing(up_ch, down_ch)
|
59
|
+
@smooth_up << (_smooth_coef_one * up_ch.last + _smooth_coef_two * @smooth_up.last).round(4)
|
60
|
+
@smooth_down << (_smooth_coef_one * down_ch.last + _smooth_coef_two * @smooth_down.last).round(4)
|
61
|
+
end
|
62
|
+
|
63
|
+
def calculate_smoothing(up_ch, down_ch)
|
64
|
+
@wilders_is_set ? calculate_subsequent_smoothing(up_ch, down_ch) : calculate_initial_smoothing(up_ch, down_ch)
|
65
|
+
end
|
66
|
+
|
67
|
+
def calculate_rmi
|
68
|
+
(0..(series.size - _pmpr)).flat_map do |index|
|
69
|
+
cla = series[index..(index + _pmpr - 1)]
|
70
|
+
up_ch, down_ch = calculate_channels(cla)
|
71
|
+
|
72
|
+
calculate_smoothing(up_ch, down_ch)
|
73
|
+
|
74
|
+
@rmi_intermediate << (@smooth_up.last.to_f / @smooth_down.last)
|
75
|
+
@rmi << ((@rmi_intermediate.last.to_f / (1 + @rmi_intermediate.last)) * 100).round(4)
|
76
|
+
end.last
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
@@ -0,0 +1,74 @@
|
|
1
|
+
module RubyTechnicalAnalysis
|
2
|
+
# Relative Strength Index
|
3
|
+
#
|
4
|
+
# Find more information at: https://www.fidelity.com/viewpoints/active-investor/how-to-use-RSI
|
5
|
+
class RelativeStrengthIndex < 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: 14)
|
11
|
+
@period = period
|
12
|
+
@rsi = []
|
13
|
+
@smooth_up = []
|
14
|
+
@smooth_down = []
|
15
|
+
@wilders_is_set = false
|
16
|
+
|
17
|
+
super(series: series)
|
18
|
+
end
|
19
|
+
|
20
|
+
# @return [Float] The current RSI value
|
21
|
+
def call
|
22
|
+
calculate_rsi
|
23
|
+
end
|
24
|
+
|
25
|
+
# @return [Boolean] Whether or not the object is valid
|
26
|
+
def valid?
|
27
|
+
period < series.length
|
28
|
+
end
|
29
|
+
|
30
|
+
private
|
31
|
+
|
32
|
+
def _smooth_coef_one
|
33
|
+
@_smooth_coef_one ||= (1.0 / period).round(4)
|
34
|
+
end
|
35
|
+
|
36
|
+
def _smooth_coef_two
|
37
|
+
@_smooth_coef_two ||= (1 - _smooth_coef_one)
|
38
|
+
end
|
39
|
+
|
40
|
+
def calculate_channels(cla)
|
41
|
+
period.times.map do |index|
|
42
|
+
diff = (cla.at(index) - cla.at(index + 1)).round(4)
|
43
|
+
|
44
|
+
[diff.negative? ? diff.abs : 0, diff.positive? ? diff : 0]
|
45
|
+
end.transpose
|
46
|
+
end
|
47
|
+
|
48
|
+
def calculate_initial_smoothing(up_ch, down_ch)
|
49
|
+
@smooth_up << RubyTechnicalAnalysis::WildersSmoothing.call(series: up_ch, period: period)
|
50
|
+
@smooth_down << RubyTechnicalAnalysis::WildersSmoothing.call(series: down_ch, period: period)
|
51
|
+
|
52
|
+
@wilders_is_set = true
|
53
|
+
end
|
54
|
+
|
55
|
+
def calculate_subsequent_smoothing(up_ch, down_ch)
|
56
|
+
@smooth_up << (_smooth_coef_one * up_ch.last + _smooth_coef_two * @smooth_up.last).round(4)
|
57
|
+
@smooth_down << (_smooth_coef_one * down_ch.last + _smooth_coef_two * @smooth_down.last).round(4)
|
58
|
+
end
|
59
|
+
|
60
|
+
def calculate_smoothing(up_ch, down_ch)
|
61
|
+
@wilders_is_set ? calculate_subsequent_smoothing(up_ch, down_ch) : calculate_initial_smoothing(up_ch, down_ch)
|
62
|
+
end
|
63
|
+
|
64
|
+
def calculate_rsi
|
65
|
+
(0..(series.size - period - 1)).flat_map do |index|
|
66
|
+
cla = series[index..index + period]
|
67
|
+
up_ch, down_ch = calculate_channels(cla)
|
68
|
+
|
69
|
+
calculate_smoothing(up_ch, down_ch)
|
70
|
+
@rsi << (100.00 - (100.00 / ((@smooth_up.last.to_f / @smooth_down.last) + 1))).round(4)
|
71
|
+
end.last
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
module RubyTechnicalAnalysis
|
2
|
+
# Statistical Methods
|
3
|
+
class StatisticalMethods < Indicator
|
4
|
+
# @param series [Array] An array of prices, typically closing prices
|
5
|
+
# def initialize(series: [])
|
6
|
+
# super(series: series)
|
7
|
+
# end
|
8
|
+
|
9
|
+
# Mean
|
10
|
+
# @return [Float] The mean of the price series
|
11
|
+
def mean
|
12
|
+
series.reduce(:+) / series.length.to_f
|
13
|
+
end
|
14
|
+
|
15
|
+
# Standard Deviation
|
16
|
+
# @return [Float] The standard deviation of the price series
|
17
|
+
def standard_deviation
|
18
|
+
return 0 if series.uniq.length == 1
|
19
|
+
|
20
|
+
Math.sqrt(variance)
|
21
|
+
end
|
22
|
+
|
23
|
+
# Variance
|
24
|
+
# @return [Float] The variance of the price series
|
25
|
+
def variance
|
26
|
+
squared_differences.reduce(:+) / series.length.to_f
|
27
|
+
end
|
28
|
+
|
29
|
+
# @return [Boolean] Whether or not the object is valid
|
30
|
+
def valid?
|
31
|
+
series.length > 0
|
32
|
+
end
|
33
|
+
|
34
|
+
private
|
35
|
+
|
36
|
+
def squared_differences
|
37
|
+
series.map { |value| (value - mean)**2 }
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,99 @@
|
|
1
|
+
module RubyTechnicalAnalysis
|
2
|
+
# Stochastic Oscillator
|
3
|
+
#
|
4
|
+
# Find more information at: https://www.investopedia.com/terms/s/stochasticoscillator.asp
|
5
|
+
class StochasticOscillator < Indicator
|
6
|
+
attr_reader :k_periods, :k_slow_periods, :d_periods
|
7
|
+
|
8
|
+
# @param series [Array] An array of arrays containing high, low, close prices, e.g. [[high, low, close], [high, low, close]]
|
9
|
+
# @param k_periods [Integer] The number of periods to use in the calculation
|
10
|
+
# @param k_slow_periods [Integer] The number of periods to use in the calculation
|
11
|
+
# @param d_periods [Integer] The number of periods to use in the calculation
|
12
|
+
def initialize(series: [], k_periods: 14, k_slow_periods: 3, d_periods: 3)
|
13
|
+
@k_periods = k_periods
|
14
|
+
@k_slow_periods = k_slow_periods
|
15
|
+
@d_periods = d_periods
|
16
|
+
@lowest_lows = []
|
17
|
+
@highest_highs = []
|
18
|
+
@close_minus_lowest_lows = []
|
19
|
+
@highest_highs_minus_lowest_lows = []
|
20
|
+
@ks_sums_close_minus_lowest_lows = []
|
21
|
+
@ks_sums_highest_highs_minus_lowest_lows = []
|
22
|
+
@ks_sums_quotients_times_one_hundred = []
|
23
|
+
@d_periods_sma = []
|
24
|
+
|
25
|
+
super(series: series)
|
26
|
+
end
|
27
|
+
|
28
|
+
# @return [Float] The current Stochastic Oscillator value
|
29
|
+
def call
|
30
|
+
calculate_stochastic_oscillator
|
31
|
+
end
|
32
|
+
|
33
|
+
# @return [Boolean] Whether or not the object is valid
|
34
|
+
def valid?
|
35
|
+
k_periods + d_periods <= series.length
|
36
|
+
end
|
37
|
+
|
38
|
+
private
|
39
|
+
|
40
|
+
def calculate_lowest_lows(lows, window_start)
|
41
|
+
@lowest_lows << lows[window_start..(window_start + k_periods - 1)].min
|
42
|
+
end
|
43
|
+
|
44
|
+
def calculate_highest_highs(highs, window_start)
|
45
|
+
@highest_highs << highs[window_start..(window_start + k_periods - 1)].max
|
46
|
+
end
|
47
|
+
|
48
|
+
def calculate_close_minus_lowest_lows(closes, window_start)
|
49
|
+
@close_minus_lowest_lows <<
|
50
|
+
(closes.at(window_start + k_periods - 1) - @lowest_lows.last).round(4)
|
51
|
+
end
|
52
|
+
|
53
|
+
def calculate_highest_highs_minus_lowest_lows
|
54
|
+
@highest_highs_minus_lowest_lows << (@highest_highs.last - @lowest_lows.last).round(4)
|
55
|
+
end
|
56
|
+
|
57
|
+
def calculate_ks_sums_close_minus_lowest_lows
|
58
|
+
@ks_sums_close_minus_lowest_lows <<
|
59
|
+
@close_minus_lowest_lows.last(k_slow_periods).sum.round(4)
|
60
|
+
end
|
61
|
+
|
62
|
+
def calculate_ks_sums_highest_highs_minus_lowest_lows
|
63
|
+
@ks_sums_highest_highs_minus_lowest_lows <<
|
64
|
+
@highest_highs_minus_lowest_lows.last(k_slow_periods).sum.round(4)
|
65
|
+
end
|
66
|
+
|
67
|
+
def calculate_ks_sums_quotients_times_one_hundred
|
68
|
+
@ks_sums_quotients_times_one_hundred <<
|
69
|
+
((@ks_sums_close_minus_lowest_lows.last.to_f / @ks_sums_highest_highs_minus_lowest_lows.last) * 100).round(4)
|
70
|
+
end
|
71
|
+
|
72
|
+
def caculate_d_periods_sma
|
73
|
+
@d_periods_sma << if @ks_sums_quotients_times_one_hundred.length >= d_periods
|
74
|
+
(@ks_sums_quotients_times_one_hundred.last(d_periods).sum.to_f / d_periods).round(4)
|
75
|
+
else
|
76
|
+
-1000
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
def calculate_stochastic_oscillator
|
81
|
+
highs, lows, closes = extract_highs_lows_closes
|
82
|
+
|
83
|
+
(0..(highs.length - k_periods)).flat_map do |index|
|
84
|
+
calculate_lowest_lows(lows, index)
|
85
|
+
calculate_highest_highs(highs, index)
|
86
|
+
calculate_close_minus_lowest_lows(closes, index)
|
87
|
+
calculate_highest_highs_minus_lowest_lows
|
88
|
+
|
89
|
+
if @close_minus_lowest_lows.length >= k_slow_periods
|
90
|
+
calculate_ks_sums_close_minus_lowest_lows
|
91
|
+
calculate_ks_sums_highest_highs_minus_lowest_lows
|
92
|
+
calculate_ks_sums_quotients_times_one_hundred
|
93
|
+
end
|
94
|
+
|
95
|
+
caculate_d_periods_sma
|
96
|
+
end.last
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
module RubyTechnicalAnalysis
|
2
|
+
# Volume Oscillator
|
3
|
+
#
|
4
|
+
# Find more information at: https://www.fidelity.com/learning-center/trading-investing/technical-analysis/technical-indicator-guide/volume-oscillator
|
5
|
+
class VolumeOscillator < Indicator
|
6
|
+
attr_reader :short_ma_period, :long_ma_period
|
7
|
+
|
8
|
+
# @param series [Array] An array of volume values
|
9
|
+
# @param short_ma_period [Integer] The number of periods to use in the calculation of the short moving average
|
10
|
+
# @param long_ma_period [Integer] The number of periods to use in the calculation of the long moving average
|
11
|
+
def initialize(series: [], short_ma_period: 20, long_ma_period: 60)
|
12
|
+
@short_ma_period = short_ma_period
|
13
|
+
@long_ma_period = long_ma_period
|
14
|
+
|
15
|
+
super(series: series)
|
16
|
+
end
|
17
|
+
|
18
|
+
# @return [Float] The current volume oscillator value
|
19
|
+
def call
|
20
|
+
calculate_volume_oscillator
|
21
|
+
end
|
22
|
+
|
23
|
+
# @return [Boolean] Whether or not the object is valid
|
24
|
+
def valid?
|
25
|
+
short_ma_period < long_ma_period && long_ma_period <= series.length
|
26
|
+
end
|
27
|
+
|
28
|
+
private
|
29
|
+
|
30
|
+
def short_ma_a
|
31
|
+
(0..(series.length - short_ma_period)).map do |index|
|
32
|
+
RubyTechnicalAnalysis::MovingAverages.new(series: series[index..(index + short_ma_period - 1)], period: short_ma_period).sma
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def _long_ma_a
|
37
|
+
@_long_ma_a ||= (0..(series.length - long_ma_period)).map do |index|
|
38
|
+
RubyTechnicalAnalysis::MovingAverages.new(series: series[index..(index + long_ma_period - 1)], period: long_ma_period).sma
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def short_minus_long_ma_a
|
43
|
+
(short_ma_a.last - _long_ma_a.last).round(2)
|
44
|
+
end
|
45
|
+
|
46
|
+
def calculate_volume_oscillator
|
47
|
+
((short_minus_long_ma_a.to_f / _long_ma_a.last) * 100).round(2)
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
module RubyTechnicalAnalysis
|
2
|
+
# Volume Rate of Change
|
3
|
+
#
|
4
|
+
# Find more information at: https://www.investopedia.com/articles/technical/02/091002.asp
|
5
|
+
class VolumeRateOfChange < Indicator
|
6
|
+
attr_reader :period
|
7
|
+
|
8
|
+
# @param series [Array] An array of volume values
|
9
|
+
# @param period [Integer] The number of periods to use in the calculation
|
10
|
+
def initialize(series: [], period: 25)
|
11
|
+
@period = period
|
12
|
+
|
13
|
+
super(series: series)
|
14
|
+
end
|
15
|
+
|
16
|
+
# @return [Float] The current volume rate of change value
|
17
|
+
def call
|
18
|
+
calculate_volume_rate_of_change
|
19
|
+
end
|
20
|
+
|
21
|
+
# @return [Boolean] Whether or not the object is valid
|
22
|
+
def valid?
|
23
|
+
period < series.length
|
24
|
+
end
|
25
|
+
|
26
|
+
private
|
27
|
+
|
28
|
+
def _calculable_series_length
|
29
|
+
@_calculable_series_length ||= series.length - period
|
30
|
+
end
|
31
|
+
|
32
|
+
def _vol_shifted
|
33
|
+
@_vol_shifted ||= _calculable_series_length.times.map do
|
34
|
+
series.at(_calculable_series_length - 1)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def delta_volume
|
39
|
+
_calculable_series_length.times.map do
|
40
|
+
series.last - _vol_shifted.last
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def calculate_volume_rate_of_change
|
45
|
+
((delta_volume.last.to_f / _vol_shifted.last) * 100).round(4)
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
module RubyTechnicalAnalysis
|
2
|
+
# Wilders Smoothing
|
3
|
+
class WildersSmoothing < Indicator
|
4
|
+
attr_reader :period
|
5
|
+
|
6
|
+
# @param series [Array] An array of prices
|
7
|
+
# @param period [Integer] The number of periods to use in the calculation
|
8
|
+
def initialize(series: [], period: 5)
|
9
|
+
@period = period
|
10
|
+
|
11
|
+
super(series: series)
|
12
|
+
end
|
13
|
+
|
14
|
+
# @return [Float] The current Wilders Smoothing value
|
15
|
+
def call
|
16
|
+
calculate_wilders_smoothing
|
17
|
+
end
|
18
|
+
|
19
|
+
# @return [Boolean] Whether or not the object is valid
|
20
|
+
def valid?
|
21
|
+
period < series.length
|
22
|
+
end
|
23
|
+
|
24
|
+
private
|
25
|
+
|
26
|
+
def _sma_first_period
|
27
|
+
@_sma_first_period ||=
|
28
|
+
Array(RubyTechnicalAnalysis::MovingAverages.new(series: series.first(period), period: period).sma)
|
29
|
+
end
|
30
|
+
|
31
|
+
def smoothing_length
|
32
|
+
(series.size - period - 1)
|
33
|
+
end
|
34
|
+
|
35
|
+
def calculate_wilders_smoothing
|
36
|
+
ws = _sma_first_period
|
37
|
+
|
38
|
+
(0..smoothing_length).each do |index|
|
39
|
+
current_smoothing = ws.at(index)
|
40
|
+
|
41
|
+
ws << ((series.at(index + period) - current_smoothing) * (1.0 / period)) + current_smoothing
|
42
|
+
end
|
43
|
+
|
44
|
+
ws.last
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
@@ -0,0 +1,69 @@
|
|
1
|
+
module RubyTechnicalAnalysis
|
2
|
+
# Williams %R
|
3
|
+
#
|
4
|
+
# Find more information at: https://www.fidelity.com/learning-center/trading-investing/technical-analysis/technical-indicator-guide/williams-r
|
5
|
+
class WilliamsPercentR < Indicator
|
6
|
+
attr_reader :period
|
7
|
+
|
8
|
+
# @param series [Array] An array of arrays containing high, low, close prices, 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: 14)
|
11
|
+
@period = period
|
12
|
+
@highest_highs = []
|
13
|
+
@lowest_lows = []
|
14
|
+
@highest_highs_minus_close = []
|
15
|
+
@highest_highs_minus_lowest_lows = []
|
16
|
+
@pct_r = []
|
17
|
+
|
18
|
+
super(series: series)
|
19
|
+
end
|
20
|
+
|
21
|
+
# @return [Float] The current Williams %R value
|
22
|
+
def call
|
23
|
+
calculate_williams_percent_r
|
24
|
+
end
|
25
|
+
|
26
|
+
# @return [Boolean] Whether or not the object is valid
|
27
|
+
def valid?
|
28
|
+
period < series.length
|
29
|
+
end
|
30
|
+
|
31
|
+
private
|
32
|
+
|
33
|
+
def calculate_lowest_lows(lows, window_start)
|
34
|
+
@lowest_lows << lows[window_start..(period - 1 + window_start)].min
|
35
|
+
end
|
36
|
+
|
37
|
+
def calculate_highest_highs(highs, window_start)
|
38
|
+
@highest_highs << highs[window_start..(period - 1 + window_start)].max
|
39
|
+
end
|
40
|
+
|
41
|
+
def calculate_highest_highs_minus_close(closes, window_start)
|
42
|
+
@highest_highs_minus_close <<
|
43
|
+
(@highest_highs.last - closes.at(period - 1 + window_start)).round(2)
|
44
|
+
end
|
45
|
+
|
46
|
+
def calculate_highest_highs_minus_lowest_lows
|
47
|
+
@highest_highs_minus_lowest_lows << (@highest_highs.last - @lowest_lows.last).round(4)
|
48
|
+
end
|
49
|
+
|
50
|
+
def calculate_pct_r
|
51
|
+
@pct_r <<
|
52
|
+
((@highest_highs_minus_close.last.to_f / @highest_highs_minus_lowest_lows.last) * -100).round(2)
|
53
|
+
end
|
54
|
+
|
55
|
+
def calculate_williams_percent_r
|
56
|
+
highs, lows, closes = extract_highs_lows_closes
|
57
|
+
|
58
|
+
(0..highs.length - period).each do |index|
|
59
|
+
calculate_highest_highs(highs, index)
|
60
|
+
calculate_lowest_lows(lows, index)
|
61
|
+
calculate_highest_highs_minus_close(closes, index)
|
62
|
+
calculate_highest_highs_minus_lowest_lows
|
63
|
+
calculate_pct_r
|
64
|
+
end
|
65
|
+
|
66
|
+
@pct_r.last
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
@@ -1,8 +1,25 @@
|
|
1
|
-
#
|
1
|
+
# Base class
|
2
|
+
require "ruby_technical_analysis/indicator"
|
2
3
|
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
4
|
+
# Indicators
|
5
|
+
require "ruby_technical_analysis/indicators/bollinger_bands"
|
6
|
+
require "ruby_technical_analysis/indicators/chaikin_money_flow"
|
7
|
+
require "ruby_technical_analysis/indicators/chande_momentum_oscillator"
|
8
|
+
require "ruby_technical_analysis/indicators/commodity_channel_index"
|
9
|
+
require "ruby_technical_analysis/indicators/envelopes_ema"
|
10
|
+
require "ruby_technical_analysis/indicators/intraday_momentum_index"
|
11
|
+
require "ruby_technical_analysis/indicators/macd"
|
12
|
+
require "ruby_technical_analysis/indicators/mass_index"
|
13
|
+
require "ruby_technical_analysis/indicators/moving_averages"
|
14
|
+
require "ruby_technical_analysis/indicators/pivot_points"
|
15
|
+
require "ruby_technical_analysis/indicators/price_channel"
|
16
|
+
require "ruby_technical_analysis/indicators/qstick"
|
17
|
+
require "ruby_technical_analysis/indicators/rate_of_change"
|
18
|
+
require "ruby_technical_analysis/indicators/relative_momentum_index"
|
19
|
+
require "ruby_technical_analysis/indicators/relative_strength_index"
|
20
|
+
require "ruby_technical_analysis/indicators/statistical_methods"
|
21
|
+
require "ruby_technical_analysis/indicators/stochastic_oscillator"
|
22
|
+
require "ruby_technical_analysis/indicators/volume_oscillator"
|
23
|
+
require "ruby_technical_analysis/indicators/volume_rate_of_change"
|
24
|
+
require "ruby_technical_analysis/indicators/wilders_smoothing"
|
25
|
+
require "ruby_technical_analysis/indicators/williams_percent_r"
|