Skip to main content

Unit Root Tests for Time Series with Non-Stationary Volatility

Project description

urvol: Unit Root Tests for Time Series with Non-Stationary Volatility

Python 3.8+ License: MIT

A comprehensive Python library implementing unit root tests robust to non-stationary volatility, based on the seminal papers:

  1. Cavaliere & Taylor (2008): "Bootstrap Unit Root Tests for Time Series with Nonstationary Volatility", Econometric Theory, 24, 43-71.

  2. Cavaliere & Taylor (2007): "Testing for unit roots in time series models with non-stationary volatility", Journal of Econometrics, 140, 919-947.

Features

  • Wild Bootstrap Tests: Robust to heteroskedasticity via wild bootstrap methodology
  • Simulation-Based Tests: Uses estimated variance profiles for critical value simulation
  • M Statistics: MZa, MZt, and MSB unit root tests with local GLS detrending
  • Volatility Models: Built-in models for single/multiple breaks, trending, smooth transitions
  • Critical Values: Automated generation for any variance profile
  • Publication-Ready: Designed for academic research with full documentation

Installation

pip install urvol

Or install from source:

git clone https://github.com/merwanroudane/urvol.git
cd urvol
pip install -e .

Quick Start

Wild Bootstrap Test

import numpy as np
from urvol import wild_bootstrap_test

# Generate data with volatility break
T = 200
np.random.seed(42)
sigma = np.where(np.arange(T+1) < 100, 1.0, 0.5)
errors = np.random.normal(0, sigma[:-1])
X = np.concatenate([[0], np.cumsum(errors)])

# Perform wild bootstrap test
results = wild_bootstrap_test(X, statistic="MZa", N=999)
print(f"Statistic: {results['statistic']:.3f}")
print(f"p-value: {results['p_value']:.3f}")
print(f"Reject at 5%: {results['reject_05']}")

Simulation-Based Test

from urvol import simulation_based_test

# Perform simulation-based test
results = simulation_based_test(X, statistic="MZa", N=10000)
print(f"Statistic: {results['statistic']:.3f}")
print(f"Critical value (5%): {results['critical_value']:.3f}")
print(f"Reject: {results['reject']}")

Variance Profile Estimation

from urvol import estimate_variance_profile
import matplotlib.pyplot as plt

# Estimate variance profile
eta_hat = estimate_variance_profile(X)

# Plot estimated profile
s_grid = np.linspace(0, 1, 100)
plt.plot(s_grid, eta_hat(s_grid), label='Estimated η(s)')
plt.plot(s_grid, s_grid, '--', label='Homoskedastic')
plt.xlabel('s')
plt.ylabel('η(s)')
plt.legend()
plt.show()

Methodological Background

The Problem

Standard unit root tests (ADF, PP, etc.) assume homoskedastic errors. However, many economic and financial time series exhibit:

  • Volatility breaks (e.g., Great Moderation, COVID-19)
  • Trending volatility (changing uncertainty over time)
  • Smooth volatility transitions

Under non-stationary volatility, standard tests can be severely size-distorted, leading to unreliable inference.

The Solution

This library implements two complementary approaches:

1. Wild Bootstrap (Cavaliere & Taylor 2008)

The wild bootstrap preserves heteroskedasticity in resampled data:

u*_t = û_t × w_t,  w_t ~ iid N(0,1)

This yields asymptotically valid tests for a wide class of volatility processes without requiring a parametric specification.

2. Simulation-Based Tests (Cavaliere & Taylor 2007)

Estimates the variance profile η(s) and simulates critical values from the appropriate limiting distribution:

η(s) = (ω²)^{-1} ∫₀ˢ ω(r)² dr

Provides asymptotically similar tests for any bounded volatility process.

API Reference

Main Functions

wild_bootstrap_test(X, statistic="MZa", trend="c", N=999, ...)

Wild bootstrap unit root test robust to non-stationary volatility.

Parameters:

  • X: Time series data (T+1,) array
  • statistic: "MZa", "MZt", or "MSB"
  • trend: "c" (constant) or "ct" (constant + trend)
  • N: Number of bootstrap replications

Returns: Dictionary with statistic, p-value, critical values, rejection decisions

simulation_based_test(X, statistic="MZa", trend="c", N=10000, ...)

Simulation-based test using estimated variance profile.

Parameters:

  • X: Time series data
  • N: Number of simulations for critical values
  • T_sim: Grid size for Brownian motion simulation

Returns: Dictionary with results and estimated variance profile

estimate_variance_profile(X, trend="c", method="diff")

Estimate the variance profile η(s).

Returns: Callable function η̂(s) on [0,1]

Volatility Models

from urvol.volatility_models import (
    single_break,
    double_break, 
    trending_volatility,
    smooth_transition,
)

# Single break at τ=0.3 with δ=3
omega = single_break(tau=0.3, delta=3.0)

# Evaluate volatility at specific points
s = np.linspace(0, 1, 100)
sigma = omega(s)

Critical Values

from urvol import get_critical_value, generate_critical_value_table

# Get homoskedastic critical value
cv = get_critical_value("MZa", trend="c", alpha=0.05)

# Generate table for specific variance profile
omega = single_break(tau=0.5, delta=3.0)
eta = lambda s: np.where(s < 0.5, s/0.55, (s-0.45)/0.55)
cv_table = generate_critical_value_table(eta, N=50000)

Examples

Example 1: Producer Price Inflation

Replicating the empirical application from Cavaliere & Taylor (2007):

import pandas as pd
from urvol import wild_bootstrap_test, estimate_variance_profile

