# asymcaus

[![PyPI version](https://badge.fury.io/py/asymcaus.svg)](https://badge.fury.io/py/asymcaus)
[![Python Version](https://img.shields.io/pypi/pyversions/asymcaus.svg)](https://pypi.org/project/asymcaus/)
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)

**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

```bash
pip install asymcaus
```

For development installation with all dependencies:

```bash
pip install asymcaus[full]
```

## 🚀 Quick Start

### Basic Usage

```python
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)

```python
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

```python
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

```python
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 variables
- `random_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 test
- `kpss_test(y, trend='c')` - KPSS stationarity test  
- `pp_test(y, trend='c')` - Phillips-Perron test
- `determine_integration_order(y)` - Automatic integration order detection

### Diagnostic Tests

- `doornik_hansen_test(residuals)` - Multivariate normality test
- `multivariate_arch_test(residuals)` - ARCH effects test
- `ljung_box_test(residuals)` - Autocorrelation test
- `run_all_diagnostics(residuals)` - Run all diagnostics

## 📊 Example: Oil Prices and Stock Markets

Replicating the UAE market analysis from Hatemi-J (2012):

```python
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:
1. Estimate restricted VAR model (under H₀)
2. Calculate leverage-adjusted residuals
3. Generate bootstrap samples
4. Compute bootstrap Wald statistics
5. 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.

1. Fork the repository
2. Create your feature branch (`git checkout -b feature/AmazingFeature`)
3. Commit your changes (`git commit -m 'Add some AmazingFeature'`)
4. Push to the branch (`git push origin feature/AmazingFeature`)
5. Open a Pull Request

## 📄 License

This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.

## 👤 Author

**Dr. Merwan Roudane**
- GitHub: [@merwanroudane](https://github.com/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:

```bibtex
@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}
}
```
