CUSUM Test
Calculate the CUSUM test for the stability of the mean of returns over time.
The test fits a constant-mean model to the returns and cumulates the (scaled) OLS residuals into a path that behaves like a Brownian Bridge under the null hypothesis of a stable mean. A stable mean keeps the path close to zero, while a mean shift partway through the series (a structural break, e.g. a regime change) drags the path away from zero; the test statistic is the maximum absolute value of that path.
The formula is as follows:
\[\text{êₜ} = \text{rₜ} - \operatorname{mean}(r),\;\; \text{the OLS residuals of the constant-mean model}\; \text{rₜ} = \mu + \text{eₜ}\] \[\text{CUSUM} = \text{max over}\; k \;\text{of} | \operatorname{Σ}(t = 1..k) \text{êₜ} | / (\text{sigmâ} \cdot \sqrt{n}),\;\; \text{with}\; \text{sigmâ} ^{2} = \text{Σêₜ} ^{2} / (n - 1)\] \[\text{P-Value} = P(\text{sup} | B(s) | > \text{CUSUM}) \text{for}\; a \;\text{Brownian Bridge}\; B (\text{the Kolmogorov distribution}),\;\; \text{with stability rejected when the p-value is below 0.05}\]For more information about the method, see the following paper:
- Ploberger, W., & Kramer, W. (1992). “The CUSUM Test with OLS Residuals.” Econometrica, 60(2), 271-285.
Also known as: CUSUM test, CUSUM of OLS residuals test, Ploberger-Kramer test.
CUSUM Test in Python or via the MCP
Install the Finance Toolkit with:
pip install financetoolkit -U
Then call get_cusum_test as shown below.
from financetoolkit import Toolkit
toolkit = Toolkit(
["BA", "AIR.PA"],
api_key="FINANCIAL_MODELING_PREP_KEY",
start_date="2021-01-01",
end_date="2025-12-31",
)
# The critical values are the asymptotic Brownian Bridge boundaries, so they
# are the same constants for every asset.
toolkit.econometrics.get_cusum_test()
Or, after setting up the Finance Toolkit MCP, ask:
Was the mean daily return of Boeing and Airbus stable from 2021 to 2025, according to the CUSUM test?
Which returns:
| BA | AIR.PA | |
|---|---|---|
| CUSUM Statistic | 0.685425 | 0.441916 |
| P-Value | 0.735342 | 0.989762 |
| Observations | 1291 | 1291 |
| Critical Value 1% | 1.63 | 1.63 |
| Critical Value 5% | 1.36 | 1.36 |
| Critical Value 10% | 1.22 | 1.22 |
| Reject Stability (5%) | 0 | 0 |
Using Your Own Data
get_cusum_test 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_cusum_test()
Parameters
get_cusum_test accepts the following parameters.
| Parameter | Type | Description | Example |
|---|---|---|---|
period | str | The data frequency for returns (daily, weekly, monthly, quarterly, or yearly). Defaults to “daily”. | "weekly" |
within_period | bool | Whether to calculate the test within the specified period or for the entire period. Thus whether to look at the test within a specific year (if period = ‘yearly’) or look at the entirety of all years. Defaults to False. Note that this requires intraday data when period = ‘daily’, since a single day only nests observations when intraday data was fetched. | True |
include_benchmark | bool | Whether to include “Benchmark” among the assets tested. Defaults to False. | True |
rounding | int | None | The number of decimals to round the results to. Defaults to None. | 2 |