asymcaus
Asymmetric Causality Testing in Python
A Python implementation of the asymmetric causality test developed by Hatemi-J (2012). This package allows testing for Granger-causal relationships between positive and negative components of time series variables separately.
📚 Background
Traditional Granger causality tests assume that the causal impact of positive shocks is the same as negative shocks in absolute terms. However, economic agents often react differently to positive versus negative news. The asymmetric causality test addresses this limitation by decomposing variables into cumulative positive and negative changes and testing causality between these components separately.
🎯 Features
- Asymmetric Causality Testing: Test causal relationships between positive and negative components
- Bootstrap Critical Values: Leverage-adjusted bootstrap for robust inference under non-normality and ARCH effects
- Multiple Information Criteria: AIC, AICC, BIC/SBC, HQC, and Hatemi-J Criterion (HJC)
- Toda-Yamamoto Procedure: Handle integrated variables without pre-testing for cointegration
- Comprehensive Diagnostics: Multivariate normality tests, ARCH tests, and autocorrelation tests
- Unit Root Tests: ADF, KPSS, and Phillips-Perron tests
📦 Installation
pip install asymcaus
For development installation with all dependencies:
pip install asymcaus[full]
🚀 Quick Start
Basic Usage
import numpy as np
from asymcaus import asymmetric_causality_test
# Generate sample data (two I(1) series)
np.random.seed(42)
T = 500
z = np.cumsum(np.random.randn(T)) # Cause variable
y = np.cumsum(np.random.randn(T)) # Effect variable
# Test if positive shocks in Z cause positive shocks in Y
result = asymmetric_causality_test(
y=y,
z=z,
component='positive',
n_bootstrap=1000
)
# View results
print(result.summary())
Full Analysis (All Components)
from asymcaus import full_asymmetric_causality_analysis, print_full_analysis
# Test all combinations
results = full_asymmetric_causality_analysis(
y=y,
z=z,
var_names=('Oil Price', 'Stock Index'),
n_bootstrap=1000
)
# Print comprehensive summary
print(print_full_analysis(results))
With Unit Root Pre-testing
from asymcaus import determine_integration_order, asymmetric_causality_test
# Determine integration order
int_order_y, _ = determine_integration_order(y)
int_order_z, _ = determine_integration_order(z)
int_order = max(int_order_y, int_order_z)
print(f"Integration order: {int_order}")
# Use Toda-Yamamoto procedure
result = asymmetric_causality_test(
y=y,
z=z,
component='positive',
integration_order=int_order, # Add extra lags
criterion='hjc'
)
Diagnostic Tests
from asymcaus import run_all_diagnostics, print_diagnostics_summary
# After running causality test, check residual diagnostics
# (Assumes you have residuals from VAR estimation)
residuals = np.random.randn(100, 2) # Example residuals
diagnostics = run_all_diagnostics(residuals)
print(print_diagnostics_summary(diagnostics))
📖 API Reference
Main Functions
asymmetric_causality_test(y, z, component='positive', ...)
Perform the Hatemi-J (2012) asymmetric causality test.
Parameters:
y(array): Dependent variable (effect)z(array): Independent variable (cause)component(str): Type of component to test'positive': Test Z+ → Y+'negative': Test Z- → Y-'positive_to_negative': Test Z+ → Y-'negative_to_positive': Test Z- → Y+'original': Test without decomposition
criterion(str): Information criterion ('aic', 'bic', 'hqc', 'hjc')max_lag(int): Maximum lag order (default: 8)integration_order(int): Order of integration for Toda-Yamamoto (default: 0)n_bootstrap(int): Number of bootstrap replications (default: 1000)log_transform(bool): Apply log transformation (default: False)var_names(tuple): Names for variablesrandom_state(int): Random seed for reproducibility
Returns: AsymmetricCausalityResult object
full_asymmetric_causality_analysis(y, z, ...)
Run asymmetric causality tests for all component combinations.
cumulative_components(y, log_transform=False)
Calculate cumulative positive and negative components.
Unit Root Tests
adf_test(y, trend='c')- Augmented Dickey-Fuller testkpss_test(y, trend='c')- KPSS stationarity testpp_test(y, trend='c')- Phillips-Perron testdetermine_integration_order(y)- Automatic integration order detection
Diagnostic Tests
doornik_hansen_test(residuals)- Multivariate normality testmultivariate_arch_test(residuals)- ARCH effects testljung_box_test(residuals)- Autocorrelation testrun_all_diagnostics(residuals)- Run all diagnostics
📊 Example: Oil Prices and Stock Markets
Replicating the UAE market analysis from Hatemi-J (2012):
import numpy as np
from asymcaus import (
asymmetric_causality_test,
full_asymmetric_causality_analysis,
print_full_analysis,
determine_integration_order
)
# Load your data (oil prices and stock index)
# oil_price = ...
# stock_index = ...
# Example with simulated data
np.random.seed(2012)
T = 488
# Simulated I(1) processes
oil_price = np.cumsum(np.random.randn(T) * 0.02) + 4.5
stock_index = np.cumsum(np.random.randn(T) * 0.015) + 5.0
# Determine integration order
int_order, _ = determine_integration_order(oil_price)
print(f"Oil price integration order: {int_order}")
# Full asymmetric causality analysis
results = full_asymmetric_causality_analysis(
y=stock_index,
z=oil_price,
var_names=('Oil', 'Stock'),
integration_order=1,
n_bootstrap=1000,
criterion='hjc'
)
print(print_full_analysis(results))
📝 Interpretation of Results
The test evaluates the null hypothesis:
H₀: Variable Z does not Granger-cause variable Y
For asymmetric components:
- Positive components (Z+ → Y+): Do positive shocks in Z cause positive shocks in Y?
- Negative components (Z- → Y-): Do negative shocks in Z cause negative shocks in Y?
- Cross effects (Z+ → Y-, Z- → Y+): Do shocks of one sign cause opposite effects?
Decision Rule:
- If Wald statistic > Bootstrap critical value → Reject H₀ (causality exists)
- Bootstrap critical values are preferred over χ² critical values when data exhibits non-normality or ARCH effects
🔬 Methodology
Cumulative Sum Decomposition
For a time series y_t, positive and negative shocks are defined as:
ε⁺_t = max(Δy_t, 0)
ε⁻_t = min(Δy_t, 0)
Cumulative sums:
y⁺_t = Σᵢ₌₁ᵗ ε⁺ᵢ
y⁻_t = Σᵢ₌₁ᵗ ε⁻ᵢ
Bootstrap with Leverage Adjustment
The bootstrap procedure:
- Estimate restricted VAR model (under H₀)
- Calculate leverage-adjusted residuals
- Generate bootstrap samples
- Compute bootstrap Wald statistics
- Derive critical values from bootstrap distribution
Information Criteria
The Hatemi-J Criterion (HJC) is the average of SBC and HQC:
HJC = (SBC + HQC) / 2
📚 References
- Hatemi-J, A. (2012). Asymmetric Causality Tests with an Application. Empirical Economics, 43(1), 447-456.
- Granger, C.W.J. & Yoon, G. (2002). Hidden Cointegration. Working Paper, UC San Diego.
- Toda, H.Y. & Yamamoto, T. (1995). Statistical Inference in Vector Autoregressions with Possibly Integrated Processes. Journal of Econometrics, 66, 225-250.
- Hacker, R.S. & Hatemi-J, A. (2006). Tests for Causality between Integrated Variables Using Asymptotic and Bootstrap Distributions. Applied Economics, 38(13), 1489-1500.
🤝 Contributing
Contributions are welcome! Please feel free to submit a Pull Request.
- Fork the repository
- Create your feature branch (
git checkout -b feature/AmazingFeature) - Commit your changes (
git commit -m 'Add some AmazingFeature') - Push to the branch (
git push origin feature/AmazingFeature) - Open a Pull Request
📄 License
This project is licensed under the MIT License - see the LICENSE file for details.
👤 Author
Dr. Merwan Roudane
- GitHub: @merwanroudane
- Email: merwanroudane920@gmail.com
🙏 Acknowledgments
- Original GAUSS code by Prof. Abdulnasser Hatemi-J and Prof. Scott Hacker
- Based on the methodology in Hatemi-J (2012), Empirical Economics
📈 Citation
If you use this package in your research, please cite:
@software{roudane2024asymcaus,
author = {Roudane, Merwan},
title = {asymcaus: Asymmetric Causality Testing in Python},
year = {2024},
url = {https://github.com/merwanroudane/asymcaus}
}
@article{hatemi2012asymmetric,
title={Asymmetric causality tests with an application},
author={Hatemi-J, Abdulnasser},
journal={Empirical Economics},
volume={43},
number={1},
pages={447--456},
year={2012},
publisher={Springer}
}
Metadata
Release files for asymcaus 2.0.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | |
|---|---|---|---|---|
| asymcaus-2.0.0.tar.gz | 31.8 kB | Without provenance | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| asymcaus-2.0.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 55.3 kB
Release files / asymcaus-2.0.0.tar.gz
| Download URL | asymcaus-2.0.0.tar.gz |
|---|---|
| Size | 31.8 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
2107d851de6d85f16fdb181bc629e517b971800277ec178c34e0eb85081a406a
|
|
BLAKE2b-256 checksum How to use checksums |
189175a85cdac6af5b2d1bb858b1a962301035ec7f506de016295103ef109719
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.13.7
|
Release files / asymcaus-2.0.0-py3-none-any.whl
| Download URL | asymcaus-2.0.0-py3-none-any.whl |
|---|---|
| Size | 23.5 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
d7b79dca29cb151b037b7abf293dceb2fbaef9930a2e78535afb6e80c3f8cd50
|
|
BLAKE2b-256 checksum How to use checksums |
350292386288024bae44e45299a0a12f45d9dea7c7ca31bbad9c803a9e23b98b
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.13.7
|
Security for asymcaus 2.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.