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.
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.