Models / Credit / private_credit_tape
Private credit tape
How do this loan tape's losses and interest fall through its tranches, and how much sits under the senior at a 1% chance of impairment?
Runs a loan tape through a one-factor Gaussian copula and a senior-first waterfall. Gives each tranche's expected loss, impairment chance, 99% tail loss and coupon cover, the pool loss distribution, and the junior size that puts the senior's impairment chance at 1%. Loans, rates and default rates come from the caller. Not a rating, an offering or advice.
On these numbers, across 5000 hypothetical paths with seed 7 over 3 years, the pool loses 5.42% on average and 25.8% at the 99th percentile, and the senior, with 25% under it, is impaired on 1.2% of paths. A size of 25.8% under it would put that chance at 0.98%.
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.
Pool expected loss
5.4195%of pool
Exact figure from the formula: 5.4168%. The gap is sampling noise.
Monte Carlo error on that
0.0842347%of pool
One standard error: stdev / sqrt(sims).
Pool loss stdev
5.9563%of pool
Pool loss, 95th percentile
17.4%of pool
Pool loss, 99th percentile
25.8%of pool
Size under the senior for a 1% chance
breach25.8%of pool
From these draws: 0.98% of paths lie above it. About 95% sampling band for this percentile: 24.7 to 27.5.
Senior impairment chance
breach1.2%
With 25% under it.
Mean excess interest
7.6876%of pool
Over the horizon. Reported, not used to absorb losses.
What flips it
The value of one input where the outcome changes, with the others held.
junior_size_pct
25.8
With less than this much under the senior, more than 1% of these paths impair it.
asset_correlation_pct
16.82%now 20
Above this correlation, more than 1% of these same paths impair the senior.
default_rate_multiple
0.891404
Above this multiple of every loan's default rate, more than 1% of these same paths impair the senior.
Flags
What went past a line, and what to read before you trust the rest.
- breachWith 25% under it, the senior is impaired on 1.2% of paths. On these draws, a 1% chance sits at 25.8%. The 1% is this model's fixed reference, not a rating or a standard. senior_chance_above_one_pct
What this leaves out
What the formula leaves out, by name.
- Prepayment, amortization and new loans. Each balance stays flat until the horizon.
- Recovery timing. A loss is taken in full at the horizon, with no workout period.
- Coverage triggers, cash traps and any turbo that would redirect excess interest to principal.
- Default timing beyond the midpoint convention, and correlation that changes in stress.
- Fat tails: the copula is Gaussian, so joint extreme defaults are thin compared with a heavier-tailed one.
- Rate changes on floating loans, hedges, reserve accounts and fees other than servicing.
- Secondary-market prices, liquidity and, for tokenized notes, anything about the chain, the wrapper or transfer limits.
- Taxes.
What it took as given (9)
What it took as given to get here.
- Draw order on each path is Z, then one e per loan in list order, from one stream seeded by the seed and the model's id. Correlation and default rates change thresholds, not draws, so moving them keeps the same paths.
- Interest is simple. A performing loan pays balance * rate * H. A defaulted loan pays for half the horizon, as if it defaulted at the midpoint.
- Servicing is charged on the starting pool for the whole horizon and is paid first from interest.
- Coupons are due on each tranche's starting size for the whole horizon, even after a write-down. Interest and principal run separately: excess interest is reported and doesn't absorb losses.
- Losses hit the most junior tranche first and are taken in full at the horizon. A loss landing exactly on an attachment point impairs nothing above it. A tranche's loss figures are percents of its own size, and pool figures are percents of the pool.
- Coupon cover for a tranche is mean interest after servicing, over the coupons due on that tranche and everything senior to it.
- Percentiles use linear interpolation between order statistics, except the junior size. That one is an order statistic: the smallest size with at most 1% of these paths above it, recounted from the same draws, and rounded up at the tenth digit so it still passes when sent back.
- Correlation moves losses between tranches, but it leaves the pool's expected loss alone. The exact figure is the sum of balance * LGD * (1 - (1 - pd)^H) over the pool.
- The sleeve is for the whole pool: gross and net are 100, tail is the 99th percentile pool loss, and carry is mean interest after servicing, per year.
The data
Tranches
| tranche | size_pct | attach_pct | detach_pct | coupon_pct | expected_loss_pct | impairment_chance_pct | tail_99_loss_pct | coupon_cover_x | coupon_cover_worst_1pct_x | coupon_shortfall_chance_pct |
|---|---|---|---|---|---|---|---|---|---|---|
| Senior | 75 | 25 | 100 | 6.5 | 0.0589 | 1.2 | 1.0667 | 2.0792 | 1.6123 | 0 |
| Mezzanine | 15 | 10 | 25 | 10 | 6.5249 | 19.24 | 100 | 1.59 | 1.2329 | 0 |
| Junior | 10 | 0 | 10 | 12 | 43.97 | 68.72 | 100 | 1.3381 | 1.0376 | 0.58 |
Interest waterfall, means over paths
| item | due | mean_paid | mean_shortfall |
|---|---|---|---|
| Interest collected | 31,907,802 | ||
| Servicing | 1,500,000 | 1,500,000 | 0 |
| Senior coupon | 14,625,000 | 14,625,000 | 0 |
| Mezzanine coupon | 4,500,000 | 4,500,000 | 0 |
| Junior coupon | 3,600,000 | 3,595,155 | 4,845 |
| Excess | 7,687,647 |
Largest contributors to expected loss
| loan | balance | default_chance_over_horizon_pct | loss_if_default | expected_loss | share_of_expected_loss_pct |
|---|---|---|---|---|---|
| Loan 11 | 4,000,000 | 22.13 | 2,600,000 | 575,411 | 10.62 |
| Loan 12 | 3,000,000 | 27.1 | 2,100,000 | 569,100 | 10.51 |
| Loan 8 | 6,000,000 | 14.26 | 3,600,000 | 513,450 | 9.48 |
| Loan 9 | 6,000,000 | 14.26 | 3,600,000 | 513,450 | 9.48 |
| Loan 10 | 5,000,000 | 16.94 | 3,000,000 | 508,248 | 9.38 |
| Loan 6 | 8,000,000 | 11.53 | 4,400,000 | 507,162 | 9.36 |
| Loan 7 | 8,000,000 | 11.53 | 4,400,000 | 507,162 | 9.36 |
| Loan 4 | 10,000,000 | 8.7327 | 5,000,000 | 436,635 | 8.06 |
| Loan 1 | 20,000,000 | 4.4328 | 8,000,000 | 354,627 | 6.55 |
| Loan 5 | 8,000,000 | 8.7327 | 4,000,000 | 349,308 | 6.45 |
Simulation
| item | value |
|---|---|
| sims | 5,000 |
| seed | 7 |
| draws_per_path | 13 |
| monte_carlo_error_pct | 0.084235 |
| exact_expected_loss_pct | 5.4168 |
| gap_in_standard_errors | 0.032 |
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/private_credit_tape.md.
# Private credit tape: 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.** How do this loan tape's losses and interest fall through its tranches, and how much sits under the senior at a 1% chance of impairment?## MethodEach path draws a market factor Z and one e per loan. A loan defaults within the horizon if sqrt(rho) Z + sqrt(1 - rho) e < normInv(1 - (1 - pd)^H). Pool loss is the sum of balance * LGD over defaults. Each tranche takes min(max(loss - attach, 0), size). Interest pays servicing, then coupons senior first.## Conventions- Draw order on each path is Z, then one e per loan in list order, from one stream seeded by the seed and the model's id. Correlation and default rates change thresholds, not draws, so moving them keeps the same paths.- Interest is simple. A performing loan pays balance * rate * H. A defaulted loan pays for half the horizon, as if it defaulted at the midpoint.- Servicing is charged on the starting pool for the whole horizon and is paid first from interest.- Coupons are due on each tranche's starting size for the whole horizon, even after a write-down. Interest and principal run separately: excess interest is reported and doesn't absorb losses.- Losses hit the most junior tranche first and are taken in full at the horizon. A loss landing exactly on an attachment point impairs nothing above it. A tranche's loss figures are percents of its own size, and pool figures are percents of the pool.- Coupon cover for a tranche is mean interest after servicing, over the coupons due on that tranche and everything senior to it.- Percentiles use linear interpolation between order statistics, except the junior size. That one is an order statistic: the smallest size with at most 1% of these paths above it, recounted from the same draws, and rounded up at the tenth digit so it still passes when sent back.- Correlation moves losses between tranches, but it leaves the pool's expected loss alone. The exact figure is the sum of balance * LGD * (1 - (1 - pd)^H) over the pool.- The sleeve is for the whole pool: gross and net are 100, tail is the 99th percentile pool loss, and carry is mean interest after servicing, per year.## 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{ "loans": [ { "name": "Loan 1", "balance": 20000000, "rate_pct": 9, "pd_annual_pct": 1.5, "lgd_pct": 40 }, { "name": "Loan 2", "balance": 12000000, "rate_pct": 10, "pd_annual_pct": 2, "lgd_pct": 45 }, { "name": "Loan 3", "balance": 10000000, "rate_pct": 10, "pd_annual_pct": 2, "lgd_pct": 45 }, { "name": "Loan 4", "balance": 10000000, "rate_pct": 11, "pd_annual_pct": 3, "lgd_pct": 50 }, { "name": "Loan 5", "balance": 8000000, "rate_pct": 11, "pd_annual_pct": 3, "lgd_pct": 50 }, { "name": "Loan 6", "balance": 8000000, "rate_pct": 12, "pd_annual_pct": 4, "lgd_pct": 55 }, { "name": "Loan 7", "balance": 8000000, "rate_pct": 12, "pd_annual_pct": 4, "lgd_pct": 55 }, { "name": "Loan 8", "balance": 6000000, "rate_pct": 13, "pd_annual_pct": 5, "lgd_pct": 60 }, { "name": "Loan 9", "balance": 6000000, "rate_pct": 13, "pd_annual_pct": 5, "lgd_pct": 60 }, { "name": "Loan 10", "balance": 5000000, "rate_pct": 14, "pd_annual_pct": 6, "lgd_pct": 60 }, { "name": "Loan 11", "balance": 4000000, "rate_pct": 15, "pd_annual_pct": 8, "lgd_pct": 65 }, { "name": "Loan 12", "balance": 3000000, "rate_pct": 16, "pd_annual_pct": 10, "lgd_pct": 70 } ], "tranches": [ { "name": "Senior", "size_pct": 75, "coupon_pct": 6.5 }, { "name": "Mezzanine", "size_pct": 15, "coupon_pct": 10 }, { "name": "Junior", "size_pct": 10, "coupon_pct": 12 } ], "asset_correlation_pct": 20, "horizon_years": 3, "servicing_bps": 50, "sims": 5000, "seed": 7}```Work them out with the method above. These values come from random paths, so yours won't match exactly: treat a gap over 10% as a problem.- Pool expected loss: 5.41948 % of pool- Monte Carlo error on that: 0.08423465796 % of pool- Pool loss stdev: 5.956289785 % of pool- Pool loss, 95th percentile: 17.4 % of pool- Pool loss, 99th percentile: 25.8 % of pool- Size under the senior for a 1% chance: 25.8 % of pool- Senior impairment chance: 1.2 %- Mean excess interest: 7.687647 % of poolWhere the outcome flips, with the other inputs held:- junior_size_pct at 25.8: With less than this much under the senior, more than 1% of these paths impair it.- asset_correlation_pct at 16.81566238: Above this correlation, more than 1% of these same paths impair the senior.- default_rate_multiple at 0.891404079: Above this multiple of every loan's default rate, more than 1% of these same paths impair the senior.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:- Prepayment, amortization and new loans. Each balance stays flat until the horizon.- Recovery timing. A loss is taken in full at the horizon, with no workout period.- Coverage triggers, cash traps and any turbo that would redirect excess interest to principal.- Default timing beyond the midpoint convention, and correlation that changes in stress.- Fat tails: the copula is Gaussian, so joint extreme defaults are thin compared with a heavier-tailed one.- Rate changes on floating loans, hedges, reserve accounts and fees other than servicing.- Secondary-market prices, liquidity and, for tokenized notes, anything about the chain, the wrapper or transfer limits.- 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/private_credit_tape/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/private_credit_tape.
curl -s https://hedgefund.md/api/v1/private_credit_tape \ -H 'content-type: application/json' \ -d '{"loans":[{"name":"Loan 1","balance":20000000,"rate_pct":9,"pd_annual_pct":1.5,"lgd_pct":40},{"name":"Loan 2","balance":12000000,"rate_pct":10,"pd_annual_pct":2,"lgd_pct":45},{"name":"Loan 3","balance":10000000,"rate_pct":10,"pd_annual_pct":2,"lgd_pct":45},{"name":"Loan 4","balance":10000000,"rate_pct":11,"pd_annual_pct":3,"lgd_pct":50},{"name":"Loan 5","balance":8000000,"rate_pct":11,"pd_annual_pct":3,"lgd_pct":50},{"name":"Loan 6","balance":8000000,"rate_pct":12,"pd_annual_pct":4,"lgd_pct":55},{"name":"Loan 7","balance":8000000,"rate_pct":12,"pd_annual_pct":4,"lgd_pct":55},{"name":"Loan 8","balance":6000000,"rate_pct":13,"pd_annual_pct":5,"lgd_pct":60},{"name":"Loan 9","balance":6000000,"rate_pct":13,"pd_annual_pct":5,"lgd_pct":60},{"name":"Loan 10","balance":5000000,"rate_pct":14,"pd_annual_pct":6,"lgd_pct":60},{"name":"Loan 11","balance":4000000,"rate_pct":15,"pd_annual_pct":8,"lgd_pct":65},{"name":"Loan 12","balance":3000000,"rate_pct":16,"pd_annual_pct":10,"lgd_pct":70}],"tranches":[{"name":"Senior","size_pct":75,"coupon_pct":6.5},{"name":"Mezzanine","size_pct":15,"coupon_pct":10},{"name":"Junior","size_pct":10,"coupon_pct":12}],"asset_correlation_pct":20,"horizon_years":3,"servicing_bps":50,"sims":5000,"seed":7}' \ | jq '{summary, flags, flips, not_modeled, inputs_hash}'The tool is private_credit_tape 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":"private_credit_tape","arguments":{"loans":[{"name":"Loan 1","balance":20000000,"rate_pct":9,"pd_annual_pct":1.5,"lgd_pct":40},{"name":"Loan 2","balance":12000000,"rate_pct":10,"pd_annual_pct":2,"lgd_pct":45},{"name":"Loan 3","balance":10000000,"rate_pct":10,"pd_annual_pct":2,"lgd_pct":45},{"name":"Loan 4","balance":10000000,"rate_pct":11,"pd_annual_pct":3,"lgd_pct":50},{"name":"Loan 5","balance":8000000,"rate_pct":11,"pd_annual_pct":3,"lgd_pct":50},{"name":"Loan 6","balance":8000000,"rate_pct":12,"pd_annual_pct":4,"lgd_pct":55},{"name":"Loan 7","balance":8000000,"rate_pct":12,"pd_annual_pct":4,"lgd_pct":55},{"name":"Loan 8","balance":6000000,"rate_pct":13,"pd_annual_pct":5,"lgd_pct":60},{"name":"Loan 9","balance":6000000,"rate_pct":13,"pd_annual_pct":5,"lgd_pct":60},{"name":"Loan 10","balance":5000000,"rate_pct":14,"pd_annual_pct":6,"lgd_pct":60},{"name":"Loan 11","balance":4000000,"rate_pct":15,"pd_annual_pct":8,"lgd_pct":65},{"name":"Loan 12","balance":3000000,"rate_pct":16,"pd_annual_pct":10,"lgd_pct":70}],"tranches":[{"name":"Senior","size_pct":75,"coupon_pct":6.5},{"name":"Mezzanine","size_pct":15,"coupon_pct":10},{"name":"Junior","size_pct":10,"coupon_pct":12}],"asset_correlation_pct":20,"horizon_years":3,"servicing_bps":50,"sims":5000,"seed":7}}}' \ | 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 private_credit_tape > mine.json # replace every number in itnode hedgefund.mjs run private_credit_tape mine.json | jq '{summary, flags, flips, not_modeled, inputs_hash}'Made-up numbers that run as they stand. Replace every one.
{ "loans": [ { "name": "Loan 1", "balance": 20000000, "rate_pct": 9, "pd_annual_pct": 1.5, "lgd_pct": 40 }, { "name": "Loan 2", "balance": 12000000, "rate_pct": 10, "pd_annual_pct": 2, "lgd_pct": 45 }, { "name": "Loan 3", "balance": 10000000, "rate_pct": 10, "pd_annual_pct": 2, "lgd_pct": 45 }, { "name": "Loan 4", "balance": 10000000, "rate_pct": 11, "pd_annual_pct": 3, "lgd_pct": 50 }, { "name": "Loan 5", "balance": 8000000, "rate_pct": 11, "pd_annual_pct": 3, "lgd_pct": 50 }, { "name": "Loan 6", "balance": 8000000, "rate_pct": 12, "pd_annual_pct": 4, "lgd_pct": 55 }, { "name": "Loan 7", "balance": 8000000, "rate_pct": 12, "pd_annual_pct": 4, "lgd_pct": 55 }, { "name": "Loan 8", "balance": 6000000, "rate_pct": 13, "pd_annual_pct": 5, "lgd_pct": 60 }, { "name": "Loan 9", "balance": 6000000, "rate_pct": 13, "pd_annual_pct": 5, "lgd_pct": 60 }, { "name": "Loan 10", "balance": 5000000, "rate_pct": 14, "pd_annual_pct": 6, "lgd_pct": 60 }, { "name": "Loan 11", "balance": 4000000, "rate_pct": 15, "pd_annual_pct": 8, "lgd_pct": 65 }, { "name": "Loan 12", "balance": 3000000, "rate_pct": 16, "pd_annual_pct": 10, "lgd_pct": 70 } ], "tranches": [ { "name": "Senior", "size_pct": 75, "coupon_pct": 6.5 }, { "name": "Mezzanine", "size_pct": 15, "coupon_pct": 10 }, { "name": "Junior", "size_pct": 10, "coupon_pct": 12 } ], "asset_correlation_pct": 20, "horizon_years": 3, "servicing_bps": 50, "sims": 5000, "seed": 7}Inputs
| Input | Type | Range | Required | What it is |
|---|---|---|---|---|
| loans | array | 1 to 100 items | yes | The loan tape. Up to 100 loans. |
| loans[].name | string | yes | A label, such as Loan 3. | |
| loans[].balance | number | 0.01 to 10000000000000 | yes | Loan balance. |
| loans[].rate_pct | number | 0 to 100 | yes | Interest rate a year. 10 means 10%. |
| loans[].pd_annual_pct | number | 0 to 100 | yes | Yearly default chance. 3 means 3%. |
| loans[].lgd_pct | number | 0 to 100 | yes | Loss given default, as a share of balance. 45 means 45%. |
| tranches | array | 1 to 10 items | yes | Tranches, senior first, sizes summing to 100. |
| tranches[].name | string | yes | A label, such as Senior. | |
| tranches[].size_pct | number | 0.01 to 100 | yes | Share of the pool. 70 means 70%. |
| tranches[].coupon_pct | number | 0 to 100 | yes | Coupon a year on the tranche's starting size. 0 for a residual. |
| asset_correlation_pct | number | 0 to 100 | yes | Asset correlation rho in the one-factor copula. 20 means 20%. |
| horizon_years | number | 0.25 to 30 | yes | Years the tape runs. |
| servicing_bps | number | 0 to 1000 | no | Servicing fee a year on the starting pool, paid first from interest. Default 0. |
| sims | whole number | 100 to 5000 | no | Monte Carlo paths, up to 5000. Default 5000. |
| seed | whole number | 0 to 2147483647 | no | Seed for the draws. The same seed gives the same bytes. Default 1. |
Hypothetical, and on made-up numbers until you replace them. Nobody traded this, and nothing here is on offer.