# Credit carry (credit_carry)

Desk: Credit. Page: https://hedgefund.md/models/credit_carry

**What it asks.** What would this list of credits earn on these spreads, what does one default cost, and how much widening takes the year's carry back?

Turns each credit's spread and recovery into a hazard rate and a one-year default chance, then gives carry, spread DV01, jump to default, excess spread over a real-world default rate, a widening table and the break-even widening. All spreads and default rates come from the caller. Not a rating, a price or advice.

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 |
| --- | --- | --- | --- | --- |
| `credits` | array | 1 to 100 items | yes | The credits held long, each earning its spread. Up to 100. |
| `credits[].name` | string |  | yes | A label, such as Credit A. |
| `credits[].notional` | number | 0.01 to 10000000000000 | yes | Position size. |
| `credits[].spread_bps` | number | 0 to 10000 | yes | Credit spread in basis points. |
| `credits[].recovery_pct` | number | 0 to 99 | yes | Recovery at default. 40 means 40%. |
| `credits[].spread_duration_years` | number | 0 to 30 | yes | Spread duration in years. |
| `credits[].real_world_pd_pct` | number | 0 to 100 | no | Optional. The caller's yearly real-world default rate. 2 means 2%. |
| `nav` | number | 0.01 to 100000000000000 | yes | Net asset value the positions are measured against. |
| `widening_bps` | array | 0 to 12 items | no | Parallel spread moves for the table. Left out or empty uses 25 to 400. |

## 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
{
  "nav": 10000000,
  "credits": [
    {
      "name": "Credit A",
      "notional": 4000000,
      "spread_bps": 120,
      "recovery_pct": 40,
      "spread_duration_years": 4.5,
      "real_world_pd_pct": 0.5
    },
    {
      "name": "Credit B",
      "notional": 2000000,
      "spread_bps": 250,
      "recovery_pct": 40,
      "spread_duration_years": 4,
      "real_world_pd_pct": 1.5
    },
    {
      "name": "Credit C",
      "notional": 2000000,
      "spread_bps": 420,
      "recovery_pct": 35,
      "spread_duration_years": 3.5,
      "real_world_pd_pct": 3
    },
    {
      "name": "Credit D",
      "notional": 1000000,
      "spread_bps": 650,
      "recovery_pct": 30,
      "spread_duration_years": 3,
      "real_world_pd_pct": 10
    },
    {
      "name": "Credit E",
      "notional": 1000000,
      "spread_bps": 900,
      "recovery_pct": 25,
      "spread_duration_years": 2.5
    }
  ],
  "widening_bps": [25, 50, 100, 200, 400]
}
```

## What the template gives

On these numbers, the book shows hypothetical carry of 337,000 a year, a parallel widening of 87.53 bps takes that back at fixed duration, and one default of Credit A costs 2,400,000, or 24% of NAV.

- Hypothetical carry per year: 337,000
- Book spread DV01: 3,850 per basis point
- Break-even widening, one year: 87.53 bps
- Largest jump to default: 2,400,000
- Largest jump, share of NAV: 24% (breach)
- Largest jump, in years of carry: 7.1217 years (watch)
- Effective number of names: 3.8462
- Excess spread over expected loss: 120 bps

Where it flips, with the other inputs held:

- `widening_bps` at 87.53246753: Over one year, a parallel widening of this size takes back the whole book's carry, at fixed duration.
- `credits.3.real_world_pd_pct` at 9.285714286: Credit D: above this yearly default rate, the spread no longer covers expected loss.
- `credits.0.real_world_pd_pct` at 2: Credit A: above this yearly default rate, the spread no longer covers expected loss.
- `credits.1.real_world_pd_pct` at 4.166666667: Credit B: above this yearly default rate, the spread no longer covers expected loss.
- `credits.2.real_world_pd_pct` at 6.461538462: Credit C: above this yearly default rate, the spread no longer covers expected loss.
- `credits.3.recovery_pct` at 35: Credit D: below this recovery, the spread no longer covers expected loss at the default rate sent.

## Formula

Hazard h = spread / (1 - R). One-year default chance = 1 - exp(-h). Carry = notional * spread. Jump to default = notional * (1 - R). Spread DV01 = notional * duration * 0.0001. Expected loss = real-world default rate * (1 - R). Break-even widening over a year = spread / duration, and for the book, total carry / total DV01.

## Assumptions

- Every credit is held long: it earns its spread and takes the loss at default.
- The hazard rate is the market-implied rate from the spread, flat over time. It carries a risk premium, so it isn't a real-world default forecast. The real-world rate is a separate number from the caller.
- Carry is notional times spread for one year, with no funding cost and no compounding.
- Jump to default is measured from par: notional * (1 - R), with no accrued interest and no mark.
- The widening table is first order: DV01 times the move, every credit moving by the same basis points, with duration fixed.
- Break-even widening compares one year's carry to a mark at a fixed duration, with no roll-down. The book's figure is total carry over total DV01, which isn't the average of the names' figures.
- Expected loss per year is the real-world default rate times (1 - R), in the same simple form the spread is quoted in.
- The sleeve's tail is the larger of the largest jump to default and the worst row of the widening table.

## Not modeled

- Correlation between defaults. Jump to default is one name at a time.
- Spread convexity and roll-down. Duration stays fixed as spreads move and time passes.
- Funding and financing of the positions.
- Rating moves, curve shape and any gap between a bond's spread and a hedge's.
- Bid-ask, liquidity and market impact on exit.
- Accrued interest at default, recovery timing and legal process.
- Taxes.

## 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/credit_carry \
  -H 'content-type: application/json' \
  -d '{"nav":10000000,"credits":[{"name":"Credit A","notional":4000000,"spread_bps":120,"recovery_pct":40,"spread_duration_years":4.5,"real_world_pd_pct":0.5},{"name":"Credit B","notional":2000000,"spread_bps":250,"recovery_pct":40,"spread_duration_years":4,"real_world_pd_pct":1.5},{"name":"Credit C","notional":2000000,"spread_bps":420,"recovery_pct":35,"spread_duration_years":3.5,"real_world_pd_pct":3},{"name":"Credit D","notional":1000000,"spread_bps":650,"recovery_pct":30,"spread_duration_years":3,"real_world_pd_pct":10},{"name":"Credit E","notional":1000000,"spread_bps":900,"recovery_pct":25,"spread_duration_years":2.5}],"widening_bps":[25,50,100,200,400]}' \
  | jq '{summary, flags, flips, not_modeled, inputs_hash}'
```

### MCP

The tool is `credit_carry` 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":"credit_carry","arguments":{"nav":10000000,"credits":[{"name":"Credit A","notional":4000000,"spread_bps":120,"recovery_pct":40,"spread_duration_years":4.5,"real_world_pd_pct":0.5},{"name":"Credit B","notional":2000000,"spread_bps":250,"recovery_pct":40,"spread_duration_years":4,"real_world_pd_pct":1.5},{"name":"Credit C","notional":2000000,"spread_bps":420,"recovery_pct":35,"spread_duration_years":3.5,"real_world_pd_pct":3},{"name":"Credit D","notional":1000000,"spread_bps":650,"recovery_pct":30,"spread_duration_years":3,"real_world_pd_pct":10},{"name":"Credit E","notional":1000000,"spread_bps":900,"recovery_pct":25,"spread_duration_years":2.5}],"widening_bps":[25,50,100,200,400]}}}' \
  | 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 credit_carry > mine.json           # replace every number in it
node hedgefund.mjs run credit_carry mine.json | jq '{summary, flags, flips, not_modeled, inputs_hash}'
```

In a script:

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

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