- Published on
Backtesting through Cross-Validation (CPCV)
- Authors

- Name
- Tails Azimuth
Table of Contents
Backtesting through Cross-Validation (CPCV)
This chapter contrasts the three primary methods for backtesting a quantitative strategy. It argues that the most common method (Walk-Forward) is flawed and easily overfit, while standard Cross-Validation has its own drawbacks. It concludes by introducing a new, more robust method called Combinatorial Purged Cross-Validation (CPCV).
The Walk-Forward (WF) Method
- What It Is: A standard historical simulation. It trains on data from
[0, t]and tests on data att+1, moving forward in time. This is the most common form of "backtesting." - Advantages:
- Has a clear, intuitive historical interpretation.
- Guarantees no information leakage (if purging is used correctly) because the training set always predates the testing set.
- Disadvantages (Critical):
- Tests a Single Path: It only tests the one historical scenario that happened, which is easily overfit.
- Path-Dependent Overfitting: The model's performance is highly dependent on the sequence of historical events (e.g., a 2007-2017 backtest is different from a 2017-2007 backtest).
- Uneven Information: Decisions at the beginning of the backtest are based on much less data than decisions at the end, making results inconsistent.
The Cross-Validation (CV) Method
- What It Is: This method tests a model's performance on "stress scenarios." It splits data into sets, then trains on sets and tests on the 1 held-out set. For example, it might train on 2009-2017 data and then test on the 2008 crisis.
- Goal: The goal is not historical accuracy, but to see how a model (trained on "normal" data) would perform under an unknown stress event.
- Advantages:
- Tests different scenarios, not just the single historical path.
- Every decision is made using an equal amount of training data.
- Uses the entire dataset for testing (no warm-up period).
- Disadvantages:
- Still only produces a single backtest path (by stitching the tests together).
- Leakage is a high risk because the training set can contain future data. Requires purging and embargoing (from Ch. 7).
The Combinatorial Purged Cross-Validation (CPCV) Method
This is the author's novel method, designed to fix the flaws of WF and CV by testing multiple paths to generate a distribution of performance metrics, not just a single number.
What It Is:
- The data is split into groups.
- A test-set size of groups is chosen (where ).
- The algorithm then generates all possible combinations of training/testing splits. For each split, it trains on groups and tests on groups.
- All training sets are purged (and embargoed) to prevent leakage.
- This combinatorial process generates unique, full-length backtest paths.
Number of Paths (): The number of unique backtest paths generated is:
- Example: Using (testing on 2 groups at a time) is a powerful "sweet spot." It generates paths while keeping the training set size large.
How It Solves Overfitting:
- WF and CV produce a single Sharpe Ratio (SR), . This has a high variance () and is easily "cherry-picked" (selection bias).
- CPCV generates different SRs for the same strategy. It produces a distribution of performance, allowing us to analyze the mean SR, .
- The variance of this mean, , is much lower than the variance of a single backtest.
- Because the variance is so much lower, it is much harder to find a "false discovery." CPCV defeats backtest overfitting by forcing the strategy to prove its profitability across many different scenarios (paths), not just the single historical one.
Cross-Validator Design in RiskLabAI
To make these complex cross-validation strategies easy to use and interchangeable, we implement a Factory and Controller design pattern.
CrossValidator(Interface): An abstract base class that defines the common API all validators must implement (split,backtest_paths,backtest_predictions). This ensures that any validator can be used in the same way.CrossValidatorFactory: A simple factory class that constructs the correct validator instance (e.g.,'purgedkfold'or'combinatorialpurged') based on a string name.CrossValidatorController: A high-level controller that acts as the main user-facing class. It uses the factory to create and hold a validator instance, simplifying the workflow.
This design allows a user to switch from a WalkForward backtest to a CombinatorialPurged backtest by changing only one line of code (the validator_type string), promoting rapid and robust experimentation.
API reference
RiskLabAI implements these in Python and Julia (signatures auto-generated from the package source):
| Python | Julia |
|---|---|
| |
| |
| |
| |
| |
| |
| |
| |