PSR Verification Report: A Published Example, Corrected
The worked example previously published on this site was arithmetically
wrong, for months. It was found by computing it rather than reading it,
corrected against scipy, and every intermediate value is now
shown so it can be checked by hand.
- Verification record
- v1.0 · first run 16 August 2026 · published as a report 4 September 2026
- Implementation under test
tools/probabilistic-sharpe-ratio-calculator.html and the worked example on the paper, in the public repository- Environment
- Python 3.11.9; numpy 2.4.6; pandas 3.0.5; scipy 1.17.1 — all newer than the package pins, so this doubles as a forward-compatibility check
- Verifier
- Cemil Ertürk
- Status
- Independent. Not peer reviewed. Synthetic data only; no claim about any real security.
With an observed Sharpe of 1.50, 24 observations, skewness of −1.20 and kurtosis of 7.00, the Probabilistic Sharpe Ratio against a zero benchmark is 99.81%. Here is the arithmetic behind that result, including the denominator that is easy to miscalculate.
Sharpe and the benchmark must be expressed at the observation frequency. For monthly observations, use monthly Sharpe values; do not enter an annualized Sharpe alongside a count of monthly returns. Kurtosis here is ordinary kurtosis, where a normal distribution has value 3, not excess kurtosis.
Inputs
| Observed Sharpe SR | 1.50 per observation period |
|---|---|
| Benchmark SR* | 0.00 per observation period |
| Observations n | 24 |
| Skewness g1 | −1.20 |
| Kurtosis g2 | 7.00 |
Calculate without intermediate rounding
The normal-return case still has a kurtosis term. Setting skewness to zero and kurtosis to 3 gives sqrt(1 + 0.5 × 1.50²) = 1.4577, not 1. Zero excess kurtosis does not remove (g2 − 1)/4 from this formula.
Change the question
Raise the benchmark from 0 to 1 while keeping the other inputs fixed. The z-statistic becomes 0.9650 and PSR falls to 83.27%, below a 95% threshold. The observed record is the same; the claim being tested is stronger.
Open the free calculator with these default inputs. It calculates locally in your browser and displays both the denominator and z.
Reproduce with Python
This check uses Python's standard library. The browser calculator uses an error-function approximation, so compare at the displayed precision rather than requiring identical last digits.
What the percentage does not mean
PSR is not the probability of a profitable next trade. Its interpretation depends on the statistical assumptions, including independent returns and reliable moment estimates. It does not account for costs or the number of strategy variants tried. Selection across many backtests needs a separate correction such as the Deflated Sharpe Ratio.
Arithmetic checked 4 September 2026. The earlier worked example printed z = 2.8955; the correct value rounded to four decimals is 2.8949. This correction leaves the displayed PSR of 99.81% unchanged.
Defect found during verification
| Quantity (SR 1.50, SR* 0, n 24) | As originally published | Correct |
|---|---|---|
| Denominator, γ₁ = 0, γ₂ = 3 | 1.000 | 1.4577 |
| Denominator, γ₁ = −1.20, γ₂ = 7.00 | 2.318 | 2.4850 |
| z-statistic (skewed, fat-tailed case) | 2.8955 | 2.8949 |
| PSR (skewed, fat-tailed case) | — | 0.9981 |
The original example treated the “normal” case as having a denominator of exactly 1, as though zero excess kurtosis removed the term. It does not: the formula uses (γ₂ − 1)/4, which at γ₂ = 3 is 0.5, so the denominator is √(1 + 0.5·1.5²) = √2.125 = 1.4577. The same slip propagated to the skewed case: √(1 + 1.80 + 1.5·2.25) = √6.175 = 2.4850, not 2.318. Throughout, γ₂ is Pearson (non-excess) kurtosis: a normal distribution has γ₂ = 3, not 0.
The z-statistic that followed was published as 2.8955. It does not follow from the corrected denominator: 1.5·√23 / 2.484955 = 2.8949; 2.8955 would need a denominator of 2.4845. A reader on r/quant pinned this on 10 September 2026, and the table above now records both values. PSR is 0.9981 either way.
Detection: evaluating the formula in scipy
instead of trusting the prose. Fix: paper, explainer and
calculator now agree and show every intermediate. Guard: the
calculator computes rather than quotes, and scripts/test_psr.js
exercises it.
Why it matters
PSR is a test people use to decide whether a backtest is trustworthy. An implementation that understates the denominator overstates the z-statistic and reports more confidence than the data supports — in the direction that makes bad strategies look good.
What this verification does not establish
- This verifies arithmetic, not statistical assumptions. PSR assumes serially independent returns; on smoothed or illiquid marks it over-reports.
- It says nothing about how many strategy variants were tried — that is the Deflated Sharpe Ratio’s job.
- The moments are treated as known. Under normality the sample excess kurtosis has variance of roughly 24/n, so at n = 24 its standard error is about 1.0; plugging a point estimate such as γ₂ = 7.00 into the denominator treats the noisiest input as exact. A stationary bootstrap of SR, skewness and kurtosis over the return series is the right next experiment; it has now been run on a synthetic process with these moments, see How uncertain is a PSR at n = 24?. No numbers from it are folded into this report, which checks arithmetic only.
Reproduce
from scipy.stats import norm; norm.cdf(1.5*23**0.5/2.4850)
→ 0.9981. Or open the
calculator with
SR 1.50, SR* 0, n 24, skew −1.20, kurtosis 7.00.