QuantJourney Backtester

QuantJourney Backtester

Share product feedback

✓

Thank you.

Your note is now in the QuantJourney inbox.

WF02 · example_wf_02_expanding_walkforward.py

WF02 Expanding Walk-Forward Validation

Same SMA(50/200) trend strategy as WF 01, but validated with an EXPANDING walk-forward — the training window grows over time (anchored start, moving end) while the test window slides forward. This mimics a strategy that keeps all history as it accumulates.

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.

UniverseXLB · XLE · XLF · XLI · XLK · XLP · XLU · XLV · XLY
Configured period2000-01-03 → 2026-01-01
BenchmarkSPY · SPDR S&P 500 ETF Trust
Initial capital$100,000
Data sourceyfinance
Engine / snapshot0.10.0 · a35f582c47dcd036
Walk-forward / OptimizationMode
1dGranularity
9Instruments
0Published plots
passedRun status

Dashboard

Compact evidence view built from the same run packet: metrics, plots and run metadata.

48 Folds
Slice diagnostic Mode
12/48 Negative slices

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.

  22 │ 2000-01 → 2013-01       │      0.38 │ 2013-01 → 2013-07       │       1.89
  23 │ 2000-01 → 2013-07       │      0.47 │ 2013-07 → 2014-01       │       2.80
  24 │ 2000-01 → 2014-01       │      0.50 │ 2014-01 → 2014-07       │       1.93
  25 │ 2000-01 → 2014-07       │      0.53 │ 2014-07 → 2015-01       │       1.00
  26 │ 2000-01 → 2015-01       │      0.55 │ 2015-01 → 2015-07       │       0.24
  27 │ 2000-01 → 2015-07       │      0.57 │ 2015-07 → 2015-12       │      -0.34
  28 │ 2000-01 → 2015-12       │      0.52 │ 2016-01 → 2016-07       │       0.37
  29 │ 2000-01 → 2016-07       │      0.51 │ 2016-07 → 2016-12       │       1.29
  30 │ 2000-01 → 2016-12       │      0.52 │ 2017-01 → 2017-06       │       1.96
  31 │ 2000-01 → 2017-06       │      0.54 │ 2017-07 → 2018-01       │       3.64
  32 │ 2000-01 → 2018-01       │      0.57 │ 2018-01 → 2018-07       │      -0.01
  33 │ 2000-01 → 2018-07       │      0.58 │ 2018-07 → 2019-01       │      -0.54
  34 │ 2000-01 → 2019-01       │      0.59 │ 2019-01 → 2019-07       │       2.49
  35 │ 2000-01 → 2019-07       │      0.56 │ 2019-07 → 2020-01       │       1.72
  36 │ 2000-01 → 2020-01       │      0.57 │ 2020-01 → 2020-07       │      -0.35
  37 │ 2000-01 → 2020-07       │      0.50 │ 2020-07 → 2020-12       │       2.43
  38 │ 2000-01 → 2020-12       │      0.53 │ 2021-01 → 2021-07       │       2.56
  39 │ 2000-01 → 2021-07       │      0.58 │ 2021-07 → 2021-12       │       1.66
  40 │ 2000-01 → 2021-12       │      0.59 │ 2022-01 → 2022-07       │      -0.49
  41 │ 2000-01 → 2022-07       │      0.61 │ 2022-07 → 2022-12       │       0.88
  42 │ 2000-01 → 2022-12       │      0.57 │ 2023-01 → 2023-06       │       0.81
  43 │ 2000-01 → 2023-06       │      0.57 │ 2023-07 → 2024-01       │       0.65
  44 │ 2000-01 → 2024-01       │      0.57 │ 2024-01 → 2024-07       │       1.94
  45 │ 2000-01 → 2024-07       │      0.61 │ 2024-07 → 2025-01       │       1.04
  46 │ 2000-01 → 2025-01       │      0.62 │ 2025-01 → 2025-07       │       0.14
  47 │ 2000-01 → 2025-07       │      0.61 │ 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.34
  Max DD (IS slice):     -33.0%    Efficiency:    2.83
  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 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 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 14: overfit ratio inf > 2.5
  ⚠ 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 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 4.4 > 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.33861525119090735, signal='green', description='< 1.5 — robust')
  MetricVerdict(name='efficiency', value=2.830402246453171, signal='green', description='> 0.7 — robust transfer')
  MetricVerdict(name='sharpe_decay', value=0.01119285763359559, signal='green', description='> -0.01/fold — stable')