Skip to main content

BoundedTest: GLS-based Unit Root Tests for Bounded Processes

Python Version License

A Python implementation of unit root tests for bounded time series based on Carrion-i-Silvestre and Gadea (2013).

Overview

boundedtest provides comprehensive tools for testing unit roots in time series that are constrained within bounds. The package implements:

  • GLS-detrending with bound-specific non-centrality parameter κ̅(c,c̄)
  • OLS-detrending for comparison
  • M-type test statistics: MZα, MSB, and MZt
  • Parametric and non-parametric long-run variance estimation
  • Critical values tailored to different bound configurations

Reference

This package implements the methodology from:

Carrion-i-Silvestre, J.L. and Gadea, M.D. (2013). "GLS-based unit root tests for bounded processes." Economics Letters, 120(2), 184-187.

Installation

pip install boundedtest

Or install from source:

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

Quick Start

import numpy as np
from boundedtest import bounded_unit_root_test

# Generate bounded data
np.random.seed(42)
data = np.cumsum(np.random.randn(200))
data = np.clip(data, -10, 10)  # Apply bounds

# Test for unit root with GLS-BOUNDS method (recommended)
result = bounded_unit_root_test(
    data=data,
    bounds=(-10, 10),
    statistic='mz_alpha',
    detrending='gls_bounds',
    lrv_method='np'
)

print(result)

Main Features

1. Detrending Methods

  • 'ols': Standard OLS detrending
  • 'gls_ers': GLS detrending with standard κ = -7 (Elliott et al., 1996)
  • 'gls_bounds': GLS detrending with bound-specific κ̅(c,c̄) (recommended)

2. Test Statistics

  • MZα: Modified Phillips-Perron statistic
  • MSB: Modified Sargan-Bhargava statistic
  • MZt: Modified t-statistic

3. LRV Estimation

  • Non-parametric ('np'): Newey-West with automatic bandwidth selection
  • Parametric ('ar'): AR-based with MAIC lag selection (Ng & Perron, 2001)

Examples

Example 1: Basic Usage

import numpy as np
from boundedtest import bounded_unit_root_test

# Generate data
np.random.seed(123)
T = 200
data = np.cumsum(np.random.randn(T))
bounds = (-5, 5)
data = np.clip(data, bounds[0], bounds[1])

# Run test
result = bounded_unit_root_test(
    data=data,
    bounds=bounds,
    detrending='gls_bounds'
)

print(result)
print(f"\nReject H0 at 5%: {result.reject_5pct}")

Example 2: Compare All Test Statistics

results = bounded_unit_root_test(
    data=data,
    bounds=bounds,
    statistic='all',  # Compute all statistics
    detrending='gls_bounds'
)

# Results is a dictionary
for stat_name, result in results.items():
    print(f"\n{stat_name}:")
    print(f"  Statistic: {result.statistic:.4f}")
    print(f"  Critical (5%): {result.critical_values['5%']:.4f}")
    print(f"  Reject H0: {result.reject_5pct}")

Example 3: Compare Detrending Methods

methods = ['ols', 'gls_ers', 'gls_bounds']

for method in methods:
    result = bounded_unit_root_test(
        data=data,
        bounds=bounds,
        statistic='mz_alpha',
        detrending=method
    )
    print(f"\n{method.upper()}:")
    print(f"  MZα = {result.statistic:.4f}")
    print(f"  Reject H0 = {result.reject_5pct}")

Example 4: With Pandas DataFrame

import pandas as pd

# Create DataFrame
df = pd.DataFrame({
    'date': pd.date_range('2020-01-01', periods=200, freq='D'),
    'series': data
})

# Test using the series
result = bounded_unit_root_test(
    data=df['series'],
    bounds=bounds,
    detrending='gls_bounds'
)

Example 5: Unemployment Rate (0-100% bounded)

# Unemployment rate bounded between 0 and 100
unemployment = np.random.uniform(3, 10, size=100)  # Example data
unemployment += np.cumsum(np.random.randn(100)) * 0.2

result = bounded_unit_root_test(
    data=unemployment,
    bounds=(0, 100),
    detrending='gls_bounds'
)

Understanding Results

The output includes:

  • Test Statistic Value: The computed test statistic
  • Critical Values: At 1%, 5%, and 10% significance levels
  • Rejection Decision: Whether to reject the unit root null hypothesis
  • Bounds Information: Original bounds and estimated c-parameters
  • Non-centrality Parameter (κ̅): Used in GLS detrending
  • LRV Estimate: Estimated long-run variance

Interpretation

  • MZα and MZt: Left-tail tests. Reject H₀ if statistic < critical value.
  • MSB: Right-tail test. Reject H₀ if statistic > critical value.
  • H₀: Series has a unit root (is I(1))
  • H₁: Series is stationary (is I(0))

