Appearance

Personalize your experience

Mode
Accent Color
Layout
Direction
All articles

Walk-Forward Validation, Properly

Why a single in-sample backtest cannot tell you whether a strategy works, how rolling windows fix it, and the degradation ratio that decides whether to deploy.

QuaTick Research12 min read2,187 words
PostLinkedIn
On this page

A backtest over your full history tells you one thing reliably: how good you became at fitting that history. It cannot distinguish a strategy that captures something durable from one that memorised the particular sequence of moves in your sample, because both produce the same beautiful equity curve. Walk-forward validation exists to force that distinction.

How walk-forward differs from a single backtest

Timeline

Windows advance; tuning only ever happens on the left

Parameters are fitted on each in-sample window and applied unchanged to the window that follows it. The out-of-sample segments are then concatenated.

The first in-sample window is where tuning is permitted. The parameters it produces are applied unchanged to the first out-of-sample window. The pair then advances and the fit is repeated, with the second out-of-sample result concatenated onto the first. A final holdout period sits at the end and is examined exactly once, after every other decision has been made.
Every point on the concatenated out-of-sample curve was produced by parameters chosen without seeing it. That is the property a single full-history backtest cannot claim.

In a single backtest you choose parameters using all available data, then report performance on that same data. The result is guaranteed to flatter you, because the parameters were selected precisely because they performed well on those observations.

Walk-forward instead moves a pair of windows through history. Parameters are fitted on the in-sample window, then applied unchanged to the immediately following out-of-sample window. The windows advance, the fit is repeated, and the out-of-sample results are concatenated into a single series. Every point on that series was produced by parameters chosen without seeing it.

Rolling windowAnchored window
In-sample periodFixed length, slides forwardGrows — always starts at the beginning
Adapts to regime changeYes, older data drops outSlowly, old data is never dropped
Data efficiencyLowerHigher
SuitsStrategies whose edge is regime dependentStrategies claiming a structural, persistent edge
RiskRefits to noise in a short windowAverages away a genuine regime shift
Two ways to advance the windows

The performance gap that tells you you have overfitted

Trend chart

A stable in-sample result with an unstable out-of-sample one

Illustrative Sharpe per window. The in-sample series is consistently excellent, which is expected and uninformative. The out-of-sample series is what carries information.

Illustrative Sharpe ratios across six walk-forward windows. In-sample values sit consistently between 2.3 and 2.7. Out-of-sample values swing between 1.3, 0.4, 1.1, negative 0.2, 0.9 and 0.3. The average out-of-sample result is well under half the in-sample average, and the variation between windows is larger than the average itself, so the strategy behaviour is not predictable from window to window.
Consistency across windows matters as much as the average. Out-of-sample results alternating between good and negative describe a strategy you cannot size, whatever the mean says.

Some degradation from in-sample to out-of-sample is normal and expected. The size of the gap is the diagnostic. A strategy that returns a Sharpe of 2.4 in-sample and 0.3 out-of-sample has not been validated; it has been shown to be curve-fitted, quite precisely.

There is no universal threshold, but a workable heuristic: out-of-sample performance below roughly half the in-sample figure is a strong signal that the parameters are fitting noise. Consistency across windows matters at least as much as the average. Out-of-sample results that alternate between excellent and awful describe a strategy whose behaviour you cannot predict, even if the mean looks acceptable.

This is the same trap the common backtesting mistakes piece describes as multiple-testing bias, seen from the validation side. Each iteration through the loop of tune, test, adjust makes the reported figure less meaningful, and nothing in the output warns you that it is happening.

Choosing window lengths

Comparison chart

How much history one in-sample window needs

Window length follows from trade frequency, not from the calendar. This shows the months required for a single in-sample window to contain 300 trades.

Months of data needed for one in-sample window to accumulate three hundred trades, by strategy frequency. A strategy taking two trades a week needs roughly thirty-five months per window, one trade a day needs about fourteen, five trades a day needs about three, and twenty trades a day needs under one. Beyond roughly twenty-four months per window there is not enough usable history to build several windows.
This is why low-frequency strategies are so hard to validate honestly. If one window already consumes three years, there is no room for the several windows the method requires.

