Skip to content

Commit 539b19f

Browse files
author
LuizFCDuarte
committed
✨ Add julia implementations of ocsb and kpss
1 parent ecf75bc commit 539b19f

File tree

2 files changed

+53
-14
lines changed

2 files changed

+53
-14
lines changed

src/statistical_tests.jl

Lines changed: 46 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,39 @@ function interpolate(x::Vector{T}, y::Vector{T}) where T <: AbstractFloat
4040
end
4141
end
4242

43+
"""
44+
_kpss_autolag(resids::Vector{T}, nobs::Int) where T <: AbstractFloat
45+
46+
Computes the number of lags for covariance matrix estimation in KPSS test
47+
using method of Hobijn et al (1998). See also Andrews (1991), Newey & West
48+
(1994), and Schwert (1989). Assumes Bartlett / Newey-West kernel.
49+
50+
# Arguments
51+
- `resids::Vector{T}`: Residuals from the KPSS regression
52+
- `nobs::Int`: Number of observations
53+
54+
# Returns
55+
- `Int`: Number of lags to use in the KPSS test
56+
"""
57+
function _kpss_autolag(resids::Vector{T}, nobs::Int) where T <: AbstractFloat
58+
covlags = Int(floor(nobs^(2.0/9.0)))
59+
s0 = sum(resids.^2) / nobs
60+
s0 = s0 > 0 ? s0 : 1e-5
61+
s1 = 0.0
62+
63+
for i in 1:covlags
64+
# Calculate dot product of shifted residuals
65+
resids_prod = dot(resids[(i+1):end], resids[1:(nobs-i)])
66+
resids_prod /= nobs / 2.0
67+
s0 += resids_prod
68+
s1 += i * resids_prod
69+
end
70+
71+
s_hat = s1 / s0
72+
pwr = 1.0 / 3.0
73+
gamma_hat = 1.1447 * (s_hat * s_hat)^pwr
74+
return max(Int(floor(gamma_hat * nobs^pwr)),1)
75+
end
4376

4477
"""
4578
kpss_test(y::Vector{T}; regression::Symbol=:c, nlags::Union{Symbol,Int}=:legacy) where T <: AbstractFloat
@@ -71,7 +104,7 @@ Table 1 of Kwiatkowski et al. (1992).
71104
- Newey & West (1994). Automatic lag selection in covariance matrix estimation. Review of Economic Studies, 61: 631-653.
72105
- Schwert (1989). Tests for unit roots: A Monte Carlo investigation. Journal of Business and Economic Statistics, 7(2): 147-159.
73106
"""
74-
function kpss_test(y::Vector{Fl}; regression::Symbol=:c, nlags::Union{Symbol,Int}=:legacy) where Fl
107+
function kpss_test(y::Vector{Fl}; regression::Symbol=:c, nlags::Union{Symbol,Int}=:auto) where Fl
75108
nobs = length(y)
76109

77110
# Set critical values based on regression type
@@ -103,37 +136,42 @@ function kpss_test(y::Vector{Fl}; regression::Symbol=:c, nlags::Union{Symbol,Int
103136
# Compute number of lags
104137
if nlags == :legacy
105138
nlags = min(Int(ceil(12.0 * (nobs/100.0)^0.25)), nobs - 1)
139+
140+
elseif nlags == :auto
141+
nlags = _kpss_autolag(residuals, nobs)
106142
elseif isa(nlags, Integer)
107143
if nlags >= nobs
108144
throw(ArgumentError("nlags must be < number of observations"))
109145
end
110146
else
111-
throw(ArgumentError("nlags must be :legacy or an integer"))
147+
throw(ArgumentError("nlags must be :legacy, :auto or an integer"))
112148
end
113149

114150
# Compute KPSS test statistic
115151
partial_sums = cumsum(residuals)
116152
eta = sum(partial_sums.^2) / (nobs^2)
117153

118154
# Compute s^2 using Newey-West estimator
119-
s2 = var(residuals) # Initial variance (γ₀)
155+
s2 = sum(residuals.^2) # Initial variance (γ₀)
120156

121157
# Add autocovariance terms
122158
for lag in 1:nlags
123159
w = 1.0 - lag/(nlags + 1) # Bartlett kernel weights
124-
γₖ = sum(residuals[lag+1:end] .* residuals[1:end-lag]) / nobs
160+
γₖ = sum(residuals[lag+1:end] .* residuals[1:end-lag])
125161
s2 += 2.0 * w * γₖ
126162
end
127163

164+
s2 = s2 / nobs
165+
128166
# Compute test statistic
129-
kpss_stat::Float32 = eta / s2
167+
kpss_stat::Float64 = (s2 == 0.0) ? 0.0 : eta / s2
130168

131169
# Compute p-value using interpolation
132-
crit_vals::Vector{Float32} = [critical_values[0.10], critical_values[0.05], critical_values[0.025], critical_values[0.01]]
133-
pvals::Vector{Float32} = [0.10, 0.05, 0.025, 0.01]
170+
crit_vals::Vector{Float64} = [critical_values[0.10], critical_values[0.05], critical_values[0.025], critical_values[0.01]]
171+
pvals::Vector{Float64} = [0.10, 0.05, 0.025, 0.01]
134172

135173
# compute p-value as kpss_stat evaluated at the interpolation of crit_vals and pvals
136-
p_value = interpolate(crit_vals, pvals)(kpss_stat)
174+
p_value::Float64 = interpolate(crit_vals, pvals)(kpss_stat)
137175

138176
return Dict(
139177
"test_statistic" => kpss_stat,

src/utils.jl

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -225,16 +225,17 @@ function selectIntegrationOrder(
225225
seasonality::Int,
226226
test::String,
227227
) where {Fl<:AbstractFloat}
228-
if test == "kpss"
228+
if test == "kpssStateSpace"
229229
return StateSpaceModels.repeated_kpss_test(y, maxd, D, seasonality)
230-
elseif test == "kpssSarimax"
231-
for d in 0:maxd
232-
diffSeries = differentiate(y, d, D, seasonality)
230+
elseif test == "kpss"
231+
for i in 0:maxd
232+
diffSeries = differentiate(y, i, D, seasonality)
233233
result = kpss_test(diffSeries)
234-
if result["p_value"] <= 0.05
235-
return d
234+
if result["p_value"] > 0.05
235+
return i
236236
end
237237
end
238+
return maxd
238239
end
239240

240241
throw(ArgumentError("The test $test is not supported"))

0 commit comments

Comments
 (0)