Advanced Usage

Generating Bounded Processes

from boundedtest.regulated_ou import generate_bounded_ar1

# Generate bounded AR(1) process
data = generate_bounded_ar1(
    T=200,
    bounds=(-5, 5),
    rho=1.0,  # Unit root
    sigma=1.0,
    burnin=100,
    seed=42
)

Custom Critical Values via Simulation

from boundedtest.regulated_ou import compute_critical_values

# Compute critical values for specific bounds
cv = compute_critical_values(
    c_lower=-0.5,
    c_upper=0.5,
    kappa=0,
    n_sim=10000,
    alpha=0.05
)

print(cv)

Accessing Individual Components

from boundedtest import (
    ols_detrend,
    gls_detrend,
    compute_mz_alpha,
    estimate_lrv_np,
    get_kappa
)

# Manual detrending
y_tilde, _, _ = ols_detrend(data)

# Compute LRV
from boundedtest.statistics import compute_ar1_residuals
residuals = compute_ar1_residuals(y_tilde)
s2 = estimate_lrv_np(residuals)

# Compute statistic
mz_alpha = compute_mz_alpha(y_tilde, s2)

API Reference

Main Function

bounded_unit_root_test(
    data,           # array-like: Time series data
    bounds,         # tuple: (lower_bound, upper_bound)
    statistic='mz_alpha',  # str: Test statistic to compute
    detrending='gls_bounds',  # str: Detrending method
    lrv_method='np',  # str: LRV estimation method
    deterministics='constant'  # str: Deterministic components
) -> BoundedTestResult

Monte Carlo Simulations

The package includes utilities for Monte Carlo experiments:

from boundedtest.regulated_ou import simulate_bounded_process

# Simulate under H0
data_h0 = simulate_bounded_process(
    T=200,
    b_lower=-5,
    b_upper=5,
    alpha=1.0,  # Unit root
    sigma=1.0
)

# Simulate under H1  
data_h1 = simulate_bounded_process(
    T=200,
    b_lower=-5,
    b_upper=5,
    alpha=0.95,  # Stationary
    sigma=1.0
)

Requirements

  • Python >= 3.8
  • NumPy >= 1.20.0
  • SciPy >= 1.7.0
  • Pandas >= 1.3.0
  • Statsmodels >= 0.13.0

Citation

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

@article{carrion2013gls,
  title={GLS-based unit root tests for bounded processes},
  author={Carrion-i-Silvestre, Josep Llu{\'\i}s and Gadea, Mar{\'\i}a Dolores},
  journal={Economics Letters},
  volume={120},
  number={2},
  pages={184--187},
  year={2013},
  publisher={Elsevier}
}

License

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

Author

Merwan Roudane
Email: merwanroudane920@gmail.com
GitHub: @merwanroudane

Contributing

Contributions are welcome! Please feel free to submit a Pull Request.

Acknowledgments

This implementation is based on the methodology developed by Josep Lluís Carrion-i-Silvestre and María Dolores Gadea. The original MATLAB code and methodological guidance were instrumental in developing this Python package.

See Also

  • Original paper: Economics Letters (2013)
  • Elliott, Rothenberg & Stock (1996): "Efficient Tests for an Autoregressive Unit Root"
  • Ng & Perron (2001): "Lag Length Selection and the Construction of Unit Root Tests"
  • Cavaliere (2005): "Limited Time Series with a Unit Root"

Metadata

Release files for boundedtest 1.0.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 boundedtest 1.0.0
File Size Uploaded Provenance
boundedtest-1.0.0.tar.gz 28.7 kB Without provenance Details

Built distribution (wheel)

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

Total release size: 53.6 kB

Release files / boundedtest-1.0.0.tar.gz

Download URL boundedtest-1.0.0.tar.gz
Size 28.7 kB
Tags Source
SHA-256 checksum
How to use checksums
89d74cb4bd95057f2cc84ca8b6006b5d884f144f2afab546fbcd80b45297d770
BLAKE2b-256 checksum
How to use checksums
45851b7d5f71069eda9a965a25cb19d893ea77645f758730c636d50dd6749728
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.2.0 CPython/3.13.7

Release files / boundedtest-1.0.0-py3-none-any.whl

Download URL boundedtest-1.0.0-py3-none-any.whl
Size 24.9 kB
Tags Python 3
SHA-256 checksum
How to use checksums
2110ac6292ca630cd3d89d01b99c6f4e961c4c8a9defd61ff0f47f8b856a3ce4
BLAKE2b-256 checksum
How to use checksums
0cfb3beb07103633711fb7feb575d4297717d41061772e65d73d4ba2d90684ec
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.2.0 CPython/3.13.7

Security for boundedtest 1.0.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.0.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