13.6 Machine-learning pitfalls: leakage, purged cross-validation, labels
Motivation
Machine-learning tooling assumes i.i.d. samples. Financial samples are serially dependent, labels overlap in time, and the signal-to-noise ratio is tiny (13.3). Under those conditions the standard workflow leaks information from test to training and produces confident, wrong models. López de Prado (2018, ch. 3, 4, 7) codified the main fixes.
Leakage taxonomy#
- Look-ahead features: any feature at that uses data after : smoothed regime probabilities (13.5), full-sample GARCH parameters, centred moving averages, fundamental data stamped with the period end rather than the publication date, index membership as of today (survivorship, M04 · 4.4).
- Preprocessing on the full sample: scaling, PCA, winsorising thresholds or feature selection fitted on all data and then cross-validated.
- Overlapping labels: a label that spans several periods shares information with neighbouring labels.
- Selection on the test set: tuning hyperparameters until the test score is good. This turns the test set into a training set (M04 · 4.6).
Overlapping labels#
Suppose the label for sample is the forward return over days, with i.i.d. daily returns of variance , and samples are taken every day.
Proposition 13.17
.
Proof
and (say ) share the returns , of which there are . Covariance is times that count; each variance is .∎
With , a sample and its neighbour have label correlation 0.95. Ordinary k-fold puts these near-duplicates on both sides of a fold boundary, and a flexible model (a forest, a boosted tree, nearest neighbours) memorises the training neighbour and "predicts" the test sample. The effective number of independent labels is roughly , not .
Purged k-fold with embargo#
Each sample carries an interval : the time its features are observed and the time its label is resolved. Samples are sorted by . Split positions into contiguous test blocks. For a test block let and .
- Purge: a sample may be used for training only ifThen no training label interval intersects any test label interval.
- Embargo: also drop the positions immediately after the block. Training samples just after the test block have features (trailing windows, serially correlated inputs) that overlap the test period; the embargo removes them. A typical is 0.01.
Worked example
daily samples, labels are 20-day forward returns (, clipped at the end), , (embargo 10 samples). Each test block has 200 positions. For the middle fold (positions 400–599): before the block, training requires , so positions 380–399 are purged; after the block, , so positions 600–619 are purged, which also covers the 10-position embargo. The training set has 760 samples instead of 800. The first and last folds lose 20 each (780 training samples). Ordinary k-fold on the middle fold has 420 (train, test) pairs with overlapping label intervals; purged k-fold has none. The reference purged_kfold_splits and count_leaks reproduce these counts; test_m13_purged_cv.py checks the same purge logic on a smaller case (, 10-day labels, middle fold 200–299).
Walk-forward evaluation (train on the past, test on the next block, roll) avoids training on the future altogether but uses each observation for testing only once and in one historical path. Purged k-fold uses all data for testing, at the cost of training on data from after the test period, which is acceptable only if the data-generating process is stable. Combinatorial purged CV (López de Prado 2018, ch. 12) generates many backtest paths and feeds the probability-of-backtest-overfitting analysis in M04 · 4.6. sklearn.model_selection.TimeSeriesSplit does not purge or embargo.
Triple-barrier labels#
Fixed-horizon labels ("sign of the 10-day return") ignore the path: a trade that hit its stop on day 2 and recovered by day 10 is labelled a win. The triple-barrier method labels event by the first of three barriers touched by :
In practice pt and sl are multiples of a volatility estimate known at (for example the GARCH forecast of 13.4), so barriers widen in stressed markets. The resolution time is exactly the label end time that purging needs.
Examplethe test's path
Prices 100, 101, 103, 106, 104, 99, 96, 97, 98, 100 at positions 0–9, barriers in log terms, vertical 4 bars. Event 0: at , label +1. Event 3 (106): at , label −1. Event 5 (99): the path 96, 97, 98, 100 stays between and in log return, inside both barriers, so the vertical barrier at gives 0. Event 7: vertical barrier clipped to , label 0.
Meta-labelling#
A primary model (a rule from M04 · 4.2, or a discretionary view) decides the side: long or short. A secondary model is trained on the meta-label
that is, "was acting on this signal profitable?". Its predicted probability decides whether and how much to trade. This separates direction (where domain knowledge lives) from sizing (where statistical learning helps), and it raises precision at the cost of recall.
Example in R-multiples (M03): a breakout rule with precision 40%, average win 2R and average loss 1R has expectancy per trade. If a meta-model discards half the signals and the kept half has precision 52% with the same payoff sizes, expectancy per kept trade becomes , and expectancy per original signal is . Whether that survives depends on the meta-model's own out-of-sample validity: it is another model fitted on the same history, and the multiple-testing penalty of M04 · 4.6 applies to it too.
Practitioner notes
- The single most common leak in retail ML for trading: computing features with
rolling(...).mean()and labels withshift(-h)in one DataFrame, then shuffling rows into k-fold. Do not shuffle; purge. - Report the number of independent labels. Ten years of daily data with 20-day labels is about 125 independent observations, not 2,500.
- Prefer few, economically motivated features. With around 0.5% (13.3), any model with enough capacity to fit noise will.