CUSUM Filter Explained: Event Sampling in Financial Machine Learning
Learn how the symmetric CUSUM filter selects financial event times from cumulative log returns, how to set its threshold, and how it differs from labels and stability tests.
In this guideThe filter asks when a move is large enough to mark an event
Short summary
A financial CUSUM filter selects times when cumulative movement in a chosen series crosses a threshold. It can turn an every-bar dataset into a smaller set of event starts, but it does not predict which way price will move next, assign the event's future label, or guarantee that the resulting sample is independent.
The filter asks when a move is large enough to mark an event
If a model creates one observation for every bar, quiet stretches can contribute many rows while a meaningful sequence of price changes is still unfolding. A CUSUM filter offers a different sampling rule: keep a running total of upward and downward changes, then mark an event when one cumulative total crosses a chosen threshold. The result is a set of timestamps rather than equally spaced observations.
The name comes from cumulative-sum procedures used in quality control to monitor sustained departures from a target. E. S. Page's 1954 paper on continuous inspection schemes is an early foundation. In financial machine learning, Chapter 2 of *Advances in Financial Machine Learning* presents a symmetric CUSUM filter as one way to sample events from market data. The Mlfin.py filtering guide likewise describes selecting timestamps from cumulative upside or downside movement.
This financial adaptation is an event-selection rule. It is not the same as a statistical CUSUM test of regression-parameter stability. The latter builds a test statistic and reference distribution to assess whether model parameters remain stable; it answers a hypothesis-testing question, not which price observations should start event labels. See the guide to CUSUM regression-stability tests for that separate method.
Accumulate positive and negative log returns separately
Let Pₜ be a strictly positive closing price and let rₜ = ln(Pₜ/Pₜ₋₁) be its log return. Starting both accumulators at zero, define:
S⁺ₜ = max(0, S⁺ₜ₋₁ + rₜ)
S⁻ₜ = min(0, S⁻ₜ₋₁ + rₜ)
The positive sum retains an uninterrupted upward run; the negative sum retains a downward run. A symmetric filter triggers an event when S⁺ₜ > hₜ or S⁻ₜ < −hₜ. Under the convention used here, the accumulator that triggered is reset to zero and begins collecting again on later observations. A fixed threshold h gives a constant cumulative log-return hurdle. A dynamic threshold hₜ can adjust for a scale such as recent volatility.
Implementations differ at the exact boundary. The Mlfin.py guide describes a trigger at or above the threshold, while its current implementation uses strict comparisons (> and <). This article follows the code's strict-crossing convention. If a cumulative sum equals h exactly, that implementation waits for a later observation to exceed it. Record the comparison and reset rules alongside the filter so another researcher can reproduce the event timestamps.
Work through a hypothetical price path
Suppose the close sequence is $100.00 → $100.20 → $100.40 → $100.60 → $100.40 → $100.20 → $100.00. Set a fixed log-return threshold h = 0.005, or 0.5%. The first three log returns sum to ln(100.60/100.00) = 0.005982, about 0.598%. The positive accumulator therefore crosses h at the $100.60 close and emits an event timestamp.
At that point, reset the positive accumulator. The next three downward changes sum to ln(100.00/100.60) = −0.005982, about −0.598%, so the negative accumulator crosses −h at the $100.00 close and emits another event. No single step in either run is 0.5%; the filter responds to the accumulated move. These invented prices illustrate the arithmetic, not market data, an executable fill, or evidence of a profitable signal.
An event timestamp records when the rule fired. It does not mean an order could have traded at that closing price. If the final close is needed to calculate the trigger, the event is known only after that bar closes; a backtest that enters at the same close needs an execution assumption that supports it. Otherwise, align the decision with the next available quote or bar and include spread, fees, slippage, and market impact.
Set the threshold in units the data can support
A fixed h is easy to reproduce but represents a fixed move size. The same 0.5% hurdle can be difficult to reach in a quiet market and easy to reach during a volatile period. A volatility-scaled threshold can define hₜ = kσₜ, where k is a chosen multiplier and σₜ is estimated using information available before the return being tested. This makes the hurdle responsive to a declared scale; it does not make event counts equal or guarantee that the events have comparable risk.
Thresholds are design choices, not defaults that work for every asset or bar frequency. A very small threshold can produce many nearby events and retain microstructure noise. A large threshold can leave long gaps and remove potentially useful observations. Compare event counts and inter-event durations across time periods, instruments, and regimes. Select parameters inside the training history, then preserve them for the untouched evaluation period instead of choosing the threshold that makes the final backtest look best.
The transform matters too. Log returns require Pₜ > 0; they are not defined for zero or negative prices. Some futures markets have traded at nonpositive prices. For such data, use a suitable change measure, such as a prespecified arithmetic difference or a carefully justified shifted transform, and express h in those units. Do not pass a nonpositive series into code that takes logarithms. Also check adjusted closes, contract rolls, missing bars, and bad prints: a data artifact can look like a large cumulative move and create a false event.
Event sampling is separate from bars, features, and labels
Time bars are created on a clock schedule. Dollar or volume bars close after a specified trading-activity amount. A CUSUM filter instead flags observations when directional cumulative movement reaches its threshold; the selected timestamps are irregular and do not represent equal volume, equal risk, or statistically independent samples. The Mlfin.py guide describes its output as event timestamps for later sampling.
Keep the steps in order: define the input series and causal threshold, select event starts, build features using only information available at each start, and then define the future outcome. The triple-barrier event-labeling guide explains one way to assign that later outcome. A CUSUM event is not a +1 or −1 label, and an upward threshold crossing is not a buy recommendation. If events' forward label periods overlap, validation still needs to account for the shared outcomes.
The filter can also be applied to another strictly positive series if its logarithmic change is meaningful; Mlfin.py documents volatility as one possible example. That flexibility does not make every transformation sensible. State the measured series, units, threshold scale, update timing, reset convention, and handling of missing or nonpositive values. Those details define what an event means in a reproducible study.
Evaluate what the filter changes
Before treating the sampled rows as a better dataset, report how many events the rule produced, when they occurred, their spacing, and which market periods dominate the sample. Compare the distribution of features and future labels with an appropriate baseline sampling rule. Repeatedly trying thresholds, transforms, instruments, or date ranges is model selection; it can create an optimistic result even when the filter itself is causal.
The filter can concentrate analysis around sustained moves, but it cannot ensure stationarity, remove serial dependence, supply a forecasting edge, or account for execution costs. A model trained on events still needs a target tied to a clearly defined question, time-aware validation, and realistic cost and fill assumptions. Treat the CUSUM rule as one explicit sampling decision in the research pipeline, not as evidence that an event is tradable.
Common questions
Q1Does a CUSUM filter tell me whether to buy or sell?
No. It identifies timestamps at which accumulated movement crosses a rule. Directional prediction, position choice, execution, and any future outcome label are separate tasks.
Q2Is a CUSUM filter the same as a CUSUM regression-stability test?
No. The financial filter samples event times from a chosen series. A regression-stability test evaluates whether model parameters appear stable under a statistical hypothesis test.
Q3Can I use log-return CUSUM on a zero or negative futures price?
No. The logarithm is undefined there. Choose and document a suitable alternative change measure, then express h in those units.
Sources and further reading
Report an issue
We’ll prepare an email with this article link. Mark receives the report only after you send it
Quick check
Read the guide? Check yourself with 3 questions
Question 01
What does a financial CUSUM filter primarily return?
Choose an answer to see the explanation
Options glossary
Volatility calculated from price changes that occurred under a stated return, sampling-window, and annualization rule; different conventions can produce different values.
Read the deeper guideIV rankThe current IV's position between a lookback-period low and high; one outlier can distort it, so it should not be read as a standalone signal.
Read the deeper guideIV percentileThe percentage of observations in a lookback window below the current IV; the result depends on the data series, window, and treatment of missing or extreme values.
Read the deeper guide