Models / Credit / credit_carry
Credit carry
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.
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, and on made-up numbers until you replace them. Nobody traded this, and nothing here is on offer.
Readings
The numbers, with their units.
Hypothetical carry per year
337,000
3.37% of NAV.
Book spread DV01
3,850per basis point
Break-even widening, one year
87.53bps
Total carry over total DV01.
Largest jump to default
2,400,000
Credit A
Largest jump, share of NAV
breach24%
Largest jump, in years of carry
watch7.1217years
Effective number of names
3.8462
One over the sum of squared notional weights.
Excess spread over expected loss
120bps
Notional-weighted across the 4 credits with a default rate.
What flips it
The value of one input where the outcome changes, with the others held.
widening_bps
87.53
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
9.2857%now 10
Credit D: above this yearly default rate, the spread no longer covers expected loss.
credits.0.real_world_pd_pct
2now 0.5
Credit A: above this yearly default rate, the spread no longer covers expected loss.
credits.1.real_world_pd_pct
4.1667now 1.5
Credit B: above this yearly default rate, the spread no longer covers expected loss.
credits.2.real_world_pd_pct
6.4615now 3
Credit C: above this yearly default rate, the spread no longer covers expected loss.
credits.3.recovery_pct
35now 30
Credit D: below this recovery, the spread no longer covers expected loss at the default rate sent.
Flags
What went past a line, and what to read before you trust the rest.
- breachA default of Credit A would cost 24% of NAV, past this model's fixed 10% line. That line is a default, not advice on where a line belongs. concentration
- watchOne default of Credit A takes 7.12 years of the whole book's carry. one_default_outweighs_carry
- watchCredit D: the 650 bps spread is below the 700 bps expected loss at the default rate sent. spread_below_expected_loss
- watchOnly 4 of 5 credits have a real-world default rate, so excess spread covers only those. partial_default_rates
What this leaves out
What the formula leaves out, by name.
- 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.
What it took as given (8)
What it took as given to get here.
- 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.
- Carry per year
- Jump to default
The data
Credits
| name | notional | spread_bps | recovery_pct | hazard_pct | implied_default_chance_1y_pct | carry_per_year | jump_to_default | spread_dv01 | break_even_widening_bps | real_world_pd_pct | expected_loss_bps | excess_spread_bps | implied_over_real_world |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Credit A | 4,000,000 | 120 | 40 | 2 | 1.9801 | 48,000 | 2,400,000 | 1,800 | 26.67 | 0.5 | 30 | 90 | 3.96 |
| Credit B | 2,000,000 | 250 | 40 | 4.1667 | 4.0811 | 50,000 | 1,200,000 | 800 | 62.5 | 1.5 | 90 | 160 | 2.721 |
| Credit C | 2,000,000 | 420 | 35 | 6.4615 | 6.2572 | 84,000 | 1,300,000 | 700 | 120 | 3 | 195 | 225 | 2.086 |
| Credit D | 1,000,000 | 650 | 30 | 9.2857 | 8.8676 | 65,000 | 700,000 | 300 | 216.67 | 10 | 700 | -50 | 0.887 |
| Credit E | 1,000,000 | 900 | 25 | 12 | 11.31 | 90,000 | 750,000 | 250 | 360 | not sent | not sent | not sent | not sent |
Parallel widening, first order
| widening_bps | mark_loss | share_of_nav_pct | one_year_carry | net_after_one_year |
|---|---|---|---|---|
| 25 | 96,250 | 0.9625 | 337,000 | 240,750 |
| 50 | 192,500 | 1.925 | 337,000 | 144,500 |
| 100 | 385,000 | 3.85 | 337,000 | -48,000 |
| 200 | 770,000 | 7.7 | 337,000 | -433,000 |
| 400 | 1,540,000 | 15.4 | 337,000 | -1,203,000 |
Paste it into your agent
No connection needed. This prompt carries the method, the conventions and what this site's engine gives on the made-up numbers, so your agent can check its own work before it touches real ones. Every prompt, with its hash, is listed at /prompts.json. A copy that stays fixed for this version, for a stack to point at: /v/1.0/models/credit_carry.md.
# Credit carry: a prompt from hedgefund.mdPaste this into your AI agent. It needs no connection to hedgefund.md. An agent that can run code should write and run it, and shouldn't work a long series by hand.**Question.** 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?## MethodHazard 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.## Conventions- 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.## Step 1: check your method on these made-up numbersThe numbers and names below are made up. They aren't market levels, typical values or anyone's real figures.```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]}```Work them out with the method above. Yours should match these within 0.5%.- Hypothetical carry per year: 337000- Book spread DV01: 3850 per basis point- Break-even widening, one year: 87.53246753 bps- Largest jump to default: 2400000- Largest jump, share of NAV: 24 %- Largest jump, in years of carry: 7.121661721 years- Effective number of names: 3.846153846- Excess spread over expected loss: 120 bpsWhere the outcome 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.If yours differ, stop and tell the person which ones and by how much. Don't go on to real numbers until they match, or until the person has seen the difference. These values come from one engine and can be wrong too.## Step 2: run it on the person's numbersReplace every input with the person's own. Ask for any number you don't have, and don't invent one.## Step 3: reportGive one sentence beginning "On these numbers,", then the values, then where the outcome flips. Always add what this leaves out:- 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.## LimitsResults are hypothetical: arithmetic on the numbers given, prepared with hindsight. Nobody achieved them, and costs and liquidity are only as assumed. They aren't a forecast, a recommendation, an offer or a record of real trading. Don't say what to buy, sell or hold, and don't use them to market an investment. Whether a person may hold or trade an instrument is a legal question for counsel.Provided as is, at your own risk, by hedgefund.md, which is not a fund and isn't anyone's adviser. Version 1.0. Source: https://hedgefund.md/models/credit_carry/prompt.md. Terms: https://hedgefund.md/termsCall it
The same code answers on this page, over HTTP, over MCP and on your own machine, and the same inputs give the same bytes. No key, and nothing is stored. Send positions by POST or run the library, because a URL can land in a server log. model.md holds all of this for your agent.
POST the inputs to /api/v1/credit_carry.
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}'The tool is credit_carry on https://hedgefund.md/mcp.
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}'One file with no dependencies, and nothing is sent anywhere.
curl -fsSL https://hedgefund.md/hedgefund.mjs -o hedgefund.mjs # read it before you run itnode hedgefund.mjs template credit_carry > mine.json # replace every number in itnode hedgefund.mjs run credit_carry mine.json | jq '{summary, flags, flips, not_modeled, inputs_hash}'Made-up numbers that run as they stand. Replace every one.
{ "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]}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. |
Hypothetical, and on made-up numbers until you replace them. Nobody traded this, and nothing here is on offer.