Calculate the Engle-Granger test for cointegration between every ordered pair of tickers in the Toolkit instance.

Two individually non-stationary series (e.g. two stock prices, each following a random walk) are cointegrated if some linear combination of them is stationary, i.e. they share a long-run equilibrium relationship even though each wanders on its own in the short run. This is the classic statistical foundation for pairs-trading: if two assets are cointegrated, deviations of the spread from its equilibrium level tend to revert, making the spread itself tradeable.

In pairs trading the slope of the regression of the dependent on the independent ticker is the hedge ratio, and its residual is the spread that is traded. Cointegration found in one window often breaks down in the next, so the relationship is usually tested again on a later sample before it is relied on. The result depends on which ticker is the dependent one, so the two orderings can disagree; get_johansen_cointegration tests the pair without that choice.

The formula is as follows:

\[\text{Step 1:}\;\; \text{Aₜ} = \alpha + \beta \cdot \text{Bₜ} + \text{uₜ},\;\; \text{the OLS regression of the dependent on the independent column level (Adj Close by default)}\] \[\text{Step 2:}\;\; \Delta \text{ûₜ} = \text{γ} \cdot \text{ûₜ₋₁} + \operatorname{Σ}(i = 1..p) \text{δᵢ} \cdot \Delta \text{ûₜ₋ᵢ} + \text{epsilonₜ},\;\; \text{an ADF regression without constant on the residuals}\] \[\text{EG} = \text{γ̂} / \operatorname{SE}(\text{γ̂}),\;\; \text{compared with the MacKinnon (1994, 2010) cointegration critical values and p-values for two variables}\] \[p = \text{the number of lags that minimises the AIC},\;\; \text{up to max\_lag (same default as get\_augmented\_dickey\_fuller)}\]

For more information about the method, see the following paper:

  • Engle, R.F. and Granger, C.W.J. (1987). “Co-integration and Error Correction: Representation, Estimation, and Testing.” Econometrica, 55(2), 251-276.

Also known as: EG test, residual-based cointegration test, pairs-trading test.

Engle-Granger Cointegration in Python or via the MCP

Install the Finance Toolkit with:

pip install financetoolkit -U

Then call get_engle_granger_cointegration as shown below.

from financetoolkit import Toolkit

toolkit = Toolkit(
    ["KO", "PEP"],
    api_key="FINANCIAL_MODELING_PREP_KEY",
    start_date="2021-01-01",
    end_date="2025-12-31",
)

engle_granger = toolkit.econometrics.get_engle_granger_cointegration()

# The 1% and 10% critical values follow the same pattern as the 5% one.
engle_granger[["Engle-Granger Statistic", "P-Value", "Critical Value 5%", "Cointegrated (5%)"]]

Or, after setting up the Finance Toolkit MCP, ask:

Were the share prices of Coca-Cola and PepsiCo cointegrated from 2021 to 2025, according to the Engle-Granger test at the 5% level?

Which returns:

  Engle-Granger Statistic P-Value Critical Value 5% Cointegrated (5%)
(‘KO’, ‘PEP’) -1.0266 0.8977 -3.341 False
(‘PEP’, ‘KO’) -2.0565 0.4991 -3.341 False

Using Your Own Data

get_engle_granger_cointegration runs on any data, not only prices: ratios, economic indicators, yield curves or your own dataset. Pass a DataFrame, a Series or a dictionary of them to Econometrics as data, with the dates in the rows or the columns, and set changes to how its changes are calculated: "percentage", "log", "difference" for rates and ratios, or None when the data are changes already. For example, for the monthly unemployment rates of the United States and Germany:

from financetoolkit import Economics, Econometrics

economics = Economics(start_date="2000-01-01")

unemployment = economics.get_unemployment_rate(
    countries=["United States", "Germany"], period="monthly"
)

econometrics = Econometrics(data=unemployment, changes="difference")

econometrics.get_engle_granger_cointegration()

Parameters

get_engle_granger_cointegration accepts the following parameters.

ParameterTypeDescriptionExample
periodstrThe data frequency (daily, weekly, monthly, quarterly, or yearly). Defaults to “daily”."weekly"
columnstrThe historical data column to test. Defaults to “Adj Close”."Close"
max_lagintThe maximum number of lagged differences to consider in the underlying ADF test on the residuals. Defaults to the Schwert (1989) rule of thumb, capped at (n - 5) // 3 for short samples.12
include_benchmarkboolWhether to include “Benchmark” among the tickers paired up. Defaults to False.True
roundingint | NoneThe number of decimals to round the results to. Defaults to None.2
Share