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.
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 +48 -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 +64 -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
@@ -0,0 +1,43 @@
1
+ require "spec_helper"
2
+
3
+ module RubyTechnicalAnalysis
4
+ RSpec.describe PivotPoints do
5
+ let(:series) { [176.65, 152, 165.12] }
6
+
7
+ let(:pivot_points) { described_class.new(series: series) }
8
+
9
+ describe "#initialize" do
10
+ it "initializes the PivotPoints object" do
11
+ expect(pivot_points).to be_an_instance_of(described_class)
12
+ end
13
+ end
14
+
15
+ describe "class methods" do
16
+ describe "#call" do
17
+ let(:pivot_points) { described_class.call(series: series) }
18
+
19
+ it "returns an array containing the current support levels, pivot, and resistance levels" do
20
+ expect(pivot_points).to eq([127.88, 139.94, 152.53, 164.59, 177.18, 189.24, 201.83])
21
+ end
22
+ end
23
+ end
24
+
25
+ describe "instance methods" do
26
+ describe "#call" do
27
+ it "returns an array containing the current support levels, pivot, and resistance levels" do
28
+ expect(pivot_points.call).to eq([127.88, 139.94, 152.53, 164.59, 177.18, 189.24, 201.83])
29
+ end
30
+ end
31
+
32
+ describe "#valid?" do
33
+ it "returns true if the object is valid" do
34
+ expect(pivot_points.valid?).to eq(true)
35
+ end
36
+
37
+ it "returns false if the object is invalid" do
38
+ expect(described_class.new(series: []).valid?).to eq(false)
39
+ end
40
+ end
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,64 @@
1
+ require "spec_helper"
2
+
3
+ module RubyTechnicalAnalysis
4
+ RSpec.describe PriceChannel do
5
+ let(:series) {
6
+ [[2.8097, 2.8437], [2.9063, 2.8543], [2.875, 2.8333], [2.8543, 2.8127], [2.974, 2.8647],
7
+ [3.073, 2.9793]]
8
+ }
9
+
10
+ let(:period) { 5 }
11
+
12
+ let(:price_channel) { described_class.new(series: series, period: period) }
13
+
14
+ describe "#initialize" do
15
+ it "initializes the PriceChannel object" do
16
+ expect(price_channel).to be_an_instance_of(described_class)
17
+ end
18
+
19
+ it "initializes with default period of 20" do
20
+ expect(described_class.new(series: series).period).to eq(20)
21
+ end
22
+ end
23
+
24
+ describe "class methods" do
25
+ describe "#call" do
26
+ let(:price_channel) { described_class.call(series: series, period: period) }
27
+
28
+ it "returns an array containing the upper and lower levels" do
29
+ expect(price_channel).to eq([2.974, 2.8127])
30
+ end
31
+ end
32
+ end
33
+
34
+ describe "instance methods" do
35
+ describe "#call" do
36
+ it "returns an array containing the upper and lower levels" do
37
+ expect(price_channel.call).to eq([2.974, 2.8127])
38
+ end
39
+ end
40
+
41
+ describe "#valid?" do
42
+ it "returns true if the object is valid" do
43
+ expect(price_channel.valid?).to eq(true)
44
+ end
45
+
46
+ it "returns false if the object is invalid" do
47
+ expect(described_class.new(series: []).valid?).to eq(false)
48
+ end
49
+ end
50
+ end
51
+
52
+ describe "secondary series" do
53
+ series = [[2.8097, 2.8437], [2.9063, 2.8543], [2.875, 2.8333], [2.8543, 2.8127], [2.974, 2.8647],
54
+ [3.073, 2.9793], [3.1563, 3.0937]]
55
+ period = 5
56
+
57
+ expected_values = [3.073, 2.8127]
58
+
59
+ it "returns the expected values" do
60
+ expect(described_class.new(series: series, period: period).call).to eq(expected_values)
61
+ end
62
+ end
63
+ end
64
+ end
@@ -0,0 +1,59 @@
1
+ require "spec_helper"
2
+
3
+ module RubyTechnicalAnalysis
4
+ RSpec.describe QStick do
5
+ let(:series) { [[62.5625, 64.5625], [64.625, 64.125], [63.5625, 64.3125], [63.9375, 64.875]] }
6
+ let(:period) { 4 }
7
+
8
+ let(:q_stick) { described_class.new(series: series, period: period) }
9
+
10
+ describe "#initialize" do
11
+ it "initializes the QStick object" do
12
+ expect(q_stick).to be_an_instance_of(described_class)
13
+ end
14
+
15
+ it "initializes with default period of 20" do
16
+ expect(described_class.new(series: series).period).to eq(20)
17
+ end
18
+ end
19
+
20
+ describe "class methods" do
21
+ describe "#call" do
22
+ let(:q_stick) { described_class.call(series: series, period: period) }
23
+
24
+ it "returns the QStick value" do
25
+ expect(q_stick).to eq(0.7969)
26
+ end
27
+ end
28
+ end
29
+
30
+ describe "instance methods" do
31
+ describe "#call" do
32
+ it "returns the QStick value" do
33
+ expect(q_stick.call).to eq(0.7969)
34
+ end
35
+ end
36
+
37
+ describe "#valid?" do
38
+ it "returns true when the series is valid" do
39
+ expect(q_stick.valid?).to be(true)
40
+ end
41
+
42
+ it "returns false when the series is not valid" do
43
+ expect(described_class.new(series: [], period: period).valid?).to be(false)
44
+ end
45
+ end
46
+ end
47
+
48
+ describe "secondary series" do
49
+ series = [[62.5625, 64.5625], [64.625, 64.125], [63.5625, 64.3125], [63.9375, 64.875], [64.5, 65.1875]]
50
+ period = 4
51
+
52
+ expected_value = 0.4688
53
+
54
+ it "returns the expected value" do
55
+ expect(described_class.new(series: series, period: period).call).to eq(expected_value)
56
+ end
57
+ end
58
+ end
59
+ end
@@ -0,0 +1,59 @@
1
+ require "spec_helper"
2
+
3
+ module RubyTechnicalAnalysis
4
+ RSpec.describe RateOfChange do
5
+ let(:series) { [5.5625, 5.375, 5.375, 5.0625] }
6
+ let(:period) { 3 }
7
+
8
+ let(:roc) { described_class.new(series: series, period: period) }
9
+
10
+ describe "#initialize" do
11
+ it "initializes the RateOfChange object" do
12
+ expect(roc).to be_an_instance_of(described_class)
13
+ end
14
+
15
+ it "initializes with default period of 30" do
16
+ expect(described_class.new(series: series).period).to eq(30)
17
+ end
18
+ end
19
+
20
+ describe "class methods" do
21
+ describe "#call" do
22
+ let(:roc) { described_class.call(series: series, period: period) }
23
+
24
+ it "returns the RateOfChange value" do
25
+ expect(roc).to eq(-8.99)
26
+ end
27
+ end
28
+ end
29
+
30
+ describe "instance methods" do
31
+ describe "#call" do
32
+ it "returns the RateOfChange value" do
33
+ expect(roc.call).to eq(-8.99)
34
+ end
35
+ end
36
+
37
+ describe "#valid?" do
38
+ it "returns true when the series is valid" do
39
+ expect(roc.valid?).to be(true)
40
+ end
41
+
42
+ it "returns false when the series is not valid" do
43
+ expect(described_class.new(series: [1, 2, 3], period: period).valid?).to be(false)
44
+ end
45
+ end
46
+ end
47
+
48
+ describe "secondary series" do
49
+ series = [5.5625, 5.375, 5.375, 5.0625, 5.1094]
50
+ period = 3
51
+
52
+ expected_value = -4.94
53
+
54
+ it "returns the expected value" do
55
+ expect(described_class.new(series: series, period: period).call).to eq(expected_value)
56
+ end
57
+ end
58
+ end
59
+ end
@@ -0,0 +1,67 @@
1
+ require "spec_helper"
2
+
3
+ module RubyTechnicalAnalysis
4
+ RSpec.describe RelativeMomentumIndex do
5
+ let(:series) {
6
+ [6.875, 6.9375, 6.8125, 6.6095, 6.7345, 6.672, 6.625, 6.6875, 6.547, 6.6563, 6.672, 6.6563]
7
+ }
8
+ let(:period_mom) { 4 }
9
+ let(:period_rmi) { 8 }
10
+
11
+ let(:rmi) { described_class.new(series: series, period_mom: period_mom, period_rmi: period_rmi) }
12
+
13
+ describe "#initialize" do
14
+ it "initializes the RelativeMomentumIndex object" do
15
+ expect(rmi).to be_an_instance_of(described_class)
16
+ end
17
+
18
+ it "initializes with default period_mom of 14" do
19
+ expect(described_class.new(series: series).period_mom).to eq(14)
20
+ end
21
+
22
+ it "initializes with default period_rmi of 20" do
23
+ expect(described_class.new(series: series).period_rmi).to eq(20)
24
+ end
25
+ end
26
+
27
+ describe "class methods" do
28
+ describe "#call" do
29
+ let(:rmi) { described_class.call(series: series, period_mom: period_mom, period_rmi: period_rmi) }
30
+
31
+ it "returns the RelativeMomentumIndex value" do
32
+ expect(rmi).to eq(13.1179)
33
+ end
34
+ end
35
+ end
36
+
37
+ describe "instance methods" do
38
+ describe "#call" do
39
+ it "returns the RelativeMomentumIndex value" do
40
+ expect(rmi.call).to eq(13.1179)
41
+ end
42
+ end
43
+
44
+ describe "#valid?" do
45
+ it "returns true when the series is valid" do
46
+ expect(rmi.valid?).to be(true)
47
+ end
48
+
49
+ it "returns false when the series is not valid" do
50
+ expect(described_class.new(series: [*1..11], period_mom: period_mom, period_rmi: period_rmi).valid?).to be(false)
51
+ end
52
+ end
53
+ end
54
+
55
+ describe "secondary series" do
56
+ series = [6.875, 6.9375, 6.8125, 6.6095, 6.7345, 6.672, 6.625, 6.6875, 6.547, 6.6563, 6.672, 6.6563, 6.5938]
57
+ period_mom = 4
58
+ period_rmi = 8
59
+
60
+ expected_value = 17.7112
61
+
62
+ it "returns the expected value" do
63
+ expect(described_class.new(series: series, period_mom: period_mom, period_rmi: period_rmi).call).to eq(expected_value)
64
+ end
65
+ end
66
+ end
67
+ end
@@ -0,0 +1,59 @@
1
+ require "spec_helper"
2
+
3
+ module RubyTechnicalAnalysis
4
+ RSpec.describe RelativeStrengthIndex do
5
+ let(:series) { [37.875, 39.5, 38.75, 39.8125, 40, 39.875] }
6
+ let(:period) { 5 }
7
+
8
+ let(:rsi) { described_class.new(series: series, period: period) }
9
+
10
+ describe "#initialize" do
11
+ it "initializes the RelativeStrengthIndex object" do
12
+ expect(rsi).to be_an_instance_of(described_class)
13
+ end
14
+
15
+ it "initializes with default period of 14" do
16
+ expect(described_class.new(series: series).period).to eq(14)
17
+ end
18
+ end
19
+
20
+ describe "class methods" do
21
+ describe "#call" do
22
+ let(:rsi) { described_class.call(series: series, period: period) }
23
+
24
+ it "returns the RelativeStrengthIndex value" do
25
+ expect(rsi).to eq(76.6667)
26
+ end
27
+ end
28
+ end
29
+
30
+ describe "instance methods" do
31
+ describe "#call" do
32
+ it "returns the RelativeStrengthIndex value" do
33
+ expect(rsi.call).to eq(76.6667)
34
+ end
35
+ end
36
+
37
+ describe "#valid?" do
38
+ it "returns true when the series is valid" do
39
+ expect(rsi.valid?).to be(true)
40
+ end
41
+
42
+ it "returns false when the series is not valid" do
43
+ expect(described_class.new(series: [*1..5], period: period).valid?).to be(false)
44
+ end
45
+ end
46
+ end
47
+
48
+ describe "secondary series" do
49
+ series = [37.875, 39.5, 38.75, 39.8125, 40, 39.875, 40.1875]
50
+ period = 5
51
+
52
+ expected_value = 78.8679
53
+
54
+ it "returns the expected value" do
55
+ expect(described_class.new(series: series, period: period).call).to eq(expected_value)
56
+ end
57
+ end
58
+ end
59
+ end
@@ -0,0 +1,91 @@
1
+ require "spec_helper"
2
+
3
+ module RubyTechnicalAnalysis
4
+ RSpec.describe StatisticalMethods do
5
+ let(:series) { [0, 1, 2, 3] }
6
+
7
+ let(:stats) { described_class.new(series: series) }
8
+
9
+ describe "#initialize" do
10
+ it "initializes the StatisticalMethods object" do
11
+ expect(stats).to be_an_instance_of(described_class)
12
+ end
13
+ end
14
+
15
+ describe "#mean" do
16
+ it "returns the mean value" do
17
+ expect(stats.mean).to eq(1.5)
18
+ end
19
+
20
+ context "secondary series" do
21
+ series = [-1, 1, 2, -2]
22
+
23
+ it "returns the expected value" do
24
+ expect(described_class.new(series: series).mean).to eq(0)
25
+ end
26
+ end
27
+
28
+ context "when the series is all zeros" do
29
+ series = [0, 0, 0, 0]
30
+
31
+ it "returns 0" do
32
+ expect(described_class.new(series: series).mean).to eq(0)
33
+ end
34
+ end
35
+ end
36
+
37
+ describe "#variance" do
38
+ it "returns the variance value" do
39
+ expect(stats.variance).to eq(1.25)
40
+ end
41
+
42
+ context "secondary series" do
43
+ series = [-1, 1, 2, -2]
44
+
45
+ it "returns the expected value" do
46
+ expect(described_class.new(series: series).variance).to eq(2.5)
47
+ end
48
+ end
49
+
50
+ context "when the series is all zeros" do
51
+ series = [0, 0, 0, 0]
52
+
53
+ it "returns 0" do
54
+ expect(described_class.new(series: series).variance).to eq(0)
55
+ end
56
+ end
57
+ end
58
+
59
+ describe "#standard_deviation" do
60
+ it "returns the standard_deviation value" do
61
+ expect(stats.standard_deviation.truncate(5)).to eq(1.11803)
62
+ end
63
+
64
+ context "secondary series" do
65
+ series = [-1, 1, 2, -2]
66
+
67
+ it "returns the expected value" do
68
+ expect(described_class.new(series: series).standard_deviation.truncate(5)).to eq(1.58113)
69
+ end
70
+ end
71
+
72
+ context "when the series is all zeros" do
73
+ series = [0, 0, 0, 0]
74
+
75
+ it "returns 0" do
76
+ expect(described_class.new(series: series).standard_deviation).to eq(0)
77
+ end
78
+ end
79
+ end
80
+
81
+ describe "#valid?" do
82
+ it "returns true when the series is valid" do
83
+ expect(stats.valid?).to be(true)
84
+ end
85
+
86
+ it "returns false when the series is not valid" do
87
+ expect(described_class.new(series: []).valid?).to be(false)
88
+ end
89
+ end
90
+ end
91
+ end
@@ -0,0 +1,106 @@
1
+ require "spec_helper"
2
+
3
+ module RubyTechnicalAnalysis
4
+ RSpec.describe StochasticOscillator do
5
+ let(:series) {
6
+ [[34.375, 33.5312, 34.3125], [34.75, 33.9062, 34.125], [34.2188, 33.6875, 33.75],
7
+ [33.8281, 33.25, 33.6406], [33.4375, 33, 33.0156], [33.4688, 32.9375, 33.0469],
8
+ [34.375, 33.25, 34.2969], [34.7188, 34.0469, 34.1406], [34.625, 33.9375, 34.5469]]
9
+ }
10
+ let(:k_periods) { 5 }
11
+ let(:k_slow_periods) { 3 }
12
+ let(:d_periods) { 3 }
13
+
14
+ let(:oscillator) {
15
+ described_class.new(
16
+ series: series,
17
+ k_periods: k_periods,
18
+ k_slow_periods: k_slow_periods,
19
+ d_periods: d_periods
20
+ )
21
+ }
22
+
23
+ describe "#initialize" do
24
+ it "initializes the StochasticOscillator object" do
25
+ expect(oscillator).to be_an_instance_of(described_class)
26
+ end
27
+
28
+ it "initializes with default k_periods of 14" do
29
+ expect(described_class.new(series: series).k_periods).to eq(14)
30
+ end
31
+
32
+ it "initializes with default k_slow_periods of 3" do
33
+ expect(described_class.new(series: series).k_slow_periods).to eq(3)
34
+ end
35
+
36
+ it "initializes with default d_periods of 3" do
37
+ expect(described_class.new(series: series).d_periods).to eq(3)
38
+ end
39
+ end
40
+
41
+ describe "class methods" do
42
+ describe "#call" do
43
+ let(:oscillator) {
44
+ described_class.call(
45
+ series: series,
46
+ k_periods: k_periods,
47
+ k_slow_periods: k_slow_periods,
48
+ d_periods: d_periods
49
+ )
50
+ }
51
+
52
+ it "returns the StochasticOscillator value" do
53
+ expect(oscillator).to eq(55.41)
54
+ end
55
+ end
56
+ end
57
+
58
+ describe "instance methods" do
59
+ describe "#call" do
60
+ it "returns the StochasticOscillator value" do
61
+ expect(oscillator.call).to eq(55.41)
62
+ end
63
+ end
64
+
65
+ describe "#valid?" do
66
+ it "returns true when the series is valid" do
67
+ expect(oscillator.valid?).to be(true)
68
+ end
69
+
70
+ it "returns false when the series is not valid" do
71
+ expect(
72
+ described_class.new(
73
+ series: [*1..(k_periods + d_periods - 1)],
74
+ k_periods: k_periods,
75
+ k_slow_periods: k_slow_periods,
76
+ d_periods: d_periods
77
+ ).valid?
78
+ ).to be(false)
79
+ end
80
+ end
81
+ end
82
+
83
+ describe "secondary series" do
84
+ series = [[34.375, 33.5312, 34.3125], [34.75, 33.9062, 34.125], [34.2188, 33.6875, 33.75],
85
+ [33.8281, 33.25, 33.6406], [33.4375, 33, 33.0156], [33.4688, 32.9375, 33.0469],
86
+ [34.375, 33.25, 34.2969], [34.7188, 34.0469, 34.1406], [34.625, 33.9375, 34.5469],
87
+ [34.9219, 34.0625, 34.3281]]
88
+ k_periods = 5
89
+ k_slow_periods = 3
90
+ d_periods = 3
91
+
92
+ expected_value = 70.7715
93
+
94
+ it "returns the expected value" do
95
+ expect(
96
+ described_class.new(
97
+ series: series,
98
+ k_periods: k_periods,
99
+ k_slow_periods: k_slow_periods,
100
+ d_periods: d_periods
101
+ ).call
102
+ ).to eq(expected_value)
103
+ end
104
+ end
105
+ end
106
+ end
@@ -0,0 +1,98 @@
1
+ require "spec_helper"
2
+
3
+ module RubyTechnicalAnalysis
4
+ RSpec.describe VolumeOscillator do
5
+ let(:series) { [17_604, 18_918, 21_030, 13_854, 10_866] }
6
+ let(:short_ma_period) { 2 }
7
+ let(:long_ma_period) { 5 }
8
+
9
+ let(:oscillator) {
10
+ described_class.new(
11
+ series: series,
12
+ short_ma_period: short_ma_period,
13
+ long_ma_period: long_ma_period
14
+ )
15
+ }
16
+
17
+ describe "#initialize" do
18
+ it "initializes the VolumeOscillator object" do
19
+ expect(oscillator).to be_an_instance_of(described_class)
20
+ end
21
+
22
+ it "initializes with default short_ma_period of 20" do
23
+ expect(described_class.new(series: series).short_ma_period).to eq(20)
24
+ end
25
+
26
+ it "initializes with default long_ma_period of 60" do
27
+ expect(described_class.new(series: series).long_ma_period).to eq(60)
28
+ end
29
+ end
30
+
31
+ describe "class methods" do
32
+ describe "#call" do
33
+ let(:oscillator) {
34
+ described_class.call(
35
+ series: series,
36
+ short_ma_period: short_ma_period,
37
+ long_ma_period: long_ma_period
38
+ )
39
+ }
40
+
41
+ it "returns the VolumeOscillator value" do
42
+ expect(oscillator).to eq(-24.88)
43
+ end
44
+ end
45
+ end
46
+
47
+ describe "instance methods" do
48
+ describe "#call" do
49
+ it "returns the VolumeOscillator value" do
50
+ expect(oscillator.call).to eq(-24.88)
51
+ end
52
+ end
53
+
54
+ describe "#valid?" do
55
+ context "when the short_ma_period is greater than the long_ma_period" do
56
+ let(:short_ma_period) { 60 }
57
+ let(:long_ma_period) { 20 }
58
+
59
+ it "returns false" do
60
+ expect(oscillator.valid?).to eq(false)
61
+ end
62
+ end
63
+
64
+ context "when the long_ma_period is greater than the series length" do
65
+ let(:long_ma_period) { 100 }
66
+
67
+ it "returns false" do
68
+ expect(oscillator.valid?).to eq(false)
69
+ end
70
+ end
71
+
72
+ context "when the short_ma_period is less than the long_ma_period and the long_ma_period is less than the series length" do
73
+ it "returns true" do
74
+ expect(oscillator.valid?).to eq(true)
75
+ end
76
+ end
77
+ end
78
+ end
79
+
80
+ describe "secondary series" do
81
+ series = [17_604, 18_918, 21_030, 13_854, 10_866, 14_580]
82
+ short_ma_period = 2
83
+ long_ma_period = 5
84
+
85
+ expected_value = -19.73
86
+
87
+ it "returns the expected value" do
88
+ expect(
89
+ described_class.new(
90
+ series: series,
91
+ short_ma_period: short_ma_period,
92
+ long_ma_period: long_ma_period
93
+ ).call
94
+ ).to eq(expected_value)
95
+ end
96
+ end
97
+ end
98
+ end