Combinatorial Purged K-Fold

Combinatorial Purged K-Fold (CPCV) (López de Prado 2018) generalises PurgedKFold by exhaustively testing all combinations of n_test_folds out of k time-ordered blocks rather than testing each block exactly once. This produces C(k, ntestfolds) train/test pairs and eliminates the path-dependency of standard walk-forward validation.

Motivation

Standard walk-forward CV (including PurgedKFold) evaluates the model on each temporal block exactly once, in a fixed order. The performance estimate is therefore a single path through the data — it depends on the specific temporal order in which the folds appear. Two practitioners using the same model and the same data can obtain different estimates simply by choosing different fold boundaries.

CPCV addresses this by averaging over all C(k, ntestfolds) possible test-set combinations. The result is a distribution of performance estimates (one per combination) rather than a single number, and the average is path-independent.

How it works

  1. Sort observations by time= and assign them to k contiguous temporal blocks.
  2. Enumerate every combination of n_test_folds blocks out of k using the combinatorial formula C(k, ntestfolds).
  3. For each combination:
    • Test set: union of the observations in the selected blocks.
    • Exclusion zone: purge observations immediately before each test block and embargo observations immediately after each test block are removed from train (same semantics as PurgedKFold).
    • Train set: all observations outside the test set and exclusion zone.
  4. Return a CrossValidationSplit with one fold per combination.

CPCV vs. PurgedKFold

PurgedKFoldCombinatorialPurgedKFold
Number of foldskC(k, ntestfolds)
Each obs in testExactly onceC(k−1, ntestfolds−1) times
Path dependencyYesNo
Provides performance distributionNoYes
n_test_folds = 1Equivalent

Setting n_test_folds = 1 recovers PurgedKFold exactly (same k folds, same purge and embargo).

Choosing k and ntestfolds

GoalGuidance
Eliminate path dependency, moderate costk=6, ntestfolds=2 → 15 folds
Richer performance distributionk=8, ntestfolds=3 → 56 folds
Reproduce PurgedKFoldntestfolds=1
Largest test setsntestfolds close to k/2

The number of folds grows combinatorially. For k=10 and ntestfolds=4 you get C(10,4) = 210 folds — each is fast to evaluate but the total run time is proportional to the fold count.

API reference

References

López de Prado, M. Advances in Financial Machine Learning. Wiley, 2018, §12 ("Backtesting through Cross-Validation").