- backtesting
- walk-forward validation
- lookahead bias
- long/short signals
- Sharpe ratio
- backtest overfitting
Walk-Forward Backtest — Would You Wire the Money? 💸
A fund manager slides a pitch deck across the table: "Our momentum model returned 31% a year for a decade. Sharpe ratio 2.4. Fully backtested." Allocators at pension funds and endowments hear this pitch weekly — and most versions of it die under a single question: how exactly was that backtest run?
There are two classic ways a beautiful backtest lies:
- ›Lookahead bias — the model was trained on data it could not have had at the time. Even one day of leakage can turn garbage into gold.
- ›Backtest overfitting — 500 variants were tried on the same history, and you are being shown the luckiest one.
Today you build a backtest that avoids sin #1 by construction — and you learn how to smell sin #2.
🧠 The mental model: an exam with only past textbooks
A walk-forward backtest is a student who must answer Tuesday's exam question using only books published up to Monday night. For Wednesday's question, Tuesday's book joins the shelf — the shelf grows, but it never contains tomorrow's answers. Concretely:
Train on: rows [i-120 ... i-1] <- a rolling 120-day window of the past
Predict: row i's target <- the NEXT day's return, strictly out of sample
Step: i -> i+1 <- slide forward, retrain, repeat
One subtlety that catches even professionals: the P&L must be booked on the return the model predicted — the next day's — not on the current row's return, because that value was the final training label one step earlier. Book the wrong day and your "out-of-sample" backtest quietly grades the model on data it has already seen.
If you have run rolling or recursive out-of-sample forecasts in econometrics (pseudo out-of-sample evaluation), this is exactly that idea — applied to a trading rule.
Why not a single split, or k-fold?
| Method | Respects time? | Verdict for market data |
|---|---|---|
| Random k-fold CV | ❌ shuffles future days into training | lookahead bias — silently inflates every metric |
| One train/test split | ✅ | honest, but a single verdict from a single regime |
| Walk-forward | ✅ | hundreds of out-of-sample verdicts across bull, bear and sideways markets |
From forecast to P&L in three lines
- ›Signal — the model predicts tomorrow's return: positive → go long (+1), negative → go short (-1).
- ›Strategy return —
signal * actual_return: you profit on up-days you were long and down-days you were short, and lose when you are wrong-footed. - ›Sharpe ratio —
mean(daily returns) / std(daily returns) * sqrt(252): risk-adjusted performance, annualised. No risk-free subtraction here — this is a directional timing strategy, fully long or fully short one asset each day (never both), so it always carries ±100% net exposure. We drop the risk-free rate as a simplification (it is small at daily frequency); a fully rigorous Sharpe uses excess returns, as the long-only ROI & Sharpe Calculator does. Rule of thumb: above 1 is good, above 2 is suspicious, above 3 means check your code.
Backtest overfitting — the silent career-ender
Bailey, Borwein, López de Prado and Zhu formalised the probability of backtest overfitting (PBO): when a strategy is chosen because it had the best backtest among many trials, the winning Sharpe is mostly selection luck, and PBO estimates how likely that winner is to disappoint out of sample. Their blunt conclusion: a stellar Sharpe discovered after enough attempts carries almost no evidence of skill.
Set your expectations for today accordingly: the price series below is a seeded synthetic random walk — by construction there is nothing to predict, so a no-skill strategy should earn a long-run Sharpe of roughly 0, plus whatever the small drift donates. Whatever number your backtest prints is drift plus the luck of one particular seed — not alpha. If a random-walk backtest ever shows you a gorgeous Sharpe, suspect the backtest before you toast the strategy.
Reading the report
Your program prints five numbers. Learn to scan them like an allocator:
| Line | What it tells you | Red flag |
|---|---|---|
Strategy Sharpe | risk-adjusted return, annualised | too good (above 2) on unexciting data |
Total Return | compounded P&L over the test span | meaningless without the risk taken to earn it |
Win Rate | share of profitable days | 50–55% is typical even for genuinely good strategies |
Longs / Shorts | what the signal rule is actually doing | Shorts: 0 means the model is being ignored |
Observations | number of out-of-sample bets | small samples make every statistic unstable |
Your Task
The walk-forward engine below is complete except for one line: the signal rule. Right now signal = 1 on every single day — permanently long. That is not a strategy; it is buy-and-hold wearing a lab coat, and the report betrays it: Shorts: 0.
Fix the rule: +1 when the model predicts a positive return, -1 when it predicts a negative one — so the model's view actually drives the book.
Predict before you run: on a random walk with a small positive drift, will the long/short version beat always-long? And what would a "no-skill" Sharpe look like here? Run it and check yourself.