What Is Cointegration?
Cointegration describes a statistical relationship between two or more non-stationary time series that share a common stochastic trend. Although each series wanders randomly on its own, a specific linear combination of them is stationary - meaning it fluctuates around a stable mean and reverts back when it drifts away. This property is the foundation of pairs trading and many other quantitative strategies.
To understand why this matters, consider stock prices. Most individual stock prices are non-stationary - they follow random walks without a fixed mean to return to. If you look at the price of Shell or BP in isolation, neither series has a tendency to snap back to any particular level. But if these two oil companies are driven by the same underlying economic forces (crude oil prices, energy demand, regulatory environment), the spread between their prices might be stationary. That spread wanders, but it reverts.
This is cointegration. It doesn't mean the two prices move in lockstep on any given day. It means that over time, they can't drift apart without limit - there's an equilibrium relationship pulling them back together. Formally, if you have two I(1) series ( X_t ) and ( Y_t ), they're cointegrated if there exists a coefficient ( \beta ) such that:
[ Z_t = Y_t - \beta X_t ]
is I(0) - that is, stationary. The series ( Z_t ) is called the cointegrating residual or the equilibrium error. When ( Z_t ) is large and positive, ( Y ) is "too high" relative to ( X ), and the relationship predicts it will fall back. When ( Z_t ) is large and negative, ( Y ) is "too low" and should rise.
Clive Granger and Robert Engle formalised this concept in their 1987 paper, work that contributed to their 2003 Nobel Prize in Economics. The insight was profound: even though individual economic variables may be unpredictable, the long-run relationship between them can be stable and exploitable. In 2026, cointegration remains one of the most important concepts in quantitative finance, time series econometrics, and systematic trading.
Cointegration vs Correlation
Cointegration and correlation measure fundamentally different things. Correlation captures how two series move together in the short term - when one goes up, does the other tend to go up too? Cointegration captures whether two series maintain a long-run equilibrium, regardless of their short-term co-movement. Two stocks can be highly correlated but not cointegrated, and vice versa.
This distinction trips up many beginners. It's tempting to assume that if two stock prices have a correlation of 0.95, they'll stay close together. But correlation says nothing about whether the gap between them is stable. Two tech stocks might move in the same direction 95% of the time, yet their price spread could widen steadily from £5 to £50 over a few years. The correlation stays high, but a pairs trade based on mean reversion of the spread would lose money.
Cointegration is the right concept for mean-reversion trading because it directly tests whether the spread is stationary. If two series are cointegrated, their spread has a defined mean and variance, and deviations from the mean are temporary. If they're merely correlated, the spread can drift without limit.
Here's a concrete example. Consider two stocks, A and B, both rising over 2024-2026. Their daily returns correlate at 0.90. But stock A climbs from £100 to £200 while stock B climbs from £100 to £300. The spread (B - A) grows from £0 to £100 - it's not stationary. These stocks are correlated but not cointegrated.
Now consider two other stocks, C and D. Stock C goes from £50 to £120 with lots of noise, and stock D goes from £25 to £60, also with noise. The ratio D/C stays close to 0.5 throughout, with temporary deviations that correct. Their daily return correlation might only be 0.70, but they're cointegrated because the linear combination (D - 0.5C) is stationary.
| Property | Correlation | Cointegration |
|---|---|---|
| What it measures | Short-term co-movement of returns | Long-run equilibrium between price levels |
| Input data | Returns (stationary) | Prices (non-stationary) |
| Can the spread drift? | Yes - correlation says nothing about spread stability | No - the spread is stationary by definition |
| Useful for mean reversion? | Not directly | Yes - this is exactly what it tests |
| Symmetric? | Yes - corr(A,B) = corr(B,A) | Not necessarily - the hedge ratio depends on direction |
| Time horizon | Short-term relationship | Long-term relationship |
| Can change over time? | Yes | Yes - cointegration can break down |
The takeaway for traders: if you're building a pairs trading strategy, test for cointegration, not correlation. Correlation is useful for portfolio diversification and risk management, but cointegration is what you need for spread-based mean-reversion strategies.
Real-World Examples of Cointegration
Cointegration appears wherever two or more assets are driven by the same underlying economic forces. Here are the most common examples that quant traders and researchers work with in 2026.
Coca-Cola and Pepsi
The classic textbook example. Both companies operate in the same industry (beverages), face similar input costs, compete for the same customers, and are subject to the same consumer trends. While their stock prices individually wander as random walks, the spread between them tends to revert to a long-run mean. When Coca-Cola's price gets "too high" relative to Pepsi (or vice versa), competitive dynamics, investor rotation, and fundamental valuation pull them back toward equilibrium.
This doesn't mean the relationship holds perfectly at all times. Earnings surprises, management changes, or a major acquisition can temporarily break the pattern. But over multi-year periods, the cointegrating relationship has been remarkably persistent.
Gold Miners and Gold Price
Gold mining companies derive their revenue primarily from selling gold. When the gold price rises, mining profits increase, and share prices tend to follow. A cointegrating relationship often exists between the gold spot price and gold mining indices (like the GDX ETF). The spread captures the market's view of mining costs, operational risk, and management quality - factors that fluctuate but tend to mean-revert.
Spot and Futures Prices
The relationship between a commodity's spot price and its futures price is one of the cleanest examples of cointegration. The two must converge at expiry (by construction), and the spread between them - the basis - reflects storage costs, interest rates, and convenience yield. Basis trading (going long the cheap leg and short the expensive leg) is a strategy built directly on this cointegrating relationship.
Cross-Listed Stocks
Companies listed on multiple exchanges (for example, a stock trading on both the NYSE and the London Stock Exchange) provide another natural example. After adjusting for the exchange rate, the two prices should track each other closely. Deviations create arbitrage opportunities, and the cointegrating relationship is maintained by arbitrageurs who trade the spread.
Interest Rate Instruments
Government bond yields across similar maturities, or interest rate swap rates across different tenors, often exhibit cointegration. The term structure of interest rates is driven by shared macroeconomic factors (inflation expectations, central bank policy, growth outlook), and the spreads between different points on the curve tend to be stationary.
The Engle-Granger Two-Step Method
The Engle-Granger method is the simplest and most widely used cointegration test for a pair of time series. It works in two steps: first, estimate the long-run relationship using ordinary least squares (OLS) regression; second, test whether the residuals from that regression are stationary using the augmented Dickey-Fuller (ADF) test.
Step 1: Estimate the Cointegrating Regression
Given two series ( Y_t ) and ( X_t ), run the OLS regression:
[ Y_t = \alpha + \beta X_t + \epsilon_t ]
The coefficient ( \beta ) is the hedge ratio - it tells you how many units of ( X ) you need to hold against one unit of ( Y ) to create a stationary spread. The intercept ( \alpha ) captures any constant offset. The residuals ( \hat{\epsilon}_t = Y_t - \hat{\alpha} - \hat{\beta} X_t ) represent the spread.
A critical point: the OLS regression here is run on price levels, not returns. This is unusual - running OLS on non-stationary data normally produces spurious results. But if the series are genuinely cointegrated, the residuals will be stationary, and the coefficient estimates are not only valid but actually converge faster than usual (this is called superconsistency).
Step 2: Test Residuals for Stationarity
Take the residuals ( \hat{\epsilon}_t ) from step 1 and run an ADF test:
[ \Delta \hat{\epsilon}t = \gamma \hat{\epsilon}{t-1} + \sum_{i=1}^{p} \delta_i \Delta \hat{\epsilon}_{t-i} + u_t ]
The null hypothesis is that ( \gamma = 0 ) (residuals have a unit root, meaning no cointegration). You reject the null - concluding cointegration exists - if the test statistic is sufficiently negative.
Important: you can't use standard ADF critical values here. Because the residuals are estimated rather than observed, the critical values are shifted. Engle and Granger (1987) tabulated special critical values, and MacKinnon (1991, 2010) refined them. Using standard ADF critical values will lead you to reject the null too often, finding cointegration where none exists.
For two series at the 5% significance level, the critical value is approximately -3.34 (compared to -2.86 for a standard ADF test). The exact value depends on the sample size and the number of variables.
Limitations of Engle-Granger
The Engle-Granger method has a few well-known drawbacks. It can only test one cointegrating relationship at a time. The result depends on which variable you place on the left-hand side of the regression - regressing Y on X can give a different conclusion than regressing X on Y. And it's limited to two variables. For testing cointegration among three or more series, you need the Johansen test.
The Johansen Test
The Johansen test is a multivariate cointegration test that addresses the limitations of the Engle-Granger method. It can simultaneously test for multiple cointegrating relationships among three or more time series, and it doesn't require you to choose which variable goes on the left-hand side. For serious quantitative work in 2026, it's the standard approach.
The Johansen test is based on a vector error correction model (VECM). Given a vector of ( n ) I(1) time series ( \mathbf{Y}_t ), the VECM representation is:
[ \Delta \mathbf{Y}t = \Pi \mathbf{Y}{t-1} + \sum_{i=1}^{p-1} \Gamma_i \Delta \mathbf{Y}_{t-i} + \mathbf{u}_t ]
The matrix ( \Pi ) contains the cointegration information. Its rank ( r ) tells you how many cointegrating relationships exist:
- If ( r = 0 ): no cointegration. The series are just a collection of random walks.
- If ( 0 < r < n ): there are ( r ) cointegrating relationships. You can decompose ( \Pi = \alpha \beta' ), where ( \beta ) contains the cointegrating vectors and ( \alpha ) contains the adjustment speeds.
- If ( r = n ): all series are stationary. No cointegration needed.
Trace Test and Maximum Eigenvalue Test
Johansen proposed two test statistics to determine ( r ):
Trace test. Tests the null hypothesis that the number of cointegrating vectors is at most ( r ) against the alternative that it's greater than ( r ). The test statistic is:
[ \lambda_{\text{trace}}(r) = -T \sum_{i=r+1}^{n} \ln(1 - \hat{\lambda}_i) ]
Maximum eigenvalue test. Tests ( r ) cointegrating vectors against ( r+1 ):
[ \lambda_{\max}(r, r+1) = -T \ln(1 - \hat{\lambda}_{r+1}) ]
In both cases, ( \hat{\lambda}_i ) are the ordered eigenvalues of a specific matrix derived from the data, and ( T ) is the sample size.
When to Use Johansen vs Engle-Granger
Use Engle-Granger when you have exactly two series and want a quick, intuitive test. Use Johansen when you have three or more series, when you want to avoid the arbitrary choice of dependent variable, or when you need to estimate the cointegrating vectors and adjustment speeds jointly. In practice, many quant teams run both as a cross-check when analysing pairs.
Testing for Cointegration in Python
Here's a complete Python implementation for testing cointegration using both the Engle-Granger and Johansen approaches. These examples use statsmodels, which is the standard library for econometric analysis in Python.
Engle-Granger Test in Python
import numpy as np import pandas as pd import statsmodels.api as sm from statsmodels.tsa.stattools import adfuller, coint def engle_granger_test( y: pd.Series, x: pd.Series, significance: float = 0.05, ) -> dict: """ Perform the Engle-Granger two-step cointegration test. Parameters ---------- y : pd.Series The dependent series (price levels, not returns). x : pd.Series The independent series (price levels, not returns). significance : float Significance level for the test (default 0.05). Returns ------- dict with test statistic, p-value, hedge ratio, and conclusion. """ # Step 1: OLS regression of y on x x_with_const = sm.add_constant(x) model = sm.OLS(y, x_with_const).fit() hedge_ratio = model.params.iloc[1] intercept = model.params.iloc[0] residuals = model.resid # Step 2: ADF test on the residuals adf_stat, p_value, _, _, critical_values, _ = adfuller( residuals, autolag="AIC" ) cointegrated = p_value < significance return { "adf_statistic": adf_stat, "p_value": p_value, "critical_values": critical_values, "hedge_ratio": hedge_ratio, "intercept": intercept, "cointegrated": cointegrated, } # --- Using the built-in statsmodels coint function --- np.random.seed(42) n = 500 # Generate two cointegrated series x = np.cumsum(np.random.normal(0, 1, n)) + 100 y = 0.8 * x + np.random.normal(0, 1.5, n) + 20 series_x = pd.Series(x, name="Stock_X") series_y = pd.Series(y, name="Stock_Y") # Quick test using statsmodels score, pvalue, _ = coint(series_y, series_x) print(f"Engle-Granger test statistic: {score:.4f}") print(f"P-value: {pvalue:.6f}") print(f"Cointegrated at 5%: {pvalue < 0.05}") # Detailed test using our function result = engle_granger_test(series_y, series_x) print(f"\nHedge ratio: {result['hedge_ratio']:.4f}") print(f"ADF statistic: {result['adf_statistic']:.4f}") print(f"P-value: {result['p_value']:.6f}") print(f"Cointegrated: {result['cointegrated']}")
The statsmodels.tsa.stattools.coint function handles the Engle-Granger test in a single call, including the correct critical values. The custom function above shows what's happening under the hood so you can adapt it for your own use cases.
Johansen Test in Python
import numpy as np import pandas as pd from statsmodels.tsa.vector_ar.vecm import coint_johansen def johansen_test( data: pd.DataFrame, det_order: int = 0, k_ar_diff: int = 1, significance_level: str = "5%", ) -> dict: """ Perform the Johansen cointegration test. Parameters ---------- data : pd.DataFrame DataFrame where each column is a price series. det_order : int Deterministic term: -1 (no constant), 0 (constant), 1 (constant + trend). k_ar_diff : int Number of lagged differences in the VECM. significance_level : str One of "1%", "5%", "10%" for critical value comparison. Returns ------- dict with trace statistics, eigenvalue statistics, critical values, and the number of cointegrating relations. """ sig_index = {"1%": 2, "5%": 1, "10%": 0}[significance_level] result = coint_johansen(data, det_order, k_ar_diff) trace_stats = result.lr1 # trace statistics trace_cvs = result.cvt[:, sig_index] # critical values for trace max_eig_stats = result.lr2 # max eigenvalue statistics max_eig_cvs = result.cvm[:, sig_index] # Count cointegrating relations using the trace test n_coint_trace = sum( t > c for t, c in zip(trace_stats, trace_cvs) ) n_coint_eigen = sum( t > c for t, c in zip(max_eig_stats, max_eig_cvs) ) return { "trace_statistics": trace_stats, "trace_critical_values": trace_cvs, "max_eigen_statistics": max_eig_stats, "max_eigen_critical_values": max_eig_cvs, "cointegrating_vectors": result.evec, "n_cointegrating_trace": n_coint_trace, "n_cointegrating_eigen": n_coint_eigen, } # Generate three cointegrated series np.random.seed(123) n = 500 common_trend = np.cumsum(np.random.normal(0, 1, n)) stock_a = common_trend + np.random.normal(0, 0.5, n) + 50 stock_b = 1.5 * common_trend + np.random.normal(0, 0.7, n) + 30 stock_c = 0.7 * common_trend + np.random.normal(0, 0.4, n) + 80 data = pd.DataFrame({ "Stock_A": stock_a, "Stock_B": stock_b, "Stock_C": stock_c, }) result = johansen_test(data) print("Johansen Cointegration Test Results") print("=" * 50) for i in range(len(data.columns)): print( f"r <= {i}: trace stat = {result['trace_statistics'][i]:.2f}, " f"critical value = {result['trace_critical_values'][i]:.2f}, " f"reject = {result['trace_statistics'][i] > result['trace_critical_values'][i]}" ) print( f"\nNumber of cointegrating relations (trace): " f"{result['n_cointegrating_trace']}" ) print( f"Number of cointegrating relations (max eigenvalue): " f"{result['n_cointegrating_eigen']}" )
A few practical notes. The det_order parameter matters: use 0 (constant, no trend) for most financial series, and -1 if you've already demeaned the data. The k_ar_diff parameter controls how many lagged differences to include - you can select it using information criteria (AIC or BIC) from a preliminary VAR estimation. Getting these settings wrong can lead to misleading results.
Cointegration and Pairs Trading
Pairs trading cointegration is the most direct commercial application of cointegration in quantitative finance. The strategy identifies two cointegrated assets, computes their spread, and trades the mean reversion of that spread - going long when the spread is unusually low and short when it's unusually high.
Here's how quant teams typically build a cointegration-based pairs trading strategy in 2026.
Step 1: Find Cointegrated Pairs
Screen a universe of stocks (usually within the same sector or industry) and test every pair for cointegration. With ( n ) stocks, you have ( n(n-1)/2 ) pairs to test, so multiple testing corrections (Bonferroni or Holm) are essential. Many quant funds restrict the search to economically sensible pairs - companies in the same industry, with similar business models - to reduce the risk of finding spurious statistical relationships.
Step 2: Estimate the Hedge Ratio
Once you've found a cointegrated pair, estimate the hedge ratio ( \beta ) from the cointegrating regression. This tells you the ratio of positions: if ( \beta = 1.3 ), you'd go long 1 unit of stock Y and short 1.3 units of stock X (or vice versa) to create a market-neutral position whose value is the spread.
Some traders use rolling OLS to let the hedge ratio adapt over time. Others use the Kalman filter for a more sophisticated dynamic estimate. The choice depends on how quickly you expect the relationship to shift.
Step 3: Calculate and Normalise the Spread
Compute the spread ( Z_t = Y_t - \beta X_t - \alpha ) and normalise it into z-scores:
[ z\text{-score}_t = \frac{Z_t - \mu_Z}{\sigma_Z} ]
where ( \mu_Z ) and ( \sigma_Z ) are the rolling mean and standard deviation of the spread.
Step 4: Generate Trading Signals
A standard approach uses z-score thresholds:
- Enter long the spread when z-score drops below -2 (spread is unusually narrow)
- Enter short the spread when z-score rises above +2 (spread is unusually wide)
- Exit when the z-score reverts to 0 (or ±0.5 as a tighter target)
- Stop loss at ±4 or similar to protect against cointegration breakdown
Step 5: Risk Management
Position sizing should reflect the volatility of the spread, not of the individual legs. Many teams size positions so that a 1-standard-deviation move in the spread corresponds to a fixed dollar risk. It's also wise to reduce position sizes during periods when the ADF test statistic on the rolling spread is weakening - a sign that cointegration may be breaking down.
Here's a simplified pairs trading implementation:
import numpy as np import pandas as pd import statsmodels.api as sm def pairs_trading_backtest( y: pd.Series, x: pd.Series, lookback: int = 60, entry_z: float = 2.0, exit_z: float = 0.5, stop_z: float = 4.0, ) -> pd.DataFrame: """ Simple pairs trading backtest using cointegration. Parameters ---------- y, x : pd.Series Price series of the two assets. lookback : int Rolling window for spread statistics. entry_z, exit_z, stop_z : float Z-score thresholds for entry, exit, and stop-loss. Returns ------- pd.DataFrame with spread, z-score, positions, and PnL. """ # Estimate rolling hedge ratio using expanding OLS hedge_ratios = pd.Series(index=y.index, dtype=float) for i in range(lookback, len(y)): window_y = y.iloc[:i] window_x = sm.add_constant(x.iloc[:i]) model = sm.OLS(window_y, window_x).fit() hedge_ratios.iloc[i] = model.params.iloc[1] # Calculate the spread spread = y - hedge_ratios * x spread_mean = spread.rolling(window=lookback).mean() spread_std = spread.rolling(window=lookback).std() z_score = (spread - spread_mean) / spread_std # Generate positions position = pd.Series(0.0, index=y.index) for i in range(lookback + 1, len(y)): z = z_score.iloc[i] prev_pos = position.iloc[i - 1] if prev_pos == 0: if z < -entry_z: position.iloc[i] = 1.0 # long the spread elif z > entry_z: position.iloc[i] = -1.0 # short the spread elif prev_pos > 0: if z > -exit_z or z < -stop_z: position.iloc[i] = 0.0 # exit else: position.iloc[i] = prev_pos elif prev_pos < 0: if z < exit_z or z > stop_z: position.iloc[i] = 0.0 # exit else: position.iloc[i] = prev_pos # Calculate PnL from spread changes spread_returns = spread.diff() pnl = position.shift(1) * spread_returns return pd.DataFrame({ "spread": spread, "z_score": z_score, "position": position, "pnl": pnl, "cumulative_pnl": pnl.cumsum(), })
This is a simplified version. Production systems at quant funds incorporate transaction costs, slippage models, dynamic position sizing, and real-time cointegration monitoring. But the core logic remains: find cointegrated pairs, compute the spread, and trade its mean reversion. The fundamentals of algorithmic execution apply directly when implementing this in a live environment.
Common Pitfalls
Cointegration testing is straightforward in theory but easy to get wrong in practice. Here are the mistakes that catch out both researchers and traders.
Spurious Cointegration
If you test enough pairs, you'll find statistically significant cointegration by chance alone. With 100 stocks, you have 4,950 pairs to test. At a 5% significance level, you'd expect roughly 248 false positives even if no true cointegration exists. Always apply multiple testing corrections (Bonferroni, Holm-Bonferroni, or false discovery rate control) and demand economic rationale for any pair you trade.
Structural Breaks
Cointegrating relationships can break down. A merger, a regulatory change, a shift in business model, or a market regime change can permanently alter the relationship between two assets. The cointegration test might look perfect on historical data but fail going forward. Monitor the spread continuously and be prepared to exit if the ADF test on rolling residuals starts losing significance.
In-Sample vs Out-of-Sample
A common trap: you find strong cointegration on 5 years of data, build a strategy, and start trading. But the cointegrating relationship might have been strongest in the first 3 years and already weakening in years 4 and 5. Always split your data into training and test periods. Better yet, use rolling or expanding window tests to track how the relationship evolves over time.
Overfitting the Hedge Ratio
The hedge ratio estimated from historical data is a point estimate with uncertainty around it. If you optimise it too precisely on past data - especially if you keep tweaking the estimation window to maximise backtest performance - you're overfitting. A hedge ratio of 1.327 estimated from OLS doesn't mean the "true" ratio is exactly 1.327. It might be anywhere from 1.1 to 1.5. Use techniques like the Kalman filter or rolling regression to let the ratio adapt, but don't over-optimise.
Ignoring Transaction Costs
Mean-reversion strategies trade frequently, and the average profit per trade can be small. Transaction costs, slippage, and market impact eat into returns quickly. A cointegration-based pairs strategy that looks excellent in a frictionless backtest often becomes marginal or unprofitable once realistic costs are included. Model your costs carefully before committing capital.
Using Correlation as a Proxy
As discussed earlier, testing for correlation instead of cointegration is a fundamental conceptual error that still appears in published research and production code. Always test the spread for stationarity directly. Sound statistical practice matters enormously here.
Frequently Asked Questions
What is the difference between cointegration and stationarity?
Stationarity is a property of a single time series - it means the series has a constant mean and variance over time, and its statistical properties don't change as you shift the observation window. Most stock prices aren't stationary; they're integrated of order one, or I(1), meaning they need to be differenced once to become stationary. Cointegration is a property of the relationship between two or more non-stationary series. Two I(1) series are cointegrated if a linear combination of them is stationary. So you can think of cointegration as a way that non-stationary series can combine to produce something stationary.
Can more than two time series be cointegrated?
Yes, and this is common in practice. Three or more series can share one or more cointegrating relationships. For example, three energy stocks might share a common trend driven by oil prices, giving rise to two independent cointegrating vectors. The Johansen test is designed specifically for this multivariate case - it can identify how many cointegrating relationships exist among ( n ) series simultaneously. The Engle-Granger test is limited to pairs, which is one reason the Johansen approach is preferred for larger systems.
How do you choose between the Engle-Granger and Johansen tests?
For a simple pair of time series, the Engle-Granger test is fine and simpler to implement. It gives you a clear hedge ratio directly from the regression. Use the Johansen test when you have three or more series, when you want to avoid choosing which variable is "dependent", or when you need to know how many cointegrating relationships exist. In practice, running both on a pair and checking that they agree is a sensible cross-validation step. If one test says cointegrated and the other doesn't, treat the evidence as inconclusive.
Does cointegration guarantee a profitable trading strategy?
No. Cointegration means the spread between two series is stationary and mean-reverting, but it doesn't tell you how fast reversion happens, how wide the spread typically gets, or whether the profits from trading the reversion exceed transaction costs. A pair might be cointegrated with a half-life of 200 days - far too slow for most trading strategies. The cointegrating relationship might also break down in the future. Cointegration is a necessary condition for spread-based mean-reversion trading, but it's not sufficient. You still need to analyse the spread's dynamics, account for trading costs, and manage the risk of regime changes.
How often should you re-test for cointegration?
There's no universal answer, but common practice among quant traders in 2026 is to re-test monthly or quarterly using a rolling window (typically 2-5 years of daily data). Some teams run continuous monitoring, checking the ADF test statistic on the rolling spread daily and flagging pairs where the evidence for cointegration is weakening. If the p-value from the cointegration test crosses above 0.10 or 0.15, many strategies will reduce position sizes or exit entirely. The key principle is that cointegration is not permanent - economic relationships shift, and your testing framework should reflect that.
What Python libraries are best for cointegration analysis?
The statsmodels library is the standard choice for cointegration testing in Python. It provides coint for the Engle-Granger test and coint_johansen for the Johansen test, along with the full suite of time series tools (ADF tests, VAR models, VECM estimation). For data handling, pandas is essential. For numerical computation and matrix operations, numpy handles everything you need. Some teams also use arch for more advanced volatility modelling and sklearn for rolling regression approaches. All of these are well-maintained and widely used in quantitative finance in 2026.
Want to go deeper on Cointegration: What It Is, How to Test & Trading Applications 2026?
This article covers the essentials, but there's a lot more to learn. Inside Quantt, you'll find hands-on coding exercises, interactive quizzes, and structured lessons that take you from fundamentals to production-ready skills — across 50+ courses in technology, finance, and mathematics.
Free to get started · No credit card required