Johansen-type Cointegration Tests with a Fourier Function
Project description
Fourier-Johansen: Johansen-type Cointegration Tests with a Fourier Function
A Python implementation of the Johansen-Fourier cointegration test that extends the pioneering Johansen (1991) cointegration test to allow for structural breaks using Fourier functions.
Reference
Pascalau, R., Lee, J., Nazlioglu, S., Lu, Y. O. (2022). "Johansen-type Cointegration Tests with a Fourier Function". Journal of Time Series Analysis 43(5): 828-852. DOI: 10.1111/jtsa.12640
Author
Dr Merwan Roudane
📧 merwanroudane920@gmail.com
🔗 GitHub
Installation
pip install fourier-johansen
Or install from source:
git clone https://github.com/merwanroudane/fourierjohansen.git
cd fourierjohansen
pip install -e .
Features
- Standard Johansen Test: Original Johansen (1991) cointegration test
- Johansen-Fourier Test: Cointegration test with Fourier function for smooth structural breaks
- SC-VECM Test: Test for sharp/discrete breaks (Harris et al. 2016)
- SBC Model Selection: Automatic selection among Johansen, SC-VECM, and Fourier models
- Union of Rejections: Combined test strategy for unknown break types
- Publication-ready Output: LaTeX, Markdown, and formatted text tables
Quick Start
import numpy as np
from fourier_johansen import johansen_fourier
# Create sample data
np.random.seed(42)
T = 200
x1 = np.cumsum(np.random.randn(T))
x2 = x1 + np.random.randn(T) * 0.5 # Cointegrated with x1
X = np.column_stack([x1, x2])
# Run Johansen-Fourier test
result = johansen_fourier(X, model=3, k=2, f=1, option=1)
print(result)
Detailed Examples
1. Standard Johansen Test
from fourier_johansen import johansen
# Generate cointegrated data
np.random.seed(42)
T = 200
x1 = np.cumsum(np.random.randn(T))
x2 = x1 + np.random.randn(T) * 0.5
x3 = np.cumsum(np.random.randn(T))
X = np.column_stack([x1, x2, x3])
# Test with restricted constant model
result = johansen(X, model=2, k=2)
print(result)
# Model options:
# 1 = None (no deterministic terms)
# 2 = Restricted constant (RC) - recommended
# 3 = Unrestricted constant
# 4 = Restricted trend (RT)
# 5 = Unrestricted trend
Output:
=================================================================
Johansen Cointegration Test Results
=================================================================
# Variables : 3
Model : Restricted Constant (RC)
VAR Lags : 2
VECM Lags : 1
Observations: 197
-----------------------------------------------------------------
Rank Eigenvalue Lambda-max Trace CV(5%)
-----------------------------------------------------------------
0 -4434.7013
1 0.052138 10.5234 21.4789* 35.070
2 0.033045 6.5678 10.8347 20.160
3 0.021234 4.2669 4.0931 9.140
=================================================================
Note: * indicates rejection of null at 5% level
2. Johansen-Fourier Test (Main Feature)
from fourier_johansen import johansen_fourier
# Create data with smooth structural break
np.random.seed(42)
T = 200
t = np.arange(T)
break_term = 5 * np.sin(2 * np.pi * t / T) # Smooth Fourier break
x1 = np.cumsum(np.random.randn(T)) + break_term
x2 = x1 + np.random.randn(T) * 0.5 + break_term
X = np.column_stack([x1, x2])
# Single frequency
result = johansen_fourier(X, model=3, k=2, f=1, option=1)
print(result)
# Cumulative frequencies (recommended when break form is unknown)
result_cum = johansen_fourier(X, model=3, k=2, f=2, option=2)
print(result_cum)
# Model options:
# 1 = Constant (unrestricted)
# 2 = Trend (unrestricted)
# 3 = Restricted Constant (RC) - most common
# 4 = Restricted Trend (RT)
# Get cointegration rank
rank = result.get_cointegration_rank()
print(f"Estimated cointegration rank: {rank}")
Output:
===========================================================================
Johansen-Fourier Cointegration Test Results
===========================================================================
# Variables : 2
Model : Restricted Constant (RC)
Frequency : 1 (Single)
VAR Lags : 2
VECM Lags : 1
Observations: 197
---------------------------------------------------------------------------
Rank Fourier Fourier CV(5%) CV(5%) Log-Lik
Lambda Trace Lambda Trace
---------------------------------------------------------------------------
0 -856.234
1 28.591* 57.342* 22.195 48.930 -842.123
2 20.404* 28.751* 14.742 29.586 -831.456
===========================================================================
Note: * indicates rejection of null at 5% level
3. SC-VECM Test (For Sharp Breaks)
from fourier_johansen import sc_vecm
# Create data with sharp break
np.random.seed(42)
T = 200
break_point = 100
x1 = np.cumsum(np.random.randn(T))
x1[break_point:] += 5 # Sharp level shift
x2 = x1 + np.random.randn(T) * 0.5
X = np.column_stack([x1, x2])
# Test rank 0
result = sc_vecm(r=0, y=X, max_lag=4, lambda_L=0.1)
print(result)
Output:
======================================================================
SC-VECM Cointegration Test Results
======================================================================
# Variables : 2
Observations : 200
Rank Tested : 0
Selected Model: Break
Break Location: 98 (fraction: 0.490)
----------------------------------------------------------------------
No Break With Break
----------------------------------------------------------------------
Trace Statistic 18.2345 55.6789
SBC 1234.567 1198.234
Optimal Lag 2 2
CV (5%) 23.453 37.630
======================================================================
Decision at 5% level: Reject H0
4. SBC Model Selection
from fourier_johansen import sbc_test
# Run SBC test - automatically selects best model
result = sbc_test(r=0, y=X, max_lag=4, lambda_L=0.1, f_max=3, option=2)
print(result)
Output:
===========================================================================
SBC Model Selection Test Results
===========================================================================
# Variables : 2
Observations : 200
Rank Tested : 0
Selected Model: Fourier
---------------------------------------------------------------------------
Johansen SC-VECM Fourier
---------------------------------------------------------------------------
Trace Statistic 18.2345 55.6789 62.3456
SBC 1234.5670 1198.2340 1156.7890
===========================================================================
Selected: FOURIER
Trace from selected model: 62.3456
Critical value (5%): 48.9300
Decision: Reject H0 (cointegration detected)
5. Union of Rejections Test
from fourier_johansen import union_test
# Combine Fourier and SC-VECM tests
result = union_test(X, model=3, k=2, f=2, option=2, lambda_loc=0.5, r=0)
print(result)
Output:
======================================================================
Union of Rejections Test Results
======================================================================
# Variables : 2
Observations : 200
Rank Tested : 0
Scale Factor : 1.0720
----------------------------------------------------------------------
Fourier SC-VECM
----------------------------------------------------------------------
Trace Statistic 62.3456 55.6789
CV (5%, original) 48.9300 37.6300
CV (5%, scaled) 52.4528 40.3394
Individual Reject Yes Yes
======================================================================
UNION TEST RESULT: REJECT H0
(Rejects if either scaled test rejects)
6. Export to LaTeX for Publications
from fourier_johansen import johansen_fourier, to_latex, to_markdown
result = johansen_fourier(X, model=3, k=2, f=1, option=1)
# Export as LaTeX table
latex_table = to_latex(result)
print(latex_table)
# Export as Markdown
markdown_table = to_markdown(result)
print(markdown_table)
LaTeX Output:
\begin{table}[htbp]
\centering
\caption{Johansen-Fourier Cointegration Test Results}
\begin{tabular}{ccccc}
\hline\hline
Rank & Eigenvalue & $\lambda_{max}$ & Trace & CV (5\%) \\
\hline
1 & 0.134567 & 28.5914$^{*}$ & 57.3421$^{*}$ & 48.930 \\
2 & 0.098765 & 20.4043$^{*}$ & 28.7507 & 29.586 \\
\hline\hline
\end{tabular}
\begin{tablenotes}
\small
\item Note: $^{*}$ indicates rejection of the null hypothesis at the 5\% significance level.
\item Fourier frequency: 1 (single).
\end{tablenotes}
\end{table}
7. Complete Analysis Workflow
import numpy as np
import pandas as pd
from fourier_johansen import (
johansen, johansen_fourier, sc_vecm, sbc_test, union_test,
to_latex, to_markdown
)
# Load your data
# data = pd.read_csv('your_data.csv')
# X = data[['var1', 'var2', 'var3']].values
# For demonstration, create synthetic data
np.random.seed(42)
T = 200
t = np.arange(T)
break_term = 3 * np.sin(2 * np.pi * t / T)
x1 = np.cumsum(np.random.randn(T)) + break_term
x2 = x1 + np.random.randn(T) * 0.5 + break_term * 0.8
x3 = np.cumsum(np.random.randn(T))
X = np.column_stack([x1, x2, x3])
print("=" * 60)
print(" COMPLETE COINTEGRATION ANALYSIS")
print("=" * 60)
# Step 1: Standard Johansen (baseline)
print("\n[1] Standard Johansen Test")
print("-" * 40)
joh_result = johansen(X, model=2, k=2)
print(joh_result)
# Step 2: Johansen-Fourier with single frequency
print("\n[2] Johansen-Fourier Test (Single Frequency)")
print("-" * 40)
jf_result = johansen_fourier(X, model=3, k=2, f=1, option=1)
print(jf_result)
# Step 3: Johansen-Fourier with cumulative frequencies
print("\n[3] Johansen-Fourier Test (Cumulative Frequencies)")
print("-" * 40)
jf_cum_result = johansen_fourier(X, model=3, k=2, f=2, option=2)
print(jf_cum_result)
# Step 4: SC-VECM for comparison
print("\n[4] SC-VECM Test")
print("-" * 40)
scvecm_result = sc_vecm(r=0, y=X, max_lag=4, lambda_L=0.1)
print(scvecm_result)
# Step 5: SBC model selection
print("\n[5] SBC Model Selection")
print("-" * 40)
sbc_result = sbc_test(r=0, y=X, max_lag=4, lambda_L=0.1, f_max=3)
print(sbc_result)
# Step 6: Union test
print("\n[6] Union of Rejections Test")
print("-" * 40)
union_result = union_test(X, model=3, k=2, f=2, option=2, r=0)
print(union_result)
# Summary
print("\n" + "=" * 60)
print(" SUMMARY OF RESULTS")
print("=" * 60)
print(f"Standard Johansen rank: {sum(joh_result.trace > joh_result.cv_trace)}")
print(f"Johansen-Fourier rank: {jf_result.get_cointegration_rank()}")
print(f"SBC selected model: {sbc_result.selected_model}")
print(f"Union test rejects H0: {union_result.reject_h0}")
Model Specifications
Johansen-Fourier Models
| Model | Description | When to Use |
|---|---|---|
| 1 | Constant (unrestricted) | General case with constant in VAR |
| 2 | Trend (unrestricted) | Data with linear trends |
| 3 | Restricted Constant (RC) | Most common - constant in cointegrating eq. only |
| 4 | Restricted Trend (RT) | Trend in cointegrating relationship |
Fourier Frequency Options
| Option | Description | Recommendation |
|---|---|---|
option=1 |
Single frequency | When break type is known |
option=2 |
Cumulative frequencies | Recommended when break type is unknown |
Frequency Selection
- Use
f=1, 2, or 3for most applications - Higher frequencies may lead to overfitting
- Use SBC to select optimal frequency automatically
Critical Values
All critical values are from:
- Johansen (1991) for standard test
- Pascalau et al. (2022) Online Appendix for Fourier tests
- Harris et al. (2016) for SC-VECM
Citation
If you use this library in your research, please cite:
@article{pascalau2022johansen,
title={Johansen-type cointegration tests with a Fourier function},
author={Pascalau, Razvan and Lee, Junsoo and Nazlioglu, Saban and Lu, Yan Olivia},
journal={Journal of Time Series Analysis},
volume={43},
number={5},
pages={828--852},
year={2022},
publisher={Wiley},
doi={10.1111/jtsa.12640}
}
License
MIT License
Contributing
Contributions are welcome! Please feel free to submit a Pull Request.
Changelog
Version 0.0.1 (2026-01-21)
- Initial release
- Implemented Johansen-Fourier cointegration test
- Added SC-VECM test for sharp breaks
- Added SBC model selection
- Added Union of rejections strategy
- Publication-ready output formatting
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file fourier_johansen-0.0.1.tar.gz.
File metadata
- Download URL: fourier_johansen-0.0.1.tar.gz
- Upload date:
- Size: 31.9 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
41f2774f55f0f73dde3bd4c0ddc401cd44554c3ea4ba155fcc837c70d5ab0694
|
|
| MD5 |
6f1e2b7f7349b0778e32b3715b782c20
|
|
| BLAKE2b-256 |
cec625def0f161d86ba1563de3487681fae6361c029d4a528d76a33f0c6f63df
|
File details
Details for the file fourier_johansen-0.0.1-py3-none-any.whl.
File metadata
- Download URL: fourier_johansen-0.0.1-py3-none-any.whl
- Upload date:
- Size: 34.8 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
5accbe615589cb9a58651eed8c8e5b2a4138f517efbee3c1b647a96664b892e1
|
|
| MD5 |
583851c3177d58d28aa215321d7bc568
|
|
| BLAKE2b-256 |
b0cfed17aa1ef91f30fda573071aec95c424b12bf811d1f05443b18c2128a7f4
|