bio-statsample-timeseries 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
data/test/helper.rb ADDED
@@ -0,0 +1,81 @@
1
+ require 'rubygems'
2
+ require 'bundler'
3
+ begin
4
+ Bundler.setup(:default, :development)
5
+ rescue Bundler::BundlerError => e
6
+ $stderr.puts e.message
7
+ $stderr.puts "Run `bundle install` to install missing gems"
8
+ exit e.status_code
9
+ end
10
+ require 'minitest/unit'
11
+ require 'shoulda'
12
+ require 'shoulda-context'
13
+ require 'mocha'
14
+
15
+ require 'bio-statsample-timeseries'
16
+
17
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
18
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
19
+ require 'bio-statsample-timeseries'
20
+ module MiniTest
21
+ class Unit
22
+ class TestCase
23
+ include Shoulda::Context::Assertions
24
+ include Shoulda::Context::InstanceMethods
25
+ extend Shoulda::Context::ClassMethods
26
+ def self.should_with_gsl(name,&block)
27
+ should(name) do
28
+ if Statsample.has_gsl?
29
+ instance_eval(&block)
30
+ else
31
+ skip("Requires GSL")
32
+ end
33
+ end
34
+ end
35
+ end
36
+ end
37
+
38
+ module Assertions
39
+ def assert_similar_vector(exp, obs, delta=1e-10,msg=nil)
40
+ msg||="Different vectors #{exp} - #{obs}"
41
+ assert_equal(exp.size, obs.size)
42
+ exp.data_with_nils.each_with_index {|v,i|
43
+ assert_in_delta(v,obs[i],delta)
44
+ }
45
+ end
46
+ def assert_equal_vector(exp,obs,delta=1e-10,msg=nil)
47
+ assert_equal(exp.size, obs.size, "Different size.#{msg}")
48
+ exp.size.times {|i|
49
+ assert_in_delta(exp[i],obs[i],delta, "Different element #{i}. \nExpected:\n#{exp}\nObserved:\n#{obs}.#{msg}")
50
+ }
51
+ end
52
+ def assert_equal_matrix(exp,obs,delta=1e-10,msg=nil)
53
+ assert_equal(exp.row_size, obs.row_size, "Different row size.#{msg}")
54
+ assert_equal(exp.column_size, obs.column_size, "Different column size.#{msg}")
55
+ exp.row_size.times {|i|
56
+ exp.column_size.times {|j|
57
+ assert_in_delta(exp[i,j],obs[i,j], delta, "Different element #{i},#{j}\nExpected:\n#{exp}\nObserved:\n#{obs}.#{msg}")
58
+ }
59
+ }
60
+ end
61
+ alias :assert_raise :assert_raises unless method_defined? :assert_raise
62
+ alias :assert_not_equal :refute_equal unless method_defined? :assert_not_equal
63
+ alias :assert_not_same :refute_same unless method_defined? :assert_not_same
64
+ unless method_defined? :assert_nothing_raised
65
+ def assert_nothing_raised(msg=nil)
66
+ msg||="Nothing should be raised, but raised %s"
67
+ begin
68
+ yield
69
+ not_raised=true
70
+ rescue Exception => e
71
+ not_raised=false
72
+ msg=sprintf(msg,e)
73
+ end
74
+ assert(not_raised,msg)
75
+ end
76
+ end
77
+ end
78
+ end
79
+
80
+ MiniTest::Unit.autorun
81
+
@@ -0,0 +1,176 @@
1
+ require(File.expand_path(File.dirname(__FILE__)+'/helper.rb'))
2
+
3
+ class StatsampleArimaSimulatorsTest < MiniTest::Unit::TestCase
4
+ def generate_acf(simulation)
5
+ ts = simulation.to_ts
6
+ ts.acf
7
+ end
8
+
9
+ def generate_pacf(simulation)
10
+ ts = simulation.to_ts
11
+ ts.pacf
12
+ end
13
+ context("AR(1) simulations") do
14
+ include Statsample::ARIMA
15
+
16
+ setup do
17
+ @series = ARIMA.new
18
+ @ar_1_positive = @series.ar_sim(1500, [0.9], 2)
19
+ @ar_1_negative = @series.ar_sim(1500, [-0.9], 2)
20
+
21
+ #generating acf
22
+ @positive_acf = generate_acf(@ar_1_positive)
23
+ @negative_acf = generate_acf(@ar_1_negative)
24
+
25
+ #generating pacf
26
+ @positive_pacf = generate_pacf(@ar_1_positive)
27
+ @negative_pacf = generate_pacf(@ar_1_negative)
28
+ end
29
+
30
+
31
+ should "have exponential decay of acf on positive side with phi > 0" do
32
+ @acf = @positive_acf
33
+ assert_equal @acf[0], 1.0
34
+ assert_operator @acf[1], :>=, 0.7
35
+ assert_operator @acf[@acf.size - 1], :<=, 0.2
36
+ #visualization:
37
+ #https://dl.dropboxusercontent.com/u/102071534/sciruby/AR%281%29_positive_phi_acf.png
38
+ #https://dl.dropboxusercontent.com/u/102071534/sciruby/AR%281%29_positive_phi_acf_line.png
39
+ end
40
+
41
+ should "have series with alternating sign on acf starting on negative side with phi < 0" do
42
+ @acf = @negative_acf
43
+ assert_equal @acf[0], 1.0
44
+ #testing for alternating series
45
+ assert_operator @acf[1], :<, 0
46
+ assert_operator @acf[2], :>, 0
47
+ assert_operator @acf[3], :<, 0
48
+ assert_operator @acf[4], :>, 0
49
+ #visualization:
50
+ #https://dl.dropboxusercontent.com/u/102071534/sciruby/AR%281%29_negative_phi_acf.png
51
+ #https://dl.dropboxusercontent.com/u/102071534/sciruby/AR%281%29_negative_phi_acf_line.png
52
+ end
53
+
54
+ should "have positive spike on pacf at lag 1 for phi > 0" do
55
+ @pacf = @positive_pacf
56
+ assert_operator @pacf[1], :>=, 0.7
57
+ assert_operator @pacf[2], :<=, 0.2
58
+ assert_operator @pacf[3], :<=, 0.14
59
+ #visualization:
60
+ #https://dl.dropboxusercontent.com/u/102071534/sciruby/AR%281%29_postive_phi_pacf.png
61
+ #https://dl.dropboxusercontent.com/u/102071534/sciruby/AR%281%29_postive_phi_pacf_line.png
62
+ end
63
+
64
+ should "have negative spike on pacf at lag 1 for phi < 0" do
65
+ @pacf = @negative_pacf
66
+ assert_operator @pacf[1], :<=, 0
67
+ assert_operator @pacf[1], :<=, -0.5
68
+ assert_operator @pacf[2], :>=, -0.5
69
+ #visualizaton:
70
+ #https://dl.dropboxusercontent.com/u/102071534/sciruby/AR%281%29_negative_phi_pacf.png
71
+ #[hided @pacf[0] = 1 to convey accurate picture]
72
+ end
73
+ end
74
+
75
+ context("AR(p) simulations") do
76
+ include Statsample::ARIMA
77
+
78
+ setup do
79
+ @series = ARIMA.new
80
+ @ar_p_positive = @series.ar_sim(1500, [0.3, 0.5], 2)
81
+ @ar_p_negative = @series.ar_sim(1500, [-0.3, -0.5], 2)
82
+ end
83
+
84
+
85
+ should "have damped sine wave starting on positive side on acf" do
86
+ @ar = @ar_p_positive
87
+ @acf = generate_acf(@ar)
88
+ assert_operator @acf[0], :>=, @acf[1]
89
+ assert_operator @acf[1], :>=, 0.0
90
+ #caution: sine curve can split on cartesian plane,
91
+ #visualization:
92
+ #https://dl.dropboxusercontent.com/u/102071534/sciruby/AR(p)_positive_phi_sine_wave.png
93
+ end
94
+
95
+ should "have damped sine wave starting on negative side on acf" do
96
+ @ar = @ar_p_negative
97
+ @acf = generate_acf(@ar)
98
+ assert_operator @acf[0], :>=, @acf[1]
99
+ assert_operator @acf[1], :<=, 0.0
100
+ assert_operator @acf[1], :>=, @acf[2]
101
+ #caution: sine curve can split on cartesian plane,
102
+ #visualization:
103
+ #https://dl.dropboxusercontent.com/u/102071534/sciruby/AR%28p%29_negative_phi_acf_sine_wave.png
104
+ end
105
+
106
+ should "have spikes from 1 to p for pacf" do
107
+ #here p = 2
108
+ @ar = @ar_p_positive
109
+ @pacf = generate_pacf(@ar)
110
+ assert_equal @pacf[0], 1.0
111
+ assert_operator @pacf[1], :>, @pacf[3]
112
+ assert_operator @pacf[1], :>, @pacf[4]
113
+ assert_operator @pacf[1], :>, @pacf[5]
114
+ assert_operator @pacf[2], :>, @pacf[3]
115
+ assert_operator @pacf[2], :>, @pacf[4]
116
+ #visualization:
117
+ #https://dl.dropboxusercontent.com/u/102071534/sciruby/AR(p)_positive_phi_pacf_spikes.png
118
+ end
119
+ end
120
+
121
+
122
+ context("MA(1) simulations") do
123
+ include Statsample::ARIMA
124
+ setup do
125
+ @series = ARIMA.new
126
+ @ma_positive = @series.ar_sim(1500, [0.5], 2)
127
+ @ma_negative = @series.ar_sim(1500, [-0.5], 2)
128
+ end
129
+
130
+ should "have one positive spike at lag 1 on acf at positive theta" do
131
+ @acf = generate_acf(@ma_positive)
132
+ assert_equal @acf[0], 1.0
133
+ assert_operator @acf[1], :>=, 0 #test if positive
134
+ #test if spike
135
+ assert_operator @acf[2], :>=, 0.1
136
+ assert_operator @acf[3], :<=, 0.2
137
+ assert_operator @acf[4], :<=, 0.2
138
+ #visualization:
139
+ #https://dl.dropboxusercontent.com/u/102071534/sciruby/MA%281%29_postive_acf.png
140
+ end
141
+
142
+ should "have one negative spike at lag 1 on acf at negative theta" do
143
+ @acf = generate_acf(@ma_negative)
144
+ assert_operator @acf[1], :<, 0
145
+ assert_operator @acf[2], :>=, @acf[1]
146
+ assert_operator @acf[3], :>=, @acf[1]
147
+ #visualization:
148
+ #https://dl.dropboxusercontent.com/u/102071534/sciruby/MA%281%29_negative_acf.png
149
+ #positive_vs_negative:
150
+ #https://dl.dropboxusercontent.com/u/102071534/sciruby/MA%281%29_acf_positive_vs_negative.png
151
+ end
152
+
153
+ end
154
+
155
+ context("MA(q) simulations") do
156
+ include Statsample::ARIMA
157
+ setup do
158
+ @series = ARIMA.new
159
+ @ma_positive = @series.ar_sim(1500, [0.5, 0.3, 0.2], 2)
160
+ @ma_negative = @series.ar_sim(1500, [-0.5], 2)
161
+ end
162
+
163
+ should "have q positive spikes at lag 1 to q on acf at positive thetas" do
164
+ @acf = generate_acf(@ma_positive)
165
+ assert_operator @acf[1], :>=, @acf[2]
166
+ assert_operator @acf[2], :>=, @acf[3]
167
+ assert_operator @acf[3], :>=, @acf[4]
168
+ #Visualization: http://jsfiddle.net/YeK2c/
169
+ end
170
+
171
+ should "have damped sine wave on pacf at positive thetas" do
172
+ #visualization: http://jsfiddle.net/7keHK/2/
173
+ end
174
+ end
175
+ end
176
+
data/test/test_pacf.rb ADDED
@@ -0,0 +1,52 @@
1
+ require(File.expand_path(File.dirname(__FILE__)+'/helper.rb'))
2
+ class StatsampleTimeSeriesPacfTestCase < MiniTest::Unit::TestCase
3
+ context(Statsample::TimeSeries) do
4
+ include Statsample::TimeSeries
5
+ setup do
6
+ @ts = (1..20).map { |x| x * 10 }.to_ts
7
+ #setting up a proc to get a closure for pacf calling with variable lags and methods
8
+ @pacf_proc =->(k, method) { @ts.pacf(k, method) }
9
+ end
10
+
11
+ should "return correct correct pacf size for lags = 5" do
12
+ assert_equal @pacf_proc.call(5, 'yw').size, 6
13
+ assert_equal @pacf_proc.call(5, 'mle').size, 6
14
+ #first element is 1.0
15
+ end
16
+
17
+ should "return correct correct pacf size for lags = 10" do
18
+ assert_equal @pacf_proc.call(10, 'yw').size, 11
19
+ assert_equal @pacf_proc.call(10, 'mle').size, 11
20
+ #first element is 1.0
21
+ end
22
+
23
+ should "have first element as 1.0" do
24
+ assert_equal @pacf_proc.call(10, 'yw')[0], 1.0
25
+ assert_equal @pacf_proc.call(10, 'mle')[0], 1.0
26
+ end
27
+
28
+ should "give correct pacf results for unbiased yule-walker" do
29
+ result_10 = [1.0, 0.8947368421052632, -0.10582010582010604, -0.11350188273265083, -0.12357534824820737, -0.13686534216335522, -0.15470588235294147, -0.17938011883732036, -0.2151192288178601, -0.2707082833133261, -0.3678160919540221]
30
+ result_5 = [1.0, 0.8947368421052632, -0.10582010582010604, -0.11350188273265083, -0.12357534824820737, -0.13686534216335522]
31
+ assert_equal @pacf_proc.call(10, 'yw'), result_10
32
+ assert_equal @pacf_proc.call(5, 'yw'), result_5
33
+
34
+ #Checking for lag = (1..10)
35
+ 1.upto(10) do |i|
36
+ assert_equal @pacf_proc.call(i, 'yw'), result_10[0..i]
37
+ end
38
+ end
39
+
40
+ should "give correct pacf results for mle yule-walker" do
41
+ result_10 = [1.0, 0.85, -0.07566212829370711, -0.07635069706072706, -0.07698628638512295, -0.07747034005560738, -0.0776780981161499, -0.07744984679625189, -0.0765803323191094, -0.07480650005932366, -0.07179435184923755]
42
+ result_5 = [1.0, 0.85, -0.07566212829370711, -0.07635069706072706, -0.07698628638512295, -0.07747034005560738]
43
+ assert_equal @pacf_proc.call(10, 'mle'), result_10
44
+ assert_equal @pacf_proc.call(5, 'mle'), result_5
45
+
46
+ #Checking for lag = (1..10)
47
+ 1.upto(10) do |i|
48
+ assert_equal @pacf_proc.call(i, 'mle'), result_10[0..i]
49
+ end
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,103 @@
1
+ require(File.expand_path(File.dirname(__FILE__)+'/helper.rb'))
2
+
3
+ class StatsampleTestTimeSeries < MiniTest::Unit::TestCase
4
+ include Statsample::Shorthand
5
+
6
+ # All calculations are compared to the output of the equivalent function in R
7
+
8
+ def setup
9
+ # daily closes of iShares XIU on the TSX
10
+ @xiu = Statsample::TimeSeries::TimeSeries.new [17.28, 17.45, 17.84, 17.74, 17.82, 17.85, 17.36, 17.3, 17.56, 17.49, 17.46, 17.4, 17.03, 17.01,
11
+ 16.86, 16.86, 16.56, 16.36, 16.66, 16.77], :scale
12
+ end
13
+
14
+ def test_acf
15
+ acf = @xiu.acf
16
+
17
+ assert_equal 14, acf.length
18
+
19
+ # test the first few autocorrelations
20
+ assert_in_delta 1.0, acf[0], 0.0001
21
+ assert_in_delta 0.852, acf[1], 0.001
22
+ assert_in_delta 0.669, acf[2], 0.001
23
+ assert_in_delta 0.486, acf[3], 0.001
24
+ end
25
+
26
+ def test_lag
27
+ #test of default lag
28
+ lag1 = @xiu.lag
29
+
30
+ assert_in_delta 16.66, lag1[lag1.size - 1], 0.001
31
+ assert_in_delta 16.36, lag1[lag1.size - 2], 0.001
32
+
33
+ #test with different lagging unit
34
+ lag2 = @xiu.lag(2)
35
+
36
+ assert_in_delta 16.36, lag2[lag2.size - 1], 0.001
37
+ assert_in_delta 16.56, lag2[lag2.size - 2], 0.001
38
+ end
39
+
40
+ def test_delta
41
+ diff = @xiu.diff
42
+
43
+ assert_in_delta 0.11, diff[@xiu.size - 1], 0.001
44
+ assert_in_delta 0.30, diff[@xiu.size - 2], 0.001
45
+ assert_in_delta -0.20, diff[@xiu.size - 3], 0.001
46
+ end
47
+
48
+ def test_ma
49
+ # test default
50
+ ma10 = @xiu.ma
51
+
52
+ assert_in_delta ma10[-1], 16.897, 0.001
53
+ assert_in_delta ma10[-5], 17.233, 0.001
54
+ assert_in_delta ma10[-10], 17.587, 0.001
55
+
56
+ # test with a different lookback period
57
+ ma5 = @xiu.ma 5
58
+
59
+ assert_in_delta ma5[-1], 16.642, 0.001
60
+ assert_in_delta ma5[-10], 17.434, 0.001
61
+ assert_in_delta ma5[-15], 17.74, 0.001
62
+ end
63
+
64
+ def test_ema
65
+ # test default
66
+ ema10 = @xiu.ema
67
+
68
+ assert_in_delta ema10[-1], 16.87187, 0.00001
69
+ assert_in_delta ema10[-5], 17.19187, 0.00001
70
+ assert_in_delta ema10[-10], 17.54918, 0.00001
71
+
72
+ # test with a different lookback period
73
+ ema5 = @xiu.ema 5
74
+
75
+ assert_in_delta ema5[-1], 16.71299, 0.0001
76
+ assert_in_delta ema5[-10], 17.49079, 0.0001
77
+ assert_in_delta ema5[-15], 17.70067, 0.0001
78
+
79
+ # test with a different smoother
80
+ ema_w = @xiu.ema 10, true
81
+
82
+ assert_in_delta ema_w[-1], 17.08044, 0.00001
83
+ assert_in_delta ema_w[-5], 17.33219, 0.00001
84
+ assert_in_delta ema_w[-10], 17.55810, 0.00001
85
+ end
86
+
87
+ def test_macd
88
+ # MACD uses a lot more data than the other ones, so we need a bigger vector
89
+ data = File.readlines(File.dirname(__FILE__) + "/fixtures/stock_data.csv").map(&:to_f).to_time_series
90
+
91
+ macd, signal = data.macd
92
+
93
+ # check the MACD
94
+ assert_in_delta 3.12e-4, macd[-1], 1e-6
95
+ assert_in_delta -1.07e-2, macd[-10], 1e-4
96
+ assert_in_delta -5.65e-3, macd[-20], 1e-5
97
+
98
+ # check the signal
99
+ assert_in_delta -0.00628, signal[-1], 1e-5
100
+ assert_in_delta -0.00971, signal[-10], 1e-5
101
+ assert_in_delta -0.00338, signal[-20], 1e-5
102
+ end
103
+ end
data/test/test_wald.rb ADDED
@@ -0,0 +1,71 @@
1
+ require(File.expand_path(File.dirname(__FILE__)+'/helper.rb'))
2
+
3
+ class StatsampleWaldTest < MiniTest::Unit::TestCase
4
+ # Wald test is useful to test a series of n acf with Chi-square
5
+ # degree of freedom. It is extremely useful to test fit the fit of
6
+ # an ARIMA model to test the residuals.
7
+
8
+ include Statsample::TimeSeries
9
+ include Statsample::Shorthand
10
+ include Distribution
11
+
12
+ def setup
13
+ #create time series to evaluate later
14
+ @wald = 100.times.map { rand(100) }.to_ts
15
+ end
16
+
17
+ def sum_of_squares_of_acf_series(lags)
18
+ #perform sum of squares for a series of acf with specified lags
19
+ acf = @wald.acf(lags)
20
+ return acf.map { |x| x ** 2 }.inject(:+)
21
+ end
22
+
23
+ def chisquare_cdf(sum_of_squares, lags)
24
+ 1 - ChiSquare.cdf(sum_of_squares, lags)
25
+ end
26
+
27
+
28
+ def test_wald_with_5_lags
29
+ #number of lags for acf = 5
30
+ lags = 5
31
+ sum_of_squares = sum_of_squares_of_acf_series(lags)
32
+ assert_in_delta chisquare_cdf(sum_of_squares, lags), 1, 0.05
33
+ assert_equal @wald.acf(lags).size, lags + 1
34
+ end
35
+
36
+
37
+ def test_wald_with_10_lags
38
+ #number of lags for acf = 10
39
+ lags = 10
40
+ sum_of_squares = sum_of_squares_of_acf_series(lags)
41
+ assert_in_delta chisquare_cdf(sum_of_squares, lags), 1, 0.05
42
+ assert_equal @wald.acf(lags).size, lags + 1
43
+ end
44
+
45
+
46
+ def test_wald_with_15_lags
47
+ #number of lags for acf = 15
48
+ lags = 15
49
+ sum_of_squares = sum_of_squares_of_acf_series(lags)
50
+ assert_in_delta chisquare_cdf(sum_of_squares, lags), 1, 0.05
51
+ assert_equal @wald.acf(lags).size, lags + 1
52
+ end
53
+
54
+
55
+ def test_wald_with_20_lags
56
+ #number of lags for acf = 20
57
+ lags = 20
58
+ sum_of_squares = sum_of_squares_of_acf_series(lags)
59
+ assert_in_delta chisquare_cdf(sum_of_squares, lags), 1, 0.05
60
+ assert_equal @wald.acf(lags).size, lags + 1
61
+ end
62
+
63
+
64
+ def test_wald_with_25_lags
65
+ #number of lags for acf = 25
66
+ lags = 25
67
+ sum_of_squares = sum_of_squares_of_acf_series(lags)
68
+ assert_in_delta chisquare_cdf(sum_of_squares, lags), 1, 0.05
69
+ assert_equal @wald.acf(lags).size, lags + 1
70
+ end
71
+ end