What problem does the deflated Sharpe solve?
The naive Sharpe significance test assumes the strategy was the only one ever tried. In practice a researcher tests dozens or hundreds of configurations and reports the best. Under the null hypothesis of zero true Sharpe for every trial, the maximum over N trials is systematically positive — so the best-looking backtest overstates significance. This is selection bias under multiple testing, the same mechanism behind the factor-zoo critique (Harvey, Liu & Zhu, 2016).
The deflated Sharpe ratio corrects for three things at once: the number of trials N, the non-normality of returns (skewness and kurtosis enter through the standard error), and the correlation structure across trials via the variance of trial Sharpes. It answers: given N trials, what Sharpe would we expect the best of them to show by pure luck, and does the observed Sharpe clear that bar?
The practical consequence is a disclosure discipline. Every strategy report must state N — every configuration tried and discarded counts. A Sharpe of 1.5 found after 1,000 trials carries far less evidence than the same Sharpe found on the first attempt. The DSR makes that intuition quantitative.
What is the formula?
Let SR be the observed annualized Sharpe, T the number of return observations, γ3 skewness, γ4 kurtosis of the strategy returns, N the number of independent trials, and V the variance of trial Sharpes. The expected maximum Sharpe under the null is E[max] ≈ √V · ((1−γe)·Φ⁻¹(1−1/N) + γe·Φ⁻¹(1−1/(N·e))), where γe is the Euler-Mascheroni constant and Φ⁻¹ the inverse normal CDF. The DSR statistic is (SR − E[max]) / √[Var], with Var = (1 − γ3·SR + (γ4−1)/4·SR²) / T, and the deflated p-value is 1 − Φ(DSR).
Two intuitions matter. First, E[max] grows with log N: going from 1 to 100 trials raises the luck benchmark substantially; going from 100 to 10,000 raises it further but more slowly. Second, negative skew and fat tails inflate the standard error, so strategies with crash risk (short-vol profiles) are penalized more than their headline Sharpe suggests.
When N=1, E[max]=0 and the DSR collapses to the standard probabilistic Sharpe ratio. Reporting N=1 when the true N is large is therefore exactly the misconduct the metric is designed to catch.
What is the reproducible code?
The script below implements the DSR from scratch on any return series. Inputs: annualized observed Sharpe, sample length T, skewness, kurtosis, trial count N, and variance of trial Sharpes V (default estimated from the single series when only one trial's returns are available). It prints the naive p-value alongside the deflated p-value so the correction is visible.
python# deflated_sharpe.py # Deflated Sharpe Ratio (Bailey & Lopez de Prado, 2014). # Run: python deflated_sharpe.py import numpy as np from scipy import stats EULER_GAMMA = float(np.euler_gamma) def expected_max_sharpe(n_trials: int, var_sharpes: float) -> float: """Expected maximum Sharpe under the null over N trials.""" if n_trials < 2: return 0.0 q1 = stats.norm.ppf(1 - 1 / n_trials) q2 = stats.norm.ppf(1 - 1 / (n_trials * np.e)) return float( np.sqrt(var_sharpes) * ((1 - EULER_GAMMA) * q1 + EULER_GAMMA * q2) ) def deflated_sharpe_pvalue( sr_observed: float, n_obs: int, skew: float, kurtosis: float, n_trials: int, var_sharpes: float, ) -> tuple[float, float]: """Return (naive p-value, deflated p-value) for H0: true SR = 0.""" # Naive: standard error ignoring selection bias and non-normality. se_naive = 1.0 / np.sqrt(n_obs) naive_p = float(1 - stats.norm.cdf(sr_observed / se_naive)) # Selection-bias benchmark. e_max = expected_max_sharpe(n_trials, var_sharpes) # Standard error corrected for skew/kurtosis (Lopez de Prado 2018, Ch.14). se = np.sqrt( (1 - skew * sr_observed + (kurtosis - 1) / 4 * sr_observed**2) / n_obs ) dsr_stat = (sr_observed - e_max) / se if se > 0 else 0.0 deflated_p = float(1 - stats.norm.cdf(dsr_stat)) return naive_p, deflated_p if __name__ == "__main__": # Worked example: SR=1.0, 5y daily (T=1260), mild negative skew. for n in (1, 10, 100, 1000): naive_p, deflated_p = deflated_sharpe_pvalue( sr_observed=1.0, n_obs=1260, skew=-0.5, kurtosis=4.0, n_trials=n, var_sharpes=0.25, ) verdict = "significant" if deflated_p < 0.05 else "NOT significant" print(f"N={n:>4}: naive p={naive_p:.4f} deflated p={deflated_p:.4f} -> {verdict}")
How do you estimate N and V honestly?
N counts every configuration evaluated, including ones never written up: parameter grids, alternative universes, discarded signal variants, and restarted experiments. A practical lower bound is the size of the researcher's grid (e.g., 5 windows × 4 thresholds × 10 assets = 200 trials). When N is genuinely unknown, report a sensitivity table (N = 10, 100, 1000) as the script above does — the reader can see at which trial count significance evaporates.
V, the variance of trial Sharpes, is estimated from the Sharpes of all trials when available. With a single return series, a conservative default is the sampling variance of the Sharpe itself; the script's default of 0.25 corresponds to a standard deviation of 0.5 across trials, a reasonable prior for strategy zoos. Overstating V flatters the strategy, so err toward larger V only with evidence.
Correlation across trials reduces the effective N. Highly correlated variants (e.g., MA(49) vs MA(50)) are close to one trial, not two. If trials are strongly correlated, the DSR with raw N is conservative — which is the safe direction for a gatekeeper metric.
What are the honest limits?
The DSR tests significance, not magnitude: a significant DSR does not promise the Sharpe persists out of sample (see the CPCV asset for the performance estimate). It also assumes the trial Sharpes are approximately jointly normal in the tail — reasonable for the benchmark but an approximation for highly non-linear ML strategies.
Nobody can audit undisclosed trials. The DSR is a honesty amplifier, not a lie detector: it quantifies disclosed search, it cannot recover hidden search. Team process (experiment tracking, seeded configs, immutable logs) is the enforcement layer; the formula is the scoring layer.
Finally, the DSR inherits return-measurement limits: survivorship-biased universes, look-ahead-contaminated features, and transaction-cost-free backtests all inflate the input Sharpe before the DSR ever sees it (see the survivorship-audit asset). Deflate the inputs first, then deflate the significance.