urvol 1.1.0
Unit Root Tests for Time Series with Non-Stationary Volatility
Requires Python: >=3.8
Keywords: econometrics, unit root, non-stationary volatility, bootstrap, time series

# urvol: Unit Root Tests for Time Series with Non-Stationary Volatility

[![Python 3.8+](https://img.shields.io/badge/python-3.8+-blue.svg)](https://www.python.org/downloads/)
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/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

```bash
pip install urvol
```

Or install from source:

```bash
git clone https://github.com/merwanroudane/urvol.git
cd urvol
pip install -e .
```

## Quick Start

### Wild Bootstrap Test

```python
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

```python
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

```python
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

```python
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

```python
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):

```python
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:

```python
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:

```bash
pytest tests/
```

With coverage:

```bash
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:

```bibtex
@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**
- Email: merwanroudane920@gmail.com
- GitHub: [@merwanroudane](https://github.com/merwanroudane)

## 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.