WFO Walk-Forward Optimization
The only backtesting method that matters — train on the past, test on the future
Learning Objectives
- •Understand walk-forward analysis and why it's the gold standard
- •Learn how to set up proper walk-forward windows
- •Know how to interpret walk-forward results for deployment decisions
Explain Like I'm 5
Walk-forward is straightforward: train your model on January through December, test it on the next January. Then slide the window forward and repeat. Each test period uses ONLY data the model has never seen. This is the closest you can get to simulating live trading inside a backtest. Anything else is lying to yourself.
Think of It This Way
Walk-forward is like a final exam with new questions every time — not the ones you studied. Regular backtesting is like taking a test where you've already seen the answers. Of course you score well. Walk-forward says "prove you actually learned something by answering questions you've never encountered."
1Why Regular Backtesting Is Broken
2Setting Up Walk-Forward Windows
3Walk-Forward Efficiency Ratio
4Visualizing Walk-Forward Performance
Walk-Forward OOS Accuracy by Window
Key Formulas
Walk-Forward Efficiency Ratio
Ratio of out-of-sample to in-sample performance. WFER > 0.5 is good, > 0.8 is excellent. A WFER of 0.91 indicates strong generalization — the model retains most of its predictive power on unseen data.
Hands-On Code
Walk-Forward Framework
import numpy as np
def walk_forward(X, y, model_fn, train_months=12, test_months=3, gap=1):
"""Proper walk-forward backtesting."""
n = len(X)
bars_per_month = n // (train_months + test_months + 12)
train_size = train_months * bars_per_month
test_size = test_months * bars_per_month
results = []
for start in range(0, n - train_size - gap - test_size, test_size):
train_end = start + train_size
test_start = train_end + gap
test_end = test_start + test_size
if test_end > n:
break
X_train, y_train = X[start:train_end], y[start:train_end]
X_test, y_test = X[test_start:test_end], y[test_start:test_end]
model = model_fn()
model.fit(X_train, y_train)
accuracy = (model.predict(X_test) == y_test).mean()
results.append({
'period': len(results),
'accuracy': accuracy,
'n_trades': len(y_test)
})
accs = [r['accuracy'] for r in results]
print(f"=== WALK-FORWARD RESULTS ({len(results)} windows) ===")
print(f"Mean OOS accuracy: {np.mean(accs):.1%}")
print(f"Std OOS accuracy: {np.std(accs):.1%}")
print(f"Min OOS accuracy: {np.min(accs):.1%}")
return resultsEach window trains from scratch on past data and tests on genuinely unseen future data. This is the only honest way to evaluate a trading model. No data leakage, no cherry-picking.
Knowledge Check
Q1.What's the main advantage of walk-forward over standard backtesting?
Assignment
Implement walk-forward validation for an XGBoost model with 12-month train and 3-month test windows. Compute WFER. If WFER < 0.5, your model is overfit — simplify it.