Skip to content

Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

ForwardFlowResults & returns

Results and returns

IRR, cash multiple, payback, shortfall and loss frequency describe different parts of the result. This chapter defines the core simulation outputs and their distributions, with the conventions needed to compare them. Read unavailable and ambiguous IRR flags alongside any return summary.

The stochastic worked examples below record early-September 2026 runs. They are historical illustrations, not refreshed 0.6.1 outputs or current GBM workspace defaults.

Implementation: outputs.rs::analyze, outputs.rs::agreement_table and forwardflow_api.rs. Hedge and monthly results are covered separately in Surfaces and hedging.

Every figure is computed for the Holder, the receiving side of the paper. The engine calls that party Owner in the ledger and the Buyer Obligor; those identifiers are quoted only where the code is being explained.

The reference run

Most examples in this chapter use one Agreement at the base terms, on a flat price path with no exits, so that every output can be checked by hand:

  • a $60,000 coin, multiple 1.475, so a Purchase Price of $88,500 payable as 60 payments of $1,475;
  • payment 1 to BTC Now, the origination payment;
  • a 5% servicing fee on every dollar delivered to the Holder;
  • the Holder buys at par, $60,000, at month 0;
  • stop sale 18 days after a missed payment, 25 bp sale cost, no static haircut;
  • Bridge { end_price: 60000, vol_annual: 0 }, BaselineCurve { lifetime: 0 }, settlement_propensity: 0, one cohort of one Agreement, seed 42.

The Holder pays $60,000 at month 0. Payment 1 goes to BTC Now. Payments 2 through 60 are delivered less the fee: 59 × $1,475 = $87,025.00 gross, $4,351.25 fee, $82,673.75 net, arriving at months 2 through 60. The engine’s horizon is term + 1 = 61 months (engine.rs::SimConfig::horizon), so every monthly vector has 62 entries, index 0 through 61.

OutputReference runWhere
Gross inflow / outflow$82,673.75 / $60,000.00owner_total_inflow, owner_total_outflow
Monthly IRR1.0953%irr_monthly
Nominal IRR per year13.144%irr_nominal_pa
Effective IRR per year13.965%irr_effective_pa
WAL31.0 monthswal_months
Payback month44payback_month
Undiscounted multiple1.3779×undiscounted_multiple
Cash recovery 12 / 24 / 3625.69% / 53.71% / 81.74%cash_recovery
Exit split1 completedexit_split
BTC Now take$1,475.00 + $4,351.25 = $5,826.25btcnow
Paper spread$0.00btcnow.paper_spread
Cumulative net cash at month 61$22,673.75cumulative_net_cash

These are the values POST /api/forwardflow/simulate returns for that configuration. The historical M0 fixture, whose effective IRR the API test simulate_endpoint_returns_fixture_numbers pins, is the same Agreement bought at 105% of the coin ($63,000) with the fixture’s 3.75% fee: inflow $83,761.56, effective IRR 12.2368%, payback month 46, multiple 1.3295×, paper spread $3,000.

Gross and net Holder flows

analyze starts from two vectors, not one. ledger.rs::Ledger::owner_monthly_gross walks every posting and accumulates, per month, the dollars that arrived at Owner (inflows) and the dollars that left Owner (outflows), both non-negative. The net vector is their difference:

\[ f_m = \text{in}_m - \text{out}_m . \]

The distinction matters only for multi-cohort books, and there it matters a great deal. In a paced book of 24 monthly cohorts, month 1 carries the second cohort’s Purchase Price out and the first cohort’s payment 1 to BTC Now (nothing to the Holder yet); month 2 carries the third cohort’s purchase out and the first cohort’s payment 2 in. Netting the two before measuring anything would cancel cash the Holder actually received against cash the Holder actually paid, and the multiple, the WAL and the recovery markers would all be understated. So:

  • IRR and payback are defined on net flows and use \(f_m\);
  • WAL, the undiscounted multiple and cash recovery are defined on gross flows and use \(\text{in}_m\) and \(\text{out}_m\) separately.

