# Deflated Sharpe (deflated_sharpe)

Desk: Fund terms and risk. Page: https://hedgefund.md/models/deflated_sharpe

**What it asks.** How much of this backtest's Sharpe survives the number of trials it was picked from?

Tests a reported Sharpe against what the best of many trials would show by luck, with skew, kurtosis and sample length. Gives the probabilistic and deflated Sharpe, the minimum sample length and the trial count where it falls below the confidence. All numbers come from the caller. It measures selection only. It can't detect lookahead or fitting that was not counted.

Results are hypothetical: arithmetic on the numbers sent, a made-up template or a stated default. Nobody achieved them, and they aren't a forecast, a recommendation, an offer or a record of real trading. Don't show any output to investors or clients, or use it to market an investment. Terms: https://hedgefund.md/terms

## Inputs

| Input | Type | Range | Required | What it is |
| --- | --- | --- | --- | --- |
| `sharpe_annual` | number | -10 to 20 | yes | Annual Sharpe of the chosen strategy. |
| `periods_per_year` | whole number | 1 to 365 | yes | Periods in a year of the returns: 252, 52 or 12. |
| `observations` | whole number | 2 to 1000000 | yes | Number of return periods in the sample. |
| `skew` | number | -50 to 50 | yes | Skew of the returns. A normal is 0. |
| `kurtosis` | number | 0 to 3000 | yes | Kurtosis of the returns, not excess. A normal is 3. |
| `trials` | whole number | 1 to 1000000000 | yes | Independent trials the strategy was picked from, including every one discarded. |
| `trial_sharpe_std_annual` | number | 0 to 20 | yes | Stdev across the trials' annual Sharpes. |
| `benchmark_sharpe_annual` | number | -10 to 20 | no | Annual Sharpe to test against in the plain probabilistic figure. |
| `confidence_pct` | number | 50 to 99.99 | yes | Confidence to compare with, in percent. |

## Template

Made-up numbers that run as they stand, so you can see the shape of a result. They aren't market levels, typical values or estimates, and the names in them don't exist. Replace every number before you read anything into a run.

```json
{
  "sharpe_annual": 1.8,
  "periods_per_year": 252,
  "observations": 1260,
  "skew": -0.5,
  "kurtosis": 6,
  "trials": 100,
  "trial_sharpe_std_annual": 0.5,
  "benchmark_sharpe_annual": 0,
  "confidence_pct": 95
}
```

## What the template gives

On these numbers, a hypothetical annual Sharpe of 1.8 over 1260 periods has a probabilistic Sharpe of 99.99% against the benchmark and a deflated Sharpe of 87.57% after 100 trials, which measures selection only, not lookahead or fitting that was not counted.

- Deflated Sharpe: 87.57% (breach)
- Probabilistic Sharpe against the benchmark: 99.99%
- Expected best of the trials, annual: 1.2653
- Annual Sharpe where the deflated figure meets the confidence: 2.0316
- Trials where it falls below: 31 trials
- Minimum sample against the benchmark: 227 periods
- Minimum sample against the best of the trials: 2,560 periods (watch)
- Sharpe a period: 0.113389

Where it flips, with the other inputs held:

- `trials` at 31: At this many independent trials or more, the deflated Sharpe falls below 95%. Below it, the figure is at or above that confidence.
- `sharpe_annual` at 2.031643036: At 100 trials, an annual Sharpe above this gives a deflated figure above 95%, with the other inputs as sent.

## Formula

S = annual / sqrt(p). V = 1 - g3 S + (g4 - 1) / 4 x S^2. PSR(S*) = Phi((S - S*) sqrt(n - 1) / sqrt(V)). S0 = sigma x ((1 - gamma) invPhi(1 - 1 / N) + gamma invPhi(1 - 1 / (N e))), sigma the trials' per-period stdev, gamma the Euler-Mascheroni constant. Deflated = PSR(S0). Minimum track = 1 + V x (invPhi(c) / (S - S*))^2.

## Assumptions

- Annual Sharpes are turned into per-period Sharpes by dividing by sqrt(periods_per_year), and that includes the trials' stdev.
- Kurtosis is not excess. A normal is 3. If the moments can't belong to one series (kurtosis under skew squared plus 1), the model stops and says so.
- The deflated figure tests against S0 alone, as in the published form, with no benchmark added. benchmark_sharpe_annual only feeds the plain probabilistic figure and its minimum sample.
- The trials' true Sharpes are taken as centered on zero, and the trials as independent. The expected maximum is the usual approximation, close for many trials and rough for a few.
- One trial means no selection, so S0 is 0.
- The trial flip is the smallest whole number of trials at which the deflated figure is under the confidence. The Sharpe flip solves the same equation for S.
- Minimum sample length is rounded up to a whole period. It keeps the sample's skew and kurtosis.

## Not modeled

- Lookahead, survivorship and bad data. This measures selection. It can't see any of them.
- Fitting not counted as a trial. Tweaks that never got logged make the true trial count higher than the number sent.
- Correlation among the trials. Send the count of independent trials, which is usually lower than the raw count.
- Costs, slippage, capacity and borrow, unless they are already in the Sharpe sent.
- Whether anything carries over to another period. A high figure says the selection effect alone doesn't explain the sample, nothing more.

## Call it

Every result has the same fields: summary, readings, tables, charts, flags, flips, formula, assumptions, not_modeled, hypothetical and inputs_hash. Read flags, flips and not_modeled with the summary, and pass them along together.

### HTTP

No key. The tools keep nothing you send. Send positions by POST, because a URL can land in a server log.

```bash
curl -s https://hedgefund.md/api/v1/deflated_sharpe \
  -H 'content-type: application/json' \
  -d '{"sharpe_annual":1.8,"periods_per_year":252,"observations":1260,"skew":-0.5,"kurtosis":6,"trials":100,"trial_sharpe_std_annual":0.5,"benchmark_sharpe_annual":0,"confidence_pct":95}' \
  | jq '{summary, flags, flips, not_modeled, inputs_hash}'
```

### MCP

The tool is `deflated_sharpe` on the server at https://hedgefund.md/mcp.

```bash
curl -s https://hedgefund.md/mcp \
  -H 'content-type: application/json' -H 'accept: application/json' \
  -d '{"jsonrpc":"2.0","id":1,"method":"tools/call","params":{"name":"deflated_sharpe","arguments":{"sharpe_annual":1.8,"periods_per_year":252,"observations":1260,"skew":-0.5,"kurtosis":6,"trials":100,"trial_sharpe_std_annual":0.5,"benchmark_sharpe_annual":0,"confidence_pct":95}}}' \
  | jq '.result.structuredContent | {summary, flags, flips, not_modeled, inputs_hash}'
```

### The library

The same code on your own machine, with no dependencies, and nothing is sent anywhere.

```bash
curl -fsSL https://hedgefund.md/hedgefund.mjs -o hedgefund.mjs   # read it before you run it
node hedgefund.mjs template deflated_sharpe > mine.json           # replace every number in it
node hedgefund.mjs run deflated_sharpe mine.json | jq '{summary, flags, flips, not_modeled, inputs_hash}'
```

In a script:

```js
import { run, template } from "./hedgefund.mjs";

const inputs = template("deflated_sharpe"); // replace every number in it
const { summary, flags, flips, not_modeled } = run("deflated_sharpe", inputs);
```