Window length is the one parameter of the validation procedure itself, and it needs to be set from the strategy's trade frequency rather than from a calendar convention.

The binding constraint is trade count, not elapsed time. An in-sample window needs enough trades for the fitted parameters to mean something — a few hundred at minimum — and each out-of-sample window needs enough trades for its result to be more than noise. A strategy taking two trades a week cannot be validated on one-month windows no matter how many windows you use.

research/walk_forward.pypython
from dataclasses import dataclass  @dataclassclass WindowResult:    is_start: int    oos_start: int    oos_end: int    params: dict    is_metric: float    oos_metric: float    oos_trades: int  MIN_IS_TRADES = 300MIN_OOS_TRADES = 40  def walk_forward(data, strategy, is_len: int, oos_len: int, step: int, anchored=False):    results: list[WindowResult] = []    cursor = 0     while cursor + is_len + oos_len <= len(data):        is_slice = data[0 : cursor + is_len] if anchored else data[cursor : cursor + is_len]        oos_slice = data[cursor + is_len : cursor + is_len + oos_len]         params, is_metric, is_trades = strategy.optimise(is_slice)        if is_trades < MIN_IS_TRADES:            raise TooFewTrades(f"in-sample has {is_trades} trades, need {MIN_IS_TRADES}")         oos_metric, oos_trades = strategy.evaluate(oos_slice, params)        results.append(            WindowResult(                is_start=0 if anchored else cursor,                oos_start=cursor + is_len,                oos_end=cursor + is_len + oos_len,                params=params,                is_metric=is_metric,                oos_metric=oos_metric,                oos_trades=oos_trades,            )        )        cursor += step     return results  def degradation_ratio(results: list[WindowResult]) -> float:    """Out-of-sample performance as a fraction of in-sample. Below ~0.5 is a warning."""    usable = [r for r in results if r.oos_trades >= MIN_OOS_TRADES]    if not usable:        raise TooFewTrades("no window had enough out-of-sample trades")    is_mean = sum(r.is_metric for r in usable) / len(usable)    oos_mean = sum(r.oos_metric for r in usable) / len(usable)    if is_mean <= 0:        return 0.0    return oos_mean / is_mean  class TooFewTrades(Exception):    pass
The loop is short. The assertions are the part that stops the procedure quietly degrading into a single in-sample fit.

The exceptions do the real work there. Raising when a window has too few trades stops the procedure from producing a confident-looking number derived from a handful of observations, which is the most common way walk-forward gets misapplied — and the resulting figure looks no different from a sound one.

How many strategies survive each stage

Decision funnel

Attrition through a research pipeline

Illustrative proportions rather than measurements. The point is that the attrition is severe and mostly happens after the stage people stop at.

Of a hundred ideas formalised enough to backtest, roughly forty are profitable in sample, which is the easiest bar to clear. About twelve survive walk-forward with an acceptable degradation ratio. Around six survive the application of realistic brokerage, statutory charges and slippage. Roughly four survive paper trading, where failures are usually operational rather than signal related. Perhaps two are still running after a quarter live.
Illustrative figures. Most people stop measuring at the second row, which is precisely the row that carries almost no information about live performance.

It is worth setting expectations about attrition before starting, because the rate at which ideas die is high enough to feel like a mistake rather than the normal operation of the process.

Most ideas fail at the in-sample stage. Of those that pass, most fail walk-forward. Of those that survive walk-forward, several fail once realistic costs and slippage are applied, because a strategy with a small per-trade edge and high turnover is exactly the kind that looks best before costs. Of the survivors, some still fail in paper trading for operational reasons that have nothing to do with the signal.

~0.5
Degradation floor
Out-of-sample below half of in-sample is a red flag
300+
In-sample trades
Per window, before a fit means anything
Once
Times you may use the final holdout
Re-testing it converts it to in-sample

A validation protocol you can actually follow