The invariant tests/invariants.rs::multi_cohort_metrics_use_gross_flows pins this: a flat 24-cohort book of identical Agreements must report the single-Agreement multiple (1.329549× at the fixture terms) and a WAL equal to the single-Agreement WAL plus the mean cohort offset (31 + 11.5 = 42.5 months). Two cohorts of one reference Agreement each show the same thing in miniature: the multiple stays 1.3779×, the WAL becomes 31.5, and payback moves to month 45.

Net IRR

outputs.rs::irr takes the net monthly vector and finds the monthly rate \(r\) at which the net present value is zero:

\[ \mathrm{NPV}(r) = \sum_{m=0}^{H} \frac{f_m}{(1+r)^m} = 0 . \]

Money is Decimal in the ledger; the solver converts to f64 first, because a root-finder on a rational type would be slow for no gain in a display quantity.

The solver (outputs.rs::irr_analysis, model audit 2026-09-06, M02) evaluates the NPV in log space: each term \(f_m (1+r)^{-m}\) is \(\exp(\ln|f_m| - m\ln(1+r))\) with the largest exponent factored out, so a 300- or 480-month horizon can neither underflow the discount factor to zero nor turn an empty tail month into \(0/0\) (before the fix that NaN read as a sign change and a healthy 480-month stream reported −100% a year). Months with a zero flow — the economically empty tail after the last receipt — are not evaluated, so padding the horizon never moves a root. A non-finite evaluation is a failure (NoIrr::Numerical), never a root.

It then classifies the flow by the sign changes along its non-zero entries. Conventional — exactly one change, one outflow phase then inflows — has exactly one root in \((-1, \infty)\) by Descartes’ rule; it is bracketed on \([-99.9%, +1{,}000%]\) a month (a root at either edge is found; one beyond is reported as no root on the scan, never as the edge) and bisected until the bracket is narrower than \(10^{-12}\). Non-conventional — several changes, a paced book’s purchases interleaved with receipts or a hedge’s premium, payoff and assignment — has no known root count: every cell of a grid (0.05% a month from −5% to +30%, 0.5% out to the edges below +200%, 5% above) is checked for a sign change and, when the NPV’s slope changes sign inside it, split at the interior extremum so two roots inside one cell are both found (the audit’s \([-10{,}000, +20{,}090, -10{,}090.14]\) has roots at exactly 0.2% and 0.7% a month, both inside the old 1% cell, and the old scan returned none). Every accepted root passes \(|\mathrm{NPV}(r)| \le 10^{-9} \sum_m |f_m|(1+r)^{-m}\). Three roots inside one 0.05% cell are not found; that limit is stated rather than hidden.

Two consequences are worth knowing:

  1. irr returns the lowest root, and irr_roots all roots located within the supported search range and resolution. When there are several the IRR is a reporting convention, not a fact of the flow: RunOutputs.irr_ambiguous is true and irr_root_count says how many (the hedge desk’s Dist carries irr_ambiguous and seeds_irr_ambiguous, the Monte Carlo summary runs_irr_ambiguous); read the cash and the multiple beside such a rate.
  2. None is a legitimate answer, and it carries a reason (irr_unavailable): single-signed flows (the Holder never receives anything, as in the solver test with origination_payments = term, or never pays), no root on the scan, or a numerical failure. The heatmap leaves unavailable cells empty, and the Monte Carlo summary drops those runs from the IRR percentiles; a missing rate alone does not identify which of these causes applies.

The unit test irr_of_simple_annuity checks −1,000 followed by twelve months of +100 (about 2.923% per month) to an NPV of \(10^{-6}\); tests/model_audit_2026_09_06.rs pins the audit’s probes — 13.965441% at 61, 240 and 300 months, 1.788676% on the 480-month term, both roots of the two-root vector, the edge roots and zero-padding invariance.

From the monthly rate the two annual figures follow:

\[ \text{nominal} = 12,r, \qquad \text{effective} = (1+r)^{12} - 1 . \]

