Strategy Optimization Pipeline
A comprehensive technical reference for BitPredict's 13-step strategy optimization pipeline. Each phase is explained with its mathematical foundations, practical rationale, and configuration guidance: from the initial automated hyperparameter search through multiple-testing correction, robustness testing, walk-forward validation, Monte Carlo simulation, and the final holdout gate.
Pipeline Overview
The optimization pipeline is a sequential multi-phase validation framework designed to combat the single greatest threat in quantitative strategy development: overfitting. A naive grid-search or random-search optimizer will reliably find parameter configurations that produce spectacular in-sample performance and catastrophic out-of-sample failure. The pipeline counters this through layered, independent testing phases: each phase attempts to falsify the hypothesis that a candidate configuration represents genuine predictive power rather than statistical artifact.
The pipeline operates in three conceptual stages:
- Search (Phases 1–2): Data splitting and hyperparameter optimization via the Tree-structured Parzen Estimator (TPE).
- Validate (Phases 3–11): A battery of statistical and robustness tests: multiple-testing correction, sensitivity analysis, noise injection, regime consistency, synthetic data, temporal stability, walk-forward OOS, and Monte Carlo simulation.
- Gate (Phases 12–14): Holdout evaluation on completely unseen data, signal correlation checks, and persistence to the database.
Each phase that fails eliminates candidate configurations from the pool: only the top_n candidates that survive all enabled phases are persisted. This “gauntlet” design means weak configurations are eliminated early, saving compute on expensive later phases.
Data Preparation & Train/Val/Holdout Splitting
Before any optimization begins, the full historical dataset is partitioned into three mutually exclusive segments: train, validation, and holdout. This is the single most important structural decision in the pipeline: an incorrectly designed split invalidates every downstream result.
Temporal Ordering Constraint
Financial data is serially correlated. Random shuffling or k-fold cross-validation, standard in machine learning, introduces look-ahead bias by allowing the model to train on data from the future relative to the test set. All splits are strictly temporal: the train period precedes the validation period, which precedes the holdout period. No data point from a later split ever influences training or validation in an earlier split.
Default Split: 70/15/15
The default allocation of 70% train, 15% validation, and 15% holdout balances three competing requirements:
- Train set must be large enough for the search to distinguish signal from noise across potentially hundreds of trials. With too little training data (e.g., 50%), the TPE sampler cannot build a reliable surrogate model of the objective surface.
- Validation set must cover enough market regimes for walk-forward and Monte Carlo phases to produce statistically meaningful results. A 15% validation split on a 3-year dataset yields ~5.4 months: typically encompassing at least one full regime cycle.
- Holdout set must remain untouched until the final gate. It must be large enough that the final Sharpe ratio and drawdown estimates have acceptable standard errors. At 15%, a 3-year dataset provides ~5.4 months of truly unseen data.
Proper temporal splitting is the foundation of honest backtesting. Every study of quantitative finance failures, from Bailey & López de Prado (2014) to Marcos López de Prado's “Advances in Financial Machine Learning” (2018), identifies data leakage through incorrect splitting as the primary cause of overestimated strategy performance. A strategy that survives rigorous temporal out-of-sample testing is far more likely to perform in live markets than one validated only in-sample.
Do not reduce the holdout split below 10% to “give the optimizer more data.” A tiny holdout period (e.g., 3%) yields Sharpe estimates with such wide confidence intervals that they are statistically meaningless. The holdout test becomes a coin flip rather than a gate.
| Parameter | Type | Default | Description |
|---|---|---|---|
| splits.train | percentage (0–100) | 70 | Fraction of data used for hyperparameter optimization. More train data yields a better surrogate model but less data for validation and holdout phases. |
| splits.val | percentage (0–100) | 15 | Fraction reserved for walk-forward validation, Monte Carlo simulation, and other train+val phases. |
| splits.holdout | percentage (0–100) | 15 | Fraction locked until the final holdout phase. Never touched before final evaluation. |
Hyperparameter Optimization
The search phase uses the Tree-structured Parzen Estimator (TPE): a Bayesian optimization algorithm that models the distribution of good and bad parameter configurations separately, then samples from the region where good configurations are most probable.
TPE Sampler: Mathematical Intuition
TPE maintains two kernel density estimators over the parameter space:
where y* is a quantile threshold (typically the top γ = 25% of observed trials by objective value). ℓ(x) models the distribution of “good” parameter vectors; g(x) models the “bad” ones. At each iteration, TPE maximises the ratio ℓ(x)/g(x): sampling where good configurations are dense and bad ones are sparse. This is equivalent to maximising Expected Improvement (EI):
Objective Modes
BitPredict supports three objective formulations, each suited to different optimization goals:
Single-Objective
Maximise or minimise a single metric (e.g., Sharpe Ratio). TPE operates directly on the scalar objective value. This is the fastest mode and sufficient when you have a clear primary metric and use the validation phases to filter on secondary criteria.
Weighted Sum
Combine multiple metrics into a scalar via user-defined weights. The search optimises:
where wi is the weight (normalised to sum to 1), norm(mi) is the metric normalised to [0,1], and di is +1 for maximise and −1 for minimise. Useful when you want to balance return and risk in a single objective: e.g., 70% Sharpe + 30% low drawdown.
Multi-Objective (NSGA-II)
When you select two or more metrics with individual directions and no weights, the search switches to the NSGA-II (Non-dominated Sorting Genetic Algorithm II) sampler. Instead of collapsing metrics into a scalar, NSGA-II searches for the Pareto front: the set of configurations where no metric can be improved without degrading another. Candidates are ranked by:
- Non-dominated sorting: front rank (configurations that dominate others are preferred).
- Crowding distance: within the same front, configurations in sparser regions are preferred to maintain diversity.
After the Pareto front is discovered, the pareto_selection strategy picks the top_n candidates: Closest to Ideal (minimum Euclidean distance to the utopia point), or Max Spread (maximum crowding distance for diverse candidates).
Convergence & n_trials
TPE convergence depends on the dimensionality of the search space. With d enabled parameters, a rule of thumb is:
For a strategy with 5 optimised parameters, 50–100 trials provides reasonable coverage. For 20 parameters, 200–500 trials is typical. The timeout (when enabled) provides a safety ceiling: the search finishes any in-progress trial before stopping, so the actual runtime may slightly exceed the timeout.
TPE consistently outperforms random search and grid search for the same compute budget in high-dimensional, non-convex optimization landscapes: exactly the kind of surface produced by financial strategy parameters. Grid search requires exponentially more trials as dimensionality increases (the curse of dimensionality); TPE learns which regions are promising and allocates trials accordingly.
Setting n_trials too low (e.g., 10 trials with 15 enabled parameters) means TPE never builds an adequate surrogate model: effectively reducing the search to random sampling. Conversely, extremely high n_trials (1,000+) on a small dataset dramatically increases the risk of finding a configuration that overfits the training data rather than one that generalises.
| Parameter | Type | Default | Description |
|---|---|---|---|
| n_trials | integer | 10 | Total search trials. 200–500 is typical for production; higher values increase search thoroughness but also overfitting risk and compute cost. |
| seed | integer | 42 | Random seed for reproducible search runs across identical configurations. |
| n_jobs | integer | 1 | Parallel trials. 1 = sequential (safe). Values >1 require thread-safe backtest and generate() functions. |
| min_trades | integer | 1 | Discard trials with fewer trades. Raise to 20–30 for production to filter out noise-dominated configurations. |
| top_n | integer | 3 | Number of best-performing trials carried forward into all subsequent validation phases. |
| objective.mode | single | weighted | multi | single | single: one metric. weighted: weighted sum. multi: Pareto-front via NSGA-II. |
| objective.metric | string | sharpe_ratio | Primary metric for single-objective mode. Options: sharpe_ratio, max_drawdown_pct, win_rate. |
| objective.direction | maximize | minimize | maximize | Direction to optimize the chosen metric. |
Multiple Testing Correction
Running N independent backtests, even purely random configurations, will produce a distribution of Sharpe ratios whose maximum grows with N. This is the multiple testing problem: the more trials you run, the higher the probability of finding a “good” result purely by chance. Without correction, the optimizer will cherry-pick the luckiest configuration and present it as skill.
Deflated Sharpe Ratio (Recommended)
Bailey & López de Prado (2014) derived the expected maximum Sharpe ratio from N independent trials under the null hypothesis of zero expected return:
where γ is the Euler-Mascheroni constant and Z−1 is the inverse standard normal CDF. The Deflated Sharpe Ratio (DSR) then tests whether the observed Sharpe ratio of the best candidate is statistically distinguishable from this expected maximum:
A low DSR p-value (typically < 0.05) means the best candidate's Sharpe is unlikely to have arisen from pure data snooping across N trials; the strategy likely captures genuine predictive power.
Alternative Methods
- Bonferroni: Divides α by N. Controls Family-Wise Error Rate (FWER): the probability of making any false discovery. Highly conservative; use when N is small (<50).
- Benjamini-Hochberg (BH): Controls the False Discovery Rate (FDR): the expected proportion of false discoveries among all rejected null hypotheses. Less conservative; better for large N.
Without multiple testing correction, a random strategy search with 500 trials will almost certainly produce a Sharpe > 2.0 by pure luck. The Deflated Sharpe Ratio is the most theoretically grounded correction for financial backtesting because it accounts for the non-uniformity of the Sharpe ratio distribution and the discrete nature of trial counts, something Bonferroni and BH do not.
| Parameter | Type | Default | Description |
|---|---|---|---|
| method | deflated_sharpe | bonferroni | bh | deflated_sharpe | Correction method. Deflated Sharpe is most theoretically grounded; Bonferroni is strictest; BH controls false discovery rate. |
| alpha | percentage (0–20) | 5 | Significance level before adjustment. Lower = stricter filtering. 5% is standard. |
Sensitivity & Parameter Surface Analysis
A single “optimal” parameter vector is a point estimate; it tells you nothing about the shape of the performance surface around that point. Sensitivity analysis probes the local neighbourhood of each top candidate by perturbing each optimised parameter and re-running the backtest.
Perturbation Methodology
For each of the top_n candidates and each enabled parameter p with range [pmin, pmax]:
For each perturbation, the backtest is re-run with the shifted parameter (all others held at their optimum). The metric drop Δm is computed relative to the unperturbed baseline. If the average drop across all perturbations exceeds max_metric_drop, the candidate is rejected.
Interpreting the Sensitivity Surface
A robust optimum sits in a flat basin: small parameter changes cause small, gradual metric degradation. A fragile optimum sits on a sharp peak: a 0.5% parameter shift causes the Sharpe to collapse from 2.5 to 0.8. Fragile optima are classic overfitting signatures: the optimizer found a narrow configuration that exploits noise in the training data rather than capturing a genuine market effect.
In live trading, market microstructure evolves continuously: the “optimal” parameter today will be slightly suboptimal tomorrow. A strategy optimised to a fragile peak will degrade immediately out-of-sample; a strategy in a flat basin degrades gracefully. Sensitivity analysis is your best defense against deploying a strategy that only works at one exact point in parameter space.
| Parameter | Type | Default | Description |
|---|---|---|---|
| perturbation | percentage (1–100) | 10 | Shift each parameter by ±(perturbation% × param_range). Smaller = tighter neighbourhood; larger = broader stress test. |
| n_perturbations | integer | 20 | Number of perturbed backtests per parameter. More = smoother sensitivity estimate but slower. |
| max_metric_drop | percentage (0–100) | 20 | Reject candidate if average metric drop across perturbations exceeds this threshold. |
Robustness & Noise Injection
Real market data is noisy: bid-ask spreads, feed errors, flash crashes, and exchange latency all introduce random perturbations that don't exist in clean historical data. Noise injection simulates this by adding controlled Gaussian noise to the price series before each backtest, then measuring how much the strategy's performance degrades.
Noise Model
Noise is multiplicative (applied as a percentage of price) rather than additive, which better reflects real market noise characteristics: a \$0.01 error on a \$1 asset is material; a \$0.01 error on a \$50,000 asset is negligible.
Fragility Score
For each candidate, n_runs independent noisy backtests are performed (each with a different random seed). The fragility score is the mean degradation of the primary metric:
where R = n_runs. If fragility exceeds max_metric_drop, the candidate is rejected. A strategy that loses 40% of its Sharpe under 0.1% Gaussian noise is unlikely to survive real exchange conditions.
The gap between backtest and live performance, known as the “implementation shortfall,” is partially explained by noise sensitivity. Strategies that are robust to small price perturbations tend to have smaller implementation shortfalls. Noise injection provides a quantitative estimate of this sensitivity before a single real trade is placed.
Noise levels above 1% simulate extreme market conditions (flash crashes, liquidity crises). A strategy that fails at 2% noise may still be viable in normal conditions. Use the noise test as a stress test, not a hard gate; consider the typical spread and volatility of your target asset when interpreting results.
| Parameter | Type | Default | Description |
|---|---|---|---|
| noise_level | percentage (0–100) | 0.1 | Std dev of Gaussian noise as % of price. 0.1% simulates typical microstructural noise; 0.5%+ simulates stress conditions. |
| n_runs | integer | 50 | Number of independent noisy backtests per candidate. More runs = tighter fragility estimate. |
| max_metric_drop | percentage (0–100) | 20 | Reject if mean metric drop across noisy runs exceeds this. |
Regime Consistency Validation
Financial markets are non-stationary; the statistical properties of returns change dramatically across different market regimes. A strategy that earns a Sharpe of 3.0 exclusively during bull markets is categorically different from one that earns 1.5 across all four regimes (bull, bear, range, transition). Regime consistency validation decomposes performance by regime and rejects strategies that are regime-dependent.
Regime Taxonomy
BitPredict classifies every bar into one of four regimes using an ensemble of Hidden Markov Models (HMM), gradient-boosted trees, and volatility clustering analysis. Each regime has distinct statistical fingerprints:
- Bull: positive drift, low volatility, right-skewed returns.
- Bear: negative drift, elevated volatility, left-skewed returns.
- Range: near-zero drift, oscillating within a band, mean-reverting dynamics.
- Transition: regime shift in progress, elevated uncertainty, poor signal-to-noise ratio.
Fitness Scoring per Regime
For each regime, a composite fitness score (0–100%) is computed:
where SR = Sharpe Ratio, PF = Profit Factor, AR = Average Return, TC = Trade Count, and DD = Max Drawdown. Each component is normalised to [0,1] across all candidates. A candidate must achieve fitness ≥ min_metric in at least min_regimes_pass regimes.
The most common failure mode in live trading is regime dependency: a strategy optimised during a bull market that fails catastrophically when the regime shifts. Regime consistency testing surfaces this before capital is committed. A strategy that passes in 3+ regimes with high fitness is far more likely to survive a regime transition than one that only works in a single regime.
Note: This phase is automatically skipped when the trigger bar timeframe is 1-minute (1m) because regime labels are not available at that resolution. The regime model operates at 1h and above.
| Parameter | Type | Default | Description |
|---|---|---|---|
| min_regimes_pass | integer | 2 | Candidate must pass fitness threshold in at least this many regimes. Set to 3 for strict multi-regime validation. |
| min_metric | percentage (0–100) | 50 | Minimum composite fitness score for a regime to count as passing. |
Synthetic Data Testing
Even with proper train/val/holdout splits, the validation set shares the same structural properties as the training data: same autocorrelation structure, same volatility clustering, same regime sequence. Synthetic data testing breaks this dependency by generating new datasets that preserve some properties of the original data while randomising others, creating a null distribution against which to test the strategy.
Block Bootstrap
The block bootstrap resamples contiguous blocks of bars (with replacement) to preserve local autocorrelation while destroying any genuine predictive patterns. If a strategy performs well on the original data but collapses on block-bootstrapped data, the original performance was likely driven by serial correlation, not predictive signal.
where T is total bars, L is block size, and B blocks are sampled with replacement to construct each synthetic dataset.
Stationary Bootstrap (Politis-Romano)
An extension of the block bootstrap where block lengths are variable, drawn from a geometric distribution with mean L. This eliminates the block-boundary artifacts of the fixed block bootstrap and produces more realistic synthetic series.
Statistical Test
For each candidate, n_synthetic datasets are generated. The strategy is backtested on each, producing a null distribution of the primary metric. The candidate passes if its real metric exceeds the (1 − α) percentile of the null distribution:
where α = significance_level / 100 and S = n_synthetic.
Synthetic data testing provides a non-parametric statistical test for strategy significance that does not rely on asymptotic assumptions (unlike Sharpe ratio t-tests). It directly answers the question: “Could a strategy with no genuine predictive power produce this level of performance simply by exploiting the autocorrelation structure of the data?”
| Parameter | Type | Default | Description |
|---|---|---|---|
| method | block_bootstrap | stationary_bootstrap | block_bootstrap | Block bootstrap uses fixed-size blocks. Stationary bootstrap uses variable-length blocks (Politis-Romano). |
| block_size | integer | 20 | Contiguous bars per bootstrap block. Set roughly to the average holding period of the strategy. |
| n_synthetic | integer | 50 | Number of bootstrapped datasets. More = tighter null distribution but slower. |
| significance_level | percentage (0.1–20) | 5 | Real metric must beat the (1 − α) percentile of synthetic null. 5% = must beat 95% of random shuffles. |
Temporal Parameter Stability
A strategy whose optimal parameters drift significantly over time is fragile: the “best” configuration found on the full training period may be suboptimal for any given sub-period. Temporal stability testing divides the training period into sliding windows and checks whether the top candidates' performance is consistent across windows.
Sliding Window Design
The training period is divided into n_windows overlapping sub-windows. Each window receives an independent backtest of each top candidate (with the parameters fixed at their optimal values from the full training period). This tests parameter stability, not re-optimisation per window.
where K = n_windows, W = total_train / K, and Δ = W · overlap_factor.
A candidate passes if at least min_pass_rate% of windows achieve a fitness score ≥ min_window_metric%.
Temporal stability is a direct measure of a strategy's robustness to market evolution. A strategy with 80% pass rate across 5 windows is far more trustworthy than one with 40%: the former works consistently; the latter only works during a specific sub-period (likely the one it overfit to).
| Parameter | Type | Default | Description |
|---|---|---|---|
| n_windows | integer | 5 | Number of sliding windows. More windows = finer temporal resolution but each window has less data. |
| min_window_metric | percentage (0–100) | 30 | Minimum fitness score for a window to count as passing. |
| min_pass_rate | percentage (0–100) | 60 | Fraction of windows that must pass. 60% with 5 windows = at least 3 of 5. |
Walk-Forward Out-of-Sample Validation
Walk-forward analysis is the gold standard for out-of-sample validation in quantitative finance. Unlike a single train/test split, walk-forward repeatedly re-optimises on an expanding or rolling training window and tests on the immediately subsequent out-of-sample period. This directly simulates how the strategy would be used in practice: periodically re-optimised on recent data, then traded forward.
Purging and Embargoing
Standard walk-forward risks data leakage at window boundaries: a trade opened at the end of training window k may still be open when the test window k begins, contaminating the “out-of-sample” evaluation. The pipeline applies:
- Purging: remove training observations whose labels overlap with the test window (e.g., trades whose holding period spans the boundary).
- Embargoing: remove a fixed number of bars after the training window before starting the test window, eliminating any lingering autocorrelation effects.
Sub-Window Validation
When n_val_windows > 1, the validation period is itself split into sub-windows, and each candidate must pass in at least min_pass_rate% of them. This prevents the optimizer from selecting a candidate that only performs on a single lucky validation period.
Walk-forward validation is the closest simulation to live trading available in a backtesting framework. A strategy that survives walk-forward with consistent metrics across all OOS windows has demonstrated genuine generalisation, not merely memorisation of a single historical period. This is the phase that most directly predicts live trading performance.
| Parameter | Type | Default | Description |
|---|---|---|---|
| min_val_metric | percentage (0–100) | 50 | Minimum scalar fitness score on the validation split. |
| min_trades | integer | 1 | Minimum trades on the validation split. Too few trades = unreliable statistics. |
| n_val_windows | integer | 1 | Split validation into sub-windows. Set >1 for stricter temporal consistency checks. |
| min_pass_rate | percentage (0–100) | 60 | Fraction of validation sub-windows that must pass (relevant when n_val_windows > 1). |
Monte Carlo Simulation
Trade-level Monte Carlo simulation reshuffles the sequence of individual trade returns to estimate the distribution of possible strategy outcomes. A strategy with 100 trades and a Sharpe of 2.0 might have achieved that Sharpe because 5 outlier trades contributed 80% of the total return: removing or reordering those trades would dramatically change the result.
Reshuffling Methods
- Shuffle: Random permutation of trade returns. Destroys all time-series structure. Simplest and fastest.
- Bootstrap: Resample trade returns with replacement. Allows some trades to appear multiple times (simulating over-representation of certain market conditions).
- Hybrid: Mixes shuffle and bootstrap at each simulation.
- Moving Block: Reshuffles blocks of consecutive trades, preserving short-term autocorrelation in trade outcomes.
- Stationary (Politis-Romano): Variable-length block bootstrap applied to trade sequences. Recommended for most strategies.
- Parametric: Fits a t-distribution to observed trade returns, then draws from it. Assumes returns are IID, useful as a baseline comparison.
Worst-Case Analysis
For each candidate, n_simulations trade sequences are generated. The worst-case Sharpe and max drawdown are evaluated at the worst_case_percentile (e.g., 5th percentile = bottom 5% of simulated outcomes). If the worst-case Sharpe falls below min_sharpe or the worst-case drawdown exceeds max_drawdown, the candidate is rejected.
Monte Carlo simulation answers the question: “How much of this strategy's performance is attributable to the specific order in which trades occurred?” If reshuffling the trade sequence, which doesn't change the distribution of trade returns, only their order, produces dramatically worse outcomes, the strategy's edge is fragile and sequence-dependent. A strategy whose performance is robust to trade-sequence reshuffling has a much higher probability of surviving future market conditions.
| Parameter | Type | Default | Description |
|---|---|---|---|
| n_simulations | integer | 10 | Number of trade-sequence reshuffles. 1000 is standard for production; higher values produce tighter percentile estimates. |
| method | shuffle | bootstrap | hybrid | moving_block | stationary | parametric | stationary | Reshuffling method. Stationary (Politis-Romano) is recommended for realistic trade-sequence simulation. |
| worst_case_percentile | integer (1–50) | 5 | Worst case defined at this percentile. 5 = bottom 5th percentile of simulated outcomes. |
| min_sharpe | float | 0.3 | Worst-case Sharpe must exceed this. Even in unlucky scenarios, an edge must exist. |
| max_drawdown | percentage (0–100) | 40 | Worst-case drawdown must not exceed this. |
Signal Correlation (Admin-Only)
Signal correlation analysis measures the pairwise correlation between the signal streams of all active strategies and the candidate being evaluated. A strategy whose signals are 95% correlated with an existing live strategy adds no diversification benefit, even if its standalone metrics are excellent.
This phase is restricted to admin users and always contributes zero compute time for standard UI optimization requests. The correlation matrix is computed across the holdout period using Spearman rank correlation (robust to non-linear relationships and outliers).
Final Holdout Test
The holdout test is the ultimate gate. The holdout data has never been seen by the optimizer, the validation phases, or any intermediate processing. It represents the closest approximation to truly future, unseen market data available in a backtesting framework.
Why Holdout Must Be Untouched
Every decision made after seeing the holdout data, adjusting thresholds, tweaking parameters, even choosing which strategy to deploy, constitutes a form of data leakage. The holdout set must be read exactly once, at the very end of the pipeline, and its results must be accepted as-is. If you don't like the holdout result and go back to re-optimise, you have effectively converted your holdout set into a validation set and now have no truly unseen data remaining.
Metrics Thresholds
Three thresholds gate the holdout phase:
- Sharpe ≥ min_sharpe: The strategy must demonstrate genuine risk-adjusted edge on completely unseen data.
- Trades ≥ min_trades: Sufficient statistical sample. A Sharpe of 5.0 on 3 trades is meaningless; a Sharpe of 1.2 on 100 trades is informative.
- Max Drawdown ≤ max_drawdown: Worst peak-to-trough decline must be tolerable. Even a high-Sharpe strategy is unusable if it draws down 60%: most traders would exit long before recovery.
The holdout test is the single most honest performance estimate in the entire pipeline. Every preceding phase can be gamed, intentionally or not, through iterative refinement. The holdout cannot. A strategy that passes the holdout test has demonstrated generalisation across a genuinely unseen market period. This is the closest a backtest can come to predicting live performance.
Never optimise, adjust parameters, or filter strategies based on holdout results. If you do, the holdout is no longer a holdout: it becomes part of your training process, and its results are now optimistically biased. The only correct response to a failed holdout test is to go back to the drawing board with a new hypothesis and fresh data.
| Parameter | Type | Default | Description |
|---|---|---|---|
| min_sharpe | float | 0.5 | Minimum Sharpe ratio on the holdout split. The primary gate: no edge on unseen data = fail. |
| min_trades | integer | 20 | Minimum trades on holdout. Too few = unreliable statistics regardless of Sharpe. |
| max_drawdown | percentage (0–100) | 30 | Maximum drawdown allowed on the holdout split. |
Persistence & Comprehensive Metrics
The final phase persists all trial results and comprehensive metrics to the database. This is not a validation gate, it always runs, for all trials, but it is essential for post-hoc analysis, audit trails, and the ability to compare candidates across optimization runs.
What Gets Stored
For each of the n_trials trials, the system stores:
- Complete parameter vector and objective value
- Full backtest result: equity curve, trade ledger, monthly P&L
- Comprehensive metrics: Sharpe, Sortino, Calmar, Win Rate, Profit Factor, Max Drawdown, CAGR, volatility, skewness, kurtosis, VaR, CVaR
- Regime-decomposed performance (per-regime Sharpe, return, win rate)
- Phase-level pass/fail flags for every enabled validation phase
- Intermediate phase results (sensitivity surfaces, noise fragility scores, etc.)
Persistence Sub-Phases
The persistence phase itself breaks down into four sub-operations:
- 14a: Bulk trial writes: All non-top_n trials written with train-set metrics only (fast batch insert).
- 14b: Top candidate writes: Top_n candidates written with full-split metrics (train + val + holdout).
- 14c: Comprehensive metrics: Per-top_n candidate: full suite of 20+ metrics computed across the entire dataset.
- 14d: Regime metadata: Per-top_n candidate: regime-decomposed metrics across all available regimes (skipped if trigger bar = 1m).
Full persistence enables the Optimization Trials page to display rich comparison tables, sortable columns, and phase-level pass/fail indicators. Without comprehensive metrics storage, users would need to re-run backtests to answer simple questions like “what was the Sortino ratio of trial #47?”
Timing & Credits Estimator
The optimization modal displays a live time and credit estimate that updates as you adjust parameters, pipeline settings, and date ranges. This estimator is a rule-based model calibrated against actual backend runtimes: it does not submit a job to get an estimate.
Estimation Model
Step 1: Row counts
One 1-minute bar per minute, 24/7 crypto markets. This is the fundamental time unit: all per-backtest timings scale from it.
Step 2: Per-backtest time
where τ1m is the calibrated ms-per-1000-1m-rows constant (6.142ms when trigger=1m, 0.327ms otherwise). Validation and holdout times scale proportionally by split fraction.
Step 3: Phase times
Each phase multiplies its backtest count by the appropriate per-split time. Phase backtest counts are products of their scaling factors:
- TPE search: n_trials × 1 (one backtest per trial on train)
- Sensitivity: top_n × n_params × n_perturbations
- Noise Injection: top_n × n_runs
- Synthetic Data: top_n × n_synthetic
- Temporal Stability: top_n × n_windows (each window = t_train / n_windows)
- Walk-Forward: top_n × n_val_windows (on validation split)
- Monte Carlo: top_n × 1 (not n_simulations, those are trade-level reshuffles)
- Holdout: top_n × 1 (on holdout split)
Step 4: Buffer and credits
Buffer doubles the raw estimate to account for I/O variance, database contention, and queue delays. Credits are billed at 1 per 3 minutes of estimated compute (feat_builder_optimization, interval_seconds=180).
Calibration
Timing constants are calibrated by running representative backtests on the VPS and measuring wall-clock time. The calibration distinguishes between 1m trigger bars (where the regime model is skipped) and all other trigger bar types (where it runs). Constants are stored in optimization_time_estimator.json and updated when backend performance characteristics change.
The estimator gives users transparency into compute cost before submitting. A user who sees “~45m · ~15 cr” can decide to reduce n_trials from 500 to 100 or disable expensive phases (Synthetic Data, Monte Carlo) to stay within budget, rather than discovering the cost after the fact.
The estimator assumes linear scaling with row count, which holds well for 1m bars but can underestimate for non-time bar types with high computational overhead (e.g., dollar bars with large lookback windows). Treat estimates as approximate (±30%).
Pipeline Execution Order
| # | Phase | Data Split | Default | Rejects On |
|---|---|---|---|---|
| 1 | Data Preparation | - | Always | - |
| 2 | Hyperparameter Optimization | Train | Always | - |
| 3 | Multiple Testing | - | Enabled | DSR/Bonferroni/BH p-value |
| 4 | Sensitivity | Train | Enabled | Avg metric drop > max_metric_drop |
| 5 | Noise Injection | Train | Enabled | Mean noisy metric drop > max_metric_drop |
| 6 | Regime Consistency | Train | Enabled | < min_regimes_pass with fitness ≥ min_metric |
| 7 | Synthetic Data | Train | Enabled | Real metric ≤ null distribution percentile |
| 8 | Temporal Stability | Train (sub-windows) | Disabled | < min_pass_rate windows passing |
| 9 | Walk-Forward OOS | Validation | Disabled | < min_pass_rate val windows passing |
| 10 | Monte Carlo | Train + Val | Disabled | Worst-case Sharpe < min_sharpe or drawdown > max_drawdown |
| 11 | Signal Correlation | Holdout | Admin-only | Correlation > threshold with existing strategies |
| 12 | Holdout Test | Holdout | Enabled | Sharpe < min_sharpe or trades < min_trades or drawdown > max_drawdown |
| 13 | Persistence | Mixed | Always | - (always runs, writes all trials) |
References & Further Reading
- Bailey, D. H. & López de Prado, M. (2014). “The Deflated Sharpe Ratio: Correcting for Selection Bias, Backtest Overfitting, and Non-Normality.” Journal of Portfolio Management, 40(5), 94–107.
- López de Prado, M. (2018). Advances in Financial Machine Learning. Wiley. Chapters 7–9 (Cross-Validation in Finance), 11–13 (Backtesting).
- Bergstra, J., Bardenet, R., Bengio, Y., & Kégl, B. (2011). “Algorithms for Hyper-Parameter Optimization.” NeurIPS: The TPE sampler used in Bayesian hyperparameter optimization.
- Akiba, T., Sano, S., Yanase, T., Ohta, T., & Koyama, M. (2019). “Optuna: A Next-generation Hyperparameter Optimization Framework.” KDD.
- Deb, K., Pratap, A., Agarwal, S., & Meyarivan, T. (2002). “A Fast and Elitist Multiobjective Genetic Algorithm: NSGA-II.” IEEE Transactions on Evolutionary Computation, 6(2), 182–197.
- Politis, D. N. & Romano, J. P. (1994). “The Stationary Bootstrap.” Journal of the American Statistical Association, 89(428), 1303–1313.
- Benjamini, Y. & Hochberg, Y. (1995). “Controlling the False Discovery Rate: A Practical and Powerful Approach to Multiple Testing.” Journal of the Royal Statistical Society: Series B, 57(1), 289–300.
- Aronson, D. R. (2006). Evidence-Based Technical Analysis. Wiley: The foundational text on walk-forward validation and out-of-sample testing in trading strategy development.