technical-analysis 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (79) hide show
  1. checksums.yaml +7 -0
  2. data/lib/technical-analysis.rb +3 -0
  3. data/lib/technical_analysis.rb +41 -0
  4. data/lib/technical_analysis/helpers/array_helper.rb +27 -0
  5. data/lib/technical_analysis/helpers/stock_calculation.rb +25 -0
  6. data/lib/technical_analysis/helpers/validation.rb +33 -0
  7. data/lib/technical_analysis/indicators/adi.rb +101 -0
  8. data/lib/technical_analysis/indicators/adtv.rb +98 -0
  9. data/lib/technical_analysis/indicators/adx.rb +168 -0
  10. data/lib/technical_analysis/indicators/ao.rb +105 -0
  11. data/lib/technical_analysis/indicators/atr.rb +109 -0
  12. data/lib/technical_analysis/indicators/bb.rb +126 -0
  13. data/lib/technical_analysis/indicators/cci.rb +105 -0
  14. data/lib/technical_analysis/indicators/cmf.rb +105 -0
  15. data/lib/technical_analysis/indicators/cr.rb +95 -0
  16. data/lib/technical_analysis/indicators/dc.rb +108 -0
  17. data/lib/technical_analysis/indicators/dlr.rb +97 -0
  18. data/lib/technical_analysis/indicators/dpo.rb +106 -0
  19. data/lib/technical_analysis/indicators/dr.rb +96 -0
  20. data/lib/technical_analysis/indicators/eom.rb +104 -0
  21. data/lib/technical_analysis/indicators/fi.rb +95 -0
  22. data/lib/technical_analysis/indicators/ichimoku.rb +179 -0
  23. data/lib/technical_analysis/indicators/indicator.rb +138 -0
  24. data/lib/technical_analysis/indicators/kc.rb +124 -0
  25. data/lib/technical_analysis/indicators/kst.rb +132 -0
  26. data/lib/technical_analysis/indicators/macd.rb +144 -0
  27. data/lib/technical_analysis/indicators/mfi.rb +119 -0
  28. data/lib/technical_analysis/indicators/mi.rb +121 -0
  29. data/lib/technical_analysis/indicators/nvi.rb +102 -0
  30. data/lib/technical_analysis/indicators/obv.rb +104 -0
  31. data/lib/technical_analysis/indicators/obv_mean.rb +110 -0
  32. data/lib/technical_analysis/indicators/rsi.rb +133 -0
  33. data/lib/technical_analysis/indicators/sma.rb +98 -0
  34. data/lib/technical_analysis/indicators/sr.rb +122 -0
  35. data/lib/technical_analysis/indicators/trix.rb +127 -0
  36. data/lib/technical_analysis/indicators/tsi.rb +139 -0
  37. data/lib/technical_analysis/indicators/uo.rb +130 -0
  38. data/lib/technical_analysis/indicators/vi.rb +117 -0
  39. data/lib/technical_analysis/indicators/vpt.rb +95 -0
  40. data/lib/technical_analysis/indicators/wr.rb +103 -0
  41. data/spec/helpers/array_helper_spec.rb +31 -0
  42. data/spec/helpers/validaton_spec.rb +22 -0
  43. data/spec/spec_helper.rb +26 -0
  44. data/spec/ta_test_data.csv +64 -0
  45. data/spec/technical_analysis/indicators/adi_spec.rb +116 -0
  46. data/spec/technical_analysis/indicators/adtv_spec.rb +98 -0
  47. data/spec/technical_analysis/indicators/adx_spec.rb +92 -0
  48. data/spec/technical_analysis/indicators/ao_spec.rb +86 -0
  49. data/spec/technical_analysis/indicators/atr_spec.rb +105 -0
  50. data/spec/technical_analysis/indicators/bb_spec.rb +100 -0
  51. data/spec/technical_analysis/indicators/cci_spec.rb +100 -0
  52. data/spec/technical_analysis/indicators/cmf_spec.rb +100 -0
  53. data/spec/technical_analysis/indicators/cr_spec.rb +119 -0
  54. data/spec/technical_analysis/indicators/dc_spec.rb +100 -0
  55. data/spec/technical_analysis/indicators/dlr_spec.rb +119 -0
  56. data/spec/technical_analysis/indicators/dpo_spec.rb +90 -0
  57. data/spec/technical_analysis/indicators/dr_spec.rb +119 -0
  58. data/spec/technical_analysis/indicators/eom_spec.rb +105 -0
  59. data/spec/technical_analysis/indicators/fi_spec.rb +118 -0
  60. data/spec/technical_analysis/indicators/ichimoku_spec.rb +95 -0
  61. data/spec/technical_analysis/indicators/indicator_spec.rb +120 -0
  62. data/spec/technical_analysis/indicators/kc_spec.rb +110 -0
  63. data/spec/technical_analysis/indicators/kst_spec.rb +78 -0
  64. data/spec/technical_analysis/indicators/macd_spec.rb +86 -0
  65. data/spec/technical_analysis/indicators/mfi_spec.rb +105 -0
  66. data/spec/technical_analysis/indicators/mi_spec.rb +79 -0
  67. data/spec/technical_analysis/indicators/nvi_spec.rb +119 -0
  68. data/spec/technical_analysis/indicators/obv_mean_spec.rb +109 -0
  69. data/spec/technical_analysis/indicators/obv_spec.rb +119 -0
  70. data/spec/technical_analysis/indicators/rsi_spec.rb +105 -0
  71. data/spec/technical_analysis/indicators/sma_spec.rb +115 -0
  72. data/spec/technical_analysis/indicators/sr_spec.rb +104 -0
  73. data/spec/technical_analysis/indicators/trix_spec.rb +76 -0
  74. data/spec/technical_analysis/indicators/tsi_spec.rb +87 -0
  75. data/spec/technical_analysis/indicators/uo_spec.rb +91 -0
  76. data/spec/technical_analysis/indicators/vi_spec.rb +105 -0
  77. data/spec/technical_analysis/indicators/vpt_spec.rb +118 -0
  78. data/spec/technical_analysis/indicators/wr_spec.rb +106 -0
  79. metadata +177 -0
