# Book risk (book_risk)

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

**What it asks.** What does this return series say about annual vol, tail loss and drawdowns, and how much data sits behind each tail figure?

Takes a return series and gives annual vol, skew, kurtosis, historical VaR and expected shortfall, normal and Cornish-Fisher VaR, and the max drawdown with its length and recovery, in percent and money. All returns come from the caller. It describes the series sent. It is not a forecast of loss, and it says when too few observations sit behind a tail figure.

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 |
| --- | --- | --- | --- | --- |
| `returns_pct` | array | 2 to 2000 items | yes | Simple return of each period in percent, oldest first. |
| `periods_per_year` | whole number | 1 to 365 | yes | Periods in a year: 252 for trading days, 12 for months. |
| `confidence_pct` | number | 90 to 99.9 | yes | Confidence for VaR and shortfall, in percent. |
| `nav` | number | over 0 to 10000000000000 | yes | NAV in money, for the money figures. |

## 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
{
  "returns_pct": [
    1.2, 0.8, 1, 0.6, 1.4, -0.4, 1, 0.8, 1.2, 0.4,
    0.6, 1, 0.8, 1.2, -0.6, 1, 0.6, 0.8, 1.4, 0.2,
    -6, -3, 1.6, 2, 1, 0.8, 0.4, 1.2, -0.8, 1,
    0.6, 1.2, 0.8, 0.4, 1, 0.6, 1.2, -1, 0.8, 1,
    0.6, 1.4, 0.8, -0.2, 1, 1.2, 0.4, 0.8, -4, 0.4,
    1, 1.2, 0.8, 0.6, 1, 0.4, 0.8, 1.2, 0.6, 1
  ],
  "periods_per_year": 12,
  "confidence_pct": 95,
  "nav": 100000000
}
```

## What the template gives

On these numbers, at 95% confidence, one period's loss line is 1.1% of NAV by history, 1.58% by a normal and 2.21% by Cornish-Fisher, with 3 of 60 observations past the historical line and a max drawdown of 8.82%.

- Annual vol: 4.4121%
- Historical VaR, one period: 1.1% (watch)
- Expected shortfall, one period: 4.3333% (watch)
- Normal VaR, one period: 1.5816%
- Cornish-Fisher VaR, one period: 2.2072% (watch)
- Max drawdown: 8.82%
- Time under water: 38.33%
- Observations past the VaR line: 3 observations (watch)

Where it flips, with the other inputs held:

- `observations` at 181: At 95% confidence, a series of at least this many observations has 10 or more past the VaR line.

## Formula

Annual vol = sample stdev x sqrt(periods). Historical VaR = -(type 7 quantile at 1 - c). Shortfall = -(mean of returns at or below that line). Normal VaR = -(mean + z x stdev), z = invPhi(1 - c). Cornish-Fisher VaR = -(mean + w x stdev), w = z + (z^2 - 1) S / 6 + (z^3 - 3 z) K / 24 - (2 z^3 - 5 z) S^2 / 36, S skew, K kurtosis less 3.

## Assumptions

- All VaR and shortfall figures are for one period of the series, not a year, and are positive for a loss.
- The stdev divides by n - 1. Skew and kurtosis are plain moment ratios with no small-sample correction, and kurtosis is not excess: a normal is 3.
- The historical line interpolates between sorted returns (type 7). Observations past the line are those at or below it.
- Normal and Cornish-Fisher VaR use the sample mean. The expansion takes excess kurtosis, which is kurtosis less 3.
- Annual vol scales by the square root of periods, which treats periods as unrelated. The lag-one autocorrelation is shown so a reader can see when they aren't.
- The carry-over vol takes that autocorrelation as an AR(1) over one year of p periods: annual vol x sqrt(1 + 2 x sum for k < p of (1 - k / p) x rho^k). With one period a year, nothing is scaled, so it equals annual vol.
- Drawdowns compound the simple returns from a starting value of 1, so a loss in the first period counts. Length runs peak to trough, recovery runs trough to the first period back at the peak.
- Under water means below the running peak at the end of the period.
- Money figures are the percent figures on the nav sent.

## Not modeled

- Positions. Only the return series is seen, so no factor, concentration or leverage view.
- Horizons longer than one period. VaR is not scaled up.
- Losses the series does not contain. History shows only what happened in it.
- Stale or smoothed marks, beyond the autocorrelation shown.
- The cost and time of getting out at the VaR line.
- Changes in the book over time. All periods count equally, with no decay.

## 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/book_risk \
  -H 'content-type: application/json' \
  -d '{"returns_pct":[1.2,0.8,1,0.6,1.4,-0.4,1,0.8,1.2,0.4,0.6,1,0.8,1.2,-0.6,1,0.6,0.8,1.4,0.2,-6,-3,1.6,2,1,0.8,0.4,1.2,-0.8,1,0.6,1.2,0.8,0.4,1,0.6,1.2,-1,0.8,1,0.6,1.4,0.8,-0.2,1,1.2,0.4,0.8,-4,0.4,1,1.2,0.8,0.6,1,0.4,0.8,1.2,0.6,1],"periods_per_year":12,"confidence_pct":95,"nav":100000000}' \
  | jq '{summary, flags, flips, not_modeled, inputs_hash}'
```

### MCP

The tool is `book_risk` 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":"book_risk","arguments":{"returns_pct":[1.2,0.8,1,0.6,1.4,-0.4,1,0.8,1.2,0.4,0.6,1,0.8,1.2,-0.6,1,0.6,0.8,1.4,0.2,-6,-3,1.6,2,1,0.8,0.4,1.2,-0.8,1,0.6,1.2,0.8,0.4,1,0.6,1.2,-1,0.8,1,0.6,1.4,0.8,-0.2,1,1.2,0.4,0.8,-4,0.4,1,1.2,0.8,0.6,1,0.4,0.8,1.2,0.6,1],"periods_per_year":12,"confidence_pct":95,"nav":100000000}}}' \
  | 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 book_risk > mine.json           # replace every number in it
node hedgefund.mjs run book_risk mine.json | jq '{summary, flags, flips, not_modeled, inputs_hash}'
```

In a script:

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

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