# Load data (Stock-Watson database)
data = pd.read_csv("PWFSA.csv")
X = data['inflation'].values

# Estimate variance profile
eta_hat = estimate_variance_profile(X, trend="c")

# Perform tests
boot_results = wild_bootstrap_test(X, statistic="MSB", N=999)
print(f"MSB Bootstrap p-value: {boot_results['p_value']:.3f}")

Example 2: Monte Carlo Simulation

Evaluating test size and power:

from urvol import wild_bootstrap_test
from urvol.volatility_models import single_break
import numpy as np

def monte_carlo_size(T=200, tau=0.8, delta=3.0, N_mc=1000, N_boot=499):
    """Compute empirical size under H0: α=1."""
    rejections = 0
    omega = single_break(tau=tau, delta=delta)
    
    for _ in range(N_mc):
        # Generate data under unit root with volatility break
        sigma = omega(np.linspace(0, 1, T+1))
        errors = np.random.normal(0, sigma[:-1])
        X = np.concatenate([[0], np.cumsum(errors)])
        
        # Test
        results = wild_bootstrap_test(X, N=N_boot)
        rejections += results['reject_05']
    
    return rejections / N_mc

size = monte_carlo_size()
print(f"Empirical size: {size:.3f}")

Theory and Asymptotic Results

Theorem 1 (Limiting Distributions under Heteroskedasticity)

Under non-stationary volatility ω(s), the M statistics converge to:

MZa → 0.5(B̃²_η(1) - B̃²_η(0) - 1) / ∫₀¹ B̃²_η(s)ds

where B̃_η|Z is the projection of the variance-transformed Brownian motion.

Theorem 2 (Consistency of Variance Profile Estimator)

sup_{s∈[0,1]} |η̂(s) - η(s)| →_p 0

Theorem 3 (Bootstrap Validity)

The wild bootstrap statistics satisfy:

MZa* →_p MZa^{c,p,η}  (in probability)

ensuring asymptotically correct inference.

Testing

Run the test suite:

pytest tests/

With coverage:

pytest --cov=urvol tests/

Contributing

Contributions are welcome! Please:

  1. Fork the repository
  2. Create a feature branch
  3. Add tests for new functionality
  4. Ensure all tests pass
  5. Submit a pull request

Citation

If you use this library in your research, please cite:

@article{cavaliere2008bootstrap,
  title={Bootstrap unit root tests for time series with nonstationary volatility},
  author={Cavaliere, Giuseppe and Taylor, A.M. Robert},
  journal={Econometric Theory},
  volume={24},
  number={1},
  pages={43--71},
  year={2008},
  publisher={Cambridge University Press}
}

@article{cavaliere2007testing,
  title={Testing for unit roots in time series models with non-stationary volatility},
  author={Cavaliere, Giuseppe and Taylor, A.M. Robert},
  journal={Journal of Econometrics},
  volume={140},
  number={2},
  pages={919--947},
  year={2007},
  publisher={Elsevier}
}

@software{urvol2024,
  author = {Roudane, Merwan},
  title = {urvol: Unit Root Tests for Non-Stationary Volatility},
  year = {2024},
  url = {https://github.com/merwanroudane/urvol}
}

License

This project is licensed under the MIT License - see the LICENSE file for details.

Author

Dr Merwan Roudane

Acknowledgments

This library implements the methodology developed by Giuseppe Cavaliere and A.M. Robert Taylor. All credit for the theoretical contributions goes to the original authors.

References

Cavaliere, G., Taylor, A.M.R. (2008). "Bootstrap Unit Root Tests for Time Series with Nonstationary Volatility". Econometric Theory 24, 43-71.

Cavaliere, G., Taylor, A.M.R. (2007). "Testing for unit roots in time series models with non-stationary volatility". Journal of Econometrics 140, 919-947.

Elliott, G., Rothenberg, T.J., Stock, J.H. (1996). "Efficient Tests for an Autoregressive Unit Root". Econometrica 64, 813-836.

Ng, S., Perron, P. (2001). "Lag Length Selection and the Construction of Unit Root Tests with Good Size and Power". Econometrica 69, 1519-1554.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

urvol-1.1.0.tar.gz (22.7 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

urvol-1.1.0-py3-none-any.whl (22.8 kB view details)

Uploaded Python 3

File details

Details for the file urvol-1.1.0.tar.gz.

File metadata

  • Download URL: urvol-1.1.0.tar.gz
  • Upload date:
  • Size: 22.7 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.11.0

File hashes

Hashes for urvol-1.1.0.tar.gz
Algorithm Hash digest
SHA256 6e352e0d2e11161bd89aaa0e2bfd9e6855c0ec2110cfff659f1112d42da540cf
MD5 e30264f9f5c4570b2eb38d56d6ab9929
BLAKE2b-256 2ae58d407b9900b2f648947536b63d262cec1c0e7e988787643884269f82c325

See more details on using hashes here.

File details

Details for the file urvol-1.1.0-py3-none-any.whl.

File metadata

  • Download URL: urvol-1.1.0-py3-none-any.whl
  • Upload date:
  • Size: 22.8 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.11.0

File hashes

Hashes for urvol-1.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 ce45b023bcacf1d1154b66bf60c4aa65f92d5a1a1a52d81df03ffc223dc3f8cf
MD5 0b5fcdd714df21299896348c41203c6d
BLAKE2b-256 033fd2beb73fd279dc664d0b2c7accbace29a0fab7507ffe57733c2ed86dd2b1

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page