A scalar process is said to have long memory if its spectral density at frequency \(λ\) is proportional to \(λ^{-2d}\) \((d ≠ 0)\) as \(\lambda \rightarrow 0\) . Long memory processes are characterized by slowly decaying autocorrelations and unbounded spectral densities at the origin. A prominent example is the \(ARFIMA(p,d,q)\) model
\[ A(L)(1-L)^d x_t = B(L)\epsilon_t \]
However, apparent long memory can arise from two fundamentally different sources:
Distinguishing between these cases is crucial
This package implements the test proposed by Qu (2011), which exploits the fact that genuine and spurious long memory exhibit different spectral properties across frequency bands.
The test statistic is based on the derivatives of the local Whittle likelihood function and is of the Kolmogorov-Smirnov type. It does not require specifying:
Load the package:
The package includes realized volatility data for Japanese Yen/US
dollar spot exchange rates from Dec 1, 1986 to Jun 30, 1999
(RV5min).
data(RV5min)
n <- length(RV5min)
cat("Sample size:", n, "\n")
#> Sample size: 2960
cat("Mean:", round(mean(RV5min), 4), "\n")
#> Mean: 0.6671
cat("Std. dev:", round(sd(RV5min), 4), "\n")
#> Std. dev: 0.889Visualize the series:
plot(RV5min, type = "l",
main = "Realized Volatility: Japanese Yen/US Dollar",
xlab = "Time (days)", ylab = "Log RV",
col = "steelblue", lwd = 0.8)
grid()The persistent fluctuations and high autocorrelations suggest possible long memory, but we need to test whether this is genuine or spurious.
The Longmemorytest() function performs the hypothesis
test:
H₀: x_t is a stationary long-memory process
H₁: x_t is affected by level shifts or smooth trends
(spurious long memory)
result <- Longmemorytest(log(RV5min), demean = TRUE, alpha = 0.05)
#>
#> --- Spurious Long Memory Test (Qu, 2011) ---
#> n = 2960, m = 269 (n^0.7), alpha = 5.00%
#> d estimate: 0.4739
#>
#> epsilon = 0.02: W = 0.414 (cv = 1.252) Does Not Reject
#> epsilon = 0.05: W = 0.370 (cv = 1.155) Does Not Reject
#> --------------------------------------------The test provides two test statistics with different trimming parameters \(\epsilon = 0.02, 0.05\)
Interpretation:
The test uses:
For the RV5min data, both tests fail to reject at the 5% level, suggesting the long memory is genuine.
Extract detailed results programmatically:
# Local Whittle estimates
result$d_estimate
#> [1] 0.4738663
# Test statistics
result$test_stat_eps02 # W statistic (ε = 0.02)
#> [1] 0.4143006
result$test_stat_eps05 # W statistic (ε = 0.05)
#> [1] 0.3696945
# Critical values
result$critical_value_eps02
#> [1] 1.252
result$critical_value_eps05
#> [1] 1.155
# Rejection decisions
result$reject_eps02
#> [1] FALSE
result$reject_eps05
#> [1] FALSEWe examine two scenarios to illustrate the test’s behavior under different DGP.
Generate an \(ARFIMA(0, 0.4, 0)\) process:
library(fracdiff)
set.seed(123)
# Simulate ARFIMA(0, d, 0) with d = 0.4
x_genuine <- fracdiff.sim(n = 1000, d = 0.4)$series
# Visualize
oldpar <- par(mfrow = c(1, 2))
plot(x_genuine, type = "l", main = "ARFIMA(0, 0.4, 0) Series",
ylab = "Value", col = "darkgreen", lwd = 0.8)
grid()
# ACF shows hyperbolic decay
acf(x_genuine, main = "ACF: Genuine Long Memory",
col = "darkgreen", lwd = 2)Apply the test:
result_genuine <- Longmemorytest(x_genuine, demean = TRUE, alpha = 0.05,
print_results = TRUE)
#>
#> --- Spurious Long Memory Test (Qu, 2011) ---
#> n = 1000, m = 126 (n^0.7), alpha = 5.00%
#> d estimate: 0.3637
#>
#> epsilon = 0.02: W = 0.637 (cv = 1.252) Does Not Reject
#> epsilon = 0.05: W = 0.637 (cv = 1.155) Does Not Reject
#> --------------------------------------------Result: Both tests fail to reject \(H₀\), correctly identifying genuine long memory.
Generate white noise with an abrupt level shift:
set.seed(456)
n <- 1000
shift_point <- 500
# White noise with level shift
x_spurious <- rnorm(n)
x_spurious[shift_point:n] <- x_spurious[shift_point:n] + 3
# Visualize
oldpar <- par(mfrow = c(1, 2))
plot(x_spurious, type = "l", main = "White Noise + Level Shift",
ylab = "Value", col = "darkred", lwd = 0.8)
abline(v = shift_point, lty = 2, col = "blue", lwd = 2)
grid()
# ACF shows spurious persistence
acf(x_spurious, main = "ACF: Spurious Long Memory",
col = "darkred", lwd = 2)Apply the test:
result_spurious <- Longmemorytest(x_spurious, demean = TRUE, alpha = 0.05)
#>
#> --- Spurious Long Memory Test (Qu, 2011) ---
#> n = 1000, m = 126 (n^0.7), alpha = 5.00%
#> d estimate: 0.5045
#>
#> epsilon = 0.02: W = 3.152 (cv = 1.252) **REJECT**
#> epsilon = 0.05: W = 2.922 (cv = 1.155) **REJECT**
#> --------------------------------------------Result: Both tests reject \(H₀\), correctly detecting spurious long memory caused by the level shift.
The test supports \(α ∈ \{0.01, 0.025, 0.05, 0.10\}\). Critical values are tabulated from simulated asymptotic distributions:
| Alpha | ε = 0.02 | ε = 0.05 |
|---|---|---|
| 10% | 1.118 | 1.022 |
| 5% | 1.252 | 1.155 |
| 2.5% | 1.374 | 1.277 |
| 1% | 1.517 | 1.426 |
Primary Reference:
Qu, Z. (2011). A Test Against Spurious Long Memory. Journal of Business & Economic Statistics, 29(3), 423-438. DOI: 10.1198/jbes.2010.09153
sessionInfo()
#> R version 4.4.3 (2025-02-28 ucrt)
#> Platform: x86_64-w64-mingw32/x64
#> Running under: Windows 11 x64 (build 26100)
#>
#> Matrix products: default
#>
#>
#> locale:
#> [1] LC_COLLATE=C
#> [2] LC_CTYPE=English_United States.utf8
#> [3] LC_MONETARY=English_United States.utf8
#> [4] LC_NUMERIC=C
#> [5] LC_TIME=English_United States.utf8
#>
#> time zone: America/New_York
#> tzcode source: internal
#>
#> attached base packages:
#> [1] stats graphics grDevices utils datasets methods base
#>
#> other attached packages:
#> [1] fracdiff_1.5-3 SpuriousMemory_1.0.0
#>
#> loaded via a namespace (and not attached):
#> [1] digest_0.6.37 R6_2.6.1 fastmap_1.2.0 xfun_0.53
#> [5] cachem_1.1.0 knitr_1.50 htmltools_0.5.8.1 rmarkdown_2.29
#> [9] lifecycle_1.0.4 cli_3.6.4 sass_0.4.9 jquerylib_0.1.4
#> [13] compiler_4.4.3 rstudioapi_0.17.1 tools_4.4.3 evaluate_1.0.3
#> [17] bslib_0.9.0 yaml_2.3.10 rlang_1.1.5 jsonlite_1.9.1