In the reference run \(r = 1.0953%\), nominal 13.144%, effective 13.965%. The effective figure is the one every other exhibit uses: the Monte Carlo percentiles, the heatmap cells, the backtest rows, the tornado and the inverse solver all read irr_effective_pa.

Weighted average life

WAL is the gross-inflow-weighted mean month of receipt:

\[ \mathrm{WAL} = \frac{\sum_m m \cdot \text{in}_m}{\sum_m \text{in}_m}, \]

in months, counted from simulation month 0 (not from each cohort’s own purchase date). It is None when there is no inflow. In the reference run the 59 equal deliveries sit at months 2 through 60, so the WAL is their mean, 31.0. In a paced book the cohort offsets are inside the number, which is why the fixture book reports 42.5 rather than 31. Read it as “how long the average delivered dollar was out”, for the book as a whole.

Payback month

Payback is the first month \(m > 0\) at which cumulative net cash is non-negative:

\[ \text{payback} = \min{, m > 0 : \textstyle\sum_{k \le m} f_k \ge 0 ,}. \]

The \(m > 0\) guard stops a book with no purchase at month 0 from reporting payback at 0. If there is no outflow at all the field is None; if cumulative net never reaches zero inside the horizon it stays None. In the reference run the Holder needs $60,000 ÷ $1,401.25 = 42.8 deliveries, so the 43rd delivery, at month 44, is the first month the running total turns positive.

Undiscounted multiple

The multiple, also written MOIC, is gross inflow over gross outflow:

\[ \text{multiple} = \frac{\sum_m \text{in}_m}{\sum_m \text{out}_m}, \]

None when nothing was invested. Reference run: 82,673.75 / 60,000 = 1.3779×. The backtest calls the same quantity moic.

Cash recovery at 12, 24 and 36

For each marker \(M \in {12, 24, 36}\) that lies inside the horizon:

\[ \text{recovery}M = \frac{\sum{m \le M} \text{in}_m}{\sum_m \text{out}_m}. \]

The numerator is inclusive of month \(M\). The denominator is the whole run’s gross outflow, not the capital deployed by month \(M\); with no outflow the list is empty. For a single vintage those coincide; for a paced book they do not, and the marker answers “of everything I will have committed to this program, how much cash is back by month 12”. The base cockpit book reports 10.04% / 40.01% / 74.41%; the reference Agreement 25.69% / 53.71% / 81.74% (11, 23 and 35 deliveries of $1,401.25 against $60,000).

Cumulative net cash

cumulative_net_cash is the running sum of the net vector, one f64 per month, index = month. It feeds the cash-recovery curve on the cockpit and the Monte Carlo cash fan. Its last entry is the run’s total net gain: $22,673.75 in the reference run, equal to the per-Agreement capital_pnl because there is one Agreement.

The exit split

ExitSplit counts Agreements by how they left the book, one bucket per engine.rs::ExitTag, plus the ones still active:

