Skip to main content

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.

Metadata

Release files for urvol 1.1.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for urvol 1.1.0
File Size Uploaded Provenance
urvol-1.1.0.tar.gz 22.7 kB Without provenance Details

Built distribution (wheel)

Table of built distributions (wheels) for urvol 1.1.0
File Interpreter ABI Platform
urvol-1.1.0-py3-none-any.whl Python 3 none any Details

Total release size: 45.5 kB

Release files / urvol-1.1.0.tar.gz

Download URL urvol-1.1.0.tar.gz
Size 22.7 kB
Tags Source
SHA-256 checksum
How to use checksums
6e352e0d2e11161bd89aaa0e2bfd9e6855c0ec2110cfff659f1112d42da540cf
BLAKE2b-256 checksum
How to use checksums
2ae58d407b9900b2f648947536b63d262cec1c0e7e988787643884269f82c325
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.2.0 CPython/3.11.0

Release files / urvol-1.1.0-py3-none-any.whl

Download URL urvol-1.1.0-py3-none-any.whl
Size 22.8 kB
Tags Python 3
SHA-256 checksum
How to use checksums
ce45b023bcacf1d1154b66bf60c4aa65f92d5a1a1a52d81df03ffc223dc3f8cf
BLAKE2b-256 checksum
How to use checksums
033fd2beb73fd279dc664d0b2c7accbace29a0fab7507ffe57733c2ed86dd2b1
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.2.0 CPython/3.11.0

Security for urvol 1.1.0

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

No provenance found

None of the files in this release were uploaded with provenance metadata.

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Release history Release notifications | RSS feed

This release

1.1.0 This release

2 release files No provenance

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page