The order matters — each step can only be done once

  1. Carve off a final holdout period before you begin

    The most recent slice of history. Do not look at it, do not compute anything on it, do not plot it. It is not part of the research loop.

  2. Develop and tune only on the in-sample windows

    This is where you are allowed to iterate freely. Everything you learn here is legitimate.

  3. Run walk-forward over the remaining data

    Both rolling and anchored. Record fitted parameters, per-window metrics and trade counts for every window.

  4. Apply realistic costs to the out-of-sample series

    Brokerage, statutory charges and a slippage assumption that is pessimistic rather than average. Costs before this point were optional; here they are not.

  5. Read the degradation ratio and the parameter stability together

    Either one alone can mislead. A good ratio with unstable parameters is a coincidence you should not fund.

  6. Run the final holdout exactly once

    If it fails, the strategy is finished. Adjusting and re-running is the one action that invalidates everything above it.

Walk-forward validation does not tell you a strategy will make money. It tells you whether the evidence you have is consistent with an edge that exists outside your sample. That is a weaker claim than most backtest reports imply, and it is the strongest claim the data can actually support.

Frequently asked questions

What is walk-forward validation?

A procedure that moves a pair of windows through history: parameters are fitted on an in-sample window, then applied unchanged to the immediately following out-of-sample window. The windows advance, the fit repeats, and the out-of-sample segments are concatenated into one series. Every point on that series was produced by parameters chosen without seeing it, which is what a single full-history backtest cannot claim.

What is the difference between rolling and anchored walk-forward?

A rolling window keeps a fixed in-sample length that slides forward, so old data drops out and the fit adapts to regime change. An anchored window always starts at the beginning of history and grows, using data more efficiently but adapting to regime shifts slowly. Running both is informative: substantial disagreement between them indicates the edge is regime dependent.

How much out-of-sample degradation is acceptable?

Some degradation is normal. As a working heuristic, out-of-sample performance below roughly half the in-sample figure signals that parameters are fitting noise. Consistency across windows matters at least as much as the average, since out-of-sample results alternating between excellent and poor describe unpredictable behaviour even when the mean looks acceptable.

Can I adjust a strategy after a poor walk-forward result and re-test?

Not without invalidating the estimate. Once the out-of-sample result informs a parameter choice, that data has become in-sample and the new out-of-sample figure is not meaningful. The discipline required is to hold back a final validation period, examine it once, and accept the answer.

How long should in-sample and out-of-sample windows be?

Set them from trade count rather than the calendar. An in-sample window needs enough trades for a fit to mean anything, on the order of several hundred, and each out-of-sample window needs enough trades for its result to exceed noise. A strategy taking two trades a week cannot be validated on one-month windows regardless of how many windows are used.

Why does parameter stability across windows matter?

Because it distinguishes a real optimum from a noisy one. If the fitted lookback swings widely between consecutive windows, the optimisation surface is flat or noisy and the optimiser is selecting whatever won by luck. A strategy with parameters that stay in a narrow band is more credible than one with a marginally better out-of-sample average and unstable parameters.

Primary sources

QuaTick Research

Markets & strategy desk

The research desk covers Indian market structure, derivatives mechanics and the regulatory changes that affect how systematic traders operate.

Backtesting Mistakes Specific to Indian Markets

The backtest errors that are specific to NSE and BSE data: expiry rollovers, corporate action adjustment, strike survivorship, circuit limits, and look-ahead bias that passes code review.

QuaTick Research11 min read

Position Sizing for Algo Portfolios

Sizing decides whether an edge compounds or gets margin-called. A comparison of fixed fractional, volatility-targeted and risk-parity sizing, with the lot-size granularity problem Indian F&O adds on top.

QuaTick Risk Desk12 min read

From Paper Trading to Live: A Go-Live Checklist

A go-live checklist for automated strategies: what paper trading cannot test, the failure modes that only appear with real fills, and how to structure a first month live.

QuaTick Risk Desk9 min read

Discussion

Sign in to join the discussion. Your posts and replies show up on your profile.