BucketFieldMeaning
CompletedcompletedAll payments delivered; title passed.
Completed earlysettledThe Buyer paid the remaining schedule in cash and took the coin (early completion).
Stopnon_performanceA hazard draw; the coin was sold and the R-1033 waterfall ran (stop).
Stop, rational modenon_performance_rationalA draw taken under rational mode (only Buyers whose coin was worth less than their amortized obligation).
Conviction walkconviction_walksThe lost-conviction rule fired (X% below entry for Y consecutive dates).
Rational boundaryrational_boundaryThe coin sat below the computed walk-away frontier for that payment date (v1.6, input #25).
OpenopenStill active at the horizon. The horizon (engine.rs::SimConfig::horizon) gives the last originating cohort its full term plus the configured settlement tail, an origination cut-off included, so a finished run reports zero here; the engine’s own comment calls the bucket “only possible mid-experiment”.

The four stop buckets are identical in cash terms: the same sale, the same waterfall, the same postings. They are counted separately so a Holder can see how much of the stop count is the hazard prior and how much is a behavioral rule. The backtest sums them into one non_performance column per vintage. How each is drawn is the behavior chapter; the waterfall is the stop chapter.

Two counters travel with the split. suppressed_defaults is the number of hazard draws rational mode set aside (a draw on a Buyer whose coin was worth more than the amortized obligation). coin_returned_to_obligors is the spot value of the coin Buyers took at early completion, net of what they paid, summed across the book; it is the Buyer’s property, never the Holder’s cash, and is reported as a transparency counter. The base cockpit book, single run at seed 42, reports 84 completed, 55 completed early, 101 stops, and $2,123,267.67 of coin value taken at early completion.

BTC Now’s take and the paper spread

BtcNowTake is built from ledger totals to EntityId::BtcNow by posting kind (ledger.rs::Ledger::total_by_kind):

  • origination_fees = Σ TxKind::OriginationFee, the first N payments of every Agreement;
  • flow_fees = Σ TxKind::FlowFee, 5% of every dollar delivered to the Holder, whether a scheduled payment, an early-completion payoff or a stop-sale delivery;
  • total_take = the two added;
  • purchase_prices = Σ TxKind::PurchasePrice, what the Holders paid BTC Now for the paper. This is acquisition proceeds, not revenue, and is reported separately for that reason;
  • paper_spread = Σ over Agreements of (Purchase Price paid by the Holder − strike). At par it is zero. At 105% of the coin it is $3,000 per $60,000 Agreement, and tests/invariants.rs::paper_spread_identity pins $720,000 on a 240-Agreement book at 105% and −$720,000 at 95%.

BTC Now’s take is fees only. On a stop, engine.rs::stop_sale posts three transfers: the delivery to Owner net of fee, the 5% FlowFee on that delivery to BtcNow, and the refund to Obligor. There is no fourth posting. Any surplus above the Purchase Price stays with the Holder (Marc, 2026-09-03; spec v1.5 change 6), and what survives of the 2026-07 founder rule is exactly this: no dial may give BTC Now a share of a stop sale. The reference run’s take is $5,826.25 on a $60,000 coin; the base cockpit book’s is $892,043.38 ($236,487.15 origination, $655,556.23 flow) on $9,619,863.62 of paper.

btcnow_fee_monthly is the take’s timeline: the ledger’s fee postings per month (ledger.rs::Ledger::btcnow_monthly_fees) plus, at each Agreement’s origination month, its paper spread. It sums to total_take + paper_spread, which is total_take at par. It is the revenue-by-year exhibit, and it shows the alignment spec §8 view 12 asks for: when paper stops paying, the fee stream on it stops too.

The per-Agreement table

outputs.rs::agreement_table produces one AgreementRow per Agreement. The columns:

ColumnDefinition
id, origination_month, strike, term_monthsIdentity and entry; with intramonth dispersion on, strike differs Agreement by Agreement.
outcome, exit_monthOne of the seven exit-split buckets, and the month the Agreement left. For a stop, the exit month is the missed payment date, not the sale date.
payments_madePayments the Buyer made, payment 1 included.
delivered_grossEvery dollar that entered the Holder’s delivery stream: scheduled payments after the first N, the early-completion payoff, the stop-sale delivery.
fee_to_btcnowThe 5% taken on those deliveries.
owner_netdelivered_gross − fee_to_btcnow.
origination_to_btcnowThe first N payments, as many of them as were made.
shortfall_usd\(\max(0, R - V)\): the remaining schedule at the stop minus the sale proceeds. Zero unless the Agreement stopped with the coin worth less than what was still owed.
stop_proceeds_usd\(V\), the recorded sale proceeds after haircut and sale cost.
buyer_refund_usd\(\min(A, \max(0, V + A - P))\), the Buyer’s dollar refund (R-1033).
stop_surplus_usd\(\max(0, V - P)\), proceeds above the Purchase Price, delivered to the Holder.
coin_returned_usdSpot value of the coin the Buyer took at early completion, net of the payoff. Zero for every other exit; a stopped Buyer receives dollars, never coin.
purchase_priceCapital the Holder deployed on this Agreement.
expected_netWhat a fully performing Agreement would deliver net of fee: \((P - \text{first } N) \times (1 - \text{fee})\), cent-rounded.
capital_pnlowner_net − purchase_price: realized profit or capital loss in hard dollars.

Here \(P\) is the Purchase Price (strike × multiple), \(A\) is every payment the Buyer made, \(R = P - A\) is the remaining schedule at the stop, and \(V\) is the proceeds. The waterfall’s derivation is in the stop chapter.

Two rows from the base cockpit book, single run at seed 42, show the two stop regimes.

A stop with a shortfall. Agreement 0 entered at $52,099.80 (payment $1,280.78), made 9 payments and missed the 10th. \(A\) = $11,527.02, \(P\) = $76,847.20, \(R\) = $65,320.18. The sale recorded \(V\) = $30,075.17, so the refund is \(\max(0, 30{,}075.17 + 11{,}527.02 - 76{,}847.20) = 0\), the shortfall is $35,245.01, and the surplus is zero. Delivered gross is eight scheduled payments plus the whole sale, $40,321.41; net of fee $38,305.34; capital P&L −$13,794.46 against a $52,099.80 Purchase Price. expected_net was $71,788.10.

A stop with a surplus. Agreement 100 entered at $34,108.24 (payment $838.49), made 34 payments, missed the 35th at month 45. \(A\) = $28,508.66, \(P\) = $50,309.65. The sale recorded \(V\) = $55,800.18, above the Purchase Price, so the Buyer’s refund is his full paid-in $28,508.66, the shortfall is zero, the surplus is $55,800.18 − $50,309.65 = $5,490.53, and the Holder is delivered \(V\) − refund = $27,291.52 from the sale on top of 33 scheduled payments. Capital P&L +$18,105.37.

The rows are what the cockpit’s drill-down table shows and what the in-browser auditor re-derives from the raw postings.

The inverse price solver

The inverse solver targets USD cash-flow IRR. An explicit Coin objective is refused on this Dollar-only route in 0.6.1; there is no native-BTC inverse-price solver.

outputs.rs::solve_purchase_price answers “what should I pay for this paper to clear my hurdle” (input #19). It takes the user’s full configuration (their stop prior, their path view, their behavior toggles) and a target effective annual IRR, and solves the purchase fraction of strike directly (2026-09-05, the audit’s finding 7). The Holder’s receipts do not depend on what the Holder paid: the schedule, the stop waterfall (which reads the Buyer’s Purchase Price, strike × multiple) and every behavioral rule are blind to it. So one prepared run gives the receipts, and with \(r = (1 + \text{target})^{1/12} - 1\) the monthly rate,

\[ \text{price} = \frac{\sum_m \text{receipts}_m ,(1+r)^{-m}}{\sum_a \text{strike}_a ,(1+r)^{-m_a}} , \]

the strikes discounted from their origination months. A second run at that price verifies it and its irr_effective_pa is returned as irr_check. Both runs are at the configuration’s seed, so the answer is reproducible; on 5,000 Agreements the solve takes about 110 ms where the earlier bisection took 2.3 s. That bisection also rejected valid targets: it first asked the IRR routine for the rate at 1% of strike, and on a short schedule that rate lies outside the routine’s search range, so a twelve-month Agreement with a 12% target returned null where the paper attains it at $79,111.18 (finding_7_the_solver_prices_the_audits_agreement_directly).

The target must be finite and inside (−90%, +1,000%); anything else is rejected as a named-input error. The answer is a SolvedPrice { price, irr_check, attainable, note }, and the cases are said rather than nulled:

  • Nothing to price. If the Holder receives nothing (the invariant test uses origination_payments = term: every payment routes to BTC Now), price is None, attainable false, and the note says so.
  • Outside the desk’s bracket. The desk prices within 1% to 500% of strike. A target above the yield at 1%, or below the yield at five times strike, gets its price and attainable: false with the note naming the edge; a hurdle of −50% on the reference Agreement is attained at 12.8745× strike, above the bracket.
  • Otherwise attainable: true when the verifying run agrees with the target within a basis point.

POST /api/forwardflow/solve_price multiplies the fraction by the configuration’s start price to print dollars. Reference Agreement, flat path, no exits:

Hurdle (effective)Fraction of strikeDollars at $60,000
10%1.08705265$65,223.16
12%1.04153031$62,491.82
15%0.97931503$58,758.90
20%0.88941692$53,365.02
30%0.74913166$44,947.90
200%0.22128214$13,276.93

On the base cockpit book (43% vol bridge, 40% lifetime stop prior, 2.5% early completion, dispersion on) the same hurdles clear at 1.11225651 (8%), 1.03835638 (12%) and 0.98898127 (15%). The invariant inverse_price_solver_finds_the_clearing_price closes the loop: solving for the IRR the fixture price produces recovers 1.05 to within 0.001, the check reproduces that IRR, and a hurdle three points higher clears at a lower price.

Monte Carlo

This section describes the legacy Dollar montecarlo route. The paired Research workspaces use separate USD/BTC observations, interpolated quantiles and an expected-shortfall loss amount, including all paths. Do not compare that amount with the legacy mean-tail IRR below as though they were the same statistic.

outputs.rs::run_monte_carlo runs \(n\) simulations with seeds base_seed, base_seed + 1, …, base_seed + n − 1 in parallel over the rayon pool, each a complete run and analyze, so the result is one RunOutputs per seed. Because the engine is seeded (ChaCha20), any single run of the fan can be reproduced by setting that seed in the cockpit, and identical requests are memoized at the API. The caps are 1 ≤ runs ≤ 100,000 and runs × book ≤ 24,000,000 Agreement-runs (forwardflow_api.rs::validate_mc); either breach is a 400 naming input #14.

Percentiles and expected shortfall

forwardflow_api.rs::summarize collects irr_effective_pa across runs, drops the None runs, sorts ascending, and reports the same statistics for WAL. forwardflow_api.rs::percentiles on a sorted vector of length \(n\):

\[ p_q = \text{sorted}\big[, \mathrm{round}(q ,(n-1)) ,\big], \]

for \(q \in {0.01, 0.03, 0.05, 0.25, 0.50, 0.75, 0.95}\), plus worst = the minimum, and mean. There is no interpolation between order statistics; p1 wants at least 1,000 runs to be stable, and the cockpit says so. p3 is reported because it is the line a credit desk provisions to; the cockpit’s outcome-distribution panel (charts.tsx) labels it that way.

Expected shortfall is the mean of the worst tail, not where the tail starts:

\[ \mathrm{ES}q = \frac{1}{k}\sum{i<k} \text{sorted}[i], \qquad k = \max\big(1, \lceil q, n \rceil\big), \]

for \(q = 0.03\) (es3) and \(0.05\) (es5). With 1,000 runs ES3 averages the worst 30 and ES5 the worst 50. A fat left tail drags ES well below p3; a thin one leaves it close.

pct_negative_irr is the share of runs with an IRR below zero, over the runs that had one.

At the base cockpit configuration, 1,000 runs from seed 42, effective IRR: worst 7.21%, p1 9.18%, p3 9.70%, p5 10.29%, p25 12.91%, p50 14.93%, p75 17.22%, p95 21.90%, mean 15.38%, ES3 9.03%, ES5 9.46%, no negative runs; WAL median 35.9 months. The archived Model Card of 3 September 2026 used a different construction (zero-drift bootstrap at trailing-24-month volatility, drawdown multipliers on) and read, for the paced book, median 13.1%, p5 +3.9%, p95 +20.6%, 1.3% negative; these are historical examples from that card, not current workspace defaults or approved forecasts.

The histogram

irr_histogram is 50 equal-width bins from the minimum IRR to the maximum:

\[ w = \frac{\max - \min}{50}, \qquad b(x) = \min!\Big(49,\ \big\lfloor \tfrac{x - \min}{w} \big\rfloor\Big), \]

reported as (left edge, count) pairs; the floor at \(w \ge 10^{-12}\) keeps a degenerate distribution from dividing by zero, and the counts sum to the runs with an IRR.

The cash fan

cash_fan answers “when is my money actually back” under uncertainty (spec §8 view 2, chip S5). For every month \(m\) up to the shortest cumulative_net_cash across runs (all runs of one configuration share a horizon), it sorts the runs’ cumulative net cash at that month and reports p5, p25, p50, p75, p95. At the base cockpit configuration the month-12 band runs from −$9.87M (p5) to −$4.51M (p95), median −$6.48M, because the 24 cohorts are still being bought; by month 84 it runs from +$2.29M to +$8.95M, median +$4.53M.

The heatmap grids

forwardflow_api.rs::ff_heatmap computes one effective IRR per cell, every cell a full run of the user’s current book at the configuration’s seed with exactly two assumptions swapped, up to 900 cells. A cell whose configuration fails validation is a hole (None), not an error for the grid; a cell with no IRR is also None.

Price × lifetime. GridSpec::PriceDefault sets the path to a bridge pinned at the column’s ending price, at the configuration’s bridge volatility (43% if the base path is not a bridge), and the stop scenario to BaselineCurve at the row’s lifetime prior. The cockpit’s axes are ending prices at 0.25 to 2.0 times the start price, rounded to $500, against priors from 80% down to 0%; the worst corner is top-left.

Volatility × conviction. GridSpec::VolConviction sets the bridge volatility to the column (10% to 100%), holds the ending price at the configuration’s bridge end (or the start price), and forces the lost-conviction rule on at the row’s depth (10% to 80% below entry) with the configuration’s consecutive-months setting, at least 1.

The IRR = 0 frontier is drawn by the cockpit, not the API. charts.tsx::Heatmap takes the sign of every cell and draws a bold segment on any edge between adjacent cells whose signs differ; holes take part in no edge. Blue cells are positive, red negative; the frontier is where they meet.

At the base cockpit configuration the price × lifetime grid has no frontier: every cell is positive, from 2.3% at 80% stops and a $15,000 ending price to 15.7% at 80% stops and $120,000. The volatility × conviction grid does cross zero: at a 10% walk depth the paper reads 15.0% at 10% volatility, −0.9% at 70% and −9.1% at 100%, while at an 80% depth the whole row stays at or above 12.6%. The frontier runs diagonally: on the 10% row it crosses between the 55% and 70% volatility columns, and in the 100% volatility column it crosses between the 50% depth row (−0.7%) and the 60% depth row (+1.9%).

The vintage backtest

forwardflow_api.rs::ff_backtest is the static-pool exhibit: one cohort originated at every fully seasoned historical month, replayed against the actual price path that followed, by the same engine as every other number. The request is the user’s configuration; path, cohorts, origination_stop_month and market_horizon are overridden per vintage (HistoricalReplay { start_index }, one cohort, no cut-off, automatic run-off horizon), and everything else (term, multiple, fee, stop prior, behavior toggles, Agreements per cohort) is the sensitivity set. The replay is rebased so month 0 equals the configuration’s start price; entry_close carries the real close as a label.

A vintage is seasoned when its whole window fits inside recorded history. The window is term + settlement_tail_months, with the tail floor(stop_sale_lag_days / 30.4375) + 1; at the default 18-day lag this is term + 1. This keeps a final-payment stop and its receipt inside the ledger. The embedded series has 174 monthly bars, February 2012 through July 2026, so with a 60-month term the last feasible start is index 174 − 1 − 61 = 112, June 2021, and there are 113 vintages. The July program’s 60-month window allowed 114; the settlement month costs one vintage, the newest. The original request is validated before work and invalid inputs return 400. An individual vintage that fails after that is a hole counted in failed. Engine 0.6.1 includes the final feasible start index.

Each VintageRow carries the month, the entry close, irr_effective_pa, moic, net_gain_usd (inflow − outflow), deployed_usd (outflow), the shortfall, the exit counts (non_performance summing all four stop buckets), the refunds, the surplus, the coin taken at early completion and btcnow_take_usd. The blended row is the realized flow, every vintage bought at equal size:

\[ \text{blended MOIC} = \frac{\sum_v \text{deployed}_v + \sum_v \text{net gain}_v}{\sum_v \text{deployed}_v}. \]

At the base cockpit configuration (10 Agreements per vintage, intramonth dispersion on) the backtest returns 113 vintages and no holes, $66,412,793.69 deployed, $138,206,160.20 net gain, blended MOIC 3.081×; the February 2012 vintage (entry close $4.90) reports 220.8% and the June 2021 vintage 17.3%. Replay constructions carry Bitcoin’s own history and its drift; they are descriptive, not probabilistic. The Model Card §6 says why the replay rows are so high under the September waterfall: a Buyer who stops while the coin is worth more than the Purchase Price hands the surplus to the Holder, and on Bitcoin’s history that surplus is large. Every replay figure should be read with that sentence attached.

The tornado

The assumption tornado (spec §8 view 6b, chip S13) is computed by the cockpit from the API, one full run per stress at the configuration’s seed. lib.ts::TORNADO defines seven stresses, each a mutation of the user’s current base configuration. The bar for each is irr_effective_pa(stressed) − irr_effective_pa(base) in percentage points; if the base or stressed run has no IRR, the change is unavailable and its reason is shown separately, without a numerical bar. Bars are sorted by absolute damage, and clicking one loads that scenario. At the base cockpit configuration, seed 42, base 14.21%, sorted as the cockpit sorts them:

Label in lib.tsChangeIRRDamage
All three (death of Bitcoin)The −70% shock, 90% lifetime prior, origination cut at month 1, one cohort of 240−35.17%−49.4 pp
Behavioral floor (all underwater walk ~2mo)Rational mode on; conviction rule at 0% depth, 2 consecutive dates−12.21%−26.4 pp
Crash + origination stop (single strike)The −70% shock, origination cut at month 1, one cohort of 2400.69%−13.5 pp
Stop-sale haircut 50%haircut = −ln 0.5: every sale records half the path price6.37%−7.9 pp
90% lifetime defaultsBaselineCurve { lifetime: 0.9 }7.30%−6.9 pp
Origination stop @ month 3origination_stop_month: 3; the book runs off9.60%−4.6 pp
BTC −70%/3mo permanent, flow continuesShock from month 1, −70% over 3 months, no recovery10.67%−3.5 pp

The ordering is the point. The behavioral floor, every underwater Buyer walking after two dates, does more damage than a 90% lifetime stop prior; the crash on its own, with origination continuing, is the mildest bar, because later cohorts strike at the lower prices. The July spec’s note that the crash bar “renders green” was superseded in v1.4 once the residual-coin kicker left the economics; under the September waterfall it is a small negative bar.

The scenario shelf (lib.ts::SHELF, spec §8.1) is the same mechanism pointed at named fears: thirteen chips, S1 to S13, each rewriting the configuration and scrolling to the panel that answers it (S5 the cash fan, S10 the solver, S11 the price × lifetime heatmap, S13 the tornado).

Reproducing the figures

Every number in this chapter came from the reference run or the base cockpit configuration posted to the API at seed 42, from the invariant suite, or from the Model Card (cargo run --release --example w0108_refresh). The base cockpit configuration is lib.ts::BASE_CONFIG: a 43% bridge to a flat ending price, 40% lifetime stop prior, drawdown multipliers off, 2.5% early completion, 24 cohorts of 10 with intramonth strike dispersion on. The stochastic worked figures record early-September 2026 runs and have not been reissued as 0.6.1 outputs. They differ from the archived Model Card construction and current GBM workspace defaults, and should not be quoted as current program outcomes; the verification chapter says how to regenerate both.