Skip to content

aether_quant.overfitting

Overfitting tests, grouped by the job (V6.6.5): PBO by CSCV, the probabilistic and deflated Sharpe, the minimum track record, the haircut Sharpe, placebo tests, the leakage audit and the selection gate the research rounds apply.

Overfitting tests, grouped by the job (V6.6.5): PBO by CSCV, the probabilistic and deflated Sharpe, the minimum track record, the haircut Sharpe, placebo tests, the leakage audit and the selection gate the research rounds apply.

pbo module-attribute

pbo = probability_of_backtest_overfitting

audit_leakage

audit_leakage(frame: DataFrame, *, feature_columns: Sequence[str], target_column: str = 'target_rank_20d', ticker_column: str = 'ticker', date_column: str = 'date', split_column: str = 'split', horizon: int = 20, class_columns: Sequence[str] = (), model_score_column: str | None = None, min_names: int = 20, seed: int = 7, group_column: str | None = None) -> dict[str, Any]

All four checks; (b) and (c) on the validation split, (d) fit on train and read on validation.

deflated_sharpe_from_inputs

deflated_sharpe_from_inputs(returns: ArrayLike, *, num_trials: int, trial_variance: float | None, annualization: float) -> dict[str, Any]

The deflated Sharpe with N and the trial variance given (evaluation.trial_ledger.deflation_inputs), not derived from a list of trial Sharpes: the variance must come from trials on the window being judged.

deflated_sharpe_ratio

deflated_sharpe_ratio(returns: ArrayLike, trial_annual_sharpes: list[float], periods_per_year: int = DAYS_PER_YEAR) -> dict[str, Any]

DSR (Bailey & López de Prado 2014): PSR against SR0, the best Sharpe expected from the number of configurations tried. Trial Sharpes are annual; they are moved to this series' period (÷ sqrt(periods_per_year)) first.

expected_max_sharpe

expected_max_sharpe(num_trials: int, trial_sharpe_variance: float) -> float | None

SR0 of the deflated Sharpe ratio: the expected maximum Sharpe of num_trials unskilled trials whose Sharpe ratios have variance trial_sharpe_variance (same units as the returned value).

haircut_sharpe

haircut_sharpe(sharpe: float, n_obs: int, num_trials: int, *, method: Literal['bonferroni', 'holm', 'bhy'] = 'bhy', periods_per_year: int = 252) -> dict[str, float]

The annual Sharpe that survives num_trials: its t-statistic SR * sqrt(n_obs / periods_per_year), the two-sided p-value, the p-value adjusted for multiple testing, and the Sharpe implied by the adjusted p-value.

At rank 1, Holm equals Bonferroni (p * M) and BHY multiplies by M * c(M) with c(M) = sum(1/i). Returns {"sharpe", "haircut_sharpe", "haircut", "t_stat", "p_value", "adjusted_p_value"}; a non-positive Sharpe gets a 100% haircut.

minimum_track_record_length

minimum_track_record_length(returns: ArrayLike, *, target_sharpe: float = 0.0, confidence: float = 0.95, periods_per_year: int = 252) -> float

Observations needed before the observed Sharpe beats target_sharpe (annual) at confidence, given the series' skew and kurtosis: 1 + (1 - g3*SR + (g4-1)/4*SR^2) * (z / (SR - SR*))^2 with per-period SR and raw kurtosis g4. inf when the observed Sharpe does not exceed the target; nan for fewer than 3 finite observations or zero variance.

placebo_p_value

placebo_p_value(observed: float | None, placebos: list[float]) -> float

Share of placebos at or above the observed Sharpe, counting the observation itself (never exactly zero).

probabilistic_sharpe_ratio

probabilistic_sharpe_ratio(returns: ArrayLike, benchmark_sharpe_per_period: float = 0.0) -> float | None

PSR (Bailey & López de Prado 2012): P(true Sharpe > benchmark) given the sample's length, skew and kurtosis. Both Sharpe ratios are per period (not annualised).

probability_of_backtest_overfitting

probability_of_backtest_overfitting(returns: ArrayLike, *, blocks: int = 16, periods_per_year: int = 252) -> dict[str, Any]

{"pbo", "num_combinations", "num_trials", "lambda_quantiles", "degradation_slope", "degradation_intercept", "prob_oos_loss"}; pbo is None when there are fewer than 2 trials or fewer rows than blocks.

selection_gate

selection_gate(scores: list[dict], p_values: Mapping[str, Mapping[str, float]], *, max_survivors: int = 5, max_correlation: float = 0.7, alpha: float = 0.05) -> dict[str, Any]

R-SLEEVE2. A survivor passes both Sharpe clauses and every placebo test; among survivors the higher Sharpe keeps its place against any other with daily correlation above max_correlation; at most max_survivors remain.