# Stress the book (stress_book)

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

**What it asks.** What does each shock do to this book of sleeves, which shock is worst, and what drives it?

Applies shocks to equity, rates, spreads, vol, token discounts and FX across a book of weighted sleeves, using the sensitivities sent. Shows P and L by sleeve, factor and scenario, the worst scenario and what drove it. The sensitivities come from the caller. With no scenarios sent it uses generic shocks chosen for round numbers, not history. It is a first-order map, not a probability or a forecast.

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 |
| --- | --- | --- | --- | --- |
| `sleeves` | array | 1 to 40 items | yes | The book. Weights are percent of NAV. Whatever they don't cover is cash at zero P and L. |
| `sleeves[].name` | string |  | yes | A label, such as Credit carry. |
| `sleeves[].weight_pct` | number | -500 to 500 | yes | Weight in percent of NAV. Negative for a short sleeve. |
| `sleeves[].equity_beta` | number | -20 to 20 | no | Percent the sleeve moves per 1% on equities. |
| `sleeves[].rates_dv01_pct_per_100bp` | number | -100 to 100 | no | Percent the sleeve gains when rates rise 100 bp. A long bond sleeve is negative: about -5 for a 5-year. |
| `sleeves[].spread_duration_years` | number | -50 to 50 | no | Spread duration in years. Positive loses when spreads widen. |
| `sleeves[].vega_pct_per_vol_pt` | number | -50 to 50 | no | Percent the sleeve gains per vol point up. |
| `sleeves[].token_discount_share_pct` | number | 0 to 100 | no | Share of the sleeve held as tokens that can trade under NAV, in percent. |
| `sleeves[].fx_delta_pct` | number | -500 to 500 | no | Percent of the sleeve exposed to the foreign currency. Gains when that currency rises. |
| `scenarios` | array | 0 to 40 items | no | Shocks. Leave out to use generic shocks, not history. |
| `scenarios[].name` | string |  | yes | A label for the shock. |
| `scenarios[].equity_pct` | number | -100 to 300 | no | Equity move in percent. |
| `scenarios[].rates_bps` | number | -2000 to 2000 | no | Rate move in basis points. |
| `scenarios[].spread_bps` | number | -2000 to 5000 | no | Spread move in basis points. Up is wider. |
| `scenarios[].vol_pts` | number | -100 to 300 | no | Implied vol move in vol points. |
| `scenarios[].token_discount_pct` | number | 0 to 100 | no | Discount of tokens to NAV in percent. |
| `scenarios[].fx_pct` | number | -100 to 300 | no | Move of the foreign currency in percent. |
| `max_loss_pct` | number | over 0 to 100 | no | Optional limit on a scenario's loss, in percent of NAV. |

