WF05 · example_wf_05_optuna_tpe_optimization.py WF05 Optuna TPE Optimization + Walk-Forward
Use Optuna's Tree-structured Parzen Estimator (TPE) to search SMA fast/slow windows over continuous integer ranges, then validate the winning parameters with a rolling walk-forward.
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.
Optimization workflow. Parameter-search workflow over 30 trials; the best observed setting was fast=23, slow=206 · observed in-sample Sharpe 0.6659. This is optimization output, not an investable or independently validated result. The subsequent walk-forward view used slice-diagnostics mode rather than per-fold refitting.
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.
Optimization workflow. Parameter-search workflow over 30 trials; the best observed setting was fast=23, slow=206 · observed in-sample Sharpe 0.6659. This is optimization output, not an investable or independently validated result. The subsequent walk-forward view used slice-diagnostics mode rather than per-fold refitting.
Artifacts
Stable links that can be referenced from GitHub README files or external docs.
Run log
Tail of the execution log for this packet.
18 │ 2009-01 → 2010-12 │ 1.25 │ 2011-01 → 2011-07 │ 1.30
19 │ 2009-07 → 2011-07 │ 1.44 │ 2011-07 → 2011-12 │ -0.25
20 │ 2010-01 → 2011-12 │ 0.59 │ 2012-01 → 2012-07 │ 1.01
21 │ 2010-07 → 2012-07 │ 1.18 │ 2012-07 → 2013-01 │ 1.39
22 │ 2011-01 → 2013-01 │ 0.61 │ 2013-01 → 2013-07 │ 1.92
23 │ 2011-07 → 2013-07 │ 0.89 │ 2013-07 → 2014-01 │ 2.72
24 │ 2012-01 → 2014-01 │ 1.62 │ 2014-01 → 2014-07 │ 1.73
25 │ 2012-07 → 2014-07 │ 1.90 │ 2014-07 → 2015-01 │ 1.05
26 │ 2013-01 → 2015-01 │ 1.76 │ 2015-01 → 2015-07 │ 0.25
27 │ 2013-07 → 2015-07 │ 1.50 │ 2015-07 → 2015-12 │ -0.80
28 │ 2014-01 → 2015-12 │ 0.44 │ 2016-01 → 2016-07 │ 0.25
29 │ 2014-07 → 2016-07 │ 0.03 │ 2016-07 → 2016-12 │ 1.49
30 │ 2015-01 → 2016-12 │ 0.19 │ 2017-01 → 2017-06 │ 2.10
31 │ 2015-07 → 2017-06 │ 0.43 │ 2017-07 → 2018-01 │ 3.50
32 │ 2016-01 → 2018-01 │ 1.40 │ 2018-01 → 2018-07 │ 0.12
33 │ 2016-07 → 2018-07 │ 1.38 │ 2018-07 → 2019-01 │ -0.56
34 │ 2017-01 → 2019-01 │ 0.81 │ 2019-01 → 2019-07 │ 2.67
35 │ 2017-07 → 2019-07 │ 0.79 │ 2019-07 → 2020-01 │ 1.66
36 │ 2018-01 → 2020-01 │ 0.70 │ 2020-01 → 2020-07 │ -0.81
37 │ 2018-07 → 2020-07 │ 0.00 │ 2020-07 → 2020-12 │ 2.53
38 │ 2019-01 → 2020-12 │ 0.54 │ 2021-01 → 2021-07 │ 2.56
39 │ 2019-07 → 2021-07 │ 0.57 │ 2021-07 → 2021-12 │ 1.66
40 │ 2020-01 → 2021-12 │ 0.63 │ 2022-01 → 2022-07 │ -0.19
41 │ 2020-07 → 2022-07 │ 1.46 │ 2022-07 → 2022-12 │ 0.00
42 │ 2021-01 → 2022-12 │ 0.87 │ 2023-01 → 2023-06 │ 0.93
43 │ 2021-07 → 2023-06 │ 0.48 │ 2023-07 → 2024-01 │ 0.53
44 │ 2022-01 → 2024-01 │ 0.25 │ 2024-01 → 2024-07 │ 2.10
45 │ 2022-07 → 2024-07 │ 0.79 │ 2024-07 → 2025-01 │ 0.96
46 │ 2023-01 → 2025-01 │ 1.24 │ 2025-01 → 2025-07 │ 0.26
47 │ 2023-07 → 2025-07 │ 0.64 │ 2025-07 → 2025-12 │ 1.44
────────────────────────────────────────────────────────────────────────────────
AGGREGATE (IN-SAMPLE SLICES — not OOS)
Composite Sharpe (IS slice): 0.75 [5%: 0.47, 95%: 1.06]
CAGR (IS slice): 9.5% Overfit Ratio: 0.72
Max DD (IS slice): -33.0% Efficiency: 1.24
Sharpe Decay: +0.007/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.88 is negative
⚠ Fold 1: slice diagnostics mode — metrics are sliced from the full-period NAV (no per-fold refit)
⚠ Fold 1: OOS Sharpe -0.50 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 5.7 > 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.1 > 2.5
⚠ Fold 12: slice diagnostics mode — metrics are sliced from the full-period NAV (no per-fold refit)
⚠ Fold 12: OOS Sharpe -0.57 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.79 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 -0.62 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.65 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.25 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.0 > 2.5
⚠ Fold 27: slice diagnostics mode — metrics are sliced from the full-period NAV (no per-fold refit)
⚠ Fold 27: OOS Sharpe -0.80 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: overfit ratio 11.6 > 2.5
⚠ Fold 33: slice diagnostics mode — metrics are sliced from the full-period NAV (no per-fold refit)
⚠ Fold 33: OOS Sharpe -0.56 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.81 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.19 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 41: overfit ratio 502.1 > 2.5
⚠ 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.8 > 2.5
⚠ Fold 47: slice diagnostics mode — metrics are sliced from the full-period NAV (no per-fold refit)
⚠ 11/48 folds have OOS Sharpe below 0.0
MetricVerdict(name='overfit_ratio', value=0.7218739859395392, signal='green', description='< 1.5 — robust')
MetricVerdict(name='efficiency', value=1.24109635507224, signal='green', description='> 0.7 — robust transfer')
MetricVerdict(name='sharpe_decay', value=0.00706822603829495, signal='green', description='> -0.01/fold — stable')