WF01 · example_wf_01_rolling_walkforward.py WF01 Rolling Walk-Forward Validation
Run a normal SMA(50/200) trend strategy, then inspect its temporal robustness with a ROLLING walk-forward and an explicit pre-OOS purge gap.
Review the source on GitHub or return to all strategies.
What this example shows
This example shows how to build a workflow-level walk-forward validation research example with fold construction, parameter selection, diagnostics, logging and evidence capture.
The goal is educational: to demonstrate the full workflow for validation setup, search space definition, fold diagnostics, objective tracking and reproducible run logging.
Walk-forward diagnostic. 48-fold slice diagnostic from one full-period run. It demonstrates window construction, pre-OOS purging and diagnostic reporting, but it is not an independent per-fold refit or out-of-sample result.
Run setup
The ordinary configuration used by the published run—kept visible here and in the Python source.
Dashboard
Compact evidence view built from the same run packet: metrics, plots and run metadata.
Metrics
This workflow emits diagnostics rather than a standard performance table.
Walk-forward diagnostic. 48-fold slice diagnostic from one full-period run. It demonstrates window construction, pre-OOS purging and diagnostic reporting, but it is not an independent per-fold refit or out-of-sample result.
Artifacts
Stable links that can be referenced from GitHub README files or external docs.
Run log
Tail of the execution log for this packet.
24 │ 2012-01 → 2014-01 │ 1.59 │ 2014-01 → 2014-07 │ 1.93
25 │ 2012-07 → 2014-07 │ 1.93 │ 2014-07 → 2015-01 │ 1.00
26 │ 2013-01 → 2015-01 │ 1.80 │ 2015-01 → 2015-07 │ 0.24
27 │ 2013-07 → 2015-07 │ 1.55 │ 2015-07 → 2015-12 │ -0.34
28 │ 2014-01 → 2015-12 │ 0.61 │ 2016-01 → 2016-07 │ 0.37
29 │ 2014-07 → 2016-07 │ 0.21 │ 2016-07 → 2016-12 │ 1.29
30 │ 2015-01 → 2016-12 │ 0.34 │ 2017-01 → 2017-06 │ 1.96
31 │ 2015-07 → 2017-06 │ 0.57 │ 2017-07 → 2018-01 │ 3.64
32 │ 2016-01 → 2018-01 │ 1.38 │ 2018-01 → 2018-07 │ -0.01
33 │ 2016-07 → 2018-07 │ 1.30 │ 2018-07 → 2019-01 │ -0.54
34 │ 2017-01 → 2019-01 │ 0.73 │ 2019-01 → 2019-07 │ 2.49
35 │ 2017-07 → 2019-07 │ 0.66 │ 2019-07 → 2020-01 │ 1.72
36 │ 2018-01 → 2020-01 │ 0.58 │ 2020-01 → 2020-07 │ -0.35
37 │ 2018-07 → 2020-07 │ 0.17 │ 2020-07 → 2020-12 │ 2.43
38 │ 2019-01 → 2020-12 │ 0.65 │ 2021-01 → 2021-07 │ 2.56
39 │ 2019-07 → 2021-07 │ 0.71 │ 2021-07 → 2021-12 │ 1.66
40 │ 2020-01 → 2021-12 │ 0.76 │ 2022-01 → 2022-07 │ -0.49
41 │ 2020-07 → 2022-07 │ 1.25 │ 2022-07 → 2022-12 │ 0.88
42 │ 2021-01 → 2022-12 │ 0.93 │ 2023-01 → 2023-06 │ 0.81
43 │ 2021-07 → 2023-06 │ 0.52 │ 2023-07 → 2024-01 │ 0.65
44 │ 2022-01 → 2024-01 │ 0.34 │ 2024-01 → 2024-07 │ 1.94
45 │ 2022-07 → 2024-07 │ 1.02 │ 2024-07 → 2025-01 │ 1.04
46 │ 2023-01 → 2025-01 │ 1.20 │ 2025-01 → 2025-07 │ 0.14
47 │ 2023-07 → 2025-07 │ 0.66 │ 2025-07 → 2025-12 │ 1.41
────────────────────────────────────────────────────────────────────────────────
AGGREGATE (IN-SAMPLE SLICES — not OOS)
Composite Sharpe (IS slice): 0.71 [5%: 0.44, 95%: 1.00]
CAGR (IS slice): 9.1% Overfit Ratio: 0.71
Max DD (IS slice): -33.0% Efficiency: 1.25
Sharpe Decay: +0.011/fold
Deflated Sharpe: n/a (in-sample; DSR not meaningful without independent trials)
PBO: n/a
(PBO unavailable: requires per-trial OOS evaluation; set WalkForwardConfig.pbo_trials=K (>=2) with an optimizer to evaluate top-K trials OOS per fold)
WARNINGS
⚠ PBO unavailable: requires per-trial OOS evaluation; set WalkForwardConfig.pbo_trials=K (>=2) with an optimizer to evaluate top-K trials OOS per fold
⚠ Walk-forward mode=slice_diagnostics: metrics are computed from a full-period NAV; pass backtester_factory for per-fold refit.
⚠ Fold 0: slice diagnostics mode — metrics are sliced from the full-period NAV (no per-fold refit)
⚠ Fold 0: OOS Sharpe -0.85 is negative
⚠ Fold 1: slice diagnostics mode — metrics are sliced from the full-period NAV (no per-fold refit)
⚠ Fold 1: OOS Sharpe -0.63 is negative
⚠ Fold 2: slice diagnostics mode — metrics are sliced from the full-period NAV (no per-fold refit)
⚠ Fold 3: slice diagnostics mode — metrics are sliced from the full-period NAV (no per-fold refit)
⚠ Fold 4: slice diagnostics mode — metrics are sliced from the full-period NAV (no per-fold refit)
⚠ Fold 5: slice diagnostics mode — metrics are sliced from the full-period NAV (no per-fold refit)
⚠ Fold 6: slice diagnostics mode — metrics are sliced from the full-period NAV (no per-fold refit)
⚠ Fold 6: overfit ratio 4.1 > 2.5
⚠ Fold 7: slice diagnostics mode — metrics are sliced from the full-period NAV (no per-fold refit)
⚠ Fold 8: slice diagnostics mode — metrics are sliced from the full-period NAV (no per-fold refit)
⚠ Fold 9: slice diagnostics mode — metrics are sliced from the full-period NAV (no per-fold refit)
⚠ Fold 10: slice diagnostics mode — metrics are sliced from the full-period NAV (no per-fold refit)
⚠ Fold 11: slice diagnostics mode — metrics are sliced from the full-period NAV (no per-fold refit)
⚠ Fold 11: overfit ratio 3.0 > 2.5
⚠ Fold 12: slice diagnostics mode — metrics are sliced from the full-period NAV (no per-fold refit)
⚠ Fold 12: OOS Sharpe -0.35 is negative
⚠ Fold 12: overfit ratio inf > 2.5
⚠ Fold 13: slice diagnostics mode — metrics are sliced from the full-period NAV (no per-fold refit)
⚠ Fold 13: OOS Sharpe -1.77 is negative
⚠ Fold 13: overfit ratio inf > 2.5
⚠ Fold 14: slice diagnostics mode — metrics are sliced from the full-period NAV (no per-fold refit)
⚠ Fold 14: OOS Sharpe -1.26 is negative
⚠ Fold 15: slice diagnostics mode — metrics are sliced from the full-period NAV (no per-fold refit)
⚠ Fold 16: slice diagnostics mode — metrics are sliced from the full-period NAV (no per-fold refit)
⚠ Fold 16: OOS Sharpe -0.66 is negative
⚠ Fold 16: overfit ratio inf > 2.5
⚠ Fold 17: slice diagnostics mode — metrics are sliced from the full-period NAV (no per-fold refit)
⚠ Fold 18: slice diagnostics mode — metrics are sliced from the full-period NAV (no per-fold refit)
⚠ Fold 19: slice diagnostics mode — metrics are sliced from the full-period NAV (no per-fold refit)
⚠ Fold 19: OOS Sharpe -0.53 is negative
⚠ Fold 19: overfit ratio inf > 2.5
⚠ Fold 20: slice diagnostics mode — metrics are sliced from the full-period NAV (no per-fold refit)
⚠ Fold 21: slice diagnostics mode — metrics are sliced from the full-period NAV (no per-fold refit)
⚠ Fold 22: slice diagnostics mode — metrics are sliced from the full-period NAV (no per-fold refit)
⚠ Fold 23: slice diagnostics mode — metrics are sliced from the full-period NAV (no per-fold refit)
⚠ Fold 24: slice diagnostics mode — metrics are sliced from the full-period NAV (no per-fold refit)
⚠ Fold 25: slice diagnostics mode — metrics are sliced from the full-period NAV (no per-fold refit)
⚠ Fold 26: slice diagnostics mode — metrics are sliced from the full-period NAV (no per-fold refit)
⚠ Fold 26: overfit ratio 7.5 > 2.5
⚠ Fold 27: slice diagnostics mode — metrics are sliced from the full-period NAV (no per-fold refit)
⚠ Fold 27: OOS Sharpe -0.34 is negative
⚠ Fold 27: overfit ratio inf > 2.5
⚠ Fold 28: slice diagnostics mode — metrics are sliced from the full-period NAV (no per-fold refit)
⚠ Fold 29: slice diagnostics mode — metrics are sliced from the full-period NAV (no per-fold refit)
⚠ Fold 30: slice diagnostics mode — metrics are sliced from the full-period NAV (no per-fold refit)
⚠ Fold 31: slice diagnostics mode — metrics are sliced from the full-period NAV (no per-fold refit)
⚠ Fold 32: slice diagnostics mode — metrics are sliced from the full-period NAV (no per-fold refit)
⚠ Fold 32: OOS Sharpe -0.01 is negative
⚠ Fold 32: overfit ratio inf > 2.5
⚠ Fold 33: slice diagnostics mode — metrics are sliced from the full-period NAV (no per-fold refit)
⚠ Fold 33: OOS Sharpe -0.54 is negative
⚠ Fold 33: overfit ratio inf > 2.5
⚠ Fold 34: slice diagnostics mode — metrics are sliced from the full-period NAV (no per-fold refit)
⚠ Fold 35: slice diagnostics mode — metrics are sliced from the full-period NAV (no per-fold refit)
⚠ Fold 36: slice diagnostics mode — metrics are sliced from the full-period NAV (no per-fold refit)
⚠ Fold 36: OOS Sharpe -0.35 is negative
⚠ Fold 36: overfit ratio inf > 2.5
⚠ Fold 37: slice diagnostics mode — metrics are sliced from the full-period NAV (no per-fold refit)
⚠ Fold 38: slice diagnostics mode — metrics are sliced from the full-period NAV (no per-fold refit)
⚠ Fold 39: slice diagnostics mode — metrics are sliced from the full-period NAV (no per-fold refit)
⚠ Fold 40: slice diagnostics mode — metrics are sliced from the full-period NAV (no per-fold refit)
⚠ Fold 40: OOS Sharpe -0.49 is negative
⚠ Fold 40: overfit ratio inf > 2.5
⚠ Fold 41: slice diagnostics mode — metrics are sliced from the full-period NAV (no per-fold refit)
⚠ Fold 42: slice diagnostics mode — metrics are sliced from the full-period NAV (no per-fold refit)
⚠ Fold 43: slice diagnostics mode — metrics are sliced from the full-period NAV (no per-fold refit)
⚠ Fold 44: slice diagnostics mode — metrics are sliced from the full-period NAV (no per-fold refit)
⚠ Fold 45: slice diagnostics mode — metrics are sliced from the full-period NAV (no per-fold refit)
⚠ Fold 46: slice diagnostics mode — metrics are sliced from the full-period NAV (no per-fold refit)
⚠ Fold 46: overfit ratio 8.5 > 2.5
⚠ Fold 47: slice diagnostics mode — metrics are sliced from the full-period NAV (no per-fold refit)
⚠ 12/48 folds have OOS Sharpe below 0.0
NOTE: slice_diagnostics mode — the metrics above are IN-SAMPLE
slices of ONE full-period run, not out-of-sample evidence.
Set QJ_WF_MODE=per_fold_refit for honest OOS validation.
Walk-forward traffic lights (IN-SAMPLE — indicative only):
MetricVerdict(name='overfit_ratio', value=0.7139517596318669, signal='green', description='< 1.5 — robust')
MetricVerdict(name='efficiency', value=1.2521303971788589, signal='green', description='> 0.7 — robust transfer')
MetricVerdict(name='sharpe_decay', value=0.01119285763359559, signal='green', description='> -0.01/fold — stable')