Class PurgedKFold
The leak: a sample's LABEL is usually computed from bars that come after it (e.g. "the 5-bar forward return"). Ordinary K-fold happily puts bar 99 in the training set and bar 100 in the test set — but bar 99's label was computed from bars 100–104, so the model has already seen the test answer. The backtest looks skillful; the skill is leakage.
Two defenses, both index arithmetic:
- Purging removes every training sample whose label window
[i, i + labelHorizon]overlaps any test label window. For a contiguous test fold[t0, t1)that means dropping training indices in[t0 - labelHorizon, t0)(labels reach INTO the fold) and[t1, t1 + labelHorizon)(labels reach OUT of it). - Embargo drops a further
embargosamples after the purge zone that follows the test fold. Serial correlation means features just after the test window still echo test-period information even when the label windows don't overlap; the embargo is the buffer for that echo. A common choice is ~1% of n.
So the training set for test fold [t0, t1) is exactly
[0, t0 - labelHorizon) ∪
[t1 + labelHorizon + embargo, n) — hand-checkable, and the tests
do. Every fold's training set must be non-empty or the split refuses:
silently training on nothing is how "great" fold scores happen.
Static, deterministic, research lane. Pair with
OverfitProbability (is the SELECTION process overfit?) and
WalkForwardAnalyzer (the strictly-forward-in-time variant).
-
Nested Class Summary
Nested ClassesModifier and TypeClassDescriptionstatic final recordOne fold: test on[testFrom, testTo), train ontrainIndices(ascending, purged and embargoed). -
Method Summary
Modifier and TypeMethodDescriptionstatic List<PurgedKFold.Split> splits(int n, int k, int labelHorizon, int embargo)
-
Method Details
-
splits
- Parameters:
n- number of samples (bars/observations), ≥ 2·kk- number of folds, ≥ 2labelHorizon- bars each label looks ahead (0 = label known at the sample's own bar; 5 = 5-bar forward return)embargo- extra bars dropped after the post-test purge zone
-