## 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
{
  "sleeves": [
    {
      "name": "Long-short equity",
      "weight_pct": 30,
      "equity_beta": 0.5
    },
    {
      "name": "Credit carry",
      "weight_pct": 25,
      "equity_beta": 0.2,
      "rates_dv01_pct_per_100bp": -1,
      "spread_duration_years": 4
    },
    {
      "name": "Tokenized bills",
      "weight_pct": 15,
      "rates_dv01_pct_per_100bp": -0.25,
      "token_discount_share_pct": 100
    },
    {
      "name": "Tokenized property",
      "weight_pct": 20,
      "equity_beta": 0.1,
      "rates_dv01_pct_per_100bp": -3,
      "spread_duration_years": 3,
      "token_discount_share_pct": 100,
      "fx_delta_pct": 20
    },
    {
      "name": "Vol overlay",
      "weight_pct": 10,
      "equity_beta": -0.5,
      "vega_pct_per_vol_pt": 2
    }
  ],
  "scenarios": [
    {
      "name": "Equities fall",
      "equity_pct": -25,
      "rates_bps": -50,
      "spread_bps": 150,
      "vol_pts": 20,
      "token_discount_pct": 3,
      "fx_pct": -2
    },
    {
      "name": "Rates up, spreads wider",
      "equity_pct": -8,
      "rates_bps": 150,
      "spread_bps": 100,
      "vol_pts": 5,
      "token_discount_pct": 2,
      "fx_pct": 0
    },
    {
      "name": "Token discount opens",
      "equity_pct": -3,
      "rates_bps": 0,
      "spread_bps": 50,
      "vol_pts": 2,
      "token_discount_pct": 15,
      "fx_pct": 0
    },
    {
      "name": "Currency slides",
      "equity_pct": -2,
      "rates_bps": 0,
      "spread_bps": 0,
      "vol_pts": 2,
      "token_discount_pct": 1,
      "fx_pct": -15
    }
  ],
  "max_loss_pct": 5
}
```

## What the template gives

On these numbers, the worst of 4 scenarios is "Token discount opens" at -6.16% of NAV, driven most by Token discount at -5.25 and the sleeve "Tokenized property" at -3.36, past the limit of 5%.

- Worst scenario, P and L: -6.16% of NAV (breach)
- Largest factor in it: -5.25% of NAV
- Largest sleeve in it: -3.36% of NAV
- Offsets in it: 0.55% of NAV
- Mildest scenario, P and L: -0.89% of NAV
- Gross weight: 100% of NAV
- Room to the limit: -1.16% of NAV (breach)

Where it flips, with the other inputs held:

- `shock_multiple` at 0.8116883117: At this multiple of the shocks, "Token discount opens" reaches the limit of 5% of NAV. The answer is exact because the map is linear.
- `sleeves.3.weight_pct` at 13.0952381: At this weight for "Tokenized property", with the others as sent, the worst of these scenarios reaches the limit of 5% of NAV.

## Formula

Sleeve P and L % = beta x equity + dv01 x rates_bps / 100 - spread_duration x spread_bps / 100 + vega x vol_pts - token_share x discount / 100 + fx_delta x fx / 100. Book P and L % of NAV = sum of weight / 100 x sleeve P and L. Shock multiple to the limit = limit / worst loss, exact because the map is linear.

## Assumptions

- Every effect is first order and additive: sensitivity times shock, with no cross terms.
- rates_dv01_pct_per_100bp is signed as a P and L for rates up 100 bp, so a long bond sleeve is negative. spread_duration_years is signed as a duration, so positive loses when spreads widen.
- The token discount cost is the share held as tokens times the discount, in percent of the sleeve.
- fx_delta_pct is the percent of the sleeve exposed. Its P and L is that share times the currency's move.
- Weights are percent of NAV and need not add to 100. The rest is cash at zero P and L, and a total over 100 means borrowed exposure.
- All shocks hit at once, with no time to act. Sleeves are as sent, with no rebalancing.
- The worst scenario is the lowest book P and L. On a tie, the first one listed. Its driver is the single most negative factor, and the single most negative sleeve.
- With no scenarios, the set is generic shocks chosen for round numbers. They describe no event and carry no likelihood.

## Not modeled

- Convexity and cross effects: gamma, rate convexity, and a vega that changes as the shock grows.
- How likely any scenario is, or how the shocks relate to one another.
- Sleeve moves that don't come through these six factors.
- Margin calls, financing, forced sales and the cost of getting out.
- A loss floor. A linear sensitivity can show a long sleeve losing more than its capital, and a flag says so.
- Taxes and fees.

## 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/stress_book \
  -H 'content-type: application/json' \
  -d '{"sleeves":[{"name":"Long-short equity","weight_pct":30,"equity_beta":0.5},{"name":"Credit carry","weight_pct":25,"equity_beta":0.2,"rates_dv01_pct_per_100bp":-1,"spread_duration_years":4},{"name":"Tokenized bills","weight_pct":15,"rates_dv01_pct_per_100bp":-0.25,"token_discount_share_pct":100},{"name":"Tokenized property","weight_pct":20,"equity_beta":0.1,"rates_dv01_pct_per_100bp":-3,"spread_duration_years":3,"token_discount_share_pct":100,"fx_delta_pct":20},{"name":"Vol overlay","weight_pct":10,"equity_beta":-0.5,"vega_pct_per_vol_pt":2}],"scenarios":[{"name":"Equities fall","equity_pct":-25,"rates_bps":-50,"spread_bps":150,"vol_pts":20,"token_discount_pct":3,"fx_pct":-2},{"name":"Rates up, spreads wider","equity_pct":-8,"rates_bps":150,"spread_bps":100,"vol_pts":5,"token_discount_pct":2,"fx_pct":0},{"name":"Token discount opens","equity_pct":-3,"rates_bps":0,"spread_bps":50,"vol_pts":2,"token_discount_pct":15,"fx_pct":0},{"name":"Currency slides","equity_pct":-2,"rates_bps":0,"spread_bps":0,"vol_pts":2,"token_discount_pct":1,"fx_pct":-15}],"max_loss_pct":5}' \
  | jq '{summary, flags, flips, not_modeled, inputs_hash}'
```

### MCP

The tool is `stress_book` 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":"stress_book","arguments":{"sleeves":[{"name":"Long-short equity","weight_pct":30,"equity_beta":0.5},{"name":"Credit carry","weight_pct":25,"equity_beta":0.2,"rates_dv01_pct_per_100bp":-1,"spread_duration_years":4},{"name":"Tokenized bills","weight_pct":15,"rates_dv01_pct_per_100bp":-0.25,"token_discount_share_pct":100},{"name":"Tokenized property","weight_pct":20,"equity_beta":0.1,"rates_dv01_pct_per_100bp":-3,"spread_duration_years":3,"token_discount_share_pct":100,"fx_delta_pct":20},{"name":"Vol overlay","weight_pct":10,"equity_beta":-0.5,"vega_pct_per_vol_pt":2}],"scenarios":[{"name":"Equities fall","equity_pct":-25,"rates_bps":-50,"spread_bps":150,"vol_pts":20,"token_discount_pct":3,"fx_pct":-2},{"name":"Rates up, spreads wider","equity_pct":-8,"rates_bps":150,"spread_bps":100,"vol_pts":5,"token_discount_pct":2,"fx_pct":0},{"name":"Token discount opens","equity_pct":-3,"rates_bps":0,"spread_bps":50,"vol_pts":2,"token_discount_pct":15,"fx_pct":0},{"name":"Currency slides","equity_pct":-2,"rates_bps":0,"spread_bps":0,"vol_pts":2,"token_discount_pct":1,"fx_pct":-15}],"max_loss_pct":5}}}' \
  | 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 stress_book > mine.json           # replace every number in it
node hedgefund.mjs run stress_book mine.json | jq '{summary, flags, flips, not_modeled, inputs_hash}'
```

In a script:

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

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