What (One-liner)
Engle-Granger two-step cointegration test: do two series share a long-run equilibrium; returns t-stat / p-value / hedge ratio / spread z-score. The mandatory pre-screen for pairs trading.
How to use
Drag cointegration_v1 onto the Canvas, fill series_a and series_b (required) + optional significance (default 0.05). Output is the boolean verdict plus the full statistics.
Typical usage:
series_a = SPY prices
series_b = IVV prices # both track the S&P 500 — should cointegrate strongly
→ is_cointegrated: True, hedge_ratio ≈ 1.0, z_score ≈ 0Core formulas
Step 1 — OLS: a_t = α + β · b_t + ε_t
Step 2 — ADF: H0: ε_t has unit root (not cointegrated)
H1: ε_t is I(0) stationary
→ p-value < significance → reject H0 → cointegrated
spread_t = a_t - α - β · b_t
z_t = (spread_t - mean(spread)) / std(spread, ddof=0)Rounding contract (pinned by the golden):
- t_stat / p_value / critical_values / z_score →
round(4) - hedge_ratio / spread_mean / spread_std →
round(6)
Assumptions & applicability
Assumptions: both series I(1) + linear cointegration + constant-only (no trend) + synchronised timestamps.
Fits: ETF pairs trading, cross-market arbitrage, macro long-run relations; n≥30 (n≥100 sturdier in practice).
Does not fit: HFT / tick, > 2 series (use Johansen), non-linear / regime-switching.
Input / Output contract
{series_a, series_b, significance?} → {is_cointegrated, t_statistic, p_value, critical_values: {1%, 5%, 10%}, hedge_ratio, spread_mean, spread_std, current_z_score, n_observations, _method}. Details in the frontmatter.
Known limitations
- Pairwise only (> 2 needs Johansen)
- Low power at n=30; n≥100 more reliable
- Constant-only (no deterministic trend)
- No SE / CI for β
- No structural-break test (Gregory-Hansen unimplemented)
- Spread std uses ddof=0 (Bloomberg sometimes uses ddof=1)
References
Engle-Granger (1987) Nobel-winning original + Hamilton (1994) Ch.19 textbook + MacKinnon (2010) critical-value tables (used internally by statsmodels). Full citations in the frontmatter.
Golden Test
tests/golden/fixtures/tier2/cointegration/, 1e-12 tolerance (post-rounding byte-equality), 4 tests:
matches_reference— field-by-field byte equality vs the wrapper-mirrored referencedeterminism— 5 reruns byte-identicalrejects_short_series— n<30 → error payloadsignificance_threshold_matters— significance flipsis_cointegratedbut leaves t_stat / p_value / hedge_ratio and other intrinsic quantities unchanged
The reference walks the same statsmodels.coint + np.linalg.lstsq OLS derivation, pinning Pangura's wrapper behaviour (input casts / rounding / design matrix / std ddof).
Changelog
- 1.0.0 (2026-04-20) — First Active