@@ -0,0 +1,90 @@
1
+ require 'technical-analysis'
2
+ require 'spec_helper'
3
+
4
+ describe 'Indicators' do
5
+ describe "DPO" do
6
+ input_data = SpecHelper.get_test_data(:close)
7
+ indicator = TechnicalAnalysis::Dpo
8
+
9
+ describe 'Detrended Price Oscillator' do
10
+ it 'Calculates DPO (20 day)' do
11
+ output = indicator.calculate(input_data, period: 20, price_key: :close)
12
+ normalized_output = output.map(&:to_hash)
13
+
14
+ expected_output = [
15
+ {:date_time=>"2019-01-09T00:00:00.000Z", :dpo=>-15.774999999999977},
16
+ {:date_time=>"2019-01-08T00:00:00.000Z", :dpo=>-19.607999999999976},
17
+ {:date_time=>"2019-01-07T00:00:00.000Z", :dpo=>-23.730500000000006},
18
+ {:date_time=>"2019-01-04T00:00:00.000Z", :dpo=>-24.407999999999987},
19
+ {:date_time=>"2019-01-03T00:00:00.000Z", :dpo=>-31.726499999999987},
20
+ {:date_time=>"2019-01-02T00:00:00.000Z", :dpo=>-17.36949999999996},
21
+ {:date_time=>"2018-12-31T00:00:00.000Z", :dpo=>-18.922999999999973},
22
+ {:date_time=>"2018-12-28T00:00:00.000Z", :dpo=>-21.498999999999995},
23
+ {:date_time=>"2018-12-27T00:00:00.000Z", :dpo=>-22.642999999999972},
24
+ {:date_time=>"2018-12-26T00:00:00.000Z", :dpo=>-22.876499999999993},
25
+ {:date_time=>"2018-12-24T00:00:00.000Z", :dpo=>-35.0085},
26
+ {:date_time=>"2018-12-21T00:00:00.000Z", :dpo=>-33.053},
27
+ {:date_time=>"2018-12-20T00:00:00.000Z", :dpo=>-29.025999999999982},
28
+ {:date_time=>"2018-12-19T00:00:00.000Z", :dpo=>-26.41850000000005},
29
+ {:date_time=>"2018-12-18T00:00:00.000Z", :dpo=>-22.48350000000005},
30
+ {:date_time=>"2018-12-17T00:00:00.000Z", :dpo=>-25.746500000000054},
31
+ {:date_time=>"2018-12-14T00:00:00.000Z", :dpo=>-26.388500000000022},
32
+ {:date_time=>"2018-12-13T00:00:00.000Z", :dpo=>-22.884000000000043},
33
+ {:date_time=>"2018-12-12T00:00:00.000Z", :dpo=>-26.35200000000006},
34
+ {:date_time=>"2018-12-11T00:00:00.000Z", :dpo=>-28.722000000000037},
35
+ {:date_time=>"2018-12-10T00:00:00.000Z", :dpo=>-29.836000000000013},
36
+ {:date_time=>"2018-12-07T00:00:00.000Z", :dpo=>-33.321500000000015},
37
+ {:date_time=>"2018-12-06T00:00:00.000Z", :dpo=>-29.007000000000033},
38
+ {:date_time=>"2018-12-04T00:00:00.000Z", :dpo=>-29.3245},
39
+ {:date_time=>"2018-12-03T00:00:00.000Z", :dpo=>-22.93399999999997},
40
+ {:date_time=>"2018-11-30T00:00:00.000Z", :dpo=>-30.462999999999965},
41
+ {:date_time=>"2018-11-29T00:00:00.000Z", :dpo=>-30.723499999999945},
42
+ {:date_time=>"2018-11-28T00:00:00.000Z", :dpo=>-31.052999999999997},
43
+ {:date_time=>"2018-11-27T00:00:00.000Z", :dpo=>-39.24899999999997},
44
+ {:date_time=>"2018-11-26T00:00:00.000Z", :dpo=>-40.02850000000001},
45
+ {:date_time=>"2018-11-23T00:00:00.000Z", :dpo=>-43.2405},
46
+ {:date_time=>"2018-11-21T00:00:00.000Z", :dpo=>-39.04850000000002},
47
+ {:date_time=>"2018-11-20T00:00:00.000Z", :dpo=>-39.16900000000004},
48
+ {:date_time=>"2018-11-19T00:00:00.000Z", :dpo=>-31.444000000000017}
49
+ ]
50
+
51
+ expect(normalized_output).to eq(expected_output)
52
+ end
53
+
54
+ it "Throws exception if not enough data" do
55
+ expect {indicator.calculate(input_data, period: input_data.size+1, price_key: :close)}.to raise_exception(TechnicalAnalysis::Validation::ValidationError)
56
+ end
57
+
58
+ it 'Returns the symbol' do
59
+ indicator_symbol = indicator.indicator_symbol
60
+ expect(indicator_symbol).to eq('dpo')
61
+ end
62
+
63
+ it 'Returns the name' do
64
+ indicator_name = indicator.indicator_name
65
+ expect(indicator_name).to eq('Detrended Price Oscillator')
66
+ end
67
+
68
+ it 'Returns the valid options' do
69
+ valid_options = indicator.valid_options
70
+ expect(valid_options).to eq(%i(period price_key))
71
+ end
72
+
73
+ it 'Validates options' do
74
+ valid_options = { period: 22, price_key: :close }
75
+ options_validated = indicator.validate_options(valid_options)
76
+ expect(options_validated).to eq(true)
77
+ end
78
+
79
+ it 'Throws exception for invalid options' do
80
+ invalid_options = { test: 10 }
81
+ expect { indicator.validate_options(invalid_options) }.to raise_exception(TechnicalAnalysis::Validation::ValidationError)
82
+ end
83
+
84
+ it 'Calculates minimum data size' do
85
+ options = { period: 4 }
86
+ expect(indicator.min_data_size(options)).to eq(6)
87
+ end
88
+ end
89
+ end
90
+ end
@@ -0,0 +1,119 @@
1
+ require 'technical-analysis'
2
+ require 'spec_helper'
3
+
4
+ describe 'Indicators' do
5
+ describe "DR" do
6
+ input_data = SpecHelper.get_test_data(:close)
7
+ indicator = TechnicalAnalysis::Dr
8
+
9
+ describe 'Daily Return' do
10
+ it 'Calculates Daily Return' do
11
+ output = indicator.calculate(input_data, price_key: :close)
12
+ normalized_output = output.map(&:to_hash)
13
+
14
+ expected_output = [
15
+ {:date_time=>"2019-01-09T00:00:00.000Z", :dr=>0.01698175787728018},
16
+ {:date_time=>"2019-01-08T00:00:00.000Z", :dr=>0.019063070371121427},
17
+ {:date_time=>"2019-01-07T00:00:00.000Z", :dr=>-0.0022258195062726527},
18
+ {:date_time=>"2019-01-04T00:00:00.000Z", :dr=>0.042689359307968244},
19
+ {:date_time=>"2019-01-03T00:00:00.000Z", :dr=>-0.09960739614994929},
20
+ {:date_time=>"2019-01-02T00:00:00.000Z", :dr=>0.0011411182959297772},
21
+ {:date_time=>"2018-12-31T00:00:00.000Z", :dr=>0.009665237150355388},
22
+ {:date_time=>"2018-12-28T00:00:00.000Z", :dr=>0.00051232788984934},
23
+ {:date_time=>"2018-12-27T00:00:00.000Z", :dr=>-0.006489788127505114},
24
+ {:date_time=>"2018-12-26T00:00:00.000Z", :dr=>0.07042157597221266},
25
+ {:date_time=>"2018-12-24T00:00:00.000Z", :dr=>-0.02587407947986453},
26
+ {:date_time=>"2018-12-21T00:00:00.000Z", :dr=>-0.038895619460562525},
27
+ {:date_time=>"2018-12-20T00:00:00.000Z", :dr=>-0.025234632357511222},
28
+ {:date_time=>"2018-12-19T00:00:00.000Z", :dr=>-0.031191666164870235},
29
+ {:date_time=>"2018-12-18T00:00:00.000Z", :dr=>0.01299255825301926},
30
+ {:date_time=>"2018-12-17T00:00:00.000Z", :dr=>-0.009306260575296044},
31
+ {:date_time=>"2018-12-14T00:00:00.000Z", :dr=>-0.031997660134542305},
32
+ {:date_time=>"2018-12-13T00:00:00.000Z", :dr=>0.010940272028385545},
33
+ {:date_time=>"2018-12-12T00:00:00.000Z", :dr=>0.0027871671707289103},
34
+ {:date_time=>"2018-12-11T00:00:00.000Z", :dr=>-0.005719339622641484},
35
+ {:date_time=>"2018-12-10T00:00:00.000Z", :dr=>0.006587928066947413},
36
+ {:date_time=>"2018-12-07T00:00:00.000Z", :dr=>-0.03565705128205121},
37
+ {:date_time=>"2018-12-06T00:00:00.000Z", :dr=>-0.011149470824608043},
38
+ {:date_time=>"2018-12-04T00:00:00.000Z", :dr=>-0.043988745806730845},
39
+ {:date_time=>"2018-12-03T00:00:00.000Z", :dr=>0.03494232276850706},
40
+ {:date_time=>"2018-11-30T00:00:00.000Z", :dr=>-0.005402394876079075},
41
+ {:date_time=>"2018-11-29T00:00:00.000Z", :dr=>-0.0076821045650491415},
42
+ {:date_time=>"2018-11-28T00:00:00.000Z", :dr=>0.038452708907254385},
43
+ {:date_time=>"2018-11-27T00:00:00.000Z", :dr=>-0.0021761539342571856},
44
+ {:date_time=>"2018-11-26T00:00:00.000Z", :dr=>0.013523710023797264},
45
+ {:date_time=>"2018-11-23T00:00:00.000Z", :dr=>-0.025398800769317886},
46
+ {:date_time=>"2018-11-21T00:00:00.000Z", :dr=>-0.0011300711944851605},
47
+ {:date_time=>"2018-11-20T00:00:00.000Z", :dr=>-0.047777897342085596},
48
+ {:date_time=>"2018-11-19T00:00:00.000Z", :dr=>-0.03963209838267967},
49
+ {:date_time=>"2018-11-16T00:00:00.000Z", :dr=>0.011075701374013924},
50
+ {:date_time=>"2018-11-15T00:00:00.000Z", :dr=>0.02467880085653107},
51
+ {:date_time=>"2018-11-14T00:00:00.000Z", :dr=>-0.02824741195442948},
52
+ {:date_time=>"2018-11-13T00:00:00.000Z", :dr=>-0.009991244785497289},
53
+ {:date_time=>"2018-11-12T00:00:00.000Z", :dr=>-0.05037413801535684},
54
+ {:date_time=>"2018-11-09T00:00:00.000Z", :dr=>-0.019281500311765565},
55
+ {:date_time=>"2018-11-08T00:00:00.000Z", :dr=>-0.006954036675398845},
56
+ {:date_time=>"2018-11-07T00:00:00.000Z", :dr=>0.030328311331403013},
57
+ {:date_time=>"2018-11-06T00:00:00.000Z", :dr=>0.010814028473634663},
58
+ {:date_time=>"2018-11-05T00:00:00.000Z", :dr=>-0.028388278388278287},
59
+ {:date_time=>"2018-11-02T00:00:00.000Z", :dr=>-0.06633066330663306},
60
+ {:date_time=>"2018-11-01T00:00:00.000Z", :dr=>0.015352279996344587},
61
+ {:date_time=>"2018-10-31T00:00:00.000Z", :dr=>0.026066572902015972},
62
+ {:date_time=>"2018-10-30T00:00:00.000Z", :dr=>0.004994346023369678},
63
+ {:date_time=>"2018-10-29T00:00:00.000Z", :dr=>-0.018770226537216828},
64
+ {:date_time=>"2018-10-26T00:00:00.000Z", :dr=>-0.015923566878980888},
65
+ {:date_time=>"2018-10-25T00:00:00.000Z", :dr=>0.021897810218978186},
66
+ {:date_time=>"2018-10-24T00:00:00.000Z", :dr=>-0.034301620796480026},
67
+ {:date_time=>"2018-10-23T00:00:00.000Z", :dr=>0.009426693859052815},
68
+ {:date_time=>"2018-10-22T00:00:00.000Z", :dr=>0.006110072500113972},
69
+ {:date_time=>"2018-10-19T00:00:00.000Z", :dr=>0.015230071289695335},
70
+ {:date_time=>"2018-10-18T00:00:00.000Z", :dr=>-0.023373570233735652},
71
+ {:date_time=>"2018-10-17T00:00:00.000Z", :dr=>-0.004321404456448352},
72
+ {:date_time=>"2018-10-16T00:00:00.000Z", :dr=>0.022037173352962736},
73
+ {:date_time=>"2018-10-15T00:00:00.000Z", :dr=>-0.021385799828913643},
74
+ {:date_time=>"2018-10-12T00:00:00.000Z", :dr=>0.035719281883889176},
75
+ {:date_time=>"2018-10-11T00:00:00.000Z", :dr=>-0.00882787946015906},
76
+ {:date_time=>"2018-10-10T00:00:00.000Z", :dr=>-0.046326089831180806},
77
+ {:date_time=>"2018-10-09T00:00:00.000Z", :dr=>0.0}
78
+ ]
79
+
80
+ expect(normalized_output).to eq(expected_output)
81
+ end
82
+
83
+ it "Throws exception if not enough data" do
84
+ expect {indicator.calculate([], price_key: :close)}.to raise_exception(TechnicalAnalysis::Validation::ValidationError)
85
+ end
86
+
87
+ it 'Returns the symbol' do
88
+ indicator_symbol = indicator.indicator_symbol
89
+ expect(indicator_symbol).to eq('dr')
90
+ end
91
+
92
+ it 'Returns the name' do
93
+ indicator_name = indicator.indicator_name
94
+ expect(indicator_name).to eq('Daily Return')
95
+ end
96
+
97
+ it 'Returns the valid options' do
98
+ valid_options = indicator.valid_options
99
+ expect(valid_options).to eq(%i(price_key))
100
+ end
101
+
102
+ it 'Validates options' do
103
+ valid_options = { price_key: :close }
104
+ options_validated = indicator.validate_options(valid_options)
105
+ expect(options_validated).to eq(true)
106
+ end
107
+
108
+ it 'Throws exception for invalid options' do
109
+ invalid_options = { test: 10 }
110
+ expect { indicator.validate_options(invalid_options) }.to raise_exception(TechnicalAnalysis::Validation::ValidationError)
111
+ end
112
+
113
+ it 'Calculates minimum data size' do
114
+ options = {}
115
+ expect(indicator.min_data_size(options)).to eq(1)
116
+ end
117
+ end
118
+ end
119
+ end
@@ -0,0 +1,105 @@
1
+ require 'technical-analysis'
2
+ require 'spec_helper'
3
+
4
+ describe 'Indicators' do
5
+ describe "EoM" do
6
+ input_data = SpecHelper.get_test_data(:high, :low, :volume)
7
+ indicator = TechnicalAnalysis::Eom
8
+
9
+ describe 'Ease of Movement' do
10
+ it 'Calculates EoM (14 day)' do
11
+ output = indicator.calculate(input_data, period: 14)
12
+ normalized_output = output.map(&:to_hash)
13
+
14
+ expected_output = [
15
+ {:date_time=>"2019-01-09T00:00:00.000Z", :eom=>-6.497226050937367},
16
+ {:date_time=>"2019-01-08T00:00:00.000Z", :eom=>-7.7025655861800235},
17
+ {:date_time=>"2019-01-07T00:00:00.000Z", :eom=>-10.852331038262774},
18
+ {:date_time=>"2019-01-04T00:00:00.000Z", :eom=>-13.901372232239117},
19
+ {:date_time=>"2019-01-03T00:00:00.000Z", :eom=>-14.868322149693872},
20
+ {:date_time=>"2019-01-02T00:00:00.000Z", :eom=>-10.542472341445862},
21
+ {:date_time=>"2018-12-31T00:00:00.000Z", :eom=>-7.266128029998595},
22
+ {:date_time=>"2018-12-28T00:00:00.000Z", :eom=>-11.754905920393293},
23
+ {:date_time=>"2018-12-27T00:00:00.000Z", :eom=>-15.149439471732872},
24
+ {:date_time=>"2018-12-26T00:00:00.000Z", :eom=>-21.397258702024285},
25
+ {:date_time=>"2018-12-24T00:00:00.000Z", :eom=>-29.127850557035778},
26
+ {:date_time=>"2018-12-21T00:00:00.000Z", :eom=>-21.640253827328284},
27
+ {:date_time=>"2018-12-20T00:00:00.000Z", :eom=>-19.510117531121228},
28
+ {:date_time=>"2018-12-19T00:00:00.000Z", :eom=>-14.184550665717635},
29
+ {:date_time=>"2018-12-18T00:00:00.000Z", :eom=>-5.583810139052782},
30
+ {:date_time=>"2018-12-17T00:00:00.000Z", :eom=>-5.714363954096145},
31
+ {:date_time=>"2018-12-14T00:00:00.000Z", :eom=>-5.49920090817078},
32
+ {:date_time=>"2018-12-13T00:00:00.000Z", :eom=>-8.427863395925653},
33
+ {:date_time=>"2018-12-12T00:00:00.000Z", :eom=>-8.897024349696329},
34
+ {:date_time=>"2018-12-11T00:00:00.000Z", :eom=>-15.409243349973261},
35
+ {:date_time=>"2018-12-10T00:00:00.000Z", :eom=>-21.68916026908629},
36
+ {:date_time=>"2018-12-07T00:00:00.000Z", :eom=>-15.003602992747133},
37
+ {:date_time=>"2018-12-06T00:00:00.000Z", :eom=>-14.327931101318542},
38
+ {:date_time=>"2018-12-04T00:00:00.000Z", :eom=>-13.565693513893978},
39
+ {:date_time=>"2018-12-03T00:00:00.000Z", :eom=>-11.780616477806618},
40
+ {:date_time=>"2018-11-30T00:00:00.000Z", :eom=>-20.874548142667777},
41
+ {:date_time=>"2018-11-29T00:00:00.000Z", :eom=>-23.304959556064855},
42
+ {:date_time=>"2018-11-28T00:00:00.000Z", :eom=>-23.906907831421474},
43
+ {:date_time=>"2018-11-27T00:00:00.000Z", :eom=>-24.18360420308819},
44
+ {:date_time=>"2018-11-26T00:00:00.000Z", :eom=>-23.02094878061384},
45
+ {:date_time=>"2018-11-23T00:00:00.000Z", :eom=>-27.26817615617346},
46
+ {:date_time=>"2018-11-21T00:00:00.000Z", :eom=>-28.2247053700715},
47
+ {:date_time=>"2018-11-20T00:00:00.000Z", :eom=>-27.37028760395389},
48
+ {:date_time=>"2018-11-19T00:00:00.000Z", :eom=>-16.94506299946587},
49
+ {:date_time=>"2018-11-16T00:00:00.000Z", :eom=>-13.43297156412281},
50
+ {:date_time=>"2018-11-15T00:00:00.000Z", :eom=>-23.978202090280167},
51
+ {:date_time=>"2018-11-14T00:00:00.000Z", :eom=>-26.374761082588922},
52
+ {:date_time=>"2018-11-13T00:00:00.000Z", :eom=>-22.593717509723117},
53
+ {:date_time=>"2018-11-12T00:00:00.000Z", :eom=>-19.69000228424736},
54
+ {:date_time=>"2018-11-09T00:00:00.000Z", :eom=>-16.918604972309158},
55
+ {:date_time=>"2018-11-08T00:00:00.000Z", :eom=>-11.567473480653877},
56
+ {:date_time=>"2018-11-07T00:00:00.000Z", :eom=>-10.367227984759097},
57
+ {:date_time=>"2018-11-06T00:00:00.000Z", :eom=>-22.183583990006944},
58
+ {:date_time=>"2018-11-05T00:00:00.000Z", :eom=>-22.338003197707696},
59
+ {:date_time=>"2018-11-02T00:00:00.000Z", :eom=>-16.2797807192085},
60
+ {:date_time=>"2018-11-01T00:00:00.000Z", :eom=>-10.135395436615527},
61
+ {:date_time=>"2018-10-31T00:00:00.000Z", :eom=>-6.606596973217312},
62
+ {:date_time=>"2018-10-30T00:00:00.000Z", :eom=>-16.275660385464448},
63
+ {:date_time=>"2018-10-29T00:00:00.000Z", :eom=>-21.877975231994814}
64
+ ]
65
+
66
+ expect(normalized_output).to eq(expected_output)
67
+ end
68
+
69
+ it "Throws exception if not enough data" do
70
+ expect {indicator.calculate(input_data, period: input_data.size+2)}.to raise_exception(TechnicalAnalysis::Validation::ValidationError)
71
+ end
72
+
73
+ it 'Returns the symbol' do
74
+ indicator_symbol = indicator.indicator_symbol
75
+ expect(indicator_symbol).to eq('eom')
76
+ end
77
+
78
+ it 'Returns the name' do
79
+ indicator_name = indicator.indicator_name
80
+ expect(indicator_name).to eq('Ease of Movement')
81
+ end
82
+
83
+ it 'Returns the valid options' do
84
+ valid_options = indicator.valid_options
85
+ expect(valid_options).to eq(%i(period))
86
+ end
87
+
88
+ it 'Validates options' do
89
+ valid_options = { period: 22 }
90
+ options_validated = indicator.validate_options(valid_options)
91
+ expect(options_validated).to eq(true)
92
+ end
93
+
94
+ it 'Throws exception for invalid options' do
95
+ invalid_options = { test: 10 }
96
+ expect { indicator.validate_options(invalid_options) }.to raise_exception(TechnicalAnalysis::Validation::ValidationError)
97
+ end
98
+
99
+ it 'Calculates minimum data size' do
100
+ options = { period: 4 }
101
+ expect(indicator.min_data_size(options)).to eq(5)
102
+ end
103
+ end
104
+ end
105
+ end
@@ -0,0 +1,118 @@
1
+ require 'technical-analysis'
2
+ require 'spec_helper'
3
+
4
+ describe 'Indicators' do
5
+ describe "FI" do
6
+ input_data = SpecHelper.get_test_data(:close, :volume)
7
+ indicator = TechnicalAnalysis::Fi
8
+
9
+ describe 'Forced Index' do
10
+ it 'Calculates FI' do
11
+ output = indicator.calculate(input_data)
12
+ normalized_output = output.map(&:to_hash)
13
+
14
+ expected_output = [
15
+ {:date_time=>"2019-01-09T00:00:00.000Z", :fi=>115287987.2000001},
16
+ {:date_time=>"2019-01-08T00:00:00.000Z", :fi=>114556606.19999972},
17
+ {:date_time=>"2019-01-07T00:00:00.000Z", :fi=>-18008575.19999913},
18
+ {:date_time=>"2019-01-04T00:00:00.000Z", :fi=>348561555.4999996},
19
+ {:date_time=>"2019-01-03T00:00:00.000Z", :fi=>-1433110593.199999},
20
+ {:date_time=>"2019-01-02T00:00:00.000Z", :fi=>6414672.59999923},
21
+ {:date_time=>"2018-12-31T00:00:00.000Z", :fi=>52094078.90000067},
22
+ {:date_time=>"2018-12-28T00:00:00.000Z", :fi=>3339247.9999993355},
23
+ {:date_time=>"2018-12-27T00:00:00.000Z", :fi=>-52641026.99999906},
24
+ {:date_time=>"2018-12-26T00:00:00.000Z", :fi=>601104008.9999986},
25
+ {:date_time=>"2018-12-24T00:00:00.000Z", :fi=>-144959996.99999917},
26
+ {:date_time=>"2018-12-21T00:00:00.000Z", :fi=>-582537190.0000021},
27
+ {:date_time=>"2018-12-20T00:00:00.000Z", :fi=>-261456813.7999983},
28
+ {:date_time=>"2018-12-19T00:00:00.000Z", :fi=>-246555930.60000032},
29
+ {:date_time=>"2018-12-18T00:00:00.000Z", :fi=>71894933.69999984},
30
+ {:date_time=>"2018-12-17T00:00:00.000Z", :fi=>-66605646.799999654},
31
+ {:date_time=>"2018-12-14T00:00:00.000Z", :fi=>-222193369.19999996},
32
+ {:date_time=>"2018-12-13T00:00:00.000Z", :fi=>58745288.49999982},
33
+ {:date_time=>"2018-12-12T00:00:00.000Z", :fi=>16673099.59999996},
34
+ {:date_time=>"2018-12-11T00:00:00.000Z", :fi=>-44588998.799999945},
35
+ {:date_time=>"2018-12-10T00:00:00.000Z", :fi=>68552489.99999909},
36
+ {:date_time=>"2018-12-07T00:00:00.000Z", :fi=>-259658176.39999956},
37
+ {:date_time=>"2018-12-06T00:00:00.000Z", :fi=>-84128672.69999996},
38
+ {:date_time=>"2018-12-04T00:00:00.000Z", :fi=>-334478362.4999998},
39
+ {:date_time=>"2018-12-03T00:00:00.000Z", :fi=>252955247.99999923},
40
+ {:date_time=>"2018-11-30T00:00:00.000Z", :fi=>-38241532.19999996},
41
+ {:date_time=>"2018-11-29T00:00:00.000Z", :fi=>-57717776.19999944},
42
+ {:date_time=>"2018-11-28T00:00:00.000Z", :fi=>307809724.99999946},
43
+ {:date_time=>"2018-11-27T00:00:00.000Z", :fi=>-15639333.199999813},
44
+ {:date_time=>"2018-11-26T00:00:00.000Z", :fi=>104063205.60000056},
45
+ {:date_time=>"2018-11-23T00:00:00.000Z", :fi=>-106071625.30000022},
46
+ {:date_time=>"2018-11-21T00:00:00.000Z", :fi=>-6219247.999999646},
47
+ {:date_time=>"2018-11-20T00:00:00.000Z", :fi=>-600986678.4000016},
48
+ {:date_time=>"2018-11-19T00:00:00.000Z", :fi=>-319277709.3999995},
49
+ {:date_time=>"2018-11-16T00:00:00.000Z", :fi=>76725619.60000016},
50
+ {:date_time=>"2018-11-15T00:00:00.000Z", :fi=>213312352.5999993},
51
+ {:date_time=>"2018-11-14T00:00:00.000Z", :fi=>-328772056.1999987},
52
+ {:date_time=>"2018-11-13T00:00:00.000Z", :fi=>-90647877.39999989},
53
+ {:date_time=>"2018-11-12T00:00:00.000Z", :fi=>-525207609.0000006},
54
+ {:date_time=>"2018-11-09T00:00:00.000Z", :fi=>-137957395.20000035},
55
+ {:date_time=>"2018-11-08T00:00:00.000Z", :fi=>-36922334.19999948},
56
+ {:date_time=>"2018-11-07T00:00:00.000Z", :fi=>205742335.19999927},
57
+ {:date_time=>"2018-11-06T00:00:00.000Z", :fi=>69268889.60000022},
58
+ {:date_time=>"2018-11-05T00:00:00.000Z", :fi=>-389165081.2999991},
59
+ {:date_time=>"2018-11-02T00:00:00.000Z", :fi=>-1342026294.4000008},
60
+ {:date_time=>"2018-11-01T00:00:00.000Z", :fi=>177925675.1999992},
61
+ {:date_time=>"2018-10-31T00:00:00.000Z", :fi=>211373463.60000008},
62
+ {:date_time=>"2018-10-30T00:00:00.000Z", :fi=>38677205.80000009},
63
+ {:date_time=>"2018-10-29T00:00:00.000Z", :fi=>-185597581.4000001},
64
+ {:date_time=>"2018-10-26T00:00:00.000Z", :fi=>-165170950.0},
65
+ {:date_time=>"2018-10-25T00:00:00.000Z", :fi=>136718771.40000024},
66
+ {:date_time=>"2018-10-24T00:00:00.000Z", :fi=>-305539796.7999995},
67
+ {:date_time=>"2018-10-23T00:00:00.000Z", :fi=>80456833.59999938},
68
+ {:date_time=>"2018-10-22T00:00:00.000Z", :fi=>38527063.6000001},
69
+ {:date_time=>"2018-10-19T00:00:00.000Z", :fi=>108156545.69999973},
70
+ {:date_time=>"2018-10-18T00:00:00.000Z", :fi=>-167452577.5999996},
71
+ {:date_time=>"2018-10-17T00:00:00.000Z", :fi=>-21785164.80000018},
72
+ {:date_time=>"2018-10-16T00:00:00.000Z", :fi=>137964214.49999976},
73
+ {:date_time=>"2018-10-15T00:00:00.000Z", :fi=>-143832137.5},
74
+ {:date_time=>"2018-10-12T00:00:00.000Z", :fi=>302529938.200001},
75
+ {:date_time=>"2018-10-11T00:00:00.000Z", :fi=>-101043431.20000133},
76
+ {:date_time=>"2018-10-10T00:00:00.000Z", :fi=>-431793575.69999963}
77
+ ]
78
+
79
+ expect(normalized_output).to eq(expected_output)
80
+ end
81
+
82
+ it "Throws exception if not enough data" do
83
+ expect {indicator.calculate([])}.to raise_exception(TechnicalAnalysis::Validation::ValidationError)
84
+ end
85
+
86
+ it 'Returns the symbol' do
87
+ indicator_symbol = indicator.indicator_symbol
88
+ expect(indicator_symbol).to eq('fi')
89
+ end
90
+
91
+ it 'Returns the name' do
92
+ indicator_name = indicator.indicator_name
93
+ expect(indicator_name).to eq('Force Index')
94
+ end
95
+
96
+ it 'Returns the valid options' do
97
+ valid_options = indicator.valid_options
98
+ expect(valid_options).to eq([])
99
+ end
100
+
101
+ it 'Validates options' do
102
+ valid_options = {}
103
+ options_validated = indicator.validate_options(valid_options)
104
+ expect(options_validated).to eq(true)
105
+ end
106
+
107
+ it 'Throws exception for invalid options' do
108
+ invalid_options = { test: 10 }
109
+ expect { indicator.validate_options(invalid_options) }.to raise_exception(TechnicalAnalysis::Validation::ValidationError)
110
+ end
111
+
112
+ it 'Calculates minimum data size' do
113
+ options = {}
114
+ expect(indicator.min_data_size(options)).to eq(2)
115
+ end
116
+ end
117
+ end
118
+ end
@@ -0,0 +1,95 @@
1
+ require 'technical-analysis'
2
+ require 'spec_helper'
3
+
4
+ describe 'Indicators' do
5
+ describe "Ichimoku" do
6
+ input_data = SpecHelper.get_test_data(:high, :low, :close)
7
+ indicator = TechnicalAnalysis::Ichimoku
8
+
9
+ describe 'Ichimoku' do
10
+ it 'Calculates Ichimoku' do
11
+ output = indicator.calculate(input_data, low_period: 3, medium_period: 10, high_period: 20)
12
+ normalized_output = output.map(&:to_hash)
13
+
14
+ expected_output = [
15
+ {:date_time=>"2019-01-09T00:00:00.000Z", :chikou_span=>157.17, :kijun_sen=>150.68, :senkou_span_a=>155.9775, :senkou_span_b=>165.765, :tenkan_sen=>150.215},
16
+ {:date_time=>"2019-01-08T00:00:00.000Z", :chikou_span=>146.83, :kijun_sen=>150.68, :senkou_span_a=>156.965, :senkou_span_b=>165.765, :tenkan_sen=>147.81},
17
+ {:date_time=>"2019-01-07T00:00:00.000Z", :chikou_span=>150.73, :kijun_sen=>150.68, :senkou_span_a=>159.82, :senkou_span_b=>167.285, :tenkan_sen=>145.41500000000002},
18
+ {:date_time=>"2019-01-04T00:00:00.000Z", :chikou_span=>156.83, :kijun_sen=>152.055, :senkou_span_a=>163.15500000000003, :senkou_span_b=>170.12, :tenkan_sen=>150.425},
19
+ {:date_time=>"2019-01-03T00:00:00.000Z", :chikou_span=>160.89, :kijun_sen=>154.725, :senkou_span_a=>165.3275, :senkou_span_b=>172.015, :tenkan_sen=>150.68},
20
+ {:date_time=>"2019-01-02T00:00:00.000Z", :chikou_span=>166.07, :kijun_sen=>157.06, :senkou_span_a=>169.23247500000002, :senkou_span_b=>176.71499999999997, :tenkan_sen=>156.79500000000002},
21
+ {:date_time=>"2018-12-31T00:00:00.000Z", :chikou_span=>163.94, :kijun_sen=>157.47, :senkou_span_a=>170.74249999999998, :senkou_span_b=>178.84975, :tenkan_sen=>154.715},
22
+ {:date_time=>"2018-12-28T00:00:00.000Z", :chikou_span=>165.48, :kijun_sen=>157.835, :senkou_span_a=>171.53, :senkou_span_b=>179.14975, :tenkan_sen=>152.62},
23
+ {:date_time=>"2018-12-27T00:00:00.000Z", :chikou_span=>170.95, :kijun_sen=>159.57999999999998, :senkou_span_a=>171.95999999999998, :senkou_span_b=>179.14975, :tenkan_sen=>151.91},
24
+ {:date_time=>"2018-12-26T00:00:00.000Z", :chikou_span=>169.1, :kijun_sen=>159.57999999999998, :senkou_span_a=>170.88, :senkou_span_b=>180.255, :tenkan_sen=>152.375},
25
+ {:date_time=>"2018-12-24T00:00:00.000Z", :chikou_span=>168.63, :kijun_sen=>159.57999999999998, :senkou_span_a=>171.5225, :senkou_span_b=>181.59, :tenkan_sen=>154.35000000000002},
26
+ {:date_time=>"2018-12-21T00:00:00.000Z", :chikou_span=>169.6, :kijun_sen=>161.1, :senkou_span_a=>171.595, :senkou_span_b=>184.67000000000002, :tenkan_sen=>158.54},
27
+ {:date_time=>"2018-12-20T00:00:00.000Z", :chikou_span=>168.49, :kijun_sen=>164.895, :senkou_span_a=>175.98247500000002, :senkou_span_b=>189.21, :tenkan_sen=>161.41500000000002},
28
+ {:date_time=>"2018-12-19T00:00:00.000Z", :chikou_span=>174.72, :kijun_sen=>166.935, :senkou_span_a=>177.64, :senkou_span_b=>190.19, :tenkan_sen=>163.72},
29
+ {:date_time=>"2018-12-18T00:00:00.000Z", :chikou_span=>176.69, :kijun_sen=>172.55995000000001, :senkou_span_a=>179.10250000000002, :senkou_span_b=>190.19, :tenkan_sen=>165.905},
30
+ {:date_time=>"2018-12-17T00:00:00.000Z", :chikou_span=>184.82, :kijun_sen=>173.83499999999998, :senkou_span_a=>180.73250000000002, :senkou_span_b=>190.19, :tenkan_sen=>167.64999999999998},
31
+ {:date_time=>"2018-12-14T00:00:00.000Z", :chikou_span=>178.58, :kijun_sen=>174.135, :senkou_span_a=>180.739875, :senkou_span_b=>191.95499999999998, :tenkan_sen=>168.925},
32
+ {:date_time=>"2018-12-13T00:00:00.000Z", :chikou_span=>179.55, :kijun_sen=>174.135, :senkou_span_a=>179.727375, :senkou_span_b=>196.31, :tenkan_sen=>169.785},
33
+ {:date_time=>"2018-12-12T00:00:00.000Z", :chikou_span=>180.94, :kijun_sen=>174.135, :senkou_span_a=>179.194875, :senkou_span_b=>196.31, :tenkan_sen=>167.625},
34
+ {:date_time=>"2018-12-11T00:00:00.000Z", :chikou_span=>174.24, :kijun_sen=>174.135, :senkou_span_a=>178.57375000000002, :senkou_span_b=>196.31, :tenkan_sen=>168.91000000000003},
35
+ {:date_time=>"2018-12-10T00:00:00.000Z", :chikou_span=>174.62, :kijun_sen=>174.135, :senkou_span_a=>180.16, :senkou_span_b=>196.31, :tenkan_sen=>169.055},
36
+ {:date_time=>"2018-12-07T00:00:00.000Z", :chikou_span=>172.29, :kijun_sen=>176.62, :senkou_span_a=>182.92000000000002, :senkou_span_b=>197.23000000000002, :tenkan_sen=>175.34495},
37
+ {:date_time=>"2018-12-06T00:00:00.000Z", :chikou_span=>176.78, :kijun_sen=>177.6, :senkou_span_a=>187.95999999999998, :senkou_span_b=>198.935, :tenkan_sen=>177.68},
38
+ {:date_time=>"2018-12-04T00:00:00.000Z", :chikou_span=>176.98, :kijun_sen=>177.6, :senkou_span_a=>189.027375, :senkou_span_b=>199.87, :tenkan_sen=>180.60500000000002},
39
+ {:date_time=>"2018-12-03T00:00:00.000Z", :chikou_span=>185.86, :kijun_sen=>180.48, :senkou_span_a=>193.76737500000002, :senkou_span_b=>204.61, :tenkan_sen=>180.985},
40
+ {:date_time=>"2018-11-30T00:00:00.000Z", :chikou_span=>193.53, :kijun_sen=>182.61475000000002, :senkou_span_a=>194.237375, :senkou_span_b=>205.07999999999998, :tenkan_sen=>178.865},
41
+ {:date_time=>"2018-11-29T00:00:00.000Z", :chikou_span=>191.41, :kijun_sen=>182.61475000000002, :senkou_span_a=>195.6725, :senkou_span_b=>205.07999999999998, :tenkan_sen=>176.84},
42
+ {:date_time=>"2018-11-28T00:00:00.000Z", :chikou_span=>186.8, :kijun_sen=>182.61475000000002, :senkou_span_a=>198.51749999999998, :senkou_span_b=>205.07999999999998, :tenkan_sen=>175.77499999999998},
43
+ {:date_time=>"2018-11-27T00:00:00.000Z", :chikou_span=>192.23, :kijun_sen=>183.72, :senkou_span_a=>202.81755, :senkou_span_b=>207.84005, :tenkan_sen=>173.4275},
44
+ {:date_time=>"2018-11-26T00:00:00.000Z", :chikou_span=>194.17, :kijun_sen=>185.055, :senkou_span_a=>205.015, :senkou_span_b=>209.01, :tenkan_sen=>175.265},
45
+ {:date_time=>"2018-11-23T00:00:00.000Z", :chikou_span=>204.47, :kijun_sen=>189.055, :senkou_span_a=>208.225, :senkou_span_b=>211.2, :tenkan_sen=>176.785},
46
+ {:date_time=>"2018-11-21T00:00:00.000Z", :chikou_span=>208.49, :kijun_sen=>192.815, :senkou_span_a=>208.08499999999998, :senkou_span_b=>211.2, :tenkan_sen=>183.105},
47
+ {:date_time=>"2018-11-20T00:00:00.000Z", :chikou_span=>209.95, :kijun_sen=>192.815, :senkou_span_a=>207.19, :senkou_span_b=>211.2, :tenkan_sen=>185.23975000000002},
48
+ {:date_time=>"2018-11-19T00:00:00.000Z", :chikou_span=>203.77, :kijun_sen=>197.555, :senkou_span_a=>208.555, :senkou_span_b=>212.26, :tenkan_sen=>189.97975000000002},
49
+ {:date_time=>"2018-11-16T00:00:00.000Z", :chikou_span=>201.59, :kijun_sen=>198.025, :senkou_span_a=>210.7325, :senkou_span_b=>212.72, :tenkan_sen=>190.44975}
50
+ ]
51
+
52
+ expect(normalized_output).to eq(expected_output)
53
+ end
54
+
55
+ it "Throws exception if not enough data" do
56
+ medium_period = 20
57
+ high_period = 40
58
+ size_limit = (medium_period + high_period + 1)
59
+
60
+ expect {indicator.calculate(input_data, high_period: size_limit)}.to raise_exception(TechnicalAnalysis::Validation::ValidationError)
61
+ end
62
+
63
+ it 'Returns the symbol' do
64
+ indicator_symbol = indicator.indicator_symbol
65
+ expect(indicator_symbol).to eq('ichimoku')
66
+ end
67
+
68
+ it 'Returns the name' do
69
+ indicator_name = indicator.indicator_name
70
+ expect(indicator_name).to eq('Ichimoku Kinko Hyo')
71
+ end
72
+
73
+ it 'Returns the valid options' do
74
+ valid_options = indicator.valid_options
75
+ expect(valid_options).to eq(%i(low_period medium_period high_period))
76
+ end
77
+
78
+ it 'Validates options' do
79
+ valid_options = { low_period: 9, medium_period: 26, high_period: 52 }
80
+ options_validated = indicator.validate_options(valid_options)
81
+ expect(options_validated).to eq(true)
82
+ end
83
+
84
+ it 'Throws exception for invalid options' do
85
+ invalid_options = { test: 10 }
86
+ expect { indicator.validate_options(invalid_options) }.to raise_exception(TechnicalAnalysis::Validation::ValidationError)
87
+ end
88
+
89
+ it 'Calculates minimum data size' do
90
+ options = { medium_period: 4, high_period: 10 }
91
+ expect(indicator.min_data_size(options)).to eq(13)
92
+ end
93
+ end
94
+ end
95
+ end