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

Understand the model. Know what moves it.

A guide to the Agreements, assumptions and calculations behind ForwardFlow. For investment, research and risk teams evaluating the Holder’s side of the program.
One Agreement · base terms · before any stop or early completion
Coin price at entry$60,000The Holder’s purchase price at par
Buyer’s payment schedule60 × $1,475$88,500 total Purchase Price
Holder’s net per later payment$1,401.25Payments 2–60, after the 5% fee

The first payment goes to BTC Now. These are contractual cash amounts under the base assumptions, not a return forecast. See the calculation.

What ForwardFlow models

A Buyer pays for one Bitcoin under an Agreement. BTC Now is Holder 0 and sells the receiving side to a Holder. ForwardFlow models the Holder’s purchases, collections, fees and stop-sale proceeds month by month under a chosen price path and Buyer behavior.

The workspace supports scenario research, risk analysis, historical replay, hedge comparison and monthly cash and P&L analysis. The REST API and MCP interface use the same server-side engine. A result answers a question about the assumptions submitted with that run.

Read assumptions before returns. Buyer behavior parameters are priors, historical replay is descriptive, and the default option surface is stylised. A reproducible calculation does not establish that its assumptions describe future outcomes. Read the model limitations →

Choose how you measure success

Choose Dollar workspace to grow USD capital, or Coin workspace to grow BTC holdings compared with holding Bitcoin. Each keeps its own draft, strategy choices and completed research. Open the workspace chooser or read the workspace guide.

Your questionArea in your workspaceHow results update
What happens along a path, and across many simulations?ResearchOne price path updates with assumptions; run Monte Carlo to explore the distribution
Which strategy changes my outcome?Compare strategiesRun selected variants on matching simulations
What could I lose, and what cash could I need?Risk & cashInspect the completed comparison and replay an adverse path
Does the result survive a different sample or stress?EvidenceRun a check; export its samples and assumptions

Both Research homes keep the price-path view, Agreement cash and exits, scenario questions and Monte Carlo close to your assumptions. Dollar simulations measure USD outcomes; Coin simulations measure BTC outcomes against holding matching contributions. Research links directly to stress maps, the purchase-price solver and Agreement detail, whose supporting measures remain labelled USD.

Advanced analysis provides the full assumptions editor, custom strategy builder, monthly exhibits and historical vintages. The original research desk and its existing shared links remain available. Specialist results stay attached to their completed run. If you change assumptions, run again before interpreting them as results for the new scenario. Dollar and Coin drafts are separate; copying a scenario between them is explicit.

Read a result in four steps

  1. Identify the construction. One scenario is one path. Monte Carlo summarizes many seeds. Historical vintages summarize different historical start months. A bridge fixes the terminal price; its spread measures uncertainty along the way.
  2. Inspect the assumptions. Check the price model, purchase price, Buyer behavior, pacing and stop-sale terms. For a hedge, also check its surface, coverage, tenor, trading costs and lifecycle.
  3. Read loss measures beside IRR. Cash-loss frequency, cash multiple and shortfall answer different questions. An unavailable IRR is not zero. An ambiguous IRR needs its flag, and IRR percentiles may exclude seeds without a defined rate.
  4. Keep the evidence. Save the completed run package or MCP replay together with the engine build and historical-data identity. Reproduce against that same build and data. A chart image alone does not contain the full run.

Results and return measures defines the metrics. Verification and reproduction explains the evidence to retain.

The vocabulary

Buyer is the paying side. Holder is the receiving side. Completion delivers the coin after the schedule is paid. Early completion pays the remaining schedule in cash and delivers the coin. A stop sells the coin for dollars and distributes the proceeds through the stop waterfall. A stopped Buyer receives dollars, never coin.

The glossary gives definitions and formula references. Technical identifiers can retain historical names—for example, Owner means Holder and Obligor means Buyer.

Get access

Use the workspace in your browser, or connect your AI assistant to ask questions in your own words. For AI access, email info@btcnow.com. BTC Now provides an access token privately; no ForwardFlow account is needed.

For the implementation team: run and rebuild locally

The engine runs on port 8080 and the web app on port 3000. Start the existing project with START_SIMULATOR.command, or start each service from its directory:

# Engine, from backend/
cargo run --release

# Web app, from web/
npm ci
npm run dev

GET /health returns OK for liveness. GET /api/forwardflow/health returns engine identity. The historical series is compiled into the engine.

The guide is generated from book/src/. Run bash book/build.sh from the repository root to regenerate the glossary and this site. Glossary definitions live in book/glossary.json and also supply the workspace’s term explanations.

The Agreement, explained

This chapter describes the Bitcoin Purchase Agreement the way the engine models it. It is not the Agreement itself and it is not a summary of the legal text. Where the code and the program differ, the difference is stated here and the code is what the numbers come from. Every rule carries the date of the ruling that made it, so a reader can trace a number in a Model Card back to a decision.

The vocabulary is the locked one (Marc, 2026-09-02). The paying side of an Agreement is the Buyer; Buyer 0 opened it. The receiving side is the Holder; Holder 0 is always BTC Now. The paying side changes hands by Transfer, the receiving side by Sale. A Partner is anyone paid a cut of BTC Now’s fees. Engine identifiers keep older names (Owner for the Holder, Obligor for the Buyer, non_performance for a stop); they are quoted in code font when the code is being explained and nowhere else.

The parties

Three entities move money in the engine, and every cent between them goes through the ledger (ledger.rs).

PartyEngine entityPaysReceives
The BuyerEntityId::Obligor(id)The scheduleA dollar refund on a stop; the coin at completion or early completion
The HolderEntityId::OwnerThe purchase price of the paper95% of every dollar delivered after the first payment
BTC Now (Holder 0)EntityId::BtcNowNothingThe purchase price of the paper at the Sale; the first payment whole; 5% of every delivered dollar

A fourth entity, EntityId::Market, is the counterparty to the stop sale. It buys the coin for dollars and the dollars flow out from it. It has no economics of its own.

What the Buyer buys and pays

The Buyer buys one coin at a price fixed on the day the Agreement opens. That price is the strike \(K\), the coin’s dollar cost at origination. The Buyer agrees to pay a Purchase Price equal to the strike times the program multiple, \(m = 1.475\):

\[ P = K \cdot m \]

contract.rs::ContractTerms::terminal computes \(P\) and quantizes it to cents. At the base terms, a $60,000 coin carries a Purchase Price of $88,500.

The Purchase Price is paid in equal monthly payments over the term \(n\), 60 months in the current program. The payment is straight division, not an annuity:

\[ \text{PMT} = \frac{P}{n} = \frac{88{,}500}{60} = 1{,}475 \]

contract.rs::ContractTerms::schedule builds the schedule. Each payment is rounded down to the cent and the final payment absorbs the residual, so the sixty payments sum to the Purchase Price exactly. The remaining schedule after \(t\) payments is what the Buyer still owes in nominal dollars:

\[ R_t = P - \text{PMT} \cdot t \]

The term is a property of today’s offer, not of the Agreement (Marc, 2026-07-29). Nothing in the engine hard-codes 60 months (spec v1.1). The multiple implies a financing rate, the rate at which an ordinary amortizing schedule of $1,475 would repay $60,000: 16.50% nominal per year at the base terms, solved by bisection in contract.rs::ContractTerms::implied_monthly_rate. The engine calls this the implied financing rate and uses it only for display and for the moneyness boundary; no ledger amount depends on it.

The Buyer may pay early or pay extra at any time without a fee. The Buyer never owes anything after a stop. There are no late fees, no returned-payment fees, no termination fees on the Buyer’s side.

The first payment goes to BTC Now

Every Agreement opens with BTC Now as Holder 0, and the first payment flows to BTC Now whole (Marc, 2026-08-31: “BTC Now always first owner”). In the engine this is input #3, origination_payments, default 1: the first \(N\) payments post from the Buyer to BTC Now as TxKind::OriginationFee and never enter the Holder’s stream (engine.rs::run, step 1). They carry no servicing fee because nothing was delivered to a Holder.

At the base terms the first payment is $1,475, which is 1.67% of the Purchase Price (one sixtieth) and 2.46% of the Holder’s capital. The Holder should read it as a day-one cost of the paper.

The Sale of the paper to a Holder

The Holder buys the paper from BTC Now at a purchase price. The default is par: 1.00 times the strike, $60,000 on the base coin (Marc, 2026-07-12; spec v1.4). Input #6b, purchase_pct_of_strike, expresses it as a fraction of the strike so that a paced book of many cohorts scales correctly. The engine posts the purchase from the Holder to BTC Now at the origination month as TxKind::PurchasePrice.

In the engine the Holder pays at origination and payment 1 arrives a month later. In the program BTC Now is Holder 0 first and a Primary Sale follows; the Holder’s economics start at its purchase date. The engine collapses this to one date, which is the conservative reading for the Holder: it pays before the first payment that it does not receive.

After the Sale, payments 2 to 60 are delivered to the Holder. The Holder never receives the coin.

The servicing fee

BTC Now keeps 5% of every dollar delivered to the Holder, deducted at each send (Marc, 2026-08-31: “we retain 5% of each send”). It is uniform and has no exemptions: scheduled payments, early-completion payoffs and stop-sale deliveries all carry it, and the termination send does too (“we get our 5% of that as well”). The Holder receives 95% of each send.

Input #4, servicing_fee_rate, is the flat rate (spec v1.5). It is not derived from the term; the earlier form, a rate per year times the term in years, died with the September program. A 60-month term at the old 1% per year happened to give the same 5%, so the base case did not move; every other term now pays the same flat rate.

fees.rs::FeeState::split takes a delivery and returns the Holder’s net and the fee, summing exactly to the delivery. The fee posting on each delivery is the difference between the rounded cumulative fee before and after (convention C2), so any single posting may differ from 5% of that delivery by a cent, but the lifetime fee on an Agreement is exact to the cent. At the base terms nothing rounds: 5% of $1,475 is $73.75 and the Holder’s net payment is $1,401.25.

On a completed Agreement the lifetime fee is \(0.05 \cdot (P - N \cdot \text{PMT})\), which is $4,351.25 at the base terms, because the first payment is never delivered and carries no fee. The invariant suite tests this identity at terms of 36, 48, 60, 84 and 120 months (invariants.rs::fee_identity_exact_to_the_cent).

The fee is the only thing BTC Now takes from the paper after the first payment. There is no dial that gives BTC Now a share of a stop sale, and the invariant suite asserts that BTC Now’s total take equals its origination fees plus its flow fees.

The stop

A stop is the Buyer ceasing to pay. It is the Buyer’s only exit other than completing, and it is the exit a Holder prices.

The Stop Date

A payment up to 15 days late carries nothing: no fee, no penalty, no consequence. Day 16 after the missed due date is the Stop Date (Marc, 2026-08-22: “stop is 15 days later and we stop on day 16 as always”; atom R-1035). A returned payment counts as no payment. The Buyer may also elect to stop in the app on any day, and that day is the Stop Date.

On the Stop Date the Agreement ends. The Buyer owes nothing further and can pay nothing further.

The engine works at monthly resolution and does not model the 15 days as a separate event. It records the missed payment date \(D\) and treats day 16 after it as the Stop Date. \(D\) is \(t+1\) for a stop drawn at payment age \(t\) (payments run first, so payment \(t\) was made) and \(t\) for a conviction walk or a rational-boundary walk, where the walk is the missed payment. The Agreement’s exit month is \(D\).

The sale for dollars

Within two business days of the Stop Date the coin is sold for dollars at market through the normal venue, in a recorded sale, costs on the Company, with the record kept (Marc, 2026-08-22, approving the drafted mechanics; atom R-1037). The sale exists to fix one number, the proceeds \(V\), against which the refund is computed. It is a recorded sale rather than an index read because Marc chose it: “official sale, to record the proceeds” (2026-08-22).

The engine sells the coin stop_sale_lag_days after the missed date (input #24, default 18 calendar days: day 16 plus two business days). The sale price is the path’s price at that point, interpolated log-linearly between the monthly marks on either side. The proceeds are

\[ V = S(D + 18/30.4375) \cdot e^{-h} \cdot (1 - c) \]

where \(h\) is the static haircut (input #15, default 0, Marc 2026-07-12: a one-coin sale has no market impact) and \(c\) is the market-sale cost (input #16, default 25 basis points, Marc 2026-07-11). All of this is engine.rs::stop_sale. The cash posts at the first monthly date at or after the sale, ceil(D + stop_sale_lag_days/30.4375). At the default 18-day lag this is the month after the missed date; at zero lag it is the missed month.

One difference to note. The ruling puts the sale’s costs on the Company; the engine takes the 25 basis points out of the proceeds, which lowers \(V\) by $150 on a $60,000 coin. The waterfall determines how that reduction is allocated between the refund and the delivery; each does not necessarily fall by $150. The sale standard itself, the venue, the deadline and the price against the index, is not yet written into the Agreement, and the model assumes a market sale at the path price less 25 basis points.

The waterfall

Let \(A\) be every payment the Buyer made, payment 1 included (engine.rs::Agreement::paid_in), \(P\) the Purchase Price and \(R = P - A\) the remaining schedule. The proceeds pay out by the R-1033 formula (Marc, 2026-08-22, reporting legal’s approval):

\[ \text{refund} = \min\big(A,\ \max(0,\ V + A - P)\big) \]

\[ \text{delivered} = V - \text{refund} \]

The refund goes to the Buyer in dollars. The delivery goes to the Holder, less the 5% fee. Read as a waterfall, the proceeds pay the Holder the remaining schedule first, then refund the Buyer up to what he paid in, and any surplus above the Purchase Price stays with the Holder (Marc, 2026-09-03: after the refund “the rest goes to” the Holder; the ruling of 2026-08-22 had left the upside with the Company, and 2026-09-03 moved it to the Holder). Three regimes follow from the formula:

RegimeConditionRefundHolder receives (gross)Shortfall against the schedule
1\(V \le R\)0\(V\)\(R - V\), the shortfall
2\(R < V \le P\)\(V - R\)exactly \(R\)0
3\(V > P\)\(A\), everything paid\(R + (V - P)\)0; the surplus \(V - P\) is the Holder’s

The Buyer never gets back more than he paid. The Holder never receives less than the proceeds allow and never more than the remaining schedule plus the surplus above the Purchase Price. Nobody owes anybody anything after a stop. engine.rs::stop_sale records shortfall_usd = max(0, R − V), stop_surplus_usd = max(0, V − P), buyer_refund_usd and stop_proceeds_usd on the Agreement, and the invariant suite checks the three regimes against these identities on every stop of a rising path.

Whether payment 1, the one BTC Now kept, sits in the Buyer’s refund base is a term-sheet item that is flagged, not decided. The engine models it as included, reading Marc’s “the maximum he paid” (2026-09-03).

Dollars, never coin

A stopped Buyer receives dollars, never coin. The refund is paid from the proceeds within ten business days with a statement (Marc, 2026-08-22). The Holder’s delivery is also in dollars; there is no in-kind residual term. A Holder who wants the coin buys it with the proceeds on its own account (Marc, 2026-09-03: “they get in dollars but of course they can just back to back it into coin anyways”). The engine posts TxKind::StopSaleDelivery from the market to the Holder and TxKind::StopRefund from the market to the Buyer; a zero refund posts nothing. Any view that reinvests stop proceeds into coin models the Holder’s own behavior, not a term of the Agreement.

This replaced two earlier designs. The July engine sized the sale to the remaining schedule and returned the residual coin to the Buyer (spec v1.2, Marc 2026-07-11). The 2026-08-21 election of dollars or coin at the agreed price was dropped the next day because a payoff available only by quitting reads like a bet on the price. Under the September program the only way to profit from Bitcoin through the Agreement is to complete it.

Early completion

The Buyer may pay the remaining schedule \(R_t\) in cash at any time and take the coin. This is early completion. It is cash only: the coin is never sold to pay for it (spec v1.5, change 3). The Holder receives \(R_t\) less the 5% fee and the Agreement closes.

engine.rs::settle posts the payoff from the Buyer to the Holder as TxKind::MakeWholeDelivery (the identifier is historical) and the fee to BTC Now. It records the Buyer’s coin equity at exit, the spot value of the coin less the payoff, as coin_returned_usd. That figure is the Buyer’s property, not a cash flow, and it appears in the outputs only as a transparency counter.

Relative to full scheduled completion at the base terms, early completion delivers the same total net scheduled cash sooner. It also closes the Agreement, removing any later stop exposure and possible surplus. Its effect on portfolio cash and IRR therefore depends on the alternative path and exit behavior. Run the early-completion propensity (input #12) at zero as a separate sensitivity. In the engine the decision is a monthly draw with probability proportional to how far the coin is above the remaining schedule, \(u_t = \text{propensity} \cdot \max(0, (S_t - R_t)/S_t)\), optionally gated by a take-profit threshold on the Buyer’s all-in cost (input #22). Those are model choices, not terms; see the behavior chapter.

Completion

When the sixtieth payment is made, the Agreement completes and the coin is delivered to the Buyer. The engine marks the Agreement Completed at the payment date that brings payments_made to the term and posts nothing further. Delivery of the coin is outside the ledger: the Holder never held it and the engine never values it. No new hazard draw occurs after payment 60 completes the Agreement. A hazard draw after payment 59 can miss payment 60 and follows the same stop waterfall and sale timing as an earlier stop. A lost-conviction walk armed at payment 59, or a rational-boundary decision at payment 60 itself, can still consume payment 60 unpaid (engine.rs::run, steps 1 and 2a); those are deterministic rules, not draws.

The two lockouts

Two lockouts were reinstated on 2026-09-03 (Marc), after the ruling of 2026-08-10 that struck the July lockout ladder.

  1. A person who has not yet signed gets a 60-minute window to accept a price. Letting two such windows lapse locks the person out of the program for seven days. This stops the free look at the price from being farmed.
  2. A Buyer who stops, for any reason, is locked out for six months. This makes re-entering at a lower strike a six-month bet with the deposits at stake.

Neither is modelled. The engine has no notion of a person across Agreements and no re-entry; each Agreement is one Buyer, once. The July rescission ladder, escalating lockouts and Application Fee are not part of the modelled program.

No qualification and no sizing

There is no qualification of Buyers and no sizing of an Agreement to the Buyer’s cash flow (Marc, 2026-08-22: “we will NOT check eligibility, we do NOT size”; atoms R-1034 and R-1036). Four checks run before a Sale or a Transfer: identity verification, sanctions screening, a bankruptcy screen on the consumer report, and the one-Bitcoin active cap, under which a Buyer’s active coin across Agreements in force may not exceed one Bitcoin. The cap is the only sizing rule. A credit-score floor was considered on 2026-08-21 and foreclosed the next day.

The engine models none of these. It has no Buyer attributes: no score, no income, no identity, no cap. Every Agreement is one coin, which is why the engine’s book of one-coin Agreements is also a book that respects the cap. The consequence for the Holder is that the stop hazard is a prior on an unscreened population, and the behavior chapter says so.

What the engine does not model

The engine models money. It leaves out the parts of the program that move the coin or move the parties.

  • The Dedicated Wallet and custody. Each Agreement’s coin sits in a per-Agreement wallet under a trust structure until completion, and on a stop the trust releases it to the Company for sale. The engine has no wallet; it prices the coin on the path and sells it at a number.
  • The Transfer of the paying side. A Buyer may hand the Agreement to another Buyer (Transferor to Transferee) for a $250 transfer fee. The engine has one Buyer per Agreement for its whole life.
  • The Sale of the receiving side after the Primary Sale. Secondary Sales between Holders, and the 1% facilitation fee on the value moved (Marc, 2026-08-31), are outside the engine. It models one Holder that buys at origination and holds to the end.
  • Referral cuts. A Partner’s cut of the first payment (X1, ruled 50% on 2026-08-31) and of the servicing fee (X2, ruled 0 for now) are splits inside BTC Now’s take. The engine reports BTC Now’s take whole.
  • The stop-sale mechanics. Venue, deadline, the record, the refund’s ten-day clock and the refund rail (dollars to the account paid from) are procedure. The engine posts the refund and the delivery at one date.
  • The lockouts, above.
  • Payment rails and timing inside a month. ACH, the 15 days of grace, returned payments and the reminder ladder collapse into one monthly mark.

One Agreement, three endings

Everything below is at the base terms and was run through the engine at POST /api/forwardflow/simulate with one cohort of one Agreement, zero hazard, and a constructed path (PathMode::Custom). The early completion was forced with the propensity (input #12) at 1.0 and the take-profit gate (input #22) at 0, and the stop with the lost-conviction rule (input #11) at X = 0%, Y = 1, so that each exit lands on the stated month; the dollar figures do not depend on how the exit was forced. A $60,000 coin, 1.475×, 60 payments of $1,475, Purchase Price $88,500, payment 1 to BTC Now, 5% on every delivered dollar, the Holder buys at par ($60,000), the stop sale 18 days after the missed payment, 25 basis points of sale cost, no haircut. The rates of return quoted are irr_nominal_pa from outputs.rs::analyze for that single Agreement on that path; they are not the Model Card’s book figures.

Completion

The Buyer pays $1,475 a month for 60 months and takes the coin.

LineDollars
Buyer pays in total88,500.00
Payment 1 to BTC Now1,475.00
Payments 2 to 60 delivered (59 × 1,475)87,025.00
Servicing fee to BTC Now (5%)4,351.25
Holder receives net (59 × 1,401.25)82,673.75
Holder paid for the paper60,000.00
Holder’s gain22,673.75
BTC Now’s total take5,826.25

The Holder’s undiscounted multiple is 1.378×. The engine prints a nominal rate of 13.1% per year (14.0% effective), a weighted average life of 31 months and payback in month 44. That is the paper’s rate with nobody stopping and nobody completing early.

Early completion at month 24

The Buyer makes 24 payments, then, with the coin at $90,000, pays the remaining schedule in cash and takes the coin.

LineDollars
Paid in over 24 payments, \(A\)35,400.00
Remaining schedule, \(R_{24}\)53,100.00
Buyer pays in cash53,100.00
Servicing fee on the payoff2,655.00
Holder receives from the payoff50,445.00
Holder’s net from payments 2 to 24 (23 × 1,401.25)32,228.75
Holder receives in total82,673.75
BTC Now’s total take (1,475 + 23 × 73.75 + 2,655)5,826.25
Buyer’s coin equity at exit (90,000 − 53,100), property36,900.00

The Holder’s total is the same $82,673.75 as at completion and its multiple is the same 1.378×; only the timing differs, and the engine prints 20.1% nominal (22.0% effective) because the money came back in two years instead of five. The Buyer’s $36,900 never enters the ledger.

A stop after twelve payments, at three prices

The Buyer makes 12 payments and misses the thirteenth. \(A = 17{,}700\), \(R = 70{,}800\), \(P = 88{,}500\). Day 16 is the Stop Date. The coin is sold 18 days after the missed date. Three prices at the sale show the three regimes. The Holder has already received 11 net payments, $15,413.75, and BTC Now has $1,475 plus 11 fees of $73.75.

LineCoin at $40,000Coin at $75,000Coin at $100,000
Proceeds \(V\) (less 25 bp)39,900.0074,812.5099,750.00
\(V + A - P\)−30,900.004,012.5028,950.00
Refund to the Buyer0.004,012.5017,700.00 (capped at \(A\))
Delivered to the Holder, gross39,900.0070,800.0082,050.00
Servicing fee on the delivery1,995.003,540.004,102.50
Holder receives net from the sale37,905.0067,260.0077,947.50
Shortfall \(R - V\)30,900.000.000.00
Surplus \(V - P\), the Holder’s0.000.0011,250.00
Holder receives in total53,318.7582,673.7593,361.25
Holder’s multiple on $60,0000.889×1.378×1.556×
BTC Now’s total take4,281.255,826.256,388.75

Three things to read off the table.

At $40,000 the sale does not cover the remaining schedule. The Buyer gets nothing back, the Holder takes the whole $39,900 and still loses $6,681.25 on its $60,000. This is the loss the paper carries, and it is a loss on any stop where the proceeds fall below the capital line described in the risk-desk chapter.

At $75,000 the proceeds clear the remaining schedule by $4,012.50, and exactly that amount goes back to the Buyer. The Holder receives precisely the remaining schedule, $70,800 gross, and its total is the same $82,673.75 it would have received at completion. In regime 2 a stop is a completion that arrived early.

At $100,000 the proceeds exceed the Purchase Price. The Buyer gets back everything he paid, $17,700, and not a dollar more; the Holder receives the remaining schedule plus the $11,250 surplus. This is the ruling of 2026-09-03 in one row: the upside on a stop belongs to the Holder. On Bitcoin’s own history it is the row that moves the replay figures, and the Model Card says to read them with the in-the-money stop rate attached.

In every column BTC Now’s take is its fees only: the first payment, 5% of the delivered payments and 5% of the delivered proceeds. In every column the sale cash posts at month 14, the Agreement’s exit month is 13, and the Buyer receives dollars.

Where each rule comes from

RuleDecider and dateApplied in
Term is a model parameter; first \(N\) payments to BTC NowMarc, 2026-07-10spec v1.1
Sale cost as an input (25 bp default)Marc, 2026-07-11spec v1.2
No static haircut; purchase price defaults to parMarc, 2026-07-12spec v1.4
Term is a property of the offer, not the AgreementMarc, 2026-07-29program
No cooling-off, no rescission; the stop is the only exitMarc, 2026-08-02program
July lockout ladder struck; no Application FeeMarc, 2026-08-10program
Stop, sale for dollars, refund by formula (R-1033); no qualification, no sizing (R-1034); one-Bitcoin active cap (R-1036)Marc, 2026-08-22, reporting legal’s approvalspec v1.5
Stop Date: 15 days late carries nothing, day 16 (R-1035); a returned payment is no paymentMarc, 2026-08-22spec v1.5
Sale within two business days, costs on the Company, record kept; refund within ten business days (R-1037)Marc, 2026-08-22spec v1.5 (18-day lag)
BTC Now is always Holder 0; the first payment flows to itMarc, 2026-08-31spec v1.5
5% of every dollar sent to a Holder, deducted at the send, termination sends includedMarc, 2026-08-31spec v1.5
Referral cuts X1 = 50%, X2 = 0; 1% facilitation on Secondary SalesMarc, 2026-08-31not modelled
Vocabulary: Buyer, Holder, Transfer, Sale, PartnerMarc, 2026-09-02spec v1.5
Surplus above the Purchase Price stays with the Holder; 5% on the stop deliveryMarc, 2026-09-03spec v1.5
Stop delivery in dollars; a Holder may buy coin with itMarc, 2026-09-03spec v1.5
Seven-day lockout after two lapsed windows; six-month lockout after a stopMarc, 2026-09-03not modelled
Early completion is cash onlyprogram rule, applied 2026-09-03spec v1.5
Behavior Engine parked on a branch; rational boundary lifted into the engineMarc, 2026-09-03spec v1.5, v1.6

The full sequence of versions, with what each one changed in the numbers, is in Versions and rulings. The formulas above are derived and tested in Contract math and the fee and The stop waterfall.

Reading BTC performance

BTC cash-flow IRR tells you the annual return implied by the investment’s dated BTC-equivalent flows. The amounts tell you how much went in, how much came back and how much remains a model value. Use both.

Start with the amounts

MeasureWhat to read
Contractual BTCActual originated Agreements × one BTC. A stopped origination plan can contain fewer Agreements than originally planned.
Gross BTC deployed into AgreementsPurchase dollars divided by each Agreement’s own entry price: one BTC per Agreement at par, including when entry prices differ within the month. A premium or discount changes the funded amount. Recycled capital can appear in more than one purchase.
Gross Holder receiptsModeled receipts after their contractual deductions. Do not subtract the servicing fee again.
Net cash contributed / recoveredNegative / positive monthly net cash, including the selected hedge’s modeled costs and settlements. These differ from gross activity.
Signed horizon derivative valueModel value of positions still open. Positive value is an asset; negative value is a liability. Neither is already settled cash.
Economic contribution / recoveryThe same monthly split with the signed horizon value included once at the end. A liability can create a hypothetical terminal contribution.
BTC surplusRecovery minus contribution on the named basis. It is an amount in BTC equivalents, not a return rate or wallet balance.

The cash and economic views reconcile:

economic recovery − economic contribution = cash recovery − cash contribution + signed horizon value.

For 24 cohorts of ten par Agreements, contractual BTC and gross BTC deployment are both 240 BTC. Earlier receipts can offset later purchases, so net additional contributions may be below 240 BTC. The cohort table shows unhedged purchase funding, fee-net receipts, gain and dated IRR. Cohort gross amounts and gains add to the book; cohort IRRs and monthly-net contribution ratios do not. Shared hedge costs are reported at book level unless an allocation rule is explicitly supplied.

Engine 0.6.0 introduced this entry-funding convention; 0.6.1 retains it while correcting monthly Coin-delta sizing. Earlier versions translated purchase dollars at the monthly reporting price; entry dispersion could therefore show 241.9214 BTC equivalents for the default 240-Agreement case. Old saved results keep that earlier definition and need a fresh run before comparison with current results.

Show both contribution and recovery sides. A horizon mark can flip a month’s sign, so adding it to recovery alone does not always reconcile. Sum cash flows over time; use the final observation for a stock such as an open position’s value.

A total return is not always an annual rate

With one contribution of 100 BTC and one recovery of 110 BTC, the recovery ratio is 1.10× and total return is 10%. If the flows are exactly one year apart, annualized IRR is also 10%. Five years apart gives about 1.92% a year. Other dated contributions and receipts change the rate again. Costs must already be included in the flow amounts being compared.

BTC cash-flow IRR remains useful when Agreements pay over time: it accounts for the dates and amounts. It uses the original modeled BTC-equivalent flows and reports unavailable or multiple-root diagnostics where needed. At constant spot, equivalent USD and BTC flow vectors have the same IRR within numerical tolerance. Changing Bitcoin prices can make their returns very different.

The net recovery ratio is recovery divided by contribution, with economic and realized-cash versions named separately. Zero contribution or a non-finite ratio makes it unavailable. The legacy net outcome per BTC of Agreement purchases uses gross purchase BTC equivalents as its denominator and signed hedge flows in its numerator. That is a different ratio. For a flat-price example with a purchase of 1, receipt of 1.5 and hedge cost of 0.2, the legacy Coin ratio is 1.30× while the USD combined multiple is 1.25×. The difference is convention, not currency performance.

What holding BTC means here

Current Research compares each strategy with retaining its same dated net BTC-equivalent contributions. Show that contribution amount beside the surplus. Each strategy can need a different amount; a larger absolute surplus alone does not establish a better use of the same starting capital.

Legacy shelf alternatives use a contractual one-coin-per-Agreement reference and their own cost, timing and collateral assumptions. Sharing market paths does not make them equal-budget investments. A sampled hedge result does not guarantee a floor, even if no path in that sample loses BTC.

A complete wallet comparison would start each strategy with the same BTC budget and track idle BTC, USD cash, conversion costs, reserves, collateral, liabilities and additional transfers. It would also say what happens when the wallet cannot meet a call. That model is not implemented. Do not interpret BTC cash-flow IRR as complete-wallet CAGR.

Reporting currency, cash and value

ForwardFlow funds Agreement purchases at their actual entry prices. It converts subsequent USD receipts, hedge cash and remaining values at the stated monthly USD/BTC price. These are explicit timing assumptions; the model does not execute a currency trade or prove an actual venue settlement. Instruments can use different quote units, settlement assets and collateral rules. Deribit’s inverse option specification, for example, describes actual BTC premiums and settlement; it does not certify that this model implements that venue’s complete rules.

Purchase-yield Agreement marks explain monthly P&L under a cost-calibrated flat-continuation convention. They are not certified reporting fair values. IFRS 13’s fair-value concept concerns a current market-participant exit value, which cost calibration alone cannot establish.

CFA Institute’s GIPS handbook distinguishes money-weighted returns sensitive to external-flow timing from time-weighted returns that neutralize those flows. A complete account return must define the account boundary and include its assets and cash. This is useful measurement context; ForwardFlow does not claim GIPS compliance.

See the workspace guide, hedging formulas, MCP capabilities and release model card.

Glossary

Find the terms used in the Agreement, the workspace and the API. Each entry includes a short definition, further explanation and a reference to the relevant method. Use the alphabet or search the guide.

A

Abuse caps

Also: caps, heavy permit, HEAVY, semaphore, 400.

Bounds on the work a request can ask for, each answering 400 with the input named: 1,200 cohorts, 50,000 Agreements, 480 months, 100,000 runs, 24 million Agreement-runs, 900 heatmap cells. Three heavy requests run at once; the rest queue.

Hosting hardening of 2026-07-13 (spec v1.4 change 13). The engine’s validate panics with a message naming the input and catch_engine unwinds it into the 400, which is why the build must never set panic = "abort".

Where it is derived

Agreement

Also: Agreements, BPA, Bitcoin Purchase Agreement.

A Bitcoin Purchase Agreement: one coin bought at a price fixed on the day it opens, paid for on a fixed monthly schedule. The Buyer pays, the Holder receives; the coin passes at completion.

An Agreement is a conditional sale of one Bitcoin. The Buyer pays the Purchase Price, entry strike times the multiple, on the contractual monthly schedule; title passes at completion. The Agreement can complete, complete early or stop. A cohort is an origination group of these one-BTC Agreements. The Holder’s purchase price is a separate amount: from engine 0.6.0, the Coin model funds it at that Agreement’s own entry strike, so a par purchase uses exactly one BTC even with dispersed entry prices; a non-par purchase uses purchase price divided by entry strike. The engine does not track Buyer identity across Agreements.

Where it is derived

Amortized obligation

Also: remaining obligation, B_t, remaining amortized obligation, obligation.

The Buyer’s remaining obligation in present-value terms: the amortization balance at the implied financing rate. $51,580.88 after payment 12 at the base terms, against a nominal $70,800. The rational modes measure underwater against it.

contract.rs::ContractTerms::remaining_obligation; \(B_0 = \text{strike}\), \(B_n = 0\). A Buyer compares the coin to what the remaining payments are worth, not their undiscounted sum; against \(R_t\) every Buyer on a flat path would be “underwater” for nineteen months and the rational mode would empty the book. So: nominal for the waterfall and the ledger, amortized for the boundary.

[ B_t = \text{strike},(1+i)^t - \text{PMT},\frac{(1+i)^t - 1}{i} ]

Where it is derived

API key

Also: X-API-Key, key name, desk key.

A desk’s credential in keyed mode: the header X-API-Key: <key> on every /api/forwardflow/* call. Issued by BTC Now, one per counterparty, compared in constant time; only its name ever reaches a log or the X-Key-Name header.

Configured on the engine as FF_API_KEYS (name:key,…, every key at least 24 characters). Never shared between institutions, never placed in a browser page; rotated or revoked by asking BTC Now. The cockpit holds its own key server-side.

Where it is derived

As-of month

Also: as_of_month, as of, as_of_spot.

The month at which the book is valued for the Greeks: the ladder month, 23 by default on the base path. Bumps start the month after; the existing book is frozen by stopping origination there.

from = as_of + 1; apply_bump leaves earlier months untouched. The as-of spot ($33,375.25 on the base path at month 23) is what delta in coins divides by.

Where it is derived

ATM vol

Also: at-the-money vol, surface ATM, ATM 12m, ATM 24m, atm_vol_used.

The surface’s vol at moneyness 1.00 (strike equal to spot) for a tenor. The placement exhibit shows the 12-month ATM beside the paper’s implied vol; the fair value’s paths run at the 24-month ATM.

VolSurface::atm(tenor_months). The stylised preset’s ATM is 41.4% from 1 to 12 months rising to 48% at 24; the flat preset is 43% everywhere. The fair value uses one vol because its paths are plain GBM — the skew enters the placement exhibit and the hedge legs, not those paths.

Where it is derived

B

backtest

Also: POST /api/forwardflow/backtest, ff_backtest.

One engine run per seasoned historical vintage: the configuration with the path, cohorts and origination window overridden per vintage, everything else held. Returns the vintage rows, the blended figures and a count of failed vintages.

Heavy route. Replay is rebased so month 0 equals the start price; agreements_per_cohort sets the units per vintage. 113 vintages and no holes at the base cockpit configuration.

Where it is derived

Base configuration

Also: BASE_CONFIG, base cockpit configuration, program base case, Reset to program base case, ≈ memo config.

What the cockpit loads first: a 43% bridge from $60,000 to $60,000, 40% lifetime stop prior, multipliers off, 2.5% early completion, 24 cohorts of 10 with dispersed strikes, par, 5%, 18-day lag, seed 42. Not the Model Card’s construction.

Defined by lib.ts::BASE_CONFIG. It sets intramonth strike dispersion on and a fixed 84-month market horizon; the engine default has dispersion off and derives its horizon. A saved draft can replace the initial workspace configuration. Read the full submitted configuration and completed run metadata rather than treating a historical base-case figure or short hash as current.

Where it is derived

Baseline hump

Also: program hump, Program hump, the hump, BaselineCurve, timing shape, actuarial curve.

The program’s hazard shape as a step function of term fraction: ×1.0 for the first 5%, ×2.0 up to 25% (the hump), ×1.2 to 40%, ×0.7 to 60%, ×0.25 after. At 60 months the hump is payments 4 to 15.

defaults.rs::shape_weights, normalized to term fraction so the shape travels with the term (spec v1.1). At 60 months it reproduces the program’s actuarial buckets exactly. The unconditional mass by year at the 40% prior: 18.7% in year one, 12.4%, 5.5%, 1.8%, 1.6%. The cockpit’s timing chips are the hump, flat, and late-loaded.

Where it is derived

Basis lock

Also: locked basis, basis schedule.

A modeled annual basis rate for an initial number of months, followed by the configured rate after that period. It describes the price of futures exposure under the selected schedule.

BasisSchedule specifies locked_months, locked_rate and after_rate. Availability, execution and future roll rates are assumptions, not verified listed-market quotes. Long and short positions apply the stated sign convention; the duration and cost must stay with the result.

Where it is derived

Basis rate

Also: basis, basis_rate, cash-and-carry rate, hurdle.

The crypto risk-free rate to a market-neutral desk (default 6%/yr): what cash-and-carry earns. The hurdle the hedged median IRR is read against; hedged excess over basis is the median minus this.

Request field basis_rate on the hedge endpoint, annual effective, ±100%. It does not enter any hedge flow — it is a reference line on the percentile strips and the subtraction behind hedged_excess_over_basis_pp. A desk that can earn the basis with no directional risk needs the hedged paper to beat it.

Where it is derived

Basis received

Also: basis paid, a short receives the basis, basis leg.

Dated-futures basis, annualised, charged monthly on the hedge notional as h·S_m·b/12 at m + 1: a long pays it, a short receives it. The coin book’s long pays 4% then 10%; the dollar book’s short is paid 4% a year.

BasisSchedule { locked_months, locked_rate, after_rate } sets b: the locked rate for the first months of each Agreement’s life (a dated contract held to expiry), the after rate on the rolls. The engine posts the basis on its own leg beside the mark-to-market, so a desk sees where the money went.

Where it is derived

Behavior Engine

Also: parked Behavior Engine, bbe/v0, BBE.

The thirty-seven-parameter behavior model parked on its own branch on 2026-09-03 (Marc). Its rational boundary was lifted into the main engine as input #25; its four-channel decomposition goes to the memo as prose.

It stays the validation instrument for the day live vintages exist and an actual-versus-model report can be run.

Where it is derived

Believed drift

Also: mu_annual, μ, Boundary believed drift %/yr, Believed drift, belief.

The Buyer’s own expected annual appreciation of the coin, the one lattice parameter that is a belief. Default 25%; zero is the pessimist who should never have signed and walks at or above par from the first dates.

Accepted within ±200% a year, and \(|\mu|\sqrt{\Delta}\) must not exceed sigma. The risk endpoint reports the frontier for a family of drifts, by default 0, 10%, 25% and 50%. At payment 1 the frontier reads 1.162, 1.011, 0.521 and 0.152 of entry respectively.

Where it is derived

Below the Agreement-purchase reference

Also: share below one coin, pct_negative (coin).

The all-seed share whose legacy net outcome per BTC of Agreement purchases is below 1×. This is a BTC-outcome event, independent of whether a path has an IRR.

Legacy Coin Dist.pct_negative uses this purchase-normalized loss event; USD pct_negative instead concerns negative valid IRRs. The Coin Research matched-contribution comparison names its own capital basis. Neither label means a custody account literally contains fewer than one BTC. Zero observed losses in a finite sample is not zero loss probability.

Where it is derived

Below the line

Also: below_schedule, below_capital, below the schedule line, below the capital line.

An active Agreement whose stop sale at this month’s mark would not cover the line: below the schedule line a stop leaves a shortfall; below the capital line it loses the Holder’s capital.

On the base path the count below the capital line peaks at 127 in month 22 and is zero from month 30; the count below the schedule line is still 62 at month 30 and first reaches zero at month 40. The loss story lives in the first two years of each vintage.

Where it is derived

Benchmarks

Also: paper vs spot vs covered call, Benchmarks, include_benchmarks, the directional desk’s comparison.

The directional desk’s comparison on the same seeds: the paper as the config runs it, spot (buy the same coins at the same months, sell at the horizon), and spot with a covered-call overwrite. Three distributions, one set of paths.

hedge::benchmarks(config, surface, seeds), returned when the hedge request sets include_benchmarks. The paper row equals the unhedged distribution. The headline is the paper’s median IRR over spot’s, in points: whether the paper beats owning the coin outright on the config’s own price model, and whether the paper’s short put is paid better than an overwrite pays.

Where it is derived

Black-Scholes

Also: BS put, BS call, bs_put, bs_call, European put.

The option formula the desk uses as written: a European put on one coin is K·e^{−rT}·N(−d₂) − S·N(−d₁), the call S·N(d₁) − K·e^{−rT}·N(d₂), with d₁ = (ln(S/K) + (r + σ²/2)T)/(σ√T) and d₂ = d₁ − σ√T.

surface::bs_put, bs_call, bs_delta_put (N(d₁) − 1) and bs_vega (S·√T·φ(d₁), dollars per vol point). The normal CDF is written out (Hart 1968 via West 2005) so the numbers a desk sees never depend on a platform’s erf. Zero tenor or zero vol degenerates to discounted intrinsic. The placement prices at the funding rate; the hedge overlay and the benchmarks price at zero rate, the surface’s own quoting convention.

[ P = K e^{-rT} N(-d_2) - S, N(-d_1), \qquad d_1 = \frac{\ln(S/K) + (r + \sigma^2/2),T}{\sigma\sqrt{T}}, \quad d_2 = d_1 - \sigma\sqrt{T} ]

Where it is derived

Blended

Also: blended MOIC, blended_moic, blended row, buy-every-month.

The realized flow of buying every vintage at equal size: blended MOIC = (Σ deployed + Σ net gain) ÷ Σ deployed. 3.081× at the base cockpit configuration, $138.2m of net gain on $66.4m deployed.

Dominated by the early vintages, which rode the whole rally from single-digit dollars. A one-asset history has survivorship in it; the rows are the information.

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

Where it is derived

Block length

Also: block_len, Block length (months), blocks.

How many consecutive de-meaned monthly returns the bootstrap glues together at a time: 6 by default, so half a year of the actual sequence, its drawdown streaks and recoveries, survives inside each block.

Each block start is drawn uniformly from the regime’s returns; the last block is truncated at the horizon. The regime must supply at least block_len + 1 bars or the run fails with RegimeTooShort.

Where it is derived

Break-even frontier

Also: IRR = 0 frontier, the cliff, Where’s the cliff, bold line = break-even.

The IRR = 0 contour on a heatmap, drawn by the cockpit as a bold segment on every edge between adjacent cells of opposite sign. At the base configuration only the volatility × conviction grid crosses it.

charts.tsx::Heatmap; holes take part in no edge. It is the break-even line an investment committee argues about.

Where it is derived

Brownian bridge

Also: bridge, Bridge (pinned), pinned bridge, end price, End price.

An endpoint-pinned price path: volatility changes the route between the chosen start and end prices. It answers a conditional question about a specified terminal price.

paths.rs::bridge. Zero volatility gives a deterministic log-linear ramp; equal endpoints then give a flat path. Bridge Monte Carlo samples intermediate prices, not terminal-price uncertainty. Legacy API defaults use a bridge; new Dollar and Coin workspaces start with illustrative GBM assumptions. A saved scenario retains its own path mode.

[ x_{k+1} = x_k + \frac{T - x_k}{n} + \sigma_m \sqrt{\tfrac{n-1}{n}}; Z_k, \qquad \sigma_m = \sigma/\sqrt{12} ]

Where it is derived

BTC cash-flow IRR

Also: IRR in coin, Coin IRR, BTC cash-flow IRR.

Annualized IRR of the Holder’s dated monthly BTC-equivalent investment flows after included costs. Signed horizon derivative value is included once where applicable. Useful for comparing cash-flow timing; not annual growth of an entire BTC wallet.

coin.rs::coin_seat_metrics solves the original BTC flows through outputs.rs::irr_analysis_f64, preserving unavailable reasons and multiple-root diagnostics. Purchases use their Agreement entry price; receipts and hedge cash use monthly spot, with signed horizon value included once where applicable. Equal USD and BTC vectors up to constant scale have equal IRRs, but constant monthly spot alone does not ensure this if entry prices differ. One contribution of 100 BTC and recovery of 110 BTC gives 10% total return; annualized IRR is 10% only when those are the only flows and one year apart. Unused reserves and total wallet growth are outside this measure.

Where it is derived

BTC net asset value

All modeled assets less liabilities expressed in BTC at the valuation date, including cash and open positions once. A complete funded-wallet NAV is not currently simulated; cumulative BTC-equivalent cash surplus is a different measure.

See the workspace measurement contract for cash, economic value, denominator and funding boundaries.

Where it is derived

BTC Now

Also: the Company, originator, servicer, BtcNow.

Originator and servicer of every Agreement, and always Holder 0. It takes the first payment whole and 5% of every dollar delivered to a Holder, never a share of a stop sale. Ledger entity BtcNow.

BTC Now pays nothing into the engine; it receives the purchase price of the paper at the Sale, the first N payments and the flow fees. The invariant suite asserts its total take equals origination fees plus flow fees. The engine assumes BTC Now is always there to service; servicer failure is a Phase 4 dial.

Where it is derived

BTC Now take

Also: total_take, Program take, BTC Now’s take, BTC Now take, btcnow, take.

Origination fees plus flow fees, and nothing else: $5,826.25 on a completed base Agreement ($1,475 + $4,351.25), $892,043.38 on the historical early-September reference book. No dial gives BTC Now a share of a stop sale.

outputs.rs::BtcNowTake, built from ledger totals by posting kind. The purchase prices Holders paid are acquisition proceeds, reported separately, and the paper spread is reported separately too so the M0 identities keep holding. A Partner’s cuts are splits inside this figure.

Where it is derived

BTC Now’s own posture

Also: 70% floor, 75% density, BTC Now posture, the ruling of 2026-08-16.

The preset encoding the ruling of 2026-08-16: puts at 70% of entry, 24-month tenor, 0.75 coins per Agreement, put on at each origination. BTC Now’s own posture as Holder 0, labelled as such — one preset among several, not a recommendation.

HedgeSpec::PutLadder { strike_pct_of_entry: 0.70, tenor_months: 24, coverage: 0.75, inception: AtOrigination }. The 70% floor is the strike; the 75% density is the coverage. It is on the shelf so a Holder can see what BTC Now does with its own book and compare, not so it is adopted.

Where it is derived

BTC recovery ratio

Net BTC recovery divided by net BTC contribution on the same cash or economic basis. A dimensionless multiple, reported as ×; ratio minus one is total return. A zero denominator is unavailable. Timing is measured separately by BTC cash-flow IRR.

See the workspace measurement contract for cash, economic value, denominator and funding boundaries.

Where it is derived

Bump

Also: bump overlay, PathBump, price bump, vol bump, what-if bump, Bumps.

The second overlay (v1.6): from a month on, every price times a factor and the log returns’ deviations from their mean times a vol factor, drift kept. It exists for the Greeks and can be set directly as a what-if.

paths.rs::apply_bump; month 0 is never bumped. hold_strikes true keeps the existing book’s strikes on the unbumped path. The risk endpoint refuses a configuration that already carries a bump, because the Greeks own it. Price factor accepted in (0.1, 10), vol factor in [0, 5).

[ r’m = \bar r + v,(r_m - \bar r), \qquad P’m = P{a-1} \exp\Big(\sum{j=a}^{m} r’_j\Big) ]

Where it is derived

Bump-and-revalue

Also: finite difference, revalue.

The method behind the Greeks: apply a bump to every future price (or to realized volatility), re-run the same seeded book, and take the difference in net gain. Valid only because every Agreement draws from its own stream.

A finite difference is a sensitivity only if the two runs differ by the bump alone. greeks_are_sensitivities_not_draw_noise bumps a 240-Agreement book by one basis point and requires at most two exits to flip. The risk endpoint refuses a configuration that already carries a what-if bump.

Where it is derived

Buyer

Also: Buyers, paying side, Obligor, obligor.

The paying side of an Agreement: the person paying the schedule for one coin. Buyer 0 opened it; the paying side changes hands by Transfer. The engine’s ledger calls the Buyer Obligor.

Locked by Marc on 2026-09-02. The Buyer pays the schedule, receives a dollar refund on a stop, and takes the coin at completion or early completion. The Buyer never owes anything after a stop and pays no late, returned-payment or termination fees. The engine identifier Obligor(id) predates the lock and was kept so stored results keep deserializing; the exit tag non_performance likewise means a stop.

Where it is derived

Buyer 0

Also: Buyer zero, original Buyer.

The Buyer who opened the Agreement. Buyers are numbered from 0; a Transfer hands the paying side to Buyer 1, then Buyer 2, and so on. The engine has one Buyer per Agreement for its whole life.

The chain is numbered rather than named: “Original Buyer” is not a word (Marc, 2026-09-02). The engine models no Transfer, so every Agreement is Buyer 0 from origination to exit.

Where it is derived

Buyer discount rate

Also: r_c_annual, r_c, Buyer discount rate %/yr, personal discount rate.

The rate at which the rational Buyer discounts the continuation value of paying: 15% a year by default, e^(−r_c/12) per month. Accepted from 0 to below 100%.

Applied at each of the four lattice steps a month, \(e^{-r_c\Delta} = 0.99688\) per step. A coin one does not expect to appreciate is not worth financing at 1.475× and a 15% discount rate, whatever the volatility.

Where it is derived

Byte ceiling

Also: FF_MAX_ESTIMATED_BYTES, estimated ledger memory, estimate_bytes, POSTING_BYTES, work estimate.

A per-request admission ceiling on estimated peak memory, FF_MAX_ESTIMATED_BYTES (1.5 GB by default). It includes the route’s retained state and response allowance where applicable; the raw posting estimate alone is not a detailed-output bound.

The raw forwardflow::estimate_bytes posting expectation and work_estimate.rs::peak_bytes serve different purposes. Admission validates the configuration first and uses a conservative peak bound for the route. In 0.6.1, estimate_simulate_request includes selected Agreement/cohort detail, typed postings and bounded JSON capacity, and simulation responses retain their process-memory reservation through serialization, compression and outgoing byte ownership. Coin routes also budget retained monthly surfaces. The process budget is separate from the per-request ceiling: requests that cannot fit alongside current work receive 503 with Retry-After. These estimates are neither measured RSS nor a precise CPU-time guarantee.

Where it is derived

C

Call spread

Also: call spreads, 1.475× / 2.5× spread, 2× / 4× spread.

A bought call and a written call with a higher strike, at the selected size and tenor. The written leg helps pay the premium but gives up protection above its strike.

Read each strike, side, premium, settlement and horizon mark. Short-option collateral and execution constraints are not established by the model; a lower net premium alone does not prove a cheaper fully funded hedge.

Where it is derived

Cancellation

Also: Cancel, cooperative cancellation, cancel token, PathError::Cancelled, 499.

A token every seed, cell, structure and vintage loop reads before each unit of work; a dropped HTTP request or a closed websocket — mid-chunk included — sets it, the units not started are reclaimed and the permit and the memory reservation return. A running month finishes; the loop is never interrupted.

forwardflow::Cancel (cancel.rs; model audit 2026-09-06, M08): run_monte_carlo_with_cancel, hedge_overlay_with_cancel, hedge_series_with_cancel, drift_sweep_with_cancel, unhedged_dists_with_cancel and greeks_with_cancel return PathError::Cancelled instead of the next unit; the API’s run_work hands a token to every heavy request and CancelOnDrop sets it when the caller goes away; the websocket selects both its permit wait and each chunk against the socket, so a close mid-chunk stops the chunk at its next seed (model audit 2026-09-07, R10). A token can carry a budget of units, which is how the tests stop a run after exactly one seed.

Where it is derived

Capability table

Also: what each instrument supports, instrument capabilities, per-instrument capability table.

The one table, per hedge structure, of what the desk computes from the contract, what stands in as a proxy, and what it does not compute and says so — cash flows, maturity, monthly mark, execution cost, margin, close-out, whole wealth.

Model audit 2026-09-06, M10’s acceptance. Read it before comparing structures on the run page: a “no (stated)” cell — the collar’s free coin, the coin-delta branch’s trading cost, any option’s margin — is a figure a Holder supplies from outside, and the label “total P&L” on that row is the contract’s cash and mark as modelled and nothing more.

Where it is derived

Capital at risk

Also: capital_at_risk_usd, dollars that can be lost, peak capital at risk.

The sum of unrecovered capital over active Agreements: the dollars a par Holder can actually lose, month by month. Peaks at $5.94m at month 23 on the base path, the default ladder month.

It is the figure the exposure ladder colours, and the month of its peak is the default as-of month for the Greeks. It is the capital line’s numerator summed across the book.

Where it is derived

Capital line

Also: capital_line, the capital line, loss line.

The sale proceeds that return the Holder’s unrecovered capital, counting what it has already been paid, over the strike. 1.053 at months 0 and 1 (payment 1 went to BTC Now, every dollar carries 5%), 0.487 at month 24, zero from 44.

exposure.rs::capital_proceeds_needed solves \(\text{delivered}(V)(1-f) = U_t\) piecewise on the waterfall: \(U_t/(1-f)\) while that is at most \(R_t\), else \(A_t + U_t/(1-f)\), flagged capital_above_purchase_price. At par the first branch holds throughout. A stop below this line loses the Holder money; after month 44 no stop at any price can.

[ V^{\text{cap}}_t = \begin{cases} U_t/(1-f) & U_t/(1-f) \le R_t \ A_t + U_t/(1-f) & \text{otherwise} \end{cases}, \qquad \ell^{C}_t = V^{\text{cap}}_t / \text{strike} ]

Where it is derived

Capital loss

Also: capital_loss_usd, Capital loss, realized capital loss.

Σ max(0, −capital P&L) over a vintage’s stops: the par Holder’s realized loss, against its outlay net of what it had already been paid. $231k on the historical early-September reference book against an EL of $990k.

Most of the shortfall lands on Agreements that had already returned most of the Holder’s capital, the dollar version of the gap between the two lines. Cohort 1 loses $57,708 of capital; cohort 23 none.

Where it is derived

Capital P&L

Also: capital_pnl, Capital P&L $, realized profit, capital loss on a row, Net cash gain.

Hard dollars: net received minus capital deployed on the Agreement, owner_net − purchase_price. Negative is money actually lost against deployed funds; a row can show a shortfall and still be positive here.

The reference Agreement’s is +$22,673.75. In the engine’s worked stop, Agreement 2 stops at age 22 with a $21,212 shortfall and a capital P&L of +$2,522, because 21 delivered payments came in first. The risk desk’s capital loss sums the negative ones over stops.

[ \text{capital P&L} = \text{owner net} - \text{purchase price} ]

Where it is derived

Carry bucket

Also: carry, carry at the purchase yield.

The mark rolling forward one month at the purchase yield: y × V(t−1, S at m−1) per Agreement on last month’s book, stops in transit included — the accrual of the month’s expected flows. On a riskless flat path, the only nonzero bucket.

Over the life of a riskless flat path the carry sums to the flat-path markup exactly — $90,695 on the test’s four Agreements.

[ \text{carry} = y,V(t-1, S_{m-1}) ]

Where it is derived

Cash fan

Also: cash_fan, When is the money back, cumulative cash band, p5 cash band.

Per month, the p5, p25, p50, p75 and p95 of cumulative net cash across the runs: when the money is actually back, under uncertainty. Chip S5 points here.

At the base cockpit configuration the month-12 band runs from −$9.87M to −$4.51M because the 24 cohorts are still being bought; by month 84 it runs from +$2.29M to +$8.95M, median +$4.53M. The dark band is the middle half, the light band 90% of futures.

Where it is derived

Cash loss

Also: pct_cash_loss, cash-loss-share, cash-loss share, share losing cash.

The share of ALL seeds (or runs) whose undiscounted multiple is below one — coins per coin below one on the coin seat. Never conditional on an IRR: a path the solver cannot rate is still a loss if less came back than went out.

Dist::pct_cash_loss and MonteCarloSummary::pct_cash_loss (audit 2026-09-05, finding 1). The share negative counts only the seeds with an IRR; a written option assigned deep in the money, or a book whose every payment BTC Now retains, can leave every seed without one. The negative-IRR share is then unavailable, not 0%. The cash-loss share is the figure to read first when seeds_without_irr is not zero.

Where it is derived

Cash recovery

Also: cash_recovery, recovery at 12, 24, 36, cash back by month 12.

Cumulative gross cash received through month 12, 24 and 36, divided by the whole run’s gross outflow. Of everything committed to this program, how much is back by then: 25.69% / 53.71% / 81.74% on the reference Agreement.

The denominator is the run’s total outflow, not the capital deployed by the marker, so on a paced book the markers read low early: 10.04% / 40.01% / 74.41% on the historical early-September reference book. Markers past the horizon are omitted.

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

Where it is derived

Cash-and-carry basis

Also: basis trade, cash and carry, carry trade.

A shelf row and a dollar asset: the dollars deployed earn the basis (4% a year by default). The dollar seat is the rate on every path; the coin seat is that multiple over where the coin ends — 1.24 flat, 0.29 at the 5th.

The paper’s convention for a dollar asset: coins per coin = (1 + r)^years ÷ (S_T / S_0). Table 19: 3.38 / 1.24 / 0.46 / 0.17 coins from −18% to +49% a year at 4.0% throughout; 41% of flat paths lose coins.

Where it is derived

Circular bootstrap

Also: circular block bootstrap, wrapping blocks, sampler weights.

The bootstrap’s draw since the model audit (M03): a block starts on any of the n returns and wraps past the window’s end, so every observation weighs 1/n and the expected log return is zero at every horizon — the sampler’s own zero drift.

paths.rs::bootstrap. The non-circular draw from 0..=n − L weighed the interior more than the edges, and after an equally weighted de-meaning the sampler carried +1.24% / −1.73% / −3.67% a year of log drift on the 2012 / 2017 / 2024 windows. Zero drift means zero expected LOG return under the sampler’s weights, nothing more: the price factor’s median and mean are the empirical block distribution’s (median log step −0.46% a month on the pricing window, log(mean(exp(step))) +0.69%), not a lognormal’s (model audit 2026-09-07). Every bootstrap path changed at the same seed; the Model Card’s zero-drift rows moved by about half a point, the replay rows not at all.

Where it is derived

Close on exit

Also: close_on_exit, futures_retained_agreement_months, retained exposure.

A calendar futures leg’s flag: close the position the month its Agreement leaves the book instead of holding it to the next reset. Default off; the result names the Agreement-months held past the exit as retained exposure.

TradingRule::close_on_exit (model audit 2026-09-07, “a calendar futures hedge can outlive the underlying Agreement”: a 12-month Agreement under a five-month reset kept a $60,000 short at month 12 and booked $30,000 of futures profit on a month-13 halving against paper that had completed). Off, the calendar rule holds through the interval — what a k-monthly roll literally does, and what every stored structure was priced on — and HedgeResult::futures_retained_agreement_months counts the Agreement-months a position was held past the paper’s exit, with a warning in the notes when positive. On, the position is closed at the exit, the close-out in the turnover and charged the trading cost.

Where it is derived

Cohort

Also: cohorts, monthly cohort, vintage cohort, Monthly cohorts, Agreements per cohort.

A group of Agreements that originate in the same month at that month’s price. The base book is 24 monthly cohorts of 10 Agreements, 240 in all; cohorts and Agreements per cohort are input #13.

Cohorts originate month 0 first, one per month, until the origination window closes. Everyone in a cohort shares the month’s entry price unless intramonth strike dispersion is on. The caps: at most 1,200 cohorts and 50,000 Agreements in a book.

Where it is derived

Coin

Also: Bitcoin, BTC, the coin.

The asset underlying each Agreement is one Bitcoin. The contractual ledger records USD payments and stop-sale cash. The Coin workspace adds a BTC unit-of-account view with an explicit purchase-funding and receipt-conversion rule.

The program describes a per-Agreement Dedicated Wallet under a trust structure until completion; the simulator does not implement custody. From engine 0.6.0, a par Agreement purchase is funded with exactly one BTC at its own entry price. Holder receipts and hedge flows are converted analytically at monthly simulated spot. One BTC of contract notional does not mean one BTC of receipts or one BTC of price sensitivity, and modeled equivalents do not establish actual BTC settlement.

Where it is derived

Coin book

Also: the coin book.

The Agreement book and modeled hedge evaluated in BTC. Each par Agreement uses one BTC at its own entry price; Holder receipts and hedge cash use monthly simulated spot.

The Coin objective evaluates BTC amounts and dated investment returns against the stated holding reference. Gross Agreement funding is distinct from monthly net contributions, which may reuse receipts. Market moves, Buyer outcomes, costs, model assumptions and funding needs can create losses. The analytical convention does not implement wallet balances, reserves or forced closure.

Where it is derived

Coin delta

Also: dC/dlnS, coin delta surface, ex-ante coin delta.

dC/dlnS: expected remaining BTC-receipt sensitivity per unit log spot move. The shared reader uses each Agreement’s actual lifecycle; one BTC of contract notional is not one BTC of delta.

coin.rs::agreement_coin_delta uses CoinDeltaSurface for performing Agreements and an analytic sale/refund/receipt conversion value for known stops awaiting sale. Both use the stated median-flat lognormal continuation volatility (zero log drift); the performing surface also uses its recorded valuation seed. From 0.6.1 it has a node at every monthly payment age, so the final receipt and other monthly transitions are represented directly. Known stops retain their actual paid count, remaining schedule and residual sale lag, including after contractual maturity. After receipt booking and conversion, remaining receipt delta is zero. Delta may be negative, zero or positive. Monthly per-Agreement figures divide by the same performing-plus-stopped-unsold population that was summed.

[ \delta_{\mathrm{performing}}=\frac{\overline C(1.01S)-\overline C(0.99S)}{\ln1.01-\ln0.99},\qquad \delta_{\mathrm{stop}}=\frac{d}{d\ln S}\mathbb E[\mathrm{Holder\ receipt}/S_{\mathrm{booking}}] ]

Where it is derived

Coin hedge-ratio sensitivity

Also: coin hedge-ratio sensitivity.

An experiment comparing coin-delta hedge sizes under explicit market and cost assumptions. The historical name of this glossary key does not describe a guarantee.

A finite simulation cannot establish a return floor in every future path. Report the observed downside percentile, sample count, uncertainty and omitted funding constraints. Historical private-paper ratios are not universal optima, and annualizing a purchase-denominator multiple does not produce wallet CAGR.

Where it is derived

Coin lending

Also: coin lent, 2% a year in coin.

An illustrative shelf alternative that assumes a fixed annual BTC growth rate over the holding period, then values the proceeds at horizon spot. The fixed growth is an input, not a guaranteed external product return.

The shelf omits counterparty failure, trading and custody frictions unless stated in its convention. Its contractual one-coin starting basis can differ from an Agreement’s purchase amount and from Research’s matched net contributions. Use it as a labeled reference, not an equal-budget or risk-free ranking.

Where it is derived

Coin seat

Also: the coin seat, coin mandate.

The Agreement and hedge book read in BTC equivalents: purchases funded at each Agreement’s own entry price, and receipts and hedge cash converted at monthly simulated spot. At par, each originated Agreement uses exactly one BTC.

Engine 0.6.0 separates actual-entry purchase funding from monthly receipt conversion. Fixed future USD receipts buy fewer BTC when spot rises and more when it falls. Read gross BTC purchases, receipts, net contributions, recovery, gain and cash-flow IRR together. The Coin view is an analytical accounting convention, not actual custody or proof that a finite wallet can meet cash calls.

Where it is derived

Coin to Buyer

Also: coin_returned_usd, coin_returned_to_obligors, coin equity, Buyer’s coin equity, coin returned.

The spot value of the coin a Buyer took at early completion, net of the payoff: the Buyer’s property, never the Holder’s cash, reported as a transparency counter. Zero on every other exit; a stopped Buyer receives dollars, never coin.

engine.rs::settle records it; $36,900 in the program chapter’s month-24 early completion at $90,000, $2,123,267.67 across the historical early-September reference book. The report view’s July fallback counted the old sale’s residual coin under this name; the live engine counts only early-completion equity.

Where it is derived

Commitment delta

Also: delta_commitment_usd_per_pct, Commitment delta, forward-flow commitment.

Delta with the full pacing, so cohorts after the as-of month strike at the bumped prices: the purchase commitment’s exposure rather than the existing book’s. Coincides with delta when no cohort originates after the as-of month.

On a book with no stops the existing book’s delta is zero and the commitment delta is positive as of month 0 (greeks_vanish_on_a_riskless_book). On the base configuration as of month 23, the last origination month, both read +$24,885.

Where it is derived

Completion

Also: completed, Completed, completes.

All payments delivered; the coin passes to the Buyer and the Agreement closes. At the base terms the Holder has received 59 net payments of $1,401.25, $82,673.75, on $60,000 paid.

The engine marks the Agreement Completed at the payment date that brings payments_made to the term and posts nothing further. Delivery of the coin is outside the ledger. No new hazard is drawn after the final payment completes the Agreement. A hazard draw after the penultimate payment, or a walk decided before payment, can still cause the final payment to be missed.

Where it is derived

Configuration hash

Also: config hash, sha8, hash.

The first eight hex characters of SHA-256 over JSON.stringify(config) in the cockpit’s field order: a fingerprint of the deal, printed on every export. BASE_CONFIG hashes to 4e8fd718.

export.ts::sha8. Key order matters: the same values with shock moved to the end hash to a0ac777b. It is a fingerprint, not an encoding; the configuration must travel with the figure. The seed is printed separately because two configurations differing only in seed are the same deal on a different path.

Where it is derived

Conservation

Also: Σ ≡ $0.00, honesty check, conservation_ok, balances to the cent.

The engine’s honesty check: the sum of every entity’s balance is exactly zero, so money only moves and is never created. The header must read Σ ≡ $0.00 ✓; anything else is an engine bug, not a market outcome.

ledger.rs::Ledger::conservation_sum; asserted at the end of every run in debug builds, reported as conservation_ok on every simulate response, and checked across scenario families by conservation_to_the_cent_across_scenarios. On a stop the three postings sum to the proceeds, which is what the Market gives up.

[ \sum_{e \in \text{entities}} \text{balance}(e) \equiv 0 ]

Where it is derived

Content integrity

Also: content-integrity check, digest check, unkeyed hash, not a signature.

What a package’s digest proves: the content is unaltered since it was sealed. It does not prove which engine produced it — an unkeyed hash is not a signature; provenance needs an engine signature or a trusted run record.

Model audit 2026-09-07 (“content hashes are not authentication”): changing a result and recomputing the digest verifies, as expected of an unkeyed SHA-256, so the wording “the run it says it is” overstated the check. The package’s identity is its content; its provenance is the engine identity it carries (build, data digest), which the desk package now keeps per answer and refuses to mix.

Where it is derived

Convention C1

Also: C1, floor to the cent, residual.

The payment is floored to the cent and the final payment absorbs the residual, so the schedule sums to the Purchase Price exactly. At 36 months payments 1–35 are $2,458.33 and the last is $2,458.45.

Decided at milestone M1 and amended after adversarial review on 2026-07-11: half-even rounding could push the accumulated schedule past \(P\) at a micro-strike and make the final payment negative. tiny_strike_schedule_never_goes_negative runs a $10 coin over 84 months.

[ \text{PMT} = \Big\lfloor \frac{P}{n} \Big\rfloor_{0.01}, \qquad p_n = P - (n - 1),\text{PMT} ]

Where it is derived

Convention C2

Also: C2, cumulative rounding, cumulative fee rounding.

The fee is rounded cumulatively: each posting is round(f × cumulative delivered) minus the previous rounded cumulative. Any single posting may wobble a cent; the lifetime fee per Agreement is exactly round(f × total delivered).

fees.rs::FeeState::split. The sum telescopes, so no intermediate rounding survives. At 36 months the postings alternate 122.92, 122.91, … and sum to $4,302.08, where naive per-posting rounding would give $4,302.20. fee_identity_exact_to_the_cent asserts the identity as an equality on Decimal.

[ \text{fee}_k = \text{round}(f \cdot \text{cum}k) - \text{round}(f \cdot \text{cum}{k-1}) ]

Where it is derived

Convention C3

Also: C3, IRR on quantized flows.

IRR is computed on the quantized flows: the Holder’s monthly net flow is built from cent-exact postings, converted to f64, and solved by a grid scan and 200 bisections. The engine matches the M0 fixtures within 0.01 points.

The fixtures were generated in exact rational arithmetic on unquantized flows, which is why the tolerance exists. Nominal is 12 times the monthly rate; effective is \((1+r)^{12} - 1\).

Where it is derived

Conviction walk

Also: conviction walks, conviction_walk, conviction_walks, walk, walks, the walk.

A stop produced by the lost-conviction rule (or the rational boundary): a walk is decided at or before a payment date and consumes it unpaid, so the walk is the missed payment. Settled like any stop.

Exit tag ConvictionWalk. Walks differ from hazard draws in timing: a draw at age \(t\) happens after payment \(t\) and misses \(t+1\); a walk at age \(t\) has payments_made \(= t - 1\) and misses \(t\) itself. A walk at the final payment date and a hazard draw after the penultimate payment can each cause the final payment to be missed.

Where it is derived

Cost in bps of deployed

Also: cost, bps of deployed, cost_bps_of_deployed, cost of carry.

Mean net premium and carry paid over mean capital deployed (the sum of purchase prices), in basis points: the hedge’s cost as a share of the money the Holder put into the paper.

HedgeResult::cost_bps_of_deployed. A running-cost figure a desk compares with the basis and with the paper’s spread: 300 bps of deployed on a book that spreads 800 bps to par leaves 500. Undiscounted on both sides.

Where it is derived

Cost in IRR points

Also: cost_irr_points, cost of the hedge in yield.

Unhedged median IRR minus hedged median IRR, in percentage points. Positive: the hedge cost yield; negative: it paid more than it cost on these seeds.

HedgeResult::cost_irr_points. The directional desk’s price of the floor: how many points of median yield the structure gives up to buy the p5 it achieves. Read with the floor achieved to see what each point bought.

[ \text{cost}{pp} = 100 \times \big(\text{median IRR}{unhedged} - \text{median IRR}_{hedged}\big) ]

Where it is derived

Coverage

Also: CoveragePoint, Coverage on the simulated path, coverage_by_cohort.

The two lines applied to the run’s path month by month: how many price-exposed Agreements sit below each line, and the dollars those positions carry. A stopped Agreement stays on the panel until the month its sale is booked.

exposure.rs::exposure, from tracks reconstructed off the ledger; which Agreements count at a month is engine.rs::price_exposed_at, the one lifecycle the hedge desk and the monthly mark read too (M04). Proceeds are priced at the month’s mark with haircut and sale cost, without the 18-day interpolation: coverage asks what a sale at this mark would do. At month 12 on the base path: 123 active, 123 below the schedule line, 117 below the capital line, capital at risk $4.54m, intrinsic shortfall $3.18m. Per cohort it reports mean months and share of Agreement-months below each line.

Where it is derived

Covered call

Also: covered-call overwrite, Benchmarks::covered_call, overwrite.

The legacy spot reference with 12-month calls written at 130% of spot at each roll. Calls start at each coin’s origination and roll annually while held. Premium uses the supplied surface and pricing rate, with no execution cost.

Cash settlement occurs at expiry; an option still open at the horizon retains its signed remaining-life model value. The reference buys one coin per Agreement and has its own capital and cost conventions. It is not the Research matched-contribution holding benchmark or an equal-budget ranking of the Agreement and market alternatives.

Where it is derived

Crash library

Also: crash presets, named crash.

One-click price paths shaped like named Bitcoin crashes — 2018, 2022, March 2020, May 2021, the FTX week, a fast crash at the vintage peak — as stylised anchors, not replays.

Each preset is a custom path through a few (month, price ratio) anchors that reproduce the episode’s depth and speed, then holds flat. They are stylisations for a stress conversation; the historical replay mode carries the actual monthly closes.

Where it is derived

Cross-book collar

Also: collar, holder of free coin.

A legacy partial Agreement-sleeve illustration: calls on separately held coin provide premium for puts on the Agreements. It does not report the complete combined coin book.

Call premium enters the Agreement-sleeve distribution; the external coin value and written-call liability do not. Call assignment is reported separately and is not charged to that distribution. Paired Research and explicit Coin sweep/monthly requests refuse this incomplete performance boundary; the Coin workspace does not offer it as a complete strategy. The Buyer’s promised coin cannot cover the Holder’s written call.

Where it is derived

Cumulative net cash

Also: cumulative_net_cash, net cash position, Cumulative, when is the money back.

The running sum of the Holder’s monthly net flow, one value per month. Its last entry is the run’s total net gain, $22,673.75 on the reference Agreement; it feeds the cash-recovery curve and the Monte Carlo cash fan.

On the base paced book it runs deeply negative while the 24 cohorts are being bought (median −$6.48M at month 12 across 1,000 runs) and ends at a median +$4.53M at month 84.

Where it is derived

Custom path

Also: Custom, anchor path, anchors, points.

A deterministic path through (month, ratio) anchors, log-linear between them and flat after the last. API only; no cockpit picker. It is how the worked stops and the exact-cents test state a price shape as data.

paths.rs::custom; the origin (0, 1.0) is implicit. Halfway between anchors at 0.40 and 0.80 the price is the geometric mean, $33,941 on a $60,000 start, not $36,000. Validation: at least one anchor, strictly increasing months after 0, positive finite ratios.

Where it is derived

Custom per-year shares

Also: CustomYearly, per-year view, custom timing curve, your per-year view, per-year bars, Late-loaded, Flat.

A timing view stated as the share of the original book that stops in each year of the term. Each year’s mass is spread over its stoppable ages and divided by the survival so far, so a flat path realizes the shares exactly.

DefaultScenario::CustomYearly { shares }, spec v1.4. One share per year, finite and non-negative, summing below 100% with a \(10^{-9}\) headroom; a year with no stoppable age (at 37 months, year four holds only the final date) must carry 0%. The cockpit seeds it with three unbranded chips: the program hump, flat through time, late-loaded stress.

[ h_t = \frac{m_t}{1 - \sum_{k<t} m_k} ]

Where it is derived

Customer

Also: Customers, funnel.

A person in the funnel before signing. Once an Agreement opens the person is Buyer 0. Not an engine word; listed because the vocabulary ruling names it.

Marc, 2026-09-02. A Customer gets a 60-minute window to accept a price; letting two such windows lapse locks the person out for seven days.

Where it is derived

D

Data digest

Also: X-Engine-Data, data_sha256, ENGINE_DATA_SHA256, series digest.

The SHA-256 of the price series compiled into the engine (data/btc_historical_monthly.csv, 174 months, February 2012 – July 2026), stamped by build.rs and sent as X-Engine-Data on every response and data_sha256 on health.

8846a81b803001d885b0cc3b830f26bfeba35405b91d1843c6376eeca5d8a1bf on the current tree — the audit’s own measurement. A data refresh changes the digest without a version bump, which is why it travels beside the build identity (model audit 2026-09-06, M07). It attests the bytes, not their provenance: vendor reconciliation, fixing and licence are the Model Card’s data section.

Where it is derived

Dealer call

Also: five-year call, 5-year call.

A modeled long call per Agreement for the chosen long tenor and strike. It is an illustrative dealer-style reference, not an executable dealer quote.

HedgeSpec::DealerCall prices the supplied terms on the model surface. Premium, execution, expiry settlement and open horizon value are separate. The model does not verify availability, credit terms, collateral or liquidity of a matching instrument.

Where it is derived

Decimal and f64

Also: Decimal, f64, cents, money.

Money is rust_decimal Decimal quantized to cents, half-to-even; boundaries and statistics (prices, rates, moneyness, hazards, IRR) are f64. A float becomes money in exactly one place, and never goes back on the ledger side.

money.rs::cents and cents_from_f64; the latter is called when a strike is fixed, a sale is recorded, coin equity at early completion is noted, and for the frontier’s reference strike. The repository rule: Decimal for money, f64 for boundaries and display, never f64 in a ledger posting.

Where it is derived

Delivered gross

Also: delivered_gross, Delivered, gross deliveries, Holder collections.

Every dollar that entered the Holder’s stream from an Agreement before the fee: scheduled payments after the first N, the early-completion payoff, the stop-sale delivery (proceeds minus refund). $87,025 on a completed base Agreement.

Per row on the drill-down; a regime-2 stop shows the same $87,025 as a completed row, because the waterfall delivered exactly the remaining schedule. Auditor check C5 re-derives it by outcome and re-sums it from the row’s own postings.

Where it is derived

Delta

Also: delta_usd_per_pct, Δ, existing-book delta.

Change in the Holder’s net gain per +1% in every price after the as-of month, existing book only. +$24,885 per 1% on the base configuration as of month 23: the Holder is long the coin where its Buyers are underwater.

The central difference of net gain under price factors \(1 \pm 0.05\), averaged over the seed ensemble, in dollars per 1%. Flat above the schedule line, where a stop delivers the schedule in full; zero on a book with no stops. As of month 48 it falls to +$1,509.

[ \Delta = \frac{1}{k}\sum_s \frac{G_s(1+b) - G_s(1-b)}{2 \cdot 100,b} ]

Where it is derived

Delta band

Also: DeltaBand, band_coins_per_agreement, band.

Rebalance::DeltaBand { band_coins_per_agreement }: reset the futures position only when the target has drifted from it by more than the band, in coins per Agreement. A band no drift can cross trades once, at signing, and never again.

Test, one cohort on a moving path: the monthly reset 53 trades and 3.39 coins of turnover, a huge band one trade and 3.32 coins; the hedged medians differ (16.02% against 16.84%) because a held position is not the melting one — the band’s cost is the path it holds.

Where it is derived

Delta in coins

Also: delta_coins, coins, delta hedge.

USD-value sensitivity expressed as the BTC position whose 1% spot move produces the same dollar change. It is distinct from Coin delta, dC/dlnS, which measures expected BTC-receipt sensitivity.

The risk desk’s USD bump-and-revalue delta is divided by 1% of the as-of BTC/USD mark. The resulting unit is a BTC position equivalent, not BTC held, deployed or returned. Read its valuation method and as-of month; do not substitute it for the Coin lifecycle delta.

[ \Delta_{\text{coins}} = \frac{\Delta}{0.01 \times S_{\text{as of}}} ]

Where it is derived

Delta series

Also: DeltaSeries, three deltas, proxy_delta_coins, greeks_delta_coins_quarterly.

Delta readings for the complete selected path: the embedded-ladder proxy, the Coin value sensitivity and optional quarterly Greeks from bump-and-revalue. The report names its path-selection rule and seed.

DollarDelta sizes from the USD ladder proxy; CoinDelta sizes from the shared lifecycle-aware BTC-receipt sensitivity used by the hedge. Coin delta includes performing and stopped-unsold Agreements, and price_exposed_agreements supplies that same population for per-Agreement display. A known stop may retain sensitivity after the term; cash already booked and converted has no remaining receipt sensitivity. Preserve both the realized-path seed and valuation seed. Optional quarterly Greeks use additional USD simulated valuations and remain a distinct measure.

Where it is derived

Deployed

Also: deployed_usd, Capital deployed, capital outstanding, Peak capital outstanding.

Gross outflow: the capital the Holder put into a vintage or a book, the sum of purchase prices. $9,619,863.62 on the historical early-September reference book with dispersed strikes; $14,400,000 on a flat path (240 × $60,000).

The denominator of the EL rate, the undiscounted multiple and cash recovery. Peak capital outstanding on the report view is the most negative point of cumulative net cash.

Where it is derived

Desk seeds

Also: seeds per structure, seeds, MAX_DESK_SEEDS.

How many seeded engine runs the fair value and the hedge overlay average (default 32, ceiling 256). Every seed is one full run of the book, once per structure; seed k is the config’s seed plus k, so the figure is reproducible.

Request field seeds on the fair-value and hedge endpoints; the cap is named in the 400 when exceeded. The unhedged distribution is identical across structures in one request because they share the seeds; the benchmarks share them too. More seeds narrow the estimate of the median, p5 and mean; the worst path is only the worst of the seeds run.

Where it is derived

Dollar book

Also: dollar seat, the dollar book.

The Agreement book evaluated in USD, including fixed receipts and price-sensitive stop outcomes. The relevant hedge follows its modeled dollar exposure.

A dollar-delta hedge and a coin-delta hedge answer different questions on the same underlying cash flows. Their cost and residual risks depend on the configuration; no fixed cost advantage, universal hedge direction or complete market neutrality is promised.

Where it is derived

Dollars lent against coin

Also: dollar lending, 7.65% on dollars, dollars lent.

A shelf row and a dollar asset: dollars earning 7.65% a year (the paper’s rate), read on the coin seat as the dollar multiple over where the coin ends — 1.48 coins on a flat median, 0.34 at the 5th, 33% of paths losing coins.

Table 19: 4.02 / 1.48 / 0.54 / 0.20 coins from −18% to +49% a year, 7.6% in dollars throughout. The engine holds the dollar row at its rate within half a point and asserts its coin median above the 4% basis row’s.

Where it is derived

Draw

Also: hazard draw, stop draw, draws, uniform draw.

A random stop: each month, after payments, every active Agreement at a reachable age draws a uniform from its own stream and stops if it falls below the hazard. A draw at age t misses payment t+1.

Step 4 of the monthly order. The draw is taken unconditionally before any price test, so a bumped price changes whether the draw matters and never the draw. Early completion is the same mechanism in step 3 against the propensity.

Where it is derived

Drawdown-scaled hazard

Also: drawdown multipliers, drawdown_hazard_multipliers, state multipliers, Drawdown-scaled hazard (tiered), ×0.5 in the money.

Each month the hazard is multiplied by a state read off the coin against the Buyer’s entry: above entry ×0.5, drawdown up to 30% ×1.0, 30–50% ×1.5, 50–70% ×2.0, deeper ×3.0, capped at 1. Off in the cockpit, on in the Model Card.

Input #23, engine.rs::drawdown_multiplier, from the drawdown memo’s mortgage double-trigger evidence: price pain coincident with a liquidity shock, never a cliff. A coin exactly at entry sits in the ×1.0 bucket, so a flat path reproduces the toggle-off run draw for draw. The ×0.5 in the money is what sets how often a winning Buyer stops, the least-evidenced number in the model.

[ h_t^{\text{eff}} = \min\bigl(1, h_t \cdot m(S_t / \text{strike})\bigr) ]

Where it is derived

Drift

Also: μ, mu_annual, drift μ %/yr, expected growth.

The continuous annual price-drift parameter μ. Under GBM, expected one-year price growth is exp(μ) − 1; median growth is exp(μ − volatility² / 2) − 1. At 0% drift and 43% volatility, expected price stays flat while median price declines about 8.83% annually.

A chosen market assumption, not a forecast. Setting both drift and volatility to zero produces constant GBM prices before shocks or bumps. Jump-diffusion includes its jump compensation; the simple GBM median formula does not describe a jump or regime-switching path. Under the risk-neutral measure the drift is replaced by the funding rate. The Buyer’s believed drift for the walk-away boundary is a separate input.

Where it is derived

Drift sweep

Also: Table 7b, coin seat sweep.

Re-run the Agreement book and selected hedges on matching seeds for several GBM price-growth assumptions, at the configured volatility and costs. Compare BTC cash-flow IRR, amounts and loss statistics.

coin.rs::drift_sweep accepts median annual price growth r and uses continuous drift mu = ln(1+r) + volatility²/2. The Coin Research sensitivity labels continuous drift separately from median price growth. Keep the captured seed, valuation seed, surface, shock/bump and costs fixed for a matched comparison. Outcomes are conditional; no fixed growth threshold applies to every configuration.

[ \mu = \ln(1 + r) + \tfrac{1}{2}\sigma^2 ]

Where it is derived

E

EAD

Also: exposure at default, exposure at the stop, Mean remaining schedule at the stop, ead_usd.

The mean remaining schedule at the stop across a vintage’s stops: the put’s strike at exercise. $43,563 on the historical early-September reference book; $75,325 for cohort 1, whose Buyers stopped early in the schedule.

EAD times the stop count is the vintage’s \(\sum R_t\), the denominator of LGD.

Where it is derived

Early completion

Also: early completions, completed early, settled, settlement, make-whole, MakeWholeDelivery, Settled.

The Buyer pays the remaining schedule in cash at once and takes the coin. Cash only; the coin is never sold to pay for it. The Holder receives the remaining schedule less the 5% fee, years early and undiscounted.

Available without a separate early-completion fee; the servicing fee still applies to the delivery. The Holder’s lifetime cash is the same as at completion ($82,673.75 at the base terms); scheduled cash arrives sooner. It also removes exposure to later stops and possible surplus proceeds, so its effect on portfolio returns depends on the scenario. In the engine it is a monthly draw with probability \(u_t = p \cdot \max(0, (S_t - R_t)/S_t)\), optionally gated by the take-profit threshold. The ledger kind MakeWholeDelivery and the exit tag settled are the engine’s older names for it.

Where it is derived

Early completion propensity

Also: settlement_propensity, Early completion %/mo, Early settlement %/mo, propensity, prepays.

Monthly chance that an in-the-money Buyer pays off the remaining schedule in cash and takes the coin, scaled by his equity in the coin: u_t = p × max(0, (S_t − R_t)/S_t). Default 2.5% a month; a Holder isolating it sets 0.

Input #12, spec §4.4, accepted 0 to 100%. After twelve payments with the coin at $100,000, \(u = 0.025 \times 0.292 = 0.73\)% a month. Nobody pays $70,800 in cash for a coin worth $60,000, so the propensity is zero there. A prior (Model Card §7). Compare zero propensity with the selected rate: early completion accelerates scheduled receipts but removes later stop exposure and possible surplus. The high early-completion scenario uses 5%.

[ u_t = p \cdot \max!\Bigl(0, \frac{S_t - R_t}{S_t}\Bigr) ]

Where it is derived

Economic hedge payoff

Also: payoff_mean_usd, hedge payoff.

Mean signed realized hedge settlements plus the signed value of derivatives still open at the horizon. Premium, basis and modeled costs are reported separately.

HedgeResult.payoff_mean_usd equals realized_settlement_mean_usd plus horizon_mark_mean_usd. A bought option can have no realized settlement and a positive horizon value; a written option can have a negative value that has not yet been paid. Those marks enter economic performance once and are excluded from realized-only cash measures. Economic payoff alone is not net profit.

Where it is derived

Effective IRR

Also: effective, effective annual rate, irr_effective_pa, eff..

The monthly IRR compounded into a true annual rate, (1 + r)^12 − 1: 13.965% on the reference Agreement. The figure the Monte Carlo, the heatmap, the backtest, the tornado and the solver all use.

The gap between the paper’s 17.80% effective implied financing rate and the Holder’s 13.97% on the same flat Agreement is BTC Now’s take: the first payment and the 5%.

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

Where it is derived

EL

Also: expected loss, el_usd, EL $, Σ shortfall.

Σ shortfall in dollars over the vintage’s stops: $990,282 on the historical early-September reference book, equal to the simulate response’s total shortfall at the same seed. EL = PD × LGD × EAD × n holds by construction.

Realized against the schedule on one seeded path, so it is the put’s realized intrinsic value, not a market value; no surface enters it. 0.4208 × 0.2251 × 43,562.82 × 240 = 990,282.

[ \text{EL} = \text{PD} \times \text{LGD} \times \text{EAD} \times n ]

Where it is derived

EL rate

Also: el_rate, EL % of deployed, EL ÷ deployed.

EL divided by capital deployed, the sum of purchase prices: 10.3% on the historical early-September reference book ($990,282 on $9.62m).

The vintage rows divide by that vintage’s deployed capital; the book row by the whole book’s.

Where it is derived

Embedded call

Also: long call, the call, call at the Purchase Price, surplus leg.

The Holder’s long call struck at the Purchase Price, about 147% of entry, exercised only by a Buyer who stops with the coin worth more than that. It exists only since the ruled waterfall and is why the base-config vega is positive.

Its realized value is the surplus column of the credit table, $149k on the base book, all in vintages originated from month 10 on. Rare by construction under the ×0.5 in-the-money multiplier, but worth the whole gap between the coin and the Purchase Price when it fires.

Where it is derived

Embedded put

Also: short put, the put, put at the schedule.

The Holder’s short put on the coin struck at the remaining schedule, exercised only when the Buyer stops: the schedule line is its strike, the ladder’s notional its size, EL its realized intrinsic value, delta and gamma its sensitivities.

One of the plan’s four legs; its exercise is behavioral, which is why the frontier family sits beside the line. It sits at 50 to 85% moneyness and 6 to 24 months, the corner of the Bitcoin surface with the heaviest skew; the engine prints its expected loss, not its market value.

Where it is derived

Embedded put ladder

Also: put ladder, the ladder, embedded_put_ladder.

The paper’s short put as a strip of European puts, one per payment age: struck at the remaining schedule R_t, expiring at the missed payment date t + 1, each weighted by the unconditional probability that the Buyer stops there.

surface::embedded_put_ladder(config, surface, r), at cohort-1 terms. The Holder is short a put on every stop date: if the coin is worth less than the remaining schedule when the Buyer stops, the stop sale falls short by the difference, which is exactly a put’s payoff. Legs run from age 1 to term − 1; each is priced on the surface at its own strike and tenor, and its weight is survival × hazard from the config’s default scenario, price-blind. The ladder’s value is Σ weight × put; the placement exhibit draws it on the surface. The perpetual delta hedge shorts the delta of this ladder, conditional on survival to date, as its proxy for the book’s delta.

[ L(\sigma) = \sum_{t=1}^{T-1} w_t ; P\big(S_0,, R_t,, \tfrac{t+1}{12},, \sigma_t,, r\big), \qquad w_t = S(t-1), h_t ]

Where it is derived

Endpoint

Also: endpoints, route, routes, the API.

One of the server’s nine routes: GET /health, six POSTs under /api/forwardflow (simulate, montecarlo, solve_price, heatmap, backtest, risk), GET history, and a WebSocket for chunked Monte Carlo. Stateless: a configuration in, results out.

An Axum server on port 8080 over the Rust crate. Money travels as strings, ratios as numbers. A well-formed request the engine refuses is a 400 naming the input; a malformed body is a 422; a missing content type 415. Heavy routes share three permits.

Where it is derived

Engine version headers

Also: X-Engine-Spec, X-Engine-Version, X-Engine-Build, X-Engine-Data, version headers, build identity, data digest.

Every response, open or keyed, success or error, carries X-Engine-Spec: v1.14 (the spec implemented), X-Engine-Version: <crate version> (from backend/Cargo.toml), X-Engine-Build: <crate version>+<spec>+<sha> (0.5.0+v1.14+1a2b3c4d5e6f, the build identity) and X-Engine-Data: <sha256> (the compiled-in price series’ digest). A run record takes them from the answer that carried its result.

Two engines at the same spec can differ by a fix, which is why the crate version travels separately from the spec — and two builds at the same version can differ by a commit or a data refresh, which is why the source revision and the series’ SHA-256 travel too (model audit 2026-09-06, M07). backend/build.rs stamps both at build time: the revision from GIT_SHA (the deploy workflow passes the commit), else git rev-parse on the build host, else unknown; the digest from the CSV the engine embeds, so a data refresh changes the header without a version bump.

Where it is derived

Entry close

Also: entry_close, Entry close, actual close.

The real Bitcoin close of the vintage’s origination month, a context label. The replay itself is rebased so month 0 equals the configured start price, which keeps dollar figures comparable across vintages.

$4.90 for February 2012, $35,026.90 for June 2021. The backtest chart draws it as a dashed line on a log scale, the cycle context behind each vintage’s IRR.

Where it is derived

Execution cost

Also: execution cost, bps of premium, exec_cost_bps, slippage.

Basis points of option premium charged on every option leg at execution (default 50 bps), bought or written, in both directions. For the variance swap it is bps of the vega notional; perpetuals carry none, and a futures leg carries none either — its trades are charged by its own futures_cost_bps (bps of the notional of every trade, the turnover’s terms at the month’s spot, default 0; model audit 2026-09-06, M10), reported as futures_trading_cost_mean_usd beside the turnover.

Request field exec_cost_bps, 0–1000. It is the bid-ask and slippage of dealing, paid on the gross premium: a put spread pays it on both legs, so the spread is not free of it because the premiums net. The benchmarks’ covered-call write charges none, so the covered call is the frictionless comparison.

Where it is derived

Exit month

Also: exit_month, the month the Agreement left.

The month an Agreement left the book. For a stop it is the missed payment date, not the sale or posting date; for completion or early completion, the payment date of the payoff.

outputs.rs::agreement_table reports it as exit_month, null while open. The stop’s cash posts one month later at the default lag; reconciling a row against the postings needs both dates.

Where it is derived

Exit split

Also: exit_split, Exit split, outcomes, Agreement outcomes, Outcome.

How every Agreement ended: completed, completed early, stop, stop under rational mode, conviction walk, rational boundary, open. The four stop buckets are identical in cash and counted apart to separate the prior from the rules.

ExitSplit, one bucket per engine.rs::ExitTag plus open. The historical early-September reference book at seed 42 reports 84 completed, 55 completed early and 101 stops. The backtest sums the stop buckets into one non_performance column. Two counters travel with it: suppressed defaults and coin taken at early completion.

Where it is derived

Expected net

Also: expected_net, Expected net $, full performance.

What a fully performing Agreement would deliver net of fee: (Purchase Price − first N payments) × (1 − fee), cent-rounded. $82,673.75 at the base terms. The benchmark each drill-down row is judged against.

Auditor check C2. A completed row lands within two cents of it (convention C2’s wobble); a stopped row’s gap to it is the schedule the Holder did not collect.

[ \text{expected net} = \text{cents}\big((P - \textstyle\sum_{k \le N} p_k),(1 - f)\big) ]

Where it is derived

Expected shortfall

Also: ES3, ES5, es3, es5, CVaR, tail mean.

The mean of the worst tail, not where the tail starts: ES3 averages the worst ⌈0.03 n⌉ runs, ES5 the worst ⌈0.05 n⌉. A fat left tail drags ES well below p3. At the base cockpit configuration ES3 is 9.03% and ES5 9.46%.

forwardflow_api.rs::summarize; with 1,000 runs ES3 averages the worst 30. It is Basel’s post-VaR measure; the outcome strip draws it as a red diamond inside the p3 zone. The risk desk’s word “shortfall” is a different quantity, the stop’s schedule gap.

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

Where it is derived

Exposure ladder

Also: ladder, Ladder, put ladder, Ladder month, ladder_month.

The book at one calendar month binned by the embedded put’s moneyness (sale proceeds ÷ remaining schedule) and tenor (months remaining), each cell with Agreement count, notional and capital at risk. What a desk maps onto listed strikes.

ladder_month left unset is the month of peak capital at risk, 23 on the base path, where 84 of 203 active Agreements sit in the 0.50–0.70 × > 36 months cell. That corner of the surface carries the heaviest put skew and the thinnest listed liquidity; the ladder makes the mapping possible and does not price it.

Where it is derived

F

Fee timeline

Also: btcnow_fee_monthly, revenue by year, How the money arrives, revenue timeline.

BTC Now’s take month by month: the ledger’s fee postings per month plus each Agreement’s paper spread at its origination month. Sums to total take plus spread. When paper stops paying, the fee stream on it stops too.

ledger.rs::Ledger::btcnow_monthly_fees; the cockpit’s “How the money arrives” exhibit charts it by year. Auditor check A7 re-derives it within a cent per month.

Where it is derived

FICO preset

Also: FICO presets, 700+, 600, 500, 400, band.

One-click loads of the baseline curve at a band’s lifetime target: 700 and above 15%, 600 to 699 35%, 500 to 599 55%, below 500 70%. They change nothing but the target.

defaults.rs::DefaultScenario::fico_preset, lib.ts::FICO_PRESETS, input #8. The program’s 40% stance sits between the 600 and 500 bands. There is no credit-score floor in the program (foreclosed 2026-08-22); the presets are a Holder’s way of stating a view.

Where it is derived

First payment to BTC Now

Also: origination payment, origination payments, first N payments, origination_payments, First N payments → BTC Now, N.

The first N scheduled payments (N = 1 in the program) go whole to BTC Now and never enter the Holder’s stream, so they carry no servicing fee. At the base terms $1,475, 2.46% of the Holder’s capital.

Input #3, posted as OriginationFee. Marc, 2026-08-31: BTC Now is always Holder 0 and the first payment flows to it. The Holder’s stream begins at payment N+1; the payments are still in the Buyer’s paid-in total. Setting N to 0 recovers the pre-program case the M0 fixtures and the memo-config badge use. The identity n_payments_identity says Holder cash at N differs from N = 0 by \(\sum_{k \le N} p_k (1-f)\).

Where it is derived

Flat annual

Also: FlatAnnual, Annualized default %/yr, Flat %/yr, constant annual stop rate.

A constant annual stop rate converted to the monthly hazard 1 − (1 − r)^(1/12), the same at every reachable age. Rate semantics, not a lifetime target: 10% a year gives 0.8742% a month and 40.4% over a 60-month term.

DefaultScenario::FlatAnnual { annual_rate }, API and the backtest page only. The backtest calls it “the framework”.

[ h = 1 - (1 - r_{\text{annual}})^{1/12} ]

Where it is derived

Flat surface

Also: Flat 43%, flat preset, flat vol.

The preset surface with one vol at every tenor and strike: 43%/yr, the cockpit’s base bridge volatility, no skew. Under it every option is plain Black-Scholes at 43%.

VolSurface::flat(0.43) on tenors 1, 3, 6, 12, 24 months and moneyness 0.50 to 1.30. It is the control: run the placement on the flat surface and on the stylised skew and the difference is what the skew alone does to the ladder.

Where it is derived

Flat-path markup

Also: markup, flat_path_markup, markup per Agreement.

The completing Agreement’s net gain to the Holder on a flat price path, per Agreement at cohort-1 terms: (the schedule’s total − the first N payments) × (1 − fee) − the purchase price, cents-exact.

surface::flat_path_markup(config). It is what the Holder earns if nobody stops and the price never moves — the paper’s gross margin before behavior. The paper’s implied vol is the flat vol at which this markup exactly pays for the weighted ladder.

Where it is derived

FlowFee posting

Also: flow_fees, Servicing fee → BTC Now, servicing take.

Buyer or Market → BTC Now with every delivery: the flat 5% on the delivered dollar, whether a scheduled payment, an early-completion payoff or a stop-sale delivery. $655,556.23 on the historical early-September reference book.

Computed as the difference of two rounded cumulatives (convention C2), so any single posting may wobble a cent while the lifetime fee per Agreement is exact. Summed as flow_fees.

Where it is derived

Free coin

Also: free coins, free_coins, the Holder’s other book, cross-book collar.

BTC held separately from the Agreement book. The legacy cross-book collar uses call premium on those external coins to support puts on the Agreements; it does not report complete combined-book performance.

The legacy CrossBookCollar Agreement-sleeve distribution includes call premium and Agreement put flows, but excludes the external coins’ value and written-call liability. Call assignment is shown separately on a leg and is not charged to that sleeve’s distribution. Paired Research and explicit Coin sweep/monthly requests reject this incomplete combined-book strategy. The Buyer’s promised BTC cannot cover a Holder’s written call.

Where it is derived

Frontier family

Also: frontier_mus, frontiers, Walk-away frontier family, FrontierFamily, Believed drift 1 … k.

The frontier drawn for several believed drifts on the same axes as the two lines: by default μ = 0, 10%, 25% and 50% a year, 1 to 8 allowed. The family brackets the book; each curve is the walk-away price of one belief.

The distance between a curve and the capital line at a month is the price fall that has to happen before that Buyer’s stop becomes the Holder’s loss. The zero-drift Buyer walks above the capital line from the first month; the 25% Buyer sits below it through payment 26 and above it after; the 50% Buyer walks almost nowhere. The other lattice parameters come from frontier_params, else the config’s rational_boundary, else the defaults.

Where it is derived

Funding rate

Also: funding_rate, funding rate %/yr, risk-free rate.

The desk’s dollar rate, annual, continuously compounded (default 4.5%/yr): the rate the placement’s puts discount at, and the rate the risk-neutral paths drift at and their flows discount at.

Request field funding_rate on the placement and fair-value endpoints. It is not the perpetual’s funding rate — that lives in the perp delta hedge spec — and it does not enter the hedge overlay’s option prices, which are at zero rate by the surface’s quoting convention. Under the risk-neutral measure the coin’s expected growth is this rate, whatever the config’s drift says.

Where it is derived

G

Gamma

Also: gamma_usd_per_pct2, Γ, curvature.

The second difference of net gain per (1%)²: the change in delta per 1% move. −$175.6 on the base configuration: the loss accelerates into a drawdown, the convexity of a short put.

Computed from the same three runs as delta, averaged over the ensemble. As of month 48 it falls to −59.

[ \Gamma = \frac{1}{k}\sum_s \frac{G_s(1+b) - 2,G_s(1) + G_s(1-b)}{(100,b)^2} ]

Where it is derived

GBM

Also: geometric Brownian motion, Gbm, Brownian motion.

Geometric Brownian motion at monthly steps: the price model a desk brings with its own drift and vol. Log return per month = (μ − σ²/2)/12 + σ√(1/12)·z, so E[S_t] = S₀·e^{μt}. Free at the far end, unlike the bridge.

PathMode::Gbm { mu_annual, vol_annual } (Phase 2). The −σ²/2 is the Itô correction that makes μ the expected growth rate of the price, not of its log. vol_annual = 0 is the deterministic exponential ramp at μ. The diffusion draws are taken from the run’s stream first, one per month, so a jump-diffusion at λ = 0 or a regime switch at equal vols is this path exactly on the same seed. The fair value runs the engine under GBM at the funding rate.

[ \ln \frac{S_{t+1}}{S_t} = \frac{\mu - \sigma^2/2}{12} + \sigma\sqrt{\tfrac{1}{12}}, z_t, \qquad z_t \sim N(0,1) ]

Where it is derived

Greeks

Also: the Greeks, Greeks by bump-and-revalue, sensitivities.

Sensitivities of the Holder’s net cash gain to a bump in the future path: delta, gamma, vega, theta, and delta and vega of the IRR. The engine is re-run bumped and finite differences are taken over a seed ensemble.

exposure.rs::greeks. Seven runs per seed: base, price up and down, commitment up and down, vol up and down, for 16 seeds by default. Signs to expect on a stopping book: positive delta, negative gamma, positive theta; vega negative where the stops are underwater and positive where the surplus dominates. The base IRR in the Greeks (15.86%) is an ensemble mean, not the seed-42 run’s 14.21%.

Where it is derived

H

Haircut

Also: static haircut, liquidation haircut, Haircut % (sale below spot), sale below spot.

An extra discount on the stop sale below the path price, stored in log units: the sale executes at spot × e^(−h). Default 0 (Marc, 2026-07-12): a one-coin sale has no market impact. A skeptic’s dial.

Input #15. The cockpit shows a percent and stores \(h = -\ln(1 - \text{pct})\); the tornado’s “Stop-sale haircut 50%” is \(h = 0.693\). Until Phase 4 adds a market-impact model, the haircut is how a Holder prices synchronized stops in a crash.

Where it is derived

Hazard

Also: monthly hazard, h_t, hazard_monthly, stop hazard, default hazard.

The probability that an Agreement which has just made payment t stops before payment t+1. A vector by payment age, zero at the final payment date, built once per run from the stop scenario and drawn against each month.

defaults.rs::DefaultScenario::monthly_hazard; the simulate response carries it as hazard_monthly. At the base terms and the 40% prior: 0.977% a month at ages 1–3, 1.953% at 4–15, 1.172% at 16–24, 0.684% at 25–36, 0.244% at 37–59. The drawdown multipliers scale it by state; rational mode moves where its stops land.

[ h_t = \min(1, K, w_t), \qquad t = 1, \dots, n-1, \qquad h_n = 0 ]

Where it is derived

health

Also: GET /health, OK, liveness.

Liveness: GET /health answers the plain text OK with status 200. The launcher polls it once a second for up to thirty seconds after starting the engine; the cockpit does not call it.

A deploy check should. The engine listens on port 8080 and the web app on 3000.

Where it is derived

Health route

Also: /api/forwardflow/health, health, liveness.

GET /api/forwardflow/health, open in both modes: {"status":"ok","engine_spec":"v1.10","version":"<crate version>","mode":"open"|"keyed"}. The plain GET /health answers OK for a deploy check.

The one route that tells a caller which mode the engine is in before any credential is sent.

Where it is derived

heatmap

Also: POST /api/forwardflow/heatmap, break-even heatmap, Where it breaks, GridSpec, PriceDefault, VolConviction.

A sensitivity grid, every cell a full run of the current book at the configuration’s seed with two assumptions swapped, up to 900 cells: price × lifetime stop share, or volatility × conviction depth. A failed cell is a hole, never a 400.

forwardflow_api.rs::ff_heatmap, cells in parallel. The cockpit’s price axis runs 0.25 to 2.0 times the start price against priors from 80% to 0%; the vol axis 10% to 100% against depths 10% to 80%. Blue cells are positive, red negative; the bold frontier is IRR = 0. Click a cell to load that scenario. Chip S11.

Where it is derived

Hedge coverage

Also: coverage, coins per Agreement, density, share of the proxy delta.

The option notional per one-BTC Agreement, or a stated share of a modeled USD or Coin delta. Coverage sizes a trade; it does not guarantee a return or complete capital protection.

Fixed coverage scales option premium, payoff and execution cost linearly. A coverage of 1 means one underlying BTC of option notional per Agreement, subject to its strike, tenor and roll rules. Delta-based coverage uses the chosen sensitivity at inception or rebalance. Generic sizing supports values up to 2 where permitted by the selected field. Contract notional, funded BTC capital and price sensitivity are different quantities; a non-par purchase changes capital without changing the Agreement’s one-BTC notional.

Where it is derived

Hedge desk

Also: /forwardflow/hedge, the Hedge desk, derivatives desk.

The derivatives desk’s page at /forwardflow/hedge (FUND_DESK_PLAN Phases 2–3): the embedded put ladder on a vol surface, the risk-neutral fair value, the hedge overlay on the paper’s own seeded paths, and the benchmarks.

Manual Run. It reads the same engine the workbench runs (forwardflow::surface and forwardflow::hedge) and adds nothing to the ledger: the surface, the fair value and every hedge are the Holder’s own view laid over the paper. Its headline is the paper’s implied vol beside the market’s and the hedged excess over basis. Exports are stamped with the config hash, the seed and the engine spec version.

Where it is derived

Hedge leg

Also: leg, HedgeLeg, paid, received, both sides of every leg.

One component of a hedge, averaged across the sample. Its debit and credit fields include premium, costs, settlements and signed open derivative values; a marked value is not cash paid or received.

HedgeLeg reports paid_mean_usd and received_mean_usd as legacy economic debit/credit fields. A leg labelled ‘(open at the horizon, marked)’ is an unrealized asset or liability. Read gross leg amounts to understand a spread or collar, and use the explicit realized-settlement and horizon-mark totals to distinguish cash from terminal valuation.

Where it is derived

Hedge overlay

Also: overlay, hedge_overlay, /api/forwardflow/hedge, the overlay.

Hedge cash flows laid over the SAME seeded paths the paper runs on: per seed the paper runs, the structure’s monthly dollar flows are built on that run’s path and book, added to the Holder’s flows, and the IRR of the combined flow is taken.

hedge::hedge_overlay(config, surface, spec, exec_cost_bps, seeds, pricing) behind POST /api/forwardflow/hedge (up to 8 structures per request). Nothing touches the ledger: the hedge is the Holder’s own act, in f64 dollars, quantized to cents only where it joins the paper’s flows. Every option is priced off the surface at its inception spot, strike and CONTRACTUAL tenor, at the option pricing’s rate; an option still open at the run’s last month is marked there at its remaining-life model value on the surface at that month’s spot, on a leg labelled “(open at the horizon, marked)” — never settled at intrinsic on a shortened life. The result is the unhedged and hedged distributions, the cost and floor figures, both sides of every leg, and the conventions in notes.

Where it is derived

Hedge preset

Also: presets, preset shelf, HedgePreset, presets from the engine.

The shelf of fifteen named structures the engine ships with every hedge response — the risk paper’s menu, seat-prefixed: four for the dollar seat, seven for the coin seat, the pair for both, and three other instruments.

hedge::presets(). A preset is a starting point, not a recommendation — every parameter is editable once chosen, and since v1.9 each is a list of legs. BTC Now’s own posture is labelled as such on the shelf and is one preset among several.

Where it is derived

Hedge ratio

Also: ratio, share of the coin delta.

The fraction of modeled coin delta covered by CoinDeltaFutures, from 0 to 2; 1 means the full modeled delta. This is a sizing assumption, not a universally preferred setting.

Increasing coverage changes exposure, basis, execution and margin requirements. The ratio that improves one sampled downside measure can worsen another, and depends on the market process, Buyer behavior and funding assumptions. Evaluate matching paths and distinct evaluation seeds; a sampled minimum or p5 is not a guaranteed floor.

Where it is derived

Hedge spec

Also: structure, HedgeSpec, hedge structure, instrument.

One hedge structure and its parameters, externally tagged on the wire: PutLadder, PutSpread, RollingPuts, PerpDeltaHedge, VarianceSwap or CrossBookCollar. Each is validated by named input before it runs.

hedge::HedgeSpec. Static puts on the paper (one put per Agreement × coverage, struck at a share of entry, held to expiry); a put spread (long the higher strike, short the lower); rolling short-dated puts struck at a share of the spot at each roll; short perpetuals reset to coverage × the proxy delta at every rebalance, with funding and margin carry; long variance at the surface’s ATM vol, rolled through the horizon; and the cross-book collar, calls on free coin paying for puts on the paper.

Where it is derived

Hedge strike

Also: strike, % of entry, strike_pct_of_entry, strike_pct_of_spot, long_strike_pct, short_strike_pct, call_strike_pct_of_spot, put_strike_pct_of_entry.

An option leg’s strike as a fraction of a reference price: of each Agreement’s entry price for the static puts (0.70 = the 70% floor), of the spot at each roll for rolling puts and written calls (0.85, 1.30). 0.01–3.00; calls to 5.00.

The entry price is the price the Agreement struck at, so a put at 70% of entry protects the Holder below the point where the stop sale starts to fall short of the schedule’s early ages. The put spread’s long strike must exceed its short. The surface is read at the leg’s own K/S, so a lower strike reads the skew’s put wing.

Where it is derived

Hedge tenor

Also: tenor (months), tenor_months, tenor of each swap, tenor of each put.

An instrument’s time to expiry in months, 1–120, always priced whole. Static structures hold to expiry — one still open at the horizon is marked there at its remaining life; rolling puts, the variance swap and the collar’s calls are re-struck every tenor while the position is live.

The surface is read at this tenor, so a 3-month rolling put pays the short-dated vol twelve times in three years while a 12-month ladder pays the 12-month vol once. On the ladder table, the tenor column is the leg’s expiry: the missed payment date t + 1.

Where it is derived

Hedge-mark bucket

Also: hedge mark, futures_mark, hedge settlement bucket, basis bucket.

Monthly realized futures/perpetual P&L: the held position times the spot move. Futures settle monthly, so this is cash. Open option/swap value is reported separately.

The attribution’s hedge-settlement bucket carries premium and execution cost. Option-value attribution combines the change in signed option/swap value with the realized settlements replacing that value. Basis carries futures basis, funding and margin carry. These attribution buckets differ from the raw cash component named hedge_settlement; use the native BTC ledger when reconciling realized cash. The horizon derivative mark belongs to economic outcome once and is not cash received.

Where it is derived

Hedged distribution

Also: hedged, Hedged — paper plus the structure, Dist, distribution.

The Holder’s outcome over the seeds with the structure’s flows added to the paper’s: median, mean, p5 and p95 effective annual IRR and the share below zero over the seeds with an IRR; the median and p5 multiple and the cash-loss share over every seed; and the seeds with, and without, an IRR.

hedge::Dist on the hedged side of a HedgeResult. Compare currency, sample count, unavailable/root diagnostics and the unhedged distribution on the same seeds. A p5 is a sampled percentile. USD multiples put negative hedge flows in gross outflows; legacy Coin multiples use a paper-purchase denominator and signed hedge numerator. Neither is a complete wallet return.

Where it is derived

Hedged excess over basis

Also: excess over basis, hedged_excess_over_basis_pp, the market-neutral desk’s number.

Hedged median USD IRR minus the requested annual cash-and-carry basis rate, in percentage points. It is a USD reference comparison, not BTC return above holding.

HedgeResult.hedged_excess_over_basis_pp is populated for structures classified as market-neutral. The comparison basis_rate is separate from the futures leg’s modeled locked/after cost schedule. A negative value means the modeled USD return did not beat this reference on the sampled paths. The reference does not establish equal capital, equal funding needs or absence of counterparty and execution risk.

[ \text{excess}{pp} = 100 \times \big(\text{median IRR}{hedged} - r_{basis}\big) ]

Where it is derived

Hedged p5

Also: floor, floor_p5_irr, p5 floor, the floor.

The selected reporting unit’s fifth-percentile annual cash-flow IRR, conditional on paths with a valid IRR. It is a sampled downside statistic, not a guaranteed minimum.

The legacy HedgeResult.floor_p5_irr field is always USD. A Coin view uses hedged_coin.p5_irr and must select adverse paths using a Coin metric. Report how many sample paths have valid IRRs; a path can lie below p5 and further samples can move it. A put strike does not guarantee the combined Agreement-and-hedge return.

Where it is derived

Histogram

Also: irr_histogram, IRR histogram, bins.

The distribution of effective IRR over the runs in 50 equal-width bins from the worst to the best, reported as (left edge, count) pairs. Counts sum to the runs that had an IRR.

The bin width is floored at \(10^{-12}\) so a degenerate distribution does not divide by zero. The cockpit’s outcome panel draws it above the percentile strip.

Where it is derived

Historical replay

Also: replay, HistoricalReplay, Start month, start_index.

Bitcoin’s actual monthly closes from a chosen start month, rescaled so month 0 equals the start price: history’s shape at your level. Seed-free; it carries whatever drift history had, so replay figures are descriptive, not probabilistic.

paths.rs::replay, \(P_k = P_0 \cdot C_{s+k}/C_s\). The run needs \(H + 1\) bars from the start index; otherwise the API returns 400 InsufficientHistory. With 174 bars a single 60-month vintage has 113 feasible starts and the paced book 90. The cockpit’s mode switch lands on index 21, November 2013; chip S9 replays the 2013 top.

Where it is derived

history

Also: GET /api/forwardflow/history, embedded series, 174 bars, historical closes.

The embedded monthly Bitcoin bars compiled into the engine: 174 months and closes from February 2012 ($4.90) to July 2026 ($62,875.50). The cockpit uses it to label replay start months and grey out those without enough history.

paths.rs::historical_closes and historical_months, parsed once from backend/data/btc_historical_monthly.csv via include_str!; no data files at runtime. Index 59 is January 2017, index 149 July 2024.

Where it is derived

Hold strikes

Also: hold_strikes, strikes held, strikes floating.

A bump option: true strikes every cohort off the path as it stood before the bump, so only the coin moves; false lets cohorts in bumped months strike at the bumped prices. The Greeks get ‘held’ by stopping origination instead.

The intramonth dispersion spread is derived from the strike path when strikes are held, so a bump does not change the spread either. exposure.rs::greeks runs every bump with hold_strikes: false and freezes the existing book with origination_stop_month; the commitment delta is the same bump on the full pacing.

Where it is derived

Holder

Also: Holders, receiving side, Owner, owner, Purchaser, forward-flow buyer.

The receiving side of an Agreement: the party that bought the paper and collects the schedule. Holder 0 is always BTC Now; the receiving side changes hands by Sale. The ledger calls the Holder Owner.

The Holder pays the purchase price of the paper (par, $60,000 on the base coin) and receives 95% of every dollar delivered after the first payment. It never receives the coin: a stop delivers dollars, and completion delivers the coin to the Buyer. Every output the engine reports is the Holder’s. Older documents say “Purchaser”, “the fund” or “forward-flow buyer”; all three now mean the Holder.

Where it is derived

Holder 0

Also: Holder zero, BTC Now as Holder 0.

BTC Now, always. Every Agreement opens with BTC Now on the receiving side; the first payment flows to it whole, and it sells the receiving side to a Holder by a Primary Sale.

Marc, 2026-08-31: “BTC Now always first owner”. In the engine the Holder pays BTC Now at origination and payment 1 arrives a month later, which collapses the Primary Sale onto the origination date; that is the conservative reading for the Holder, who pays before the first payment it does not receive. Whether BTC Now sits in the ledger as Holder 0 with the first payment as its own is an open term-sheet question (limits, item 3).

Where it is derived

Horizon

Also: H, simulated window, 84 months.

The last simulated month: (cohorts that originate − 1) + term + the settlement tail, ⌊lag/30.4375⌋ + 1 months (one at the program’s 18 days) — or the market horizon when the configuration sets one. One vintage at 60 months runs to month 61; the base paced book of 24 cohorts to month 84, so the path has 85 marks.

engine.rs::SimConfig::horizon. The tail (engine.rs::settlement_tail_months; spec v1.5 change 4 for the first month, widened 2026-09-05 for lags past a month) lets a stop at the last stoppable payment date price and settle inside the ledger rather than be clamped; it also costs the backtest a vintage per tail month, since a vintage is seasoned only when term + tail months of history follow it. market_horizon_months, when set, pins the path’s last month so a pacing change cannot move the bridge’s endpoint date.

[ H = \max\big(H_{\text{market}},\ (\text{cohorts} - 1) + \text{term} + \lfloor \text{lag}/30.4375 \rfloor + 1\big) ]

Where it is derived

I

Implied financing rate

Also: paper rate, implied rate, financing rate, implied monthly rate.

The monthly rate at which a level payment of PMT over the term has present value equal to the strike: 1.375% a month at the base terms, 16.50% nominal, 17.80% effective. Display and boundary only; no ledger amount depends on it.

contract.rs::ContractTerms::implied_monthly_rate solves the ordinary annuity identity by bisection on \(i \in [-0.5, 1.0]\), with the unquantized PMT. A multiple of 1.0 gives zero and a multiple below 1.0 a negative rate (0.90× at 60 months: about −4.07% nominal). It is not the Holder’s return: the Holder pays par, misses the first payment and gives up 5%, so the flat run prints 13.97% effective against the paper’s 17.80%.

[ \text{PMT} = \text{strike} \cdot \frac{i,(1+i)^n}{(1+i)^n - 1} ]

Where it is derived

Implied vol

Also: implied volatility, IV, vol.

The annual volatility that, put into Black-Scholes, reproduces an option’s market price — the number a surface stores at each tenor and strike, as an annual fraction (0.43 = 43%/yr).

Implied vol is the market’s price of an option restated in volatility units, so options across strikes and tenors compare on one scale. The surface stores it; the ladder, the hedge legs and the benchmarks’ calls each read the vol at their own strike and tenor. It is distinct from realized vol, which is measured from a path after the fact, and from the paper’s implied vol, which is the flat vol the paper’s markup pays for.

Where it is derived

In-the-money stop

Also: stop in the money, winning Buyer stops.

A stop while the coin is worth more than the Purchase Price: the Holder is paid the schedule, the Buyer refunded in full, and the surplus stays with the Holder. How often this happens is the least-evidenced number in the model.

The engine produces such stops at half the baseline hazard with the multipliers on and at the full hazard with them off; neither rate has been observed. The rational robot never stops with a surplus. On Bitcoin’s history a handful per vintage moved the single-vintage replay median from 38.2% to 47.9% between two runs of the same morning. Model Card §6 asks that this sentence be attached to every replay figure.

Where it is derived

Inception

Also: Inception, AtOrigination, AtMonth, inception month, when the hedge is put on.

When a static hedge is put on: at each origination (every cohort hedges at its own month and spot, off each Agreement’s entry price), or the book at a month (everything on the books then hedges at once; later Agreements stay unhedged).

hedge::Inception. The put ladder, the put spread and the collar’s puts take an inception; the rolling puts, the perp hedge and the variance swap are rolled through the horizon and have none. An AtMonth past the horizon is rejected by name.

Where it is derived

include_postings

Also: include_agreements, raw postings flag.

Simulate request flags. include_postings (default false) attaches the raw double-entry ledger, which the audit needs; include_agreements (default true) attaches the per-Agreement table, about 106 KB of the base book’s 112 KB body.

Side-runs that only read scalars pass include_agreements: false and read agreement_count instead; the tornado, the heatmap and the solver do. A reproduction posts the stamped configuration with postings on and runs the tie-out.

Where it is derived

Initial margin

Also: initial margin, % of notional, initial_margin_pct, margin carry.

The margin the short perpetual posts as a fraction of its notional, 0–1. Its cost is the carry of that capital at the perp’s funding rate, charged monthly and reported as the leg margin carry.

Margin is not lost — it is capital tied up. The overlay charges only its carry, not the margin itself, so the hedged flows show the cost of holding the capital idle rather than the capital moving in and out. No forced close is modelled: the position is held whatever the mark.

Where it is derived

Intramonth proxy

Also: intramonth_call, intramonth call, 18-day sigma, 18-day convention.

The call met inside the month, on the paper’s 18-day convention: the month’s mark re-read on its log move scaled by √(18/30.4375) ≈ 0.769, plus the basis, floored at zero. A proxy for a typical 18-day move, not the path’s own worst point.

The engine is monthly and cannot see the month’s path; the month-end mark is the call actually settled. On the base configuration’s median seed the peak call is $349,793 against a proxy of $261,053 (test).

[ \text{intramonth}_m = \max\Big(0,, -\Big(\text{mark}_m,\frac{e^{kr}-1}{e^{r}-1} + \text{basis}_m\Big)\Big), \qquad k = \sqrt{18/30.4375} ]

Where it is derived

Intramonth strike dispersion

Also: dispersion, strike dispersion, intramonth_strike_dispersion, dispersed strikes.

Each Agreement draws its own entry price log-normally around its month’s mark, mean-preserving, with a spread of the monthly volatility of the twelve months before its cohort month over √2 — the path mode’s stated volatility with fewer than three returns behind it; zero on a flat path. On in the cockpit since 2026-07-13, off in the engine default.

Input #20 (engine.rs::entry_sigma_monthly). An entry executed at a uniformly random time inside the month sees half the month’s variance on average. On a 43% bridge the first three cohorts disperse at 8.8%, later ones at the trailing year’s realized figure. The spread reads only what is known at entry (2026-09-05, the audit’s finding 2): before, the whole generated path’s volatility fed it, so a crash placed at month 24 moved the strikes struck at month 0. The draw comes from the Agreement’s own stream. The purpose is behavioral: thresholds fire across a cohort over a range of prices instead of all at once for ten identical twins. Dispersion changes Agreement terms and behavioral thresholds, so it can change realized receipts and returns. Mean preservation before cent rounding does not preserve every book’s outcome; par Coin funding remains one BTC per Agreement at its own entry price.

[ \text{strike} = \text{cents}\Big(S_m \exp\big(\sigma_{\text{intra}} z - \tfrac{1}{2}\sigma_{\text{intra}}^2\big)\Big), \qquad \sigma_{\text{intra}} = \sigma_m/\sqrt{2} ]

Where it is derived

Intrinsic shortfall

Also: intrinsic_shortfall_usd, intrinsic loss.

Over Agreements below the schedule line at a month, Σ (R_t − V_m): the book’s intrinsic loss if every one of them stopped at this month’s price. $3.18m at month 12, a peak of $3.73m at month 21 on the base path.

The intrinsic value of the embedded put ladder, marked at the path. It is a what-if, not a realized figure; the realized book shortfall is the credit table’s EL.

[ \text{intrinsic shortfall}m = \sum{\text{below schedule}} (R_t - V_m) ]

Where it is derived

IRR ambiguity

Also: irr_ambiguous, irr_root_count, seeds_irr_ambiguous, multiple roots, several roots.

A flow with several sign changes can have several IRRs; the engine reports the flag and the count, the cockpit shows them beside the figure and keeps them in exports, and the hedged root nearest the unhedged rate is the stated convention.

outputs.rs::irr_analysis (model audit 2026-09-06, M02) classifies the flow and searches a finite rate grid with extremum checks; it does not certify that every mathematical root was found. Roots outside the supported monthly range or three tightly clustered roots inside one grid cell can be missed. The flags count the located roots; RunOutputs.irr_ambiguous / irr_root_count and Dist.seeds_irr_ambiguous carry it. Model audit 2026-09-07 (R09): the cockpit declared the fields and readIrr still returned an ambiguous 10.00% unqualified, so a root chosen under a convention read as an unambiguous outcome; the qualification now travels with the figure on every page and in exports, the cash multiple and net cash beside it.

Where it is derived

J

Jump rate

Also: λ, jump_rate_annual, jumps per year.

λ, the expected number of jumps per year in a jump-diffusion (2.0 = two a year on average). Each month’s jump count is Poisson(λ/12). At λ = 0 no extra draw is taken and the path is GBM’s.

Named-input validated: finite and non-negative. The compensator the drift absorbs scales with λ, so a higher jump rate under a negative mean jump does not lower the expected price — it widens the distribution around the same μ.

Where it is derived

Jump-diffusion

Also: JumpDiffusion, jump diffusion, diffusion σ.

GBM plus a Poisson stream of jumps: each month’s count is Poisson(λ/12), each jump adds one log-size draw from the jump law (Merton or Kou). The drift is compensated so E[S_t] = S₀·e^{μt} holds with the jumps in; λ = 0 is GBM exactly.

PathMode::JumpDiffusion { mu_annual, vol_annual, jump_rate_annual, jump }. The diffusion σ is the volatility of the continuous part only; the jumps add their own variance on top. The compensator λ·(E[e^J] − 1)/12 is subtracted from each month’s log step. The Poisson count uses Knuth’s product method on the run’s uniforms so the draw sequence is fixed by the engine, not by a library.

[ \ln \frac{S_{t+1}}{S_t} = \frac{\mu - \sigma^2/2 - \lambda,(E[e^J]-1)}{12} + \sigma\sqrt{\tfrac{1}{12}}, z_t + \sum_{i=1}^{N_t} J_i, \qquad N_t \sim \text{Poisson}(\lambda/12) ]

Where it is derived

K

Keyed mode

Also: keyed, FF_API_KEYS set.

The engine with keys in FF_API_KEYS: every /api/forwardflow/* route needs X-API-Key (on the websocket, a token will do) except health and openapi.json. Bad or missing credentials get 401 JSON naming what is missing, never a value.

The hosted engine at btcnow-forwardflow.fly.dev runs keyed; health reports "mode":"keyed". Limits are counted per key and a success carries X-Key-Name.

Where it is derived

Kou jumps

Also: Kou, JumpKind::Kou, double exponential jumps.

Kou (2002) double exponential: a jump is up with probability p_up, size Exponential(η₊); else down, size Exponential(η₋). Mean up-jump 1/η₊, mean down-jump 1/η₋ in log terms. Needs η₊ > 1 for the compensator to exist.

JumpKind::Kou { p_up, eta_up, eta_down } — η₊ = 10 is a mean +10% log up-jump, η₋ = 4 a mean −25% log down-jump. E[e^J] = p·η₊/(η₊ − 1) + (1 − p)·η₋/(η₋ + 1). The asymmetry lets down-jumps be fat and up-jumps thin, the shape a crypto desk usually wants.

Where it is derived

L

Ladder leg

Also: leg, LadderLeg, age, payment age.

One put of the embedded ladder: the stop at payment age t (t payments made, the next missed), struck at R_t, expiring at month t + 1, with its weight, its S₀/K and K/S, the surface’s vol there and the put’s dollar value.

surface::LadderLegmonth, strike_usd, tenor_months, weight, moneyness (S₀/K, the engine’s convention), implied_vol and put_value_usd. The ladder table on the Hedge desk is one row per leg; the last column, W × put, is the leg’s contribution to the ladder value.

Where it is derived

Ladder value

Also: W × put, ladder_value_usd_per_agreement, weighted ladder.

What the surface says the Holder’s short puts are worth per Agreement: Σ weight × put over the ladder’s legs, each leg counted with the probability the Buyer stops there.

Read beside the flat-path markup: where the markup exceeds the ladder value, the paper’s price more than pays for the puts the Holder is short at the surface’s vols; where the ladder is worth more, the market values those puts above what the paper pays. Their crossing in flat vol is the paper’s implied vol.

Where it is derived

Lattice

Also: the lattice, Lattice sigma %/yr, sigma_annual, CRR, backward induction, optimal stopping.

The optimal-stopping grid behind the walk-away frontier: Cox-Ross-Rubinstein steps in log-spot, four a month, on a full-width grid, decisions at payment dates only, under the Buyer’s drift, volatility and discount rate.

boundary.rs::rational_frontier. Lattice volatility defaults to 41.4%, the trailing-24-month realized; \(\Delta = 1/48\) year, \(u = e^{\sigma\sqrt\Delta} = 1.0616\), \(q = (e^{\mu\Delta} - d)/(u - d)\). The grid is full width rather than a tree from a single root so a coin that halved at payment 1 is visible; the Phase 1 review found that a rooted tree kept such a Buyer paying until payment 4. At each date the Buyer picks the best of pay, settle and walk.

[ u = e^{\sigma\sqrt{\Delta}}, \quad d = 1/u, \quad q = \frac{e^{\mu\Delta} - d}{u - d}, \quad \Delta = \tfrac{1}{48} ]

Where it is derived

Ledger

Also: the ledger, double-entry ledger, double-entry.

An append-only list of postings in exact cents. Every dollar in a run is one transfer between two of four entities: the Holder, BTC Now, a Buyer, and the Market. Both sides are written at once, so conservation holds by construction.

ledger.rs::Ledger. An amount must be cent-quantized and non-negative or the call panics; a zero amount is dropped. verify_balances recomputes every balance from the raw postings, which is what the in-browser auditor does in TypeScript. The coin is never on the ledger; only dollars are.

Where it is derived

Leg

Also: legs, Leg::Futures, Leg::Option.

One position in a generic structure: listed futures per on-book Agreement (side, sizing, basis, margin) or a European option per Agreement (kind, side, strike, tenor, roll, coverage). Each posts into the one set of flows as leg N: ….

hedge.rs::Leg has two variants, externally tagged on the wire. A futures leg’s sizing is DollarDelta, CoinDelta or Coins, resolved at each rebalance; an option leg’s strike and coverage are resolved at each inception. A one-leg structure reproduces the fixed shape it copies bit for bit.

Where it is derived

Legs structure

Also: HedgeSpec::Legs, generic structure, Legs.

HedgeSpec::Legs { legs }: a hedge built as a list of legs rather than a fixed shape. The paper’s whole menu — loss-line ladders, the market-neutral short, coin-delta futures, spreads, the split and the pair — is written this way.

Three predicates read the list: is_coin_seat (a coin-delta futures leg or a coin-delta coverage), revalues_the_ladder_monthly (anything sized at the dollar delta) and is_market_neutral (a short futures leg at the dollar delta). Eleven of the fifteen presets are Legs structures.

Where it is derived

LGD

Also: loss given default, loss given stop, Shortfall ÷ remaining schedule.

Σ shortfall over Σ remaining schedule at the stop, across the vintage’s stops: the share of the put’s strike that was lost. 0.225 on the historical early-September reference book; 0.531 for cohort 1, 0.043 for cohort 23.

Measured against the schedule, the put’s strike, not against the Holder’s capital; the capital-loss column is the par Holder’s version.

[ \text{LGD} = \frac{\sum \text{shortfall}}{\sum R_t} ]

Where it is derived

Lifecycle

Also: state_at, Performing, StoppedAwaitingSale, SoldAwaitingCash, Closed, price_exposed_at, stop_sale_timing, one lifecycle.

The one state of an Agreement at a month that every consumer reads: performing; stopped awaiting sale (the coin still held — price exposure); sold awaiting cash (the month the sale’s cash lands — a receivable, no price exposure); closed.

engine.rs::state_at, with price_exposed_at and price_exposure_end read off it. The timing is engine.rs::stop_sale_timing, the stop’s own: pos = D + lag/30.4375; the sale executes inside ⌊pos⌋ and is booked at ⌈pos⌉, the first payment date at or after the sale point — never before the last mark that priced it (model audit 2026-09-07, R02) — where the delivery, the fee and the refund post. Coverage, capital at risk, the ladder, the hedge desk’s on-book test and the monthly mark all read it, so a stopped Agreement leaves the three panels together (model audit 2026-09-06, M04 — before, risk and hedges dropped it at missed + 1 regardless of the lag while the mark carried it to the sale).

Where it is derived

Lifetime stop prior

Also: lifetime, Lifetime default %, Lifetime stop %, lifetime target, stop prior, 40%.

The share of the original book that ever stops over the term on a flat path: 40% in the program’s pricing stance, 70% in the Model Card’s Panel B. The baseline curve is scaled by bisection so a flat path lands it exactly.

Input #7, BaselineCurve { lifetime }, accepted in [0%, 100%). No BTC Now vintage has been observed, so it is a prior on an unscreened population, not a measurement. With drawdown multipliers on the realized share becomes path-dependent by design. With a custom timing curve it is the sum of the per-year bars.

[ 1 - \prod_{t=1}^{n-1}\bigl(1 - \min(1, K,w_t)\bigr) = \text{lifetime} ]

Where it is derived

Lockout

Also: lockouts, six-month lockout, seven-day lockout.

Two program lockouts reinstated 2026-09-03: seven days after letting two 60-minute price windows lapse, six months after a stop. Neither is modelled; the six-month one enters only through the rational boundary’s walk cost.

The engine has no notion of a person across Agreements and no re-entry, so a Buyer who stops is simply gone. The walk cost of input #25 (2.5% of the coin’s cost, about one payment) lumps the lockout, the re-strike at market and the lost access into one number on the Buyer’s side of the decision. The July lockout ladder and the rescission ladder remain dead.

Where it is derived

Loss-line strike

Also: LossLineAtExpiry, loss line at expiry, the Holder’s loss line.

A put strike set from the Holder’s modeled unrecovered cost at the option’s expiry age. It changes with the terms, fees, purchase price and elapsed payments.

Strike::LossLineAtExpiry reads the configured capital line. A contractual strike does not guarantee the combined investment’s return: premium, coverage, stops, tenor gaps and execution still matter. A zero resolved strike means no put is bought under this rule.

Where it is derived

Lost-conviction rule

Also: conviction rule, conviction, X% below entry price, Y consecutive months, capitulation, Conviction exits, Conviction walks.

Deterministic capitulation: a Buyer whose coin has sat X% below the entry price for Y consecutive payment dates walks at the next one. Off by default; X = 50%, Y = 6 when on. Measured against the strike, never the obligation.

Input #11, spec §4.3. A breaching month extends the streak, a non-breaching month resets it; when the streak reaches Y a walk is armed for the next payment date and executes there as the missed payment. Nobody starts underwater: at signing the coin is the entry price. BTC moves about 12% in an ordinary month, so X below about 25% reads normal volatility as capitulation. Chip S4 sets X = 0, Y = 1; the tornado’s behavioral floor X = 0, Y = 2.

[ S_t < (1 - X),\text{strike} ]

Where it is derived

M

M0 fixtures

Also: fixtures, M0_FIXTURES.md, closed-form fixtures.

Closed-form contract-math figures computed on 10 July 2026 in exact rational arithmetic before the engine existed, sharing no code with it: a flat path, no exits, $60,000, 1.475×, a 105% purchase price and the July fee basis.

The engine must reproduce them within two cents. They keep their historical fee by pinning the flat rate per term (3.75% at 60 months); regenerating them at 5% would cost their independence. Their 60-month N = 0 effective IRR of 13.3226% matched the Python engine exactly.

Where it is derived

MakeWholeDelivery posting

Also: payoff, early-completion payoff.

Buyer → Holder at early completion: the remaining schedule R_t, paid in cash, net of the fee. The identifier predates the vocabulary; it means early completion, cash only since spec v1.5.

engine.rs::settle posts it and the fee to BTC Now, and records the Buyer’s coin equity at exit, spot less the payoff, as coin_returned_usd. settlements_deliver_remaining_schedule_on_upside checks every such posting comes from the Buyer’s account.

Where it is derived

Margin buffer

Also: MarginBuffer, buffer, p95_worst_call_of_par, p99_worst_call_of_par.

The buffer the treasury sizes, over the seeds as shares of each seed’s par: the worst single call at p95 and p99, the deepest point of the cumulative futures cash at p95, and the month of the worst call at the median. Zero without futures.

Base configuration, the futures preset, 8 seeds (test): on GBM p95 worst call 3.83% of par, p99 3.88%, p95 worst cumulative 4.92%, the worst month 20.5; on the bridge 4.16%, 4.20% and 5.08%. The p99 interpolates between the top seeds — run more seeds for a tail that means more.

Where it is derived

Margin path

Also: margin_balance, initial margin path, futures_notional.

The month-by-month futures cash the treasury must post: the variation call, the intramonth proxy, the initial margin held (margin_balance, margin share × gross notional) and the notional, banded over the seeds. Zero without futures.

The initial margin is capital, reported and — only when a leg’s margin_funding_rate is set — charged at rate/12 on the basis channel (default zero; at 6% the test charges $2,806 over the life on a $44,521 peak). Summarised by the margin buffer.

Where it is derived

Margin peak

Also: margin_peak_of_par, initial margin peak, margin pocket.

The peak modeled futures collateral requirement: the configured initial margin share times gross notional, summed across futures legs. It is reserved capital, not an expense.

margin_peak_of_par divides this amount by the stated purchase/par reference. A zero value only means no futures margin is modeled by that structure; it does not establish zero short-option, swap or venue collateral. Funding cost and collateral deposits are separate.

Where it is derived

Market

Also: the market, EntityId::Market.

The ledger’s fourth entity: the external counterparty of the stop sale. It buys the coin for dollars, and the three stop postings flow out from it, so its balance goes negative by exactly the sale amount.

The Market entity is what makes a stop balance without the coin ever being on the ledger. Everything the three parties end up with was paid by a Buyer, paid by the Holder or sold to the market.

Where it is derived

Market-neutral seat

Also: market neutral, delta hedged in listed futures, Table 20.

A dollar-delta hedge intended to reduce modeled first-order USD price exposure. Rebalancing and basis settings are explicit inputs.

Residual stop, timing, nonlinear, basis and model risks remain. The predicate identifying a dollar-delta short is a trading-rule label, not proof of zero market risk, deployability or a stable return across all paths.

Where it is derived

Matched BTC holding reference

BTC retained from the same dated contribution schedule as the selected strategy. Current Research uses strategy-specific net economic contributions; this is not a common starting-budget comparison.

See the workspace measurement contract for cash, economic value, denominator and funding boundaries.

Where it is derived

Memory budget

Also: FF_MEMORY_BUDGET_BYTES, process budget, MemoryBudget, memory 503.

One counter for the process: every route reserves its peak bound before it runs and releases it when the work ends; a request that does not fit beside the runs in progress is 503 with Retry-After: 5, computing nothing.

forwardflow_api.rs::MemoryBudget (model audit 2026-09-07, R04: the byte ceiling was per request, so several individually admissible requests could exceed the machine together). The limit is FF_MEMORY_BUDGET_BYTES, default the per-request ceiling (1.5 GB) so one maximal request fits; fly.toml sets 1,200,000,000 for the 2 GB machine. The reservation is taken after the heavy permit (a queued request holds none) and moves into the blocking task with the permit, so it is released when the work ends, not when the request future drops.

Where it is derived

Merton jumps

Also: Merton, JumpKind::Merton, normal jumps.

Merton (1976): the log jump size is Normal(mean_log, vol_log²). A mean of −0.20 is a typical jump of about −18%; the size’s standard deviation is the second parameter.

JumpKind::Merton { mean_log, vol_log }. E[e^J] = exp(mean_log + vol_log²/2), the term the compensator uses. Symmetric around its mean, so a crash-biased calibration sets the mean negative.

Where it is derived

Missed date

Also: missed payment date, D, the missed payment.

The payment date the Buyer did not pay: t+1 for a hazard draw at age t (payments run first), t for a walk. Day 16 after it is the Stop Date; the sale is priced 18 days after it; it is the Agreement’s exit month.

stop_sale is called with missed = m + 1 for every draw and missed = m for every walk. From the missed date the Agreement is stopped awaiting its sale (engine.rs::state_at): still price-exposed, on the coverage panel and the hedge desk’s book, until the month the sale is booked — ⌈D + lag/30.4375⌉, the first payment date at or after the sale point (R02) — the month after at the program’s 18 days.

Where it is derived

Model Card

Also: Model Cards, the card, v2.0 draft, v1.1.

The document that stamps one configuration and one set of Holder-return figures for a data room, citing the spec version it was run on. v1.1 (6 August 2026) is stamped; v2.0 (3 September 2026) is a draft pending Marc’s stamp.

Three cards exist. v2.0 describes the September program on spec v1.6: paced zero-drift median 13.1%, single vintage 14.9%, single-vintage replay median 47.9%. Until Marc stamps it, the figures a Holder may be shown are v1.1’s, which describe a product that no longer exists. cargo run --release --example w0108_refresh reproduces the table.

Where it is derived

Moneyness

Also: underwater, in the money, out of the money, M_t.

Spot over the amortized obligation, S_t / B_t. Below 1 the coin is worth less than the discounted value of what the Buyer still owes: underwater. The exposure ladder uses a different ratio, spot over the remaining schedule.

contract.rs::ContractTerms::moneyness; engine.rs::Agreement::underwater tests \(S_t < B_t\). The rational-default redirect and the frontier’s walk_below_moneyness use this ratio. The risk desk’s ladder buckets Agreements by \(V_m / R_t\), the ratio a hedging desk maps onto listed strikes, and calls that moneyness as well; the two differ by the \(R_t / B_t\) column, 1.373 after payment 12.

[ M_t = S_t / B_t ]

Where it is derived

Moneyness bucket

Also: MONEYNESS_BUCKETS, < 0.50, 0.50–0.70, 0.70–0.85, 0.85–1.00, 1.00–1.20, ≥ 1.20.

The ladder’s rows: sale proceeds at the month’s mark over the remaining schedule, in six buckets from < 0.50 to ≥ 1.20, lower bound inclusive. The put’s moneyness, not the amortized-obligation moneyness of the rational modes.

A cohort-0 Agreement at month 23 on the base path has \(V/R_{23} = 33{,}292/54{,}575 = 0.610\); an Agreement originated that month sits at \(0.9975/1.475 = 0.676\), the same bucket.

[ \text{moneyness} = \frac{V_m}{R_t} ]

Where it is derived

Monte Carlo

Also: MC, Outcome distribution, simulated futures, the fan, montecarlo.

The same book run across many price futures at consecutive seeds, same assumptions, different market luck. On a bridge it is the uncertainty between the pinned endpoints, not a view on where the price ends.

outputs.rs::run_monte_carlo runs seeds base, base + 1, … in parallel and summarizes IRR and WAL percentiles, expected shortfall, a histogram and the cash fan. Serious readers judge the tail, not the median. At the base cockpit configuration, 1,000 runs from seed 42: median 14.93%, p5 10.29%, p95 21.90%, none negative. The Model Card’s figures use the zero-drift bootstrap instead.

Where it is derived

Monte Carlo cache

Also: MC_CACHE, memoized, memoization, LRU.

An in-memory LRU of 64 Monte Carlo summaries keyed by run count and the configuration’s serialization. A hit skips the engine. Sound only because the engine is seeded: the same request always gives the same summary.

Built server-side from the struct, so a client’s JSON key order does not matter. Empty after a restart. A change that broke determinism would break the cache silently, which is one reason the invariant suite diffs golden outputs. A single simulate is not cached.

Where it is derived

montecarlo

Also: POST /api/forwardflow/montecarlo, montecarlo/ws, ff_monte_carlo.

N seeded runs summarized: IRR and WAL percentiles, expected shortfall, the share negative, a 50-bin histogram, the cash fan and the base seed. Identical requests are memoized; a WebSocket variant streams progress per 1,000-run chunk.

Request: config and runs. The base book at 1,000 runs took 0.30 s cold and 0.006 s memoized. The WebSocket sends Progress, Complete and Error frames and takes the heavy permit per chunk.

Where it is derived

Monthly report

Also: hedge series, hedge_series, the months, HedgeSeries.

Monthly cash, values, P&L attribution, margin and deltas for a hedged sleeve, with pointwise bands across the sample and one complete selected path. BTC flow components and open-value stocks are separate.

POST /api/forwardflow/hedge_series records selection.metric and selection.seed. Coin selects the lower-median economic BTC gain; Dollar selects the lower-median finite USD IRR, falling back to USD multiple if no IRR exists. An explicit in-sample selected_seed replays that path. Pointwise p5, median and p95 bands are not individual paths. Preserve costs, surface, market seeds and the fixed valuation seed for comparison.

Where it is derived

N

Naked write

Also: naked, written option on the paper, (naked).

The rule: a written option on the paper is naked. The coin inside an Agreement is promised to the Buyer, who owns its upside, so nothing covers a written call and a written put is a second short. Every written leg’s label ends (naked).

The engine does not forbid the write — a call spread needs one — it labels it. The covered write stays the cross-book collar’s business, where the free coin sits on the other book. The risk paper’s rule: never write a call on a coin inside an Agreement.

Where it is derived

Negative paths

Also: pct_negative_irr, share negative, runs below zero, futures that lose money.

The share of runs whose effective IRR is below zero, over the runs that had one — conditional. Read the cash-loss share beside it: a run with no IRR can still be a loss. Unavailable when no run has an IRR.

On the histogram the 0% line is marked: futures left of it lose money outright. The heatmap’s break-even frontier is the same threshold drawn across two assumptions. pct_negative_irr is over the runs with an IRR; pct_cash_loss is the share of ALL runs whose undiscounted multiple is below one, and runs_without_irr says how many had no IRR and why.

Where it is derived

Net BTC contribution

The BTC equivalent of negative net monthly flows. State whether economic contributions include a terminal liability mark or realized-cash contributions exclude it. This differs from gross Agreement purchases and opening wallet capital.

See the workspace measurement contract for cash, economic value, denominator and funding boundaries.

Where it is derived

Net BTC recovery

The BTC equivalent of positive net monthly flows. Economic recovery can include unpaid horizon value; realized-cash recovery excludes it. A recovery retained inside a strategy is not an external distribution.

See the workspace measurement contract for cash, economic value, denominator and funding boundaries.

Where it is derived

Net gain

Also: net_gain_usd, Net gain, base_net_gain_usd, collections minus capital.

Receipts less purchases plus the included signed hedge flows, in the stated currency. Unhedged cash gain and hedged economic gain differ when the latter includes unpaid horizon derivative value.

net_gain_usd and net_gain_coins require their cash/economic convention. BTC equivalents use each month’s modeled price. Economic surplus equals realized net recovery minus realized net contribution plus the signed horizon mark; this is not a custody balance or an annual return.

Where it is derived

Net IRR

Also: IRR, Net IRR (effective), Net IRR (eff.), irr_effective_pa, irr_monthly, annualized return.

USD cash-flow IRR: the monthly rate that balances the Holder’s dated net dollar flows, annualized as (1 + monthly rate)^12 − 1. Net means after the deductions included in those flows; it does not imply all investor expenses are modeled.

outputs.rs::irr_analysis solves the supplied monthly flow vector and retains unavailable reasons and multiple-root diagnostics. A conventional flow has one outflow phase followed by receipts; non-conventional flows can have more than one root. Read the selected-root convention, cash amounts and timing beside the rate. A hedged economic IRR can include signed derivative value at the horizon; an unhedged scalar describes the Agreement flows. BTC cash-flow IRR is a separate currency measure.

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

Where it is derived

Net outcome per BTC of Agreement purchases

Also: coin multiple.

Legacy ratio: gross Holder receipt BTC equivalents plus signed hedge economic BTC flows, divided by gross Agreement purchase BTC equivalents. Display as ×, not BTC. Hedge costs reduce the numerator.

coin.rs::coin_seat_metrics reports this legacy purchase-denominator ratio as median_multiple and p5_multiple. Its denominator sums each Agreement’s USD purchase divided by its own entry strike: at par exactly one BTC per originated Agreement, even with intramonth dispersion. Non-par purchase amounts change that capital. Receipts and hedge cash use monthly simulated spot. The ratio differs from net-contribution recovery and a starting-wallet budget. Negative values represent net liabilities, not a negative physical coin count. USD and Coin multiples coincide only where their price and flow-side conventions align.

[ \text{coins per coin} = \frac{\sum_m c^{\text{in}}_m + \sum_m h_m}{\sum_m c^{\text{out}}_m} ]

Where it is derived

Nominal IRR

Also: nominal, irr_nominal_pa, nominal annual rate.

The monthly IRR times 12: 13.144% on the reference Agreement. Quoted in the program chapter’s three-endings tables; everywhere else the effective figure is used.

The same convention names the implied financing rate: 16.50% nominal is \(12 i\), 17.80% effective is \((1+i)^{12} - 1\).

[ \text{nominal} = 12,r ]

Where it is derived

Notional

Also: notional_usd, put ladder notional, total_notional_usd, Σ remaining schedule.

The sum of the remaining schedule over active Agreements: the size of the puts the Holder is short, the embedded put ladder’s notional. $4.58m in the ladder’s largest cell at month 23 on the base path.

Reported per coverage month and per ladder cell; ladder_and_coverage_tie_to_each_other holds the totals equal.

Where it is derived

O

One-Bitcoin cap

Also: one-Bitcoin active cap, the cap.

A Buyer’s active coin across Agreements in force may not exceed one Bitcoin. The only sizing rule in a program with no qualification and no sizing (Marc, 2026-08-22, R-1036). Not modelled.

The program requires identity verification, sanctions screening, a bankruptcy screen and the active-BTC cap before a Sale or Transfer. The engine does not represent Buyer identity or those checks. One BTC per Agreement fixes contract notional; it does not prove that two active Agreements belong to different Buyers or enforce a per-Buyer aggregate cap. The stop hazard remains an assumed population prior.

Where it is derived

Open

Also: open, still active, Open.

An Agreement still active at the end of the simulated window. The horizon gives the last cohort its full term plus a month, so a finished run reports zero here; the engine calls the bucket ‘only possible mid-experiment’.

exit_month is null while open; the drill-down’s delivered figure for an open row is the scheduled deliveries so far.

Where it is derived

Open mode

Also: open, FF_API_KEYS unset.

The engine with FF_API_KEYS unset or empty: every /api/forwardflow/* route answers without a credential and one warning is logged at start, FF_API_KEYS unset — the API is open. A local cargo run is open; limits apply per client IP.

GET /api/forwardflow/health reports "mode":"open". The cockpit’s proxy forwards as-is with no FF_API_KEY, so local development is unchanged.

Where it is derived

OpenAPI document

Also: openapi.json, Swagger, OpenAPI.

The engine’s own OpenAPI 3 description of every route, served open in both modes at GET /api/forwardflow/openapi.json and compiled in from backend/openapi/openapi.json. The book’s Try-it page loads it into a Swagger UI.

Hosted copy: https://btcnow-forwardflow.fly.dev/api/forwardflow/openapi.json. Its security schemes describe the X-API-Key header and the websocket token.

Where it is derived

Option mark

Also: option_mark, open options’ value, marked options.

Signed model value of open options at month end, using remaining tenor, spot and the stated volatility surface and pricing rate. A written option is a liability; no open option means zero.

A surviving option’s horizon mark enters economic return once, but is not settled cash or available funding. The realized-cash series excludes that terminal value and reports it separately. Paying premium exchanges cash for an option asset; the marked P&L view includes changes in that asset alongside cash. This is a model mark, not a verified exit quote.

Where it is derived

Option rate

Also: rate_annual, OptionPricing, option pricing rate.

OptionPricing::rate_annual: the Black-Scholes rate every option is priced at. Default zero, the surface’s own quoting convention; the risk paper prices at 4.5% (OptionPricing::paper()).

The option-pricing rate is an explicit assumption used with the supplied volatility surface and tenor; it is not necessarily zero. USD discounting, futures funding and BTC cash-flow conversion are separate conventions. A supplied surface is not a claim of executable quotes.

Where it is derived

Option value bucket

Also: option_value, option value.

The ninth attribution bucket: the change in the option mark plus the settlement cash of the positions closed this month, a payoff booked against the mark it replaces, never twice. At inception it offsets the premium to the execution cost.

Each month after inception it is the option’s decay and re-pricing; at the run’s last month it carries the horizon mark of a position still open. On the audit’s bought 24-month call (flat $60,000, 43% vol, zero rate and cost) month 0 is +$6,834.90 against the premium’s −$6,834.90, so total_pnl[0] is 0.00 where it read −$6,834.90 before, and month 1 is the −$288.76 of decay on the flat path. Attribution::option_value; the identity reads total = cash + Δmark + Δoption mark + hedge cash = the nine buckets.

Where it is derived

Origination window

Also: origination stop, origination_stop_month, runoff, Origination stops at month, book runs off.

The months in which cohorts originate. Input #18 stops new purchases from a given month on (0 = never); existing Agreements run to completion. Stopping origination caps the size of the position, never the rate on what is owned.

engine.rs::SimConfig::effective_cohorts caps the cohort count with it and the horizon shortens. runoff_caps_size_never_rate checks that stopping at month 6 leaves the IRR identical to \(10^{-9}\). The Greeks use it to freeze the existing book at the as-of month.

Where it is derived

OriginationFee posting

Also: OriginationFee, origination_fees, Origination (first N) → BTC Now, origination take.

Buyer → BTC Now for payments 1 to N: one of the first N payments routed whole to BTC Now. Never delivered to the Holder, so it carries no servicing fee. $1,475 per base Agreement.

Summed as origination_fees, $236,487.15 on the historical early-September reference book. Per row, origination_to_btcnow counts as many of the first N as were made.

Where it is derived

Owner net

Also: owner_net, Holder (net), Holder net, net delivered, owner_total_inflow.

Delivered gross less the 5% servicing fee: what the Holder actually received from an Agreement. $82,673.75 on a completed base Agreement. Book-wide, owner_total_inflow.

The identifier keeps the ledger’s Owner for the Holder. Capital P&L is this figure minus the purchase price.

Where it is derived

P

P&L attribution

Also: attribution, the identity, total_pnl, buckets.

The month’s P&L split into nine buckets that sum to it exactly: carry, price, stops, early completion, hedge mark, hedge settlement, basis, option value and residual. Total = cash in − cash out + the change in the paper’s mark + the change in the options’ mark + the hedge’s cash — total P&L on marks, not a cash report.

Every bucket is the sum over Agreements of a per-Agreement formula on the flat-continuation mark (V(t,S)) at the purchase yield. The identity holds every month on every seed and on the mean band (the mean is linear); the test measures the gap at (2\times10^{-15}) of par on the bridge and on GBM, futures and put ladder alike.

[ \text{total}_m = (\text{in}_m - \text{out}_m) + \text{mark}m - \text{mark}{m-1} + \text{option mark}m - \text{option mark}{m-1} + \text{hedge cash}_m ]

Where it is derived

p1

Also: 1st percentile.

The 1st percentile, the stress floor beside the worst observed run. It wants at least 1,000 runs to be stable, and the cockpit says so. 9.18% at the base cockpit configuration.

With fewer than 100 runs p1 is the worst run itself, since \(\mathrm{round}(0.01 (n-1))\) is zero.

Where it is derived

p3

Also: 3rd percentile, the p3 line, institutional worst case.

The 3rd percentile of the distribution: the line a credit desk provisions to, the institutional worst case (spec v1.4). 9.70% effective IRR at the base cockpit configuration over 1,000 runs.

The outcome-distribution strip draws the bad 3% as a red zone from the worst observed run to p3, with the tail mean (ES3) as a red diamond inside it.

Where it is derived

p5

Also: 5th percentile, bad-luck case.

The empirical fifth percentile of the named measure over the stated sample. About 5% of the sampled values lie at or below this part of the distribution; it is not a guaranteed minimum.

Read the variable, currency and sample count. IRR percentiles use paths with a supported IRR and retain missing/ambiguous counts. Cash or BTC-loss statistics do not disappear when IRR is unavailable. A pointwise percentile band is not one realizable individual path. Historical worked examples are not the current scenario’s result.

Where it is derived

Pacing

Also: paced book, deployment pacing, book and pacing.

How the book is built over time: one cohort a month for as many months as the window allows. Paced entry ladders the strikes, the built-in dollar-cost averaging that decides how a crash lands.

A paced book’s outputs are book-level: IRR and payback on net flows, WAL and the multiple on gross flows, so purchases in one month are never netted against deliveries in the same month. The Model Card’s paced book is 24 cohorts of 20 over an 84-month horizon.

Where it is derived

Also: A, paid-in total, payments made, what he paid.

Every payment the Buyer made, payment 1 included: 1,475 × k after k payments at the base terms. It is the refund base of the stop waterfall and the complement of the remaining schedule.

engine.rs::Agreement::paid_in. Whether payment 1, which BTC Now kept, belongs in the refund base is a term-sheet question flagged rather than ruled; the engine models it as included, reading Marc’s “the maximum he paid” (2026-09-03). At the base terms the difference is $1,475 per stop that pays any refund.

Where it is derived

Paper spread

Also: spread, paper_spread, origination margin.

What BTC Now earns or gives up on the sale of the paper itself: Σ (purchase price − strike) over the book. Zero at par; $3,000 per base Agreement and $720,000 on the 240-Agreement book at 1.05×, −$720,000 at 0.95×.

outputs.rs::BtcNowTake::paper_spread, pinned by paper_spread_identity. It enters the revenue timeline at each Agreement’s origination month and is kept out of total_take.

[ \text{spread} = \sum_{\text{Agreements}} (\text{purchase} - \text{strike}) ]

Where it is derived

Paper’s implied vol

Also: paper implied vol, paper_implied_vol, the paper’s own implied vol.

The flat vol σ at which the behavior-weighted put ladder equals the flat-path markup: the vol at which the paper’s price exactly pays for the puts the Holder is short. Below it on the surface, the markup more than covers the ladder.

surface::paper_implied_vol(config, r), bisection on σ in (1%, 300%); the ladder value is increasing in σ so the root is unique when it exists. None when no root sits in range — usually because the markup exceeds the ladder even at 300%, which says the markup covers more than the total loss of every stopped coin; the cockpit then prints none and says why. Where the surface’s vols at the ladder’s strikes sit below this number, the paper is cheap in the market’s terms; above it, the market values the puts at more than the paper pays.

[ \sigma^\ast : ; \sum_t w_t, P\big(S_0, R_t, \tfrac{t+1}{12}, \sigma^\ast, r\big) = \text{markup} ]

Where it is derived

Partner

Also: Partners, referrer.

Anyone paid a cut of BTC Now’s fees. The referral cuts (50% of the first payment, 0% of the servicing fee, ruled 2026-08-31) are splits inside BTC Now’s take and are not modelled.

The one word for the party formerly called a referrer (Marc, 2026-09-02). A Partner’s cut of the first payment (X1, ruled 50%) and of the servicing fee (X2, ruled 0 for now) are divisions of BTC Now’s own take; the engine reports BTC Now’s take whole.

Where it is derived

Path mode

Also: path, price path, PathMode, Mode, the path.

The generator of the monthly Bitcoin price the whole book lives on: a Brownian bridge (the cockpit default), a historical replay, a zero-drift bootstrap, or a custom anchor path. The shock and the bump overlay any of them.

paths.rs::generate, input #2c. Prices are f64 and become cents only when a strike is fixed or a sale is recorded. The path decides who is underwater, what a stop sale fetches and whether early completion is worth taking; everything downstream is a consequence of it.

Where it is derived

Payback month

Also: payback, Payback, payback_month, money back.

The first month after 0 at which cumulative net cash is non-negative: month 44 on the reference Agreement (the 43rd delivery of $1,401.25 covers $60,000), month 48 on the base book at seed 42. None if never reached inside the horizon.

Defined on net flows. The \(m > 0\) guard stops a book with no purchase at month 0 from reporting payback at 0. Chip S5 and the cash fan answer the same question under uncertainty.

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

Where it is derived

Payment

Also: payments, PMT, monthly payment, scheduled payment.

One of the equal monthly amounts the Buyer pays: the Purchase Price divided by the term, floored to the cent, the last payment absorbing the residual. $1,475.00 at the base terms.

Straight division, not an annuity (\(\text{PMT} = P/n\)). Payment \(t\) is schedule()[t-1]. Payments 1 to N go whole to BTC Now; each later payment splits 95% to the Holder and 5% to BTC Now, $1,401.25 and $73.75 at the base terms. At 36 months the payment is $2,458.33 and the last one $2,458.45 (convention C1).

[ \text{PMT}^* = \lfloor P / n \rfloor_{0.01}, \qquad p_n = P - (n-1),\text{PMT}^* ]

Where it is derived

PaymentDelivery posting

Also: PaymentDelivery, delivery, deliveries, delivered, send.

Buyer → Holder for payments N+1 to the term: a scheduled payment net of the servicing fee, $1,401.25 at the base terms, posted alongside a FlowFee of $73.75.

A “delivery” or “send” is any dollar that enters the Holder’s stream: a scheduled payment after the first N, an early-completion payoff or a stop-sale delivery. All three pass through fees.rs::FeeState::split.

Where it is derived

Payout over premium

Also: payout ÷ premium, payout_over_premium, payout ratio.

Mean economic payoff divided by mean net premium and carry paid, when that denominator is positive. The numerator includes open horizon value; the ratio is not a cash-recovery ratio.

HedgeResult.payout_over_premium has a legacy wire value of zero when the denominator is nonpositive; the interface should show unavailable in that case. Above 1 means economic payoff exceeds the reported net premium/carry on this sample. It does not establish fair pricing, identify the cause of a model difference or guarantee protection. Read realized settlements, open value and all costs separately.

Where it is derived

PD

Also: probability of default, stop rate, Stops ÷ Agreements, stop frequency.

Stops divided by Agreements in the vintage: 0.421 on the historical early-September reference book (101 of 240). A stop is any outcome other than completed, completed early or open; all four stop tags count.

exposure.rs::exposure, per origination month and once for the book. The identity \(\text{EL} = \text{PD} \times \text{LGD} \times \text{EAD} \times n\) holds by construction. Cohort 1 on the base path has 4 stops of 10, cohort 23 has 7 of 10 but almost no loss.

[ \text{PD} = \frac{\text{stops}}{\text{Agreements}} ]

Where it is derived

Peak bound

Also: peak_bytes, peak_postings, 2·term − N + 4, peak_postings_per_agreement.

The most postings a run can write, from the engine’s own rules: 2·term − N + 4 an Agreement, the ledger’s vector sized to exactly it; with the Agreement state and the path, the bytes admission reserves — a bound, not an expectation.

work_estimate.rs::peak_bytes, SimConfig::peak_postings_per_agreement (model audit 2026-09-07, R04: the expected ledger size was not a ceiling — the auditor’s 480-month Agreement on a path to 1% of entry with early completion at 100% was expected at 355.7 postings and wrote 956; the bound is 963, and the vector’s capacity is exactly 963). The 50,000-Agreement version was admitted light at an expected 782,557,368 bytes; its peak is 2,917,431,256 and it is refused. A light route whose peak is over 128 MB (HEAVY_BYTES_THRESHOLD) takes the heavy guard whatever its Agreement-run count. The expectation remains what a user is shown.

Where it is derived

Per-Agreement stream

Also: agreement_rng, own stream, per-Agreement draw stream, unconditional draws.

Since spec v1.6 every Agreement draws its strike, stop and early-completion decisions from its own ChaCha20 stream keyed on (seed, id), and the draws are taken before any price test. A bump changes decisions, never the random numbers.

The key is built by splitmix64 from the seed and the Agreement id. Under the earlier single shared stream, one Agreement leaving a month earlier shifted every later draw in the book and the Greeks were unreadable. greeks_are_sensitivities_not_draw_noise bumps a 240-Agreement book by one basis point and requires at most two exits to flip.

Where it is derived

Per-Agreement table

Also: drill-down, AgreementRow, agreement_table, Per-Agreement drill-down, the rows.

One row per Agreement: entry, outcome, exit month, payments made, delivered gross, fee, owner net, shortfall, proceeds, refund, surplus, coin equity, purchase price, expected net, capital P&L. Every cent traceable.

outputs.rs::agreement_table, about 106 KB of the base book’s simulate body; pass include_agreements: false on runs that only read scalars. The cockpit’s drill-down sorts on any column and exports the rows as a stamped CSV.

Where it is derived

Percentile

Also: percentiles, p25, p50, p75, p95, median, Net IRR percentiles.

A quantile of the sorted outcomes. The calculation convention depends on the result: the legacy Monte Carlo route selects an order statistic; paired Research and hedge distributions interpolate between adjacent observations.

forwardflow_api.rs::percentiles uses sorted[round(q (n−1))], without interpolation, for the legacy Dollar Monte Carlo and cash fan. Research and hedge::Dist use linear interpolation. IRR quantiles exclude paths with unavailable IRRs; count those paths separately and read cash-loss measures across all paths. A sample percentile is not a guaranteed outcome floor.

Where it is derived

Percentile strip

Also: percentile strips, strip chart, p5–p95 bar.

The Hedge desk’s chart of a distribution over the seeds: the bar runs p5 to p95, the tick is the median, the ring the mean. The basis rate and zero are the reference lines; the hedged, unhedged and benchmark strips share the seeds.

One strip per distribution, on one axis of effective annual IRR, so the eye reads the floor (left end), the cost (the medians’ gap) and the upside given up (right end) in one glance. A strip whose left end sits right of the basis line is a structure that beats the basis in its 5th percentile.

Where it is derived

Perp funding

Also: perp funding %/yr, funding_rate_annual (perp), perpetual funding.

The annual funding rate the short perpetual pays on its notional (0.10 = 10%/yr; negative means the short is paid), ±100%, charged monthly. Perpetuals carry funding and margin cost, never an execution cost.

Reported as the leg perpetual funding, paid side. On a perpetual swap, funding flows between longs and shorts to hold the perp at spot; in a contango market shorts are paid, so a negative rate is a real case. It is a different number from the desk’s funding rate on the fair-value endpoint.

Where it is derived

Perpetual delta hedge

Also: perp hedge, delta hedge, perpetual short.

A short in perpetual futures sized to the book’s proxy delta, reset at each rebalance and marked monthly, carrying funding and margin — the market-neutral desk’s hedge.

The proxy delta is the embedded put ladder’s delta, weighted by the stop probabilities still ahead of each Agreement; the surplus above the Purchase Price is left unhedged. Funding and the carry on initial margin are paid monthly on the short notional. This is the one structure for which the hedged excess over basis is reported.

Where it is derived

Posting

Also: postings, transfer, raw postings.

One ledger transfer: month, from, to, amount, kind, Agreement id. A completed base Agreement produces 120 postings; the historical early-September reference book 17,728. Ask for them with include_postings on the simulate endpoint.

Entities serialize as "Owner", "BtcNow", "Market" and {"Obligor": id}; kinds are the seven TxKind variants. Every reported total can be rebuilt from the postings by hand, which is the drill-down audit.

Where it is derived

Posting month

Also: cash lands at, posts at.

The first payment date at or after the stop sale point, ⌈D + lag/30.4375⌉, clamped to the horizon — never before the last mark that priced the sale: with the default 18-day lag the month after the missed date, at 45 days two months after. The three stop postings carry it; the price is not rounded, only the date.

Only a lag of zero (or a whole number of months) posts at the sale point itself; any fraction of a month posts at the next payment date, whose mark is the later of the two the sale interpolates (model audit 2026-09-07, R02: the nearest date put a 10- or 45-day sale’s cash a month before the mark that priced it, so a later mark revised realised cash). The refund’s ten-business-day deadline is inside the same month at this resolution, so the refund posts with the delivery.

Where it is derived

Premium and carry paid

Also: premium paid, premium_paid_mean_usd, net premium.

Mean over the seeds of the net premium and carry a structure costs per run: option premium bought less premium received, plus execution cost, funding and margin carry. Negative when the structure is net premium-positive.

HedgeResult::premium_paid_mean_usd. It is the cost side of the hedge before any settlement; the payoff is the other side. A collar or a put spread can be net negative here, meaning the written legs paid for the bought ones, at the price of the assignments those legs carry in the payoff.

Where it is derived

Price bucket

Also: price line, coin price, coin-price line.

The coin-price line: last month’s book rolled one month at y, re-priced at this month’s spot minus at last month’s, same ages. Small — about 0.24 coins per Agreement at signing, the dollar seat’s delta — a dollar schedule being price-blind.

Price enters only through the stop and early-completion masses and the sale proceeds; the September stop’s surplus above the Purchase Price makes every expected stop a long call, and a stop in transit is re-marked here at the month’s spot — a whole coin for its ~1.6 months, hedged the same months — so at +10% a month the line peaks at 9.6% of par (test, month 50). At origination it carries (V(0,S_m) - V(0,K)) when the strike is not the spot. The book rolls first, then is re-priced: the factor is that order.

[ \text{price} = (1+y)\big(V(t-1, S_m) - V(t-1, S_{m-1})\big) ]

Where it is derived

Price multiple

Also: multiple, 1.475×, markup.

The Purchase Price as a multiple of the coin’s cost: 1.475 in the program, so a $60,000 coin is sold for $88,500. It implies the paper’s financing rate; a multiple below 1.0 implies a negative one.

Input #5, accepted in \(0 < m < 100\). The report view’s configuration line prints \((\text{multiple} - 1)/\text{term-years}\), 9.5% at the base terms, a simple markup per year that is not the implied financing rate. The inverse input, a nominal rate to a multiple, exists in contract.rs::pmt_from_nominal_rate but is not wired to the cockpit.

Where it is derived

Proceeds

Also: V, sale proceeds, stop proceeds, stop_proceeds_usd, recorded proceeds.

The recorded dollars from the stop sale: the interpolated path price 18 days after the missed payment, less the haircut and the 25 bp sale cost. The one number the Holder, the Buyer’s statement and the auditor share.

Stored on the Agreement as stop_proceeds_usd. At the base terms with the coin at $60,000, \(V = 59{,}850\). Everything in the waterfall is computed from \(V\), \(A\) and \(P\); the three postings of a stop sum to \(V\), which is what the Market entity gives up.

[ V = \operatorname{cents}\big(S \cdot e^{-\text{haircut}} \cdot (1 - \text{sale cost})\big) ]

Where it is derived

Purchase Price

Also: Agreement price, terminal value, terminal, P, all-in cost.

What the Buyer pays for the coin in total: strike times the price multiple, quantized to cents. At the base terms $60,000 × 1.475 = $88,500, paid as 60 payments of $1,475.

contract.rs::ContractTerms::terminal computes \(P\). It is the letter the stop waterfall uses too: the Buyer’s refund is capped where \(V + A\) exceeds \(P\), and any proceeds above \(P\) are the Holder’s surplus. Not to be confused with the purchase price of the paper, which is what the Holder pays BTC Now.

[ P = \text{cents}(\text{strike} \times \text{multiple}) ]

Where it is derived

Purchase price of the paper

Also: purchase % of coin cost, purchase_pct_of_strike, par, Holder’s purchase price, capital deployed.

What the Holder pays BTC Now per Agreement, as a fraction of that Agreement’s strike. Par (1.00×, $60,000 on the base coin) since spec v1.4; the premium is the negotiable.

Input #6b. Posted at origination as PurchasePrice from the Holder to BTC Now; the fraction form keeps a paced book of many cohorts sane when each cohort strikes at a different price. Before v1.4 the default was 1.05×, $63,000, which is what the M0 fixtures and any July document use. The difference between purchase price and strike, summed over the book, is the paper spread.

[ \text{purchase} = \text{cents}(\text{strike} \times \text{pct}) ]

Where it is derived

Purchase-yield mark

Also: purchase yield, mark_paper, reporting mark, purchase_yield_annual.

Purchase-yield attribution mark: expected remaining Agreement flows along a flat continuation of current spot, discounted at the yield that calibrates a fresh Agreement to its purchase cost.

series.rs::purchase_yields and mark_paper define a model convention for explaining monthly P&L. Stops in transit use expected proceeds at current spot discounted to their modeled receipt date. Cost calibration does not establish market-participant fair value or auditor acceptance. Monthly-settled futures have no separate surviving mark; open options and swaps can retain signed model value. Read the stated method and limitations.

Where it is derived

PurchasePrice posting

Also: PurchasePrice, purchase_prices.

Holder → BTC Now at the origination month: the Holder buys the paper, strike times input #6b ($60,000 at par). Paper acquisition, not BTC Now’s take; the identifier names the Holder’s purchase, not the Buyer’s Purchase Price.

The only outflow kind from the Holder (auditor check A2b). Summed as purchase_prices in the BTC Now block and reported separately from total_take.

Where it is derived

Put ladder

Also: static put ladder, puts on the paper.

A put bought on each Agreement at its origination, struck at a share of its entry price, for a fixed tenor — the simplest floor under the coin the Holder is short.

Coverage is coins per Agreement (one Agreement is one coin). The premium leaves the Holder’s flows at inception; the intrinsic value at expiry comes back. BTC Now’s own posture is a put ladder at 70% of entry with 75% coverage over a 24-month window.

Where it is derived

Put skew

Also: put_skew_points, skew points, five points of skew.

OptionPricing::put_skew_points: vol points added to the surface’s vol whenever a put is priced (5: a 40% surface prices puts at 45%); calls read the surface as it is. Default zero — the surface’s smile already carries its skew.

The risk paper’s Appendix D prices every put five points over its implied vol at a 4.5% rate. On the engine the year-end loss-line put goes from 5.77% of spot the default way to 6.00% the paper’s way (the_papers_pricing_moves_a_put_the_way_black_scholes_says).

Where it is derived

Put spread

Also: spread.

A put bought at a higher strike and one sold at a lower strike on the same Agreement and tenor: cheaper than the ladder, protection that stops below the lower strike.

The written put’s premium offsets the bought put’s; below the short strike the Holder is unprotected again, which is the trade the spread makes for its lower cost.

Where it is derived

R

Rate limit

Also: 429, Retry-After, concurrency cap, per-key limit.

Per key in keyed mode, per client IP in open mode: 120 requests per rolling minute and at most 4 concurrent heavy runs. A breach is HTTP 429 with a JSON error and Retry-After in seconds; a refused request is not itself counted.

State is in memory on the one machine. Heavy means a route taking the server’s HEAVY permit — Monte Carlo and its websocket, heatmap, backtest, risk, fair value, hedge, coin seat. Behind these sit the server-wide three permits and the engine’s own work caps, which answer 400.

Where it is derived

Rational boundary

Also: rational_boundary, boundary, Rational boundary, input #25, fifth behavior mode, pessimist robot.

The fifth behavior mode (v1.6): a Buyer whose coin sits below the computed walk-away frontier for the payment date does not pay; the walk is the missed payment. The one behavior number that carries no prior.

Input #25, Option<BoundaryParams>; exit tag RationalBoundary. The frontier is computed once per run by boundary.rs::rational_frontier from cohort-1 terms and, being scale-invariant in the strike, serves every Agreement. At step 1, before payment \(t\), the Buyer compares spot with frontier[t − 1] times the strike. Lifted from the parked Behavior Engine and re-derived for the September stop (Marc, 2026-09-03).

[ S_m < f_t \cdot \text{strike} ;\Rightarrow; \text{do not pay; arm the walk} ]

Where it is derived

Rational default

Also: rational mode, rational_default, Rational redirection, redirect, the ruthless robot, Stop (rational).

Keeps the hazard, moves the stops: a drawn stop sticks only on a Buyer whose coin is below the amortized obligation; an in-the-money draw is redirected to a random underwater Agreement, or suppressed and counted if there is none.

Input #9, spec §4.2; the exit tag is non_performance_rational. Underwater means \(S_t < B_t\) against the amortized obligation, not the entry price and not the nominal schedule. The same lifetime curve concentrates into drawdowns; on a strong rally draws are suppressed and the realized share bends below the target. With the multipliers also on, the drawing Agreement’s hazard is scaled first, then the redirect looks for someone underwater.

Where it is derived

Realized hedge settlements

Also: realized_settlement_mean_usd, hedge settlements, settlements.

Signed option, swap and futures cash settlements already modeled as received or paid. Open horizon derivative values, premiums, basis and costs are separate.

HedgeResult.realized_settlement_mean_usd is the mean economic payoff less horizon_mark_mean_usd. A long-dated option still open at the horizon can therefore report zero realized settlement alongside a nonzero economic payoff. Read the final open value before treating economic recovery as cash available.

Where it is derived

Realized volatility

Also: realized vol, Realized vol, base_vol_annual, σ_real.

The engine’s own definition: the population standard deviation of a path’s monthly log returns, annualized by √12; zero for fewer than three prices. The base seed-42 bridge realizes 39.4% against its 43% input.

exposure.rs::realized_vol. It sets the intramonth dispersion spread and translates a vega bump into a vol factor. The examples that print the Model Card’s regime figures divide by \(n - 1\) instead: 40.6% versus 41.4% on the trailing 24 months. A bridge realizes a little below its input because pinning removes variance.

[ \sigma_{\text{real}} = \sqrt{12};\sqrt{\frac{1}{n}\sum_{i=1}^{n}(r_i - \bar r)^2} ]

Where it is derived

Rebalance

Also: rebalance every, rebalance_months, rebalance date.

Months between resets of the perpetual delta hedge, 1–120. At each rebalance date the short position is reset to coverage × the proxy delta of the book, in coins; between dates it is held and marked monthly.

The proxy delta is the delta of the embedded put ladder’s legs still ahead of each Agreement on the book, conditional on survival to date, at the surface’s vol for each strike and tenor — not exposure::greeks, which is an ensemble figure costing seven engine runs per date. The surplus call above the Purchase Price is left out: the desk shorts the put’s delta. A shorter rebalance tracks the delta closer and pays no more funding, since funding is on the notional, not the trade.

Where it is derived

Rebalancing rule

Also: TradingRule, trading rule, rebalance policy, Calendar, PriceMove, min_trade_coins, lot_coins.

A futures leg’s TradingRule: when to reset the position — Calendar { months }, DeltaBand, PriceMove { pct } — with a minimum trade, a lot (CME is 5) and a margin funding rate. A parameter with a measured cost, not an assumption.

series.rs::rebalance_policy_comparison (POST /api/forwardflow/rebalance_policies) runs one structure under each policy on the same seeds at the desk’s own execution cost (the request’s exec_cost_bps applies to option premium; futures turnover has its separate futures_cost_bps) and reports turnover and trade count beside each hedged distribution, every row saying the cost it was priced at; the page subtracts each median from the monthly reset’s. A reset smaller than the minimum is skipped and the net position is rounded to the lot. A calendar position can remain past the Agreement’s exit until its next reset unless close_on_exit is enabled. On the wire a plain calendar rule still reads and writes rebalance_months.

Where it is derived

Redline

Also: the Redline, legal review.

The legal review that rules on drafting questions the engine cannot: whether a credit-flavoured identifier such as Obligor may live in code, and the sale standard’s venue, deadline and index once written into the Agreement.

The vocabulary ruling maps Obligor to “the Buyer of record” and leaves the identifier for Redline to rule on. Term-sheet questions the engine has taken a position on pending an answer are listed in the limits chapter.

Where it is derived

Refund

Also: refunds, Buyer refund, buyer_refund_usd, Refunds to Buyers, StopRefund, Refund $.

The dollars a stopped Buyer gets back out of the sale proceeds: min(paid in, max(0, proceeds + paid in − Purchase Price)). Zero when the sale does not clear the remaining schedule; never more than he paid.

The R-1033 rule (Marc, 2026-08-22, reporting counsel’s approval). Since \(R = P - A\), the middle term is \(V - R\): the refund is the proceeds above the remaining schedule, floored at zero and capped at the paid-in total. It is paid in dollars from the proceeds within ten business days; a zero refund posts nothing. At the base terms after 12 payments, a $75,000 coin refunds $4,012.50 and a $100,000 coin refunds the full $17,700.

[ \text{refund} = \min\big(A,\ \max(0,\ V + A - P)\big) ]

Where it is derived

Regime

Also: regimes, the three regimes, regime 1, regime 2, regime 3.

One of the three cases of the stop waterfall, ordered by where the proceeds fall: below the remaining schedule (shortfall), between the schedule and the Purchase Price (refund), or above it (surplus).

Regime 1: \(V \le R\), refund 0, the Holder takes everything and books a shortfall. Regime 2: \(R < V \le P\), the Holder receives exactly \(R\), everything above refunds the Buyer, a completion that arrived early. Regime 3: \(V > P\), the refund cap binds at \(A\) and the Holder keeps the surplus. Shortfall and surplus cannot both be positive. The same word also names a bootstrap window (regime start).

Where it is derived

Regime start

Also: Regime from, regime_start_index, modern regime, trailing 24 months, full history.

The month from which the bootstrap samples returns. January 2017 (index 59) is the cockpit’s modern-regime default; July 2024 (index 149) starts the trailing-24-month pricing window; February 2012 (index 0) is the full-history stress.

Bar \(i\) is the month \(12(y - 2012) + (m - 2)\). The modern regime drops the early hundred-fold years without dropping institutional-era volatility. Marc ruled on 2026-08-05 that the pricing stance is current volatility, the trailing 24 months, with full history as the printed stress.

Where it is derived

Regime switching

Also: RegimeSwitching, calm σ, stressed σ, two-state vol, Markov vol.

A two-state monthly Markov chain on the volatility: calm or stressed, one drift. Each month’s log return uses the state’s σ, then the state may switch for the next month. Equal vols reduce to GBM exactly.

PathMode::RegimeSwitching { mu_annual, calm_vol, stressed_vol, p_calm_to_stressed, p_stressed_to_calm, start_stressed }. Month k’s return uses the state entering month k; the switch draw for the next month follows. A chain that can never move still takes its draws, so the path depends on the seed alone, never on the parameters’ zeros. The long-run share of months in the stressed state is p_cs / (p_cs + p_sc).

Where it is derived

Remaining schedule

Also: R, R_t, remaining nominal schedule, what remains, schedule still owed.

What the Buyer still owes in nominal dollars after t payments: the tail of the schedule. At the base terms $88,500 − $1,475 t: $70,800 after payment 12, $60,475 after payment 19.

engine.rs::Agreement::remaining_schedule, read from a suffix-sum table. It is what the Buyer pays at early completion, what the stop waterfall pays the Holder first, the numerator of the schedule line, the embedded put’s strike and the risk desk’s EAD. The paid-in total is its complement, \(A_t = P - R_t\).

[ R_t = \sum_{k=t+1}^{n} p_k ]

Where it is derived

Research workspace

Also: the workbench, /forwardflow, cockpit, Forward-Flow Simulator, Workbench.

The workspace at /forwardflow for testing a theory. Its main scenario reruns after accepted assumption changes, with a 300 ms debounce.

Choose a question or a scenario, inspect the assumptions, then review returns, cash flows, stress and the ledger. Single-run exhibits describe one seeded path; Monte Carlo exhibits describe a distribution. Save a baseline to compare completed runs.

Where it is derived

Residual bucket

Also: residual.

What no bucket’s formula claims: the fee’s cent rounding, the conviction rule’s memory (the model restarts each month with no streak), a completion’s last payment against its discounted mark, the cent between (V(0,K)) and the purchase price.

Measured, never assumed away: at most (9\times10^{-8}) of par across the identity test. A residual that grows is a formula that is wrong.

Where it is derived

risk

Also: POST /api/forwardflow/risk, ff_risk, RiskRequest, risk endpoint.

The exposure layer (v1.6): the two lines, coverage by month and cohort, the ladder at a month, PD·LGD·EAD per vintage, the Greeks and the frontier family. Options: ladder month, bumps, believed drifts, lattice parameters, Greek seeds.

Heavy route; 0.05 s on the base configuration. config.bump must be null because the Greeks own the bump. The response carries no stamp; the Risk desk page stamps its exports with the spec version from a constant in the page.

Where it is derived

Risk desk

Also: /forwardflow/risk, the Risk desk, exposure layer, Risk desk page.

The exposure layer’s page at /forwardflow/risk (spec v1.6): the two lines with the frontier family, coverage on the simulated path, the exposure ladder, the Greeks and PD·LGD·EAD by vintage, on the engine’s own seeded paths.

Manual Run. Its headline is the book-wide share of Agreement-months below the capital line. Exports are stamped config ‹hash› · seed ‹seed› · engine spec v1.6.

Where it is derived

Risk-neutral fair value

Also: fair value, FairValue, PV per Agreement, PV of the book, risk-neutral value.

The paper priced the way a desk prices any claim on the coin: the engine run under GBM at the funding rate with the surface’s ATM vol, the Holder’s monthly cash discounted at the funding rate to month 0, averaged over seeds.

surface::fair_value(config, surface, funding_rate, seeds). The measure is the desk’s — drift at its funding rate, vol off its surface — while behavior (hazards, multipliers, propensity, the conviction rule, the rational modes) stays the config’s: our priors for the Buyer, their measure for the price. Every seed is one full engine run (PathMode::Gbm); the Holder’s receipts and purchases (Ledger::owner_monthly_gross) are discounted continuously and averaged. The shock and bump overlays are cleared — a fair value is not a stress. pv_per_agreement_usd is the book’s PV over the number of Agreements.

[ PV = \frac{1}{N}\sum_{s=1}^{N} \sum_{m=0}^{H} e^{-r,m/12}, C_{s,m} ]

Where it is derived

Rolled calls

Also: rolled listed calls, 12-month calls at the fixed strike.

Calls bought at the configured strike reference and tenor, renewed at expiry while the Agreement remains eligible. Coverage and any years limit determine the size and stopping rule.

HedgeSpec::RolledCalls prices each premium on the supplied surface and records premium, execution, settlement and any remaining horizon value separately. A fixed entry strike may become expensive after a rally. BTC amounts are reporting equivalents of modeled USD flows, not a claim that a venue executes or settles this strategy in BTC.

Where it is derived

Rolling puts

Also: rolling short-dated puts.

Short-dated puts struck at a share of the current spot, bought again at every expiry while the Agreement is on the book — protection that follows the price, at a premium paid every roll.

Rolling keeps the strike near the money, which is why it is the most expensive structure on the shelf over a five-year Agreement: the premium is paid many times and, on a path that never crashes, never comes back.

Where it is derived

Ruling

Also: rulings, Marc, 2026-08-22, R-1033, R-1035, R-1037, decider.

A decision by Marc on a program term or a modelling choice, cited by date. R-1033 is the refund formula, R-1035 the Stop Date, R-1037 the sale within two business days (all 2026-08-22); 2026-09-03 moved the surplus to the Holder.

Each rule in the program chapter carries the date of the ruling that made it, so a number in a Model Card can be traced to a decision. Two items are flagged rather than ruled: payment 1 in the refund base, and the sale standard.

Where it is derived

Run package

Also: completed-run package, research package, ff-run-package/1, export this run, verify a package.

The durable bundle of a completed run — the request as sent, engine and cockpit identity, timestamps, the result and its warnings — sealed with a SHA-256 over canonical JSON, a content-integrity check (unaltered since sealing — not proof of which engine produced it); “export this run” writes it, “verify a package” reads it.

package.ts (model audit 2026-09-06, M07): schema ff-run-package/1; the engine block carries spec, version, build and data digest, the cockpit its spec and commit; verifyPackage recomputes the digest before a file’s numbers are read and configDiff sets the packaged request beside the live inputs. The stamp’s eight characters are a display reference; the package’s digest is the run’s content identity — a matching digest proves the file unaltered since it was sealed, not that the engine produced it (an unkeyed hash is not a signature; model audit 2026-09-07). The nested shapes (engine, cockpit, timestamps, warnings) are validated before anything is rendered, and a desk package keeps the identity of every answer and refuses a mixed one (R06, R07). The Scenario export remains an input export.

Where it is derived

Runs

Also: paths, Monte Carlo runs, input #14, number of runs.

How many seeded simulations a Monte Carlo summarizes: 1 to 100,000, and runs × book size at most 24,000,000 Agreement-runs. Run i is the single run at seed + i.

Spec input #14, forwardflow_api.rs::validate_mc. The 24 million cap is the 100,000-run ceiling at the 240-Agreement base book. p1 wants at least 1,000 runs to be stable. The workbench defaults to 2,000, the report view to 1,000.

Where it is derived

S

Sale

Also: Primary Sale, Secondary Sale, Selling Holder, Buying Holder, holder swap.

The receiving side of an Agreement changing hands. A Primary Sale is Holder 0 (BTC Now) selling the paper; a Secondary Sale is any later one, carrying a 1% facilitation fee. The engine models one Holder buying at origination.

The word is capitalised to separate it from the stop sale, which is the sale of the coin for dollars. The engine collapses the Primary Sale onto the origination date and models no Secondary Sale; the 1% facilitation fee on the value moved (Marc, 2026-08-31) is outside the engine.

Where it is derived

Sale cost

Also: market-sale cost, sale_cost_bps, 25 bp, execution cost, Market-sale cost (bps).

Execution cost of the stop sale in basis points: proceeds × (1 − b/10,000). 25 bp in the program (Marc, 2026-07-11), so V = 0.9975 × S at the base terms; $150 on a $60,000 coin.

Input #16, accepted from 0 to below 10,000 bps. The same fraction goes to the rational frontier’s walk payoff. The ruling puts the sale’s costs on the Company; the engine takes them out of the proceeds, which lowers the refund and the delivery.

Where it is derived

Scenario report

Also: /forwardflow/v2, Holder Analytics, diligence report, Report view.

The manual-run report at /forwardflow/v2, with overview, terms, cash flows, credit, stress, methodology and assumptions.

Shows completed server responses and reports failed analyses explicitly. Changes to the scenario draft require a new run. The former frozen July first-paint and offline fallback has been removed.

Where it is derived

Scenario shelf

Also: SHELF, chips, S1, S4, S5, S12, one-click chips.

Thirteen one-click chips, S1 to S13, each a named fear that rewrites the configuration and scrolls to the panel that answers it: a 50% crash the day after wiring, the bleed, doubled stops, the ruthless robot, the kitchen sink.

lib.ts::SHELF, spec §8.1. S5 targets the cash fan, S9 replays the 2013 top, S10 the solver, S11 the heatmap, S13 the tornado. S4 is rational mode plus the conviction rule at X = 0, Y = 1.

Where it is derived

Schedule

Also: the schedule, payment schedule, contracted schedule, nominal schedule.

The fixed list of monthly payments that adds up to the Purchase Price exactly. Built once per Agreement at origination and never recomputed; nothing in it depends on the price of the coin.

contract.rs::ContractTerms::schedule builds it under convention C1. The schedule is a contract amount in exact cents: the early-completion payoff, the stop waterfall’s first claim and the risk desk’s schedule line all read its tail. The test schedule_sums_to_terminal_any_term checks the sum at terms 1 to 120.

Where it is derived

Schedule line

Also: schedule_line, the schedule line, put strike line.

The remaining nominal schedule over the strike, R_t / strike. Above it a stop sale delivers the whole remaining schedule and the Holder loses only future yield. 1.45 after payment 1, through 1.00 between payments 19 and 20.

It is the strike of the embedded put: the Holder’s shortfall on a stop is \(\max(0, R_t - V)\). Coverage counts the Agreements whose sale at this month’s mark would land below it; the ladder’s notional is the sum of its numerators. The cockpit reports the first row at or below 1.0, month 20.

[ \ell^{S}_t = \frac{R_t}{\text{strike}} ]

Where it is derived

Seasoned

Also: fully seasoned, seasoning.

A vintage whose whole window, term + 1 months, fits inside recorded history. With 174 bars through July 2026 and a 60-month term the last seasoned start is June 2021, index 112; there are 113.

The extra month lets a stop at the final stoppable age settle inside the ledger; it costs one vintage against the July program’s 114. Months too close to the present are greyed out in the cockpit’s replay picker for the same reason.

Where it is derived

Seed

Also: seeds, seeded, ChaCha20, random-number seed.

The random-number key of a run, default 42. Same configuration and seed, byte-identical output. It drives the price path and, through a per-Agreement key, every Agreement’s own draws.

engine.rs::run seeds a ChaCha20 generator from config.seed for the path alone; engine.rs::agreement_rng(seed, id) derives one stream per Agreement through splitmix64. Monte Carlo run \(i\) uses seed + i, so any run of a distribution is reproducible alone. A change of seed changes the path and every Agreement’s draws together, “another world”; a change of anything else on the same seed holds the world fixed. Determinism is a property of one engine version: v1.6’s per-Agreement streams draw differently from v1.5 at the same seed.

Where it is derived

Seed ensemble

Also: greek_seeds, ensemble, 16 seeds, ensemble mean.

The Greeks average over consecutive seeds starting at the configuration’s seed, 16 by default and at most 256, so a bump is not the property of one path. The ensemble also gives the Greeks’ base net gain and base IRR.

Seven runs per seed, so 112 runs at the default. Per-Agreement streams remove draw noise from each difference; the ensemble averages the remaining threshold noise.

Where it is derived

Seeds with IRR

Also: seeds_with_irr, seeds with an IRR.

The count of seeds whose combined flow has an IRR at all: a flow with a single sign (all out, or all in) has none, and a flow can change sign with no root in the solver’s range. The IRR statistics are over these seeds only; the multiples and the cash-loss share are over every seed.

Dist::seeds_with_irr, beside seeds_total, seeds_without_irr and without_irr_reasons (single-signed, no root in range, non-finite). A hedge that pays more than the paper returns on a path can leave the combined flow single-signed or rootless; such seeds have no IRR and are out of the IRR statistics, but they are still paths: pct_cash_loss, median_multiple and p5_multiple count them (audit 2026-09-05, finding 1). When this number is below the seeds run, read the cash-loss share and the multiples beside the IRR.

Where it is derived

Servicing fee

Also: fee, flow fee, FlowFee, 5%, servicing_fee_rate, program fee, Servicing fee % of each delivery.

BTC Now keeps a flat 5% of every dollar delivered to the Holder, deducted at each send: scheduled payments after the first N, early-completion payoffs and stop-sale deliveries alike. The Holder receives 95%.

Input #4, a flat share not derived from the term (Marc, 2026-08-31; spec v1.5). At the base terms one delivery of $1,475 splits into $73.75 of fee and $1,401.25 to the Holder; the lifetime fee on a completed Agreement is $4,351.25. The fee is rounded cumulatively (convention C2), so a single posting may differ from 5% of that delivery by a cent while the lifetime fee is exact. It is the only thing BTC Now takes after the first payment; no dial gives it a share of a stop sale.

[ \text{fee}k = \text{round}(f,C_k) - \text{round}(f,C{k-1}), \qquad \text{net}_k = d_k - \text{fee}_k ]

Where it is derived

Shock designer

Also: shock, Shock, crash overlay, Drop %, Over months, Recover to %.

A crash overlaid on any path: from month X the price is multiplied by a log-linear ramp reaching 1 − Z at month X + W − 1, then held, or recovered log-linearly to R times the unshocked level at the horizon.

Input #17, paths.rs::apply_shock, applied after generation and before the bump. A cleaner crash instrument than moving the bridge’s endpoints, which changes every month. Chip S1 is −50% from month 1 over 3 months; the tornado’s crash row is −70% over 3 months, whose ramp reads 0.669, 0.448, 0.300.

Where it is derived

Shortfall

Also: shortfalls, schedule shortfall, shortfall_usd, total_shortfall_usd, Schedule lost to stops, Stop-sale shortfall, Shortfall $.

On a stop, the remaining schedule minus the sale proceeds where the sale fell short: max(0, R − V). The Holder’s loss against the schedule in dollars, before the fee. Zero once the sale covers the schedule.

Regime 1 of the waterfall. The shortfall is not the Holder’s capital loss: the Holder has already received net payments, and the row’s capital_pnl is the number for realized loss. Book shortfall equals the risk desk’s EL, $990,282.37 on the historical early-September reference book at seed 42.

[ \text{shortfall} = \max(0,\ R - V) ]

Where it is derived

SimConfig

Also: config, configuration, the inputs panel, Assumptions.

The whole inputs panel as one object, the config member of every POST body: inputs #1 to #25 by their engine field names. Decimal fields travel as strings; optional inputs are null when off; enums are externally tagged objects.

engine.rs::SimConfig, the SimConfig type in lib.ts. Two defaults matter: SimConfig::default in the engine and BASE_CONFIG in the cockpit, which differ only in intramonth strike dispersion. Validation panics name the input and become HTTP 400.

Where it is derived

simulate

Also: POST /api/forwardflow/simulate, ff_simulate, one run.

One seeded run: the Holder’s outputs, the per-Agreement table, the path, the hazard vector, the conservation flag and, on request, the postings. 0.010 s at the base book; not gated by the heavy semaphore.

Request: config, include_postings (default false), include_agreements (default true). The base book’s body is 111,692 bytes raw, 17,500 gzipped, 2,547 gzipped without the rows. The workbench fires it 300 ms after any edit.

Where it is derived

solve_price

Also: POST /api/forwardflow/solve_price, price solver, inverse price solver, What should I actually pay, hurdle, clearing price, input #19.

The inverse of the deal card: given a target effective IRR and the client’s own configuration, the purchase price at which the paper returns exactly that rate — solved directly as the present value of the Holder’s net receipts at the target over the present value of the strikes, from one prepared run, and verified by a second. At 12% the base book clears at 103.84%, $62,301.38.

outputs.rs::solve_purchase_price (2026-09-05, the audit’s finding 7): the Holder’s receipts do not depend on what the Holder paid, so no search is needed and both runs are at the configuration’s seed. Returns SolvedPrice { price, irr_check, attainable, note }: price is None only when the Holder receives nothing; a target beyond the desk’s 1%–500% bracket gets its price with attainable: false and the edge named. The target must lie in (−90%, +1,000%). Chip S10.

Where it is derived

Spec version

Also: spec, v1.5, v1.6, the specification, change block.

A number the specification carries, v1.0 through v1.8, changing when the engine’s definition changes. v1.5 (2026-09-03) applied the September program; v1.6 (the same day) the exposure layer; v1.7 (2026-09-04) the price models, the surface and the hedge desk; v1.8 (2026-09-05) the coin seat.

The spec is the intent, the crate is the truth: where they differ the crate wins until someone changes it. A ruling is applied by a spec version, sometimes weeks later; from 22 August to 3 September the engine still priced the July product.

Where it is derived

Spot benchmark

Also: spot, Benchmarks::spot, buy and hold.

Buy one coin per Agreement at its entry price in its origination month, sell every coin at the horizon’s mark. The same coins, the same months, the same seeded paths as the paper — with no schedule, no stops and no fees.

The directional Holder’s alternative: own the coin instead of the paper on it. On a rising path spot keeps the whole rise, where the paper caps the Holder at the schedule; on a falling path spot takes the whole fall, where the paper’s schedule and stop sale cushion it. The paper’s median over spot’s is the benchmarks’ headline.

Where it is derived

Spread to par

Also: spread, spread_bps, spread to purchase.

(PV − purchases) ÷ purchases in basis points, both discounted to month 0: positive means the paper is worth more under the surface’s measure than it costs. The gap is the spread the paper pays for behavior and illiquidity.

FairValue::spread_bps. Purchases are the sum of every purchase price the Holder paid, discounted the same way as the receipts — a paced book buys over many months, so the later purchases are worth less at month 0. Par is the purchase book; the spread is how far the risk-neutral PV sits above or below it.

[ \text{spread}{bps} = 10{,}000 \times \frac{PV{book} - PV_{purchases}}{PV_{purchases}} ]

Where it is derived

Stamp

Also: stamped, export stamp, config ‹hash› · seed ‹seed›.

A label on an exported exhibit identifying its configuration fingerprint and seed. Retain the full completed run package and engine identity to reproduce the analysis.

A short configuration hash is a fingerprint, not the configuration itself or a source-build identifier. Chart and CSV stamps vary by export; specialist results also require their endpoint options. Save the complete request, response warnings, engine build and historical-data digest. MCP replay metadata uses a separate canonical hash.

Where it is derived

Stop

Also: stops, stopped, stopping, Non-perf., non-performance, non_performance, default, defaults.

A Buyer ceasing payments. A payment up to 15 days late carries nothing; day 16 is the Stop Date, the Agreement ends, and the coin is sold for dollars within two business days. The proceeds are divided by a fixed waterfall.

The stop is the Buyer’s only exit other than completing, and the exit a Holder prices. On the Stop Date the Buyer owes nothing further. The coin is sold for dollars (modelled 18 days after the missed payment, priced on the path), the Holder is paid the remaining schedule first, the Buyer is refunded up to what he paid in, and any surplus above the Purchase Price stays with the Holder. A stopped Buyer receives dollars, never coin. The engine’s identifier for the outcome is non_performance, and a few cockpit labels still say “default”; both mean a stop.

[ \text{refund} = \min\big(A,\ \max(0,\ V + A - P)\big), \qquad \text{delivered} = V - \text{refund} ]

Where it is derived

Stop (rational)

Also: non_performance_rational, NonPerformanceRational, rational stop.

A stop drawn under rational mode: only a Buyer whose coin was worth less than the amortized obligation. Same sale, same waterfall, same postings as any stop; counted apart in the exit split.

Includes a draw redirected from an in-the-money Buyer onto an underwater one. The missed date is \(t + 1\) like any draw.

Where it is derived

Stop Date

Also: Stop Dates, day 16.

Day 16 after a missed payment due date (R-1035). A payment up to 15 days late carries nothing; on day 16 the Agreement ends and the sale for dollars begins. A returned payment counts as no payment.

Marc, 2026-08-22: “stop is 15 days later and we stop on day 16 as always”. The Buyer may also elect to stop in the app on any day, and that day is the Stop Date. The engine works at monthly resolution and does not model the 15 days as an event: it records the missed payment date \(D\) and sells the coin stop_sale_lag_days (default 18, day 16 plus two business days) after it.

Where it is derived

Stop mass

Also: weight, ladder weight, unconditional stop probability, stop_mass.

The unconditional probability of a stop at one payment age: survival to the previous age × the hazard at this age, from the config’s default scenario. Price-blind; the weights sum to the scenario’s lifetime stop rate.

The weight on each ladder leg. Drawdown multipliers, the conviction rule, the rational modes and early completion are all ignored, so the ladder is the paper’s short put under the Holder’s prior for behavior, not under any price path. The perpetual hedge’s proxy delta uses the same masses conditional on having reached the current age.

[ w_t = \Big(\prod_{k<t} (1 - h_k)\Big), h_t ]

Where it is derived

Stop sale

Also: sale for dollars, recorded sale, forced sale, liquidation.

The sale of the coin for dollars after a stop: within two business days of the Stop Date, at market through the normal venue, in a recorded sale. Modelled 18 days after the missed payment, priced on the path.

Marc, 2026-08-22 (R-1037), approving the drafted mechanics; costs on the Company, record kept. The engine takes the 25 bp out of the proceeds, which lowers the refund and delivery by $150 on a $60,000 coin. The venue, deadline and index are not yet written into the Agreement (the sale standard, a term-sheet question). “Liquidation” is the retired July word.

Where it is derived

Stop-sale lag

Also: sale lag, stop_sale_lag_days, 18 days, Sale lag (days after the missed payment), Stop-sale lag (days).

Calendar days from the missed payment date to the recorded sale: 18 by default, day 16 (the Stop Date) plus two business days. The sale is priced on the path between the monthly marks; cash lands at the first monthly date at or after the sale.

Input #24 accepts 0–90 calendar days. The sale price is log-linearly interpolated between monthly marks at pos = missed month + lag/30.4375. Receipt cash is booked at ceil(pos), the first modeled monthly date at or after sale. At the default 18-day lag this is the month after the missed payment; at zero lag it is the missed month. The realized path and the performing Coin valuation continuation both retain the full required sale and booking tail. A short supplied valuation path extends flat without moving the cash date.

[ S = \exp\big((1-f)\ln S_k + f \ln S_{k+1}\big), \qquad f = \text{pos} - \lfloor \text{pos} \rfloor,\ \text{pos} = D + \tfrac{\text{lag}}{30.4375} ]

Where it is derived

StopRefund posting

Also: refund posting.

Market → Buyer at the payment date after the sale: the Buyer’s dollar refund, min(A, max(0, V + A − P)). Dropped when zero, so a regime-1 stop writes no refund line at all.

Every refund in a run is a StopRefund to an Obligor; the book’s buyer_refunds_usd equals their sum (auditor check C13). The program pays it within ten business days with a statement, inside the same month at the engine’s resolution.

Where it is derived

Stops bucket

Also: stops realised against the prior, stop release, early-completion bucket.

Stops realised against the prior: a stopped Agreement books its cash (payment plus the proceeds expected at the month’s spot, discounted) less its rolled mark, and the recorded proceeds against that expectation the month they land; a survivor books the stop the model expected and released. Averages to zero under the config’s own hazard.

For the stopped Agreement the rolled mark is ((1+y),V(t-1, S_m)); for the survivor the release is (q,(V(t,S_m) - \text{stop value}\cdot d^k)), (q) the model’s stop mass at the age, (k) its posting lag, (d = 1/(1+y)); a walk the rational boundary expected releases the same way, and the early-completion bucket is the same pair with the payoff. A book that stops exactly as the hazard says shows zero here.

Where it is derived

StopSaleDelivery posting

Also: stop-sale delivery, delivery to the Holder.

Market → Holder at the payment date after the sale: the proceeds less the Buyer’s refund, net of the fee. It can exceed the remaining schedule, because the surplus above the Purchase Price is the Holder’s.

One of the three stop postings, together with the servicing fee and Buyer refund; their amounts sum to the net sale proceeds. Receipt cash posts at ceil(missed month + lag/30.4375), with the default 18-day lag landing in the next month. The Coin view converts that receipt at its booking month spot, which can differ from the interpolated sale price.

Where it is derived

Strike

Also: coin cost, entry price, entry, coin’s cost, K.

The coin’s dollar cost at origination, fixed on the day the Agreement opens: the month’s path price, or a dispersed draw around it. The Purchase Price is the strike times the multiple.

Quantized to cents at origination, the one place a path price becomes money before a stop. The conviction rule measures its drawdown against the strike, the two lines are drawn as fractions of it, and the frontier is scale-invariant in it. With intramonth strike dispersion on, each Agreement in a cohort draws its own strike around the month’s mark.

Where it is derived

Stylised surface

Also: Stylised skew, stylised preset, btc_stylised, illustrative skew.

An illustrative Bitcoin skew — a shape, not a snapshot: ATM 41.4% from 1 to 12 months rising to 48% at 24; the 0.70 put +9, +6, +4 vol points at 3, 12, 24 months; the 1.30 call −2, −1, 0. The default surface when none is sent.

VolSurface::btc_stylised(), on tenors 1, 3, 12, 24 and moneyness 0.70, 1.00, 1.30. Its source string says so: stylised illustration, not market data; import a real snapshot. Beyond 0.70 the wing convention continues the put skew in total variance, so the deep puts of the capital-loss zone carry more than the 0.70 vol (52.8% at 0.50× at 12 months against the 47.4% quote) — a convention, until a real snapshot replaces this.

Where it is derived

Suppressed defaults

Also: suppressed stops, suppressed_defaults, suppressed.

Hazard draws that rational mode set aside because nobody was underwater that month. Counted on the run rather than dumped on an in-the-money Buyer: Marc’s answer to spec open question 3 (suppress, with a visible counter).

SimResult::suppressed_defaults. rational_mode_suppresses_when_nobody_is_underwater requires a positive count and fewer stops than the naive mode on a zero-vol bridge to $240,000.

Where it is derived

Surface interpolation

Also: bilinear, bilinear interpolation, between knots.

How a vol is read between the grid’s knots and beyond them: linear in the vol between quoted strikes (ln moneyness), total variance linear in the tenor between quoted tenors, and beyond the quoted moneyness the wing convention — total variance σ²T linear in ln K/S at the edge’s own slope, Lee-bounded, so there is no kink at the edge.

VolSurface::vol_at(tenor_months, moneyness) reads each bracketing tenor row linearly in the vol between its quotes and, beyond the quoted moneyness, by the wing convention (surface.rs::row_vol): total variance \(w = \sigma^2 T\) continues linearly in \(k = \ln K/S\) at the edge cell’s own slope \(dw/dk = 2\sigma_e\sigma’ T\), clamped to Lee’s bound \(|dw/dk| \le 2\) and floored at zero, matching the edge slope when the Lee bound does not clamp it — a flat continuation put a concave kink at every sloped edge, which is a butterfly priced below zero (model audit 2026-09-07, R01). Between two quoted tenors the two rows’ readings blend in total variance, linearly in the tenor (with price-space checks applied separately); beyond the quoted tenors the surface is flat in tenor. A flat vol has zero slope, so a flat surface reads the same everywhere. Every price beyond the quoted range is the convention, not a quote: the surface reports quoted_moneyness and wing on the wire, and every desk response carries surface_note.

Where it is derived

Surface moneyness

Also: K/S, moneyness axis, strike over spot.

The surface’s column axis: strike over spot, K/S. Below 1 are puts struck under the market (the paper’s zone), above 1 are calls struck over it. Note the engine’s own moneyness is the reciprocal, S₀/K.

The ladder table shows both: S₀/K the way contract.rs and the exposure ladder read the coin, and K/S the way the surface is indexed. Interpolation between columns is linear in ln(K/S); beyond the quoted columns, the disclosed wing convention extrapolates total variance. Deep puts outside the stylised surface’s 0.70–1.30 quoted range use extrapolated volatility, not a flat edge quote.

Where it is derived

Surface tenor

Also: tenor axis, tenors_months.

The surface’s row axis: time to expiry in months. Between quoted tenors, total variance is interpolated linearly in tenor. Beyond the quoted tenors, volatility is flat in tenor.

Tenors must be ascending and positive. The ladder’s legs are looked up at their own expiry, the missed payment date t + 1 in months; the fair value’s paths run at the 24-month ATM vol, the paper’s weighted average life.

Where it is derived

Surplus

Also: stop surplus, stop_surplus_usd, Surplus to the Holder, Stop-sale surplus, Surplus $.

Sale proceeds above the whole Purchase Price on a stop, max(0, V − P). After the Buyer has been refunded everything he paid, this is the Holder’s (Marc, 2026-09-03). Never BTC Now’s.

Regime 3 of the waterfall: the Holder is delivered \(R + (V - P)\), the remaining schedule plus the surplus. It is the long call struck at the Purchase Price that the risk desk describes, exercised only by a Buyer who stops in the money, and the reason the replay figures moved so much under the September rule. On the historical early-September reference book it sums to $148,595.85.

[ \text{surplus} = \max(0,\ V - P) ]

Where it is derived

Switch probability

Also: P(calm → stressed), P(stressed → calm), p_calm_to_stressed, p_stressed_to_calm, transition probability.

The monthly probability of leaving one vol state for the other in regime switching: P(calm → stressed) and P(stressed → calm), each in [0, 1]. Their reciprocals are the expected months spent in each state.

Named-input validated to [0, 1]. A P(calm → stressed) of 0.05 means a stressed spell arrives about once every 20 months; a P(stressed → calm) of 0.30 means it lasts about three. Both zero freezes the chain in its starting state.

Where it is derived

T

Take-profit gate

Also: settlement_min_return, Profit ≥ % of all-in cost, gate, Take-profit gate.

When on, early completion needs the coin to beat the whole Purchase Price by the stated margin: S_t ≥ P(1 + x). At the cockpit’s 10% that is $97,350 on the base coin. Off by default; the Model Card runs ungated.

Input #22 (Marc, 2026-07-12), accepted 0 to 1000%. Ungated, the Buyer is coldly marginal and compares the coin to what remains; gated, he anchors on his all-in cost, sunk payments included. The draw still happens first (v1.6) so the stream never depends on the price. A 0% gate silences every early completion on a flat path, because the coin never beats $88,500.

[ S_t \ge P,(1 + x) ]

Where it is derived

Tenor bucket

Also: tenor, TENOR_BUCKETS, months remaining, ≤ 6 mo, 7–12 mo, 13–24 mo, 25–36 mo, > 36 mo.

The ladder’s columns: months remaining on the Agreement, n − t, in five buckets ≤ 6, 7–12, 13–24, 25–36 and > 36 months. At month 23 every cell is in the > 36 column; by month 48 the book has spread across 7–36.

The tenor axis is what a desk needs to place the put ladder on a listed expiry surface.

Where it is derived

Term

Also: term_months, Term (months), 60 months, n.

Number of monthly payments per Agreement: 60 in the current program, a property of today’s offer and not of the Agreement. Nothing in the engine hard-codes it; the payment, the hazard shape and the horizon re-derive from it.

Input #6, accepted 1 to 480 months (spec v1.1, Marc 2026-07-10). The baseline hazard is drawn after payment ages 1 through term−1; a draw after term−1 can miss the final payment. A positive baseline lifetime stop prior therefore needs a term of at least 2. The fee is no longer derived from the term: 5% flat at every term since spec v1.5.

Where it is derived

The bracket

Also: four readings, three readings, behavior bracket, model-risk band.

The four readings of the same paths a Holder should run instead of one house view: price-blind (#7 alone), drawdown-multiplied (#7 + #23), rational robot (#7 + #9), and the frontier (#25 at several drifts). The band is the answer.

No single mode is BTC Now’s house view because no vintage exists to make it one. The first three differ mostly in how often an in-the-money Buyer stops; the frontier answers a different question, the floor under a fully informed Buyer with a stated belief. Zero the early-completion propensity and run again.

Where it is derived

The in-browser auditor

Also: audit, The audit, tie-out, runAudit, audit.ts, re-derived.

An independent TypeScript check of USD ledger integrity, selected USD metrics and per-Agreement contract rows against Rust, using exact integer cents for money.

audit.ts::runAudit, behind the workbench’s “The audit” button, which fetches the run with postings. Checks A (ledger integrity), B (metrics from postings) and C (contract math per Agreement, the waterfall included). A defect in agreement_table that the ledger did not share fails C and passes A. This does not independently validate Coin valuation, hedge pricing, Monte Carlo statistics or the behavioral priors.

Where it is derived

The invariant suite

Also: invariants, tests/invariants.rs, tests/exposure.rs, cargo test, gates.

The Rust tests that assert identities which must hold for every run: 88 green on 3 September 2026 (42 unit, 32 invariants, 9 exposure, 5 API), 2 ignored directional checks. Red blocks a commit; memo numbers never gate.

cargo test --release --workspace. Conservation, the closed-form fixtures, the fee identity, the N-payments identity, term parametricity, determinism, gated orderings, the September stop, fail-fast inputs and the risk desk. Tolerances: two cents per Agreement lifetime, 0.01 pp of IRR, 0.01 months of WAL. It proves the arithmetic, not the priors.

Where it is derived

The memo

Also: memorandum, the memorandum, memo §09, drawdown memo.

The July 2026 memorandum: the pricing stance (the zero-drift bootstrap), the actuarial hazard buckets, the drawdown memo’s double-trigger multipliers, and the tables the custom per-year mode mirrors. Dated before the September rules.

Its figures were produced under v1.4’s economics and must be read with the history chapter’s translation table. The parked Behavior Engine’s four-channel decomposition goes to it as prose (Marc, 2026-09-03).

Where it is derived

The monthly order

Also: four steps, steps of a month, one month, in order, step 1, step 4.

Each simulated month runs in a fixed order: originate, scheduled payments (the boundary’s decision first), walks, early completions, hazard draws. The order is part of the model: it decides who has paid before who leaves.

engine.rs::run. A Buyer who leaves by early completion or a draw at age \(t\) has already made payment \(t\); a walk consumes the date unpaid. A successfully paid final date completes the Agreement before later exit draws. A pending walk, a boundary decision before payment, or a hazard draw after the penultimate payment can instead cause that final payment to be missed.

Where it is derived

The pair

Also: pair, ladder plus spread, both seats hedged.

A combined put ladder and call spread intended to address different downside and upside exposures. Read the configured legs and their total cost together.

The component flows sum into one structure. Its USD and BTC outcomes still use different measurement conventions; the combination does not guarantee either return or remove funding requirements.

Where it is derived

The plan

Also: FUND_DESK_PLAN.md, fund-desk plan, six phases, Phase 1, Phase 2.

The original six-phase implementation plan, FUND_DESK_PLAN.md. It records design intent and proposed work, not the current release status.

Paths, surfaces, hedge comparisons and monthly analysis are now implemented. Remaining limitations and open modelling questions are described in the guide. Plan estimates are not delivery commitments.

Where it is derived

The seven posting kinds

Also: TxKind, posting kind, kinds.

Every dollar in a run is one of seven kinds: PurchasePrice, OriginationFee, PaymentDelivery, FlowFee, MakeWholeDelivery, StopSaleDelivery, StopRefund. BTC Now’s take is OriginationFee plus FlowFee and nothing else.

ledger.rs::TxKind. There is no posting kind for a share of a stop sale, and the repository’s rules forbid adding one. Two identifiers predate the vocabulary: MakeWholeDelivery is an early-completion payoff and PurchasePrice names the Holder’s purchase of the paper, not the Buyer’s Purchase Price.

Where it is derived

The shelf

Also: shelf, Table 18, ShelfRow, ShelfParams.

Illustrative market references evaluated on the same market path and origination dates: spot holding, fixed-rate BTC growth, call overwrites, basis and other stated alternatives.

hedge.rs::benchmarks_with_shelf uses a contractual one-coin-per-Agreement basis and the supplied ShelfParams. Agreement purchase prices, recycled cash, strategy contributions, execution and collateral conventions can differ. Same seeds alone do not make these equal-capital comparisons. Research’s matched-contribution holding reference is a separate benchmark.

Where it is derived

The split

Also: split hedge, the other 20%, futures plus calls.

A combined structure assigning part of the modeled coin exposure to futures and part to calls. The selected sizing and strike rules define the proportions.

A split is one strategy with all component cash flows and costs counted. Read futures settlement, basis and option premiums, settlements and open value separately. Historical sample proportions are presets, not universal recommendations.

Where it is derived

The tie-out harness

Also: audit harness, audit-harness.ts, harness.

The same auditor run from Node against the engine on four adversarial configurations, exiting non-zero on any failure: the base bridge, everything on at an awkward 48-month term, a replay with no stops, and a $3.37 coin over two months.

cd web && npx tsx app/forwardflow/audit-harness.ts. On 3 September 2026 it printed 32/32, 32/32, 32/32 and 29/29 ties on 240, 70, 240 and 15 Agreements.

Where it is derived

The two lines

Also: lines, LineRow, two_lines.

The two curves every desk draws on BPA paper: the schedule line and the capital line, both sale proceeds as a fraction of the entry price by payments made, both falling every month. Scale-invariant in the strike.

exposure.rs::two_lines; pinned by two_lines_match_the_plan_table. Between the lines a stop leaves a shortfall against the schedule but returns the Holder’s capital; below the capital line it does not. The 25 bp sale cost is not in the lines; it enters through the proceeds when the lines meet a path.

Where it is derived

Theta

Also: theta_usd_per_month, Θ, markup accrual.

Not a bump: the markup accrual on a path with no price risk. The book is run on a flat bridge and the net gain divided by its weighted average life: +$127,160 per month on the base configuration ($4,571,951 over 35.95 months).

Positive by construction; the flat path’s own stops are already inside it. Unchanged by the as-of month.

[ \Theta = \frac{G_{\text{flat}}}{\text{WAL}_{\text{flat}}} ]

Where it is derived

Tornado

Also: assumption tornado, What actually kills this, TORNADO, stresses, S13.

Seven stresses, each a mutation of the current base configuration run at its seed; the bar is the stressed effective IRR minus the base in percentage points, sorted by damage. Clicking a bar loads the scenario.

lib.ts::TORNADO: origination stop at month 3, BTC −70%/3mo permanent with flow continuing, 90% lifetime stops, stop-sale haircut 50%, the behavioral floor (X = 0, Y = 2), crash plus origination stop, and all three. The early-September worked example in the results chapter is historical; current outcomes depend on the saved scenario. If the base or stressed run has no IRR, the change is unavailable, with its reason shown separately and no numerical bar.

Where it is derived

Transfer

Also: Transferor, Transferee, buyer swap.

The paying side of an Agreement changing hands: the Transferor hands over, the Transferee takes over, for a $250 transfer fee. Not modelled; the engine has one Buyer per Agreement.

Transferor and Transferee keep the legal direction of the Assignment and Transfer Agreement. The engine has no notion of a person across Agreements, so each Agreement is one Buyer, once.

Where it is derived

Turnover

Also: futures_turnover_coins_mean, futures_trades_mean, trades, trade count.

futures_turnover_coins_mean: coins the futures leg traded over the life, mean over seeds — the opening, every reset, the close-outs — and futures_trades_mean, the months a reset moved the position. What a rebalancing rule costs.

The minimum trade thins the count monotonically (test: 55 trades at zero, 49 at (10^{-6}) coins, 5 at 0.05); lots of five on a 40-Agreement book move the notional by at most 2.45 coins against the unrounded leg. A reset that moves nothing is not a trade.

Where it is derived

U

Undiscounted multiple

Also: multiple, MOIC, moic, Undiscounted multiple, cash multiple.

Gross cash received over gross cash invested, ignoring timing: 1.3779× on the reference Agreement ($82,673.75 on $60,000). It keeps the IRR honest; amortizing paper has modest multiples at healthy IRRs. The backtest calls it moic.

None when nothing was invested. Defined on gross flows, so a flat 24-cohort book reports the single-Agreement multiple. The M0 fixtures at 105% and the old fee print 1.3295× for the same Agreement.

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

Where it is derived

Unhedged distribution

Also: unhedged, Unhedged — the paper alone, the paper alone.

The paper alone on the same seeds: the Holder’s IRR and multiple distribution with no hedge flows. Identical for every structure in one request and to the benchmarks’ paper row, because they share the seeds.

HedgeResult::unhedged. It is the control every hedged number is read against — the same config, the same seeded paths, the same behavior — so any difference is the structure alone.

Where it is derived

Unrecovered capital

Also: U_t, unrecovered_capital_usd, Unrecovered capital $.

The Holder’s purchase price less the net payments it has received so far, floored at zero: $60,000 after payments 0 and 1, then $60,000 − $1,401.25 (t − 1), reaching zero at payment 44 at the base terms.

The numerator of the capital line and, summed over active Agreements, the risk desk’s capital at risk. It counts net payments after the first N, so the payment-1 refund-base question does not touch it.

[ U_t = \max!\Big(0,\ \text{purchase} - \sum_{k=N+1}^{t} p_k,(1-f)\Big) ]

Where it is derived

Uploaded path

Also: upload, path upload, uploaded prices.

A price path the desk supplies as a list of monthly prices; the engine rebases it to the start price and runs the book on it.

The upload must cover the whole horizon (cohorts plus term plus one month); extra points are ignored and a short list is refused with the named input. It lets a desk run its own generator’s paths through the engine without trusting ours.

Where it is derived

V

Variance swap

Also: var swap.

A contract paying the difference between realised and agreed variance, in vol points times a vega notional — the way a desk hedges the volatility in the paper rather than its price.

Struck at the surface’s at-the-money vol for the tenor and rolled through the horizon on its CONTRACTUAL tenor; realised variance is the annualised zero-mean sum of squared monthly log returns. Since the model audit of 6 September 2026 (M10) every swap is marked each month at its remaining-life value — (\tfrac{N_{\text{vega}}}{2K}\big(\sigma^2_{\text{exp}} - K^2\big)) with (\sigma^2_{\text{exp}} = (\sigma^2_t, t + K^2 (T - t))/T), the realised months as they were and the unrealised at the strike (hedge.rs::variance_swap_mark) — zero at inception, decaying to (-N_{\text{vega}} K/2) in vol points on a path with no moves; the settlement on the full contractual window is booked against the mark it replaces (option_value), and a swap whose window runs past the run’s last month is marked there on the (open at the horizon, marked) leg, never settled on a window cut to the horizon. Its settlements arrive late and can be large, which is why the hedged IRR is solved as the root nearest the unhedged rate.

Where it is derived

Variation call

Also: variation_call, the call, margin call.

The futures cash the month took, floored at zero: max(0, −(futures mark + futures basis)). The call the desk posts; a received basis is not a call, and a flat path with a zero basis posts nothing.

Non-negative on every seed every month; the worst single call across the seeds is the margin buffer’s headline figure.

[ \text{call}_m = \max\big(0,, -(\text{futures mark}_m + \text{futures basis}_m)\big) ]

Where it is derived

Vega

Also: vega_usd_per_vol_point, vega_method, Vol bump (points), vol_bump.

Change in net gain per one-point rise in volatility. On every path mode — the bridge included — the base path is kept through the as-of month and the deviations of the log returns after it are scaled around their mean, never a regeneration; +$8,631 per point on the base configuration (the earlier +$29,718 re-drew the bridge from inception, strikes included), zero on an existing book of fixed schedules, negative on a falling bridge.

Vega has two signs under the September waterfall: the Holder is short a put at the schedule and long the surplus above the Purchase Price, so a wider path feeds the call as much as the put. On the base bridge pinned at $60,000 at both ends the ensemble vega is positive; greeks_have_the_signs_of_a_short_put_on_a_stopping_book pins it negative on a bridge to $30,000. It is a realized-volatility sensitivity, not an implied-volatility vega; the spec’s −$24k figure predates per-Agreement streams and is stale.

Where it is derived

Vega notional

Also: vega notional $ per vol point, vega_notional_usd, variance notional.

The variance swap’s size in dollars per vol point, 0–1e12. The swap pays vega notional ÷ (2K) × (σ² − K²) in vol points, long variance, K the surface’s ATM vol for the tenor; no premium, the execution cost is bps of this notional.

HedgeSpec::VarianceSwap { tenor_months, vega_notional_usd }, the standard convention. Realized variance is the zero-mean annualized sum of squared monthly log returns over the swap’s months; the swap is rolled at the surface’s then-ATM through the horizon. It pays when the path is rougher than the surface said it would be, whatever the direction.

[ \text{settlement} = \frac{N_{vega}}{2K},\big(\sigma_{realized}^2 - K^2\big) \times 100 ]

Where it is derived

Vintage

Also: vintages, VintageRow, origination month, cohort by origination month.

Agreements grouped by origination month. In the backtest, one cohort originated at a historical month and replayed on the real path that followed; on the risk desk, a row of the credit table.

The backtest returns 113 vintages at a 60-month term, February 2012 to June 2021, each carrying entry close, IRR, MOIC, net gain, deployed, shortfall, the exit counts, refunds, surplus, coin taken and BTC Now’s take. No BTC Now vintage has yet been observed; the word here means simulated history.

Where it is derived

Vintage backtest

Also: backtest page, static-pool analysis, Every vintage, decomposed.

The static-pool exhibit: one cohort at every seasoned historical month, replayed on the actual Bitcoin path that followed by the same engine, exits decomposed and netted to the Holder after fees, with a blended buy-every-month row.

Replay constructions carry Bitcoin’s own history and its drift; they are descriptive, not probabilistic, and the early vintages dominate the blend: a 2012 entry at $4.90 rebased to $60,000 rides the whole rally. Read the rows, not the blend, and read every replay figure with the in-the-money-stop sentence attached.

Where it is derived

Vol surface

Also: volatility surface, VolSurface, surface, the surface, edited surface.

Implied volatility by tenor and moneyness K/S: rows are tenors in months, columns are strikes as a share of spot. Every option the desk prices is read off it; between quotes the vol is interpolated, beyond the quoted moneyness the wing convention prices, and every surface is checked for arbitrage in price space before it prices anything.

surface::VolSurfacetenors_months, moneyness, vols (one row per tenor) and a source string that says where the numbers came from. It can be built directly, from the two presets (flat, stylised) or from a delta-quoted grid (from_delta_quotes), the shape Deribit and the OTC desks quote in: each delta is converted to a strike at its own vol, K/S = exp(σ²T/2 − d₁σ√T). The cockpit’s grid is editable cell by cell; an edited surface is sent back to every desk endpoint as surface. Both presets are shapes for the exhibit, never market data — the desk imports its own snapshot.

Where it is derived

Vol-surface placement

Also: placement, placement exhibit, Placement, /api/forwardflow/placement.

The exhibit that draws the paper’s put ladder on the surface: each leg’s strike and tenor, the vol read there, the put’s value and its weighted contribution, with the paper’s own implied vol beside the market’s 12-month ATM.

surface::placement(config, surface, r) behind POST /api/forwardflow/placement. Returns the legs, paper_implied_vol, surface_atm_12m, ladder_value_usd_per_agreement and markup_usd_per_agreement. It is the exhibit a derivatives desk draws before it talks about price: where the paper’s puts sit on the skew, and whether the markup pays for them at the market’s vols.

Where it is derived

W

WAL

Also: weighted average life, Weighted Average Life, wal_months.

Weighted average life: the gross-inflow-weighted mean month of receipt, in months from simulation month 0. How long the average delivered dollar was out. 31.0 months on the reference Agreement; 34.9 on the base book at seed 42.

None when there is no inflow. Defined on gross inflows, so a paced book’s cohort offsets are inside it: a flat 24-cohort book reports 31 + 11.5 = 42.5. The paper pays back monthly from month two, so the average dollar is out far shorter than the term.

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

Where it is derived

Walk cost

Also: walk_cost_of_strike, Walk cost, % of coin cost, cost of walking.

What walking costs the Buyer as a lump, as a fraction of the coin’s cost: 2.5% by default, $1,500 on the base coin, about one payment. It lumps the six-month lockout, the re-strike at market and the lost access into one number.

Subtracted from the refund in the lattice’s walk payoff. A fraction of the strike so that the frontier is scale-invariant. A higher walk cost deepens the frontier (walk_cost_and_belief_deepen_the_frontier). It is the only place a lockout is priced, and on the Buyer’s side.

Where it is derived

Walk-away frontier

Also: frontier, walk_below_of_entry, walk_below_spot, walk_below_moneyness, FrontierRow, rational frontier.

The spot below which a rational Buyer with a given belief is better off walking, one number per payment date, by backward induction on the lattice. About half of entry through year one at 25% drift; 1.16 of entry at payment 1 at zero drift.

boundary.rs::rational_frontier. Walking beats settling only where the refund is zero, so the frontier lies at or below the schedule line and is a single threshold per date. It is the crossing between the highest node where walking wins and the next node up, interpolated in log-spot; None means no node walks. Reported in dollars, as a fraction of entry and as moneyness against \(B_{t-1}\). Every curve meets at 2.4% of entry at payment 59 and nobody walks at payment 60.

[ S^\ast_t = S_k \cdot \exp\Bigl(\sigma\sqrt{\Delta} \cdot \frac{g_k}{g_k - g_{k+1}}\Bigr) ]

Where it is derived

Waterfall

Also: stop waterfall, R-1033, the ruled waterfall.

How stop-sale proceeds are divided: the Holder is paid the remaining schedule first, the Buyer is refunded up to what he paid in, and any surplus above the Purchase Price stays with the Holder. Nobody owes anything after.

Ruled 2026-08-22 (the refund formula) and 2026-09-03 (the surplus to the Holder), applied in spec v1.5. Three regimes follow: \(V \le R\) (refund 0, shortfall \(R - V\)), \(R < V \le P\) (the Holder gets exactly \(R\), the rest refunds the Buyer), \(V > P\) (the Buyer refunded in full, the Holder keeps \(R + V - P\)). BTC Now takes its 5% of the delivered dollars and nothing else.

Where it is derived

WebSocket

Also: montecarlo/ws, Progress frame, chunked Monte Carlo.

GET /api/forwardflow/montecarlo/ws: the same Monte Carlo over a socket in chunks of 1,000 runs, a Progress frame per chunk, then Complete with the summary, so a heavy book neither times out nor looks dead.

The client’s first text frame is the request JSON. A cached request skips straight to Complete; a disconnect stops the server after the current chunk. Streamed and one-shot results are the same numbers. The URL is derived from the API URL by swapping the scheme, so a hosted backend must serve wss too.

Where it is derived

Websocket token

Also: token, ws token, ?token=.

The one credential the Monte Carlo websocket takes besides a key: ?token= = base64url(<key-name>|<expiry>) + . + base64url(HMAC-SHA256 over it), expiry at most 10 minutes ahead. Minted server-side; the browser never sees a key.

Signed with FF_TOKEN_SECRET (at least 32 characters; unset = tokens disabled). The token names a key, so the socket is counted against that key’s limits. A desk’s own code needs none — it sends X-API-Key on the upgrade.

Where it is derived

Wing convention

Also: wing, quoted_moneyness, Lee’s bound, beyond the quoted range, LEE_SLOPE_BOUND.

Beyond the quoted moneyness each tenor row continues linearly in total variance (σ²T against ln K/S) at its edge cell’s own slope, clamped to Lee’s bound |dw/dk| ≤ 2 and floored at zero — matching the edge slope when the bound does not clamp it. A convention, never a quote.

surface.rs::row_vol, WING_CONVENTION (model audit 2026-09-07, R01). The flat continuation it replaced put a concave kink in the vol at every sloped edge, and a kink in the vol is a butterfly priced below zero; a wing steeper than Lee’s bound is itself a static arbitrage, so the slope is clamped and the check names the clamp when it bites. Between quoted tenors total variance is linear in the tenor; beyond them the surface is flat in tenor. The surface reports quoted_moneyness and wing on the wire and every desk response carries surface_note. On the stylised preset the put wing moved the fee-gross embedded ladder $5,598.11 → $5,659.39 per Agreement; the flat preset is bit-identical.

Where it is derived

Worst

Also: worst run, worst observed run, floor.

The minimum of the distribution: the single worst run observed. 7.21% effective IRR at the base cockpit configuration over 1,000 runs. Reproducible alone by its seed.

Every run of a Monte Carlo is a full seeded run, so the worst one can be loaded into the cockpit at seed base + i and read path by path.

Where it is derived

Worst month of cash

Also: futures_worst_month_of_par_median, futures_worst_month_of_par_p95, worst month.

The largest modeled monthly futures cash outflow from settlements and basis, expressed against the stated reference amount. It is a monthly cash screen, not a daily margin limit.

Report the result’s sample and sign convention. Initial collateral, short-option/swap requirements, intramonth moves and forced closure can add funding needs outside this measure. A sample percentile is not a guaranteed maximum.

Where it is derived

Worst path

Also: worst_path_irr, worst_path_seed, worst seed.

The lowest hedged IRR across the seeds with one, and the seed that produced it; null when no seed has an IRR, when the worst multiple stands in. Run that seed on the workbench under the same config to see the path.

HedgeResult::worst_path_irr (None when no seed has an IRR) and worst_path_seed, beside worst_path_multiple and worst_path_multiple_seed, which are always present. Seed k of the desk’s seeds is the config’s seed plus k, so the worst path is reproducible anywhere the config is. It is the tail beyond the p5 and the first thing to inspect when the floor looks too good.

Where it is derived

Z

Zero-drift bootstrap

Also: bootstrap, Real returns, trend removed, ZeroDriftBootstrap, block bootstrap, de-meaned.

Real monthly log returns from a chosen regime, mean removed, glued together in blocks of six consecutive months. History’s volatility with no assumed appreciation: the memorandum’s pricing stance and the Model Card’s production mode.

paths.rs::bootstrap. Zero drift means zero EXPECTED LOG RETURN of a generated path: the blocks are drawn circularly (a start on any of the window’s n returns, wrapping past the end, the last block cut at the horizon), so every observation weighs exactly 1/n under the sampler’s own rule and the de-meaned mean, zero, is the expected log return of every month; the median and the mean of the price factor are the empirical block distribution’s, not a lognormal’s — on the pricing window the median monthly log step is −0.46% and log(mean(exp(step))) +0.69%, so the median price drifts slightly down and the mean up; no exact Gaussian mean/median relation is claimed (model audit 2026-09-07) (model audit 2026-09-06, M03 — the earlier non-circular draw carried +1.24% / −1.73% / −3.67% a year of log drift on the three windows). De-meaning asserts that the paper’s return is earned by the schedule, not by the coin: Bitcoin’s mean monthly log return is +5.5% over the full history and +3.7% from 2017. The endpoint is free. The three windows in use: full history from Feb 2012 (86.5% realized), the modern regime from Jan 2017 (69.1%, the cockpit’s default), and the trailing 24 months from Jul 2024 (40.6%, the pricing regime).

Where it is derived

Zero-rate convention

Also: zero rate, at zero rate.

Options on the hedge desk are priced with the interest rate set to zero: the surface’s volatility does all the work, and the funding rate only appears where dollars are discounted.

The hedge overlay prices every option leg with Black-Scholes at a zero rate, on the surface’s vol for that strike and tenor. Present values in the placement and fair-value exhibits use the funding rate instead. Keeping the two apart makes a hedge’s cost a pure volatility number and a fair value a pure discounting number.

Where it is derived

Limits and assumptions

ForwardFlow calculates the consequences of an explicit scenario. Its tests check arithmetic, accounting identities and selected model properties. They do not establish that Buyer behavior, market assumptions or execution conditions describe future outcomes.

This chapter distinguishes implemented features from remaining modelling gaps. The verification chapter describes the checks; the surfaces and hedging chapter describes the current hedge and monthly analysis capabilities.

Buyer behavior is not calibrated

The repository does not supply an observed BTC Now vintage dataset for calibrating stop rates or early completion. Treat the behavior settings as priors. Historical Bitcoin prices provide market paths, not observations of how Buyers would have behaved under these Agreements.

AssumptionBase engine settingInterpretation
Lifetime baseline stop target40%Calibrates the baseline hazard before competing exits and behavior overlays; the realized stop share can differ
Drawdown multipliersOffWhen enabled, scale the stop hazard with entry-relative drawdown; the multipliers are assumptions
Early-completion propensity2.5% per monthA scale factor for an equity-dependent probability, not a flat 2.5% exit rate
Rational boundaryOffAn alternative decision rule using assumed beliefs, discount rate, volatility and walk cost

Sources: defaults.rs::DefaultScenario, engine.rs::SimConfig::default, engine.rs::drawdown_multiplier, boundary.rs::BoundaryParams.

Compare price-blind behavior, drawdown-scaled hazards and the rational boundary. Also run with early-completion propensity at zero. Early completion accelerates the remaining scheduled receipts relative to full scheduled completion, but it also removes exposure to a later stop and its possible surplus. Its effect on the whole portfolio is scenario-dependent.

In-the-money stops drive upside

A stop can benefit the Holder when sale proceeds exceed the Purchase Price: the Holder receives the remaining schedule plus the surplus, before its servicing fee. The frequency and timing of these stops are particularly important on rising historical paths.

With drawdown multipliers off, the baseline hazard is price-blind. With them on, the in-the-money multiplier is 0.5. Neither setting is an observed stop rate for the program. The rational-boundary mode offers a different behavior construction. Compare them rather than interpreting a high replay return as evidence that surplus stops will recur.

The waterfall is in engine.rs::stop_sale; the behavior rules are in Buyer behavior. Keep the stop prior and behavior mode beside any quoted historical return.

Vega can have either sign

The Holder is exposed both to shortfalls and to surplus proceeds. Wider price paths can affect both, so the sign of the paper’s volatility sensitivity depends on the scenario and Buyer behavior.

The risk analysis computes a continuation sensitivity: it keeps the path through the as-of month and scales log-return deviations after that month. This applies to every path mode, including the bridge. A flat path has no deviations to scale. This is a sensitivity to the modelled path amplitude, not an option surface’s implied-volatility vega (exposure.rs::greeks).

A volatility surface and hedge overlay are already implemented. Their separate pricing conventions are described in Surfaces and hedging. A sign in one risk-analysis run is not a universal hedge direction.

Monthly resolution

The engine uses one price mark and one payment step per month. A stop sale is priced between adjacent marks by log-linear interpolation. At the base lag of 18 calendar days after the missed payment, the sale sits about 0.5914 months after that date; cash posts at the first monthly date at or after the sale (engine.rs::stop_sale_timing, using the ceiling of the fractional month).

The 18-day lag approximates the program’s day-16 stop plus a sale within two business days. It does not model an actual business-day calendar, venue execution or intramonth price recovery. Monthly margin and intramonth stress proxies likewise do not reconstruct an observed daily collateral account.

One path, many seeds, or historical starts

A single scenario answers what happens on one seeded path. Monte Carlo summarizes the specified path family across seeds. A historical analysis compares start months from the embedded price series, with simulated Buyer exits. These are different constructions.

The workspaces can include a single-run exhibit and a distribution on the same page. Read each exhibit’s run count and construction. IRR statistics can be conditional on seeds with a defined IRR; keep missing and ambiguous IRR counts, cash-loss frequency and cash multiples alongside them. See Results and returns.

Historical starts can overlap substantially and share the same market episodes. They are not independent trials or a probability distribution for future performance. The embedded series is identified by the engine’s historical-data digest; it is not a live price feed.

A bridge fixes the terminal price

A Brownian bridge conditions every path on the selected end price at the market horizon. Its Monte Carlo spread measures variation along the path, while all paths share the same terminal price. Moving the horizon changes the question.

The engine also supports GBM, jump diffusion, regime switching, historical replay, a zero-drift bootstrap, custom anchors and uploaded monthly paths (paths.rs::PathMode). Choose a model that expresses the question; an unpinned stochastic model also brings its own drift, volatility and distribution assumptions.

Sale terms and execution

The model sells at the interpolated path price, applies a log haircut, and deducts a sale cost—25 basis points by default. It does not select a venue or establish that this execution price is attainable.

The documented program ruling allocates sale costs to the Company, while the engine deducts them from sale proceeds before the waterfall. This affects how the cost is allocated between the Holder and Buyer. The sale standard, cost allocation and authoritative Agreement wording need to be reconciled by the program owner; this guide records the implemented convention. See The Agreement.

Payment 1 in the refund base

The model includes the first payment in the Buyer’s paid-in amount, even though BTC Now retained it (engine.rs::Agreement::paid_in). The program specification flags confirmation of that treatment as a term-sheet question. At the base terms, the first payment is $1,475. The stop waterfall shows how paid-in cash limits the refund.

Lockouts and re-entry

The engine has no persistent Buyer identity, re-entry history or operational enforcement of lockouts. Each Agreement is simulated independently as an Agreement, not as a complete lifetime relationship with a person.

The rational boundary’s walk cost is an assumed economic penalty. It does not simulate a six-month lockout, its enforcement or a later Agreement. Custody, paying-side Transfers, secondary Sales between Holders and Buyer screening are also outside the cash-flow engine.

Synchronized stops and market impact

Shared price moves and behavior rules can cause many stops in the same period. The core stop-sale model still applies the same configured haircut and sale-cost rate regardless of the quantity sold. It does not derive execution from market depth or congestion.

A zero haircut is a modelling assumption, not proof that a portfolio has no impact. Stress sale costs and haircut alongside clustered exits, and state the assumed execution conditions. The hedge trading-cost settings are separate from an endogenous impact model for Agreement stop sales.

Surfaces, jumps and hedge limitations

Jump models, regime switching, surface import, option pricing, fair-value analysis, hedge comparisons and monthly marks are implemented. The earlier description of these as future features is superseded.

The remaining limits matter:

  • Surface quality. The default surface is a stylised illustration, not a market snapshot. Importing quotes does not guarantee their freshness or liquidity. Prices outside the quoted moneyness use the disclosed wing convention; outside quoted tenors they use the stated tenor convention. Discrete arbitrage checks do not prove continuous arbitrage freedom everywhere.
  • Fair-value assumptions. The calculation uses an assumed market process, volatility and discounting basis, while Buyer behavior remains a model input. Its output is a model value, not an executable dealer quote.
  • Hedge lifecycle. Options can outlive an Agreement. Futures may remain after exit under a calendar policy unless configured to close. Written options can be naked against the Agreement. Retain the result’s coverage and lifecycle warnings.
  • Funding and liquidity. Reported cash needs, initial margin and variation proxies do not form a constrained cash account that enforces collateral, solvency or forced deleveraging. Positive lifetime P&L does not demonstrate that an institution could meet every interim cash call.

Sources: surface.rs, hedge.rs, series.rs; see Surfaces and hedging.

Servicing and legal interpretation

The core Agreement simulation assumes servicing continues under the configured rules. It does not simulate a servicing interruption, transition to a backup servicer, or a legal change that impairs the Agreement’s cash flows. Operational custody, enforceability and the governing documents require review outside this model.

Saved results and historical references

The Scenario report now displays completed server responses. The frozen July first-paint and offline fallback has been removed. If an analysis fails, its error remains visible; a draft edit does not relabel an earlier result as a new calculation.

Some worked figures and test descriptions in the technical chapters record a dated configuration or earlier specification. They are examples and historical evidence. Re-run on the intended source build and historical dataset before using them as current figures.

The ignored July tornado reference remains a historical comparison, not a passing acceptance gate for the September waterfall. Verification records that distinction.

Capabilities and remaining gaps

AreaImplementedRemaining limitation
Scenario researchSeeded simulation, distributions, comparisons and price solverOutcomes depend on supplied assumptions
Market modelsEight path modes, shocks and surface inputNo automatic calibration or live quote feed
Risk and hedgingExposure, sensitivities, hedge comparison and monthly analysisSimplified execution, lifecycle and collateral assumptions
EvidenceLedger checks, tests, completed-run exports and MCP replay metadataTests do not validate empirical priors or legal terms
AccessWeb workspace, REST API and invitation-based MCP sourceA deployed build and a supported client must be verified separately
Portfolio operationsModelled cash flows and hedge cost conventionsNo production servicing, custody or trading system

FUND_DESK_PLAN.md records the original phased plan. It is not a current release manifest or a commitment to delivery dates.

Questions for the program owner

Confirm the authoritative treatment of payment 1 in the refund base, sale-cost allocation, the sale standard and the structure of the Holder’s ownership. Distinguish those contractual decisions from analysis choices such as the behavior base case, discounting basis and surface source.

A scenario can choose a convention to quantify its effect. It cannot settle an unresolved term. The dated versions and rulings chapter records the decisions cited by this guide; the latest governing documents must establish their current status.

Before sharing a result

Retain the full assumptions, seed sequence, response warnings, engine build and historical-data identity. State whether the result is a single path, Monte Carlo distribution, historical replay or model value. Show downside and cash needs beside return measures, and separate empirical evidence from chosen priors. Reproduction and interpretation are both necessary parts of a review.

Measurement and release status

BTC cash-flow IRR, total return, gross purchases and net contributions answer different questions. Read the measurement contract and release model card. A surviving derivative mark is economic value, not received cash; a purchase-yield attribution mark does not certify market-participant fair value. No finite Monte Carlo sample establishes a return floor in every future path.

Dollar and Coin workspaces

Choose the workspace that matches how you measure success. Dollar focuses on growing USD capital. Coin focuses on growing BTC holdings compared with holding Bitcoin.

Both use the same Agreement model. Your choice changes the questions, strategy selection and measures you see. It does not change the Agreement’s contractual dollar payments or the Buyer’s rights.

Choose your objective

Dollar workspaceCoin workspace
Main resultDollar gain and NPV at your stated dollar hurdleBTC surplus against holding matching dated BTC contributions
DownsideProbability and severity of losing dollarsProbability and severity of ending below the BTC holding reference
Strategy comparisonUSD improvement over unhedged AgreementsBTC improvement over unhedged Agreements
Adverse pathsActual paths ranked by dollar outcomesActual paths ranked by BTC outcomes

Open the workspace chooser. Each workspace keeps its own assumptions, selected strategies and completed comparison in your browser. Use Use in another workspace to copy a scenario deliberately. The target’s previous draft is saved aside and can be restored. Existing scenario links and the original research desks remain available.

Four steps

  1. Research: choose a question, edit assumptions and watch one simulated Bitcoin price path update. Review the Agreement’s cash flows and exits, then run Monte Carlo to see a range of outcomes in your workspace’s units. New starting cases use an illustrative GBM with zero drift and 43% annual volatility, so the terminal price can vary. Existing links retain their own market model. These settings are assumptions, not forecasts.
  2. Compare strategies: inspect the selected variants on the same simulations used in Research, or change the selection and run again. Start with 256 for exploration; larger runs and separate-seed checks help assess sampling stability. Unhedged Agreements are always included.
  3. Risk & cash: inspect the loss distribution, cash screen and an actual adverse seed. Dollar and BTC can rank the same path differently.
  4. Evidence: challenge the completed run with separate seeds or a named stress, review the conventions and export the samples and exact requests.

The price path is one seeded example, not a median path or a forecast. Bitcoin market prices are shown in USD per BTC in both workspaces. The unhedged Agreement preview accumulates net cash in Dollar and monthly BTC conversion equivalents in Coin; the Coin chart is not a funded wallet balance. Monte Carlo reports the selected strategies across matching seeds, using dollar outcomes in Dollar and BTC outcomes in Coin.

Research also links directly to stress maps, the purchase-price solver and Agreement detail. These supporting exhibits retain their USD Agreement measures. The comparison shows trade-offs; it does not search every possible hedge or automatically choose the best strategy for you. Advanced analysis provides custom strategy construction, all assumptions, the full USD Agreement research desk and ledger, monthly exhibits, USD exposure and sensitivity, and historical Agreement vintages. Each exhibit keeps its stated units.

Reading the Coin result

Each Agreement represents one BTC. Purchase funding uses its own entry price, so 240 par Agreements deploy 240 BTC, including when entry prices vary within a month. A purchase premium or discount changes that funding amount. USD receipts and hedge flows convert at the month’s simulated BTC price; their net BTC flow determines additional contributions or recovery. The holding reference retains those same dated net contributions. BTC surplus is recovery minus that reference.

Gross BTC deployment counts all Agreement purchases. Same-month receipts can support later purchases, and hedges can need additional capital. These offsets and costs explain why net contributions differ from gross deployment. The selected-path cohort table reconciles unhedged purchase cost, receipts and gain to the book. Each strategy can require different additional contributions; the comparison does not assume an identical starting wallet.

Economic outcomes include any surviving derivative mark at the horizon. A positive mark is not already received cash; a negative mark can imply an additional terminal economic contribution. Realized-only contributions, recoveries and signed marks are available separately. The economic and realized bridges each retain both their contribution and recovery sides. No BTC discount rate is assumed.

This is an analytical conversion convention, not a funded BTC wallet. Actual conversion trading costs, cash reserves, borrowing and external free BTC are not represented by this benchmark. See hedging methods for the exact definitions.

BTC cash-flow IRR and wallet growth

BTC cash-flow IRR is an annualized return on dated investment cash flows. It is not the annual growth of an entire starting BTC wallet. Purchases are funded at each Agreement’s entry price; USD receipts and hedges convert at monthly spot. IRR accounts for the dates of these BTC contributions and receipts and excludes unused BTC reserves. Falling Bitcoin prices can make dollar receipts buy more BTC, so BTC IRR can be much higher than USD IRR on the same path.

A single contribution of 100 BTC and recovery of 110 BTC gives 10% total return, after any costs already included in those flows. It is also 10% annualized IRR only when those are the only flows and exactly one year apart; over two years the annualized rate is about 4.88%. Dated payments explain why both the rate and the amounts matter.

The Research headline labels this rate This path’s BTC cash-flow IRR. It always describes unhedged Agreements on one seed. Choosing a strategy does not apply that hedge to the preview. Use the strategy selector in completed Monte Carlo results to see the hedged distribution. A draft change does not recalculate an earlier completed comparison.

To check a particular return, use Export this path. The file preserves the completed assumptions, amounts and engine version. Read gross BTC deployed, net contributions and recoveries alongside the dated IRR. Starting-case results can change when assumptions or calculation methods change; figures from an earlier release should stay attached to that release’s saved result.

For a wallet comparison, define the starting BTC budget, reserves, conversion costs, future cash calls and any derivative collateral first. Use the same starting budget for every strategy and holding BTC, and check that the wallet can meet its obligations on every simulated path. The current contribution-matched benchmark does not establish that feasibility.

Understand the Bitcoin growth assumption

The GBM drift setting is a continuous annual parameter. Expected one-year price growth is exp(drift) − 1; median one-year price growth is exp(drift − volatility² / 2) − 1. At the starting 0% drift and 43% volatility, expected price stays constant while median price declines about 8.83% per year. Zero drift is therefore not a flat typical price path. These are properties of the assumed model, before any shock or bump, not market forecasts.

Coin Research shows these implications beside the completed price path. Its growth sensitivity check changes the drift on matching seeds, keeps the configured volatility and selected hedge, and reports BTC cash-flow IRRs separately for unhedged Agreements and that hedge. The check runs only when requested. Read its simulation count and unavailable or ambiguous rates; an exploratory sample does not establish tail stability. To test a truly flat price control, set both drift and volatility to zero and remove shocks and bumps: BTC and USD cash-flow IRRs should then agree.

Read dollar cash needs separately

In both workspaces, operational cash estimates are labelled USD. That screen assumes receipts remain available as dollars under monthly netting. The BTC outcome assumes monthly conversion equivalents. Converting receipts could remove dollars needed for a later margin call, so the two readings do not establish one jointly funded strategy.

A cash allowance checks whether the modeled monthly need exceeds an amount. It does not change trades or simulate forced closure. Daily margin calls and a reserve/conversion policy require further modelling. Read limits and assumptions before relying on a result.

Keep the evidence

Completed results retain their original inputs when the draft changes. A notice tells you when the displayed comparison uses earlier assumptions. Navigation reuses that completed experiment; leaving during a run cancels its unfinished batches.

Saved results from before engine 0.6.0 retain their earlier monthly-purchase conversion convention and carry a legacy-funding notice. Run a new baseline before replaying or comparing them with current results. New evidence packages specify the unit of every summary field; USD cash requirements stay USD inside a Coin package.

Browser storage is bounded. If a sample cannot be saved, export it before closing or reloading; the interface reports the problem. Browser-local drafts are not shared team accounts.

Export assumptions to transfer the draft, or export all samples from Evidence to retain the completed analysis. The run includes its objective, benchmark/conversion conventions, selected strategies, seeds, engine build and data identity. Replaying an adverse path checks that it reproduces its saved outcome.

Ask through MCP

Use the same MCP connection for either objective. For example:

I measure success in BTC. Compare these strategies against holding the matching contributed BTC. Show the downside and state the cash-conversion assumptions.

Or:

I measure success in USD. Compare the dollar gain, loss severity and cash needs of the unhedged Agreements and these hedges.

The hedge_risk_samples tool accepts objective: "coin" or objective: "dollar". An assistant should clarify an unspecified objective before ranking strategies. No second connection or access token is needed just to change objectives.

The measurement guide defines gross purchases, net contributions, recovery ratios, BTC cash-flow IRR and mark treatment. The release model card identifies the supported version and remaining validation limits.

Inputs and defaults

This chapter explains the core simulation inputs using the specification’s established input numbers. It gives the JSON field, default, validation bounds and effect. Workspace labels may be shorter than the historical labels in these tables. The API explorer supplies the deployed schema, and Surfaces and hedging covers additional price models and desk-specific parameters. The chapters that derive the math are linked from each group.

Where the inputs live

Every input is a field of engine.rs::SimConfig, the config member of every request body on the API (see The API) and the SimConfig type in lib.ts. Distinguish the engine defaults, the legacy desk configuration and a new workspace scenario.

  • engine.rs::SimConfig::default is the program base case as the engine understands it, used by the tests and the examples.
  • lib.ts::BASE_CONFIG supplies the legacy and advanced desk base configuration.
  • New Dollar and Coin workspace scenarios (scenario.ts::defaultWorkspaceScenario) start from that configuration but use illustrative GBM with continuous annual drift 0 and volatility 43%. Saved scenarios keep their own path mode and assumptions.

Two differences matter: intramonth strike dispersion (#20) is off in the engine default and on in the workspace; market_horizon_months is null in the engine default and explicitly 84 in BASE_CONFIG. The default 24-cohort, 60-month book needs 84 months either way, but the explicit horizon keeps the market endpoint fixed when pacing is shortened. MCP fills omitted fields from the engine default, not the workspace draft. The archived Model Card run of 3 September 2026 is a historical configuration, not a current default: drawdown multipliers (#23) on, 24 cohorts of 20, the zero-drift bootstrap from the trailing 24 months. Its §2 states it in full.

Input validation is defined by engine.rs::SimConfig::check, called before API work estimation and by the engine’s validate wrapper. The engine wrapper panics on failure with a message naming the input; forwardflow_api.rs::catch_engine unwinds the panic into HTTP 400 with that message as the body. A few checks live in the module that owns the input: the custom per-year curve in defaults.rs::custom_yearly_hazard, the custom path in paths.rs::custom, the boundary’s parameters in boundary.rs::BoundaryParams::validate, the risk desk’s bumps in exposure.rs::greeks. Path generation failures (paths.rs::PathError) are ordinary errors and reach the client the same way. The error texts below were read back from the running engine on 2026-09-03. Where a message says “default”, that is the engine’s identifier for a stop.

Numbering. The spec table runs #1 to #25 with three gaps that are not fields of SimConfig. #10 is the state of #9 when it is off. #14 (Monte Carlo runs) and #19 (the inverse price solver, target_effective_irr, accepted in \( -0.9 < r < 10 \) by outputs.rs::solve_purchase_price) are request fields of their own endpoints; the solver, the heatmap grids and the vintage backtest are described in The API. #21, a pooled-vehicle view, was removed (Marc, 2026-07-12; spec v1.4 change 11): recycling multiplies wealth, never the rate. The bump overlay and the seed carry no spec number.

Types on the wire. Money-exact fields are Decimal in Rust and travel as JSON strings ("1.475"); prices, rates and probabilities are f64 numbers; counts are unsigned integers; optional inputs are null when off; the enums (path, scenario) are externally tagged objects such as {"Bridge": {"end_price": 60000, "vol_annual": 0.43}}.

Price path

The path is generated once per run by paths.rs::generate from the seed; the shock and the bump overlays are then applied to it, in that order. It has horizon + 1 monthly marks, month 0 to month \( H \), where engine.rs::SimConfig::horizon sets

\[ H_{\min} = (C_{\text{eff}} - 1) + n + \left\lfloor\frac{\text{lag days}}{30.4375}\right\rfloor + 1, \]

with \( C_{\text{eff}} \) the cohorts that actually originate (#13 capped by #18) and \( n \) the term. horizon() uses market_horizon_months when supplied, which must be at least this minimum; otherwise it uses the minimum. At the base case that is \( 23 + 60 + 1 = 84 \), so 85 marks; the extra month lets a stop at the last stoppable payment date settle inside the ledger (spec v1.5 change 4). Derivations: Price paths, shocks and bumps.

#Cockpit labelFieldTypeDefaultRange and error textWhat it does, where it is read
1Start pricestart_pricef64, dollars60,000finite, \( 0 < p < 10^{12} \). start price (input #1) out of range: {p}Month-0 price: the first cohort’s strike, and the level every path mode is rebased to. Read in engine.rs::run for path generation and cohort strikes, and by the risk desk for the frontier’s terms.
2End pricepath.Bridge.end_pricef64, dollars= start (60,000)finite, \( 0 < p < 10^{12} \). end price (input #2) out of range: {p}The pinned final mark of the bridge. paths.rs::bridge.
2bVolatility %/yrpath.Bridge.vol_annualf64, fraction0.430 to 5. volatility (input #2b) must be 0–500%, got {v}Annual volatility of the bridge, \( \sigma_m = \sigma / \sqrt{12} \) per month; 0 gives the deterministic log-linear ramp. paths.rs::bridge.
2cModepathenumBridgesee the modes belowWhich generator runs. paths.rs::generate.

The eight modes of paths.rs::PathMode (the four additional stochastic/upload modes are derived in Surfaces and hedging):

ModeCockpitFieldsNotes
Bridge“Bridge (pinned)”end_price, vol_annualSequential Brownian bridge in log space, each step conditioned on the distance left to the endpoint.
HistoricalReplay“Historical replay”, field “Start month”start_indexActual monthly closes from that index, rescaled so month 0 equals #1. Fails with historical replay needs {H+1} months from index {i}, have {k} when the horizon runs past the series (paths.rs::PathError::InsufficientHistory). The cockpit’s mode switch lands on index 21, November 2013.
ZeroDriftBootstrap“Real returns, trend removed”, fields “Regime from” and “Block length (months)”regime_start_index, block_lenBlock bootstrap of de-meaned log returns from the regime start onward. Fails with bootstrap regime slice too short: {k} returns when fewer than block_len + 1 bars remain (paths.rs::PathError::RegimeTooShort). The cockpit’s switch uses block 6 from index 59, January 2017.
CustomCustom anchors / crash presetspoints: [[month, ratio], ...]Piecewise log-linear through anchors from (0, 1.0), flat after the last. paths.rs::custom requires at least one anchor (custom path (input #2c) needs at least one anchor point), strictly increasing months after 0 (custom path (input #2c): anchor months must be strictly increasing and start after month 0) and positive finite ratios (custom path (input #2c): price ratios must be positive and finite).
GbmGeometric Brownian motionmu_annual, vol_annualUnpinned monthly lognormal path; drift is the continuous rate of expected price growth. Current workspace starting cases use this mode.
JumpDiffusionJump diffusionmu_annual, vol_annual, jump_rate_annual, jumpGBM diffusion with compensated Poisson log jumps; Merton or Kou jump sizes.
RegimeSwitchingRegime switchingmu_annual, calm_vol, stressed_vol, p_calm_to_stressed, p_stressed_to_calm, start_stressedTwo monthly volatility states with a common continuous drift.
UploadUploaded monthly pricespricesAt least horizon + 1 positive finite prices; rebased to the configured start price.

The embedded series (paths.rs::historical_closes, compiled in from backend/data/btc_historical_monthly.csv) has 174 monthly bars, February 2012 to July 2026. Bar \( i \) is the month \( 12(y - 2012) + (m - 2) \), so January 2017 is 59 and July 2024, the start of the Model Card’s trailing-24-month pricing window, is 149. GET /api/forwardflow/history returns the month list.

Paper terms

The contract math is in contract.rs::ContractTerms; Contract math and the fee derives it. Every Agreement in a run shares these five inputs. Only the strike varies, by cohort month and, when #20 is on, within a cohort.

#Cockpit labelFieldTypeDefaultRange and error textWhat it does, where it is read
3First N payments → BTC Noworigination_paymentsu321\( N \le n \). N (input #3) cannot exceed the termPayments 1 to N route whole to BTC Now and are never delivered to the Holder, so they carry no servicing fee; 0 turns the toggle off. engine.rs::run step (1). A stop’s refund base still counts them (engine.rs::Agreement::paid_in).
4Servicing fee % of each deliveryservicing_fee_rateDecimal, fraction“0.05”\( 0 \le f < 1 \). servicing fee (input #4) must be 0–100% of each delivered dollar, got {f}Flat share of every dollar delivered to the Holder: payments after the first N, early-completion payoffs, stop-sale deliveries (Marc, 2026-08-31; spec v1.5 change 2). Not derived from the term. contract.rs::ContractTerms::fee_rate, split by fees.rs::FeeState::split with cumulative rounding, so the lifetime fee is exact to the cent.
5Price multiple ×multipleDecimal“1.475”\( 0 < m < 100 \). price multiple (input #5) must be positive and sane, got {m}The Purchase Price as a multiple of the coin’s cost. The implied financing rate is a display derived from it by contract.rs::ContractTerms::implied_monthly_rate (a bisection on the annuity identity); 1.475× at 60 months is 16.50% nominal. contract.rs::ContractTerms::terminal, schedule.
6Term (months)term_monthsu32601 to 480. term (input #6) must be 1–480 months, got {n}Payments per Agreement; the payment, the hazard shape and the horizon re-derive from it, and nothing hard-codes 60 (spec v1.1 change 1). A hazard draw after payment n−1 can miss payment n; no new hazard is drawn after completion at n. A positive baseline lifetime prior therefore needs \( n \ge 2 \).
6bPurchase price % of coin costpurchase_pct_of_strikeDecimal, fraction“1.00” (par)\( 0 < q < 100 \). purchase price (input #6b) must be a positive fraction of strike, got {q}What the Holder pays per Agreement, as a fraction of that Agreement’s strike; par since v1.4 (was 1.05). Posted at origination in engine.rs::run as PurchasePrice from Owner to BtcNow.

At the base terms a $60,000 coin at 1.475× is a Purchase Price of $88,500. contract.rs::ContractTerms::schedule divides it into 60 payments quantized to cents toward zero, the last absorbing any residual; here every payment is exactly $1,475.00. Payment 1 goes to BTC Now; each later payment splits $73.75 to BTC Now and $1,401.25 to the Holder, who paid $60,000 at par.

Behavior

Who stops, who completes early, and how the price bends both. The monthly order is fixed in engine.rs::run: (1) scheduled payments, with the rational boundary’s decision before each; (2) walks armed at an earlier date, then the conviction streaks; (3) early completions; (4) the hazard draws. Who stops paying derives the hazards and the modes. The bracket a Holder runs is #7 alone, then #23, #9 and #25 (spec §1, row 25).

#Cockpit labelFieldTypeDefaultRange and error textWhat it does, where it is read
7Lifetime default % (and the per-year bars in custom mode)scenarioenum{"BaselineCurve": {"lifetime": 0.4}}see the variants belowThe hazard \( h_t \) by payment age, built once per run by defaults.rs::DefaultScenario::monthly_hazard and drawn against in engine.rs::run step (4); \( h_n \equiv 0 \).
8the chips 700+ · 600 · 500 · 400(loads #7)presetnonenoneLifetime targets 15% · 35% · 55% · 70% (defaults.rs::DefaultScenario::fico_preset, lib.ts::FICO_PRESETS): the baseline curve at those targets, not separate fields.
9Rational defaultrational_defaultboolfalsenoneOn: a drawn stop sticks only if the Buyer’s coin is below the amortized obligation \( B_t \) (engine.rs::Agreement::underwater); an in-the-money draw is redirected uniformly to an underwater Agreement, or suppressed and counted when none exists (Marc, v1.1). engine.rs::run step (4).
10(the off state of #9)Stops land by the hazard alone, blind to price.
11Lost-conviction rule; X% below entry price; Y consecutive monthsconviction = {enabled, x_underwater, y_consecutive}bool, f64, u32off; 0.5 and 6 when onwhen on: \( 0 \le X \le 1 \), \( Y \ge 1 \). lost-conviction rule (input #11): X must be 0–100% and Y ≥ 1A Buyer whose coin has sat below \( (1 - X) \) times the strike for \( Y \) consecutive payment dates walks at the next one; the walk is the missed payment. X is against the strike, not the obligation (Marc, v1.1). engine.rs::run step (2b) arms, step (2a) executes; defaults.rs::ConvictionRule.
12Early completion %/mosettlement_propensityf64, per month0.0250 to 1. settlement propensity (input #12) must be 0–100%, got {p}Monthly chance of early completion, scaled by how far in the money the coin is: \( u_t = p \cdot \max(0, (S_t - R_t)/S_t) \). The Buyer pays the remaining schedule \( R_t \) in cash and takes the coin. engine.rs::run step (3), engine.rs::settle.
22Take-profit gate; Profit ≥ % of all-in costsettlement_min_returnOption<f64>null (off); the cockpit sets 0.10 when switched on0 to 10. settlement take-profit gate (input #22) must be 0–1000%, got {x}When set, early completion needs \( S_t \ge P (1 + x) \), the coin beating the whole Purchase Price \( P \) by \( x \); at 10% that is $97,350 on the base coin. The Agreement’s random number is drawn first regardless (v1.6, so the stream never depends on the price); the gate is then checked before that number is compared with \( u_t \). engine.rs::run step (3).
23Drawdown-scaled hazarddrawdown_hazard_multipliersboolfalse (engine and cockpit); on in the Model Card’s runnoneMultiplies \( h_t \) by a state read off the coin against the Buyer’s entry: in the money ×0.5, drawdown ≤30% ×1.0, 30–50% ×1.5, 50–70% ×2.0, deeper ×3.0, capped at 1. engine.rs::drawdown_multiplier, step (4).

The three variants of defaults.rs::DefaultScenario:

VariantJSONSemanticsValidation and error text
BaselineCurve{"BaselineCurve": {"lifetime": 0.40}}The program hump, normalized to term fraction (at 60 months: ages 1–3 ×1.0, 4–15 ×2.0, 16–24 ×1.2, 25–36 ×0.7, 37–60 ×0.25), scaled by bisection so the flat-path lifetime stop share equals the target exactly.\( 0 \le L < 1 \): lifetime default target (input #7/#8) must be in [0%, 100%), got {L}. And \( n \ge 2 \) unless \( L = 0 \): lifetime default target (input #7) needs a term (input #6) of at least 2 months — the final payment date cannot default.
FlatAnnual{"FlatAnnual": {"annual_rate": 0.10}}A constant annual stop rate converted to the monthly hazard \( 1 - (1 - r)^{1/12} \); rate semantics, not a lifetime target. API only, no cockpit picker.\( 0 \le r \le 1 \): flat annual default rate (input #7) must be 0–100%, got {r}.
CustomYearly{"CustomYearly": {"shares": [s1, ..., sY]}}shares[y] is the share of the original book that stops in year y+1, summing to the lifetime; each year’s mass is spread over its stoppable ages and divided by the survival so far, so a flat path realizes the shares exactly (v1.4).One share per year, \( \lceil n/12 \rceil \) of them: custom per-year defaults (input #7) need one share per year of the term: {Y} for {n} months, got {k}. Each finite and non-negative: custom per-year defaults (input #7) must be finite and non-negative. Sum below 100% with a \( 10^{-9} \) headroom: custom per-year defaults (input #7) must sum below 100%, got {pct}%. A year with no stoppable age must carry zero: custom per-year defaults (input #7): year {y} has no defaultable payment age at a {n}-month term (the final payment date cannot default) — its share must be 0%. All in defaults.rs::custom_yearly_hazard.

The validation messages above retain the phrase “the final payment date cannot default”. This refers to the absence of a new hazard draw after the final payment, not to immunity of that receipt: a draw after payment term−1 can miss the final payment, as can a walk decided before it.

Book and pacing

#Cockpit labelFieldTypeDefaultRange and error textWhat it does, where it is read
13Monthly cohortscohortsu32241 to 1,200. cohorts (input #13) must be at most 1,200, got {c}. Zero cohorts or Agreements per cohort are rejected with a named minimum-count messageMonths in a row in which a cohort originates, month 0 first. engine.rs::run, the origination block.
13Agreements per cohortagreements_per_cohortu3210cohorts × per-cohort ≤ 50,000. book size (inputs #13 × #18) must be at most 50,000 Agreements, got {b}Agreements signed in each originating month, at that month’s price unless #20 is on. The message’s “#18” names this count; the spec lists both counts under #13.
18Origination stops at month (0 = never)origination_stop_monthOption<u32>null\( \ge 1 \). origination window (input #18): stop month must be at least 1No cohort originates from this month on; the book runs off. engine.rs::SimConfig::effective_cohorts caps #13 with it and the horizon shortens unless the market horizon is set. Stopping origination caps the size of the position, never the rate on what is owned.
20Intramonth strike dispersionintramonth_strike_dispersionboolfalse in SimConfig::default; true in BASE_CONFIG (Marc, 2026-07-13)noneEach Agreement draws its own strike log-normally around its month’s price, mean-preserving, with \( \sigma_{\text{intra}} = \sigma_{\text{monthly}} / \sqrt{2} \) from the realized log returns of the twelve months before its cohort month — the path mode’s stated volatility with fewer than three behind it; zero on a flat path — from the Agreement’s own stream. engine.rs::run, the origination block; engine.rs::entry_sigma_monthly.
Market horizon (months)market_horizon_monthsOption<u32>null\( \ge \) the pacing’s requirement, \( \le 2{,}400 \). market horizon (market_horizon_months) must be at least the pacing's own requirement of {n} months (last cohort + term + the stop-sale settlement tail), got {h}The last month the price path is generated to, so a pacing change cannot move the bridge’s endpoint date (audit 2026-09-05, finding 2). null = \( (\text{cohorts} - 1) + \text{term} + \lfloor \text{lag}/30.4375 \rfloor + 1 \). engine.rs::SimConfig::horizon.

The base book is 24 × 10 = 240 Agreements.

The stop

After a missed payment, day 16 is the Stop Date, the coin is sold for dollars within two business days, and the proceeds pay by the ruled waterfall (Marc, 2026-08-22 and 2026-09-03; spec v1.5 change 1). Three inputs shape the sale; The stop waterfall derives the rest.

#Cockpit labelFieldTypeDefaultRange and error textWhat it does, where it is read
15Haircut % (sale below spot)haircutf64, log units0\( \ge 0 \), finite. stop-sale haircut (input #15) must be non-negative, got {h}The sale executes at spot \( \times e^{-h} \). The cockpit shows a percent and stores \( h = -\ln(1 - \text{pct}) \); the tornado’s “haircut 50%” is \( h = 0.693 \). Default 0 since v1.4 (Marc, 2026-07-12): a one-coin sale has no market impact, and the delay is priced by #24. engine.rs::stop_sale.
16Market-sale cost (bps)sale_cost_bpsf64, basis points250 to below 10,000. market-sale cost (input #16) must be 0–10000 bps, got {b}Execution cost of the sale: proceeds \( \times (1 - b/10{,}000) \). engine.rs::stop_sale; the same fraction goes to boundary.rs::rational_frontier.
24Sale lag (days after the missed payment)stop_sale_lag_daysf64, calendar days (the cockpit rounds to whole days)180 to 90. stop-sale lag (input #24) must be 0–90 days, got {d}Days from the missed payment date \( D \) to the recorded sale: day 16 is the Stop Date (R-1035) plus two business days (R-1037). The sale is priced by log-linear interpolation between the marks at \( D + d/30.4375 \); the cash posts at the first payment date at or after sale, \( \lceil D + d/30.4375 \rceil \); the horizon carries \( \lfloor d/30.4375 \rfloor + 1 \) months past the last term so the sale prices and lands inside the ledger at every lag (engine.rs::settlement_tail_months). engine.rs::stop_sale.

At the base lag the sale sits \( 18 / 30.4375 = 0.591 \) of the way from the missed date’s mark to the next, and the cash posts one month after the missed date. Worked at the base terms, with the coin at $60,000 when sold and 12 payments made: proceeds \( V = 60{,}000 \times (1 - 0.0025) = 59{,}850.00 \); paid in \( A = 12 \times 1{,}475 = 17{,}700 \); Purchase Price \( P = 88{,}500 \); refund \( \min(A, \max(0, V + A - P)) = \min(17{,}700, \max(0, -10{,}950)) = 0 \); the Holder is delivered $59,850.00 less 5% ($2,992.50), net $56,857.50; shortfall \( R - V = 70{,}800 - 59{,}850 = 10{,}950.00 \) (engine.rs::stop_sale).

The overlays

Multiplicative overlays applied to the generated path before the run starts, the shock first (engine.rs::run). Neither changes the seed’s draws.

#Cockpit labelFieldTypeDefaultRange and error textWhat it does, where it is read
17Shock designer; Drop %; Start month; Over months; Recover to % (blank = flat)shock = {start_month, drop_pct, duration_months, recover_to_pct}Option; u32, f64, u32, Option<f64>null; the cockpit toggle loads start 6, drop 0.5, 3 months, no recovery\( 0 < Z < 1 \): shock designer (input #17): drop must be 0–100% exclusive, got {Z}. \( W \ge 1 \): shock designer (input #17): duration must be at least 1 month. \( R > 0 \) when given: shock designer (input #17): recovery level must be positive, got {R}From month \( X \) the path is multiplied by a log-linear ramp reaching exactly \( 1 - Z \) at month \( X + W - 1 \), then held, or recovered log-linearly to \( R \) times the unshocked level at the final mark. Composes with any path mode. paths.rs::apply_shock.
none in the cockpit (the Risk desk uses it internally)bump = {from_month, price_factor, vol_factor, hold_strikes}Option; u32, f64, f64, boolnull\( 0.1 < \text{price} < 10 \): path bump: price factor must be within (0.1, 10), got {f}. \( 0 \le \text{vol} < 5 \): path bump: vol factor must be within [0, 5), got {f}. Must be null when asking for the Greeks: risk desk: clear the what-if bump (config.bump) before asking for the GreeksFrom from_month on, every price is multiplied by price_factor and the log returns’ deviations from their mean are scaled by vol_factor, so realized volatility moves while the drift is kept. hold_strikes true keeps strikes and purchase prices on the unbumped path (the existing book); false lets later cohorts strike at the bumped prices (the commitment). paths.rs::apply_bump; strikes in engine.rs::run. v1.6.

The rational boundary

Input #25 (spec v1.6 change 6) is the fifth behavior mode: a Buyer whose coin sits below the computed walk-away frontier for the payment date does not pay; the walk is the missed payment, settled like any stop with the exit tag RationalBoundary. The frontier is computed once per run by boundary.rs::rational_frontier from cohort-1 terms and is scale-invariant in the strike, so it serves every Agreement, dispersed strikes included. engine.rs::run reads frontier[t − 1] before payment \( t \) as a fraction of the entry price and compares spot with that fraction times the Agreement’s strike. The risk desk derives the lattice.

Cockpit label (Risk desk page)FieldTypeDefaultRange and error textWhat it does
Run the book with the rational boundary as the behavior moderational_boundaryOption<BoundaryParams>null (off)On when the object is present.
Lattice sigma %/yrrational_boundary.sigma_annualf640.414\( 0 < \sigma < 2.5 \). rational boundary (input #25): sigma must be 0–250%, got {s}The lattice’s annual volatility: the pricing stance’s trailing-24-month realized, 41.4%.
Boundary believed drift %/yrrational_boundary.mu_annualf640.25finite, \( \lvert \mu \rvert < 2 \). rational boundary (input #25): believed drift must be within ±200%/yr, got {m}The Buyer’s believed annual drift; zero is the pessimist who walks at or above par from the first dates.
Buyer discount rate %/yrrational_boundary.r_c_annualf640.15\( 0 \le r < 1 \). rational boundary (input #25): discount rate must be 0–100%, got {r}The Buyer’s discount rate on the continuation value.
Walk cost, % of coin costrational_boundary.walk_cost_of_strikef64, fraction0.025\( 0 \le c < 1 \). rational boundary (input #25): walk cost must be 0–100% of the coin's cost, got {c}What walking costs the Buyer as a lump (the six-month lockout, the re-strike at market, the lost access); 2.5% of a $60,000 coin is $1,500, about one payment.

One more check runs inside boundary.rs::rational_frontier: if \( \lvert \mu \rvert \sqrt{\Delta} \) exceeds \( \sigma \), with \( \Delta = 1/48 \) of a year (the lattice takes four price steps a month), the branch probability leaves \( [0, 1] \) and the run fails with rational boundary (input #25): |mu|·√Δ exceeds sigma — raise sigma or lower |mu|. The defaults are boundary.rs::BoundaryParams::default; the object is validated by boundary.rs::BoundaryParams::validate, called from engine.rs::validate and again by the frontier.

The seed

Cockpit labelFieldTypeDefaultRangeWhat it does, where it is read
Seedseedu6442anySeeds the ChaCha20 stream that draws the path (engine.rs::run) and, through engine.rs::agreement_rng, one independent stream per Agreement keyed on (seed, Agreement id) via splitmix64 (v1.6). Same seed and inputs give a byte-identical run; a bump or a changed exit elsewhere changes decisions, never the random numbers behind them. Monte Carlo run \( i \) uses seed \( + i \) (outputs.rs::run_monte_carlo).

The Risk desk request

POST /api/forwardflow/risk takes a config plus six options of forwardflow_api.rs::RiskRequest, shaping exposure.rs::exposure_report (the two lines, coverage, the exposure ladder, PD·LGD·EAD per vintage, the Greeks) and a frontier family. A config error surfaces as above; the option errors are listed here.

Cockpit label (Risk desk page)FieldTypeDefaultRange and error textWhat it does, where it is read
Ladder monthladder_monthOption<u32>null = the month of peak capital at riskclamped to the horizonThe calendar month at which the exposure ladder is cut, and the “as of” month of the Greeks, whose bumps start the month after. exposure.rs::exposure, exposure.rs::greeks.
Price bump %price_bumpf64, fraction0.05\( 0 < b < 0.5 \). risk desk: price bump must be a fraction in (0, 0.5)Delta and gamma by revaluing with every future price × \( (1 \pm b) \), strikes held for the existing book, released for the commitment delta. exposure.rs::greeks via paths.rs::PathBump.
Vol bump (points)vol_bumpf64, fraction (0.05 = 5 points)0.05\( 0 < v < 1 \). risk desk: vol bump must be a fraction in (0, 1)Vega: on every path mode, scale log-return deviations after the as-of month around their mean. The pre-as-of path is retained; a flat path is a no-op. The method is named in vega_method. exposure.rs::greeks.
Believed drift 1 … k %/yrfrontier_musVec<f64>, fractions[0, 0.10, 0.25, 0.50]1 to 8 entries. frontier_mus must hold 1–8 believed driftsOne frontier per believed drift, other lattice parameters shared; forwardflow_api.rs::ff_risk calls boundary.rs::rational_frontier once per drift, each bound by the #25 checks.
Lattice sigma %/yr · Buyer discount rate %/yr · Walk cost, % of coin costfrontier_paramsOption<BoundaryParams>nullthe #25 rangesThe lattice parameters other than drift. Resolution in ff_risk: this field, else the config’s rational_boundary, else BoundaryParams::default(); the response echoes the resolved set as frontier_params.
(not exposed; the cockpit omits it and the server’s default applies)greek_seedsu32161 to 256. risk desk: greek_seeds must be 1–256Seeds averaged for each Greek, starting at the config’s seed. exposure.rs::greeks.

forwardflow_api.rs::HEAVY admits three risk, Monte Carlo, heatmap or backtest requests at a time.

The Monte Carlo request

POST /api/forwardflow/montecarlo and the WebSocket GET /api/forwardflow/montecarlo/ws take forwardflow_api.rs::MonteCarloRequest: a config and runs (spec input #14, “Monte Carlo runs”). Run \( i \) is the config with seed \( + i \); on a bridge that is the same endpoints redrawn, endpoint-pinned by design (spec §9, question 4). Outputs covers the percentiles.

FieldTypeDefaultRange and error textNotes
runsu64none; the pages choose1 to 100,000. runs must be 1–100000 (input #14)forwardflow_api.rs::MAX_MC_RUNS.
runs × book≤ 24,000,000 Agreement-runs. runs × book size must stay ≤ 24000000 Agreement-runs — lower runs (input #14) or the book (inputs #13 × #18)forwardflow_api.rs::MAX_MC_AGREEMENT_RUNS: the full run ceiling at the 240-Agreement base book; bigger books get proportionally fewer runs. Checked by forwardflow_api.rs::validate_mc before any engine work.

Identical requests, keyed on (runs, the config’s JSON), are memoized in a 64-entry LRU (forwardflow_api.rs::MC_CACHE), sound because the engine is seeded. The WebSocket variant runs chunks of 1,000 (forwardflow_api.rs::MC_CHUNK) with a Progress frame per chunk.

Connect an AI assistant

Ask questions in your own words. Your assistant uses ForwardFlow to run the model on BTC Now’s server and explain the results. No ForwardFlow account or model installation is needed.

1. Request your access token

Email info@btcnow.com with your name, organisation and the AI client you want to use.

BTC Now will arrange access and send your token privately. Keep it in your client’s connection settings, not in the chat. Each invited person or organisation receives its own token.

2. Connect your assistant

Choose your setup guide:

Connection details for other MCP clients
SettingValue
Nameforwardflow
Server URLhttps://btcnow-forwardflow.fly.dev/mcp
TypeStreamable HTTP
Headers → KeyAuthorization
Headers → ValueBearer YOUR_ACCESS_TOKEN

Replace YOUR_ACCESS_TOKEN with the token BTC Now sent you. Include the space after Bearer.

Your client must support a remote HTTP MCP server with an authentication header. Save the settings and check the client’s connection status.

ChatGPT web and other connection options.

3. Ask your first question

Use ForwardFlow to help me frame an analysis. I want to understand the Holder’s return from Agreements. Ask me only the missing questions, then explain the assumptions before calculating.

The assistant should establish whether you want USD growth or BTC growth compared with holding the same contributed BTC, and what you want to compare. It asks up to three short questions at a time and should keep answers you already gave. If you do not know a technical assumption, it can propose an explicit illustrative starting case and test how the answer changes when that assumption changes.

Before calculating, the assistant shows the objective, capital basis, horizon and main assumptions. ForwardFlow checks the prepared request and supplies the calculation inputs. The assistant then uses the engine’s results, including warnings and unavailable metrics, to explain the answer. A prepared request does not certify the assumptions or establish investment suitability.

Once the first case is clear, try:

Compare that with Bitcoin falling 40% in month six and staying lower. Keep the other assumptions the same. What changes for the Holder?

An Agreement represents one contractual BTC. A question about a fixed wallet with reserves, reinvestment or daily margin requires a different portfolio model; ForwardFlow cannot silently treat a 100 BTC wallet as 100 Agreements. The connection supplies tools, an analyst guide and an optional starter prompt. It does not install a skill in your assistant.

Need help?

Email info@btcnow.com. If the service is busy, wait and try again; invited users share the server’s computing capacity.

Explore the model’s assumptions · Technical reference

Compare hedge risk

Ask: “Compare my hedge variants with unhedged on matching simulations. Show dollar and coin losses, worst-5% loss, and the cash needed before protection pays. Explain the market-path assumptions and test another seed sample.”

The hedge_risk_samples tool supplies the observations. It returns at most 32 simulations per call, or 8 with monthly detail. Combine raw observations from non-overlapping seed batches only when the assumptions, valuation seed, engine build and historical-data identity match. Cash is a monthly screen; daily collateral calls and forced closure are not simulated. Read the comparison guide.

Choose Dollar or Coin

Tell your assistant which outcome matters: grow USD capital or grow BTC compared with holding the same contributed BTC. Both use the same MCP connection and access token. For example: “Use Coin. Compare the current hedge against unhedged Agreements and holding BTC. Show BTC surplus, the chance of ending below holding, and USD cash needs separately.” The assistant should confirm the objective before judging which strategy is better. How the holding comparison works.

Connect with ChatGPT desktop or Codex

Use the ChatGPT desktop app to ask ForwardFlow questions in plain English. The model runs on BTC Now’s server. Codex CLI and IDE users can use the configuration option below. ChatGPT in a browser has a separate setup.

You need a ForwardFlow access token. Request one from info@btcnow.com; BTC Now will send it privately. No ForwardFlow account is needed.

1. Add the connection

In ChatGPT desktop, open Settings → MCP servers → Add server. Fill in:

FieldWhat to enter
Nameforwardflow
TypeStreamable HTTP
URLhttps://btcnow-forwardflow.fly.dev/mcp
Headers → KeyAuthorization
Headers → ValueBearer YOUR_ACCESS_TOKEN

Replace YOUR_ACCESS_TOKEN with your token, keeping the space after Bearer. Paste it into the header value, not into a chat message.

Leave Bearer token env var and Headers from environment variables empty. Those fields are for a different setup.

Select Save. The connection and token stay saved on this computer; keep its configuration private.

2. Check that it connects

Select Restart when prompted. Open a chat and enter:

/mcp

Check that forwardflow is connected and enabled. Review any permission request to use its tools.

3. Ask your first question

Use ForwardFlow to run the base case. Explain the assumptions and the Holder’s return and cash flows in plain English. Include any warnings from the model.

ChatGPT should use the ForwardFlow tools to calculate the answer. Then try:

Compare that with Bitcoin falling 40% in month six and staying lower. Keep the other assumptions the same. What changes for the Holder?

Need help?

Email info@btcnow.com. Do not include your token in the message or a screenshot.

The connection is missing, fails, or reports that it is busy
  • Missing connection: confirm you saved it in the desktop app, restart, then check /mcp again.
  • Invalid token or 401: check the Authorization header, the space after Bearer, and that you replaced the placeholder. Ask BTC Now to confirm your token is active if it still fails.
  • No tools or an incorrect address: copy the full URL above, including /mcp, and select Streamable HTTP.
  • Connected, but no model result: ask ChatGPT explicitly to use the ForwardFlow tools and check their permissions.
  • Service busy: wait for the suggested retry interval, then ask again. Invited users share the server’s capacity.
Using ChatGPT in a browser?

This guide is for the desktop app’s custom MCP connection. Saving it does not also connect ChatGPT on the web. See web connection options.

Codex CLI and IDE

Local ChatGPT desktop, Codex CLI and IDE clients share MCP configuration on the same Codex host. A connection saved there does not need to be added again for each client.

If configuring Codex directly, add this entry to your user configuration, normally ~/.codex/config.toml, replacing the placeholder privately:

[mcp_servers.forwardflow]
url = "https://btcnow-forwardflow.fly.dev/mcp"
http_headers = { Authorization = "Bearer YOUR_ACCESS_TOKEN" }

Restart the client. In Codex CLI, use /mcp to check the connection. This service uses the supplied token; codex mcp login is for OAuth servers and is not needed here. Environment-based credentials are also supported, but the variable must exist in the environment of the host running the client.

Setup checked against OpenAI’s MCP documentation on 14 September 2026. Back to AI connections.

Connect with Claude Code

You need Claude Code installed and a ForwardFlow access token. Request your token from info@btcnow.com; BTC Now will send it privately.

This guide is for Claude Code in your terminal.

1. Save the connection once

Copy the block for your terminal and run it. When asked, paste your token and press Enter. It stays hidden while you type.

macOS using zsh (the default shell):

{
  read -rs 'ffAccessToken?ForwardFlow access token: '
  echo
  claude mcp add --transport http \
    --header "Authorization: Bearer $ffAccessToken" \
    --scope user forwardflow https://btcnow-forwardflow.fly.dev/mcp
  unset ffAccessToken
}
Linux or macOS using bash
{
  read -rsp 'ForwardFlow access token: ' ffAccessToken
  echo
  claude mcp add --transport http \
    --header "Authorization: Bearer $ffAccessToken" \
    --scope user forwardflow https://btcnow-forwardflow.fly.dev/mcp
  unset ffAccessToken
}
Windows PowerShell
& {
  $ffAccessToken = Read-Host 'ForwardFlow access token' -AsSecureString
  $ffTokenValue = [System.Net.NetworkCredential]::new('', $ffAccessToken).Password
  claude mcp add --transport http --header "Authorization: Bearer $ffTokenValue" --scope user forwardflow https://btcnow-forwardflow.fly.dev/mcp
  Remove-Variable ffAccessToken, ffTokenValue
}

The options precede the server name, as required by Claude Code. --scope user saves this connection outside the project in your user configuration. The token is stored on this computer; keep that configuration private. You do not need to enter it each time you open Claude Code.

2. Open Claude Code

Start a new session:

claude

Enter /mcp, select forwardflow and check that it connects. Review any tool-permission request from Claude Code.

3. Ask a question

Use ForwardFlow to help me frame an analysis. I want to understand the Holder’s return from Agreements. Ask me only the missing questions, then explain the assumptions before calculating.

Claude should establish Dollar or Coin, the comparison and the capital basis, asking at most three missing questions at a time. If you do not know a technical assumption, ask for a labelled illustrative starting case and a sensitivity. Claude should show the prepared assumptions before calling the calculation tool. The calculations run on BTC Now’s server; check that Claude uses the ForwardFlow tools when reporting model numbers.

For an optional guided start, type / and choose /forwardflow:start_analysis (MCP), or enter:

/mcp__forwardflow__start_analysis

Then state your question in ordinary language. This server prompt takes no arguments. It supplies the same analyst guidance available through describe_model and the MCP resource forwardflow://guides/analyst-v1. These are server capabilities, not an automatically installed Claude skill. If the prompt is not listed, restart Claude Code and check /mcp; asking in ordinary language still works. How Claude Code discovers MCP prompts.

Need help?

Email info@btcnow.com. Never include your token in the message or a screenshot.

Already connected before, replacing a token, or seeing an error?
  • An existing user connection or an old token: remove the saved connection with the command below, then repeat step 1 with your current token and restart Claude Code. This also replaces the earlier setup that asked you to enter a key in every terminal.
  • Missing or invalid token (401): confirm with BTC Now that your token is active. ForwardFlow uses a token, not an OAuth sign-in.
  • An old address still appears: a local or project connection can override the user connection. Remove the obsolete forwardflow entry from that scope; contact us if you need help identifying it.
  • Claude Code is not found: finish the installation and reopen your terminal.
  • The service is busy: wait for the suggested retry interval, then ask again.

To remove the user connection created by this guide:

claude mcp remove --scope user forwardflow

This removes the saved connection. Ask BTC Now to revoke the token if it should no longer grant access.

Setup follows Anthropic’s MCP documentation, checked 14 September 2026. These commands configure Claude Code; they do not add a connection to Claude’s web app. Model tools and result details.

API reference

The engine is a Rust crate. Everything a desk sees of it goes through one small Axum server, backend/src/main.rs, and one module, backend/src/api/forwardflow_api.rs. The server has no saved-run database: requests supply assumptions and receive results. It retains bounded computation caches and access-limit state. The workspace calls these calculations and derives display summaries and independent checks from the responses. The MCP interface exposes a bounded subset for assistants.

This chapter documents each route: method, path, request body field by field, response body field by field, a curl that was actually run against http://localhost:8080 on 2026-09-03 with its response trimmed, and the error semantics. It closes with the WebSocket protocol, client snippets in Python and TypeScript, the hosting notes and the result identity needed for reproduction. Examples dated 3 September 2026 are historical worked responses; consult the deployed OpenAPI schema for current field shapes.

Two conventions apply everywhere.

  • Money travels as strings. Every ledger amount is a cent-quantized Decimal and serializes as a JSON string ("1475.00", "0.05"). Ratios, moneyness, IRRs and statistics are f64 and serialize as JSON numbers. The split is deliberate: nothing that posts to the ledger is ever a float.
  • Same numerical request, same build. The engine is seeded (ChaCha20, seed in the configuration). Repeat numerical requests against the same build, historical data and runtime environment for deterministic reproduction; headers, timing and progress metadata are not the calculation. The Monte Carlo memoization below depends on this.

The configuration object, SimConfig, is the same for every POST route. Its fields are documented one by one in Every input; this chapter names them only where a route treats them specially.

The server

main.rs calls lib.rs::build_router, sizes the rayon pool to max(1, cores − 1) so the async accept loop is never starved by a Monte Carlo, wraps the routes in gzip compression and a CORS layer configured by FF_CORS_ORIGINS, and listens on 0.0.0.0:8080.

MethodPathWhat it doesHeavy
GET/healthLiveness, plain-text OKno
POST/api/forwardflow/simulateOne seeded run: outputs, per-Agreement table, path, hazard, postingsconditional
POST/api/forwardflow/montecarloN seeded runs, summarizedyes
GET/api/forwardflow/montecarlo/wsThe same, over a WebSocket with progress framesyes, per chunk
POST/api/forwardflow/solve_priceThe purchase price that clears a target USD IRRconditional
POST/api/forwardflow/heatmapA sensitivity grid, every cell a full runyes
POST/api/forwardflow/backtestOne run per seasoned historical vintageyes
GET/api/forwardflow/historyThe embedded monthly price seriesno
POST/api/forwardflow/riskThe exposure report and the frontier familyyes
GET/api/forwardflow/crash_libraryNamed monthly crash anchorsno
GET/api/forwardflow/surfacesIllustrative volatility surfacesno
POST/api/forwardflow/placementPlace the book’s exposure on a surfaceconditional
POST/api/forwardflow/fair_valueModel value under stated pricing assumptionsyes
POST/api/forwardflow/hedgeCompare hedged and unhedged resultsyes
POST/api/forwardflow/hedge_researchPaired raw Dollar/BTC observationsyes
POST/api/forwardflow/hedge_seriesMonthly cash, marks and risk exhibitsyes
POST/api/forwardflow/rebalance_policiesCompare trading policiesyes
POST/api/forwardflow/coin_seatDollar and coin results across drift assumptionsyes
GET/api/forwardflow/healthJSON engine and access-mode identityno
GET/api/forwardflow/openapi.jsonMachine-readable REST contractno

The additional pricing and hedge requests are documented in Surfaces and hedging and the API explorer. MCP uses /mcp on the engine and is a separate protocol.

“Heavy” means the route takes a permit from forwardflow_api.rs::HEAVY, a three-permit semaphore. At most three heavy calculations run at once and eight may wait in the compute queue. Capacity refusals return 503 with Retry-After. Executed requests also reserve memory from the shared process budget.

simulate, solve_price and placement take this guard when their estimate exceeds 1,000,000 Agreement-runs or 128,000,000 bytes at peak. Their current book caps cannot reach the count threshold, but the byte threshold can apply, including to detailed simulation responses. Request flags therefore matter to admission; the raw engine book estimate is not the complete response estimate.

Choose the reporting objective

simulate can include the complete unhedged USD/BTC observation with include_research: true. Use hedge_research with an explicit objective: "coin" or "dollar" for paired simulations. Legacy Dollar-only routes, including solve_price and montecarlo, accept an omitted objective for compatibility or an explicit "dollar"; an explicit Coin, null or unrecognized objective is refused. A Dollar purchase-price solution is not a BTC target return. There is no BTC purchase-price solver.

Detailed simulation requests reserve memory for their selected postings, Agreement rows, research arrays and JSON output as well as the underlying engine book. Serialization is bounded, and the reservation stays with the response through compression and transmission. Large output requests can be refused before calculation; request fewer Agreements or omit detail you do not need. This is a resource limit, not a change to Agreement economics.

Errors and caps

The engine validates its inputs with named assertions in engine.rs::validate. A failed assertion panics with a message that names the input; forwardflow_api.rs::catch_engine catches the panic and returns it as HTTP 400 with the message as the body. This is why the build must never set panic = "abort": the 400 depends on unwinding.

$ curl -s -i -X POST localhost:8080/api/forwardflow/simulate \
    -H 'Content-Type: application/json' -d @bad.json   # term_months: 1700
HTTP/1.1 400 Bad Request
term (input #6) must be 1–480 months, got 1700

A PathError from paths.rs also maps to 400. Asking for a historical replay that runs off the end of the series:

HTTP/1.1 400 Bad Request
historical replay needs 85 months from index 170, have 4

A missing field or wrong JSON value type normally returns 422 before reaching the engine. Invalid JSON syntax returns 400; a missing Content-Type: application/json returns 415. An explicitly unsupported objective also returns 400. A bounded simulation response that cannot fit its declared serialization allowance returns 413; internal failures can return 500. Read the response message rather than treating every 400 as an economic-input error or every 500 as a blocking-task failure.

The caps, all of which answer 400 with the input named (hosting hardening, spec v1.4 item 13, 2026-07-13):

CapLimitWhere
Cohorts (input #13)≤ 1,200engine.rs::validate
Book size (inputs #13 × #18)≤ 50,000 Agreementsengine.rs::validate
Term (input #6)1 – 480 monthsengine.rs::validate
Monte Carlo runs (input #14)1 – 100,000forwardflow_api.rs::validate_mc
Estimated work, every route≤ 24,000,000 Agreement-runsforwardflow_api.rs::WorkEstimate::check
Heatmap grid1 – 900 cellsff_heatmap
Frontier drifts1 – 8ff_risk
Greek seeds1 – 256ff_risk, exposure.rs::greeks
Price bump, vol bump(0, 0.5), (0, 1)exposure.rs::greeks

The 24 million Agreement-run ceiling is the 100,000-run Monte Carlo at the base 240-Agreement book, and since the audit of 2026-09-05 (finding 8) every route estimates its work in that unit before it takes a thread — simulate = book, montecarlo = book × runs, solve_price = book × 2, heatmap = book × cells, backtest = agreements_per_cohort × vintages, risk = book × (1 + 7 × greek_seeds), placement = book, fair_value = book × seeds, hedge = book × seeds × Σ structure units (+ 2 with the benchmarks), rebalance_policies = book × seeds × structure units × policies, coin_seat = book × seeds × (Σ structure units + 1) × drifts, hedge_series = book × (seeds × structure units + the Greeks’ runs when include_greeks) — to bound estimated work before execution. hedge_research uses the hedge estimate without the legacy shelf benchmarks, then adds 2 × book × seeds for paired cash and coverage, plus response-memory allowances. The estimate is not a runtime guarantee. A refusal names the estimate, the formula with the inputs’ values and the inputs to lower.

GET /health

Returns the text OK with status 200 for a liveness check. Deployment verification uses /api/forwardflow/health to check the running version, source build, data digest and access mode as well.

POST /api/forwardflow/simulate

One run of the engine on one seeded path. Handler: forwardflow_api.rs::ff_simulate.

Request

FieldTypeDefaultMeaning
configSimConfigrequiredThe whole inputs panel
include_postingsboolfalseAttach the raw double-entry postings
include_agreementsbooltrueAttach the per-Agreement table (about 106 KB of the 112 KB default body at the base book; 5,837 bytes remain without it); side-runs that only read scalars pass false
include_researchboolfalseAttach the unhedged USD/BTC research observation and monthly replay from this same run

Response

FieldTypeMeaning
outputsRunOutputsThe Holder’s outputs, below
agreementsAgreementRow[]One row per Agreement; empty when include_agreements is false
agreement_countintBook size, always populated
pathnumber[]The monthly price path, index = month, length horizon + 1
conservation_okboolThe ledger’s postings sum to zero (ledger.rs::conservation_sum)
hazard_monthlynumber[]The stop hazard by payment age for the configured scenario and term, length term + 1, h[0] = h[term] = 0 (defaults.rs::DefaultScenario::monthly_hazard)
postingsPosting[] or nullThe ledger, when asked for
research{outcome, conventions}Present only with include_research: true; otherwise omitted. outcome is a ResearchOutcome, and conventions is the same ResearchConventions returned by paired hedge research

The optional research observation uses research.rs::unhedged_observation on the existing simulated book; it does not run another path. It equals hedge_research.samples[0].unhedged for matching configuration, seed and zero USD discount rate. It always includes monthly spot, paper_cash, hedge_cash, margin, hedge_cash_required and total_cash_required; hedge and margin values are zero. npv_usd uses zero discounting and equals net_gain_usd. The BTC holding comparison retains matching dated net contributions; it is a monthly-conversion equivalent, not a funded wallet balance. USD cash assumes receipts remain in dollars. Agreement rows and raw postings can both be omitted independently. Invalid non-finite BTC conversions are refused with 400, using the same guards as paired research.

RunOutputs (outputs.rs::RunOutputs, computed by outputs.rs::analyze):

FieldTypeMeaning
irr_monthlynumber or nullMonthly IRR of the Holder’s net cash flows (outputs.rs::irr_analysis, the lowest root); null when the flows are single-signed, no root lies in [−0.999, 10] per month, or the evaluation failed (irr_unavailable says which). irr_ambiguous (boolean) and irr_root_count say when the flow has several roots
irr_nominal_panumber or null12 × monthly
irr_effective_panumber or null(1 + monthly)^12 − 1, the figure quoted everywhere
wal_monthsnumber or nullWeighted average life of gross inflows
payback_monthint or nullFirst month cumulative net cash is ≥ 0
undiscounted_multiplenumber or nullGross inflow ÷ gross outflow
owner_total_inflow, owner_total_outflowstringGross dollars to and from the Holder (Owner in the ledger)
cash_recovery[int, number][]Cumulative gross cash ÷ outflow at months 12, 24, 36
exit_splitExitSplitCounts: completed, settled (early completion), non_performance, non_performance_rational, conviction_walks, rational_boundary, open
suppressed_defaultsintStop draws suppressed by rational mode
coin_returned_to_obligorsstringSpot value of coin taken at early completion, net of the payoff; Buyer property, never cash
total_shortfall_usdstringΣ over stops of (remaining schedule − sale proceeds) where the sale fell short
buyer_refunds_usdstringΣ dollar refunds to stopped Buyers
stop_surplus_usdstringΣ proceeds above the remaining schedule delivered to the Holder
cumulative_net_cashnumber[]Cumulative net Holder cash by month; feeds the cash fan
btcnowobjectorigination_fees, flow_fees, total_take (their sum), purchase_prices, paper_spread (Σ purchase − coin cost, zero at par)
btcnow_fee_monthlynumber[]BTC Now’s revenue by month; sums to total_take plus the spread

The exit vocabulary in the JSON is the engine’s: non_performance is a stop, settled is an early completion. Identifiers were kept when the words were locked (Marc, 2026-09-02, spec v1.5).

AgreementRow (outputs.rs::AgreementRow, built by outputs.rs::agreement_table):

FieldTypeMeaning
id, origination_month, term_monthsintIdentity
strikestringThe coin’s cost at entry (dispersed within the cohort month when input #20 is on)
outcomestringcompleted, settled, non_performance, non_performance_rational, conviction_walk (singular here, conviction_walks in the split), rational_boundary or open
exit_monthint or nullMonth of the exit; null while open
payments_madeintPayments made before the exit
delivered_grossstringEvery dollar that entered the Holder’s delivery stream
owner_netstringThe same net of the 5% servicing fee
fee_to_btcnowstringServicing fee taken on this Agreement’s deliveries
origination_to_btcnowstringThe first N payments routed to BTC Now, as far as they were made
shortfall_usdstringRemaining schedule at the stop minus the sale proceeds, where positive
coin_returned_usdstringCoin value taken at early completion, net of the payoff
purchase_pricestringCapital the Holder deployed
stop_proceeds_usdstringRecorded sale proceeds V after haircut and sale cost
buyer_refund_usdstringThe Buyer’s refund, \( \min(A,\ \max(0,\ V + A - P)) \), from engine.rs::stop_sale (Marc, 2026-08-22 and 2026-09-03, spec v1.5)
stop_surplus_usdstringProceeds above the remaining schedule kept by the Holder
expected_netstringWhat full performance would deliver: (terminal − first N) × (1 − fee)
capital_pnlstringowner_net − purchase_price

A posting (ledger.rs::Posting) has month, from, to, amount, kind, agreement. Entities are "Owner" (the Holder), "BtcNow", "Market" and {"Obligor": id} (the Buyer). Kinds are PurchasePrice, OriginationFee, PaymentDelivery, FlowFee, MakeWholeDelivery, StopSaleDelivery, StopRefund.

Example. One Agreement at the base terms, run clean so the schedule can be read off: a $60,000 coin, 1.475×, 60 payments of $1,475, payment 1 to BTC Now, 5% on every delivered dollar, the Holder buys at par. The configuration is BASE_CONFIG from lib.ts with vol_annual: 0, lifetime: 0, settlement_propensity: 0, cohorts: 1, agreements_per_cohort: 1 and intramonth_strike_dispersion: false.

The response excerpt below requests both include_postings: true and include_agreements: true alongside that configuration.

$ curl -s -X POST http://localhost:8080/api/forwardflow/simulate \
    -H 'Content-Type: application/json' -d @one.json
{
  "outputs": {
    "irr_effective_pa": 0.139654410356713,
    "wal_months": 31.0,
    "payback_month": 44,
    "undiscounted_multiple": 1.3778958333333333,
    "owner_total_inflow": "82673.75",
    "owner_total_outflow": "60000.00",
    "exit_split": { "completed": 1, "settled": 0, "non_performance": 0, "…": 0 },
    "btcnow": { "origination_fees": "1475.00", "flow_fees": "4351.25",
                "total_take": "5826.25", "purchase_prices": "60000.00", "paper_spread": "0.00" },
    "…": "…"
  },
  "agreements": [ { "id": 0, "outcome": "completed", "exit_month": 60, "payments_made": 60,
                    "delivered_gross": "87025.00", "owner_net": "82673.75",
                    "fee_to_btcnow": "4351.25", "origination_to_btcnow": "1475.00",
                    "capital_pnl": "22673.75", "…": "…" } ],
  "agreement_count": 1,
  "conservation_ok": true,
  "postings": [
    { "month": 0, "from": "Owner", "to": "BtcNow", "amount": "60000.00", "kind": "PurchasePrice", "agreement": 0 },
    { "month": 1, "from": { "Obligor": 0 }, "to": "BtcNow", "amount": "1475.00", "kind": "OriginationFee", "agreement": 0 },
    { "month": 2, "from": { "Obligor": 0 }, "to": "Owner", "amount": "1401.25", "kind": "PaymentDelivery", "agreement": 0 },
    { "month": 2, "from": { "Obligor": 0 }, "to": "BtcNow", "amount": "73.75", "kind": "FlowFee", "agreement": 0 },
    "… 116 more"
  ]
}

Payments 2 to 60 deliver $1,401.25 to the Holder and $73.75 to BTC Now each; 59 × $1,401.25 = $82,673.75, and 59 × $73.75 + $1,475 = $5,826.25. The full base book (24 cohorts × 10 Agreements, 43% bridge, 40% lifetime stop share, seed 42) returns irr_effective_pa 0.1421, exits 84 completed / 55 early / 101 stopped, total_shortfall_usd 990,282.37, buyer_refunds_usd 603,951.54 and stop_surplus_usd 148,595.85. That body is 111,692 bytes raw, 17,500 gzipped, 2,547 gzipped with include_agreements: false.

POST /api/forwardflow/montecarlo

runs seeded runs with seeds seed, seed + 1, …, seed + runs − 1 (outputs.rs::run_monte_carlo, in parallel), summarized. Handler: forwardflow_api.rs::ff_monte_carlo.

Request: config (SimConfig) and runs (int, 1 – 100,000, subject to the Agreement-run cap).

Response (MonteCarloSummary):

FieldTypeMeaning
runsintRuns summarized
irr_effectivePercentiles or nullDistribution of irr_effective_pa over the runs that had one; null when no run has an IRR (spec v1.12 — never a numeric zero)
wal_monthsPercentilesDistribution of wal_months
pct_negative_irrnumber or nullShare of the runs with an IRR whose IRR is below zero — conditional; null when none has one
pct_cash_lossnumberShare of ALL runs whose undiscounted multiple is below one — the cash-loss frequency, never conditional on an IRR; read it first when runs_without_irr is not zero
multiplePercentilesThe undiscounted multiple over every run that deployed capital
runs_without_irr, without_irr_reasonsint, NoIrrCountsRuns without an IRR, counted by reason: single_signed, no_root_in_range, non_finite
notestringWhat the IRR statistics are conditional on
irr_histogram[number, int][]50 equal bins from the worst to the best IRR: (left edge, count)
cash_fanCashFanPoint[]Per month: month, p5, p25, p50, p75, p95 of cumulative_net_cash across runs
base_seedintThe configuration’s seed

Percentiles carries worst, p1, p3, p5, p25, p50, p75, p95, mean, es3, es5. In forwardflow_api.rs::percentiles, over the sorted sample of size \( n \), a percentile is the nearest-rank value

\[ x_{(i)},\quad i = \operatorname{round}\big(p,(n-1)\big), \]

and the expected shortfall is the mean of the worst \( \lceil p,n \rceil \) runs (at least one):

\[ \mathrm{ES}p = \frac{1}{k}\sum{j=1}^{k} x_{(j)},\quad k = \lceil p,n \rceil . \]

p3 is the empirical third percentile, not a worst-case guarantee; es3 and es5 are what you average if you land in the tail, not where the tail starts.

Memoization. Results are cached in forwardflow_api.rs::MC_CACHE, an LRU of 64 summaries keyed by runs plus the serde serialization of the configuration. The key is built server-side from the struct, so a client’s JSON key order does not matter. The base book at 1,000 runs took 0.30 s cold and 0.006 s memoized.

$ curl -s -X POST http://localhost:8080/api/forwardflow/montecarlo \
    -H 'Content-Type: application/json' -d '{"config": <BASE_CONFIG>, "runs": 1000}'
{
  "runs": 1000,
  "irr_effective": { "worst": 0.0721, "p1": 0.0918, "p3": 0.0970, "p5": 0.1029, "p25": 0.1291,
                     "p50": 0.1493, "p75": 0.1722, "p95": 0.2190, "mean": 0.1538,
                     "es3": 0.0903, "es5": 0.0946 },
  "wal_months": { "p5": 34.48, "p50": 35.91, "p95": 37.34, "…": "…" },
  "pct_negative_irr": 0.0,
  "irr_histogram": [ [0.0721, 3], [0.0777, 2], "… 48 more" ],
  "cash_fan": [ "…", { "month": 84, "p5": 2288927.49, "p50": 4529348.68, "p95": 8948182.21, "…": "…" } ],
  "base_seed": 42
}

The bridge is endpoint-pinned, so this distribution is the uncertainty between $60,000 and $60,000, not a view on where the price ends. The archived Model Card’s pricing stance uses the zero-drift bootstrap instead; see Price paths.

Unavailable IRR statistics are null

A path whose flows never change sign (every payment retained by BTC Now, a written call assigned deep in the money) has no IRR, and since the follow-up audit of 6 September 2026 (finding 1; spec v1.12) no route prints a zero for one. Here irr_effective and pct_negative_irr are null when no run has an IRR; on the hedge routes (/hedge, /hedge_series, /rebalance_policies, /coin_seat) every Dist’s median_irr, mean_irr, p5_irr, p95_irr and pct_negative are null when no seed has one (the coin seat’s pct_negative is over all seeds and stays present), and the figures derived from a missing median or p5 — HedgeResult.cost_irr_points, floor_p5_irr, hedged_excess_over_basis_pp — are null with it. What is always present is the cash: pct_cash_loss, the multiples, seeds_with_irr / seeds_without_irr and without_irr_reasons. The audit’s zero-receipts probe (12 of 12 payments to BTC Now, eight seeds) reads median_irr null … pct_cash_loss 1.0, seeds_with_irr 0, single_signed 8; tests/openapi_shapes.rs holds every nullable field documented as such in openapi.json. A client should read the count before the statistic, and the cockpit does (reading.ts: “—”, with the reason).

The engine’s identity travels with every answer

Every response carries X-Engine-Spec, X-Engine-Version, X-Engine-Build (0.7.0+v1.14+<sha>: crate version + spec + the source revision build.rs stamps from GIT_SHA or git rev-parse, else unknown) and X-Engine-Data (the SHA-256 of the compiled-in price series; model audit 2026-09-06, M07); the browser WebSocket API does not expose handshake headers, so Monte Carlo also puts the identity on its Complete frame as engine; /api/forwardflow/health reports them as engine_spec, version, build, git_sha and data_sha256. A desk that records a completed run should take the identity off that answer, never off a global “last response” — the cockpit’s record builders do (record.ts, spec v1.12 item 4). See The version headers.

GET /api/forwardflow/montecarlo/ws

The same computation over a WebSocket, in chunks of 1,000 runs (forwardflow_api.rs::MC_CHUNK), so a heavy book neither times out nor looks dead. Handler: forwardflow_api.rs::handle_mc_socket.

Protocol. The client’s first text frame is the MonteCarloRequest JSON, exactly the REST body. The server then sends text frames, each a JSON object with an event field:

FrameFieldsWhen
Progressdone, total, pct (integer, 100·done/total)After every chunk
Completesummary (a MonteCarloSummary)Once; the client should close
ErrormessageValidation failure, engine 400, or a first frame that is not valid JSON; the server closes

A request already in the memo cache skips straight to Complete. If the client disconnects, the server stops after the current chunk. The heavy permit is taken per chunk, not per request, so a long Monte Carlo yields to other work between chunks. A transcript from a Node client (WebSocket is global in Node 22+), base book, seed 4242 to miss the cache, 3,000 runs:

{"done":1000,"event":"Progress","pct":33,"total":3000}
{"done":2000,"event":"Progress","pct":66,"total":3000}
{"done":3000,"event":"Progress","pct":100,"total":3000}
{"event":"Complete","summary":{"runs":3000,"base_seed":4242,
  "irr_effective":{"p5":0.1024,"p50":0.1494,"es5":0.0933,"…":"…"},"pct_negative_irr":0,"…":"…"}}

and the two error shapes:

{"event":"Error","message":"runs must be 1–100000 (input #14)"}
{"event":"Error","message":"expected ident at line 1 column 2"}

POST /api/forwardflow/solve_price

The inverse of the deal card (input #19): given a target effective annual IRR and the client’s own configuration, the purchase price at which the paper returns exactly that rate. outputs.rs::solve_purchase_price solves it directly — the Holder’s receipts do not depend on the price paid, so the price is the present value of the net receipts at the target rate over the present value of the strikes at their origination months, from one prepared run on the configuration’s seed — and verifies it with a second run (see The inverse price solver).

Request: config and target_effective_irr (number, e.g. 0.12; must lie in (−0.9, 10)).

Response: pct_of_strike (string, eight decimals; null only when the Holder receives nothing), usd_at_start_price (number, the fraction × start_price, or null), target_effective_irr echoed, irr_check (the verifying run’s effective IRR at that price), attainable (true when the price lies inside the desk’s 1%–500% bracket and the check agrees within a basis point) and note (the method, or why the target is not attained — the bracket edge it lies beyond, or nothing received).

$ curl -s -X POST http://localhost:8080/api/forwardflow/solve_price \
    -H 'Content-Type: application/json' -d '{"config": <BASE_CONFIG>, "target_effective_irr": 0.12}'
{"pct_of_strike":"1.03835638","usd_at_start_price":62301.3828,"target_effective_irr":0.12,"irr_check":0.12000000467405858,"attainable":true,"note":"the present value of the Holder's net receipts at the target rate (monthly compounding of the effective annual target) over the present value of the strikes at their origination months, from one prepared run; verified by a run at that price"}

At a 12% hurdle the base book clears at 103.7% of the coin’s cost, $62,220.60 on a $60,000 coin. The clean single Agreement of the simulate example clears at 104.15%, $62,491.80.

POST /api/forwardflow/heatmap

A sensitivity grid: every cell is a full deterministic run on the configuration’s seed, cells computed in parallel (M4, spec v1.3). Handler: forwardflow_api.rs::ff_heatmap. The request carries config plus a flattened GridSpec selected by grid:

gridRow axisColumn axisWhat each cell runs
PriceDefaultlifetimes (number[], lifetime stop share)end_prices (number[], dollars)A bridge to the column’s end price at the base bridge vol (43% when the base path is not a bridge), the baseline curve at the row’s lifetime
VolConvictionx_underwater (number[], fraction)vols (number[], annual)A bridge to the base end price at the column’s vol, the lost-conviction rule forced on at the row’s X with the configuration’s Y

Response: cells[row][col], the effective annual IRR, or null where the run is undefined or the cell fails validation. A bad cell is a hole, never a 400, because the rest of the map is still information. Grids are capped at 900 cells.

$ curl -s -X POST http://localhost:8080/api/forwardflow/heatmap -H 'Content-Type: application/json' \
    -d '{"config": <BASE_CONFIG>, "grid": "PriceDefault", "end_prices": [30000,60000,90000], "lifetimes": [0.2,0.4,0.6]}'
{"cells":[[0.1360,0.1459,0.1547],[0.1224,0.1421,0.1578],[0.0999,0.1298,0.1525]]}

The centre cell, 40% and $60,000, is the base run’s 0.1421 exactly. Read across for the price, down for the stops; the break-even frontier is the IRR = 0 contour on a wider grid.

POST /api/forwardflow/backtest

The static-pool vintage exhibit: one cohort originated at every fully seasoned historical month, each replayed against the actual price path that followed, all by the same engine. Handler: forwardflow_api.rs::ff_backtest.

Request: config only. path, cohorts and origination_stop_month are overridden per vintage (HistoricalReplay { start_index }, 1, none); everything else, including agreements_per_cohort as the units per vintage, is the sensitivity set. The replay is rebased so month 0 equals start_price, which keeps dollar figures comparable across vintages.

A vintage is seasoned when term + 1 months of history follow it (the extra month lets a stop at the last age settle, spec v1.5 item 4). With 174 monthly bars, February 2012 to July 2026, and a 60-month term, that is 113 vintages, February 2012 to June 2021.

Response: vintages (VintageRow[]), blended_net_gain_usd, blended_deployed_usd, blended_moic (every vintage bought at equal size), failed (vintages the engine rejected; holes, not a 400).

VintageRow:

FieldTypeMeaning
month[int, int]Origination (year, month)
start_indexintIndex into the history series
entry_closenumberThat month’s actual close, a context label
irr_effective_pa, moicnumber or nullFrom analyze
net_gain_usd, deployed_usd, shortfall_usdnumberInflow − outflow, outflow, Σ shortfall
completed, settled, non_performanceintExits; non_performance sums all four stop buckets
coin_returned_usd, buyer_refund_usd, stop_surplus_usd, btcnow_take_usdnumberAs in RunOutputs
$ curl -s -X POST http://localhost:8080/api/forwardflow/backtest -H 'Content-Type: application/json' \
    -d '{"config": <BASE_CONFIG>}'
{ "vintages": [ "…", { "month": [2021, 6], "start_index": 112, "entry_close": 35026.9,
    "irr_effective_pa": 0.1729, "moic": 1.3697, "net_gain_usd": 219143.32, "deployed_usd": 592714.83,
    "shortfall_usd": 28602.45, "completed": 6, "settled": 0, "non_performance": 4,
    "buyer_refund_usd": 46994.76, "stop_surplus_usd": 23506.44, "btcnow_take_usd": 57300.22, "…": "…" } ],
  "blended_net_gain_usd": 138206160.2, "blended_deployed_usd": 66412793.69, "blended_moic": 3.081, "failed": 0 }

The early vintages dominate the blend: a 2012 entry at $4.90 rebased to $60,000 rides the whole rally. Read the rows, not the blend, and see Limits on survivorship in a one-asset history.

GET /api/forwardflow/history

The embedded monthly bars (paths.rs::historical_months, paths.rs::historical_closes, compiled in with include_str!; no data files at runtime). Response: months ([year, month][]) and closes (number[]), 174 each, [2012, 2] to [2026, 7]; the first close is 4.9, the last 62,875.5, the highest 115,765. The cockpit uses it to label replay start months and to grey out the ones without enough remaining history.

POST /api/forwardflow/risk

The exposure layer (spec v1.6, Marc’s “start”, 2026-09-03): the two lines, coverage by month and by cohort, the exposure ladder at a month, PD·LGD·EAD per vintage, the Greeks by bump-and-revalue, and the rational frontier for a family of believed drifts. Handler: forwardflow_api.rs::ff_risk; the report is exposure.rs::exposure_report, the frontier boundary.rs::rational_frontier. The derivations are in The risk desk; this section is the wire format.

Request

FieldTypeDefaultMeaning
configSimConfigrequiredconfig.bump must be null; the Greeks own the bump
ladder_monthint or nullmonth of peak capital at riskMonth for the ladder and the as-of month for the Greeks
price_bumpnumber0.05Fraction, for delta and gamma
vol_bumpnumber0.05Vol points as a fraction, for vega
frontier_musnumber[][0, 0.10, 0.25, 0.50]Believed annual drifts, 1 to 8
greek_seedsint16Seeds averaged for the Greeks, 1 to 256
frontier_paramsBoundaryParams or nullconfig.rational_boundary, else BoundaryParams::default() (σ 0.414, μ 0.25, r_c 0.15, walk cost 0.025)Lattice parameters other than μ

Response. The ExposureReport fields are flattened to the top level, then frontiers and frontier_params.

lines (LineRow[], one per payments made, t = 0 to term, from exposure.rs::two_lines, scale-invariant in the coin’s cost so one table serves the book):

FieldMeaning
tPayments made so far
schedule_lineRemaining nominal schedule ÷ entry price
capital_lineSale proceeds that return the Holder’s unrecovered capital ÷ entry price
capital_above_purchase_priceThe capital line sits in the surplus regime (see below)
remaining_schedule_usd, unrecovered_capital_usdThe same in dollars

With \( U_t = \max(0,\ \text{purchase} - \text{net received}) \), \( R_t \) the remaining schedule, \( A_t \) the payments made and \( f \) the fee, exposure.rs::capital_proceeds_needed gives

\[ V^{\text{cap}}_t = \begin{cases} U_t/(1-f) & \text{if } U_t/(1-f) \le R_t \\ A_t + U_t/(1-f) & \text{otherwise,} \end{cases} \]

because between \( R_t \) and the Purchase Price the Holder is delivered exactly \( R_t(1-f) \) and the rest refunds the Buyer. At the base terms the lines read 1.475 / 1.0526 at \( t = 0 \), 1.4504 / 1.0526 at \( t = 1 \), 0.885 / 0.4872 at \( t = 24 \), and the capital line is zero from \( t = 44 \).

coverage (CoveragePoint[], one per calendar month): month, spot, active, below_schedule, below_capital (active Agreements whose sale at this month’s price, haircut and sale cost applied, would not cover the line), notional_usd (Σ remaining schedule), capital_at_risk_usd (Σ unrecovered capital), intrinsic_shortfall_usd. active counts the price-exposed Agreements (engine.rs::price_exposed_at, the one lifecycle the hedge desk and the monthly mark read too): a stopped Agreement stays on the panel until the month its sale is booked, round(D + lag/30.4375) — the month after the missed date at the program’s 18 days, three months on at 90.

coverage_by_cohort (CohortCoverage[]): origination_month, agreements, months_below_schedule and months_below_capital (mean per Agreement), share_below_schedule and share_below_capital (share of active Agreement-months).

ladder (Ladder): month, spot, active, total_notional_usd, total_capital_at_risk_usd, and 30 cells, one per moneyness bucket (< 0.50, 0.50–0.70, 0.70–0.85, 0.85–1.00, 1.00–1.20, ≥ 1.20, sale proceeds ÷ remaining schedule) × tenor bucket (≤ 6 mo, 7–12 mo, 13–24 mo, 25–36 mo, > 36 mo), each with agreements, notional_usd, capital_at_risk_usd. The bucket labels are the moneyness and tenor strings above, verbatim.

credit (CreditRow[], one per vintage and a book row last with origination_month: null): agreements, stops, completed, completed_early, pd (stops ÷ Agreements), ead_usd (mean remaining schedule at the stop), lgd (Σ shortfall ÷ Σ remaining schedule at the stop), el_usd (Σ shortfall), el_rate (÷ capital deployed), capital_loss_usd (Σ negative capital P&L on stops), deployed_usd, refunds_usd, surplus_usd. The book row’s el_usd equals the simulate response’s total_shortfall_usd.

greeks (Greeks, from exposure.rs::greeks):

FieldMeaning
price_bump, vol_bump, as_of_month, as_of_spot, seeds, vega_methodWhat was run
base_net_gain_usd, base_irrSeed-ensemble mean of the unbumped book as it stands at as_of_month
delta_usd_per_pctΔ net gain per +1% in every price after as_of_month, no cohort originating after it
delta_commitment_usd_per_pctThe same with the full pacing, later cohorts striking at the bumped prices
delta_coinsdelta_usd_per_pct ÷ (1% × as_of_spot), the coins a market-neutral desk would short
gamma_usd_per_pct2Second difference per (1%)²
vega_usd_per_vol_pointΔ net gain per +1 vol point: the base path kept through as_of and the deviations of the log returns after it scaled around their mean, on every path mode (vega_method says so); an existing book has zero
theta_usd_per_monthFlat-path net gain per month of weighted average life, the markup accrual
delta_irr_pp_per_pct, vega_irr_pp_per_vol_pointThe same in IRR percentage points
base_vol_annualRealized annualized vol of the base path

Delta is the central difference over the seed ensemble,

\[ \Delta = \frac{1}{n_s}\sum_{s} \frac{G_s(1+b) - G_s(1-b)}{2 \cdot 100,b}, \]

with \( b \) the price bump; every Agreement draws from its own seeded stream, so the difference is the bump’s alone.

frontiers (FrontierFamily[]): for each mu_annual, rows (FrontierRow[], one per payment): m (row index, payment − 1), payment, walk_below_spot, walk_below_of_entry, walk_below_moneyness (the boundary below which walking is optimal, in dollars, as a fraction of entry, and as spot ÷ amortized obligation; null when no lattice node walks), remaining_obligation, remaining_schedule. frontier_params echoes the lattice parameters used.

$ curl -s -X POST http://localhost:8080/api/forwardflow/risk -H 'Content-Type: application/json' \
    -d '{"config": <BASE_CONFIG>}'
{
  "lines": [ { "t": 0, "schedule_line": 1.475, "capital_line": 1.0526, "capital_above_purchase_price": false,
               "remaining_schedule_usd": 88500.0, "unrecovered_capital_usd": 60000.0 }, "… 60 more" ],
  "coverage": [ "…", { "month": 12, "spot": 30126.08, "active": 123, "below_schedule": 123, "below_capital": 117,
                       "notional_usd": 6876400.20, "capital_at_risk_usd": 4542958.33, "intrinsic_shortfall_usd": 3180156.59 }, "…" ],
  "coverage_by_cohort": [ "… 24 rows" ],
  "ladder": { "month": 23, "spot": 33375.25, "active": 203, "cells": [ "… 30 cells" ], "…": "…" },
  "credit": [ "…", { "origination_month": null, "agreements": 240, "stops": 101, "completed": 84, "completed_early": 55,
                     "pd": 0.4208, "ead_usd": 43562.82, "lgd": 0.2251, "el_usd": 990282.37, "el_rate": 0.1029,
                     "capital_loss_usd": 230645.54, "deployed_usd": 9619863.62, "refunds_usd": 603951.54, "surplus_usd": 148595.85 } ],
  "greeks": { "as_of_month": 23, "as_of_spot": 33375.25, "seeds": 16, "base_net_gain_usd": 5072824.14, "base_irr": 0.1586,
              "delta_usd_per_pct": 23259.16, "delta_commitment_usd_per_pct": 24884.96, "delta_coins": 69.69,
              "gamma_usd_per_pct2": -175.64, "vega_usd_per_vol_point": 8631.36, "theta_usd_per_month": 127160.35,
              "delta_irr_pp_per_pct": 0.0910, "vega_irr_pp_per_vol_point": 0.0415, "base_vol_annual": 0.3941, "…": "…" },
  "frontiers": [ "…", { "mu_annual": 0.25, "rows": [ { "m": 0, "payment": 1, "walk_below_spot": 31270.64,
                   "walk_below_of_entry": 0.5212, "walk_below_moneyness": 0.5212, "remaining_obligation": 60000.0,
                   "remaining_schedule": 88500.0 }, "… 59 more" ] }, "…" ],
  "frontier_params": { "sigma_annual": 0.414, "mu_annual": 0.25, "r_c_annual": 0.15, "walk_cost_of_strike": 0.025 }
}

Two things to notice. The ladder month, left unset, came back as 23, the month of peak capital at risk, which on this path is also the last origination month; the existing-book delta and the commitment delta therefore coincide, since no cohort originates after the as-of month either way. And the base IRR in the Greeks (0.1586) is a 16-seed mean, not the seed-42 run’s 0.1421. The whole call took 0.05 s.

Connecting from your own code

The API is plain JSON over HTTP; no client library is needed. Python, standard library only:

import json, urllib.request

API = "http://localhost:8080"

def post(path, body):
    req = urllib.request.Request(
        f"{API}{path}", data=json.dumps(body).encode(),
        headers={"Content-Type": "application/json"}, method="POST")
    try:
        with urllib.request.urlopen(req) as r:
            return json.load(r)
    except urllib.error.HTTPError as e:
        raise RuntimeError(f"{e.code}: {e.read().decode()}") from None  # 400 carries the named input

config = json.load(open("base_config.json"))          # a SimConfig; Decimals as strings
run = post("/api/forwardflow/simulate", {"config": config, "include_agreements": False})
print(run["outputs"]["irr_effective_pa"], run["agreement_count"])

mc = post("/api/forwardflow/montecarlo", {"config": config, "runs": 1000})
print(mc["irr_effective"]["p5"], mc["irr_effective"]["es5"])

Parse the string fields with decimal.Decimal, not float, if you intend to re-add them; the ledger is exact to the cent and a float sum will not be.

TypeScript, the cockpit’s own client. web/app/forwardflow/lib.ts exports the types in this chapter (SimConfig, RunOutputs, AgreementRow, MonteCarloSummary, RiskResponse, and the rest) and an api object built on one helper:

const API = process.env.NEXT_PUBLIC_API_URL || "http://localhost:8080";

async function post<T>(path: string, body: unknown, signal?: AbortSignal): Promise<T> {
  const r = await fetch(`${API}${path}`, {
    method: "POST",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify(body),
    signal,
  });
  if (!r.ok) throw new Error(await r.text());   // the 400 body is the engine's message
  return r.json();
}

export const api = {
  simulate: (config, includePostings = false, opts?) =>
    post<SimulateResponse>("/api/forwardflow/simulate",
      { config, include_postings: includePostings, include_agreements: opts?.includeAgreements ?? true },
      opts?.signal),
  monteCarlo: (config, runs, signal?) =>
    post<MonteCarloSummary>("/api/forwardflow/montecarlo", { config, runs }, signal),
  monteCarloWs: (config, runs, onProgress) =>
    new Promise<MonteCarloSummary>((resolve, reject) => {
      const ws = new WebSocket(`${API.replace(/^http/, "ws")}/api/forwardflow/montecarlo/ws`);
      ws.onopen = () => ws.send(JSON.stringify({ config, runs }));
      ws.onmessage = (ev) => {
        const msg = JSON.parse(ev.data as string);
        if (msg.event === "Progress") onProgress(msg.pct as number);
        else if (msg.event === "Complete") { resolve(msg.summary); ws.close(); }
        else if (msg.event === "Error") { reject(new Error(msg.message)); ws.close(); }
      };
      ws.onclose = () => reject(new Error("closed before finishing"));  // a no-op once settled
    }),
  // solvePrice, heatmap, backtest, risk, history follow the same shape
};

Two habits the cockpit keeps and a client should copy: abort superseded requests through the AbortSignal, and pass include_agreements: false on any run that only reads scalars. The real monteCarloWs also guards against settling the promise twice; the excerpt drops that for length.

Hosting notes

  • Where the client points. The workspace normally calls its same-origin /api/forwardflow/ proxy, which forwards to FF_API_URL and adds the server-held FF_API_KEY. NEXT_PUBLIC_API_URL explicitly bypasses that proxy for an open engine. The token route supplies the normal WebSocket URL and short-lived token; see Access.
  • CORS. A 24-hour max-age, so the browser pays one preflight per route per day, not one per POST. With FF_CORS_ORIGINS unset any origin is allowed; the hosted engine lists https://ff.btcnow.com (see Access).
  • Compression. CompressionLayer gzips every response the client accepts compressed; tiny bodies are skipped. Measured on the base book: 111,692 bytes raw, 17,500 gzipped, 2,547 gzipped and slim.
  • Memoization. Identical Monte Carlo requests, REST or WebSocket, are served from the 64-entry LRU. This is sound only because the engine is seeded; a change that breaks determinism breaks the cache silently, which is one reason the invariant suite diffs golden outputs.
  • Memory. The server uses jemalloc so freed pages return to the operating system after a Monte Carlo peak; the system allocator holds them.
  • Concurrency. Three heavy permits, rayon on cores − 1 threads, the async runtime on the rest. A deploy that puts the server behind a proxy should allow WebSocket upgrades on /api/forwardflow/montecarlo/ws and a request timeout long enough for a 100,000-run Monte Carlo, or use the WebSocket for anything large.

Result identity and configuration fingerprints

The completed run package records the arguments and engine identity associated with the response. Preserve that package for reproduction, including endpoint-specific settings. A scenario edited later is not the completed request.

Some chart and CSV exports use export.ts::sha8: the first eight hex characters of SHA-256 over JSON.stringify(config). That fingerprint depends on property order and numeric/string representation; it does not encode the assumptions and cannot identify an engine build by itself. Keep the full request with any standalone exhibit.

MCP uses a separate canonical replay hash with recursively sorted keys. Its run_id is not interchangeable with sha8. See Verification and reproduction and MCP result metadata.

Access — keys, tokens, limits

The engine runs in one of two modes, decided once at start by the environment it reads (backend/src/access.rs::AccessConfig::from_env).

  • Open modeFF_API_KEYS unset or empty. Every /api/forwardflow/* route answers without a credential and the engine logs one warning at start, FF_API_KEYS unset — the API is open. A local cargo run --release is open when no keys are supplied.
  • Keyed modeFF_API_KEYS holds name:key,name:key, every key at least 24 characters. Every /api/forwardflow/* route requires a credential except three that stay open in both modes: GET /health, GET /api/forwardflow/health and GET /api/forwardflow/openapi.json. The hosted engine at https://btcnow-forwardflow.fly.dev runs keyed.

Two more variables complete the contract: FF_TOKEN_SECRET, an HMAC-SHA256 secret of at least 32 characters that enables the websocket token below (unset = tokens disabled), and FF_CORS_ORIGINS, comma-separated exact origins (unset = permissive).

The header

A desk authenticates with one header on every request:

X-API-Key: <key>

The same key can instead be sent as Authorization: Bearer <key>. Both REST and MCP accept these headers; sending both together is refused with HTTP 400. Invited parties need no ForwardFlow account or login. See the connection guide to request a token and choose a supported client.

The engine compares the presented key against every configured key in constant time (access.rs::name_for_key). Only the key’s name ever leaves the engine: it appears in the tracing log and is attached to authenticated responses as X-Key-Name. The key is never logged, never echoed and never part of an error.

Getting, keeping and revoking a key

Email info@btcnow.com to request access. BTC Now issues one named token per invited person or organisation and delivers it privately. No ForwardFlow account is needed. Keep the token in your client’s credential settings or a private server configuration.

To replace or revoke a token, contact BTC Now. The operator updates FF_API_KEYS and restarts or rolls every engine instance; removal takes effect for new requests on each restarted instance. There is no guaranteed one-minute revocation window. Already running work is not retroactively cancelled. The short-lived websocket token has its own expiry. See operator steps.

The limits

Keyed callers share REST and MCP allowances under the key’s name. Anonymous callers are counted by IP; explicit MCP open mode has a separate anonymous rate state. These counters and the compute/memory budgets live in each server process, so multiple machines do not form a distributed quota.

A dropped request or disconnected websocket requests cooperative cancellation at the next engine checkpoint. A single simulation may finish its current unit before its compute resources are released. Simulation response memory remains reserved while its serialized or compressed response bytes are still owned by the transport. The websocket also watches for disconnects while a chunk is running; a socket that sends no request within ten seconds of upgrading is closed.

Every estimate checks the configuration before computation. Requests must fit both their per-request peak ceiling (FF_MAX_ESTIMATED_BYTES, default 1.5 GB) and the free part of the shared process budget (FF_MEMORY_BUDGET_BYTES, 1.2 GB in fly.toml). A request that does not fit beside current work returns 503 with Retry-After: 5 and does not calculate. Detailed simulation flags add Agreement rows, postings and bounded response buffers to the estimate.

LimitValueOn breach
Requests per rolling minute120429, Retry-After: <seconds> until the oldest request leaves the window
Concurrent heavy-route requests, including every MCP request4429 too many concurrent runs for this key, Retry-After: 5

The per-key concurrency classification includes /mcp, montecarlo and its websocket, heatmap, backtest, risk, fair_value, hedge, hedge_research, hedge_series, rebalance_policies and coin_seat. It also includes simulate, solve_price and placement when their complete estimate exceeds 1,000,000 Agreement-runs or 128,000,000 bytes peak memory. MCP protocol calls consume a per-key slot without necessarily running a calculation.

The four per-key slots do not override the shared three-calculation compute limit, its eight waiting places, or the memory budget. Each route checks its own work formula against the 24,000,000 Agreement-run ceiling; MCP adds the tighter tool limits. A refusal names the route and inputs to reduce. See Errors and caps for the underlying configuration limits.

401 and 429

Both are JSON, both name what went wrong, and neither repeats a value it was sent.

HTTP/1.1 401 Unauthorized
{"error":"missing X-API-Key header"}

HTTP/1.1 401 Unauthorized
{"error":"invalid X-API-Key"}

HTTP/1.1 429 Too Many Requests
Retry-After: 37
{"error":"rate limit exceeded: 120 requests per minute for this key"}

The websocket route’s 401s add missing X-API-Key header or token query parameter, token expired, token signature invalid, token expiry is more than 10 minutes ahead and websocket tokens are disabled on this engine (FF_TOKEN_SECRET unset).

The version headers

Every response, open or keyed, success or error, carries

X-Engine-Spec: v1.14
X-Engine-Version: 0.7.0
X-Engine-Build: 0.7.0+v1.14+<sha>
X-Engine-Data: <sha256>

X-Engine-Spec is the spec the engine implements (access.rs::ENGINE_SPEC); X-Engine-Version is the crate version from backend/Cargo.toml; X-Engine-Build is the three joined (access.rs::ENGINE_BUILD): crate version, spec and the source revision backend/build.rs stamps at build time — GIT_SHA from the environment (the deploy workflow passes the commit), else git rev-parse --short=12 HEAD on the build host, suffixed -dirty when that host’s backend/ tree had uncommitted changes, else unknown — identifying the deployed source revision. Local edits made under the same -dirty revision can share that marker; use an immutable committed build for reproducible shared results. X-Engine-Data is the SHA-256 of the compiled-in price series (data/btc_historical_monthly.csv), the data half of the identity: a data refresh changes the header without anyone bumping a version. The browser WebSocket API does not expose handshake headers, so Monte Carlo also puts the identity on its Complete frame as engine (spec, version, build, data_sha256), and /api/forwardflow/health reports them as engine_spec, version, build, git_sha and data_sha256. A desk that stamps its own exhibits should record the build and the data digest beside the configuration hash, off the answer that produced them.

The websocket token

A browser cannot send a header on a WebSocket upgrade, so GET /api/forwardflow/montecarlo/ws alone accepts a second credential, the query parameter ?token=<t> (access.rs::mint_token / verify_token):

t       = base64url(payload) + "." + base64url(HMAC-SHA256(FF_TOKEN_SECRET, payload))
payload = "<key-name>|<expiry unix seconds>"        expiry at most 10 minutes ahead

The token names a key rather than carrying one, so the socket is counted against that key’s limits and answered with its X-Key-Name. It is minted server-side by whoever holds a key and the secret — for the cockpit, by the web app’s own GET /api/forwardflow/token route — and the browser learns only the token and the socket URL. A desk’s own code needs no token: it sends X-API-Key on the upgrade request.

The health route and the OpenAPI document

GET /api/forwardflow/health is open in both modes. The following illustrates a 0.7.0 response; read the live response for the actual deployed version and build:

{"status":"ok","engine_spec":"v1.14","version":"0.7.0","build":"0.7.0+v1.14+1a2b3c4d5e6f","git_sha":"1a2b3c4d5e6f","data_sha256":"8846a81b803001d885b0cc3b830f26bfeba35405b91d1843c6376eeca5d8a1bf","mode":"keyed"}

GET /api/forwardflow/openapi.json is the OpenAPI 3 document for every route, compiled into the binary from backend/openapi/openapi.json and also open: https://btcnow-forwardflow.fly.dev/api/forwardflow/openapi.json. The next chapter, Try it, loads it into a Swagger UI.

A first call

Against the hosted engine, the base configuration, one run, scalars only:

$ curl -s -i -X POST https://btcnow-forwardflow.fly.dev/api/forwardflow/simulate \
    -H 'X-API-Key: REPLACE_ME_desk_alpha_key_0001' \
    -H 'Content-Type: application/json' \
    -d '{"config": <BASE_CONFIG>, "include_agreements": false}'
HTTP/2 200
content-type: application/json
x-key-name: desk-alpha
x-engine-spec: v1.14
x-engine-version: 0.6.0
{"outputs":{"irr_monthly":…},"agreements":[],"agreement_count":240,…}

REPLACE_ME_desk_alpha_key_0001 is a placeholder, never a key; <BASE_CONFIG> is the configuration object from Every input.

The cockpit at ff.btcnow.com holds its own key server-side: the browser calls same-origin /api/forwardflow/<path>, the cockpit’s server adds the key and forwards, and the websocket carries a token minted the same way. Nothing a desk can see in the page is a credential.

Paired hedge research

POST /api/forwardflow/hedge_research accepts config, 0–8 structures, 1–32 seeds, optional objective (dollar or coin, default dollar for legacy callers), optional surface, option_rate, put_skew_points, exec_cost_bps (default 50), discount_rate_annual (default 0), valuation_seed (default 42), and include_monthly (default false; at most 8 seeds when true). Surface warning acceptance follows the other hedge endpoints. Cross-book collars and wrapping seed ranges are refused.

The response includes surface_note and any warnings alongside the calculation fields. samples contains ordered per-seed unhedged and strategy outcomes, dollar and coin net gain, NPV, cash needs, horizon marks and paper exposure peaks. Optional monthly arrays support replay inspection. See the comparison guide for definitions. The served OpenAPI describes every field.

Keep valuation_seed fixed: it controls estimation noise in the coin-delta trading rule independently of realized paths. To scale a run, make sequential requests with disjoint consecutive seed ranges and identical assumptions. Check engine build/data identity and merge raw samples before calculating statistics. Do not average separate batches’ percentiles. The endpoint uses the shared heavy queue, per-key limits, cancellation and memory budget. Larger browser runs stop when the page closes.

MCP exposes the same bounded calculation as hedge_risk_samples, with replay inputs and engine identity. Small individual tool calls do not establish precise tail risk.

The additive workspace contract retains response schema: 1 and returns objective plus conventions version 2. The objective changes interpretation only: the same inputs return identical raw samples for Dollar and Coin. Present invalid objectives are refused. A price path that produces a non-finite BTC conversion is refused with HTTP 400 instead of returning a null numeric result. discount_rate_annual discounts USD only; no BTC NPV is implied.

Each outcome now carries coin_holding: contributed_coins, recovered_coins, holding_benchmark_coins, surplus_coins, realized_cash_contributed_coins, realized_cash_recovered_coins and signed horizon_mark_coins. Fund each Agreement purchase at its actual entry price, then combine that BTC outlay with the month’s USD receipts and hedge economic flows converted at monthly spot. Negative net BTC amounts are contributions; positive amounts are recoveries. Holding retains exactly the same dated contributed BTC, so the benchmark equals contributions and surplus equals existing net_gain_coins (up to floating-point tolerance when subtracting totals). These are strategy-specific net contributions, not a common initial budget. Gross acquisition at par is exactly one BTC per Agreement, before receipt offsets and hedge needs. Purchase prices different from strike and phased purchases follow their actual dated net flows. Reserves, borrowed capital, external free BTC and conversion trading costs are excluded.

Economic totals include surviving derivative marks at the horizon; realized-only fields exclude them. Negative marks may create a hypothetical terminal contribution. monthly.spot gives the spot used for each replay month. paper_cash + hedge_cash is realized net USD cash; add horizon_mark_usd in the final month to reconcile USD economic totals. For BTC, use monthly.btc_book.net_realized_cash_btc and add coin_holding.horizon_mark_coins once; purchases use their own entry prices, so dividing the total USD flow by monthly spot does not reconstruct the BTC result. The USD cash-requirement screen retains receipts in dollars and reserves futures margin. It is separate from monthly BTC conversion: the two readings do not establish one jointly funded portfolio or wallet. Full conventions.

Coin measurement fields in 0.6.0

simulate retains its legacy scalar fields and adds coin_irr_unavailable, coin_irr_ambiguous and coin_irr_root_count; USD diagnostics describe USD only. Set include_research: true for the complete unhedged observation and convention metadata.

hedge_research accepts structures: [] for unhedged-only sampling. Coin holding outcomes provide paper_purchase_coins, paper_receipt_coins, economic_recovery_multiple, economic_total_return, realized_cash_recovery_multiple and realized_cash_total_return. Nullable ratios are unavailable when their denominator is not positive and finite or the calculated ratio is non-finite. BTC cash-flow IRR is annualized; total return is a dimensionless recovery ratio minus one, not an annual rate.

Engine 0.6.0 and ResearchConventions.version: 2 fund each Agreement purchase at its actual entry price: one BTC at par. agreement_notional_coins records actual originated Agreements times one BTC; paper_receipt_offset_coins records same-month receipt offsets against Agreement purchases before hedge cash. The old monthly-purchase conversion method remains part of saved pre-0.6.0 evidence and must not be mixed with current results.

Detailed unhedged observations expose coin_cohorts, with count, contractual BTC, funded purchases, fee-net receipts, gain and dated Coin return per origination month. Monthly also returns these unhedged rows on median_seed. Shared hedge costs are not silently allocated. Gross cohort amounts and gains reconcile to the book; cohort IRRs and net-contribution ratios are not added.

When monthly observations are requested, monthly.btc_book contains the engine’s BTC-equivalent component ledger, with modeled USD originals and the conversion spot available beside it. Sum flow components; use end observations for value stocks. Realized cash excludes horizon marks and the economic bridge adds the signed mark once. Fees already netted from receipts must not be deducted twice. The measurement guide defines the denominators and reporting/settlement boundary.

Keep valuation_seed fixed when comparing hedges or replaying a path. Legacy hedge, coin-seat and monthly endpoints disclose their optional valuation-seed convention; research’s fixed valuation seed is independent of the realized config.seed. A matched market seed alone is insufficient if the estimated hedge rule changes.

hedge_series accepts objective (legacy default dollar) and optional selected_seed. Its response records objective, valuation_seed and selection: { metric, seed }. Coin selects the lower-median economic BTC gain; Dollar selects the lower-median finite USD IRR, or USD multiple if every IRR is unavailable. selected_seed must belong to the requested sample. A replay keeps both the market seed and the valuation seed. Explicit Coin monthly and Coin drift requests reject the incomplete legacy cross-book collar.

Monthly btc_book includes flow components and separate open derivative/Agreement value stocks for the selected path and bands. Research’s optional monthly ledger does not compute monthly valuation stocks: those two fields are null, not zero. Its signed horizon derivative value remains available separately in coin_holding.

The hedge response separates realized_settlement_mean_usd from horizon_mark_mean_usd; their sum is payoff_mean_usd. A remaining mark is economic value, not already received cash. Monthly delta_series.price_exposed_agreements gives the population matching total delta, including stopped collateral awaiting sale. Raw delta equivalents and actual strategy positions are different quantities.

API explorer

Read the request schema and try an engine operation from this page. For natural-language questions, use Connect an AI assistant.

Choose how the request is sent. Without a credential in this viewer, requests use the workspace’s same-origin proxy and its server-held key. To use your party’s access, choose the intended engine in the viewer, select Authorize and enter either an API key or a Bearer credential. Supply one method only; authenticated requests go directly to the selected server.

The viewer does not persist authorization across reloads. Executing an operation uses the engine’s normal capacity and rate allowance. Read the access rules before running a large analysis.

To request your own access token, email info@btcnow.com. BTC Now delivers it privately. In the viewer’s Bearer authorization field, enter the token itself; the viewer adds Bearer to the request header. This differs from an MCP client’s generic header value, where you enter the full Bearer YOUR_ACCESS_TOKEN text. Choose only one authorization method.

The schema comes from the engine serving this workspace. If the explorer cannot load, open the workspace schema or the hosted engine’s schema. Each describes the build serving that address.

Loading the API explorer… If the viewer does not appear, open the schema directly.

How the engine works

This chapter is the map of the machine. It says what the pieces are, in what order one simulated month runs, which conventions fix the timing and the cents, and why the same request always returns the same bytes. The math of each piece has its own chapter; this one shows where each piece sits and names the function that owns it.

Three layers

The simulator is three programs stacked on one another.

LayerWhereJob
EngineRust crate forwardflow (backend/forwardflow/)All model math. Config in, a ledger and a list of Agreements out. No I/O, no clock, no network.
APIAxum server (backend/src/main.rs, backend/src/api/forwardflow_api.rs)Stateless HTTP over the crate. Runs the engine on a blocking thread, turns validation panics into HTTP 400, memoizes Monte Carlo, enforces the abuse caps.
CockpitNext.js app (web/app/forwardflow/)Research, risk analysis, hedge strategies, historical vintages, monthly analysis and scenario reporting. The browser displays server results and derives presentation summaries and independent ledger checks.

The source defines the implemented calculation. The specification records its intent. A disagreement requires investigation; it is not resolved merely by displaying a result. lib.rs lists the modules: contract (the schedule and the implied rate), defaults (hazards), paths (prices), ledger, fees, money, engine (the monthly loop), outputs (IRR, WAL, Monte Carlo), exposure and boundary (risk analysis), and surface, hedge, series and coin (pricing, hedge flows, monthly analysis and coin-denominated analysis). The server exposes the calculations through the REST API and the bounded MCP interface. The API reference lists the current route families.

There is no database. A request carries the whole configuration; the response carries the whole result. There is no saved-run database. The server does retain bounded computation caches and access-limit state; the browser can retain scenario drafts and completed run records locally.

Three entities and the Market boundary

Every dollar the engine moves belongs to one of three parties, or crosses the boundary to the market. They are the variants of ledger.rs::EntityId.

Ledger entityThe word in this bookWho
Ownerthe HolderThe receiving side of the paper. Holder 0 is always BTC Now; a Holder n bought the paper by Sale.
BtcNowBTC NowOriginator and servicer. Takes the first N payments and the servicing fee, never a share of a stop sale.
Obligor(id)the Buyer of Agreement idThe paying side. Buyer 0 opened the Agreement; the paying side changes hands by Transfer.
Marketthe marketThe external counterparty of the recorded stop sale. Dollars enter the system here when a coin is sold.

The identifiers Owner and Obligor are older than the vocabulary lock (Marc, 2026-09-02) and stay in the code so that stored results keep deserializing. The words in prose are Holder and Buyer.

The Market entity is what makes a stop balance. The coin is never on the ledger; only dollars are. When a stop sells the coin, the proceeds are posted from Market to the Holder, to BTC Now and to the Buyer, and the Market’s balance goes negative by exactly the sale amount. Everything the three parties end up with was either paid by a Buyer, paid by the Holder, or sold to the market.

The ledger

ledger.rs::Ledger is an append-only list of postings. A posting is one transfer: month, from, to, amount, kind, Agreement id. There are no debits without credits, because Ledger::transfer writes both sides of one amount at once. That is what “double-entry” means here, and it gives conservation for free.

\[ \sum_{e \in \text{entities}} \text{balance}(e) \equiv 0 \]

ledger.rs::Ledger::conservation_sum computes that sum; engine.rs::run asserts it is zero at the end of every run in debug builds, ff_simulate reports it as conservation_ok on every response, and the invariant test conservation_to_the_cent_across_scenarios checks it across scenario families. Ledger::verify_balances recomputes every balance from the raw postings and compares it with the running map, which is the drill-down audit: any reported total can be rebuilt from the postings by hand.

Three rules in Ledger::transfer keep the ledger honest. An amount must already be quantized to the cent, or the call panics. An amount must be non-negative; a refund is a transfer from the market to the Buyer, not a negative transfer the other way. An amount of exactly zero is dropped, so a stop whose refund is zero posts no StopRefund line at all. The invariant test stop_sale_regime_one_exact_cents asserts that last point.

Money: Decimal and f64

The engine keeps two kinds of numbers apart on purpose.

Money is rust_decimal::Decimal, quantized to cents by money.rs::cents, which rounds half-to-even. Every ledger amount, every schedule line, every fee posting is Decimal. Sums of Decimals are exact, so the schedule sums to the terminal value to the cent and the lifetime fee is exact to the cent.

Boundaries and statistics are f64. Prices on the path, the sale price of a stop, the implied financing rate, moneyness, hazards, IRR, WAL and the percentiles of a Monte Carlo are all floats. A float becomes money in exactly one place: money.rs::cents_from_f64, which is called when a strike is fixed at origination, when a sale price is turned into proceeds, when the value of the coin at early completion is recorded as the Buyer’s equity, and for the frontier’s reference strike. Once a number has become money it never goes back to a float on the ledger side.

The rule in the repository’s instructions is the short form: Decimal for money, f64 for boundaries and display, never f64 in a ledger posting.

One month, in order

engine.rs::run is a loop over calendar months \(m = 0, 1, \dots, H\). Before the loop it validates the configuration, seeds the path generator, draws the price path, applies the shock overlay and the bump overlay in that order, and (when input #25 is on) computes the rational walk-away frontier once. Then, each month, five steps run in a fixed order. The order is fixed for reproducibility, and because it is part of the model: which step runs first decides who has paid before who leaves.

StepWhat happensCode
0. OriginateIf \(m\) is still inside the origination window, a cohort of agreements_per_cohort Agreements opens. Each gets a strike (the month’s price, or a dispersed draw around it), a schedule, a fee state, and its own random stream. The Holder pays BTC Now for the paper, strike times input #6b: one PurchasePrice posting per Agreement.engine.rs::run, origination block
1. PaymentsEvery active Agreement at age \(1 \le t \le \text{term}\) makes payment \(t\), unless a walk is pending for it. With the rational boundary on, a Buyer whose coin is below the frontier for this payment date does not pay; the walk is armed instead. Payment \(t \le N\) goes whole to BTC Now (OriginationFee). Payment \(t > N\) splits into PaymentDelivery to the Holder and FlowFee to BTC Now. Payment \(\text{term}\) closes the Agreement as Completed.engine.rs::run, step (1)
2. Walks(2a) Every pending walk executes: the missed payment date is this month, and stop_sale runs. (2b) With the conviction rule on, streaks are updated: a Buyer whose coin is below \((1-X)\) times the strike for \(Y\) consecutive payment dates has a walk armed for the next payment date.engine.rs::run, step (2); engine.rs::stop_sale
3. Early completionsWhen the propensity \(p\) (input #12) is above zero, every active Agreement with no walk pending at age \(1 \le t < \text{term}\) draws a uniform \(x\) from its own stream. If the coin beats the remaining schedule, the Buyer completes early with probability \(u_t = p \cdot \max(0, (S_t - R_t)/S_t)\), paying \(R_t\) in cash and taking the coin. With the take-profit gate on, the draw still happens but only counts once \(S_t \ge P(1+x)\).engine.rs::run, step (3); engine.rs::settle
4. Hazard drawsEvery active Agreement with no walk pending at age \(1 \le t < \text{term}\) draws a uniform \(d\) from its own stream and stops if \(d < h_t\), where \(h_t\) is the scenario’s monthly hazard, scaled by the drawdown multiplier (capped at 1) when input #23 is on. In rational mode (input #9) the draw only sticks on a Buyer whose coin is below the amortized obligation; otherwise it is redirected to a random underwater Buyer, or suppressed and counted if there is none.engine.rs::run, step (4); engine.rs::drawdown_multiplier; engine.rs::Agreement::underwater

Two consequences of the order matter to a risk desk. First, an early completion or a hazard draw in month \(m\) follows that month’s payment. The draw records the next payment date as missed. Second, a successfully paid final date completes the Agreement before any further draw, but a hazard draw after payment \(\text{term}-1\) can miss the final payment. A pending walk or the boundary’s decision before payment can also consume it unpaid. defaults.rs calibrates the baseline lifetime target over draw ages 1 through term−1 and sets the hazard at the term to zero.

The rational-boundary decision sits inside step 1 rather than in a step of its own, because it is a decision about this payment. The Buyer looks at the coin, looks at the frontier for payment \(t\), and either pays or does not. A Buyer who does not pay has a walk pending, and (2a) executes it in the same month. The exit carries the tag RationalBoundary and is counted separately in the exit split. This mode was lifted from the parked Behavior Engine into the main engine as spec change v1.6 #6 (Marc, 2026-09-03); the frontier itself is derived in The risk desk.

The behavior models behind steps 2 to 4 are in Who stops paying. The conviction rule and the boundary are “walks” and the hazard is a “draw”: walks are decided at or before a payment date and consume it unpaid, draws happen after the payment date has passed.

Timing conventions

Every exit is stamped with a month, and the stop has three dates. The conventions are deterministic and written into engine.rs; reconciling a per-Agreement row against the postings needs all of them.

A draw at age \(t\) misses payment \(t+1\). Step 4 runs after step 1, so the Buyer made payment \(t\) this month. The missed payment date \(D\) is the next one, \(m+1\), and stop_sale is called with missed = m + 1. The Agreement’s exit_month is \(D\), and payments_made is \(t\).

A walk is the missed payment. A conviction walk or a boundary walk pending at month \(m\) consumes that payment date unpaid. The missed date is \(m\) itself; stop_sale is called with missed = m. So a walk at age \(t\) has payments_made \(= t-1\) and exit_month \(= m\).

Day 16 is the Stop Date; the sale is 18 days after the missed payment. A payment up to 15 days late carries nothing (R-1035). The coin is sold for dollars within two business days of day 16 (R-1037), which the engine models as stop_sale_lag_days = 18 calendar days after \(D\) (input #24). The sale is priced on the path by log-linear interpolation between the monthly marks:

\[ \text{pos} = D + \frac{\text{lag}}{30.4375}, \qquad k = \lfloor \text{pos} \rfloor, \qquad f = \text{pos} - k, \qquad S_{\text{sale}} = \exp\big((1-f)\ln S_k + f \ln S_{k+1}\big) \]

At 18 days, \(f = 0.5914\). The proceeds are \(V = S_{\text{sale}} \cdot e^{-\text{haircut}} \cdot (1 - \text{sale cost})\), turned into cents by cents_from_f64. Lag 0 sells at the missed date’s mark, lag 30.4375 at the next one; the invariant test stop_sale_regime_one_exact_cents checks all three.

Cash lands at the first monthly date at or after the sale. The three stop postings (StopSaleDelivery, FlowFee, StopRefund) are dated \(\lceil\text{pos}\rceil\), clamped to the horizon. At 18 days that is always \(D + 1\). The exit month stays \(D\).

Horizon. engine.rs::SimConfig::horizon uses the automatic run-off horizon below unless a supplied market_horizon_months extends it. A supplied shorter horizon is rejected. The automatic horizon is

\[ H = (\text{effective cohorts} - 1) + \text{term} + \left\lfloor\frac{\text{stop sale lag days}}{30.4375}\right\rfloor + 1 \]

The effective cohort count includes the origination cut-off. The last cohort originates at month \(\text{effective cohorts} - 1\); its Buyers can miss payment \(\text{term}\) at the latest. The settlement tail keeps that final stop’s sale and receipt inside the ledger. At the default 18-day lag it adds one month: 24 cohorts and 60 payments give \(H = 23 + 60 + 1 = 84\), with 85 path marks.

The whole stop is worked, with its three regimes, in The stop waterfall.

Determinism

The engine is fully seeded. Same configuration and same seed produce the same postings, byte for byte. Three mechanisms make that true, and a fourth makes it useful.

The path has its own stream. engine.rs::run seeds a ChaCha20 generator from config.seed (ChaCha20Rng::seed_from_u64) and uses it only for the price path: the bridge’s increments or the bootstrap’s block picks. Historical replay and custom paths draw nothing. Shocks and bumps are deterministic overlays applied after the draw.

Every Agreement has its own stream. engine.rs::agreement_rng(seed, id) builds a 256-bit ChaCha20 key from the configuration seed and the Agreement’s id. The mixing is splitmix64: the id is multiplied by the golden-ratio constant \(\text{0x9E3779B97F4A7C15}\), the product is rotated left by 23 bits and XORed with the seed, and four rounds of splitmix64 (add the constant, apply the finalizer) fill the four 64-bit words of the key. Agreement 17 under seed 42 gets the same stream in every run of every configuration that uses seed 42, whatever the other 239 Agreements did. Its dispersed strike (input #20), its early-completion draw, its hazard draw and, in rational mode, the redirect pick all come from this stream.

Draws are unconditional. In steps 3 and 4 the uniform is drawn before the engine looks at the price, every month the Agreement is eligible, whether or not the price makes the draw matter. Spec v1.6 introduced both this and the per-Agreement streams for one reason: bump-and-revalue. When the risk desk bumps every future price by 5% and reruns, the random numbers behind every decision are identical; only the decisions change. The difference between the two runs is a sensitivity, not draw noise. Under the earlier single shared stream, one Agreement leaving a month earlier shifted every later draw in the book, and the Greeks were unreadable.

Monte Carlo seeds are consecutive. outputs.rs::run_monte_carlo runs \(n\) simulations with seeds \(\text{base}, \text{base}+1, \dots, \text{base}+n-1\) (wrapping_add), in parallel on the rayon pool, collected in order. Run \(i\) of a 10,000-run Monte Carlo is exactly the single run you get by asking for seed \(\text{base}+i\). The WebSocket variant runs the same seeds in chunks of 1,000 (forwardflow_api.rs::run_chunk passes the chunk offset), so streamed and one-shot results are the same numbers. The reported base_seed on the summary is what a reader needs to reproduce any run of the distribution.

One consequence when comparing across versions: changing how the streams are derived changes the draws. The Model Card notes that the v1.6 per-Agreement streams draw differently from v1.5 at the same seed, which moved the zero-drift medians by a tenth of a point and the single-vintage replay median by ten points. Determinism is a property of one engine version.

The posting kinds

ledger.rs::TxKind has seven variants. Every dollar in a run is one of them.

KindFrom → ToWhenMeaning
PurchasePriceHolder → BTC NowOrigination monthThe Holder buys the paper. Strike times input #6b (par at the base terms: $60,000). Paper acquisition, not BTC Now’s take. The identifier names the Holder’s purchase, not the Buyer’s Purchase Price \(P\).
OriginationFeeBuyer → BTC NowPayments 1 to NOne of the first N payments, routed whole to BTC Now (input #3, N = 1 at the base terms). Never delivered to the Holder, so it carries no servicing fee.
PaymentDeliveryBuyer → HolderPayments N+1 to termA scheduled payment, net of the servicing fee.
FlowFeeBuyer or Market → BTC NowWith every deliveryThe flat servicing fee on the delivered dollar (5%, Marc 2026-08-31). Posted alongside a PaymentDelivery, a MakeWholeDelivery or a StopSaleDelivery.
MakeWholeDeliveryBuyer → HolderEarly completionThe remaining schedule \(R_t\), paid in cash, net of the fee. The identifier predates the vocabulary; it means early completion (spec v1.5 change 3, cash only).
StopSaleDeliveryMarket → HolderPayment date after the saleStop-sale proceeds less the Buyer’s refund, net of the fee. Can exceed the remaining schedule: the surplus above the Purchase Price is the Holder’s (Marc, 2026-09-03).
StopRefundMarket → BuyerPayment date after the saleThe Buyer’s dollar refund, \(\min(A, \max(0, V + A - P))\) (R-1033, Marc 2026-08-22). Dropped when zero.

BTC Now’s take is OriginationFee plus FlowFee, and nothing else (outputs.rs::analyze computes total_take as exactly that sum). There is no posting kind for a share of a stop sale, and the repository’s rules forbid adding one.

The money conventions

Three conventions fix how rational numbers become cents. They were decided at milestone M1 (M0_FIXTURES.md, “Cent-quantization conventions”) and they are what makes every identity in Verification testable to the cent.

C1. The payment is floored to the cent; the final payment absorbs the residual. contract.rs::ContractTerms::schedule computes

\[ \text{PMT} = \Big\lfloor \frac{P}{\text{term}} \Big\rfloor_{0.01}, \qquad \text{payment}_{\text{term}} = P - (\text{term} - 1),\text{PMT} \]

where \(P = \text{strike} \times \text{multiple}\) is the Purchase Price of the coin to the Buyer (the terminal value), itself rounded half-even to the cent by ContractTerms::terminal before the division. Flooring rather than rounding was an amendment after adversarial review (2026-07-11): rounding half-even could push the accumulated schedule past \(P\) at a micro-strike and make the final payment negative. With the floor, the residual is always non-negative and the schedule sums to \(P\) exactly. At the base terms, \(88{,}500 / 60 = 1{,}475.00\) is a whole cent and all 60 payments are equal. At 36 months, \(88{,}500 / 36 = 2{,}458.3\overline{3}\): payments 1 to 35 are $2,458.33 and payment 36 is $2,458.45.

C2. The fee is rounded cumulatively. fees.rs::FeeState::split keeps, per Agreement, the cumulative dollars delivered and the cumulative fee posted, and computes each fee posting as a difference:

\[ \text{fee}_k = \text{round}(f \cdot \text{cum}k) - \text{round}(f \cdot \text{cum}{k-1}), \qquad \text{net}_k = \text{delivery}_k - \text{fee}_k \]

Each posting conserves by construction (net plus fee equals the delivery), and the lifetime fee is exactly \(\text{round}(f \times \text{total delivered})\) whatever the individual postings did. The price of that exactness is that a single posting may wobble by a cent against \(\text{round}(f \times \text{delivery})\). At the base terms there is no wobble: 5% of $1,475.00 is $73.75, and the Holder nets $1,401.25 on every delivered payment. At 36 months the wobble appears: 5% of $2,458.33 is $122.9165, so the first fee posting is $122.92, the second is $122.91 (because \(\text{round}(0.05 \times 4{,}916.66) = 245.83\)), and the cumulative total is exact at every step. The fee applies to every delivered dollar: scheduled payments after the first N, early-completion payoffs, and stop-sale deliveries (Marc, 2026-08-31, “we retain 5% of each send”). The first N payments are not deliveries and carry no fee.

C3. IRR is computed on the quantized flows. outputs.rs::analyze builds the Holder’s monthly net flow from the ledger (ledger.rs::Ledger::owner_monthly_gross, inflows and outflows kept separate and netted only for IRR and payback), converts each cent-exact Decimal to f64, and outputs.rs::irr solves for the monthly rate by a grid scan in steps of 0.01 for a sign change of NPV over \((-0.95, 5.0)\) followed by 200 bisection steps. Nominal is 12 times the monthly rate; effective is \((1+r)^{12} - 1\). The M0 fixtures were generated in exact rational arithmetic on unquantized flows, so the Rust engine is required to match them within 0.01 percentage points, not exactly. The metrics are derived in Outputs.

A worked example at the base terms

Take the engine’s own default configuration (engine.rs::SimConfig::default): a $60,000 coin, 1.475× so \(P = $88{,}500\), 60 payments of $1,475, the first to BTC Now, 5% servicing, the Holder buys at par, sale 18 days after a missed payment at 25 bp cost and no haircut, a bridge from $60,000 to $60,000 at 43% volatility, the baseline hazard curve at a 40% lifetime prior, 2.5% monthly early-completion propensity, 24 cohorts of 10, seed 42. Posting this as POST /api/forwardflow/simulate with include_postings: true gives 240 Agreements, an 85-mark path, 17,728 postings and conservation_ok: true.

Origination. In month 0, ten PurchasePrice postings of $60,000.00 move from the Holder to BTC Now.

A delivered payment. Agreement 2 (cohort 0) pays $1,475.00 at age 1, posted whole to BTC Now as OriginationFee. At age 2 the same $1,475.00 splits: PaymentDelivery $1,401.25 to the Holder, FlowFee $73.75 to BTC Now.

A stop. Agreement 2 draws below its hazard at age 22 (month 22). Payment 22 was made in step 1; the draw in step 4 misses payment 23, so exit_month is 23 and payments_made is 22. The sale sits at position \(23 + 18/30.4375 = 23.591\), between the month-23 mark of $33,375.25 and the month-24 mark of $36,038.10; log-linear interpolation gives $34,925.24, and after 25 bp the proceeds are \(V = $34{,}837.93\). The Buyer has paid \(A = 22 \times 1{,}475 = $32{,}450\); the remaining schedule is \(R = 88{,}500 - 32{,}450 = $56{,}050\). Then \(V + A - P = -21{,}212.07 < 0\), so the refund is zero and no StopRefund posts. The whole $34,837.93 is delivered. Under C2 the Agreement had delivered \(21 \times 1{,}475 = $30{,}975\) with a cumulative fee of $1,548.75; the new cumulative is $65,812.93 and \(\text{round}(0.05 \times 65{,}812.93) = 3{,}290.65\), so the fee posting is $1,741.90 and the Holder’s StopSaleDelivery is $33,096.03. Both post in month 24, the payment date after the sale. The row shows shortfall_usd \(= R - V = $21{,}212.07\), stop_surplus_usd 0, owner_net $62,522.28 and capital_pnl $2,522.28: the Holder got its $60,000 back and a little more, because 21 delivered payments came in before the stop.

The three regimes of the waterfall, by the same arithmetic, for a Buyer who stops after 12 payments (\(A = $17{,}700\), \(R = $70{,}800\)) with 11 prior deliveries:

Proceeds \(V\)Refund \(\min(A, \max(0, V+A-P))\)Delivered \(V - \text{refund}\)FeeHolder netShortfall \(\max(0, R-V)\)Surplus \(\max(0, V-P)\)
$50,000$0$50,000.00$2,500.00$47,500.00$20,800$0
$80,000$9,200$70,800.00$3,540.00$67,260.00$0$0
$100,000$17,700$82,300.00$4,115.00$78,185.00$0$11,500

Below the schedule line the Holder takes everything and books a shortfall. Between the schedule and the Purchase Price the Holder is made whole first and the Buyer gets the rest of what he paid. Above the Purchase Price the Buyer is refunded in full and the surplus stays with the Holder. The refund base \(A\) includes payment 1, which BTC Now kept; that is modelled as “the maximum he paid” and is flagged as a term-sheet question, not a ruling (spec v1.5 change 8).

Abuse caps and the memoized Monte Carlo

The API runs on a shared box, so the engine and the server both bound the work a request can ask for. The cohort, book-size and Monte Carlo-work caps and the semaphore were added in the hosting hardening of 2026-07-13 (spec v1.4 change 13). Every cap returns HTTP 400 naming the offending input; the semaphore queues rather than refuses.

CapValueWhere
Cohorts (input #13)at most 1,200engine.rs::validate
Book size (cohorts × Agreements per cohort)at most 50,000 Agreementsengine.rs::validate
Term (input #6)1 to 480 monthsengine.rs::validate
Monte Carlo runs (input #14)1 to 100,000forwardflow_api.rs::validate_mc
Monte Carlo work (runs × book)at most 24,000,000 Agreement-runsforwardflow_api.rs::validate_mc
Concurrent heavy requests (Monte Carlo, heatmap, backtest)3 permits; the rest queueforwardflow_api.rs::HEAVY

The 24 million figure is the 100,000-run ceiling at the default 240-Agreement book; a bigger book gets fewer runs. The engine’s validate panics on a bad input with a message that names it; forwardflow_api.rs::catch_engine catches the unwind and turns it into the 400. That is why the build profiles must never set panic = "abort": the whole error path depends on unwinding.

Identical Monte Carlo requests are memoized. forwardflow_api.rs::MC_CACHE is an LRU of 64 summaries keyed by the run count and the configuration’s JSON. A hit returns the stored summary without touching the engine; the WebSocket variant sends its Complete frame at once. This is sound only because the engine is seeded: the same configuration and run count always produce the same summary, so a cached copy is the answer, not an approximation of it. The cache is in memory and is empty after a restart. A single simulate is not cached; it is fast enough not to need it.

The server also leaves one core free for the async runtime (main.rs sizes the rayon pool at cores minus one), gzips responses, and caches CORS preflights for a day. None of that changes a number.

What byte-identical means

The repository’s rule reads “same config ⇒ byte-identical output”. Here is the precise claim.

Given the same engine build, the same SimConfig (every field, including seed) and the same request flags, POST /api/forwardflow/simulate returns the same response body, byte for byte: the same postings in the same order with the same amounts, the same per-Agreement rows, the same path, the same outputs. Two consecutive requests at the base configuration above return bodies with the same SHA-256, and that is the check anyone can repeat with curl and shasum. The invariant test seeded_determinism in backend/forwardflow/tests/invariants.rs asserts the same thing one level down: two runs of one configuration serialize to identical postings, and a different seed serializes to different ones.

What is covered: every random draw (the path, dispersed strikes, early-completion draws, hazard draws, rational-mode redirects), every posting, every derived output, and every run of a Monte Carlo individually by its seed. Parallelism does not enter: each Monte Carlo run is independent and collected in order.

What is not covered: a different engine version, which may derive the streams differently (v1.5 to v1.6 did); a different configuration that looks the same but differs in a defaulted field; and floating-point results across different hardware or compilers, which the repository does not test. On one build on one machine, the claim is exact.

The risk desk depends on this. A bump-and-revalue Greek is the difference between two runs that share every draw and differ only in the prices. A Model Card figure carries its seed and its configuration, and is the deterministic function of them. If a reader reproduces a run and gets a different number, the build or the configuration changed, and the engine’s job is to make that the only possible explanation.

Contract and fees

Start with the scheduled cash flows of one Agreement: what the Buyer pays, what BTC Now retains, and what reaches the Holder. This chapter derives the payment schedule, implied financing rate, remaining balances and fee rounding.

Implementation: contract.rs, fees.rs, money.rs, and the origination and payment steps of engine.rs::run. The reference fixtures are M0_FIXTURES.md.

The worked numbers use the base terms throughout: a $60,000 coin, a 1.475× multiple, 60 monthly payments, the first payment to BTC Now, a flat 5% servicing fee on every dollar delivered to the Holder, and a Holder who buys at par. Every figure below is either printed by the engine, asserted by a test, or one line of arithmetic from a figure that is.

The terms in one line

An Agreement is a conditional sale of one coin. The Buyer pays a fixed dollar schedule; when the schedule is complete, title to the coin passes. The engine’s contract.rs::ContractTerms holds five fields:

FieldMeaningBase value
strikeThe coin’s dollar cost at origination, quantized to cents$60,000.00
multipleThe price multiple over the strike1.475
term_monthsNumber of monthly payments60
origination_paymentsFirst N payments routed whole to BTC Now (0 = off)1
servicing_fee_rateFlat share of every delivered dollar0.05

Nothing in contract.rs may hard-code a term. “Sixty months” is a default, not an assumption (spec v1.1 change 1, Marc, 2026-07-10). Every quantity below is written for a general term \(n\) and then evaluated at \(n = 60\).

Money is rust_decimal::Decimal quantized to cents; rates and boundaries are f64. money.rs::cents rounds half-to-even. A ledger posting is never an f64 (project rule 5).

The terminal value

The terminal value is the Agreement’s full dollar price, the Purchase Price in the program’s contract language:

\[ P = \text{cents}(\text{strike} \times \text{multiple}) . \]

At the base terms \(P = 60{,}000 \times 1.475 = $88{,}500.00\). This is contract.rs::ContractTerms::terminal. The engine’s stop waterfall calls this quantity \(P\) as well; the letter is shared on purpose (see the stop waterfall).

The schedule and convention C1

The nominal payment is the terminal value divided by the term:

\[ \text{PMT} = \frac{P}{n} = \frac{\text{strike} \times \text{multiple}}{n} . \]

At the base terms \(\text{PMT} = 88{,}500 / 60 = $1{,}475.00\), a whole number of cents. At other terms it usually is not, and a schedule of sixty identical cent amounts would not sum to the terminal. Convention C1 fixes this (M0_FIXTURES.md, amended after adversarial review 2026-07-11):

  1. Quantize PMT to cents rounding toward zero (a floor, for positive money).
  2. Every payment but the last is that floored amount.
  3. The final payment absorbs the residual, so the schedule sums to \(P\) exactly.

In contract.rs::ContractTerms::schedule:

\[ \text{PMT}^* = \lfloor \text{PMT} \rfloor_{0.01}, \qquad p_t = \text{PMT}^* ;(t < n), \qquad p_n = P - (n-1),\text{PMT}^* . \]

The floor, rather than half-even rounding, is what makes \(p_n \ge \text{PMT}^*\) at every strike. With half-even rounding an upward round on a tiny strike could push \((n-1),\text{PMT}^*\) past \(P\) and make the last payment negative; the invariant tiny_strike_schedule_never_goes_negative runs a $10 coin over 84 months to hold the line.

The 36-month example shows the residual:

Term\(P/n\) unquantized\(\text{PMT}^*\)Payments 1 to \(n-1\) sum toFinal payment
362,458.333…$2,458.33$86,041.55$2,458.45
481,843.75$1,843.75$86,656.25$1,843.75
601,475.00$1,475.00$87,025.00$1,475.00
841,053.5714…$1,053.57$87,446.31$1,053.69

At 36 months the last payment is twelve cents larger than the others; at 84 months, twelve cents as well. The unit test residual_absorbed_at_inexact_terms in contract.rs pins the 36-month case, and schedule_sums_to_terminal_any_term checks the sum at terms 1, 7, 24, 36, 48, 60, 84 and 120.

schedule()[t-1] is payment \(t\). The engine builds the vector once per Agreement at origination and never recomputes it.

The remaining nominal schedule

After \(t\) payments the Buyer still owes the tail of the schedule:

\[ R_t = \sum_{k=t+1}^{n} p_k . \]

This is contract.rs::ContractTerms::remaining_schedule, and inside the engine engine.rs::Agreement::remaining_schedule, which reads a suffix-sum table built at origination so the lookup is one index rather than a re-sum (spec v1.4 change 13; the values are identical). Because C1 makes all but the last payment equal, at the base terms \(R_t = 88{,}500 - 1{,}475,t\): $87,025 after payment 1, $70,800 after payment 12, $60,475 after payment 19, $35,400 after payment 36, zero after payment 60.

\(R_t\) is the dollar figure the program treats as “what remains.” It is what the Buyer pays at early completion (engine.rs::settle), what the stop waterfall pays the Holder first (engine.rs::stop_sale), and the numerator of the risk desk’s schedule line \(R_t / \text{strike}\).

The Buyer’s paid-in total is the complement, \(A_t = P - R_t\) (engine.rs::Agreement::paid_in, payment 1 included). The waterfall’s refund base is this \(A\).

The implied financing rate

The paper carries a rate even though the contract states none. It is the monthly rate \(i\) at which a level payment of PMT over \(n\) months has present value equal to the strike, the ordinary annuity identity:

\[ \text{PMT} = \text{strike} \cdot \frac{i,(1+i)^n}{(1+i)^n - 1} . \]

The engine calls \(12,i\) the nominal annual rate and \((1+i)^{12} - 1\) the effective annual rate, and the accepted name for either is the implied financing rate. contract.rs::ContractTerms::implied_monthly_rate solves the identity with the unquantized PMT (\(P/n\) as a float, so the rate does not jump with a cent of C1 residual) by bisection.

Two details of the solver matter for a desk that explores below-par pricing:

  • The domain is signed. Bisection runs on \(i \in [-0.5, 1.0]\) for 200 halvings. The right-hand side is increasing in \(i\) on that interval, so the bracket always contains the root. A multiple of exactly 1.0 gives \(i = 0\), and a multiple below 1.0 gives a negative rate, which is the correct reading of paper priced below its coin: at 0.90× and 60 months the solver returns a nominal rate of about −4.07% per year (invariant below_par_multiple_gets_a_negative_implied_rate). An earlier solver clamped at zero and corrupted the moneyness boundary for below-par inputs; the signed domain is the fix.
  • The zero point is guarded. When \(|i| < 10^{-12}\) the identity degenerates to \(\text{strike}/n\) rather than \(0/0\).

At the base terms:

QuantityValueWhere checked
\(i\) monthly0.0137481contract.rs::implied_monthly_rate
Nominal, \(12,i\)16.4978% (16.50% in the fixtures)unit test known_60mo_values; fixture “Paper rate nom.”
Effective, \((1+i)^{12}-1\)17.80%arithmetic from \(i\)

The rate is an f64, a display and boundary quantity, never a ledger amount. The engine solves it lazily, once per Agreement, on the first moneyness check (engine.rs::Agreement::implied_rate, an OnceCell); a run with neither rational mode on never pays for the bisection at all. The simulate response does not carry the rate; the report view’s configuration line prints \((\text{multiple} - 1) / \text{term-years}\), 9.5% at the base terms, which is a simple markup per year and not this rate.

Note what this rate is not. It is not the Holder’s return. The Holder pays the purchase price, not the strike, misses the first N payments, and gives up 5% of every dollar. The flat single-Agreement run at par prints an effective IRR of 13.97% against the paper’s 17.80%; the gap is BTC Now’s take. The return figures are derived in Outputs.

The remaining amortized obligation and moneyness

If the paper is read as an annuity at rate \(i\), then after \(t\) payments the Buyer’s obligation in present-value terms is the standard amortization balance:

\[ B_t = \text{strike},(1+i)^t - \text{PMT},\frac{(1+i)^t - 1}{i}, \qquad B_t = \text{strike} - \text{PMT},t ;\text{ when } i = 0 . \]

This is contract.rs::ContractTerms::remaining_obligation, with the rate solved inside, and remaining_obligation_at(t, i), the same formula with the rate supplied by the caller for hot loops. \(B_0 = \text{strike}\) and \(B_n = 0\) (unit test obligation_endpoints).

Moneyness is spot over that balance:

\[ M_t = \frac{S_t}{B_t} , \]

contract.rs::ContractTerms::moneyness. \(M_t < 1\) means the coin is worth less than the discounted value of what the Buyer still owes; the rational-default mode reads this as “underwater” (engine.rs::Agreement::underwater tests \(S_t < B_t\)) and the walk-away frontier of boundary.rs reports its threshold in this moneyness too. Who stops paying uses it.

At the base terms, with the spot held at the $60,000 entry:

After payment \(t\)\(R_t\) (nominal)\(B_t\) (amortized)\(R_t / B_t\)\(B_t / \text{strike}\)\(M_t\) at $60,000
088,500.0060,000.001.4751.0001.000
187,025.0059,349.891.4660.9891.011
679,650.0055,962.781.4230.9331.072
1270,800.0051,580.881.3730.8601.163
1960,475.0045,993.621.3150.7671.305
2453,100.0041,662.811.2750.6941.440
3635,400.0029,978.891.1810.5002.001
4817,700.0016,214.751.0920.2703.700
591,475.001,455.001.0140.02441.2
600.000.000

The \(B_t\) column is the formula evaluated at the solved \(i\); the other columns are ratios of the first three. Read the third column: the nominal tail exceeds the amortized balance by 47.5% at origination and the gap closes to zero at the last payment, because \(R_t\) still contains the paper’s future markup and \(B_t\) has discounted it away.

Two remaining quantities

The engine carries both \(R_t\) and \(B_t\) for one Agreement, and they answer different questions.

\(R_t\) is a contract amount. It is what a court, a Buyer and a Holder would agree is owed in dollars, and it is exact Decimal cents. Every cash event uses it: the early-completion payoff is \(R_t\), the stop waterfall pays the Holder \(\min(V, R_t)\) before any refund, the shortfall is \(\max(0, R_t - V)\), and the risk desk’s schedule line and put-ladder notional are sums of it.

\(B_t\) is an economic amount. It is the value of the remaining schedule at the paper’s own rate, an f64, and it exists so the engine can ask whether a Buyer who could reason like a desk would keep paying. A Buyer compares the coin to what the remaining payments are worth, not to their undiscounted sum; a coin at $65,000 after twelve payments is comfortably above \(B_{12} = $51{,}581\) even though it is below \(R_{12} = $70{,}800\). If the engine used \(R_t\) as the rational boundary, every Buyer would be “underwater” at a flat price through the nineteenth payment (\(R_{19} = $60{,}475\) still exceeds the $60,000 coin), and the rational mode would empty the book on a path that has not moved. Using \(B_t\) puts the boundary where a present-value thinker puts it.

So: nominal for the waterfall and the ledger, amortized for the boundary. One caveat for readers of the risk desk: the exposure ladder of spec v1.6 buckets Agreements by \(S_t / R_t\), the ratio a hedging desk maps onto listed strikes, and calls that “moneyness” as well. The two ratios differ by the \(R_t/B_t\) column above; the risk desk chapter says which one each exhibit uses.

The inverse input

Input #5 is specified in both directions: enter a multiple and see the rate above, or enter a nominal annual rate and derive the payment and the multiple. The engine’s half of the inverse direction is contract.rs::pmt_from_nominal_rate, which evaluates the annuity identity forward with \(i = r_{\text{nom}} / 12\). The function is not wired to an endpoint or a cockpit field yet; it is reached through its unit tests.

\[ \text{PMT}(r_{\text{nom}}) = \text{strike} \cdot \frac{i,(1+i)^n}{(1+i)^n - 1}, \qquad \text{multiple} = \frac{\text{PMT} \cdot n}{\text{strike}} . \]

At 0% the function returns \(\text{strike}/n\) directly (unit test inverse_input_zero_rate_degenerates: $1,000.00 at 60 months). At 16.50% nominal the M0 fixtures give:

TermPMTMultiple
36$2,124.261.2746×
48$1,715.821.3727×
60$1,475.071.4751×
84$1,208.871.6924×

The 60-month row is seven cents above the program’s $1,475.00 because 16.50% is the rounded display of 16.4978%; unit test inverse_input_matches_fixture holds the fixture to a cent. The output of the inverse input is unquantized; it becomes a schedule only when the derived multiple is fed back through C1.

The first N payments to BTC Now

Input #3 routes the first N scheduled payments whole to BTC Now (spec v1.1 change 2; N = 1 is the program). In the payment step of engine.rs::run, for a payment of age \(t \le N\) the entire \(p_t\) posts from the Buyer to BTC Now as OriginationFee, and the Holder’s stream begins at payment \(N+1\).

Three consequences the fee section relies on:

  • Those N payments are never “delivered.” They do not pass through the fee split, so they carry no servicing fee. The dollars delivered to the Holder over a completed Agreement are \(P - \sum_{k \le N} p_k\), which at the base terms is \(88{,}500 - 1{,}475 = $87{,}025\).
  • They are still in the Buyer’s paid-in total \(A\). Whether payment 1 sits in the stop refund base is flagged as a term-sheet item, modelled as yes (spec v1.5 change 8).
  • Setting N to 0 recovers the pre-program “toggle off” case that the M0 fixtures and the memo-config badge use.

The §6.4 fixture identity, tested as n_payments_identity, says the Holder’s lifetime cash at N versus at 0 differs by \(\sum_{k \le N} p_k ,(1 - f)\): at the base terms $1,475 × 0.95 = $1,401.25, one net payment. The identity is exact in the fixtures’ rational arithmetic; the Rust test allows the fee’s one-cent wobble (a 2¢ tolerance) and runs at the fixture rates, N = 1 and N = 3, terms 36 to 84.

The servicing fee and convention C2

The servicing fee is a flat share \(f\) of every dollar delivered to the Holder: scheduled payments after the first N, early-completion payoffs, and stop-sale deliveries, the termination send included. Program: \(f = 5%\) (Marc, 2026-08-31; spec v1.5 change 2, applied 2026-09-03). The Holder receives \((1 - f)\) of each delivery and BTC Now the rest; on every dollar the program pays out, BTC Now’s cut is the same 5%, with no exemptions.

At the base terms one scheduled delivery of $1,475.00 splits into a fee of $73.75 and a net payment to the Holder of $1,401.25. Both are whole cents, so at these terms every posting is identical; the engine prints 59 PaymentDelivery postings of 1,401.25 and 59 FlowFee postings of 73.75 for a completed Agreement.

The rounding rule matters at other terms. Convention C2, in fees.rs::FeeState::split, is cumulative rounding. Each Agreement keeps a running total of dollars delivered, \(C_k\), and of fee posted, and the fee on delivery \(k\) is the difference of two rounded cumulatives:

\[ \text{fee}k = \text{round}(f , C_k) - \text{round}(f , C{k-1}), \qquad C_k = C_{k-1} + d_k, \qquad \text{net}_k = d_k - \text{fee}_k . \]

round is money.rs::cents, half-to-even. Per-posting conservation, \(\text{net}_k + \text{fee}_k = d_k\), holds by construction (unit test split_conserves_each_delivery, 84 deliveries at the historical 5.25%). The state is per Agreement (engine.rs::Agreement::fee), and every delivery kind flows through the same FeeState, so a stop-sale delivery after twelve payments continues the twelve-payment cumulative.

Proving the lifetime identity

Claim. Over any sequence of deliveries \(d_1, \dots, d_K\) to one Agreement, the total fee posted is exactly \(\text{round}(f , C_K)\), the rounded fee on the lifetime delivered total, while each individual posting is within one cent of \(\text{round}(f , d_k)\).

Lifetime exactness. The sum telescopes:

\[ \sum_{k=1}^{K} \text{fee}k = \sum{k=1}^{K} \big[\text{round}(f C_k) - \text{round}(f C_{k-1})\big] = \text{round}(f C_K) - \text{round}(f C_0) = \text{round}(f C_K), \]

since \(C_0 = 0\) and \(\text{round}(0) = 0\). No intermediate rounding survives.

Per-posting wobble. Write \(\text{round}(x) = x + \varepsilon(x)\) with \(|\varepsilon| \le \tfrac{1}{2}\) cent. Then \(\text{fee}k = f d_k + \varepsilon(f C_k) - \varepsilon(f C{k-1})\), and the two error terms differ by at most one cent. A posting may therefore sit one cent above or below the naive \(\text{round}(f d_k)\), and never further.

Sign and cap. \(f C_k \ge f C_{k-1}\) and rounding is monotone, so \(\text{fee}_k \ge 0\). For \(f < 1\) and any delivery of a few cents or more, \(\text{fee}_k \le f d_k + 1\text{¢} \le d_k\). split asserts both in debug builds.

The identity is what makes the fee testable to the cent. Invariant fee_identity_exact_to_the_cent runs the flat zero-exit path at terms 36, 48, 60, 84 and 120 and asserts flow_fees == cents(f × terminal) with N = 0 and cents(f × (terminal − p_1)) with N = 1, as equalities on Decimal, not tolerances.

Worked at the base terms with N = 1: delivered \(C_K = 87{,}025.00\), lifetime fee \(0.05 \times 87{,}025 = $4{,}351.25\), Holder’s lifetime cash \($82{,}673.75\). With N = 0: fee $4,425.00, Holder $84,075.00. The engine prints exactly these (origination_fees 1,475.00, flow_fees 4,351.25, owner_total_inflow 82,673.75 on the single-Agreement flat run).

Worked at 36 months, where the wobble is visible. Each delivery of $2,458.33 carries a fee of \(0.05 \times 2{,}458.33 = 122.9165\), not a whole cent. The engine’s FlowFee postings run 122.92, 122.91, 122.92, 122.92, 122.91, 122.92, …, alternating as the cumulative error crosses half a cent, and the net postings to the Holder run 2,335.41, 2,335.42, …. Summing naive per-posting rounding, 35 × $122.92, would give $4,302.20. The exact lifetime figure is \(0.05 \times 86{,}041.67 = 4{,}302.0835\), which rounds to $4,302.08, and that is what the engine posts in total. Twelve cents of drift over one Agreement, gone.

The historical basis shows the same thing at the base term. At the pre-program 3.75% the per-payment fee was $55.3125; the postings ran 55.31, 55.31, 55.32, and sixty of them summed to exactly $3,318.75, which is the lifetime_total_is_exact unit test in fees.rs.

The fee’s history

The fee has been restated three times. The engine’s method (C2, the flat skim on delivered dollars) did not change; the rate did, and where the rate comes from did.

BasisRate at 60 monthsRuledSpec
0.75% per year × term-years, a skim on every delivered dollar3.75%Marc, 2026-07-10v1.1 §3.2
1% per year × term-years (cockpit base case)5.00%Marc, 2026-07-13v1.4 change 12
Flat share of every delivered dollar, not derived from the term5.00%Marc, 2026-08-31v1.5 change 2, applied 2026-09-03

The first form was written as fee_rate = base_fee_pa × (term_months / 12); the identifier base_fee_pa survives only in the M0 fixture file and in the spec’s §3.2 history paragraph, never in the engine. Its intent was that a completed Agreement paid a headline “0.75% per year of terminal value,” and the skim on delivered dollars was chosen over a time-based accrual because it earns the fee on stop-sale proceeds as well and stops earning on paper that has stopped. The second form raised the yearly figure and produced 5% at 60 months. The third dropped the term from the formula: “we retain 5% of each send,” uniform, at every term, with no exemptions. At the base term the last two are cash-identical; at 36 months the program now takes 5% where the July form took 3%, and at 84 months 5% where it took 7%.

ContractTerms::fee_rate remains a method rather than a field read so that the fee engine’s call sites read the same under either basis. The M0 fixtures keep their 0.75%-per-year figures by pinning the flat rate to the historical value per term (0.0225, 0.03, 0.0375, 0.0525, 0.075); the invariant suite constructs them that way (contract_math_config in tests/invariants.rs). Spec §6.5, which once asserted that the fee rate rises linearly with the term, now asserts the opposite.

The one property that has never moved: BTC Now takes fees only, the first N payments and the flat share of delivered dollars, never a share of a stop sale (project rule 2; spec v1.5 change 6). The stop chapter shows where the surplus of a sale goes instead.

The purchase price and the paper spread

The Holder pays the purchase price at origination, input #6b, expressed as a fraction of the strike:

\[ \text{purchase} = \text{cents}(\text{strike} \times \text{pct}) , \]

where \(\text{pct}\) is purchase_pct_of_strike.

The program default is par, 1.00× (Marc, 2026-07-12; spec v1.4 change 4, replacing the earlier 1.05×). At the base terms the Holder pays $60,000.00 for paper with a $88,500 schedule; the origination block of engine.rs::run posts it as PurchasePrice from Owner to BtcNow in the Agreement’s origination month. The fraction form, rather than a dollar input, is what keeps a 24-cohort paced book sane when each cohort strikes at a different price.

The paper spread is what BTC Now earns or gives up on the sale of the paper itself:

\[ \text{spread} = \sum_{\text{Agreements}} (\text{purchase} - \text{strike}) , \]

outputs.rs::BtcNowTake::paper_spread. At par it is zero, so the base case is unchanged; at 1.05× on the base coin it is $3,000 per Agreement and $720,000 on the default 240-Agreement book, and at 0.95× it is −$720,000 (invariant paper_spread_identity). It is reported separately from total_take, which stays fees-only so the M0 identities keep holding, and it enters the revenue timeline at each Agreement’s origination month.

For the Holder the purchase price is the whole of the outflow. On the flat single-Agreement run at par, $60,000 out and $82,673.75 in gives an undiscounted multiple of 1.3779×, payback in month 44, a weighted average life of 31.0 months and an effective IRR of 13.97%. The same run at 1.05× would multiply 1.3123× on $63,000. The M0 fixtures, generated at 105% and the historical 3.75% fee, print 1.3295× for the same Agreement; the program’s figures are the par-and-5% ones here. The Holder’s rate on the paper is derived in Outputs, and the inverse question, the price at which the paper clears a stated hurdle under a stated stop view, is input #19 in Every input.

Intramonth strike dispersion

Input #20. Real originations happen throughout a month, not at its close. With the toggle on (the cockpit’s base case since 2026-07-13, Marc; the engine’s own SimConfig::default leaves it off), each Agreement draws its own entry strike around the cohort month’s price instead of ten identical twins striking at one mark.

The spread is derived from what is known at entry, per cohort month, in engine.rs::run (engine.rs::entry_sigma_monthly). Let \(r_j\) be the path’s monthly log returns over the twelve months before the cohort month \(m\) (returns \(m-12 \dots m-1\), as many as exist) and \(\sigma_m^2\) their population variance; with fewer than three such returns, \(\sigma_m\) is the volatility the path mode states divided by \(\sqrt{12}\) (paths.rs::PathMode::stated_vol_annual: the bridge’s or the diffusion model’s σ, a replay’s realized volatility over the twelve recorded months before its start, the bootstrap’s regime; zero for a literal path, which states none). Before 2026-09-05 the spread was the whole generated path’s realized volatility, so a crash placed at month 24 moved the strikes struck at month 0 — the audit’s finding 2; the test finding_2_a_future_crash_cannot_move_month_zero_strikes pins the fix. An entry executed at a uniformly random time inside the month sees half the month’s variance on average, so

\[ \sigma_{\text{intra}} = \frac{\sigma_m}{\sqrt{2}} . \]

Each Agreement’s strike is then a mean-preserving log-normal draw from its own seeded stream:

\[ \text{strike} = \text{cents}\Big( S_m \exp\big(\sigma_{\text{intra}} z - \tfrac{1}{2}\sigma_{\text{intra}}^2\big) \Big), \qquad z \sim N(0,1) , \]

with \(S_m\) the month’s price. The half-variance term makes \(\mathbb{E}[\text{strike}] = S_m\), so dispersion changes the texture of a cohort without moving its average entry. On a 43%-per-year bridge the monthly deviation is about \(0.43/\sqrt{12} = 12.4%\) and \(\sigma_{\text{intra}}\) about 8.8%: the first three cohorts use exactly that, later cohorts the trailing year’s realized figure. On a flat path the stated and the realized variance are both zero, \(\sigma_{\text{intra}} = 0\), and the toggle changes nothing (invariant dispersion_is_inert_on_a_flat_path, so the fixtures stay valid either way).

The draw comes from the Agreement’s own ChaCha20 stream (engine.rs::agreement_rng, seeded from the run seed and the Agreement id), so a strike never depends on what the rest of the book drew earlier; that is what lets the risk desk’s bump-and-revalue Greeks be sensitivities rather than draw noise (spec v1.6). Everything downstream of the strike follows: the terminal, the schedule, the purchase price and the two remaining quantities are all per Agreement. The purpose is behavioral. Thresholds such as moneyness, the conviction rule and the coverage lines then fire across a cohort over a range of prices instead of all at once at a single price (Marc, 2026-07-12). The invariant intramonth_dispersion_smears_strikes_and_preserves_the_mean checks that a cohort’s strikes are distinct, that the book’s mean strike is within 3% of the undispersed book, and that the effective IRR moves by less than two points.

When the Greeks hold strikes fixed under a price bump, the dispersion draws come off the unbumped path, so the existing book’s strikes do not move with the bump; see Price paths, shocks and bumps.

The base Agreement in numbers

Everything above, for one Agreement at the base terms on a flat $60,000 path with no exits. Reproduce the table by posting the base configuration with one cohort of one Agreement, zero lifetime stop prior, zero early-completion propensity, vol_annual 0 and dispersion off to POST /api/forwardflow/simulate with include_postings: true (see The API); the run returns 120 postings. The identities behind it are pinned by cargo test --release (closed_form_contract_math_matches_m0_fixtures and fee_identity_exact_to_the_cent), which run at the fixtures’ own terms, 1.05× and the historical fee rates, not at the par-and-5% terms of this table.

QuantityValueSource
Strike$60,000.00input #1
Terminal value \(P\)$88,500.00terminal()
Payment, all 60$1,475.00schedule()
Implied financing rate, nominal / effective16.50% / 17.80%implied_monthly_rate()
Payment 1$1,475.00 to BTC NowOriginationFee posting, month 1
Payments 2 to 60, each$1,401.25 to the Holder, $73.75 to BTC NowPaymentDelivery and FlowFee postings
Dollars delivered to the Holder$87,025.00delivered_gross
Lifetime servicing fee$4,351.25flow_fees
Holder’s lifetime cash$82,673.75owner_total_inflow
BTC Now’s take$5,826.25total_take (fees only)
Purchase price$60,000.00 (par)PurchasePrice posting, month 0
Paper spread$0.00paper_spread
Holder’s undiscounted multiple1.3779×undiscounted_multiple
Holder’s effective IRR13.97%irr_effective_pa
Payback month / WAL44 / 31.0 monthspayback_month, wal_months

Conservation: $88,500 of Buyer payments equals $82,673.75 to the Holder plus $5,826.25 to BTC Now. The ledger’s sum over every posting is zero to the cent (conservation_to_the_cent_across_scenarios). The figures in the Model Card are medians of stochastic runs with stops, early completions and a live price; this table is the contract with nothing happening to it, which is the right place to start reading the rest of the math.

The stop waterfall

A stop is the Buyer ceasing payments. It is the one exit where the engine touches the market: the coin is sold for dollars, and the dollars are divided between the Buyer and the Holder by a fixed rule. This chapter derives that rule from the code, prices the sale on the path, walks the three regimes the rule produces, and reproduces the test that pins every cent.

The rule is the September program’s, ruled by Marc on 2026-08-22 (the refund formula, the Stop Date) and 2026-09-03 (the surplus to the Holder, delivery in dollars), and applied in spec v1.5 §3.3. The code is engine.rs::stop_sale; everything below is read off it.

The five quantities

Every stop is described by five dollar amounts, all cent-quantized Decimal (money.rs::cents).

SymbolMeaningWhere it lives
\(P\)The Purchase Price, strike × multiplecontract.rs::ContractTerms::terminal
\(A\)Every payment the Buyer made, payment 1 includedengine.rs::Agreement::paid_in
\(R\)The remaining schedule, \(R = P - A\)engine.rs::Agreement::remaining_schedule
\(V\)The recorded proceeds of the sale for dollarsengine.rs::stop_sale, field stop_proceeds_usd
refundThe Buyer’s dollar refund out of \(V\)engine.rs::stop_sale, field buyer_refund_usd

At the base terms \(P = 88{,}500\) and every payment is $1,475, so after \(k\) payments \(A = 1{,}475k\) and \(R = 88{,}500 - 1{,}475k\). Payment 1 went to BTC Now and was never delivered to the Holder, but it is in \(A\): the refund base is what the Buyer paid, not what the Holder received. The spec flags this as a term-sheet question that is modelled but not yet decided (v1.5 change 8).

The missed date

The engine runs each month in a fixed order (engine.rs::run): scheduled payments first, then pending walks, then early completions, then hazard draws. That order fixes the missed payment date \(D\), the age at which the Buyer did not pay.

A hazard draw at age \(t\) happens in step (4), after payment \(t\) has already been made in step (1). The payment that will be missed is the next one, so \(D = t + 1\). The code passes m + 1 to stop_sale for every draw, naive or rational, including a rational-mode draw redirected onto an underwater Agreement.

A conviction walk is armed at the payment date on which the streak breaches (step 2b) and executes at the next payment date (step 2a), where the pending walk consumes the date without a payment. The walk is the missed payment, so \(D = t\), the age at which it executes.

A rational-boundary walk is decided at the top of step (1), before the payment, when the coin sits below the frontier for that date, and executes in the same month’s step (2a). Again \(D = t\).

A draw cannot fire on the final payment date (t < term_months), but a walk can consume it (t <= term_months). To let a stop at age \(\text{term}\) settle inside the ledger, engine.rs::SimConfig::horizon simulates \(\lfloor \text{lag}/30.4375 \rfloor + 1\) months past the last cohort’s term (engine.rs::settlement_tail_months): one month at the program’s 18 days (v1.5 change 4), three at the 90-day stress (2026-09-05, the audit’s finding 6).

The Stop Date and the sale

A payment up to 15 days late carries nothing. Day 16 after \(D\) is the Stop Date (R-1035, Marc 2026-08-22): the Agreement ends and the sale for dollars begins. The sale follows within two business days (R-1037, Marc 2026-08-22). The engine models the sale at \(D + \text{lag}\) calendar days, where lag is input #24 stop_sale_lag_days, default 18: day 16 plus two business days. The input is validated to 0–90 days (engine.rs::validate).

The lockouts that follow a stop (six months, reinstated 2026-09-03) are outside the engine.

Pricing the sale on the path

The path holds one mark per month, \(S_0, S_1, \dots\). The sale happens between marks, so engine.rs::stop_sale interpolates. With the month of 30.4375 days (365.25 ÷ 12),

\[ \text{pos} = D + \frac{\text{lag}}{30.4375}, \qquad k = \lfloor \text{pos} \rfloor, \qquad f = \text{pos} - k, \]

\[ S = \exp\big((1-f)\ln S_k + f \ln S_{k+1}\big) = S_k^{,1-f}, S_{k+1}^{,f}. \]

Log-linear interpolation is the geometric mean weighted by the fraction of the month elapsed. It is the same convention the custom path mode uses between its anchors (paths.rs::custom), so a custom path and a sale on it agree. Both \(k\) and \(k+1\) are clamped to the last mark of the path as a guard, but the horizon carries the settlement tail, so the mark past the sale date always exists and no sale prices at a clipped mark (finding_6_a_90_day_lag_settles_inside_the_ledger checks lags 0, 18, 30, 30.4375 and 90 at the final defaultable age).

The two ends of the lag are worth naming. Lag 0 sells at the missed date’s own mark, \(S = S_D\). Lag 30.4375 sells exactly one month later, \(S = S_{D+1}\). The default 18 days sits at \(f = 18/30.4375 = 0.5914\) of the way between them. The test stop_sale_regime_one_exact_cents in tests/invariants.rs checks all three on a path that falls between the marks; the table in the walk-through below prints the values.

At monthly resolution this is a small refinement: the difference between an 18-day and a 30-day sale is a fraction of one month’s price move. It is modelled because the ruling names a day count, and because on a path that is falling through the missed month the sale is priced on the way down rather than at the top of it.

The proceeds

The sale price is reduced by the static haircut, input #15, and the market-sale cost, input #16:

\[ V = \operatorname{cents}\big(S \cdot e^{-\text{haircut}} \cdot (1 - \text{sale cost})\big). \]

The program haircut is 0 (Marc, 2026-07-12: a one-coin sale has no market impact, and stops never synchronize because payment dates differ by Buyer). The sale cost is 25 bp, so at the base terms \(V = 0.9975,S\). The haircut slider and the tornado’s 50% stress remain for a reader who wants an execution discount. \(V\) is stored on the Agreement as stop_proceeds_usd; it is the one number the auditor, the Holder and the Buyer’s statement share.

The refund and the delivery

The R-1033 rule (Marc, 2026-08-22, reporting counsel’s approval) reads: if the Buyer’s payments plus the sale proceeds come to the Purchase Price or less, the Buyer gets nothing back; if they come to more, the Buyer is refunded the excess, never more than he paid in. In engine.rs::stop_sale:

\[ \text{refund} = \min\big(A,\ \max(0,\ V + A - P)\big), \qquad \text{delivered} = V - \text{refund}. \]

Since \(R = P - A\), the middle term is \(V - R\): the refund is the proceeds above the remaining schedule, floored at zero and capped at what the Buyer paid. Read from the Holder’s side, the same two lines say: the Holder is paid the remaining schedule first, the Buyer is refunded up to what he paid, and whatever is left after both is the Holder’s. The refund is paid in dollars. A stopped Buyer never receives coin (v1.5 change 1).

The code guards both amounts with debug_assert!(delivered >= Decimal::ZERO && refund >= Decimal::ZERO), and both hold algebraically: the refund is at most \(V - R \le V\) when it is positive, and zero otherwise.

The fee on the delivery

The delivery is one more send to the Holder, so it carries the flat 5% servicing fee like every other delivered dollar (Marc, 2026-08-31: 5% of each send, termination sends included). engine.rs::stop_sale passes delivered through fees.rs::FeeState::split, which uses the cumulative-rounding convention: the fee posting is \(\operatorname{round}(0.05 \times \text{cum after}) - \operatorname{round}(0.05 \times \text{cum before})\), so the Agreement’s lifetime fee is exact to the cent even when a single posting wobbles by one. The Holder receives \(\text{delivered} - \text{fee}\).

The fee is BTC Now’s only take from a stop. There is no share of the surplus and no dial that could create one; the invariant test stop_sale_waterfall_identities_across_regimes asserts that BTC Now’s total take equals origination fees plus flow fees on a book where surpluses occur. That is the part of the July founder invariant that survived (spec v1.5 change 6).

Shortfall and surplus

Two per-Agreement fields describe how far the sale landed from the schedule and from the price:

\[ \text{shortfall} = \max(0,\ R - V), \qquad \text{surplus} = \max(0,\ V - P). \]

The shortfall is the Holder’s loss against the remaining schedule on that Agreement, in dollars, before the fee; it is zero once the sale covers the schedule. The surplus is the delivery above the remaining schedule, the part of the proceeds that exceeds the whole Purchase Price after the Buyer has been made whole. Both are stop_surplus_usd and shortfall_usd on the Agreement row, and both sum to book totals (total_shortfall_usd, stop_surplus_usd in outputs.rs). They cannot both be positive: a surplus needs \(V > P \ge R\).

The shortfall is not the Holder’s capital loss. The Holder paid \(P_{\text{buy}}\) (par, $60,000, at the base terms) and has already received net payments; the row’s capital_pnl is net received minus capital deployed and is the number to read for realized loss. The risk desk’s PD, LGD and EAD are built from the shortfall and the remaining schedule at the stop (see The risk desk).

The three regimes

The min and max make the rule piecewise. Ordering \(V\) against \(R\) and \(P\) gives three regimes.

RegimeConditionRefundDelivered to the HolderShortfallSurplus
1\(V \le R\)0\(V\)\(R - V\)0
2\(R < V \le P\)\(V - R\)exactly \(R\)00
3\(V > P\)\(A\)\(R + (V - P)\)0\(V - P\)

In regime 1 the sale did not cover the schedule; the Buyer’s payments cushion the Holder’s loss and the Buyer gets nothing back. In regime 2 the Holder gets exactly the remaining schedule and every dollar above it goes back to the Buyer, up to what he paid. In regime 3 the Buyer has been refunded in full, so the cap binds, and the Holder keeps the rest: the remaining schedule plus the surplus above the Purchase Price.

The worked examples below are at the base terms after twelve payments: \(A = 17{,}700\), \(R = 70{,}800\), \(P = 88{,}500\). Each was run through the engine on a custom path with the conviction rule set to walk on the first month below the strike (x_underwater: 0.0, y_consecutive: 1, hazard 0, early completion 0, one Agreement, lag 18), and every figure below is what the API returned. The path anchors are (11, 1.0), (12, r) for regime 1 and (11, 1.0), (12, 0.9), (13, r) for regimes 2 and 3; the walk is armed at month 12 and executes at month 13, so \(D = 13\) and the sale prices on the flat segment after month 13.

Regime 1, \(r = 0.7\). The sale prices at $42,000, so \(V = 42{,}000 \times 0.9975 = 41{,}895.00\). Then \(V + A - P = -28{,}905\), the refund is $0, and the whole \(V\) is delivered. Fee $2,094.75, net to the Holder $39,800.25. Shortfall \(70{,}800 - 41{,}895 = 28{,}905.00\). The Holder’s capital_pnl on the row is −$4,786.00: eleven delivered payments net of fee, $15,413.75, plus $39,800.25, less the $60,000 paid at par.

Regime 2, \(r = 1.4\). The sale prices at $84,000, \(V = 83{,}790.00\). \(V + A - P = 12{,}990\), below \(A\), so the refund is $12,990.00 and the delivery is \(83{,}790 - 12{,}990 = 70{,}800.00\), the remaining schedule to the cent. Fee $3,540.00, net $67,260.00. Shortfall 0, surplus 0. The Holder ends the Agreement with what completion would have paid (delivered_gross $87,025.00, the same as a completed row), forty-six months before the last scheduled payment and undiscounted.

Regime 3, \(r = 1.8\). The sale prices at $108,000, \(V = 107{,}730.00\). \(V + A - P = 36{,}930\), above \(A\), so the cap binds: refund $17,700.00, everything the Buyer paid. Delivery \(107{,}730 - 17{,}700 = 90{,}030.00\), which is \(R + 19{,}230\); the surplus is \(V - P = 19{,}230.00\). Fee $4,501.50, net $85,528.50.

All three post at month 14 (\(\operatorname{round}(13 + 18/30.4375) = \operatorname{round}(13.59) = 14\)) with exit month 13.

The same rule in the ruling’s own example, one coin at a Purchase Price of $90,000 with $24,000 paid (Marc, 2026-08-22): proceeds $66,000 refund $0; $80,000 refund $14,000; $90,000 refund $24,000; $110,000 refund $24,000, with $110,000 left on the receiving side (the Company in the ruling’s words; the Holder since the ruling of 2026-09-03).

Posting month and exit month

The stop has two dates in the ledger, and they differ on purpose.

The exit month is \(D\), the missed payment date: a.status = Exited { month: missed, tag }. It is the month the Agreement stopped being paid. The per-Agreement table reports it as exit_month (outputs.rs::agreement_table), and the exposure layer counts the Agreement as active only in the months before it (exposure.rs).

The posting month is the first payment date at or after the sale point, \(\lceil D + \text{lag}/30.4375 \rceil\) — the point itself when it is a whole month — always inside the horizon (the settlement tail is sized for it). With the default lag of 18 days the fraction is 0.59, so the cash lands at \(D + 1\); at 45 days at \(D + 2\); at 0 days at \(D\) itself. The sale is priced at the log-linear interpolation of the marks at \(\lfloor \cdot \rfloor\) and \(\lfloor \cdot \rfloor + 1\), and the posting month’s mark is the later of the two, so the cash is never booked before the last observation that priced it (model audit 2026-09-07, R02: booking at the nearest payment date put a 10-day or a 45-day sale’s cash one month before the mark it was priced on, so a price move strictly after the cash date revised realised cash — the audit’s $60,000 path with the following mark halved: $56,857.50 → $45,277.95 at 10 days, → $40,809.67 at 45; tests/model_audit_2026_09_07_r02.rs runs every allowed lag). The engine keeps the cash at monthly resolution because the ledger is monthly; the price is not rounded, only the posting date.

The refund deadline in the program (ten business days after the sale, R-1037) is inside the same month at this resolution, so the refund posts with the delivery.

The ledger postings

engine.rs::stop_sale writes three transfers, all at the posting month, all from the external Market entity, which is the counterparty of the recorded sale (ledger.rs::EntityId):

PostingFromToAmountTxKind
The delivery, net of feeMarketOwner (the Holder)\(\text{delivered} - \text{fee}\)StopSaleDelivery
The feeMarketBtcNowfeeFlowFee
The refundMarketObligor(id) (the Buyer)refundStopRefund

The ledger drops zero-amount transfers (ledger.rs::Ledger::transfer), so a regime-1 stop writes no StopRefund posting and the test asserts exactly that. Conservation holds by construction: the three amounts sum to \(V\), and \(V\) is what the Market gives up. No posting ever names the coin; the ledger is dollars only, and the coin’s disposal is represented entirely by the Market paying \(V\).

On the Agreement the stop leaves stop_proceeds_usd, buyer_refund_usd, stop_surplus_usd, shortfall_usd, and sets coin_returned_usd to zero. Two book totals accumulate in engine.rs::run: buyer_refunds_usd and stop_surplus_usd. The test stop_sale_waterfall_identities_across_regimes recomputes every row’s refund, shortfall and surplus from \(V\), \(A\) and \(P\), checks that the sum of StopRefund postings equals the book’s refund total, that each row’s delivered_gross equals its scheduled deliveries plus \(V - \text{refund}\), and that all three regimes actually occur on its rising path (500 Agreements on a flat-vol bridge from $60,000 to $240,000, naive draws).

The exact-cents test, walked

stop_sale_regime_one_exact_cents in tests/invariants.rs is one stop with every cent by hand. The configuration: one Agreement at the base terms, a custom path with anchors (5, 1.0), (6, 0.6), hazard 0, early completion 0, the conviction rule on with x_underwater: 0.0 and y_consecutive: 1, lag 18.

  1. The path. paths.rs::custom holds $60,000 through month 5, moves to $36,000 at month 6 and stays there. The strike is the month-0 price, $60,000.
  2. Months 1 to 6. The Buyer pays $1,475 six times: payment 1 to BTC Now, payments 2 to 6 delivered to the Holder, $7,375.00 gross, $368.75 of fee. \(A = 8{,}850.00\), \(R = 79{,}650.00\).
  3. Month 6, step (2b). The rule compares the spot with \((1 - 0) \times 60{,}000\). $36,000 is below it, the streak reaches 1, and a walk is armed for the next payment date. (At months 1 to 5 the spot equalled the strike; the comparison is strict, so the streak stayed at 0.)
  4. Month 7, step (1). The pending walk consumes the payment date; nothing is paid. Step (2a) executes the walk with missed = 7, tag ConvictionWalk. \(D = 7\).
  5. The sale. \(\text{pos} = 7 + 18/30.4375 = 7.5914\); \(S_7 = S_8 = 36{,}000\), so the interpolation is $36,000 exactly. \(V = 36{,}000 \times 0.9975 = 35{,}910.00\).
  6. The waterfall. \(V + A - P = 35{,}910 + 8{,}850 - 88{,}500 = -43{,}740\). Refund $0. Delivered $35,910.00.
  7. The fee. Cumulative delivered goes from $7,375.00 to $43,285.00; 5% of those is $368.75 and $2,164.25; the posting is the difference, $1,795.50. Net to the Holder $34,114.50.
  8. The record. Shortfall \(79{,}650 - 35{,}910 = 43{,}740.00\). Surplus 0. Posting month \(\operatorname{round}(7.5914) = 8\). Exit month 7. Lifetime fee on the row $2,164.25. No StopRefund posting. Conservation sum $0.00.

The test then swaps in the path (5, 1.0), (6, 0.6), (8, 0.5), which keeps falling through the missed month, and runs the same stop at three lags. The marks are \(S_7 = 60{,}000 \sqrt{0.6 \times 0.5} = 32{,}863.35\) and \(S_8 = 30{,}000\).

Lag (days)\(f\)Sale price \(S\)\(V\)Posts at
00$32,863.35 (\(S_7\))$32,781.207
100.3285$31,893.68$31,813.957
180.5914$31,138.59$31,060.748
30.43751$30,000.00 (\(S_8\))$29,925.008

The test asserts the two ends exactly and that 18 days lands strictly between them. The other rows were produced by the same configuration through the API.

What died with the July rule

Before v1.5 the stop was the sale rule of spec v1.2 (Marc, 2026-07-11), described there under the older word that the vocabulary lock of 2026-09-02 retired; the doc comment on input #15, the haircut, still carries it. Three things about it are gone.

The sale was sized to the remaining schedule. Only enough coin was sold to deliver \(R\); the Holder’s entitlement was capped at the Agreement. Now the whole coin is sold, and the proceeds are divided by the waterfall.

The residual coin went back to the Buyer. Whatever coin was not needed for \(R\) was the Buyer’s property and was returned to him, in coin, at the sale price. Now the Buyer receives a dollar refund by formula, and coin never reaches a stopped Buyer.

The sale was priced at the next month’s mark. Now it is priced at \(D + \text{lag}\) days by interpolation, which at the default lag is 41% of the way back toward the missed date’s mark.

The founder’s July sentence, that the Holder can never keep more than it is owed, described that rule and is superseded for the Holder (spec v1.5 change 6). What was kept is that BTC Now takes fees only.

The Model Card’s reading of the change (v2.0, 3 September 2026) is the practical summary. On a driftless path the two rules deliver almost the same cash, and the zero-drift medians moved by a tenth of a point. The right tails and every historical-replay row moved a lot, because under the September rule a Buyer who stops while the coin is worth more than the Purchase Price hands the surplus to the Holder, and on Bitcoin’s own history that surplus is large.

Why the difference matters to a Holder

Three consequences follow from the rule as coded, and a desk should hold all three at once.

The surplus is the Holder’s. In regime 3 the delivery is \(R + (V - P)\), not \(R\). The July rule capped the Holder at \(R\) on every stop; the September rule caps the Buyer at \(A\) instead. This is the long call at the Purchase Price described in The risk desk: struck at 1.475× entry, exercised only by a Buyer who stops in the money. It is rare by construction (the drawdown multipliers, input #23, halve the hazard in the money when they are on, as they are in the Model Card’s run; see Who stops paying), but when it fires it is worth the whole gap between the coin and the Purchase Price, and the Model Card marks it as the least-evidenced behavior in the model and the one now driving the replay upside.

A stopped Buyer never gets coin. The refund is dollars, whatever the price did. There is no partial lot, no delivery to a wallet, no Buyer-side price exposure after the Stop Date. The engine sets coin_returned_usd to zero on every stop; only early completion leaves coin with a Buyer. The ruling’s reasoning (Marc, 2026-08-22) was that a stop is a refund right with no upside: the only way to profit from Bitcoin under the Agreement is to complete the purchase.

The Holder may buy coin with the dollars. Delivery is in dollars by ruling (Marc, 2026-09-03), with no in-kind residual term. A directional desk that wanted the coin can buy it back with the proceeds on its own account, at its own execution cost. The engine models the dollars; the planned reinvest-into-coin view models the Holder’s own act, not a term of the paper. A stop is still a forced sale for dollars at the bottom of a drawdown, and the Holder’s exposure is to the sale price, not to the coin thereafter, unless it chooses otherwise.

Between the first and the second consequence sits the regime that will occur most often on a falling path, regime 1, where nothing changed in kind from July: the sale fails to cover the schedule, the Holder books the shortfall, and the Buyer’s payments cushion it. What changed there is only the pricing date. The whole loss story of the paper still lives in the first nineteen months of each vintage, where \(R\) sits above the entry price (\(88{,}500 - 1{,}475 \times 19 = 60{,}475\) after payment 19, \(59{,}000\) after payment 20) (The risk desk draws that line).

Buyer behavior

Buyer behavior determines when an Agreement stops or completes early. The model combines baseline hazards, price-dependent multipliers, underwater redirects, a lost-conviction rule and a rational walk-away boundary. These rules are assumptions to test, not calibrated observations of the program.

This chapter explains their order and interactions. Implementation: defaults.rs, steps 1–4 of engine.rs::run, and boundary.rs.

The repository supplies no observed BTC Now vintage dataset with which to calibrate these rules. The baseline is the program’s actuarial prior, the multipliers are motivated by mortgage evidence, and the frontier follows the explicit assumptions about the Buyer’s beliefs and choices. The last section says how a Holder should bracket them. The worked numbers use the base terms: a $60,000 coin at 1.475×, 60 payments of $1,475, payment 1 to BTC Now, 5% on every delivered dollar, par purchase, the stop sale 18 days after the missed payment at 25 bp.

The four steps of a month

engine.rs::run evaluates every calendar month in a fixed order, and the order is part of the model (spec §4):

  1. Scheduled payments. Every active Agreement at age \(1 \le t \le n\) pays payment \(t\), unless a walk is pending. Under the rational boundary (input #25) the decision comes first: below the frontier the payment is not made and a walk is armed.
  2. Walks. (2a) A walk armed at this date executes now; the walk is the missed payment. (2b) The lost-conviction streaks update and may arm a walk for the next date.
  3. Early completions. One uniform draw per active Agreement at ages \(1 \le t < n\), against the propensity \(u_t\).
  4. Stop draws. One uniform draw per active Agreement at ages \(1 \le t < n\), against the hazard \(h_t\), multiplied and redirected as the toggles say.

Two timing facts follow. An early completion or hazard draw at age \(t\) follows payment \(t\). Early completion pays the remaining schedule immediately; a hazard draw misses payment \(t+1\). A walk in step 2 consumes the date unpaid; the missed payment is \(t\) itself. The stop waterfall takes the missed date from there.

Since v1.6 every Agreement carries its own ChaCha20 stream, seeded from the configuration seed and its id (engine.rs::agreement_rng), and the draws in steps 3 and 4 are taken unconditionally before any price test. A bumped price changes which draws matter, never the draws themselves; that is what makes the risk desk’s bump-and-revalue a sensitivity rather than noise.

The baseline hazard

Input #7 in its default form is DefaultScenario::BaselineCurve { lifetime }. It produces a vector \(h_1, \dots, h_n\) of monthly hazards indexed by payment age (defaults.rs::DefaultScenario::monthly_hazard; index 0 is unused so that h[t] reads directly). \(h_t\) is the probability that an Agreement which has just made payment \(t\) stops before payment \(t+1\).

The hump

The shape is a step function of term fraction \(x = t/n\) (defaults.rs::shape_weights):

Term fractionWeight \(w_t\)At 60 months
\(x \le 0.05\)1.0payments 1 to 3
\(0.05 < x \le 0.25\)2.0payments 4 to 15 (the hump)
\(0.25 < x \le 0.40\)1.2payments 16 to 24
\(0.40 < x \le 0.60\)0.7payments 25 to 36
\(x > 0.60\)0.25payments 37 to 60

At 60 months this reproduces the program’s actuarial buckets exactly (shape_at_60_matches_actuarial_buckets). At 36 months the hump runs from payment 2 to payment 9; at 120 months from payment 7 to payment 30. Normalizing to term fraction is spec v1.1 change 1 (Marc, 2026-07-10): the term is a parameter, so the shape travels with it.

The hazard is the shape times one scalar:

\[ h_t = \min(1, K, w_t), \qquad t = 1, \dots, n-1, \qquad h_n = 0 . \]

The reachable domain

No new hazard draw occurs after the final payment. An Agreement that pays at age n completes before step 4, so monthly_hazard sets h_n = 0 and lifetime calibration uses draw ages 1…n−1. A draw after payment n−1 can still cause the Buyer to miss payment n, with the sale and receipt occurring later. A conviction walk armed earlier or a rational-boundary decision can also consume the final date unpaid. SimConfig::check rejects a positive baseline lifetime target below two months because there is no eligible earlier draw age.

Calibrating K

\(K\) is chosen so that on a flat path, with no other mode on, the lifetime stop share equals the input exactly (defaults.rs::calibrate_k):

\[ 1 - \prod_{t=1}^{n-1} \bigl(1 - \min(1, K,w_t)\bigr) = \text{lifetime} . \]

The left side is increasing in \(K\), so bisection works, over the bracket \([0, 1/\max w]\): at the upper bound the heaviest bucket stops with certainty, so any target in \([0, 1)\) is reachable at any term with a reachable age. Eighty halvings fix \(K\). A target of zero short-circuits to \(K = 0\) (zero_lifetime_means_zero_hazard).

At the base terms and the 40% program prior:

Ages\(w_t\)\(h_t\) per month
1 to 31.00.977%
4 to 152.01.953%
16 to 241.21.172%
25 to 360.70.684%
37 to 590.250.244%
600

\(K = 0.009767\). The product over ages 1 to 59 gives a lifetime share of 0.400000; baseline_hits_lifetime_target_over_reachable_ages checks this within \(10^{-9}\) at terms 36 to 120 and targets 15% to 70%, and short_terms_reach_high_lifetime_targets checks that a two-month term, with a single reachable age, lands 70% exactly. The unconditional mass by year follows: 18.7% of the original book in year one, 12.4% in year two, 5.5% in year three, 1.8% in year four, 1.6% in year five. The simulate response carries the vector as hazard_monthly, and the cockpit’s “when they stop paying” exhibit charts it.

The FICO presets

Input #8 is a one-click load of the baseline with a band’s lifetime target (defaults.rs::DefaultScenario::fico_preset):

BandLifetime\(K\) at 60 monthsHump hazard
700 and above15%0.0031220.62%
600 to 69935%0.0082451.65%
500 to 59955%0.0152093.04%
below 50070%0.0228094.56%

The presets change nothing but the target. The program’s pricing stance is 40%, which sits between the 600 and 500 bands; the Model Card runs 40% as Panel A and 70% as Panel B.

Flat annual

DefaultScenario::FlatAnnual { annual_rate } has rate semantics, not lifetime semantics. The monthly hazard is the rate that compounds to the annual one:

\[ h = 1 - (1 - r_{\text{annual}})^{1/12}, \qquad h_t = h ;; (1 \le t < n), \qquad h_n = 0 . \]

A 10% annual rate gives \(h = 0.8742\%\) per month, and over the 59 reachable ages of a 60-month term a lifetime share of 40.4%, near the program prior by coincidence. flat_annual_compounds_over_defaultable_months checks a 13-month term: ages 1 to 12 carry the rate and the survival product is 0.90.

Custom per-year shares

DefaultScenario::CustomYearly { shares } (spec v1.4 change 5, Marc, 2026-07-12) lets a Holder state a timing view in the form the memo’s tables use: shares[y] is the unconditional share of the original book that stops in year \(y+1\). Mass semantics, so the shares sum to the lifetime share. defaults.rs::custom_yearly_hazard converts them to hazards in two moves.

First, each year’s mass spreads uniformly over its reachable ages. Year \(y\) covers ages \(12y+1\) to \(\min(12(y+1), n-1)\); the final payment date is excluded here for the same reason as above. Second, mass becomes hazard by survival division:

\[ h_t = \frac{m_t}{\prod_{k<t}(1 - h_k)} = \frac{m_t}{1 - \sum_{k<t} m_k} . \]

The denominator is the share of the book still on it when age \(t\) arrives, so on a flat path the realized year-by-year shares equal the inputs to the last digit (custom_yearly_realizes_shares_exactly, tolerance \(10^{-12}\)).

Take shares of 10%, 8%, 5%, 3%, 2% at 60 months. Year one carries \(0.10/12 = 0.8333\%\) of the book at each of ages 1 to 12; the hazard is 0.8333% at age 1 and rises to 0.9174% at age 12 as the survivors thin, then steps down to 0.7407% at age 13. Year five has eleven reachable ages (49 to 59), 0.1818% each, and the hazard at age 59 is 0.2519%.

Validation lives inside custom_yearly_hazard, so every consumer of the hazard sees the same rules. Each names input #7:

  • one share per year of the term, \(\lceil n/12 \rceil\) of them (custom_yearly_rejects_wrong_length);
  • every share finite and non-negative (custom_yearly_rejects_negative_share);
  • the sum at most \(1 - 10^{-9}\); a sum within a few ulps of one drives the survival denominator through zero mid-curve, and the headroom keeps every hazard finite and in \([0, 1]\) (custom_yearly_rejects_sum_at_or_above_one, custom_yearly_rejects_sum_within_ulps_of_one, custom_yearly_hazards_stay_in_range_near_the_cap);
  • a year with no reachable age must carry a zero share. At 37 months, year four contains only payment 37, the final date, so its share must be 0% (custom_yearly_rejects_mass_in_undefaultable_year).

The cockpit seeds this mode with three unbranded chips (the program hump, flat through time, late-loaded stress), not with named asset-class tables: published curves blend the life-event and collateral channels, and the collateral channel is what the next modes model.

Drawdown state multipliers

Input #23 (drawdown_hazard_multipliers, spec v1.4 change 10, from the program’s drawdown memo §09) is the graded middle ground between a price-blind draw and a rational robot. Each month, before the draw is compared to the hazard, the hazard is multiplied by a state read off this Buyer’s coin against this Buyer’s entry (engine.rs::drawdown_multiplier):

State of the coin against entryMultiplier
Above the entry price0.5
Drawdown up to 30%1.0
Drawdown over 30% to 50%1.5
Drawdown over 50% to 70%2.0
Drawdown over 70%3.0

\[ h_t^{\text{eff}} = \min\bigl(1, h_t \cdot m(S_t / \text{strike})\bigr) . \]

A coin exactly at entry has drawdown zero and sits in the ×1.0 bucket, so a flat path at entry with the toggle on reproduces the toggle-off run draw for draw (drawdown_multipliers_scale_defaults_by_state, 2,000 Agreements; it then checks that a rally to $240,000 cuts stops and a collapse to $12,000 raises them). Depth enters through the multiplier and duration through months spent in the state. There is no cliff: a 40% drawdown costs 1.5× the hazard for each month it persists, 2.93% instead of 1.95% a month at the hump, 0.37% instead of 0.24% in the tail. Realized lifetime shares become path-dependent by design; the flat path lands the target, deep crashes run above it.

The evidence is the mortgage double trigger (memo §09). Price alone rarely stops anyone: in the underwater-mortgage literature only about 6% of defaults were purely strategic, and the median walk-away carried negative equity of about 62%. What produces a stop is price pain coincident with a liquidity shock. The multipliers embed that correlation without modeling the shock: a Buyer whose coin has halved is not assumed to walk, but to be twice as likely to let a bad month become a missed payment. The ×0.5 above entry is the same logic in reverse, and under the September waterfall it matters more than it used to: an in-the-money stop hands the surplus above the Purchase Price to the Holder, and the archived Model Card (§6) names how often such Buyers stop as the least-evidenced behavior in the model. With the X-suite oracle running the same multipliers, the deterministic crash grid ties within ±0.3 points (spec v1.4 change 10).

The rational-default redirect

Input #9 (rational_default, spec §4.2) keeps the hazard and moves the stops. Step 4 draws each Agreement as before. If the draw fires and the Buyer is underwater, the stop sticks, tagged NonPerformanceRational. If the draw fires and the Buyer is above water, the engine builds the pool of active Agreements at reachable ages that are underwater this month and picks one uniformly, from the drawing Agreement’s own stream so the redirect too is fixed by the seed. If the pool is empty the draw is suppressed and counted (SimResult::suppressed_defaults); it never spills onto an in-the-money Buyer. That was Marc’s answer to spec open question 3 (v1.1, 2026-07-10): suppress, with a visible counter.

Underwater here means moneyness below one against the amortized obligation, not against the entry price and not against the nominal schedule (engine.rs::Agreement::underwater):

\[ S_t < B_t, \qquad B_t = \text{strike},(1+i)^t - \text{PMT},\frac{(1+i)^t - 1}{i} , \]

with \(i\) the implied monthly rate, solved once per Agreement and cached. The contract chapter derives \(B_t\) and says why it, rather than \(R_t\), is the boundary: a coin at $60,000 after twelve payments is above water against \(B_{12} = $51{,}581\), and against \(R_{12} = $70{,}800\) every Buyer on a flat path would be “underwater” for nineteen months.

The effect is the memo’s moneyness conditioning in one toggle: the identical lifetime curve concentrates into drawdown periods and underwater cohorts. On a strong rally nobody is underwater late in the term, so draws are suppressed and the realized share bends below the target (rational_mode_suppresses_when_nobody_is_underwater: a zero-vol bridge to $240,000 with 200 Agreements must show a positive suppression count and fewer stops than the same seed with the toggle off). With input #23 also on, the drawing Agreement’s hazard is scaled by its own state first, then the redirect looks for someone underwater.

The lost-conviction rule

Input #11 (ConvictionRule, spec §4.3) is the strategic cohort, deterministic and separate from the randomizer. The rule has two dials, \(X\) and \(Y\), default off and \(X = 50\%\), \(Y = 6\) when on. Each month at step 2b, for every active Agreement at a payment age, the engine tests the coin against the entry price:

\[ S_t < (1 - X),\text{strike} . \]

A month that breaches extends the streak; a month that does not resets it to zero. When the streak reaches \(Y\), a walk is armed for the next payment date; at that date the Buyer skips step 1 and the walk executes at step 2a with that date as the missed payment, tagged ConvictionWalk. Measuring \(X\) against the strike rather than the obligation was Marc’s answer to open question 2 (v1.1, 2026-07-10): the conviction rule is about the Buyer’s own entry; the obligation boundary belongs to the rational toggle.

At the base terms with the defaults on, a Buyer walks at the seventh payment date after six consecutive payment dates with the coin below $30,000. conviction_walks_fire_on_deep_drawdown runs a zero-vol bridge to $18,000 with no hazard and 50 Agreements; all 50 walk and none complete. The tornado’s “behavioral floor” is this rule at \(X = 0\), \(Y = 2\): every Buyer whose coin has sat below entry for two dates walks at the third. Scenario chip S4 (spec §8.1) is the ruthless version, \(X = 0\), \(Y = 1\), with the rational toggle on.

Early completion

Early completion (input #12, spec §4.4) is the up-side exit and the model cannot reproduce the memo without it. At step 3, for each active Agreement at a reachable age, the propensity is

\[ u_t = p \cdot \max!\Bigl(0, \frac{S_t - R_t}{S_t}\Bigr) , \]

with \(p\) the monthly propensity (default 2.5%) and \(R_t\) the remaining nominal schedule after this month’s payment. The Buyer completes if the uniform draw falls below \(u_t\). The factor is the Buyer’s equity in the coin as a share of the coin: nobody pays $70,800 in cash for a coin worth $60,000, and the propensity is zero there. After twelve payments with the coin at $100,000, \(u = 0.025 \times 0.292 = 0.73\%\) per month; after 36 payments at $120,000, 1.76%. engine.rs::settle then posts \(R_t\) from the Buyer to the Holder less the fee, and the Buyer takes the coin, cash-only by ruling (spec v1.5 change 3, Marc, 2026-09-03); the coin-sale make-whole of v1.4 no longer exists. Every completed-early Agreement has strictly positive coin equity, because \(u_t > 0\) requires \(S_t > R_t\) (settlements_deliver_remaining_schedule_on_upside).

Input #22, the take-profit gate (settlement_min_return, spec v1.4 change 6, Marc, 2026-07-12; engine.rs::validate accepts 0 to 1000%), is the behavioral mirror of the conviction rule. Ungated, the Buyer is coldly marginal: he compares the coin to what remains, sunk payments ignored. Gated at \(x\), he only completes once the coin beats his all-in cost by the margin:

\[ S_t \ge P,(1 + x) = \text{strike} \times \text{multiple} \times (1 + x) . \]

At the base terms and the cockpit’s \(x = 10\%\), that is $97,350. The gate applies before the propensity test. On a flat path at $60,000 the ungated rule fires late in the term, once \(R_t\) falls below the coin, and a 0% gate silences every one of them because the coin never beats $88,500 (take_profit_gate_blocks_settlements_below_all_in); on a run to $200,000 a 10% gate opens and a 1000% gate stays shut (take_profit_gate_opens_above_threshold). Default off. The propensity is a prior (archived Model Card §7) and early completion accelerates scheduled receipts but removes later stop exposure and possible surplus. A Holder isolating its effect sets the propensity to zero (spec §4.4). The Model Card runs 2.5%, ungated.

The rational boundary

Input #25 (rational_boundary: Option<BoundaryParams>, spec v1.6 change 6, Marc, 2026-09-03) is the fifth mode and the only one that carries no prior. It was lifted from the parked Behavior Engine and re-derived for the September stop. The question it answers: at each payment date and each price, what would a Buyer do who values the Agreement correctly under his own beliefs? The answer is a frontier, the spot below which walking is optimal, one number per payment date. boundary.rs::rational_frontier computes it by backward induction; the engine reads it as a rule.

The parameters (BoundaryParams, validated by BoundaryParams::validate naming input #25):

ParameterDefaultAcceptedMeaning
sigma_annual41.4%above 0, below 250%Volatility of the lattice, the trailing-24-month realized at the v1.1 refresh
mu_annual25%within ±200%The Buyer’s believed annual drift; zero is the pessimist who should never have signed
r_c_annual15%0 to below 100%The Buyer’s personal discount rate
walk_cost_of_strike2.5%0 to below 100%The cost of walking as a fraction of the coin’s cost, $1,500 at the base terms, about one payment

The walk cost lumps the six-month lockout, the re-strike at market and the loss of access into one number, and it is a fraction of the strike so that the frontier is scale-invariant (frontier_is_scale_invariant_in_the_strike checks $15,000 and $240,000 coins against the $60,000 one to \(10^{-6}\)).

The lattice

Time runs in price steps finer than decisions: SUB = 4 steps per month, \(\Delta = 1/48\) of a year, 240 steps over a 60-month term. Each step is a Cox-Ross-Rubinstein move in log-spot, \(u = e^{\sigma\sqrt{\Delta}}\), \(d = 1/u\); at the defaults \(\sigma\sqrt{\Delta} = 0.05976\), \(u = 1.0616\). The up-branch probability matches the believed drift, so the expected gross return per step is \(e^{\mu\Delta}\):

\[ q = \frac{e^{\mu\Delta} - d}{u - d} , \]

which is 0.5287 at \(\mu = 25\%\), 0.4851 at \(\mu = 0\), and must lie in \([0, 1]\); the code rejects \(|\mu|\sqrt{\Delta} > \sigma\) with a message to raise sigma or lower the drift. Continuation values discount at the Buyer’s rate, \(e^{-r_c \Delta} = 0.99688\) per step.

The grid is full width rather than a tree from a single root: node \(k \in [-K_{\max}, K_{\max}]\) has spot \(S_0 u^k\), with \(K_{\max} = 240 + 12\), 505 nodes. A recombining tree rooted at entry reaches only \(e^{-4\sigma\sqrt{\Delta}} = 0.787\) of entry at month one, so it could not see a coin that had halved at payment 1; the Phase 1 adversarial review (2026-09-03) found that such a Buyer kept paying until payment 4. The full grid sees the walk region from the first date (frontier_covers_every_payment_and_is_visible_from_payment_one), and rational_boundary_walks_the_book_in_a_crash_and_spares_it_in_a_rally pins the fix: a coin at 45% of entry at payment 1 walks at payment 1. At the outermost nodes the missing neighbor is the node itself.

The three payoffs

Controls apply at payment dates only, steps \(s = 4t\) for \(t = 1 \dots n\). At the date of payment \(t\), before paying, with \(A = \sum_{k<t} p_k\) the payments already made and \(R = P - A\) the remaining schedule with payment \(t\) included, the Buyer chooses the best of (boundary.rs::rational_frontier, closure decide):

\[ \text{walk} = \text{refund}(t, S) - c_{\text{walk}}, \qquad \text{refund} = \min\bigl(A, \max(0, S(1 - c_{\text{sale}}) + A - P)\bigr) , \]

\[ \text{settle} = S - R ;; (\text{only if } S > R), \]

\[ \text{pay} = -p_t + \mathbb{E}\bigl[e^{-r_c\Delta}, V_{\text{next step}}\bigr], \qquad \text{pay}_n = -p_n + S . \]

Walking is the September stop from the Buyer’s side: the coin is sold at spot less the sale cost, the refund is what the waterfall pays him, and the walk cost is subtracted. Settling is cash-only early completion: pay the rest, keep the coin. Paying buys the continuation; at the last date the coin is owned at once, the terminal condition. Between dates the value propagates, \(V = e^{-r_c\Delta}(q V^{+} + (1-q) V^{-})\).

The lattice’s settle uses \(R\) with payment \(t\) included, because the decision precedes the payment; the engine’s step 3 uses \(R_t\) after payment \(t\), because payments ran first. Same quantity, read one payment apart.

The frontier

At each payment date the code records, for every node, the gap between walking and the better of paying and settling. The frontier is the crossing between the highest node where the gap is positive and the next node up, interpolated on the gap in log-spot:

\[ S^\ast_t = S_k \cdot \exp\Bigl(\sigma\sqrt{\Delta} \cdot \frac{g_k}{g_k - g_{k+1}}\Bigr) , \]

clamped to the step. It is not the node itself; a node-valued frontier would jump by 6% at a time. None means no node on the grid walks at that date. FrontierRow carries the crossing in dollars (walk_below_spot), as a fraction of entry (walk_below_of_entry, the frame of the risk desk’s two lines), and in moneyness against \(B_{t-1}\) (walk_below_moneyness), plus \(B_{t-1}\) and \(R\) themselves.

Why one threshold

Walking beats settling only where the refund is zero. With \(V = S(1 - c_{\text{sale}})\), if the refund is positive it equals \(\min(A, V - R)\), and

\[ \text{settle} - \text{walk} = S - R - \text{refund} + c_{\text{walk}} \ge S - V + c_{\text{walk}} = S,c_{\text{sale}} + c_{\text{walk}} > 0 . \]

So above the schedule line the Buyer who wants out settles rather than walks. Where the refund is zero the walk payoff is the constant \(-c_{\text{walk}}\), while the pay payoff increases with spot through the continuation. A constant against an increasing function crosses once, so the walk region is a lower region and the frontier is a single threshold per date. walk_region_sits_where_the_refund_is_zero checks that every crossing lies at most one grid step above \(R / (1 - c_{\text{sale}})\), which at payment 12 is $72,456.

A function of belief

The frontier depends on what the Buyer believes about the coin, and the engine reports it for several beliefs rather than one. The risk endpoint (POST /api/forwardflow/risk, default frontier_mus of 0, 10%, 25% and 50% per year) returned the following at the base terms and default parameters, as a fraction of entry:

Payment\(\mu = 0\)\(\mu = 10\%\)\(\mu = 25\%\)\(\mu = 50\%\)
11.1621.0110.5210.152
61.0840.9520.5190.168
120.9840.8720.5050.187
240.7730.7060.4670.221
360.5430.5080.3850.233
480.2870.2760.2400.187
590.0240.0240.0240.023
60nonenonenonenone

Read the first column. Under zero believed drift the rational Buyer walks at 116% of entry at payment 1, and in moneyness against \(B\) the boundary stays at or above 1.0 through payment 46. A coin you do not expect to appreciate is not worth financing at 1.475× and a 15% discount rate, whatever the volatility; the lattice found this on 2026-08-06 and the new stop did not change it (pessimist_robot_walks_at_par). At \(\mu = 10\%\) the boundary sits within about a percent of the amortized obligation for two years, the Buyer who is indifferent to the deal. At the default \(\mu = 25\%\) the frontier is about half of entry through the first year and 0.24 of entry at payment 48. At \(\mu = 50\%\) it starts at 15% of entry. Every column meets at 2.4% of entry at payment 59: two payments remain, $2,950, walking costs $1,500, so the Buyer pays whenever the coin is worth more than about $1,450. At payment 60 paying always wins.

The tests fix the directions: higher sigma does not raise the frontier (higher_sigma_deepens_the_frontier, 30% against 60%), and neither does a higher walk cost or a stronger belief (walk_cost_and_belief_deepen_the_frontier). Every number in the other four modes is a prior about people; this one is arithmetic about the contract, given a belief. That is why the plan (§4, Marc, 2026-09-03) lifted it out of the parked Behavior Engine and left the thirty-seven-parameter machine on its branch.

In the engine

With input #25 on, engine.rs::run computes the frontier once per run from cohort-1 terms and keeps walk_below_of_entry per row; scale invariance makes that vector serve every Agreement, dispersed strikes included. At step 1, before payment \(t\), the Buyer reads frontier[t − 1] and compares:

\[ S_m < f_{t} \cdot \text{strike} ;\Rightarrow; \text{do not pay; arm the walk} . \]

The walk executes at step 2a in the same month with the exit tag RationalBoundary (rational_boundary in the exit split and the per-Agreement table), and the missed date is \(t\): the walk is the missed payment. The stop waterfall then runs as for any stop. The engine test runs a zero-vol collapse to $12,000 past the pessimist robot, with no hazard and no early completion; all 30 Buyers walk with exit month equal to payments made plus one, and the same book on a rally to $240,000 under \(\mu = 25\%\) completes in full.

The bracket a Holder should run

No single mode is BTC Now’s house view, because there is no vintage to make it one. The plan (§4), the spec (input #25) and the archived Model Card (§7) ask for a bracket instead, four readings of the same paths:

ReadingInputsWhat it assumes
Price-blind#7 aloneStops happen at the prior’s rate regardless of the coin
Drawdown-multiplied#7 + #23Stops correlate with drawdown through the graded multipliers, halved in the money
Rational robot#7 + #9, or S4 with #11 at \(X = 0, Y = 1\)The prior’s stops land only on underwater Buyers; nobody stops in the money
The frontier#25 at several \(\mu\)Buyers walk exactly when the contract is worth less than walking, under their belief

The first three differ mostly in how often an in-the-money Buyer stops, which under the September waterfall is the parameter that moves the upside: a stop above the Purchase Price hands the surplus to the Holder, and the replay medians move by ten points on a handful of such stops per vintage (archived Model Card §6). The frontier answers a different question, the floor under a fully informed Buyer with a stated belief, and the risk desk draws it beside the schedule line and the capital line. A Holder who runs all four has the band; the plan’s recommendation (§6) is to state the band and the best-estimate preset, and to say that the band is the answer. The six-month lockout after a stop (Marc, 2026-09-03) is not modelled (spec v1.5 change 8); it enters only through the walk cost of input #25.

Price paths and shocks

Each run follows a monthly Bitcoin price path from entry to the market horizon. That path affects stop-sale proceeds, early completion and the Holder’s exposure. This chapter explains the core generators, shock and sensitivity overlays, interpolation and seeded draws.

Implementation: paths.rs and engine.rs::run. Additional diffusion, jump, regime and upload models are detailed in Surfaces and hedging.

Prices are f64 throughout. They are model inputs, not ledger amounts, and become cents only at the moment a posting is written (a strike at origination, a sale at a stop). That rule is stated at the head of paths.rs and in the repository’s critical rules (“Decimal for money, f64 for boundaries/display”).

The shape of a path

A path has \(H + 1\) entries, indexed by month: path[0] is the start price (input #1, $60,000 at the base terms) and path[H] is the last month simulated. The horizon is set by the book, not by the path (engine.rs::SimConfig::horizon):

\[ H = \max\big(H_{\text{market}},\ (\text{cohorts} - 1) + \text{term} + \lfloor \text{lag}/30.4375 \rfloor + 1\big) . \]

Cohorts here are those that actually originate, capped by the origination window (input #18, engine.rs::SimConfig::effective_cohorts). The tail is the settlement allowance for the last stop (engine.rs::settlement_tail_months): a payment missed at age term is sold lag days later (input #24) and priced between the two marks around the sale date, so the ledger needs the mark past it — one month at the program’s 18 days (v1.5’s extra month), three at 90 days. Before 2026-09-05 the tail was one month at every lag, and a 90-day sale on a two-month Agreement posted in month 3 at the last mark’s price instead of month 5 at the interpolated one (the audit’s finding 6; finding_6_a_90_day_lag_settles_inside_the_ledger). \(H_{\text{market}}\) is the market horizon, market_horizon_months, unset by default: when the cockpit sets it, a pacing change (fewer cohorts, an origination window) no longer shortens the path and moves the bridge’s endpoint date — with it set, cutting 24 cohorts to 3 leaves the path bit-identical, where before month-one spot moved from $60,623.59 to $60,720.54 (finding 2; finding_2_the_market_horizon_pins_the_path_under_pacing_changes). It must be at least the pacing’s own requirement. At the base terms a single vintage has \(H = 61\); the paced book of 24 monthly cohorts has \(H = 84\), which is the 84-month horizon the Model Card’s production runs use.

The order of operations in engine.rs::run is fixed:

  1. Generate the path from the chosen mode (paths::generate), consuming the run’s random stream if the mode needs it.
  2. Apply the shock designer, if set (paths::apply_shock).
  3. If a bump is set with hold_strikes, keep a copy of the path as it stands; that copy is the strike path.
  4. Apply the bump, if set (paths::apply_bump).
  5. At each cohort month, derive the intramonth dispersion spread from the twelve months before it on the strike path if one was kept, otherwise on the final path (the stated volatility when fewer than three returns precede the month).

Each cohort’s Agreements then strike at strike_path[m] (or path[m] when no strike path was kept), and every later reading of the coin, from the monthly moneyness checks to the stop sale, is against the final path. A change that only touches months from \(m\) on — a shock at \(m\), a bump from \(m\), the origination window closing at \(m\) with the market horizon set — leaves every strike struck and every posting dated before \(m\) unchanged (finding_2_prefix_invariance_nothing_dated_before_a_change_moves).

The Brownian bridge

PathMode::Bridge { end_price, vol_annual } is the default mode and the one the cockpit’s two price fields drive (inputs #1, #2 and #2b). Start and end are pinned; the volatility fills the middle. Monte Carlo under this mode is many bridges between the same two points, so “what if the coin ends at $X” is a two-field question and the distribution of outcomes comes entirely from the route taken.

The construction is sequential and lives in paths.rs::bridge. Work in log space with \(x_k = \ln(P_k / P_0)\), so \(x_0 = 0\), and let the target be

\[ T = \ln\frac{P_{\text{end}}}{P_0}, \qquad \sigma_m = \frac{\sigma}{\sqrt{12}} . \]

At step \(k\) (producing month \(k+1\)) there are \(n = H - k\) steps remaining. The next increment is drawn conditional on the remaining distance to the endpoint:

\[ x_{k+1} = x_k + \frac{T - x_k}{n} + \sigma_m \sqrt{\frac{n - 1}{n}}; Z_k, \qquad Z_k \sim N(0, 1). \]

The mean of the step is the fraction of the remaining distance that one step out of \(n\) should cover; the variance is \(\sigma_m^2 (n-1)/n\), which is the variance of one step of a Brownian bridge with \(n\) steps to go. At the last step \(n = 1\), the variance is zero and the mean is exactly the remaining gap, so the endpoint is hit by construction. The code then overwrites path[H] with end_price to remove floating-point residue (a debug_assert checks the residue was below \(10^{-9}\) relative). Prices are \(P_k = P_0 e^{x_k}\).

This is the sequential conditioning the note at the head of paths.rs::bridge insists on: the steps are never independent samples forced to the endpoint afterwards, which would give the wrong interior variance. The conditional recursion gives the bridge law without any rescaling.

Arithmetic at the default volatility of 43%: \(\sigma_m = 0.43 / \sqrt{12} = 0.12413\), so a free month would move the log price by 12.4% one standard deviation. On the 84-month horizon the first step’s standard deviation is \(0.12413 \sqrt{83/84} = 0.12339\); with two steps to go it is \(0.12413 \sqrt{1/2} = 0.08777\); the final step is deterministic. Pinning the endpoint takes only a little variance out of the early months and all of it out of the last one.

Vol 0 is the log-linear ramp. With \(\sigma = 0\) every increment is exactly \((T - x_k)/n\), which is constant, so the path is a straight line in log space from start to end. Start = end gives a flat path at $60,000 for every month, which is why the contract-math fixtures use Bridge { end_price: 60_000, vol_annual: 0.0 }: no price movement, nothing behavioral fires, and the ledger shows the pure schedule. The test zero_vol_bridge_is_deterministic_ramp checks that two different seeds produce the same flat path. A ramp from $60,000 to $90,000 over 84 months has a monthly log step of \(\ln 1.5 / 84 = 0.004827\), about 0.48% a month, 5.8% a year continuously compounded, with no noise at all.

A caution the cockpit states next to the volatility field: 43% is the recent coin, and the bootstrap’s default regime (2017 onward) ran near 70%. The two modes’ defaults embed different Bitcoins.

Historical replay

PathMode::HistoricalReplay { start_index } replays the embedded monthly closes from a chosen bar, rebased so the path starts at the configured start price (paths.rs::replay):

\[ P_k = P_0 \cdot \frac{C_{s+k}}{C_s}, \qquad k = 0, \dots, H, \]

where \(C\) is the chronological close series and \(s\) is the start index. The shape of the path is Bitcoin’s actual history from that month; only the level is rescaled. Replay includes whatever drift history had, so the Model Card calls its replay rows descriptive rather than probabilistic.

Feasibility. The run needs \(H + 1\) bars from index \(s\), so the condition is \(s + H < N\) with \(N\) the number of bars. Otherwise generate returns PathError::InsufficientHistory { start, needed, available }, which the API maps to HTTP 400 rather than a crash. With 174 bars and the paced book’s \(H = 84\), the feasible starts are indices 0 through 89, ninety of them, from February 2012 to July 2019; that is the “90 feasible historical start months” of the Model Card’s paced-replay construction, and why its starts end mid-2019. A single vintage at \(H = 61\) has 113 feasible starts, the Model Card’s single-vintage replay row. The GET /api/forwardflow/history endpoint returns the months and closes so the cockpit can disable start dates that would not fit.

The vintage backtest (POST /api/forwardflow/backtest, see The API) is this mode run once per feasible start with the rest of the configuration held fixed.

The embedded data

The series is backend/data/btc_historical_monthly.csv, compiled into the crate with include_str! and parsed once into a OnceLock (paths.rs::historical_closes, historical_months). There are no data files at runtime. The file follows the investing.com export convention: newest first, quoted fields, thousands separators inside the quotes, MM/DD/YYYY dates, and the columns Date, Price, Open, High, Low, Vol., Change %. The engine reads only Date and Price, reverses the rows into chronological order, and checks in debug builds that the months ascend. The first data line of the file is the bar for July 2026 at a close of $62,875.5; the last line is February 2012 at $4.9.

Parsed, that is 174 monthly bars, index 0 = February 2012 through index 173 = July 2026, hence 173 monthly log returns. Two indices recur in the examples and the cockpit: index_of_month(2017, 1) is 59, the “modern regime” default of the bootstrap picker, and index_of_month(2024, 7) is 149, the start of the trailing-24-month window the Model Card prices on. index_of_month returns the first bar at or after the requested month; a month before the series clamps to index 0, and a month after the series returns None so that a caller can never silently fall back to 2012 (the test index_of_month_rejects_future_dates).

The zero-drift block bootstrap

PathMode::ZeroDriftBootstrap { block_len, regime_start_index } is the memorandum’s pricing stance and the mode the Model Card’s production figures are run on (paths.rs::bootstrap).

Take the closes from the regime start onward, form their log returns, and remove the mean:

\[ r_i = \ln\frac{C_{i+1}}{C_i}, \qquad \bar r = \frac{1}{n}\sum_i r_i, \qquad \tilde r_i = r_i - \bar r . \]

Then build the path by concatenating blocks of consecutive de-meaned returns, drawn circularly (model audit 2026-09-06, M03): each block start \(s\) is drawn uniformly from all \(n\) positions \(0 \dots n-1\), the \(L\) returns \(\tilde r_{s \bmod n} \dots \tilde r_{(s+L-1) \bmod n}\) are appended in order — a block that starts near the window’s end wraps to its beginning — and the loop repeats until \(H\) returns have been used; the last block is cut where the horizon lands. Prices are \(P_k = P_0 \exp(\sum_{j<k} \tilde r_j)\). Under this rule every observation has weight exactly \(1/n\) at every position of every block, the cut block included, so the expected log return of every month is the de-meaned mean, zero. Zero drift means zero expected log return of the path under the sampler’s own weights — and nothing more: the median and the mean of the price factor are those of the empirical block distribution, not a lognormal’s, so no exact Gaussian mean/median relation holds. On the 24 de-meaned returns of the pricing window the median monthly log step is −0.46%, \(\log \mathbb{E}[e^{\text{step}}]\) is +0.69% and half the population log variance is +0.685%: the median price drifts slightly down and the mean up (model audit 2026-09-07, bootstrap-interpretation.json). (The earlier draw took starts from \(0 \dots n-L\) without wrapping, which weighted interior returns more than the edges while the de-meaning weighted them equally, and left an exact log drift of +1.24%, −1.73% and −3.67% a year on the three windows below; tests/model_audit_2026_09_06.rs enumerates the sampler’s weights and samples 4,000 five-year paths per window.) The regime must supply at least \(L\) returns; otherwise PathError::RegimeTooShort is returned, and the cockpit clamps the picker.

Why de-meaned. The bootstrap removes the selected window’s average log return so the Holder can test Buyer behavior without carrying that historical log-growth assumption forward. It retains the observed deviations, including large moves and the ordering within each block. This means zero expected log return, not zero expected price appreciation: converting log returns back to prices can still produce a rising arithmetic mean price. Volatility is not an input in this mode; it comes from the chosen historical window. The resulting paths are conditional scenarios, not forecasts.

The block length. The default is 6. A block of six consecutive months preserves half a year of the actual sequence, which is where drawdown streaks and the recoveries after them live; drawing single months would destroy that clustering. The bootstrap has no pinned endpoint, so unlike the bridge its terminal price is free, and the Monte Carlo distribution reflects both the route and the destination.

The regimes in use. The three windows that appear in the Model Card and the cockpit, with their realized annualized volatility computed from the population variance of the window’s log returns (the definition in exposure.rs::realized_vol, below), and in brackets the sample-variance figure the Model Card prints (examples/w0108_refresh.rs divides by \(n - 1\)):

RegimeStart indexLog returnsMean per monthRealized vol, population (sample)
Full history, Feb 20120173+5.5%86.5% (86.8%)
Modern regime, Jan 201759114+3.7%69.1% (69.4%)
Trailing 24 months, Jul 202414924−0.1%40.6% (41.4%)

The trailing window is the production pricing regime (Model Card §4): 24 returns, which asserts that current volatility persists. The full-history window is the printed stress. The “modern regime” from 2017 is the cockpit’s default when the bootstrap is selected, chosen to drop the early hundred-fold years without dropping the institutional-era volatility. The mean column is what de-meaning removes.

The custom path

PathMode::Custom { points } (v1.4, spec input #2c+) is a deterministic, seed-free path through anchor points. Each anchor is (month, ratio), the price at that month as a multiple of the start price. The origin \((0, 1.0)\) is implicit. Between anchors the path is linear in log space; after the last anchor it is flat (paths.rs::custom):

\[ \ln\frac{P_m}{P_0} = \ell_i + (\ell_{i+1} - \ell_i),\frac{m - m_i}{m_{i+1} - m_i}, \quad m_i \le m \le m_{i+1}, \qquad \ell_i = \ln \rho_i , \]

and \(P_m = P_0 \rho_{\text{last}}\) for \(m\) past the last anchor. Validation: at least one anchor, months strictly increasing and starting after month 0, ratios positive and finite; a violation panics with the input number, which the API turns into a 400.

The test custom_hits_anchors_interpolates_log_linear_and_extends_flat is the worked example. Anchors \((6, 0.40), (18, 0.80), (30, 0.40)\) on a $60,000 start give $24,000 at month 6, $48,000 at month 18, $24,000 at month 30, and $24,000 at every month after. Month 12, halfway between the first two anchors, is the geometric mean: \(60{,}000 \sqrt{0.40 \times 0.80} = $33{,}941.13\), not the arithmetic $36,000. Log-linear interpolation means a constant monthly rate of change between anchors, which is what a “fell 60% over six months” instruction means.

The mode exists so that mid-path reversals (double dips, whipsaws, a rally into a crash) can be written as data literals. It is exposed in the engine, the API and the TypeScript types; the cockpit has no picker for it.

The shock designer

The shock (spec input #17, paths::Shock) is a multiplicative overlay applied after generation, so it composes with any path mode. Its four fields are the start month \(X\), the drop \(Z \in (0, 1)\), the duration \(W \ge 1\), and an optional recovery level \(R\). Write \(F = 1 - Z\) for the floor and \(H\) for the last month. The factor multiplying path[m] is (paths.rs::apply_shock):

\[ f(m) = \begin{cases} 1 & m < X \\ F^{,(m - X + 1)/W} & X \le m \le X + W - 1 \\ F & m > X + W - 1, ; \text{permanent} \\ \exp!\Big(\ln F + (\ln R - \ln F),\dfrac{m - X - W + 1}{H - (X + W - 1)}\Big) & m > X + W - 1, ; \text{recovering} \end{cases} \]

The ramp is log-linear and reaches exactly \(F\) at the trough month \(X + W - 1\) (clamped to \(H\) if the ramp would run off the end). With no recovery the factor stays at \(F\) for good. With a recovery, the factor moves log-linearly from \(F\) at the trough to \(R\) at month \(H\); \(R = 1\) is a full round trip back onto the unshocked path, \(R = 0.8\) ends 20% below it. Validation (engine.rs::validate): \(0 < Z < 1\), \(W \ge 1\), \(R > 0\) and finite.

Worked example from the test shock_designer_overlays_any_path: a flat $60,000 path with \(X = 6\), \(Z = 0.5\), \(W = 3\), permanent. Months 0 to 5 are untouched. Month 6 is \(60{,}000 \times 0.5^{1/3} = $47{,}622\), month 7 is \(60{,}000 \times 0.5^{2/3} = $37{,}798\), month 8 is exactly $30,000, and every later month is $30,000. With \(R = 1\) on a single-vintage run (\(H = 61\)) the recovery spans \(61 - 8 = 53\) months; at month 35, 27 months into the recovery, the factor is \(0.5^{,1 - 27/53} = 0.712\), about $42,700, and month 61 is back at $60,000.

A larger shock at the base terms: \(X = 12\), \(Z = 0.70\), \(W = 6\) on the paced book. The floor is 0.30; the ramp reads 0.818, 0.669, 0.548, 0.448, 0.367, 0.300 at months 12 through 17, and the coin sits at 30% of its unshocked level from month 17 onward. The tornado’s “BTC −70%/3mo permanent, flow continues” row is the faster version, \(X = 1\), \(Z = 0.70\), \(W = 3\): the ramp reads 0.669, 0.448, 0.300 at months 1 through 3. The test crash_with_flow_continuing_beats_single_strike runs that shock on the default bridge (43% volatility, seed 9, rational mode on): a book that keeps originating through the crater, with later cohorts striking low, beats a single vintage struck at the top.

The shock is the cleaner crash instrument because it leaves the underlying path mode alone. Moving the bridge’s endpoint changes every month; the shock changes only the months from \(X\) on, and by a stated amount.

The bump overlay

The bump (v1.6, paths::PathBump) is the second overlay, applied after the shock and before the run. It has four fields: from_month, price_factor, vol_factor and hold_strikes. It exists for the risk desk’s Greeks, which are computed by bump-and-revalue, and the desk can also set it directly as a what-if. Validation: the price factor within \((0.1, 10)\), the vol factor within \([0, 5)\).

Let \(a = \max(m_{\text{from}}, 1)\) with \(m_{\text{from}}\) the from_month; month 0 is never bumped. Two operations, in this order (paths.rs::apply_bump):

Vol factor. Take the log returns of the future months, \(r_m = \ln(P_m / P_{m-1})\) for \(m = a \dots H\), and their mean \(\bar r\). Rebuild the future path from \(P_{a-1}\) with the deviations around the mean scaled by \(v\):

\[ r’m = \bar r + v,(r_m - \bar r), \qquad P’m = P{a-1} \exp!\Big(\sum{j=a}^{m} r’_j\Big). \]

The mean log return of the future segment is unchanged, so the drift is kept, and the realized volatility of the segment scales by exactly \(v\). At \(v = 0\) every future return equals \(\bar r\) and the path becomes a ramp (the test bump_overlay_scales_prices_and_vol checks the returns are constant). On a flat path every deviation is zero, so the vol factor is a no-op and the risk desk reports vega as exactly zero. The same continuation-scaling method applies to a bridge.

Price factor. Multiply every price from month \(a\) on by \(p\). A factor of 1.10 from month 1 leaves month 0 at $60,000 and lifts every later month by 10% (the same test).

Strikes held or floating. With hold_strikes = true the engine strikes every cohort off the path as it stood before the bump, so the existing book’s terms do not move and only the coin does (the test bump_overlay_scales_prices_and_vol checks the held strikes equal the unbumped run’s). With hold_strikes = false cohorts that originate in bumped months strike at the bumped prices. The intramonth dispersion spread (below) is derived from the strike path when strikes are held, so a bump does not change the spread either. The Greeks never set hold_strikes: exposure.rs::greeks runs every bump with hold_strikes: false and gets the existing book by stopping origination at the bump month (origination_stop_month), so no cohort strikes on a bumped price, which is the “strikes held” of spec v1.6 by another route. The commitment delta is the same bump on the full pacing, where later cohorts do strike at the bumped prices. On a book with no stops the existing book’s delta is zero: the schedule is fixed in dollars and only a stop sale reads the price.

How the desk uses it (exposure.rs::greeks): delta and gamma from price factors \(1 \pm 0.05\) starting the month after the ladder month; vega from a vol bump of ±5 points (the API defaults, price_bump and vol_bump 0.05), which on every path mode is translated into a vol factor \(v = (\sigma_{\text{real}} \pm 0.05)/\sigma_{\text{real}}\) against the base path’s realized volatility (labelled in the report as path-amplitude scaling). The Greeks themselves are the subject of The risk desk. The risk endpoint refuses to compute Greeks while a what-if bump is set in the config, so the two uses do not stack.

Reading prices between the marks

The path has one price per month, but two places in the engine need a price at a finer resolution.

The stop sale. A stop is recorded 18 days after the missed payment date by default (input #24, stop_sale_lag_days; day 16 is the Stop Date and the sale follows within two business days, Marc, 2026-09-03, spec v1.5). The sale price is the log-linear interpolation between the monthly marks (engine.rs::stop_sale):

\[ \text{pos} = m_{\text{missed}} + \frac{\text{lag}}{30.4375}, \qquad k = \lfloor \text{pos} \rfloor, \quad f = \text{pos} - k, \qquad P_{\text{sale}} = \exp\big((1 - f)\ln P_k + f \ln P_{k+1}\big). \]

At 18 days, \(f = 18 / 30.4375 = 0.5914\), so the sale price sits 59% of the way in log space from the missed month’s mark to the next one; at lag 0 it is the missed month’s mark, at about 30 days the next mark. The postings land at the first payment date at or after the sale, \(\lceil\text{pos}\rceil\), which at 18 days is the month after the missed one. The proceeds are the sale price less the haircut (input #15, default 0) and the 25 bp sale cost; what happens to them is the waterfall of The stop waterfall. The Model Card notes that at monthly resolution the difference between an 18-day and a 30-day sale is a fraction of one month’s move.

Intramonth strike dispersion (input #20, default off in the engine’s SimConfig::default; the cockpit’s base configuration turns it on, Marc, 2026-07-13, spec v1.4). Originations happen throughout a month, not at its close. With the toggle on, each Agreement in a cohort draws its own strike around the cohort month’s price. The spread is derived from the path’s own past so that it fits any mode, uses only what is known at entry, and is exactly zero on a flat path (engine.rs::entry_sigma_monthly): with \(\sigma_m\) the population standard deviation of the path’s monthly log returns over the twelve months before the cohort month (the mode’s stated volatility over \(\sqrt{12}\) when fewer than three returns precede it),

\[ \sigma_{\text{intra}} = \frac{\sigma_m}{\sqrt 2}, \qquad K = P_m \exp!\big(\sigma_{\text{intra}} Z - \tfrac12 \sigma_{\text{intra}}^2\big), \quad Z \sim N(0,1). \]

The \(1/\sqrt 2\) is the standard deviation of a price observed at a uniformly random time inside the month relative to its end, and the half-variance term makes the draw mean-preserving, so the expected strike equals the monthly mark. On a path whose realized volatility is 41.4%, \(\sigma_m = 0.1195\) and \(\sigma_{\text{intra}} = 0.0845\), so one standard deviation of entry price is about 8.5% around the month’s mark. The purchase price is the strike times input #6b (par at the base terms), so it disperses with it. The test intramonth_dispersion_smears_strikes_and_preserves_the_mean checks the spread and the mean; dispersion_is_inert_on_a_flat_path checks that toggling it on a flat path changes nothing to the cent. The purpose is to smear threshold behavior (moneyness, the conviction rule, coverage) across a cohort instead of firing it for ten identical twins at once (Marc, 2026-07-12, spec v1.4).

The seed, and what it drives

The engine is fully seeded (ChaCha20) and the same configuration gives byte-identical output; the test seeded_determinism runs one configuration twice and compares. Since v1.6 the seed feeds two kinds of stream, and the distinction is what makes the bump a sensitivity rather than noise.

The run stream is ChaCha20Rng::seed_from_u64(config.seed) (engine.rs::run). It is consumed by path generation only: the bridge’s normals and the bootstrap’s block starts. Replay and the custom path do not touch it, and the shock and the bump are deterministic overlays. So the seed drives the path, and only the path, at the book level.

The Agreement streams. Every Agreement gets its own ChaCha20Rng, keyed from (config.seed, agreement id) through a splitmix64 expansion (engine.rs::agreement_rng). The Agreement’s strike draw under dispersion, its stop draws at every payment date, its early-completion draws and, in the rational mode, its redirect pick all come from that stream, and the draws are made unconditionally at each date whether or not they are used. Consequently a bumped price, a shock, or a different exit elsewhere in the book changes decisions but never the random numbers behind them. Two runs on the same seed that differ only in the bump produce the same draws Agreement by Agreement, and the difference in the Holder’s outcome is attributable to the price alone. That is what bump-and-revalue needs (test greeks_are_sensitivities_not_draw_noise).

Monte Carlo and ensembles. Run \(i\) of a Monte Carlo of \(n\) runs uses seed \(\text{base} + i\) (outputs.rs::run_monte_carlo, seeds base_seed..base_seed+n); the API’s streaming endpoint runs chunks with a seed offset the same way (forwardflow_api.rs::run_chunk), and identical requests are memoized because the result is a pure function of the request. The Greeks average over greek_seeds seeds (default 16, at most 256) starting at the config’s seed. A change of seed therefore changes the path and every Agreement’s draws together, which is the intended meaning of “another world”; a change of anything else on the same seed holds the world fixed.

The Model Card’s figures are stamped at seed 42, and it records that the v1.6 move to per-Agreement streams changed what seed 42 draws relative to the v1.5 run of the same morning: the zero-drift medians moved by a tenth of a point, the single-vintage replay median by ten points. Same seed, different draw assignment; the seed is reproducibility, not a pinned outcome across engine versions.

Realized volatility of a path

The engine’s own definition, used for the intramonth spread and for translating a vega bump into a vol factor, is the population standard deviation of the monthly log returns, annualized by \(\sqrt{12}\) (exposure.rs::realized_vol):

\[ \sigma_{\text{real}} = \sqrt{12};\sqrt{\frac{1}{n}\sum_{i=1}^{n}(r_i - \bar r)^2}, \qquad r_i = \ln\frac{P_i}{P_{i-1}}, \]

and zero for a path shorter than three prices. Note the two conventions in the repository. The engine divides by \(n\); the examples that print the Model Card’s regime figures (examples/w0108_refresh.rs, examples/bridge_print_set.rs) divide by \(n - 1\). On the trailing 24-month window the two read 40.6% and 41.4%; on the full history 86.5% and 86.8%. The Model Card quotes the sample figures. Neither convention enters a ledger posting; the difference matters only when a reader tries to reproduce a printed number.

A bridge at 43% does not realize 43%: the pinning removes variance, most of it in the last months, so the realized figure on one bridge is a little below the input and varies by seed. The bootstrap realizes whatever its window’s de-meaned returns happen to give under the blocks drawn. A replay realizes history’s figure for that window. A custom path or a ramp realizes the volatility of its own kinks, which for a pure ramp is zero.

Choosing a mode

ModeRandomnessEndpointDriftWhat it answers
BridgeSeeded normalsPinnedSet by the endpoints“If the coin ends at $X, what do the routes there do to the paper?”
Historical replayNoneFreeHistory’s“What would this book have done from month M?”
Zero-drift bootstrapSeeded block startsFreeZero by constructionThe pricing stance: current volatility, no appreciation
CustomNoneFreeAs drawnA stated reversal shape as a data literal
Shock (overlay)NoneScaledAdds a crashCrash timing and depth on top of any of the above
Bump (overlay)NoneScaledKept (vol factor) or shifted (price factor)The Greeks; a what-if from a month on

GBM, jump diffusion, regime switching, uploaded monthly paths and the volatility surface are implemented and documented in Surfaces and hedging. Their modelling limits are in Limits and assumptions. The inputs that select and parameterize each mode are listed with their numbers in Every input.

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.

Risk and sensitivities

Locate the exposure before choosing a hedge. The risk analysis shows schedule and capital lines, coverage, exposure by age and moneyness, credit-loss measures and sensitivities to the modelled path. It also compares rational walk-away boundaries under different Buyer beliefs.

Implementation: exposure.rs, boundary.rs and forwardflow_api.rs::ff_risk; reference tests: tests/exposure.rs. Open Risk analysis to run a scenario.

The worked snapshots below were recorded in early September 2026; they are not refreshed 0.6.1 results or current GBM workspace defaults. Their numbers use the base terms: a $60,000 coin, 1.475×, 60 monthly payments of $1,475 (Purchase Price $88,500), the first payment to BTC Now, 5% servicing on every dollar delivered to the Holder, the Holder at par, the stop sale 18 days after the missed payment, 25 bp sale cost. Where a number needs a whole book, it comes from the cockpit’s base configuration: 24 monthly cohorts of 10 Agreements, a 60,000 → 60,000 bridge at 43% volatility, a 40% lifetime stop prior, 2.5% monthly early-completion propensity, dispersed strikes, seed 42. Every figure was printed by POST /api/forwardflow/risk on that configuration, asserted by a test, or is one line of arithmetic from one that was.

What the desk draws

The plan (§1) writes the paper as four legs: a fixed-dollar amortizing annuity; a short put on the coin struck at the remaining schedule, exercised only when the Buyer stops; a long call struck at the Purchase Price, exercised only when the Buyer stops with the coin worth more than the Purchase Price; and the Buyer’s early completion option. Five exhibits put numbers and signs on legs two and three, and the last section maps the legs back onto them.

The two lines

Both lines are sale proceeds as a fraction of the coin’s entry price, indexed by \(t\), the number of payments made. Both fall every month. exposure.rs::two_lines builds them once from cohort-1 terms; they are scale-invariant in the strike, so one table serves a book of dispersed strikes.

The schedule line

Above the schedule line a stop sale delivers the whole remaining schedule and the Holder loses only the future yield on that Agreement. It is the remaining nominal schedule over the strike:

\[ \ell^{S}t = \frac{R_t}{\text{strike}}, \qquad R_t = \sum{k=t+1}^{n} p_k , \]

with \(R_t\) from contract.rs::ContractTerms::schedule summed from \(t+1\). At the base terms \(R_t = 88{,}500 - 1{,}475,t\): 1.475 before any payment, 1.450 after payment 1, zero at payment 60. It is also the strike of the embedded put: the waterfall pays the Holder \(\min(V, R_t)\) first (the stop waterfall), so the Holder’s shortfall on a stop is \(\max(0, R_t - V)\), a put on the proceeds struck at \(R_t\).

The capital line, from the waterfall

The capital line is the sale proceeds that return the Holder’s own money, counting what it has already been paid. Let \(U_t\) be the unrecovered capital after \(t\) payments,

\[ U_t = \max!\Big(0,\ \text{purchase} - \sum_{k=N+1}^{t} p_k,(1-f)\Big), \]

where \(N\) is the number of first payments routed to BTC Now and \(f\) the servicing fee. At the base terms \(U_0 = U_1 = 60{,}000\) (payment 1 never reaches the Holder) and \(U_t = 60{,}000 - 1{,}401.25,(t-1)\) after that, reaching zero at \(t = 44\): by then the Holder has received \(43 \times 1{,}401.25 = $60{,}253.75\) net.

The proceeds that recover \(U_t\) are read off the waterfall, not off a discount. With \(A_t = P - R_t\) the Buyer’s paid-in total and \(V\) the sale proceeds, the waterfall delivers the Holder \(V\) while \(V \le R_t\) (the refund is zero), exactly \(R_t\) while \(R_t < V \le P\) (everything above the schedule refunds the Buyer, up to \(A_t\)), and \(V - A_t\) above \(P\) (the refund is capped, the surplus is the Holder’s). The Holder nets \((1-f)\) of what is delivered. So exposure.rs::capital_proceeds_needed solves \(\text{delivered}(V),(1-f) = U_t\) piecewise:

\[ V^{\text{cap}}_t = \begin{cases} \dfrac{U_t}{1-f} & \text{if } \dfrac{U_t}{1-f} \le R_t \\ A_t + \dfrac{U_t}{1-f} & \text{otherwise,} \end{cases} \qquad \ell^{C}_t = \frac{V^{\text{cap}}_t}{\text{strike}} . \]

The second branch exists because between \(R_t\) and \(P\) the Holder cannot be delivered more than \(R_t\); if \(R_t(1-f)\) is short of \(U_t\), capital is recoverable only from the surplus above the Purchase Price, and the row is flagged capital_above_purchase_price. At par the first branch holds at every \(t\): \(U_1/(1-f) = 63{,}157.89 < R_1 = 87{,}025\), and the ratio only improves. capital_line_is_piecewise_when_it_would_cross_the_schedule_line builds a Holder at 160% of the coin’s cost, where \(96{,}000/0.95 > R_1\), and checks the line jumps to \(1{,}475 + 96{,}000/0.95\) dollars with the flag set.

The table at the base terms

two_lines at the base terms, which the test two_lines_match_the_plan_table pins against the plan’s own arithmetic, \(\ell^S_t = (88{,}500 - 1{,}475,t)/60{,}000\) and \(\ell^C_t = \max(0,\ 60{,}000 - 1{,}401.25,(t-1))/0.95/60{,}000\):

Payments made \(t\)Schedule lineCapital line\(R_t\)\(U_t\)
01.4751.053$88,500$60,000
11.4501.053$87,025$60,000
61.3280.930$79,650$52,993.75
121.1800.782$70,800$44,586.25
191.0080.610$60,475$34,777.50
240.8850.487$53,100$27,771.25
360.5900.192$35,400$10,956.25
440.3930.000$23,600$0
600.0000.000$0$0

The plan prints the same rows as 1.45/1.05, 1.33/0.93, 1.18/0.78, 1.00/0.63, 0.89/0.49, 0.59/0.19, 0.39/0.00. Six of the seven agree to the plan’s rounding; at month 19 the plan’s own formula gives 0.610, so 0.63 is a slip in the plan’s table, not a different definition. The schedule line crosses 1.0 between payments 19 and 20 (\(t = 19.32\)); the cockpit reports the first row at or below 1.0, month 20. The 25 bp sale cost is not in the lines; it enters through the proceeds when the lines meet a path.

Two things a desk reads first. The capital line starts above the entry price: a stop at any coin price below 105.3% of entry in the first month loses the Holder money, because payment 1 went to BTC Now and every delivered dollar carries the 5%. And it reaches zero at month 44: after that no stop, at any price, can lose the Holder capital on that Agreement.

Why the loss story lives in the first two years

Between the two lines a stop leaves a shortfall against the schedule but still returns the Holder’s capital. Below the capital line it does not. At month 24 the capital line sits at 0.487 of entry, inside a normal Bitcoin drawdown; at month 36 it is 0.192, which is not. So the window in which a par Holder can lose capital is the first two years of each vintage, and it narrows every month. Coverage on the base path says the same in Agreement-months: the count below the capital line peaks at 127 in month 22 and is zero from month 30, while the count below the schedule line is still 62 at month 30 and first reaches zero at month 40. The plan adds that every breach of the loss line in the memo’s historical replays came before month 18; that sentence is the plan’s, and this chapter’s evidence is the engine’s.

Coverage on the simulated path

Coverage applies the two lines to the run’s path, month by month. exposure.rs::exposure first reconstructs each Agreement’s monthly state from the ledger (exposure.rs::tracks): origination month, exit month, and the months in which a payment posted (OriginationFee or PaymentDelivery), so AgreementTrack::payments_made_by(m) is exact even when a walk consumed a payment date unpaid.

Per month

For each calendar month \(m\) the proceeds a stop sale at that month’s mark would record are priced as the engine prices them, exposure.rs::stop_proceeds:

\[ V_m = S_m, e^{-\text{haircut}},(1 - \text{sale cost}) , \]

the same function as engine.rs::stop_sale without the 18-day interpolation: coverage asks what a sale at this month’s mark would do, not when the actual sale happened. Then over every Agreement active at \(m\), with \(t\) its payments made by \(m\):

CoveragePoint fieldDefinition
activeAgreements active at \(m\)
below_scheduleThose with \(V_m < R_t\): a stop would leave a shortfall
below_capitalThose with \(V_m < V^{\text{cap}}_t\): a stop would lose the Holder’s capital
notional_usd\(\sum R_t\), the embedded put ladder’s notional
capital_at_risk_usd\(\sum U_t\), the dollars that can actually be lost
intrinsic_shortfall_usd\(\sum_{\text{below schedule}} (R_t - V_m)\), the book’s intrinsic loss if every one of them stopped now

A check at month 0: the mark is $60,000, so \(V_0 = 59{,}850\). Ten Agreements of cohort 0 are active with no payment made, so all ten sit below the schedule line (\(R_0 = 1.475 \times \text{strike}\)). Eight sit below the capital line: with dispersed strikes the capital line is \(\text{strike}/0.95\), which $59,850 clears only for a strike below $56,857.50, and two of the ten drew below that. The endpoint prints 10 / 10 / 8.

The base bridge dips to $28,581 at month 11. At month 12 the book has 123 active Agreements, 123 below the schedule line and 117 below the capital line, capital at risk $4.54m, intrinsic shortfall $3.18m. The largest intrinsic shortfall is $3.73m at month 21 (mark $28,792, 188 of 188 active below the schedule line). Peak capital at risk is $5.94m at month 23, the last origination month: 203 active, 200 below the schedule line, 63 below the capital line.

One lifecycle, one timing rule

Every consumer reads the same state of an Agreement at a month — coverage, capital at risk and the ladder here, the hedge desk’s on-book test (hedge.rs::on_book_at) and the monthly mark (series.rs) — from engine.rs::state_at: Performing (on the book and paying; a draw at age \(t\) marks the exit at \(t+1\) but the paper performs until the missed date), StoppedAwaitingSale { missed, sale_month } (the missed date has passed and the coin is still held for the sale: price exposure), SoldAwaitingCash { landed_month } (the month the sale’s cash lands: a settlement receivable, no price exposure), and Closed. The timing is engine.rs::stop_sale_timing, the stop’s own: \(\text{pos} = D + \text{lag}/30.4375\); the sale executes inside \(\lfloor \text{pos} \rfloor\) and is booked at \(\lceil \text{pos} \rceil\), the first payment date at or after the sale point — whose mark is the later of the two the sale reads, so cash is never booked before the last mark that priced it (model audit 2026-09-07, R02) — where stop_sale posts the delivery, the fee and the refund. The coin is price-exposed at every mark strictly before the landed month (engine.rs::price_exposed_at); tracks sets exit_month to price_exposure_end, the payoff date for a completion or an early completion and the landed month for a stop. At the program’s 18 days that is the month after the missed date, so the base numbers did not move; at 90 days the coin stays on the panel three months (model audit 2026-09-06, M04: before, tracks and the hedge desk both hard-coded month + 1, and at a 90-day lag coverage read zero active, zero notional and zero capital at risk in the two months the mark still read $46,109 and $36,205 while the Greeks — the engine’s own re-run — read a $287 per 1% delta; tests/model_audit_2026_09_06_m04.rs runs the probe and the six lags 0, 10, 18, 45, 60 and 90).

Per cohort

CohortCoverage sums the per-Agreement counters by origination month: months_below_schedule and months_below_capital are the mean active months an Agreement of that cohort spent below each line; share_below_schedule and share_below_capital divide by the cohort’s active Agreement-months. On the base path cohort 0 spent a mean 27.0 months below the schedule line and 17.6 below the capital line (62% and 41% of its active months); cohort 23, originated at the trough’s exit, spent 10.5 and 1.3 (33% and 4%). The page’s headline is the book-wide share of Agreement-months below the capital line.

The exposure ladder

The ladder cuts the book at one calendar month (ladder_month; left unset, the month of peak capital at risk) and bins every active Agreement by the embedded put’s moneyness and tenor. Moneyness is what a stop sale at that month’s mark would realize against the put’s strike, the remaining schedule:

\[ \text{moneyness} = \frac{V_m}{R_t} , \qquad \text{tenor} = n - t \text{ months remaining.} \]

The buckets are exposure.rs::MONEYNESS_BUCKETS and exposure.rs::TENOR_BUCKETS: six of moneyness (\(<0.50\), \(0.50\)–\(0.70\), \(0.70\)–\(0.85\), \(0.85\)–\(1.00\), \(1.00\)–\(1.20\), \(\ge 1.20\), lower bound inclusive) by five of tenor (\(\le 6\), 7–12, 13–24, 25–36, \(> 36\) months). Each of the 30 cells carries the Agreement count, the notional \(\sum R_t\) and the capital at risk \(\sum U_t\). Notional is the size of the puts the Holder is short; capital at risk is what a par Holder can actually lose in that cell, and it is the figure the page colours. ladder_and_coverage_tie_to_each_other holds the totals equal to the coverage row of the same month and the cells’ sums equal to the totals.

A worked cell. At month 23 on the base path the mark is $33,375.25, so \(V = 33{,}291.81\). A cohort-0 Agreement at a $60,000 strike that paid every date has \(t = 23\): \(R_{23} = 54{,}575\), moneyness 0.610, tenor 37, unrecovered capital \(60{,}000 - 22 \times 1{,}401.25 = $29{,}172.50\). It lands in the \(0.50\)–\(0.70\) × \(> 36\) cell. A cohort-23 Agreement originated that month at a strike near the mark has moneyness \(0.9975/1.475 = 0.676\), the same cell. That is why 84 of the 203 active Agreements sit there (notional $4.58m, capital at risk $2.85m), 74 in \(0.70\)–\(0.85\), 41 in \(0.85\)–\(1.00\), 3 in \(1.00\)–\(1.20\) and 1 below 0.50, and why every cell at month 23 is in the \(> 36\) column: the oldest Agreement has 37 payments left. The tenor axis spreads out later. At month 48 (mark $62,345) the same book has 125 active Agreements, all at \(\ge 1.20\), split 6 / 59 / 60 across 7–12, 13–24 and 25–36 months, capital at risk $814k against a notional of $2.78m.

This is the shape a desk maps onto listed strikes: a put ladder from about 1.05 of spot down to zero, with the capital at risk in the 0.50–0.85 rows. The plan notes that corner of the surface carries the heaviest put skew and the thinnest listed liquidity, which is the first thing a desk will say about a flat 43% bridge; the ladder makes the mapping possible, it does not price it.

PD, LGD and EAD per vintage

The credit triple is read off outputs.rs::agreement_table, per origination month and once for the whole book (origination_month: null, last row). A stop is any row whose outcome is not completed, settled or open; the tags non_performance, non_performance_rational, conviction_walk and rational_boundary all count.

QuantityDefinition in exposure.rs::exposure
PDstops ÷ Agreements in the vintage
EADmean \(R_t\) at the stop (the remaining schedule at the missed date)
LGD\(\sum \text{shortfall} ,/, \sum R_t\) over the vintage’s stops, with shortfall \(= \max(0, R_t - V)\) from engine.rs::stop_sale
EL\(\sum \text{shortfall}\) in dollars
EL rateEL ÷ capital deployed (the sum of purchase prices)
Capital loss\(\sum \max(0, -\text{capital P&L})\) over stops, where capital_pnl = net delivered to the Holder − purchase price
Refunds, surplus\(\sum\) buyer_refund_usd, \(\sum\) stop_surplus_usd over the vintage’s stops

The identity a credit desk expects holds by construction:

\[ \text{EL} = \text{PD} \times \text{LGD} \times \text{EAD} \times n , \]

since \(\text{PD} \cdot n\) is the stop count, \(\text{EAD} \cdot \text{stops} = \sum R_t\), and LGD divides the shortfall by that sum. On the base configuration the book row reads 240 Agreements, 101 stops, 84 completed, 55 completed early; PD 0.421, EAD $43,563, LGD 0.225, EL $990,282 (10.3% of $9.62m deployed), and \(0.4208 \times 0.2251 \times 43{,}562.82 \times 240 = 990{,}282\). The book’s EL equals the simulate response’s total_shortfall_usd at the same seed.

Two columns separate the put’s intrinsic loss from the par Holder’s loss. EL is measured against the schedule, the put’s strike; capital loss against the Holder’s outlay net of what it had already been paid. On the base book they are $990k and $231k: most of the shortfall lands on Agreements that had already returned most of the Holder’s capital, the dollar version of the gap between the two lines. Refunds ($604k) and surplus ($149k) are the waterfall’s other two branches.

The vintages tell the timing story. Cohort 1, its first two years spent in the bridge’s dip, has 4 stops of 10, EAD $75,325, LGD 0.531, EL $159,921, a capital loss of $57,708 and no refunds. Cohort 23, originated at the trough’s exit, has 7 stops of 10 but LGD 0.043, EL $9,506, no capital loss, $108,878 of refunds and $46,735 of surplus: its Buyers stopped with the coin above the schedule, so the waterfall paid the Holder in full, refunded them, and handed the Holder the surplus. On the base configuration the drawdown multipliers (input #23) are off, so those in-the-money Buyers stopped at the full baseline hazard, price-blind; whether Buyers with a winning coin stop at anything like that rate is the behavior the Model Card names as the least-evidenced in the model, where it runs at the ×0.5 in-the-money multiplier.

The Greeks by bump-and-revalue

The Greeks are sensitivities of the Holder’s net cash gain, collections minus capital deployed (exposure.rs::net_gain, from outputs.rs::analyze), to a bump in the future path. Nothing is differentiated analytically; exposure.rs::greeks re-runs the engine with the bump applied and takes finite differences, averaged over a seed ensemble.

The as-of month and the two books

The book is valued as it stands at the ladder month, as_of. The bump starts the month after, from = as_of + 1, through paths.rs::PathBump (apply_bump leaves earlier months untouched). Two books are revalued. The existing book is the configuration with origination_stop_month cut to from, so no cohort originates after the as-of month and every strike is set on unbumped prices. The commitment is the full pacing, in which later cohorts strike at the bumped prices and their schedules grow with the price. On the base configuration the as-of month defaults to 23, the last origination month, so the two coincide; as of month 0 they differ, and greeks_vanish_on_a_riskless_book checks that a book with no stops has a zero existing-book delta and a positive commitment delta.

Seven runs per seed, for seeds \(s, s+1, \dots, s+k-1\) (greek_seeds, default 16): base, price up, price down, commitment up, commitment down, vol up, vol down. base_net_gain_usd and base_irr are ensemble means of the first, which is why the Greeks’ base IRR on the base configuration (15.86%) is not the seed-42 run’s 14.21%.

Delta and gamma

With \(b\) the price bump (default 0.05) and \(G_s(\cdot)\) the net gain under seed \(s\), every future price multiplied by \(1 \pm b\):

\[ \Delta = \frac{1}{k}\sum_s \frac{G_s(1+b) - G_s(1-b)}{2 \cdot 100,b}, \qquad \Gamma = \frac{1}{k}\sum_s \frac{G_s(1+b) - 2,G_s(1) + G_s(1-b)}{(100,b)^2} , \]

in dollars per 1% and per (1%)². Delta in coins divides by 1% of the as-of mark:

\[ \Delta_{\text{coins}} = \frac{\Delta}{0.01 \times S_{\text{as of}}} , \]

the number of coins whose 1% move matches the book’s, which is what a market-neutral desk shorts in perpetuals or futures to flatten it. The commitment delta repeats the central difference with the full pacing. The same differences on the effective IRR give delta_irr_pp_per_pct in percentage points.

Vega

Vega is one method on every path mode, and the response names it (vega_method: “path-amplitude scaling of the log-return deviations after as_of on the base path (every path mode), seed ensemble”). The base path is kept through the as-of month, bit for bit, and the deviations of the log returns after it are scaled around their mean so that the realized volatility of the base path (exposure.rs::realized_vol, log returns, annualized by \(\sqrt{12}\)) moves by \(\pm v\) (default 0.05, five points) — a PathBump { from_month: as_of + 1, vol_factor }, never a regeneration; vega is the difference over the span actually applied, in vol points, and the scaling is a no-op on a flat path. That holds on the bridge, GBM, jump diffusion, regime switching, the bootstrap and the replay alike: until the follow-up audit of 6 September 2026 (finding 2) the bridge branch moved the bridge’s own vol_annual and re-simulated from inception, which re-drew months 0–23 and every strike struck on them, so an existing book of fixed dollar schedules — which has no sensitivity to future volatility — reported a vega of +$6,977.44 per vol point on the audit’s fixed book (24 × 10, 60-month terms, 84-month horizon, 43% bridge, dispersion on, no stops, no early completion, month 12, 32 seeds); finding_2_the_vol_bump_leaves_the_existing_books_history_alone_on_every_path_mode (tests/audit_2026_09_06.rs) now reads 0.000000 on every mode, finding_2_the_bumped_path_shares_every_month_up_to_as_of_bit_for_bit holds the months through as-of identical, and the notes say only the months after as-of differ. So this is the continuation’s vega — the only vega an existing book has — never a whole-life re-draw; on the base configuration it is +$8,631 per vol point where the old bridge method printed ≈ +$30k. bump_overlay_scales_prices_and_vol checks the mechanics: month 0 untouched, every later price scaled, a zero vol factor giving constant log returns, hold_strikes pinning every strike to the unbumped level.

Theta

Theta is not a bump. It is the markup accrual on a path with no price risk: the engine is run on a flat bridge (end price = start price, zero volatility, no shock, no bump) and the net gain is divided by the weighted average life in months:

\[ \Theta = \frac{G_{\text{flat}}}{\text{WAL}_{\text{flat}}} . \]

On the base configuration the flat run collects $18,971,951.25 on $14,400,000 deployed (no dispersion on a flat path, so 240 × $60,000) with a WAL of 35.95 months: \(4{,}571{,}951.25 / 35.954 = $127{,}160\) per month. That is what the paper earns for being held, positive by construction; the flat path’s own stops (proceeds $59,850 against an early schedule) are already inside it.

Why per-Agreement streams make this a sensitivity

A finite difference is only a sensitivity if the two runs differ by the bump alone. Since v1.6 every Agreement draws its stop and early-completion decisions from its own ChaCha20 stream, seeded from (config seed, Agreement id) through splitmix64 (engine.rs::agreement_rng). A bumped price changes which side of a threshold a draw falls on; it never changes the draw, and it never shifts the random numbers of every later Agreement, which a single shared stream would do the moment one exit moved. greeks_are_sensitivities_not_draw_noise bumps a 240-Agreement book (12 cohorts of 20, on the base bridge) by one basis point from month 24 and requires at most two of 240 exits to flip, then checks that the delta at 2% and at 5% agree in sign and within a factor of two. The seed ensemble averages the remaining threshold noise. The endpoint refuses a configuration that already carries a bump, because the Greeks own it.

The signs, and the base-config numbers

From the endpoint on the base configuration, as of month 23 (mark $33,375.25), 16 seeds, bumps 5% and 5 points — re-read on 6 September 2026 under the as-of-preserving bumps (the two audits’ finding 2: the existing book’s horizon pinned to the base run’s, the vol bump a continuation bump on every mode), so the delta, gamma and vega rows differ from the 3 September figures (+24,885 / −175.6 / +29,718) the earlier method printed:

GreekValueUnit
Delta, existing book+23,259dollars per +1% in every future price
Delta, commitment+23,259dollars per +1% (coincides: no cohort after month 23)
Delta in coins69.7coins at the as-of mark
Gamma−28.9dollars per (1%)²
Vega+8,631dollars per +1 vol point (the continuation after month 23, every mode)
Theta+127,160dollars per month of WAL, flat path
Delta of IRR+0.083pp per +1%
Vega of IRR+0.036pp per vol point
Realized vol of the base path39.4%annualized

These are the signs in this dated example. Higher prices can reduce shortfalls below the schedule line and increase surplus proceeds above the Purchase Price. In the intermediate waterfall regime, a stop delivers the remaining schedule. Buyer behavior and the path determine the aggregate sensitivities. Theta is the flat-path gain-per-WAL proxy described above; vega is recomputed over the continuation after the selected as-of month. Neither the example’s signs nor its magnitudes apply to every scenario.

Vega’s sign is the one the plan got wrong, and the plan’s Phase 1 status says so. Under the v1.2 stop sale, sized to the schedule with the residual coin returned, the Holder was short a put and nothing else, so more volatility could only mean more stops in the money and the sign was negative. Under the ruled waterfall (Marc, 2026-09-03; spec v1.5) the Holder also keeps the surplus above the Purchase Price on an in-the-money stop, a long call, and a wider path feeds that call as much as it feeds the put. So vega is negative where the book’s stops are underwater and positive where the surplus dominates. On the base bridge, with both endpoints pinned at $60,000, the wider paths wander above the strike as often as below and the ensemble vega is positive. On a falling bridge to $30,000 with drawdown multipliers, greeks_have_the_signs_of_a_short_put_on_a_stopping_book gets the negative vega and the positive delta. A desk reading the number should read vega_method and the path shape beside it.

The walk-away frontier family

The fifth exhibit is not read off the run. It is the computed boundary below which a rational Buyer is better off walking, drawn on the same axes as the two lines for several values of the one parameter that is a belief. boundary.rs::rational_frontier was lifted from the parked Behavior Engine and re-derived for the September stop; the same function is the fifth behavior mode, input #25 (who stops paying).

The lattice

Optimal stopping by backward induction on a full-width log-price grid with Cox-Ross-Rubinstein steps, four price steps per month (SUB = 4), decisions at payment dates only, under the Buyer’s believed annual drift \(\mu\), the lattice volatility \(\sigma\) and the Buyer’s own discount rate \(r_c\) (boundary.rs::BoundaryParams, defaults 0.414, 0.25, 0.15, and a walk cost of 2.5% of the coin’s cost: $1,500 at the base terms, about one payment):

\[ \Delta = \tfrac{1}{48}\ \text{yr}, \quad u = e^{\sigma\sqrt{\Delta}}, \quad d = 1/u, \quad q = \frac{e^{\mu\Delta} - d}{u - d}, \quad \text{disc} = e^{-r_c \Delta} . \]

At the defaults \(\sigma\sqrt{\Delta} = 0.0598\). The grid spans \(4n + 12\) nodes on each side of the entry price (PAD = 12), beyond what a tree rooted at a single node could reach: such a tree sees only \(e^{-4\sigma\sqrt{\Delta},t}\) at date \(t\), and at month 1 could not see a coin at half of entry. The Phase 1 review found that defect (a Buyer whose coin halved at payment 1 kept paying until payment 4); rational_boundary_walks_the_book_in_a_crash_and_spares_it_in_a_rally now pins the walk at payment 1 with a custom path at 45% of entry.

The three choices

At the payment date of payment \(t\), with \(A = \sum_{k<t} p_k\) the payments made before this date, \(R = P - A\) the remaining schedule with payment \(t\) included, spot \(S\) and \(V = S(1 - \text{sale cost})\):

\[ \text{pay} = -p_t + \text{disc}^{4},\mathbb{E}[\text{value at the next date}] \quad (\text{at } t = n:\ -p_n + S), \]

where the expectation runs over the four price steps to the next payment date and \(\text{disc}\) is applied at each step, so one month discounts at \(e^{-r_c/12}\). The other two choices are

\[ \text{settle} = S - R \ \text{ if } S > R, \text{ else } -\infty, \qquad \text{walk} = \min(A, \max(0, V + A - P)) - c_{\text{walk}} . \]

The walk payoff is the September refund (R-1033) in the Buyer’s frame, less the cost of walking. The lattice sells at the decision date’s spot with the sale cost only, not the engine’s 18-day lag and haircut: the frontier is a boundary, and the engine’s stop prices the sale. Where the refund is positive (\(V > R\)), settling beats walking by \(S - V + c_{\text{walk}} > 0\), so walking is optimal only where the refund is zero, and the walk region is a single lower region per payment date. The frontier is the crossing between the highest grid node where walking wins and the next node up, interpolated in log-spot on the value gap; None means no node walks at that date. walk_region_sits_where_the_refund_is_zero holds the whole frontier at or below the schedule line to within one grid step. Each row is reported as walk_below_spot, walk_below_of_entry (spot ÷ strike, the two lines’ frame) and walk_below_moneyness (spot ÷ the amortized obligation from contract.rs::ContractTerms::remaining_obligation_at).

Row \(m = t - 1\) is the decision before payment \(t\), so the frontier row for payment \(t\) sits beside line row \(t - 1\). In engine.rs::run (step 1) a Buyer whose spot is below frontier[t − 1] × strike does not pay; the walk is the missed payment, executed in step 2a as stop_sale with the missed date \(m\) itself and the exit tag rational_boundary.

The frontier on the two lines

The endpoint’s default family is \(\mu \in {0, 0.10, 0.25, 0.50}\) at the default lattice parameters; the frontier is scale-invariant in the strike (frontier_is_scale_invariant_in_the_strike). As a fraction of entry:

Payment\(\mu = 0\)\(\mu = 10%\)\(\mu = 25%\)\(\mu = 50%\)Schedule line (row \(t-1\))Capital line (row \(t-1\))
11.1621.0110.5210.1521.4751.053
21.1491.0000.5210.1551.4501.053
61.0840.9520.5190.1681.3520.954
120.9840.8720.5050.1871.2050.807
240.7730.7060.4670.2210.9100.512
360.5430.5080.3850.2330.6150.217
480.2870.2760.2400.1870.3200.000
590.0240.0240.0240.0230.0490.000
60nonenonenonenone0.0250.000

Every curve lies below the schedule line, as the lattice requires. Where it lies relative to the capital line is the desk’s question. The zero-drift Buyer walks at 1.16 of entry at payment 1, above the capital line: his stop costs the Holder yield, not capital, though it is a shortfall against the schedule. His frontier stays above par through payment 11 and crosses below 1.0 at payment 12 (pessimist_robot_walks_at_par checks moneyness \(\ge 1\) at payments 2, 4, 7 and 10). The 25% Buyer walks only near half of entry through the first year, below the capital line through payment 26 (0.457 against 0.463) and above it from payment 27; his stops are the ones that lose the Holder capital, and they happen only in a halving. The 50% Buyer walks almost nowhere. Higher \(\sigma\), a higher walk cost and a stronger belief all deepen the frontier (higher_sigma_deepens_the_frontier, walk_cost_and_belief_deepen_the_frontier).

What each curve means to a desk

The frontier is the only behavior number in the engine that does not depend on BTC Now’s priors, which is why the plan lifted it. Each curve is the walk-away price of a Buyer with one belief about the coin, and the family brackets the book. The zero-drift Buyer is the pessimist robot who should never have signed: a coin he does not believe will appreciate is never worth financing at 1.475×, so he walks at or above par from the first months and a book of such Buyers is a book of stops in any flat market. That is the lattice’s finding of 2026-08-06, unchanged by the new stop. A believing Buyer walks only far inside the money of the put, where the stop already costs the Holder capital; his curve says where the capital line is actually tested. The distance between a curve and the capital line at a given month is the price fall that has to happen before that Buyer’s stop becomes the Holder’s loss. Run with input #25, the engine turns any one curve into a book’s exits.

The four legs on these numbers

The annuity is the schedule line’s numerator and theta’s numerator: \(R_t\) falling by one payment a month, and the flat-path net gain per month of WAL, +$127,160 on the base book.

The short put at the schedule is the schedule line as its strike, the ladder’s notional as its size, EL as its realized intrinsic value, and the positive delta and negative gamma as its price sensitivities. Its exercise is behavioral, which is why the frontier family sits beside the line: the curves say at what price a rational Buyer exercises it.

The long call at the Purchase Price is the surplus column of the credit table ($149k on the base book, all of it in vintages originated from month 10 on) and the positive half of vega. It exists only since the ruled waterfall, and it is why the base-config vega is positive rather than the negative the plan expected.

The Buyer’s completion option is the completed_early count (55 of 240); it is not a risk exhibit, since it only ever accelerates the annuity.

The capital line is not a leg. It is where the put’s intrinsic loss stops being paid out of the Holder’s yield and starts being paid out of its capital; the capital-at-risk figures on coverage and the ladder are the size of that, month by month.

Reproducing this chapter

POST /api/forwardflow/risk with the cockpit’s BASE_CONFIG and no options returns every number above except the month-48 ladder (ladder_month: 48) and the flat-path check (POST /api/forwardflow/simulate with the bridge’s volatility at zero). cargo test --release --workspace runs the nine tests of tests/exposure.rs and the seven unit tests in boundary.rs. The wire format is in The API; the reproduction rules are in Verification and reproduction.

Surfaces and hedging

Compare the Agreement with a hedge under the same scenario. This chapter explains the price models, volatility surface, option valuation, hedge cash flows and monthly marks. It also states the costs, lifecycle rules and funding assumptions behind the comparison.

Implementation: paths.rs, surface.rs, hedge.rs, series.rs and coin.rs; reference tests include tests/paths_models.rs, tests/surface.rs and tests/hedge.rs. Use Hedge strategies for comparisons and Monthly analysis for cash and P&L.

Historical worked figures in this chapter record earlier configurations and fixtures; they predate the 0.6.0 Coin funding and stop-state corrections and the 0.6.1 monthly Coin sensitivity correction unless explicitly stated otherwise. They are not current workspace results or market quotes. Re-run a saved request on the recorded engine to reproduce an old figure. The default surface is stylised; read its source and the engine identity with each completed analysis.

Price models

The path mode is one enum, paths.rs::PathMode, and the four Phase 2 models sit beside the bridge, the replay, the bootstrap and the custom path. All four share one convention: the diffusion draws are the first horizon standard normals of the run’s seeded stream, one per month (paths.rs::draw_normals), and every model that needs more draws (jump counts, jump sizes, regime switches) takes them after. So the diffusion under a jump-diffusion at \(\lambda = 0\), or a regime switch at equal vols, is the same path as plain GBM on the same seed: the reductions are exact (jump_diffusion_without_jumps_is_gbm_exactly, regime_switching_with_equal_vols_is_gbm_exactly).

Geometric Brownian motion, PathMode::Gbm { mu_annual, vol_annual }. The monthly log step is paths.rs::gbm_log_step:

\[ \ln \frac{S_{k+1}}{S_k} = \frac{\mu - \tfrac{1}{2}\sigma^2}{12} + \sigma \sqrt{\tfrac{1}{12}}, z_k , \]

so \(\mathbb{E}[S_t] = S_0 e^{\mu t}\) with \(t\) in years; the \(-\sigma^2/2\) is the Itô correction. At \(\sigma = 0\) it is the exponential ramp at \(\mu\) (gbm_zero_vol_is_the_exponential_ramp_at_mu). Unlike the bridge, the far end is free. The drift must lie within ±500%/yr and the vol in [0, 500%), or the input is named in a 400.

Jump-diffusion, PathMode::JumpDiffusion { mu_annual, vol_annual, jump_rate_annual, jump }. Each month adds \(N_k \sim \text{Poisson}(\lambda/12)\) jumps (paths.rs::poisson_count, Knuth’s product method on the run’s own uniforms, so the draw sequence is this crate’s and not a library’s), each a log-size draw \(J\) from paths.rs::JumpKind, and the drift is compensated so that \(\mathbb{E}[S_t] = S_0 e^{\mu t}\) still holds with the jumps in (paths.rs::jump_diffusion):

\[ \ln \frac{S_{k+1}}{S_k} = \frac{\mu - \tfrac{1}{2}\sigma^2}{12} - \frac{\lambda}{12},\kappa + \sigma \sqrt{\tfrac{1}{12}}, z_k + \sum_{i=1}^{N_k} J_i , \qquad \kappa = \mathbb{E}[e^{J}] - 1 . \]

The compensator \(\kappa\) is JumpKind::compensator. For Merton (1976), \(J \sim \mathcal{N}(m, v^2)\) and \(\kappa = e^{m + v^2/2} - 1\). For Kou (2002), \(J\) is an up-jump \(\text{Exp}(\eta_+)\) with probability \(p\) and a down-jump \(-\text{Exp}(\eta_-)\) otherwise, and

\[ \kappa = p,\frac{\eta_+}{\eta_+ - 1} + (1 - p),\frac{\eta_-}{\eta_- + 1} - 1 , \]

which is why the validation insists on \(\eta_+ > 1\) (kou_rejects_eta_up_at_or_below_one): below it the mean of \(e^{J}\) does not exist. A Kou draw of eta_down: 4 has a mean down-jump of −25% in log terms, a Merton mean_log: −0.20 a typical jump of about −18%. Both compensations are checked on the terminal mean over seeds (merton_jump_diffusion_mean_is_compensated, kou_jump_diffusion_mean_is_compensated). \(\mu\) is the expected growth rate jumps included; \(\sigma\) is the diffusion’s vol alone.

Regime switching, PathMode::RegimeSwitching. A two-state monthly Markov chain on the vol, calm or stressed, one drift. Month \(k\)’s return uses the state entering month \(k\) at that state’s \(\sigma\); then one uniform decides the switch for the next month at p_calm_to_stressed or p_stressed_to_calm (paths.rs::regime_switching). A chain that can never move still takes its draws, so the path depends on the seed alone and never on a parameter’s zero; a chain pinned in one state is GBM at that vol (regime_switching_pinned_in_one_state_is_gbm_at_that_vol). start_stressed begins the run in the stressed state.

Upload, PathMode::Upload { prices }. The desk’s own path: at least horizon + 1 monthly prices, month 0 first, finite and positive, rebased so \(S_0\) is the run’s start price (paths.rs::upload, upload_rebases_to_the_start_price_and_ignores_extra_points). Fewer points than the horizon needs is PathError::UploadTooShort with both counts named. Deterministic and seed-free, like the custom path.

The crash library, paths.rs::crash_library, is data rather than a mode: six named drawdowns, each a PathMode::Custom anchor list (piecewise log-linear from \((0, 1.0)\), flat after the last anchor) served by GET /api/forwardflow/crash_library and loaded as config.path = { Custom: { points } }.

idAnchors (month, ratio)What it stylises
2018(12, 0.16)The 2018 bear market: about −84% over twelve months, no recovery in the horizon
2022(13, 0.23)The 2022 bear market through the FTX collapse: about −77% over thirteen months
march-2020(1, 0.50), (4, 1.00)March 2020: −50% inside one month, back to the start by month 4
may-2021(2, 0.50), (6, 0.75)May–June 2021: −50% over two months, then 75% of the start by month 6
ftx-week(1, 0.75)The FTX week: −25% inside one month, flat after
peak-crash(12, 1.00), (15, 0.40)Synthetic: flat through twelve months of originations, then −60% over three

Every shape is monthly anchors, not tick data; the March 2020 move took a week and at monthly resolution it is one down mark and a three-month climb. every_crash_preset_hits_its_anchors and crash_presets_run_through_the_engine hold them to their stated shapes and through a full run. The shock designer still composes on top of any of these, since it is applied after generation.

The volatility surface

surface.rs::VolSurface is implied vol by tenor and moneyness: tenors_months ascending and positive, moneyness ascending and positive, vols[i][j] a finite fraction in [0, 500%), and a source string that names a venue and a timestamp for a real snapshot or carries the preset’s own warning. The moneyness axis is \(K/S\), strike over spot, the option-quoting convention: puts below 1, calls above. The engine’s own moneyness (contract.rs, the exposure ladder) is the coin’s price over the strike, its reciprocal, and the two are never mixed: the ladder’s legs report both.

Lookup is VolSurface::vol_at(tenor_months, moneyness): each bracketing tenor row is read linearly in the vol between its quotes (ln moneyness) and, beyond the quoted moneyness, by the wing convention (surface.rs::row_vol; model audit 2026-09-07, R01): total variance \(w = \sigma^2 T\) continues linearly in \(k = \ln K/S\) at the edge cell’s own slope \(dw/dk = 2\sigma_e \sigma’ T\), clamped to Lee’s bound \(|dw/dk| \le 2\) and floored at zero, matching the edge slope when the Lee bound does not clamp it — the flat continuation it replaced put a concave kink at every sloped edge, a butterfly priced below zero. Between two quoted tenors the rows blend in total variance, linearly in the tenor (with the price-space checks applied separately); beyond the quoted tenors the surface is flat in tenor (surface.rs::bracket). Every price beyond the quoted range is the convention, not a quote: the surface reports quoted_moneyness and wing, and every desk response carries surface_note.

The check (VolSurface::arbitrage_violations(rate); model audit 2026-09-06 M06, and 2026-09-07 R01 for the nodes and the wings): every call the surface prices on $100 of spot — on the quotes, on seven log-spaced strikes inside each cell, and on eight log-spaced wing points out to 0.25× the first quote and 4× the last, where the wing convention prices — must fall with the strike, be convex in it, sit within \(\max(S - Ke^{-rT}, 0) \le C \le S\) and rise with the tenor at the same forward moneyness. The convexity triple is taken over three consecutive quotes, over (the last fine point to the left, the node, the first fine point to the right) across every quoted node including both edges, and over every remaining consecutive fine triple, wings included. The audit’s mild peak — vols 0.40 / 0.41 / 0.40 at 0.8 / 1.0 / 1.2, one year — prices the butterfly \(C(99) - 2C(100) + C(101)\) at −$0.0294 and was accepted, because the old check deliberately skipped the triple centred on a node (a kink in the vol is not exempt from price convexity: under linear-in-vol interpolation the price’s second derivative across a node carries \(C_\sigma \sigma’’\)); it is refused naming the node (“the 12-month calls at 0.97×, 1.00× and 1.02× spot … a peak in the quoted vol at 1.00× is a butterfly priced below zero; smooth the quotes or add a node”), and so is the same peak moved to any other node. A wing failure says “beyond the quoted range (lo–hi)” and, when the edge slope was clamped to Lee’s bound, that a wing that steep is itself an arbitrage. The flat preset is bit-identical inside and outside its quotes; on the stylised preset the put wing moved the fee-gross embedded ladder $5,598.11 → $5,659.39 per Agreement (+1.09%; 28 of its 59 legs strike below 0.70× spot), the 24-month dealer call not at all (tests/model_audit_2026_09_07_r01.rs). The 60-month leg of the ladder, at \(K/S = 0.025\), reads the 24-month row’s put wing: 81.2% against the 52% quote at 0.70.

From delta quotes. Deribit and the OTC desks quote a tenor-by-delta grid, and VolSurface::from_delta_quotes converts it. Every delta is the Black-Scholes spot delta at zero rate; inverting \(\Delta_{\text{call}} = N(d_1)\) or \(\Delta_{\text{put}} = N(d_1) - 1\) for \(d_1\) and solving \(d_1 = (\ln(S/K) + \sigma^2 T/2)/(\sigma\sqrt{T})\) gives surface.rs::moneyness_from_delta:

\[ \frac{K}{S} = \exp!\Big(\tfrac{1}{2}\sigma^2 T - d_1,\sigma\sqrt{T}\Big), \]

so the 50-delta call lands at \(K/S = e^{\sigma^2 T/2}\), a shade above 1, and a 25-delta put below it. Because \(\sigma\sqrt{T}\) differs by tenor, the same delta lands at a different strike in every row; the shared moneyness axis is the union of every converted strike, and each tenor row is filled onto it by linear interpolation in \(\ln K/S\) between its own quotes and by the wing convention outside them (surface.rs::interp_ln_wing) — for the wire and the eye: the surface records each row’s own quoted columns (quoted_columns) and reads the row on those, its wing from its own edge, so another tenor’s strike inside this row’s wing is not a node of this row (read linear in the vol between union nodes, the wing’s samples were chords under a concave curve and every multi-tenor grid carried a kink at its short row’s edge quote; r01_review_a_multi_tenor_delta_grid_is_read_on_each_rows_own_quotes_and_accepted). delta_quoted_constructor_round_trips_to_moneyness checks that a 25-delta put at 55% vol and three months lands below spot with \(\Delta = -0.25\) exactly, and that a two-tenor, three-delta grid gives a six-knot axis with put skew at both tenors.

The two presets, served by GET /api/forwardflow/surfaces in the shape every desk endpoint accepts back as surface:

  • VolSurface::flat(0.43): 43% at every knot, tenors 1/3/6/12/24 months, moneyness 0.50 to 1.30 in six knots; the base bridge’s volatility with no skew.
  • VolSurface::btc_stylised(): tenors 1/3/12/24, moneyness 0.70/1.00/1.30. ATM 41.4% from 1 to 12 months rising to 48% at 24; the 0.70 put at +9 vol points at 1 and 3 months, +6 at 12, +4 at 24; the 1.30 call at −2, −2, −1, 0. Its source reads “stylised illustration, not market data; import a real snapshot”, and the interpolation makes the ATM at 18 months 44.7%. Beyond the quoted 0.70–1.30 moneyness range, the wing convention prices the surface. Deep puts therefore use extrapolated volatility, not a flat copy of the 0.70 quote.

The stylised surface is the default wherever no surface is sent (forwardflow_api.rs::surface_or_default). It is a shape for the exhibits to have skew on, and the plan’s own warning stands: the market prices the ladder’s corner richer than a flat 43%, and by how much is a question for a snapshot.

Black-Scholes as used

The option math is written out in surface.rs so the numbers a desk sees never depend on a platform’s erf: norm_cdf is Hart (1968) as given by West (2005), accurate to about \(10^{-14}\); norm_inv is Acklam’s rational approximation with one Halley step against norm_cdf. With \(S\) the spot, \(K\) the strike, \(T\) the tenor in years, \(\sigma\) the vol and \(r\) the continuously compounded rate (surface.rs::d1d2):

\[ d_1 = \frac{\ln(S/K) + (r + \tfrac{1}{2}\sigma^2),T}{\sigma\sqrt{T}}, \qquad d_2 = d_1 - \sigma\sqrt{T}, \]

\[ \text{put} = K e^{-rT} N(-d_2) - S,N(-d_1), \qquad \text{call} = S,N(d_1) - K e^{-rT} N(d_2), \]

surface.rs::bs_put and bs_call, on one coin, in dollars. At zero tenor or zero vol both degenerate to the discounted intrinsic. The put’s spot delta is bs_delta_put \(= N(d_1) - 1\), in \((-1, 0)\), and −1 or 0 with no time or vol left; the vega is bs_vega \(= S\sqrt{T},\varphi(d_1)\), dollars per unit of vol, the same for put and call. put_call_parity_holds checks \(\text{call} - \text{put} = S - Ke^{-rT}\) at five points, including zero tenor and zero vol, and pins the textbook pair 10.4506 / 5.5735 at \(S = K = 100\), one year, 20%, 5%. price_put_on_surface reads the vol at \((T, K/S)\) and prices at it; the ladder and every hedge leg go through that one path.

Two rates appear in the chapter. Placement uses its funding_rate, 4.5%/yr continuously compounded by default. Hedge options use the explicit option_rate in OptionPricing, default zero, with the requested put-skew adjustment. Neither rate is the Coin delta continuation drift described below.

The embedded put ladder and the paper’s implied vol

The plan writes the paper’s loss as one short put on the coin struck at the remaining schedule, exercised only when the Buyer stops. surface.rs::embedded_put_ladder writes it as a strip: one European put per defaultable payment age \(t = 1, \dots, n-1\) (a draw after age n−1 can miss the final payment), at cohort-1 terms with the strike equal to the start price. Leg \(t\) is

\[ \text{strike} = R_t, \qquad \text{tenor} = t + 1 \text{ months}, \qquad w_t = \Big(\prod_{k<t}(1 - h_k)\Big), h_t , \]

with \(R_t\) the remaining schedule from exposure.rs::two_lines (the schedule line’s numerator), the expiry the missed payment date, and \(w_t\) the unconditional stop probability at that age from surface.rs::stop_mass: survival times the hazard of the config’s default scenario. The weights are price-blind on purpose. The drawdown multipliers, the conviction rule, the rational modes and early completion are all ignored, so the weights sum to the scenario’s lifetime stop rate (ladder_weights_sum_to_the_lifetime_stop_rate_and_strikes_amortize: 0.400 at the base configuration, 59 legs). Each leg carries the surface’s vol at \((t+1, R_t/S_0)\) and its Black-Scholes value at the request’s rate.

POST /api/forwardflow/placement on the base configuration, stylised surface, 4.5%:

Age \(t\)Strike \(R_t\)TenorWeight \(w_t\)\(S_0/R_t\)Vol at \(R_t/S_0\)Put value
1$87,02520.00980.68939.4%$26,421.56
6$79,65070.01820.75339.8%$19,875.75
12$70,800130.01620.84741.4%$14,967.53
19$60,475200.00870.99245.8%$11,630.96
24$53,100250.00821.13049.4%$9,910.44
59$1,475600.001540.6852.0%$0.79

Two things to read off the table. Through payment 19 the strike sits above the spot, so in the surface’s convention these are puts at \(K/S > 1\): deep in the money, and looked up on the call wing (1.45 is beyond the 1.30 edge, hence the 39.4% at age 1). That is not a mistake; a put and a call at the same strike and tenor share one implied vol, and an in-the-money put is priced off the out-of-the-money call’s. The heaviest leg is age 4 (\(w = 0.019\)), where the baseline hump peaks, and the ladder’s dollars sit in the first two years, which is where the risk desk already put the loss story.

The placement adds three scalars. ladder_value_usd_per_agreement is \(\sum_t w_t \cdot \text{put}_t\), what the market says the Holder’s short puts are worth per Agreement, behavior-weighted: $5,598.11 on the stylised surface, $5,468.53 on the flat 43% one. markup_usd_per_agreement is surface.rs::flat_path_markup, the completing Agreement’s net gain to the Holder at cohort-1 terms,

\[ \text{markup} = \Big(P - \sum_{k \le N} p_k\Big)(1 - f) - \text{purchase} = (88{,}500 - 1{,}475)\times 0.95 - 60{,}000 = $22{,}673.75 , \]

cents-exact then converted, and markup_pv_usd_per_agreement is surface.rs::flat_path_markup_pv, the same receipts discounted continuously at the desk’s rate to signing: $13,751 at the endpoint’s default rate. The puts are present values, so the markup they are set against must be one too.

paper_implied_vol is surface.rs::paper_implied_vol: the flat vol \(\sigma^*\) at which the weighted, fee-net ladder exactly equals the markup’s present value,

\[ \sum_{t=1}^{n-1} w_t,(1-f), \text{put}(S_0, R_t, \tfrac{t+1}{12}, \sigma^*, r) = \text{markup PV} , \]

like for like on both sides: the Holder’s shortfall on a stop is \((1-f)(R_t - V)\) because BTC Now takes its fee on what the sale delivers, so the Holder is short \((1-f)\) of each put, and the markup it earns is net of the same fee. Found by bisection on \((1%, 1000%)\); the ladder value is increasing in \(\sigma\), so the root is unique when it exists. Read against the market: where the surface’s vols at the ladder’s strikes sit below \(\sigma^*\), the markup more than pays for the puts; above it, the market values what the Holder is short at more than the paper pays.

On the base configuration the endpoint prints 160.8% at par: a markup worth $13,751 today against a fee-net ladder worth $5,318 on the stylised surface, whose at-the-money vol is 41.4% at 12 months. The paper pays for its embedded puts at roughly four times the market’s volatility. Dearer paper implies a lower vol, because a smaller markup buys fewer puts: 115.0% at 105% of the coin’s cost, 75.5% at 110%; beyond about 115% the markup’s present value falls below the ladder’s intrinsic floor and no vol balances it (paper_implied_vol_exists_and_is_positive pins the ordering). The weights are BTC Now’s 40% prior over the hump, price-blind; the surface is an illustration; both are inputs a desk replaces.

Risk-neutral fair value

surface.rs::fair_value prices the book under the desk’s measure and leaves the Buyer under BTC Now’s. The engine runs over seeds seeds under

\[ \text{PathMode::Gbm}{\mu = r_f,\ \sigma = \sigma_{\text{ATM}}(24)} , \]

the funding rate as the drift and the surface’s ATM vol at 24 months, the paper’s weighted average life, as the vol; the config’s shock and bump overlays are cleared, since a fair value is not a stress, and everything behavioral (hazards, multipliers, propensity, the conviction rule, the rational modes) stays the config’s. The skew enters the placement, not these paths. Per seed the Holder’s gross monthly receipts and purchases are read off the ledger (ledger.rs::owner_monthly_gross, cent-exact, converted once) and discounted continuously to month 0:

\[ \text{PV} = \sum_m \text{in}_m, e^{-r_f m/12}, \qquad \text{cost} = \sum_m \text{out}_m, e^{-r_f m/12}, \qquad \text{spread} = \frac{\text{PV} - \text{cost}}{\text{cost}} \times 10^4 \text{ bps}, \]

averaged over the seeds. Seed \(k\) is the config’s seed plus \(k\), run in parallel, so the figure is byte-reproducible (higher_funding_rate_lowers_the_pv runs the base book twice at 4 seeds and asserts equality). The purchases are discounted too, because a paced book buys over 24 months.

POST /api/forwardflow/fair_value on the base configuration, defaults (stylised surface, 4.5%, 32 seeds): atm_vol_used 0.48, pv_per_agreement_usd $73,662.46, pv_book_usd $17,678,990.57, purchase_book_usd $14,720,604.16, spread_bps 2,009.7. At a zero funding rate the PV per Agreement is $79,661.41 and the spread 2,987.7 bps (higher_funding_rate_lowers_the_pv). A riskless single Agreement on a flat path at zero rate returns exactly the markup: PV − cost = $22,673.75, spread 3,779 bps (fair_value_of_a_riskless_book_is_its_undiscounted_net_gain).

In this worked configuration, the model value is about 20% above the assumed purchase price. That difference is conditional on the stated market and behavior assumptions; it does not identify an observed premium for behavior or illiquidity. Note what the gap is not. It is a GBM book at 48% vol drifting at 4.5%, with a 40% lifetime stop prior that is price-blind on the base configuration; a desk that turns on the drawdown multipliers, brings its own hazard, or imports a surface with a different 24-month ATM will print a different one, and the endpoint is built so it can. The plan also asks for this figure per Agreement, monthly, as a NAV mark; that is Phase 4’s, and pv_per_agreement_usd is the book-level number today.

The hedge overlay

hedge.rs::hedge_overlay never touches the ledger. Per seed the paper runs as the config says (engine.rs::run, seed = the config’s seed plus \(k\)), the Holder’s gross monthly flows are read off the ledger, and the structure’s monthly dollar flows are built on that run’s path and book (hedge.rs::hedge_flows) in f64, quantized to cents only where they join the paper’s (hedge.rs::combined_metrics). The IRR of the combined flow is outputs.rs::irr, converted to effective annual, and the undiscounted multiple is gross dollars in over gross dollars out with the hedge’s flows on the hedged side. The unhedged figure is the same paper on the same seeds with a zero overlay, so the two distributions differ only by the hedge, and every structure in one request shares one unhedged (hedge_endpoint_runs_structures_and_fills_excess_over_basis).

Three conventions, stated in every result’s notes. Options are priced on the surface at the inception’s spot, at the option pricing’s rate, for their CONTRACTUAL tenor (hedge.rs::buy_put and its siblings; audit 2026-09-05, finding 3); an option still open at the run’s last month is marked there at its remaining-life model value on the surface at that month’s spot (hedge.rs::option_close), on a leg labelled “(open at the horizon, marked)” — never settled at intrinsic on a shortened life, and a tenor running past the horizon by more than 120 months is refused (finding_3_option_tenors_are_priced_whole_and_marked_at_the_horizon: dealer calls of 13, 24 and 60 months on a 13-month horizon cost $3,417, $6,835 and $15,185, where they had all cost $3,417). An execution cost, exec_cost_bps of premium (50 by default, 0 to 1,000), is paid on every option leg traded, bought or written, and never on a perpetual (premium_and_execution_cost_scale_with_coverage: 1.005 × the premium at 50 bps, and the payoff untouched). Coverage is coins per Agreement, one Agreement being one coin, and every cost and payoff scales with it. An Agreement is on the hedge desk’s book while it is price-exposed — from its origination month up to its payoff date, or, for a stop, through every mark before the month its sale is booked, \(\lceil D + \text{lag}/30.4375 \rceil\) (hedge.rs::on_book_at reads engine.rs::price_exposed_at, the one lifecycle coverage and the monthly mark read too; model audit 2026-09-06, M04). Price exposure includes a delayed sale. A futures position follows its selected rebalancing and close-on-exit rules; calendar rules can retain a position beyond exit, which the result reports separately. An option programme incepts or rolls only while the Agreement is performing (hedge.rs::rolls_at): a coin already in transit is a short-dated exposure the futures leg carries, not one to write a fresh 3- or 12-month option on — which the desk used to do at the missed month, a 1.3% overstatement of the premium of every re-bought programme on the base book (Table D1: $5,118,459 → $5,050,244 mean premium over 4 seeds).

Instruments

hedge.rs::HedgeSpec supports the fixed structures below, three Coin structures and composed Legs, externally tagged on the wire. Structures with an explicit Inception support AtOrigination, each cohort hedging at its own origination month at that month’s spot with the strike a share of each Agreement’s own entry; or AtMonth(m), the whole book on the books at \(m\) hedging at once, later cohorts unhedged.

Put ladder, PutLadder { strike_pct_of_entry, tenor_months, coverage, inception }: one put per Agreement, coverage coins, struck at the share of entry, held to expiry (hedge.rs::buy_put). Premium \(\times (1 + \text{exec})\) out at inception, \(\max(K - S_{\text{expiry}}, 0)\) per coin in at expiry. On a zero-vol surface it costs its intrinsic and pays nothing on a flat path (put_ladder_on_a_zero_vol_surface_costs_its_premium_and_pays_nothing); on a dip it lifts the p5 (put_ladder_pays_in_a_crash_and_lifts_the_floor).

Put spread, PutSpread { long_strike_pct, short_strike_pct, … }: buy_put at the long strike and hedge.rs::write_put at the short one, execution cost on both legs’ gross premium; the long strike must exceed the short or the 400 names the put spread.

Rolling puts, RollingPuts { strike_pct_of_spot, tenor_months, coverage }: bought at each Agreement’s origination at a share of the then-spot, rolled at each expiry at the then-spot while the Agreement is on the book, through the horizon. The strike floats with the coin; the ladder’s does not.

Perpetual delta hedge, PerpDeltaHedge { rebalance_months, funding_rate_annual, initial_margin_pct, coverage }. The book’s delta is a documented proxy, hedge.rs::proxy_delta_coins, not exposure.rs::greeks, which is an ensemble figure costing seven engine runs per seed per date. For every Agreement on the book at month \(m\) with \(t\) payments made, the legs of its own embedded ladder still ahead of it, weighted by the stop mass at age \(k\) conditional on having reached \(t\):

\[ \Delta_m = \sum_{a \in \text{book}} \sum_{k = t_a + 1}^{n-1} \Big(\prod_{j=t_a+1}^{k-1}(1 - h_j)\Big) h_k \cdot \big(1 - N(d_1)\big)\big(S_m, R_k, \tfrac{k+1-t_a}{12}, \sigma(k+1-t_a, R_k/S_m)\big) \text{ coins}, \]

the surplus call above the Purchase Price left out: the desk shorts the put’s delta. The position is reset to \(\text{coverage} \times \Delta_m\) at every rebalance and marked monthly, \(-\text{position} \times (S_j - S_{j-1})\); funding is paid monthly on the short notional, \(\text{position} \times S_{j-1} \times r_{\text{perp}}/12\), and margin carry is initial_margin_pct of that (perp_hedge_with_zero_funding_on_a_flat_path_is_a_no_op, and the margin leg at 10% of the funding leg). A negative funding rate means the short is paid, and the carry is money in.

Variance swap, VarianceSwap { tenor_months, vega_notional_usd }: long variance at the surface’s ATM vol for the tenor, \(K = \sigma_{\text{ATM}}(\text{tenor})\), rolled through the horizon on its contractual tenor (seven 12-month swaps to an 84-month horizon, every_preset_runs_on_the_base_config); no premium, the execution cost charged in basis points of the vega notional at each inception. Since the model audit of 6 September 2026 (M10) every swap is marked each month at its remaining-life value, \(\tfrac{N_{\text{vega}}}{2K}(\sigma^2_{\text{exp}} - K^2)\) with \(\sigma^2_{\text{exp}} = (\sigma^2_t\, t + K^2 (T - t))/T\) — the realised months as they were, the unrealised at the strike (hedge.rs::variance_swap_mark; zero at inception, \(-N_{\text{vega}} K/2\) in vol points at expiry on a path with no moves) — carried in the series’ option_mark, its settlement on the full window booked against the mark it replaces; a swap whose window runs past the run’s last month is marked there on the (open at the horizon, marked) leg and never settled on a window cut to the horizon (m10_the_variance_swap_is_marked_monthly_on_its_contractual_tenor: on the clean 12-month book the second swap’s one visible month reads −$1,791.67, a twelfth of a swap, where it used to settle −$21,500 as a whole one). The realized variance is the zero-mean annualized sum of squared monthly log returns, \(\sigma^2_{\text{real}} = \tfrac{12}{N}\sum (\ln S_j/S_{j-1})^2\) (hedge.rs::realized_variance), and the payoff is the standard vol-point convention,

\[ \text{payoff} = \frac{N_{\text{vega}}}{2 K_{\text{pts}}}\big(\sigma^2_{\text{pts}} - K^2_{\text{pts}}\big) = 50, N_{\text{vega}},\frac{\sigma^2_{\text{real}} - K^2}{K} , \]

since \(K_{\text{pts}} = 100K\) (variance_swap_pays_realized_minus_strike_in_vol_points).

Legacy cross-book collar, CrossBookCollar { free_coins, call_strike_pct_of_spot, put_strike_pct_of_entry, tenor_months, coverage }: calls are written on separately held free_coins and their premium supports puts on the Agreements. The legacy Agreement-sleeve distribution includes that premium and the put flows, but excludes the external coin’s value and written-call liability. Call assignment is reported on its separate leg and is not charged to the Agreement-sleeve distribution. This is incomplete combined-book performance, so paired Research and explicit Coin sweep/monthly requests refuse it; the Coin workspace does not offer it as a complete strategy. The Buyer’s promised coin cannot cover a written call by the Holder.

Outputs and the two desks’ numbers

Every structure returns a hedge.rs::HedgeResult: the spec, the two Dists — median, mean, p5 and p95 effective annual IRR with linear-interpolated percentiles and the share below zero, all over the seeds_with_irr seeds that have an IRR; the median and p5 multiple and pct_cash_loss (the share whose multiple is below one) over EVERY seed; and seeds_without_irr with without_irr_reasons (single-signed, no root in range, non-finite). A seed whose combined flow has no IRR is still a path (audit 2026-09-05, finding 1: a naked written call assigned at 5× left eight seeds at a 0.29× multiple and a 0% share negative; finding_1_a_losing_written_call_is_a_cash_loss_on_every_seed now reads 100% cash loss, eight without an IRR, no root in range). Then the figures a desk reads first:

FieldDefinition in hedge_overlay
cost_irr_points(unhedged median IRR − hedged median IRR) × 100; negative when the hedge paid more than it cost
cost_bps_of_deployedmean net premium and carry paid ÷ mean purchase prices, in bps
premium_paid_mean_usdpremium bought less premium received, plus execution cost, funding and margin carry; negative for a net premium-positive structure
payoff_mean_usdsigned economic payoff: realized settlements plus horizon derivative value
realized_settlement_mean_usdrealized option, swap and futures settlements; premiums, basis and costs are separate
horizon_mark_mean_usdsigned value of derivatives still open at the horizon; not cash received
payout_over_premiumeconomic payoff ÷ net premium and carry paid; legacy wire value 0 for a nonpositive denominator, displayed as unavailable
floor_p5_irr, worst_path_irr, worst_path_seedlegacy USD p5 and lowest valid USD IRR with its seed; Coin views must use hedged_coin.p5_irr and a Coin-selected path
hedged_excess_over_basis_pp(hedged median IRR − basis_rate) × 100, filled by the API from its 6% default
legsgross economic debits and credits by leg; parenthetical marked legs are unrealized values, not transfers

The excess-over-basis field is a USD diagnostic: hedged median USD IRR less the requested annual cash-and-carry basis. It is not a BTC return comparison, and the reference does not remove counterparty, funding or execution risk. basis_rate is one annual comparison input; a futures leg’s locked/after basis schedule determines its modeled cash costs separately.

POST /api/forwardflow/hedge on the base configuration, stylised surface, 50 bps, 32 seeds. The paper alone: median IRR 15.01%, mean 15.45%, p5 11.37%, p95 20.00%, median multiple 1.311, 0.0% of seeds negative. Then:

StructureHedged medianHedged p5Cost, IRR ptsCost, bps deployedPremiumPayoffPayout ÷ premiumExcess over 6% basis
Put ladder 70% / 12m12.98%10.95%2.03516$813,995$384,3890.47
BTC Now’s posture, 70% / 24m / 0.7510.40%8.68%4.61907$1,430,783$366,0270.26
Put spread 85/60 / 12m13.25%11.14%1.75733$1,155,770$814,3810.70
Rolling 3m puts at 85% of spot10.10%1.87%4.903,994$6,299,110$4,934,9890.78
Perp delta hedge, monthly12.51%11.58%2.50221$348,663$-320,031-0.926.51 pp
Variance swap 12m, $10,000 vega15.10%10.57%-0.090$350$55,377158.22
Cross-book collar, 100 free coins22.38%17.67%-7.37-1,071$-1,689,903$979,8120.00

Every hedged IRR is the root nearest the unhedged paper’s rate (outputs.rs::irr_near): a hedge’s late settlements give the combined flow several sign changes, and the first root a scan meets from −95% a month is not the rate anyone means. Excess over basis is populated for structures classified as market-neutral, including supported short DollarDelta futures legs. A put alone is not classified as a market-neutral position.

The posture is the ruling of 2026-08-16 as ruled — a 70% floor, 75% density, a 24-month window — and on a bridge pinned at its start it is the dearest floor on the shelf after the rolling puts: 4.61 points of median and a p5 of 8.68%, below the unhedged 11.37%, because two-year puts on a market that ends where it began pay out 0.26 of their premium. Its worst path is seed 43 at 8.65%. The perpetual’s legs show where its cost lives: funding $316,966, margin carry $31,697, and a mark-to-market that paid $1,979,617 and received $1,659,585. Its p5 is the highest of the floors, which is what a delta hedge is for, and its median is 2.50 points lower, which is what it costs. The rolling puts buy the most protection and the most premium: 40% of deployed capital, a floor that falls to 1.87%, and a worst path of -1.88% (seed 63) on a bridge that never crashes.

The variance swap at $10,000 of vega notional is nearly free on this path — a hedged median of 15.10% against 15.01% — because a bridge’s realised variance sits close to the surface’s strike; it is the structure for a desk that thinks the surface is wrong, not for one that wants a floor. The cross-book collar is the coin-holder’s trade: 100 free coins write 130%-of-spot calls each year and the premium ($3,288,310 over the book’s life) pays for 85% puts on every Agreement ($1,581,966), lifting the paper’s median to 22.38% and its p5 to 17.67%. The calls are covered, so their assignment — $2,510,336 of upside above the strike over the same life — is the free coin’s give-up, reported on its own leg and never posted to the paper’s flows: the paper is financed, the coin book is capped, and a desk reads both lines.

Benchmarks

hedge.rs::benchmarks returns legacy market references on the same seeds as the Agreements when include_benchmarks is set. Spot: one coin per Agreement bought at its entry price in its origination month, every coin sold at the horizon’s mark. Covered call: that spot position overwritten with 12-month calls struck at 130% of spot at each roll, written at each coin’s origination and rolled every 12 months while held. A call still open at the horizon retains its signed remaining-life model value. Premium uses the supplied surface and option-pricing rate, with no execution cost; cash settlement occurs at expiry. Its IRR is the root nearest the spot reference’s. Agreements: outputs.rs::analyze on the same run. At par, actual-entry funding is one BTC per Agreement, including when entry strikes are dispersed. Non-par Agreement purchases change that funded BTC amount. The one-coin reference still has its own costs and capital basis. It differs from Research’s matched dated net contributions and is not an equal-budget or equal-cost ranking.

On the base configuration: the paper’s median 15.01% against spot’s -0.05% (mean -0.68%, p5 -8.93%, p95 7.12%, 50.0% of seeds negative, multiple 0.997) and the covered call’s 1.81% (mean 0.91%, p5 -7.00%, p95 8.57%, 46.9% negative, multiple 1.078). A bridge pinned at its start is a market that goes nowhere for seven years, and on it the overwrite’s premium is most of a spot position’s return; the paper’s 15.01% is the markup on the same coin at the same strike, with the Buyer paying the premium month by month and the stop sales taken out. The plan’s stripped-down reading of the paper as a five-year covered call is what this table tests, and a desk will run it on its own drift, where spot has one.

Presets

hedge.rs::presets is the shelf the cockpit offers and the engine tests (every_preset_runs_on_the_base_config, fifteen entries, every figure finite, the spec externally tagged): BTC Now’s posture; eleven of the risk paper’s structures as legs, each label carrying its table — the dollar seat’s loss-line ladder, year-end put and listed-futures delta hedge, the coin seat’s futures at 80% and at the full coin delta, its rolled 12-month calls, the dealer call and half of it, the call spread and the split, and the pair on both seats (the section below); and three of the table above — the monthly perpetual delta hedge, the variance swap at $10,000 vega notional, and the cross-book collar on 100 free coins with 1.30 calls and 0.85 puts at 12 months. “Dollar seat: BTC Now’s own posture — 70% floor, 75% density, 24-month window (2026-08-16)” is the put ladder at 70% of entry, 24 months, 0.75 coins per Agreement, at origination: the ruling of 2026-08-16, BTC Now’s own stance on its retained book, and the second row of the table above. The label says what the code says: one preset among several, never a recommendation. A Holder brings its own.

The desk as legs — the paper’s menu and the shelf

The risk paper (BTC Now BPA Risk Analysis v3.10, September 2026) defines the hedge menu a desk must carry, which the fixed shapes above cannot spell. HedgeSpec::Legs { legs } can: a structure is a list of legs, every leg posting into the one set of monthly flows under the label leg N: <description> (hedge.rs::Leg::describe), and a one-leg structure reproduces the fixed shape it copies bit for bit (a_legs_put_ladder_reproduces_the_put_ladder_shape_exactly; the tests are tests/legs.rs and tests/shelf.rs).

A leg is one of two kinds. Leg::Futures { side, sizing, basis, rule, initial_margin_pct, surface_vol } is listed futures per on-book Agreement, Long or Short, sized at every rebalance — rule is a TradingRule { rebalance, min_trade_coins, lot_coins, margin_funding_rate, futures_cost_bps, close_on_exit } (Phase 4a, hedge.rs::futures_leg): the policy is Calendar { months } (the original reset every k months, held through the interval whether or not the Agreement is still on the book — the default; close_on_exit: true closes the position the month the Agreement leaves the book, the close-out in the turnover and charged the trading cost, and the result’s futures_retained_agreement_months counts the Agreement-months a position was held past the paper’s exit, a speculative exposure the notes flag when positive — model audit 2026-09-07; on the wire the older rebalance_months: k still reads and writes), DeltaBand { band_coins_per_agreement } (reset when the target drifts from the position by more than the band) or PriceMove { pct } (reset when the spot has moved more than the share since the last reset); a reset smaller than the minimum is skipped, the book’s net position is rounded to the lot (CME is 5), and the initial margin is charged at the funding rate (default zero); the result reports futures_turnover_coins_mean and futures_trades_mean, and series.rs::rebalance_policy_comparison puts the policies side by side — DollarDelta { share } is the embedded ladder’s proxy delta times the share; CoinDelta { share } the lifecycle-aware coin delta, using a performing surface and known-stop valuation at the leg’s surface_vol; Coins { per_agreement } a fixed count. Leg::Option { kind, side, strike, tenor_months, roll, coverage, inception, years_limit } is a European put or call per Agreement, Buy or Write, held Once or rolled AtEachExpiry, with inception and rolls only while the Agreement performs; a live option runs to its own expiry; Coverage is Coins(c), ShareOfDollarDelta(s) or ShareOfCoinDelta(s). Three predicates on the spec — is_coin_seat, revalues_the_ladder_monthly, is_market_neutral (the perpetual delta hedge or a composition containing a short Dollar-delta futures leg; a classification, not proof that all risk is offset) — tell the API what it holds.

Strikes. Strike is PctOfEntry(k), PctOfSpot(k) at the inception, PctOfPurchasePrice(k) — entry times the multiple, so 1.0 is the paper’s 1.475× — or LossLineAtExpiry. The last is the dollar seat’s strike, the only price the Holder needs to defend: the loss line is the Holder’s unrecovered cost: the capital line of exposure.rs::two_lines, read through exposure::loss_line_of_entry(config, months) at the option’s expiry age — the sale proceeds, as a share of entry, that return the Holder’s outlay net of the fee. It sits above entry at signing (1.053, since payment one went to BTC Now), 0.782 at month 12, 0.487 at 24, and melts to zero by month 44, when no stop can lose the Holder money. A put struck there protects the capital and nothing above it; a put whose strike is zero is not bought, so the quarterly ladder buys fourteen puts, expiring at 3, 6, …, 42 months (the_loss_line_strike_reads_the_capital_line_at_expiry_and_stops_when_it_is_zero).

Dated futures. The position \(h\) per Agreement is reset at each rebalance, and at \(m+1\) two things post in dollars — the mark \(h,(S_{m+1} - S_m)\) and the basis \(h,S_m, b/12\) — on two legs, … — mark-to-market and … — basis. BasisSchedule { locked_months, locked_rate, after_rate } is the basis lock: locked_rate for the first locked_months of each Agreement’s life, then after_rate. This rate schedule is a dated-futures proxy; it does not simulate contract expiry. A long pays the basis; a short receives it. So the coin book’s long pays 4% then 10% and the dollar book’s short is paid 4% a year. The margin balance is a capital requirement: initial_margin_pct of the gross notional is reported at its monthly peak as margin_peak_of_par. The balance itself is not an expense, but a nonzero margin_funding_rate charges financing on that balance through the basis channel. The desk also reports the worst month of cash, futures_worst_month_of_par_median and _p95 — the worst single month’s marks and basis as a positive share of par.

The rule. A written option on the paper is naked. The coin inside an Agreement is promised to the Buyer, who owns its upside above the Purchase Price, so nothing covers a written call. The engine does not forbid the write (a call spread needs one); every written leg’s label ends (naked), and the covered write stays the CrossBookCollar’s business, where the free coin sits on the other book.

The option rate and the skew. OptionPricing { rate_annual, put_skew_points } is the Black-Scholes rate, and vol points of skew added to the surface’s vol for every put, calls reading the surface as it is. The default is zero and zero because that is the surface’s own quoting convention (the smile is built at zero rate, its skew already in the vol) and it reproduces every earlier figure bit for bit. The paper’s setting, OptionPricing::paper(), is 4.5% with five points (Appendix D). The two do not move a put the same way: a 12-month at-the-money put on a flat 40% surface is 15.85% of spot the default way and 15.30% the paper’s — at the money the rate’s discount outweighs the skew — while at the year-end loss line the skew wins, 5.77% against 6.00% (the_papers_pricing_moves_a_put_the_way_black_scholes_says).

The paper’s menu as the shelf of preloads. hedge.rs::presets is fifteen entries, eleven of them the paper’s structures as legs, each label carrying its table. The paper’s figures below are the risk paper’s — \(S_0\) $78,000, the Purchase Price 1.475× spot over 60 payments, 5% servicing, payment one retained, 40% realised vol, its own hazard prior, options at 4.5% with five points of put skew — and the engine’s are its tests, on the engine’s hazard, surface and loss line.

  • Put ladder on the loss line, quarterly, re-bought at each expiry (Table D1). The paper: 12.2% of spot in year one at 40% implied (20.6% at 55%), 0.3–1.5% in year two, nothing in year three. The engine on the riskless book, flat 40%, default pricing: fourteen puts, 16.4% of spot per coin all in, the first strike 100.3% of entry and the last 4.5%.
  • Twelve-month put at the year-end loss line, re-bought at month 12 (Table 7d). The paper: 0.72× spot at signing, 0.44× at month 12, 4.8–8.8% of spot by vol, about 4.4% of par, lifting the flat-coin 5th-percentile dollar IRR from +3.9% to +7.3%. The engine: the line 78.2% of entry at month 12 and 48.7% at 24, the puts 5.77% and 0.39% of spot.
  • Delta hedged in listed futures — the market-neutral seat (Table 20). The paper: unhedged dollar medians 11.0 / 14.6 / 17.5 / 22.4% at −18 / flat / +22 / +49% a year become 13.8 / 13.5 / 13.2 / 12.8%, the worst month of cash −1.6 to −3.0% of par at the median, margin 11% of par on day one. The engine: −18% takes 12.4% to 14.5% and +49% takes 28.3% to 24.1%, the worst month 2.7–3.6% of par (p95 5.8–7.1), margin peak 7.4% of par. The bear side lands where the paper does and the bull side does not, because the engine’s proxy delta peaks near 0.21 coins against the paper’s 0.30; the test asserts the shape (the spread narrows, the bear side inside 8–20%), not the paper’s numbers.
  • Coin seat: futures at 80% of the coin delta, 4% for 24 months then 10% (Table 7c) and at the full delta (7a); rolled 12-month calls at 1.475× (7b); the 5-year dealer call and half of it (Table 7). The previous section’s structures as legs, reproduced exactly, their paper figures there; on the flat book the 80% leg holds 1.150 coins per coin with a margin peak of 0.350 of par.
  • Call spread, long 1.475× short 2.5× of entry, two years (Table 7d). The paper: 10.7% of spot at 45% IV, 5.7 points of dollar yield on a flat coin. The engine at flat 45% and the paper’s pricing: 10.70% of spot per coin, the short leg (naked) and assigned above 2.5× (the_call_spread_costs_about_what_the_paper_says).

The split and the pair are single structures. The split (§6.3) is one Legs with two legs: futures at 80% of the coin delta and rolled 12-month calls at the Purchase Price on ShareOfCoinDelta(0.2); the paper puts those calls at 0.03–0.09 coins over the life, lowering the 5th percentile by about 0.03 coins. The pair (§6.5) is the quarterly loss-line ladder and the call spread in one structure, both seats at once: the paper’s 15% of par, the dollar seat at 7% on a flat coin, the coin seat below one coin from +22% up. The engine holds the pair’s premium to the sum of its two structures’ within a part in a million.

The shelf rows. hedge.rs::benchmarks_with_shelf fills Benchmarks.shelf with six ShelfRows — the shelf, the paper’s Table 18 (§13.1) — on the same coins, months and marks as spot, ShelfParams defaulting to the paper’s rates (2% coin lending, 30% of the coins overwritten, 10% out, 4% basis, 7.65% on dollars lent against coin). Coin lending grows the count at the rate over the months held. The covered calls write a one-month call each month on the share of the coins then held, at the mark or 1.1× it, premium and settlement both in coin at the month-end mark. Cash-and-carry basis and dollars lent against coin are dollar assets, read on the coin seat the paper’s way — the dollar multiple \((1+r)^{\text{years}}\) over where the coin ends, \(S_T/S_0\) — so a 4% basis is 1.24 coins on the paper’s flat median and 0.29 at the 5th percentile, losing coins on 41% of paths. The tie-out (the_shelf_ties_out_to_table_18, 48 seeds × 10 Agreements on a median-flat 40% GBM, flat 45% surface, 4.5%) — engine | paper, as dollar median / dollar 5th / coin median / coin 5th / paths losing coins: hold the coin −1.6 / −21.4 / 1.00 / 1.00 / 0% | −0.4 / −25.4 / 1.00 / 1.00 / 0%; BPA at par 15.1 / 8.2 / 1.26 / 0.85 / 21% | 14.6 / 4.1 / 1.35 / 0.75 / 21%. The paper’s Table 19 runs the same holdings by drift, and the engine’s drift sweep is where a desk reads that.

Two books, two hedges — the coin seat

The same paper can be held by a Holder whose unit of account is the coin. That is the coin seat: the brain’s risk paper (v3.6–v3.9) brought into the engine as coin.rs and three variants of hedge.rs::HedgeSpec (CoinDeltaFutures, RolledCalls, DealerCall); the tests are tests/coin.rs. Historical worked examples below quote the private risk paper, not the current workspace defaults or forecasts. Its figures are the author’s recorded runs (\(S_0\) $78,000, 1,500 paths per drift unless said otherwise); the engine’s are from tests/coin.rs.

The coin flows (0.6.0). Each Agreement represents one underlying Bitcoin. The Coin allocator funds its actual dollar purchase at that Agreement’s own entry price: if Agreement a originates in month m_a, its dollar purchase is Q_a and its entry strike is K_a, the gross funding is Q_a / K_a BTC. A par purchase has Q_a = K_a and therefore uses exactly one BTC per Agreement, including with dispersed entry prices. An 80%-of-entry purchase uses approximately 0.8 BTC and a 120% purchase approximately 1.2 BTC, subject to cent rounding of the USD purchase. The dollar ledger is unchanged.

Holder dollar receipts I_m and hedge economic flows H_m use the monthly conversion price S_m. Thus btc.rs::paper_coin_flows, also used by coin.rs::coin_flows, supplies

\[ c_m^{\mathrm{out}} = \sum_{a:m_a=m}\frac{Q_a}{K_a},\qquad c_m^{\mathrm{in}}=\frac{I_m}{S_m},\qquad h_m=\frac{H_m}{S_m},\qquad F_m=c_m^{\mathrm{in}}-c_m^{\mathrm{out}}+h_m. \]

The legacy net outcome per BTC of Agreement purchases uses gross purchases as its denominator:

\[ M_{\mathrm{paper}}=\frac{\sum_m c_m^{\mathrm{in}}+\sum_m h_m}{\sum_m c_m^{\mathrm{out}}}. \]

The BTC cash-flow IRR solves sum(F_m × (1+i)^(-m)) = 0 and reports (1+i)^12 − 1; the modeled hedge costs and signed horizon value enter once. With several roots, the hedged rate nearest the unhedged Coin rate is selected, with the ambiguity reported. Equal USD and BTC flow vectors up to a constant scale have the same IRR; different entry and conversion prices need not produce that condition.

The legacy Coin and USD multiples use different hedge-cost conventions: a negative hedge flow reduces the Coin numerator, while USD gross outflows increase. A 1-BTC purchase, 1.5-BTC receipt and 0.2-BTC hedge cost give a legacy Coin ratio of 1.30×; a receipt/all-outflow ratio is 1.25×. The net-contribution recovery ratio below has another stated denominator. None is a complete-wallet holdings ratio.

Numerical support and interpretation. BTC cash-flow IRR uses the original floating-point BTC equivalents directly in the log-space solver. It does not narrow them to Decimal or replace an unrepresentable flow with zero. Every accepted root must satisfy the solver’s relative NPV residual check on those flows; unsupported inputs report an unavailable reason. The dollar ledger remains cent-exact Decimal. An annualized cash-flow IRR excludes unused BTC reserves and does not describe annual growth of an entire starting wallet; see the workspace measurement guide.

The mass model. coin.rs::MassModel gives the seat’s exposure before the path is known: the survival-weighted expected flows of one Agreement along one price path from a given age, under the engine’s own rules — the config’s hazard and drawdown multipliers, early completion, the conviction rule and the rational boundary, and the stop priced exactly as engine::stop_sale prices it. Mass leaves by completion, early completion or stop; what remains is alive. It is the brain’s bpa_model.run_agreement in Rust (holder_coin there is the same conversion), and it matches the engine’s own mean over 200 seeds of ten Agreements on a flat path within 0.03 coins per coin (mass_model_matches_the_engine_monte_carlo_on_a_flat_path).

The performing delta surface. coin.rs::CoinDeltaSurface estimates dC/dlnS, BTC per unit log-price move, for one performing Agreement. It has a node at every monthly payment age, from origination through the month before the final payment; the value after the final payment is zero. Its log-price grid covers 0.1–10 times entry and expands in 1.5-times steps when needed. Each node values the mass model’s remaining receipts on common random continuation paths, bumps spot by ±1%, and differences:

\[ \delta_{\mathrm{performing}}(S/K,a)=\frac{\overline C(1.01S)-\overline C(0.99S)}{\ln1.01-\ln0.99}. \]

The continuation convention is median-flat lognormal, ln(S_(j+1)/S_j) = σ z_j / sqrt(12). It has zero log drift, not zero expected arithmetic price growth: E[S_t] = S_0 exp(σ²t/2). It is the exposure-estimation convention, not a claim that the research path uses that drift or that the option surface is a forecast. surface_vol supplies σ; the fixed valuation_seed defines the performing node streams. No BTC discount rate is applied.

Actual monthly observations read their own age row, interpolating only in log price. Fractional display ages interpolate between adjacent monthly states; they do not smooth a still-outstanding final receipt over a three-month interval. This corrects the quarterly age approximation used before engine 0.6.1, which could understate sensitivity around payments retained by BTC Now and the final Holder receipt. Existing quarterly node streams are preserved; additional monthly nodes use their own deterministic seed namespace. Stochastic valuation and interpolation in price remain approximations, not a guarantee of full protection.

Every continuation includes the entire configured stop-sale tail (coin.rs::node_value uses MassModel::slots): a 90-day lag needs three extra months. A short supplied path extends flat without advancing the sale or receipt date. The default 18-day tail retains the previous canonical random stream. surface_domain records extrapolated columns; these remain model values. Known stops use the separate lifecycle calculation below, including sale cash that arrives after the contractual term.

Known stops and monthly receipt timing. The shared coin.rs::agreement_coin_delta reads the actual lifecycle. A stopped Agreement keeps its known paid count, remaining schedule, refund waterfall and residual sale lag; it is not put back into a performing continuation. A sold receipt is observed after that month’s booking and BTC conversion, so it has no remaining Coin receipt exposure. series.rs::delta_series_of and Coin-delta hedge sizing use this same reader. price_exposed_agreements counts performing plus stopped-unsold Agreements and is the denominator for a per-Agreement delta display.

For a known stop, let S be current spot, X the fractional-month sale spot, Y the receipt month’s conversion spot, q = exp(−haircut) × (1 − sale_cost_bps/10,000), R the remaining schedule, P the full schedule and η the servicing fee. The fee-net BTC-equivalent receipt is

\[ C_{\mathrm{stop}}=(1-\eta),\mathbb E!\left[\frac{qX-(qX-R)^+ +(qX-P)^+}{Y}\right]. \]

This is the same refund waterfall: the Holder first receives up to the remaining schedule, the Buyer is refunded up to amounts paid, and excess above the full schedule returns to the Holder. One underlying BTC does not imply one BTC of price sensitivity.

For residual sale delay n+f months (n an integer, 0≤f<1), log-linear sale interpolation and the same median-flat continuation give

\[ a=\operatorname{Var}\ln(X/S)=(n+f^2)\sigma^2/12,\quad b=\operatorname{Var}\ln(Y/S)=\lceil n+f\rceil\sigma^2/12,\quad c=\operatorname{Cov}(\ln(X/S),\ln(Y/S))=(n+f)\sigma^2/12. \]

The analytic sensitivity used by coin.rs::stopped_coin_delta, with Φ the standard normal CDF, is

\[ \delta_{\mathrm{stop}}=(1-\eta)e^{b/2}\left[\frac P S\Phi!\left(\frac{\ln(qS/P)-c}{\sqrt a}\right)-\frac R S\Phi!\left(\frac{\ln(qS/R)-c}{\sqrt a}\right)\right]. \]

A zero strike contributes zero. At zero volatility, the implementation uses the piecewise derivative of the waterfall, with a midpoint convention at a kink. The formula is checked against independent Gaussian integration of actual sale/refund/conversion cash, and the generated surface is checked against the certain-stop analytic case at 18-, 45- and 90-day lags, plus a 120-day internal stress outside the API’s accepted 0–90-day range (coin.rs::allocator_stop_tests).

Two deterministic checks illustrate the units. An early stop with no Buyer refund returns 0.9975 × 0.95 = 0.947625 BTC under unchanged future spot; its BTC sensitivity to a uniform price move is zero. A final-payment stop with $1,475 still scheduled returns $1,401.25 after the fee. At $59,400 spot its expected receipt is 0.023590067 BTC and its Coin delta is −0.023590067 BTC, even though the contractual term has ended. If the Holder instead receives sale surplus after a fixed Buyer refund, Coin delta can be positive and a signed hedge can be short. These are state-dependent claims, not a blanket one-coin hedge rule.

The futures hedge. HedgeSpec::CoinDeltaFutures { hedge_ratio, basis_locked_months, basis_locked_rate, basis_after_rate, rebalance_months, surface_vol } uses the lifecycle-aware expected BTC receipt sensitivity. For each price-exposed Agreement at month \(m\), aged \(a\), the signed position is

\[ h_m = \rho \cdot \big(-\delta_{\mathrm{state}}(S_m, a)\big) \ \text{coins of futures}, \]

\(\rho\) the hedge ratio, reset at every rebalance; each month the mark \(h_m (S_{m+1} - S_m)\) is settled and the basis \(h_m S_m, b/12\) paid, both posted in dollars at \(m+1\) — the dollar seat reads them through the combined flows, the coin seat converts them at \(S_{m+1}\). The basis lock sets \(b\): basis_locked_rate for the first basis_locked_months of each Agreement’s life, a dated listed contract held to expiry, then basis_after_rate on the rolls; the runs use 4% for 24 months, the listed December contract, then 10%. Negative Coin delta normally produces a long position; positive Coin delta produces a short one. The actual lifecycle and selected rebalancing rule determine when it changes. The result reports gross notional and worst cumulative futures loss as shares of gross USD purchases. This legacy branch has no initial-margin input; a generic futures leg is needed to supply margin and funding assumptions. On a flat path the hedge pays only the basis; on a path that doubles the long gains dollars and the seat keeps more coins (coin_delta_futures_on_a_flat_path_pay_only_the_basis). The risk paper’s basis lock (run_v38_basis_lock.py, 1,000 paths, flat coin, log of 2026-09-04): 4% for \(L\) months then 10 / 15 / 20% gives 0.99 / 0.86 / 0.72 coins per coin with no lock, 1.10 / 1.05 / 1.00 at 24 months, against 1.15 at 4% throughout — most of the notional sits in the first two to three years because the delta does.

The hedge ratio. Compare several ratios on matching market and Buyer paths, holding the valuation seed and other assumptions fixed. More coverage can reduce one exposure while increasing basis, execution and funding needs. The private risk paper’s sampled ratios were exploratory cases, not universal settings; their numerical claims are not retained as live model guidance without a complete saved configuration. A sampled p5 or minimum does not guarantee a floor. Report sample size and uncertainty even when no sampled path loses BTC. Annualizing a legacy purchase-denominator multiple is not BTC cash-flow IRR or wallet CAGR.

Rolled calls and the dealer call. HedgeSpec::RolledCalls { strike_pct_of_entry, tenor_months, coverage, years_limit } buys a call at strike_pct_of_entry times each Agreement’s entry, at origination and again at expiry while the Agreement remains on the book and within the optional years limit. Each tenor is cut to the remaining Agreement term. Premium uses the supplied surface and option-pricing rate; intrinsic settlement at expiry and any signed open value at the horizon remain distinct. The Coin view converts each component at its modeled month’s price (rolled_calls_and_the_dealer_call_price_off_the_surface). It is run_v39_listed_hedges.py::rolled_calls — premium expressed in BTC equivalents at the roll date, sized to the surviving mass, Black-Scholes at 45% IV and 4.5% — with the engine’s surface in place of a fixed IV. HedgeSpec::DealerCall is one call per Agreement for the whole tenor, 60 months as the reference, its leg labelled a dealer quote: the model does not verify the availability or executability of a matching listed instrument.

The drift sweep is the exhibit a coin mandate reads. coin.rs::drift_sweep re-runs the config under PathMode::Gbm at the sweep’s vol for each median annual return \(r\), with

\[ \mu = \ln(1 + r) + \tfrac{1}{2}\sigma^2 , \]

so the median price path grows at the specified rate. The sweep uses matching seeds throughout and reports both reporting units. Expected price growth, median price growth and the Agreement’s BTC return are distinct. Keep volatility, shock/bump overlays, surface, costs and the valuation seed attached to each completed comparison. Historical private-paper grids are examples of the method, not evidence of a fixed growth threshold at which this model always gains or loses BTC.

What the two books are. Dollar and Coin are different reporting objectives on the same Agreement economics. A fixed dollar receipt has price exposure when read in BTC equivalents, so a Coin hedge may go in the opposite direction from a USD hedge. The size and sign must follow the selected book and metric rather than a universal one-coin rule. Both objectives remain exposed to Buyer outcomes, hedge costs, model assumptions and funding requirements. Neither has a guaranteed return floor.

Running it — the monthly report, the mark, the margin, one delta, the rule

A Holder’s monthly report explains what the sleeve earned and which line moved. series.rs::hedge_series keeps the seeded paths, monthly book, cash, values and P&L, with pointwise bands across the sample and one complete selected path. objective: "coin" selects the lower-median economic BTC gain; Dollar selects the lower-median finite USD IRR, falling back to USD multiple when no IRR exists. The response records selection.metric and selection.seed. An explicit selected_seed replays that member of the requested sample. Pointwise percentile bands are not individual paths. Matching inputs, costs and the explicit valuation_seed preserve the overlay’s lifetime economics. This is the monthly report.

The mark. The purchase-yield mark is an attribution convention calibrated to purchase cost, not an independently validated market-participant fair value: \(y\) is the monthly rate at which a fresh Agreement’s expected flat-path flows under the config’s own rules equal its purchase price (series.rs::purchase_yields, solved once at the start price), and \(V(t, S)\) is the expected remaining Holder flows of an Agreement aged \(t\) along a flat continuation of the spot \(S\) — coin::MassModel::flows, every behavioural rule and the September stop the config’s — discounted at \(y\). A stop in transit is marked at the proceeds the model expects at the month’s spot (coin::MassModel::stop_value: the coin sold at the spot less haircut and sale cost, the refund, the fee), discounted at \(y\) until they land and re-marked each month — never at the recorded proceeds, which the engine prices between the next two months’ marks, so a month’s report needs only the month’s spot (the_month_is_reported_on_the_month_spot_alone: bump the path from month 30 and months 0–29 report identically, mark and every bucket). The futures carry no mark of their own — they are cash-settled monthly — and the open options carry the option mark below, since the follow-up audit of 6 September 2026. An origination enters at cost — \(V(0, K)\) at the Agreement’s own strike \(K\) is the purchase price to the cent, and the month-0 mark equals the purchases (the_month_zero_mark_is_the_purchase_price_on_a_flat_path) — so no day-one gain is booked; when the strike is not the month’s spot (intramonth strike dispersion, a held strike) the fresh mark \(V(0, S_m) - V(0, K)\) is a day-one move and is booked as price, the residual staying the cent (a_dispersed_strike_enters_at_cost_and_its_day_one_move_is_price), and that is why this and not the risk-neutral fair value is the reporting mark: at the funding rate the base configuration is worth $73,662 per Agreement against $61,336 paid, and a 2,010 bps gain on signing day is a valuation, not a month’s result. The risk paper’s Section 14 marks the same way, at the purchase yield. And \(y\) is not the schedule’s rate: on the base configuration the test prints 15.29% a year against the completing schedule’s 13.97% (schedule_yield_annual, reported beside it), because at par a stop sells a coin worth most of the remaining schedule at once — fewer dollars, sooner.

The identity. Every month, on every seed and on the mean band, the attribution is

\[ \text{total} = (\text{in} - \text{out}) + \Delta,\text{mark} + \Delta,\text{option mark} + \text{hedge cash} = \text{carry} + \text{price} + \text{stops} + \text{early} + \text{hedge mark} + \text{hedge settlement} + \text{basis} + \text{option value} + \text{residual}, \]

with hedge cash the option premium and settlement plus the futures mark and basis, and the option mark every open option’s remaining-life value on the surface at the month’s spot (series.rs::Book::option_mark, since the follow-up audit of 6 September 2026); the option value bucket is its change plus the settlement cash of the positions closed that month, so a bought call is no longer a loss of its premium on the day it is bought. Per Agreement, with \(d = 1/(1+y)\) and \(S_m\) the month’s spot,

\[ \text{carry} = y,V(t-1, S_{m-1}), \qquad \text{price} = (1+y)\big(V(t-1, S_m) - V(t-1, S_{m-1})\big), \]

the book rolling forward one month at \(y\) first, then re-priced at the new spot at the same ages — the factor is that order (carry, price). A stop in transit adds its re-mark at the month’s spot to the price line, and an origination adds \(V(0, S_m) - V(0, K)\). Stops are realised against the prior: an Agreement that stopped this month books its cash (the payment plus the proceeds expected at the month’s spot, discounted at \(y\) from their landing month) less \((1+y),V(t-1, S_m)\), and the month the sale cash lands books the recorded proceeds against that expectation at the month’s spot — the sale’s interpolated spot against the month-end’s; a survivor books the stop the model expected and released, \(q,\big(V(t, S_m) - \text{stop value}\cdot d^k\big)\), \(q\) the stop mass at age \(t\) and \(k\) the posting lag. Early completion is the same pair with the payoff. Under the config’s own hazard the stops bucket averages to zero over a book. The hedge has four attribution buckets: hedge_mark carries futures mark cash; the attribution field hedge_settlement carries premium and settlement cash excluding option settlements already assigned to option_value; basis carries futures basis, trading costs and margin financing; and option_value carries the change in Book::option_mark (every open position at Black-Scholes on the surface at the month’s spot with its remaining tenor, the premium’s own pricing, a written option negative, a position still open at the last month staying in the mark at the signed value included once in the overlay’s economic terminal outcome, not settled cash) plus the settlement cash of the positions closed that month, so a termination or expiry is booked once. The report is total P&L on marks, with the cash beside it: the paper at the purchase yield and the options at their remaining life are marks, hedge_premium, hedge_settlement, futures_mark and futures_basis are the month’s cash, and total_pnl is the first with the second inside it — a reader who wants the month’s cash alone adds the four cash buckets to in − out. tests/audit_2026_09_06.rs pins it on the audit’s own case: a bought 24-month call on a flat $60,000 path at 43% (premium $6,834.90) reads total_pnl[0] 0.00 — the month-0 option mark offsets the premium to the cent — and month 1 = 2,244.58 of carry − 288.76 of decay = 1,955.82; a written call’s month 0 is 0 too (finding_3_a_bought_call_is_an_asset_at_inception_and_decays_after, finding_3_a_written_call_is_a_liability_at_inception), and finding_3_the_identity_holds_with_the_option_bucket_on_a_stopping_book holds the identity with the bucket in it. Before the audit the options were cash alone, so total_pnl[0] read −$6,834.90: paying the model price for an equally valuable asset booked as a loss. the_identity_holds_every_month_on_a_bridge_and_on_gbm (8 seeds, 240 Agreements, futures and put ladder, bridge and GBM) holds the gap under \(10^{-6}\) of par every month and measures it at \(2 \times 10^{-15}\). On a riskless flat path every bucket but carry is zero and the carry sums to the flat-path markup exactly, $90,695 on four Agreements. The price line is small — a fixed dollar schedule is price-blind except through the masses and the sale: 0.24 coins of the month’s move per Agreement at signing, the dollar seat’s delta, under 0.5% of par at ±3% a month — and the September stop’s surplus makes every expected stop a long call, and a stop in transit rides the line as a whole coin for its ~1.6 months (hedged the same months): at +10% a month it peaks at 9.6% of par in month 50.

The residual is what no formula claims: the fee’s cent rounding against the model’s unrounded net payment, the conviction rule’s memory (the model restarts each month with no streak), a completion’s last payment against its discounted mark, and at origination the cent between \(V(0, K)\) and the purchase price. Measured, not assumed away: at most \(9 \times 10^{-8}\) of par across the identity test.

The margin path. The treasury posts the futures’ cash, and the variation call is what it posts:

\[ \text{call}_m = \max\big(0,, -(\text{futures mark}_m + \text{futures basis}_m)\big), \]

the month’s futures cash floored at zero — a received basis is not a call, and a flat path with a zero basis posts nothing (variation_calls_are_non_negative_and_zero_on_a_flat_path_with_zero_basis). A call is met inside a month the engine cannot see, so the intramonth proxy re-reads the mark on the paper’s 18-day sigma: the month’s log move \(r\) scaled by \(k = \sqrt{18/30.4375} \approx 0.769\), the mark multiplied by \((e^{kr}-1)/(e^{r}-1)\), plus the basis, floored at zero. A proxy: a typical 18-day move inside the month, not the path’s own worst point; the month-end mark is the call actually settled. The margin buffer is over the seeds as shares of each seed’s par: the worst single call at p95 and p99, the deepest point of the cumulative futures cash at p95, and the month of the worst call at the median. margin_balance is the initial margin, charged only when a leg’s margin_funding_rate is set, at rate/12 on the basis channel (at 6% the test charges $2,806 over the life on a $44,521 peak). The test’s figures, base configuration, the futures preset, 8 seeds: on GBM the p95 worst call is 3.83% of par, the p99 3.88%, the p95 worst cumulative 4.92%, the worst month 20.5 at the median; on the bridge 4.16%, 4.20% and 5.08%; the median seed’s peak call $349,793 against an intramonth proxy of $261,053.

One delta for a stated purpose. The delta series carries three readings on the same selected path. DollarDelta sizes from the embedded-ladder proxy (hedge::proxy_delta_coins), a USD sensitivity expressed in BTC position units. CoinDelta uses coin::agreement_coin_delta, the lifecycle-aware remaining BTC-receipt sensitivity described above. It uses the performing surface while payments continue and the known-stop waterfall while collateral awaits sale. Coin delta may be negative, zero or positive; it need not vanish at the contractual term if a stopped receipt is still pending. The per-Agreement display divides by price_exposed_agreements, including performing and stopped-unsold Agreements, rather than only the performing count.

The optional Greeks (exposure::greeks, eight valuation seeds every third month when include_greeks is set) bump and revalue the USD Agreement book and remain a separate measure. The quantities cannot be compared as interchangeable coin holdings. Keep the realized-path seed, valuation seed, selected objective and coverage rule attached to every sizing comparison.

The rule. The rebalancing rule is a parameter of the futures leg — Calendar { months }, a delta band, PriceMove { pct } — with a minimum trade and a lot (CME is 5), and its cost is measured rather than assumed: series.rs::rebalance_policy_comparison runs one structure under each policy on the same seeds at the desk’s own execution cost (the request’s exec_cost_bps, bps of premium on every option leg; a futures leg carries none, so a structure of futures alone reads the same at any value of this premium-cost input; its own futures_cost_bps still charges futures turnover) and reports turnover in coins and the trade count; every row’s result.exec_cost_bps and the response’s notes say the cost used, and the run page’s exhibit 05 says “net of N bps”. Until the follow-up audit of 6 September 2026 (“Costs”) the comparison priced every policy at zero by convention, so a structure with an option leg compared its policies on a different cost basis from the run above the table; the_policy_comparison_is_priced_at_the_desks_execution_cost pins the fix — a futures leg beside a bought put ladder, monthly against quarterly, hedged median 22.19% at zero and 22.18% at 50 bps, each row equal to the overlay of the same structure under the same policy at that cost. The page subtracts each hedged median from the monthly reset’s. The test’s one cohort on a moving path: monthly, 16.02% hedged median, 3.39 coins turned over, 53 trades; a band no drift can cross, 16.84%, 3.32 coins, one trade; yearly, 16.21%, 5 trades. The minimum trade thins the reset monotonically — 55 trades at zero, 49 at \(10^{-6}\) coins, 5 at 0.05 — and lots of five keep the identity conserved. A policy’s cost is the path it holds: a held position is not the melting one.

To reproduce: POST /api/forwardflow/hedge_series with the cockpit’s BASE_CONFIG and one structure (the loss-line ladder when omitted; 16 seeds by default; include_greeks for the third delta); POST /api/forwardflow/rebalance_policies for the table. cargo test --release -p forwardflow --test series -- --nocapture prints every figure above.

What each instrument supports

The model audit of 6 September 2026 (M10) asked for one table saying, per instrument, which of the desk’s measures the branch actually computes — so that the label “total P&L” on the run page never implies a capability a branch lacks. This is that table, read off hedge.rs and series.rs for engine 0.6.1, spec v1.14. Yes means the figure is computed from the instrument’s own contract and validated against an independent book (the Black-Scholes reference to \(1.5 \times 10^{-11}\), the stop waterfall to the cent, the monthly identity to \(2 \times 10^{-15}\) of par); proxy means a stated approximation stands in for it; no (stated) means the desk does not compute it and says so in the result’s notes, and a Holder comparing structures must supply it from outside.

Structure (HedgeSpec)Cash flowsContractual maturityInterim mark (monthly)Execution costMargin / cash requirementsExposure close-outAgreement + modeled hedge outcome
Put ladder PutLadderyes — premium out at inception, intrinsic in at expiryyes — the contractual tenor; open at the horizon, marked thereyes — Black-Scholes on the surface at the month’s spot (option_mark)yes — bps of premiumno (stated) — a bought option posts none; none modelledno (stated) — held to expiry; a stop leaves the put in place and its payoff countedyes — Agreement and modeled hedge only; reserves are excluded
Put spread PutSpreadyes — both legs’ premium and payoffyesyes — both legs markedyes — on both legs’ gross premiumno (stated) — the written put is naked on the paper; no margin modelledno (stated) — held to expiryyes
Rolling puts RollingPutsyes — each roll’s premium at the then-spot, each expiry’s payoffyes — each put its tenor; a roll only while the Agreement performsyesyesno (stated)proxy — no fresh put after the Agreement leaves (rolls_at); the live put runs to its expiryyes
Perpetual delta hedge PerpDeltaHedgeyes — mark, funding and margin carry monthlyno (stated) — perpetual; runs while the Agreement is price-exposedyes — cash-settled monthly; the mark is the cashno (stated) — no trading cost on the perpetual; the execution cost is bps of premiumyes — initial_margin_pct on the short notional, carried at the funding rate; the variation call in the seriesyes — reset to coverage × the proxy delta of the price-exposed book; closed the month the sale’s cash lands (M04)yes
Variance swap VarianceSwapyes — settlement on the full window at expiryyes — the contractual tenor, rolled through the horizon (M10)yes — remaining-life value, realised months as they were, unrealised at the strike (variance_swap_mark)yes — bps of vega notional at each inceptionno (stated)no (stated) — the swap runs its tenor whatever the book does; it is not sized to the exposureyes
Cross-book collar CrossBookCollarproxy — the puts’ flows and the calls’ premium are the paper’s; the call’s assignment is the free coin’s give-up, on its own leg, never posted to the paperyesproxy — the puts are marked; the written calls are not (the free coin’s business)yes — on both sidesno (stated) — covered by the free coin; no marginno (stated) — the calls roll through the horizon on the free coin whatever the paper doesno (stated) — the free coin’s value, its opportunity cost and the call liability are outside the hedged distribution; the subsidised paper is not the whole strategy
Coin-delta futures CoinDeltaFuturesyes — mark and basis posted at \(m + 1\), in dollars or coins by seatproxy — the basis lock (basis_locked_months) is a rate schedule standing in for a dated contract held to expiry; no expiry is simulatedyes — cash-settled monthlyno (stated) — no trading cost on this branch; use a Legs futures leg with futures_cost_bpsno (stated) — no margin input; margin_peak_of_par is zeroyes — sized from lifecycle-aware exposure at calendar resets; a position can remain after the Agreement exits until the next reset; surface_domain reports the delta surface domainyes
Rolled calls RolledCallsyesyes — each call cut to the term, re-bought at each expiry while the Agreement performsyesyesno (stated)proxy — no fresh call after the Agreement leaves; the live call runs to its expiryyes
Dealer call DealerCallyesyes — one call for the whole tenor; open at the horizon, markedproxy — the model’s value on the surface; no dealer bid exists for a five-year callyesno (stated)no (stated) — held to expiry; no unwind is pricedyes
Leg: futures Legs › Futuresyes — mark and basis at \(m + 1\); a long pays the basis, a short receives itproxy — the basis schedule (locked months, then the after-rate); no expiryyes — cash-settled monthlyyes — futures_cost_bps on the notional of every trade (default 0), on the basis channelyes — initial_margin_pct, margin_funding_rate, margin_peak_of_par, the variation and intramonth calls and the buffer; no variation-margin cash account, venue haircut or forced deleveraging (stated)yes — the rebalancing policy targets the price-exposed book; under the calendar rule the position is held to its next reset even past the Agreement’s exit unless close_on_exit is set, and futures_retained_agreement_months names the Agreement-months retained — a speculative exposure, a warning when positive (model audit 2026-09-07)yes
Leg: option Legs › Optionyesyesyesyes — bps of premiumno (stated) — a written leg is naked on the paper and its label says so; no marginproxy — inception and rolls only while the Agreement performs; a live option runs to expiryyes

Three sentences the table leans on. The monthly report is total P&L on marksmark_paper, option_mark and the futures’ cash — with the cash reported beside it (hedge_premium, hedge_settlement), and the identity holds bucket by bucket; a “no (stated)” cell means that instrument’s figure in the total is the contract’s cash and mark as modelled and nothing more. Margin and cash requirements are reported (initial margin, the variation call, the intramonth proxy, the buffer) and do not constitute a constrained cash and margin account: available cash, deposits and releases, written-option collateral, venue haircuts and forced deleveraging are Phase 4b and are not in any row above. Close-out follows the one lifecycle: a futures position follows its rebalancing and close-on-exit rules; an option runs to its own expiry, and the unhedged residual (a put still open on a sold coin, a call bought on a coin that stopped) stays in the flows and in the risk desk’s coverage, never dropped. The capability table is the word for this section.

What a desk should read first

Read the selected objective and the actual Agreement/BTC funding count first. For Coin, compare BTC deployed, receipts, net contributions, recoveries and gain before the annualized IRR. Check loss probability and expected shortfall across matched simulations, then inspect an actual adverse path. Price the hedge with the recorded surface, option rate, basis, execution costs and funding assumptions.

Read realized cash settlement separately from signed open derivative value. The legacy payoff_mean_usd includes both, so it cannot be treated as cash collected. Keep USD margin requirements separate from BTC recovery: neither a sensitivity hedge nor a positive terminal outcome proves that a finite wallet can meet interim cash calls.

Reproduce current results with the exact saved configuration, structure, price/valuation seeds and engine identity. POST /api/forwardflow/hedge_research gives matched per-seed outcomes; hedge_series gives the selected monthly path and bands. The wire formats are in The API; the validation procedure is in Verification and reproduction. Historical figures elsewhere in this chapter require their historical configuration and engine version.

Compare hedges across possible outcomes

Open Hedge strategies or Risk analysis, choose up to eight strategies, and run the comparison. Unhedged is always included. Each strategy faces the same Bitcoin path and Buyer outcomes on each seed. A bridge pins the terminal market price; fixed historical/custom/uploaded paths do not sample terminal-price uncertainty. The experiment description above the results identifies that distinction.

Start with 256 simulations to explore. Use 1,024 or 2,048 and Check on new simulations to assess whether the result persists on a separate seed range. At 32 simulations, a 5% tail contains only 1.6 observations of sample weight. More simulations improve sampling precision under the assumptions; they do not calibrate the market or Buyer model.

The primary table compares:

  • Mean NPV: all monthly dollar cash flows, including surviving derivative marks at the horizon, discounted at the chosen effective annual rate. Zero discount gives economic gain, including any signed horizon mark; it is not all realized cash. The approximate 95% error band is 1.96 times the sample standard error; it measures Monte Carlo noise only.
  • Capital loss probability: the proportion of scenarios with negative undiscounted net gain. A 95% Wilson interval remains nonzero even when no loss is observed. Dollar loss uses a half-cent numerical tolerance; coin loss uses 1e-10 BTC.
  • Average loss in the worst 5%: expected shortfall of nonnegative capital loss, max(0, −net_gain_usd). Sort losses from greatest to least and average exactly 5% of sample weight, using a fractional boundary observation. A profitable tail is shown as zero loss. The Coin reading applies the same rule to its dated BTC flows: Agreement funding at actual entry and receipts/hedges at monthly spot.
  • Cash needs: the 95th percentile and maximum of the initial cash required on the monthly path. Hedge cash excludes Agreement purchases and receipts; total cash includes them. Receipts remain available. At each month, required cash is supplied futures margin minus cumulative realized net cash, floored at zero. Derivative marks at the final horizon are excluded from cash available to meet margin. The optional cash allowance reports how often this requirement exceeds the amount entered.
  • Paired improvement: the proportion with higher NPV than unhedged on the same seed, plus the mean paired NPV difference. This is not the difference between separately ranked percentiles.

A cash allowance is a monthly liquidity screen. It does not change trades or simulate forced closure. Daily calls, short-option/swap collateral, venue rules and intramonth timing remain outside this calculation. A strategy can have attractive lifetime results and still require unaffordable interim cash. Cross-book collars are excluded because their free-coin capital is outside the Agreement ledger.

Expand Sampling stability, coin outcomes and IRR to compare the full sample with its first half. This is a diagnostic, not a convergence certificate. IRR statistics retain the missing and ambiguous-root counts. Paper exposure shows distributions of peak unrecovered capital and schedule shortfall; these are paper exposures, not net derivative Greeks or expected losses.

Run stress on matching seeds changes either the surface volatility level, option execution costs, or the lifetime Buyer default prior. The saved inputs and changed assumption remain visible. These are separate conditional experiments, without assigned probabilities; do not pool them into the primary distribution. Futures basis, trading costs, margin and lifecycle remain the selected strategy’s inputs. These simple stresses do not model a jointly stochastic future volatility surface or basis process.

Inspect an actual adverse path replays the seed with the greatest dollar loss, greatest hedge cash need or greatest NPV deterioration. Export all samples saves every scalar observation, exact batch request, assumptions, seed range and engine/data identity. A changed engine is refused during a batch run or saved-path inspection. Runs continue while the page stays open, in sequential batches of at most 32; they are not durable server jobs. Cancellation preserves the previous completed result.

Implementation: forwardflow/src/research.rs::compare_with_cancel runs one immutable Agreement book per seed and applies every selected overlay to it. research.rs::observation defines the cash/NPV calculations; web/app/forwardflow/research.ts defines ES, quantiles, Wilson intervals and paired aggregation. Batches retain raw observations, never averages of batch percentiles. Agreement ledger and trading-rule calculations remain those described earlier in this chapter. Automated reconciliation tests compare these outcomes with the original hedge engine and verify batch splitting and horizon-mark handling.

The coin-delta estimation seed (valuation_seed, API default 42) stays fixed across batches, stress cases and separate realized-path samples. Changing it changes the estimated trading rule; it is not changed automatically when config.seed advances. The browser records both seeds in every exact request.

The Coin holding benchmark

Dollar asks how much USD the Holder can gain or lose. Coin asks whether the Holder recovers more BTC than retaining the BTC contributed to the strategy. These are reporting objectives on the same Agreement books, paths and trades; switching objective does not change raw economics.

The supported Coin convention funds each Agreement purchase at its own actual entry price and converts Holder receipts and hedge cash at the monthly simulated spot. With the dated BTC economic flow F_m defined above:

  • Contributed BTC: sum(max(−F_m, 0)).
  • Recovered BTC: sum(max(F_m, 0)).
  • Holding benchmark: retain those same dated contributed BTC through the horizon.
  • BTC surplus over holding: recovered BTC minus contributed BTC, equal to sum(F_m).
  • BTC shortfall: max(0, −surplus). Event counts use a 1e-10 BTC numerical tolerance; tail loss averages exactly the worst 5% of sample mass. No USD discount rate is applied to BTC.

Netting occurs in BTC after applying the appropriate funding/conversion price to each component. It is no longer correct to net all USD cash and divide that net by monthly spot when Agreement entry prices differ. Receipts can offset same-month purchase funding or hedge costs; later negative months require additional contributions. Each strategy has its own matched contribution schedule, so this is not a common initial-budget comparison. Reserves, borrowed capital, conversion trading costs and external free BTC are outside this convention. Gross Agreement funding is one BTC per originated Agreement at par; net contributions can be smaller when earlier receipts are reused. The legacy purchase-denominator multiple is separate from the recovered/contributed ratio.

Surviving derivative marks enter the final month’s economic flow at horizon spot. They can increase economic recovery or, for a negative net month, add a hypothetical terminal contribution. coin_holding.realized_cash_contributed_coins and realized_cash_recovered_coins exclude marks; horizon_mark_coins reports the signed BTC-equivalent mark separately. None of these fields claims a live wallet balance or execution of BTC conversions. Monthly replay supplies the actual spot vector so every amount can be reconciled.

The USD cash screen retains receipts in dollars and reserves supplied futures margin. Coin economic outcomes instead value monthly BTC conversion. These separate conventions cannot establish that the same portfolio meets cash calls: converting receipts may remove dollars needed later. Reserve allocation, borrowing, daily margin and forced closure require a reconciled cash and conversion policy before any joint funding-feasibility claim.

Implementation: btc.rs::paper_coin_flows, btc.rs::observe and btc.rs::holding; ResearchConventions records the policy and ResearchBatch::with_objective selects reporting context. Tests cover nonconstant spot, netted phased contributions, purchase price different from strike, hedge costs, positive/negative horizon marks, unchanged economics across objectives and deterministic batch/replay identity. Version 0.6.0 changes Coin outcomes where actual Agreement entry differs from the monthly price; old monthly-purchase-conversion results must retain their old convention and be re-run for comparison. The underlying USD ledger is unchanged.

Amounts, ratios and settlement

BTC cash-flow IRR remains a useful annual timing measure alongside the amount bridge. One contribution of 100 BTC and recovery of 110 BTC gives 10% total return. With only those two flows one year apart, annualized IRR is also 10%; two years apart it is about 4.88%. Intermediate cash flows can change it again. Costs already included in Holder receipts and hedge flows must not be deducted a second time.

Keep gross Agreement purchases and gross Holder receipts separate from monthly net contributions and recoveries. The economic recovery ratio is recovered_coins / contributed_coins; the realized-only ratio uses the corresponding realized cash fields. Ratios are dimensionless (×); total return is ratio minus one, and a zero denominator is unavailable. These are distinct from the legacy purchase-denominator multiple above. Compute per-path ratios before summarizing their distribution.

The horizon bridge is economic recovery − economic contribution = realized cash recovery − realized cash contribution + signed horizon derivative value. A terminal mark can change a month’s sign, so economic recovery alone need not equal realized recovery plus the mark. Display both contribution sides.

All of these BTC figures are unit-of-account conversions of modeled flows. They do not establish an actual BTC settlement asset, executable FX trade or withdrawable custody balance. For example, a venue’s inverse option can quote and settle in BTC while another instrument uses USD-sized contracts and different collateral. ForwardFlow does not claim to reproduce a venue’s settlement and margin rules. See the measurement guide and release model card.

Release model card

Version 3.3 · 14 September 2026 · engine/API 0.7.0 guided MCP · specification v1.14. Owner: Marc Dumpff. Authorized for research release by Marc on 14 September 2026. Independent model approval has not been recorded.

This card describes the 0.7.0 source contract. The repository’s release record separates local checks, deployment verification and Claude conversation acceptance; authorization alone does not establish which build is running. Read the engine version, build/source revision and historical-data digest from the completed answer or engine health. Preserve the frontend calculation version and exact numerical request too. Older saved results keep their original definitions and versions.

Supported use

Test explicit theories about Agreement cash flows, Buyer behavior and hedges. Dollar reports USD outcomes; Coin reports BTC-equivalent investment flows. BTC cash-flow IRR is a useful annual timing measure and should be read with purchases, contributions, recoveries, total return and the stated holding reference. The model computes conditional outcomes; it does not forecast the market or execute trades.

The funding convention introduced in 0.6.0 and retained in 0.7.0 funds each Agreement at its own entry price: one BTC per par Agreement. It separates contractual BTC, gross deployment, receipt offsets, realized net cash and signed horizon value, and provides unhedged cohort reconciliation. Coin-specific IRR diagnostics remain separate from USD diagnostics. Purchase-normalized multiples use gross BTC funded at entry; net economic and realized recovery ratios are separately named. Holding BTC uses each strategy’s matching dated contributions, not one common starting budget. See Reading BTC performance.

Guided AI analysis

The MCP connection now guides the assistant through the objective, comparison, Agreement-flow capital basis, timing and assumptions. Before calculating, it prepares a brief and asks about missing essentials. Dollar and Coin starting cases use the same versioned presets as the browser. A fixed-wallet experiment remains unsupported. This changes the AI workflow, not the model mathematics or the browser’s starting assumptions. A prepared brief records what the caller supplied; it does not independently establish human approval.

MCP calculation calls require a signed preparation that expires after one hour. To replay an analysis, prepare its saved numerical inputs again. Connecting exposes guidance and an optional starting prompt; it does not install a skill. See the MCP guide.

Corrections retained from 0.6.1

Engine 0.6.1 calculates performing Coin sensitivity at every monthly payment age. It corrects the last eligible historical vintage and refuses an explicit Coin objective on Dollar-only API routes. Detailed simulation responses retain their memory reservation while the response is held or sent. Dollar Risk includes numerical loss frequency and severity, and the Dollar cash multiple explicitly uses gross Agreement purchases. The BTC funding convention introduced in 0.6.0 remains unchanged. Re-run older hedge comparisons to apply the corrected sizing.

These corrections do not complete the audit’s remaining calibration, pricing-domain, execution or production-capacity checks.

Starting assumptions

New workspace cases use illustrative GBM with continuous annual drift 0 and volatility 43%; the median price can fall even when expected price is constant. API/legacy defaults use an endpoint-pinned bridge. Saved scenarios retain their own construction. Neither setting is a calibrated forecast. The exact saved SimConfig, surface, costs, market seeds and fixed valuation seed define an experiment.

Limits that remain

Buyer behavior parameters are priors. Monthly paths and price history do not calibrate how Buyers would perform. Option surfaces and execution, basis and funding assumptions are supplied inputs. Sampled p5, tail loss and minimum outcomes are not guaranteed floors; valid-IRR counts and sampling uncertainty stay visible.

The model does not simulate a complete funded BTC wallet, conversion policy, daily margin, option/swap collateral, forced closure or equal-budget portfolio ranking. The USD cash screen and analytical monthly BTC conversion are separate conventions. Reporting in BTC does not assert BTC settlement. A purchase-yield attribution mark is not certified financial-reporting fair value or auditor acceptance.

Evidence and AI access

Completed evidence retains the objective, conventions, request, seeds, engine/data identity and calculation version. The verifier checks recognized package structure and integrity, not investment suitability. Automated tests check their stated accounting, numerical and lifecycle properties; they do not constitute independent model approval.

Local 0.7.0 checks passed 442 backend tests, including 27 MCP integration tests, plus the frontend, documentation and acceptance-harness checks. These verify the connection protocol and calculation safeguards. Actual Claude conversation acceptance remains pending manual testing; the repository also provides a seven-case conversation evaluation harness.

The AI tool capability table names supported objectives. A single Coin scenario returns the complete research observation; Coin Monte Carlo and hedge comparisons use the research tool. The existing purchase-price solver is USD-only and refuses a Coin objective. See MCP reference.

The repository’s MODEL_CARD.md contains the full release card, evidence paths and approval status. The prior v2.1 card is archived under docs/model-cards/; its dated example outputs are not current workspace results. Limits and assumptions remain part of every research interpretation.

Verification and reproduction

This chapter explains how calculations are checked and how a result can be reproduced. Five layers of verification target different classes of error; they do not constitute empirical validation of the model.

  1. Invariants by construction. The Rust suite asserts identities that must hold for every run. This is cargo test --release --workspace.
  2. Closed forms from outside the engine. The M0 fixtures were computed in exact rational arithmetic before the engine existed; the engine must reproduce them within two cents.
  3. An independent re-derivation. The in-browser auditor independently recomputes selected USD ledger metrics and Agreement contract rows in TypeScript and compares them with Rust. It does not validate every Coin, hedge or Monte Carlo output.
  4. Determinism. The same configuration and seed give byte-identical postings, so every figure is a function of a config hash and a seed.
  5. Named reproduction commands. Every row of the Model Card comes from a runnable example in the crate.

The historical test snapshot on 3 September 2026 recorded 88 passing tests with 2 ignored (the directional checks at the end of this chapter): 42 unit tests inside the forwardflow crate, 32 in tests/invariants.rs, 9 in tests/exposure.rs and 5 API tests in backend/src/api/forwardflow_api.rs.

Vocabulary follows the lock of 2 September 2026: the Buyer pays, the Holder receives, a stop is the Buyer ceasing payments, an early completion is the Buyer paying the remaining schedule in cash. Engine identifiers keep their older names and appear in code font: Owner is the Holder’s ledger account, Obligor(id) a Buyer’s, non_performance a stop, settled an early completion.

What the tests are allowed to assume

Spec §6 (v1.1; §6.3 and §6.5 reworded for the flat fee in v1.5) sets the rule: acceptance rests on internal invariants, not on reproducing the earlier Python engine, which ran on different inputs and is a directional reference only. So the suite has two kinds of test: gates, identities and orderings that hold by construction, where red blocks a commit; and directional checks against memo numbers, which print, never gate, and are marked #[ignore] so a plain cargo test skips them.

Tolerances sit at the top of tests/invariants.rs: money within MONEY_TOL = 0.02 dollars per Agreement lifetime against the unquantized fixtures, IRR within IRR_TOL = 0.0001 (0.01 percentage points), WAL within 0.01 months. Two cents is the rounding the fixtures do not model: convention C2 (cumulative fee rounding) wobbles at most one cent per stream, convention C1 (the final payment absorbing the schedule residual) at most one more.

The invariant suite

Every test in tests/invariants.rs and tests/exposure.rs, grouped by the spec §6 item it serves, then the groups the spec added later: the September stop (v1.5), input validation and the risk desk (v1.6).

§6.1 Ledger conservation

TestGuarantee
conservation_to_the_cent_across_scenariosThree runs (pure contract math; a busy 24-cohort book with every exit kind on a volatile bridge; a 37-month term with the C1 residual in play) satisfy ledger.rs::Ledger::conservation_sum() == 0 and verify_balances(). Money moves; it is never created.

§6.2 Closed-form contract math

TestGuarantee
closed_form_contract_math_matches_m0_fixturesFor every fixture case (36, 48, 60, 84 months; N = 0, 1, 3) the Holder’s total inflow, BTC Now’s take, payback month, WAL and monthly IRR match the fixtures within tolerance. Everything completes; nothing is suppressed.
implied_paper_rate_matches_fixturescontract.rs::ContractTerms::implied_monthly_rate × 12 equals the fixture’s nominal implied financing rate within 1e-5 (16.50% at 60 months).
multi_cohort_metrics_use_gross_flowsA flat 24-cohort book of identical Agreements has exactly the per-Agreement multiple (1.329549 at the fixture price and fee) and a WAL of 31 + 11.5 = 42.5 months. outputs.rs::analyze uses gross flows; netting same-month deliveries against purchases would corrupt both.
agreement_deserialization_rebuilds_scheduleThe schedule is derived, not serialized; an Agreement round-tripped through JSON still knows its payments and fee state.
tiny_strike_schedule_never_goes_negativeA $10 strike over 84 months leaves a non-negative final payment (convention C1), conserves, and completes.
below_par_multiple_gets_a_negative_implied_rateA multiple below 1.0 implies a negative rate; exactly 1.0 implies zero and a straight-line obligation.

§6.3 The fee identity

TestGuarantee
fee_identity_exact_to_the_centWith N = 0 the lifetime fee equals money::cents(fee_rate × terminal) exactly at 36, 48, 60, 84 and 120 months. With N = 1 it equals cents(fee_rate × (terminal − payment 1)): the first payment is never delivered, so it carries no fee. Convention C2 in fees.rs::FeeState::split, made testable.
paper_spread_identityAbove par the Holder’s premium over coin cost is BTC Now’s origination margin: 240 Agreements at 105% give exactly $720,000, par zero, 95% −$720,000; the revenue timeline sums to take plus spread.

§6.4 The N-payments identity

TestGuarantee
n_payments_identityHolder cash with N payments retained differs from N = 0 by exactly \( \sum_{k \le N} \mathrm{PMT}_k ,(1 - f) \), and BTC Now’s take rises by the retained payments less the fee they would have carried, which is exactly what the Holder loses, at every term for N = 1 and 3.

§6.5 Term parametricity

TestGuarantee
term_parametricity_sweep_is_smooth_and_monotoneSweeping 24 to 96 months at the program’s flat 5%: ContractTerms::fee_rate() stays 5% (never term-derived, spec v1.5), the payment falls, the lifetime fee equals cents(0.05 × delivered) at every term, IRR falls at a fixed multiple. No discontinuity at 60.

§6.6 Determinism

TestGuarantee
seeded_determinismTwo runs of the same busy config at seed 1234 serialize to identical posting lists; a different seed does not.
dispersion_is_inert_on_a_flat_pathStrike dispersion scales with realized volatility, zero on a flat path, so the toggle changes no cent and the fixtures hold with it on.
intramonth_dispersion_smears_strikes_and_preserves_the_meanOn a volatile bridge strikes within a cohort differ, the book-mean strike stays within 3%, conservation holds, IRR moves under 2 points.

§6.7 Orderings that are gated

Memo numbers never gate. Orderings that must hold whatever the numbers are do.

TestGuarantee
defaults_reduce_owner_irrA 40% lifetime prior on a flat path, with a 12% haircut pinned so the sales are lossy, costs yield against the no-stop case at the same 105% price, produces stops and a positive shortfall.
rational_mode_suppresses_when_nobody_is_underwaterOn a strongly rising path the rational mode suppresses draws on in-the-money Buyers and the realized stop count falls below the naive mode’s.
conviction_walks_fire_on_deep_drawdownOn a collapse to 30% of entry with X = 50%, Y = 6, all 50 Agreements walk.
settlements_deliver_remaining_schedule_on_upsideOn a run to $200,000 early completions occur, every MakeWholeDelivery posting comes from the Buyer’s account (cash-only, v1.5), and gross deliveries equal terminal less payment 1 per Agreement whatever the mix.
haircut_and_sale_cost_are_live_inputsSame seed and hazards, so the same stop count; Holder recovery falls from frictionless to base to harsh.
crash_with_flow_continuing_beats_single_strikeA permanent −70% from month 1 hurts a book that keeps originating through the crater less than one that bought at the top.
runoff_caps_size_never_rateStopping origination at month 6 shrinks the position and leaves the IRR identical to 1e-9.
drawdown_multipliers_scale_defaults_by_stateThe graded multiplier leaves a flat path unchanged, cuts stops on a rise (×0.5 in the money), raises them in a crash.
take_profit_gate_blocks_settlements_below_all_inA 0% gate silences every early completion on a flat path, where the coin never beats the $88,500 all-in price.
take_profit_gate_opens_above_thresholdThe gate opens once the path clears all-in × 1.10; a gate of 10× never opens.
inverse_price_solver_finds_the_clearing_priceoutputs.rs::solve_purchase_price recovers 1.05 from the IRR that 1.05 produces, clears a higher hurdle lower, and returns None when no Holder inflow exists.
shock_designer_overlays_any_pathpaths.rs::apply_shock: pre-shock path untouched, trough exactly 1 − Z, monotone ramp, a recovery ending at R × the unshocked level.

The September stop (spec v1.5)

TestGuarantee
stop_sale_waterfall_identities_across_regimesOn a rising path with naive stops every stopped row satisfies refund = min(A, max(0, V + A − P)), shortfall = max(0, R − V), surplus = max(0, V − P), delivered = scheduled deliveries after the first N + (V − refund), and returns no coin. All three regimes occur. Book refunds and surplus equal the row sums; every refund is a StopRefund posting to an Obligor; BTC Now’s take is origination plus flow fees and nothing else.
stop_sale_regime_one_exact_centsOne stop, every cent by hand; worked below. Also pins the sale lag: 0 days sells at the missed date’s mark, 30.4375 days at the next, 18 days strictly between.
agreement_table_reports_usd_shortfallOn a crash every row ties (net + fee = gross; capital P&L = net − purchase price; completed rows within 2 cents of expected net), only stops carry a shortfall, a lossy stop refunds nothing and returns no coin, and the book shortfall is the row sum. On a rising path refunded stops carry no shortfall.

Inputs that fail fast

TestGuarantee
absurd_inputs_fail_fast_with_named_inputA 1,700-month term, a 150% annual stop rate, an infinite price, a negative sale cost, a negative multiple, a negative purchase percentage, a 100% prior and a NaN prior each panic naming the spec input (input #6, #7, #1, #16, #5, #6b). The API maps these to HTTP 400.
new_inputs_fail_fast_with_named_inputA 150% shock (#17), an origination stop at month 0 (#18) and a 200-day sale lag (#24) are rejected by name.

The risk desk (spec v1.6, tests/exposure.rs)

TestGuarantee
two_lines_match_the_plan_tableexposure.rs::two_lines reproduces the plan’s §1 table at the base terms: schedule line \( (88{,}500 - 1{,}475,t)/60{,}000 \), capital line \( \max(0,, 60{,}000 - 1{,}401.25,(t-1)) / 0.95 / 60{,}000 \): 1.0526 at month 1, 0.4872 at month 24, zero from month 44. Both fall every month.
capital_line_is_piecewise_when_it_would_cross_the_schedule_lineAt 160% of coin cost the early capital line sits above the Purchase Price and switches to paid + U/(1 − fee), flagged per row; at par it is direct throughout.
greeks_vanish_on_a_riskless_bookWith no exits on a flat path delta, gamma and vega are exactly zero and theta is the positive markup accrual; only the commitment delta is non-zero as of month 0.
greeks_have_the_signs_of_a_short_put_on_a_stopping_bookOn a falling bridge with stops: positive delta, negative vega. (The plan records a positive ensemble vega at the base config, where the Holder is long the surplus; on 3 September 2026 the risk endpoint at BASE_CONFIG and its own bump defaults returned about +$29,700 per vol point, so the −$24,000 in the spec’s v1.6 change block is stale. The test covers the falling case only.)
greeks_are_sensitivities_not_draw_noiseA 1 bp bump flips at most two exits in 240 Agreements; delta at 2% and 5% bumps agrees in sign and within a factor of two.
ladder_and_coverage_tie_to_each_otherLadder totals equal the coverage row for the same month, cells sum to totals, the credit book row is the sum of the vintages.
rational_boundary_walks_the_book_in_a_crash_and_spares_it_in_a_rallyUnder zero believed drift on a collapse to 20% of entry all 30 Buyers cross the frontier at the first unpaid date; a believing Buyer (μ = 25%) on a rally never walks; a coin at 45% of entry at payment 1 walks at payment 1.
bump_overlay_scales_prices_and_volpaths.rs::apply_bump leaves month 0 alone, scales every later price, holds strikes when asked, and with a zero vol factor gives constant log returns.
new_inputs_fail_fast_with_named_inputA negative lattice volatility is rejected as input #25.

Unit tests inside the crate

The frontier tests in boundary.rs are the ones a risk desk will ask about: frontier_covers_every_payment_and_is_visible_from_payment_one (60 rows; at μ = 25%, σ = 41.4% the walk region is visible at payment 1 and sits between 20% and 100% of entry), pessimist_robot_walks_at_par (μ = 0 walks at or above par from the earliest dates), frontier_is_scale_invariant_in_the_strike, higher_sigma_deepens_the_frontier, walk_cost_and_belief_deepen_the_frontier, walk_region_sits_where_the_refund_is_zero (walking beats settling only where the refund is zero, so the crossing lies at most one grid step above the schedule line) and rejects_absurd_params.

The rest check one module each: contract.rs (the schedule sums to terminal at any term; $88,500, $1,475.00 and 16.50% at 60 months; the inverse input 16.50% → $1,475.07), defaults.rs (the baseline curve hits its lifetime target over reachable ages; the 60-month shape matches the actuarial buckets; custom yearly shares realize exactly), paths.rs (bridges pin both endpoints, the bootstrap is seed-stable, replay rebases), fees.rs (an exact lifetime total when the per-payment fee is not a whole cent), ledger.rs (an unquantized amount panics; conservation and balances), money.rs (half-even rounding) and outputs.rs (IRR). The five API tests check that simulate returns the fixture numbers, an invalid config maps to 400 with the named input, Monte Carlo summarizes, the risk endpoint returns the exposure layer with four frontiers of 60 rows, and the price solver recovers about $63,000 at the fixture IRR.

The M0 fixtures

M0_FIXTURES.md and tests/fixtures/m0_closed_form_fixtures.json were produced on 10 July 2026 by a Python generator in exact rational arithmetic (Fraction, no floats in the money). Every aggregate was computed by closed-form annuity identities and again by a month-by-month loop, and asserted equal; IRR and the implied rate were solved by bisection and by Newton to 1e-12. The fixtures share no code with the engine.

The scenario is pure contract math: a flat path, no stops, no early completions, a $60,000 strike, 1.475× (terminal $88,500), a purchase price of $63,000 (105% of coin cost, the default until spec v1.4 moved it to par on 12 July 2026). contract_math_config in tests/invariants.rs reproduces it: a zero-volatility bridge, BaselineCurve { lifetime: 0.0 }, zero early-completion propensity, conviction off, one Agreement.

Why the fixture fee is 0.75% per year times term years

The program’s fee is a flat 5% of every dollar delivered to the Holder (Marc, 2026-08-31; spec v1.5). The fixtures were generated under the July product’s fee, 0.75% per year of terminal value times the term in years: 2.25% at 36 months, 3.00% at 48, 3.75% at 60, 5.25% at 84, 7.50% at 120. Regenerating them at 5% would cost their independence, since the generator would be edited in the same session as the engine. So the fixture configs pin the flat rate per term to exactly the historical value:

servicing_fee_rate: dec!(0.0075) * Decimal::from(term_months) / dec!(12),
purchase_pct_of_strike: dec!(1.05),

Nothing in the engine derives the fee from the term any more; contract.rs::ContractTerms::fee_rate returns servicing_fee_rate unchanged. The fixture tests prove the arithmetic at the pinned rates; term_parametricity_sweep_is_smooth_and_monotone proves the program’s rule at 5%.

Two fixture identities recur in the auditor. Conservation: Holder total plus BTC Now take equals $88,500 at every term and every N. The fee identity: with N = 0 the lifetime fee is fee_rate × terminal to the cent ($3,318.75 at 60 months); with N ≥ 1 it is fee_rate × (terminal − N × PMT). The 60-month, N = 0 effective IRR of 13.3226% matched the Python engine’s contractual bridge IRR of 13.32% exactly.

The exact-cents stop sale, worked

stop_sale_regime_one_exact_cents is the suite’s one stop computed entirely by hand, at the base terms and program defaults: par purchase, 5% fee, 25 bp sale cost, 18-day lag. The path is a custom anchor set, 100% of entry through month 5 and 60% from month 6, extended flat. Conviction is set to X = 0, Y = 1 so the Buyer walks at the first payment date after the drawdown appears.

The Buyer makes payments 1 to 6, \( A = 6 \times 1{,}475 = 8{,}850 \). Payment 1 went to BTC Now; payments 2 to 6 were delivered, \( 5 \times 1{,}475 = 7{,}375 \). Payment 7 is missed, so the exit month is 7. engine.rs::stop_sale prices the sale at \( 7 + 18/30.4375 = 7.59 \) months, log-linearly between the marks at months 7 and 8, both $36,000 here:

\[ V = 36{,}000 \times e^{-\text{haircut}} \times (1 - 0.0025) = 35{,}910.00 . \]

The waterfall (spec v1.5, R-1033) with \( P = 88{,}500 \) and \( R = P - A = 79{,}650 \):

\[ \text{refund} = \min\big(A,\ \max(0,\ V + A - P)\big) = \min(8{,}850,\ \max(0,\ -43{,}740)) = 0 , \] \[ \text{delivered} = V - \text{refund} = 35{,}910.00, \qquad \text{shortfall} = \max(0,\ R - V) = 43{,}740.00, \qquad \text{surplus} = \max(0,\ V - P) = 0 . \]

The fee on the delivery is 5% of $35,910.00, which is $1,795.50, so the StopSaleDelivery posting to the Holder is $34,114.50. Both postings land at month \( \lceil 7.59 \rceil = 8 \). A zero refund posts nothing. Lifetime, the row shows delivered gross \( 7{,}375 + 35{,}910 = 43{,}285.00 \) and a fee of exactly 5% of that, $2,164.25: five payment fees of $73.75 plus $1,795.50, with no rounding residue, which is convention C2 doing its job.

The second half moves the lag on a path with a further anchor at 50% at month 8: a 0-day lag sells at month 7’s mark, a 30.4375-day lag at month 8’s, and 18 days lands strictly between.

The in-browser auditor

The workbench page has a section titled “The audit”. Its button fetches the current run with include_postings: true and hands the response to audit.ts::runAudit, an independent TypeScript implementation that reads only the raw postings, the per-Agreement rows and the configuration, works in exact integer cents (toCents, halfEvenDiv), and uses a scan-and-bisect IRR written from the definition. Each check reports the engine figure, the recomputed figure, the difference and a pass flag.

Below, \( V \) is the recorded sale, \( A \) the payments made, \( P \) the Purchase Price (strike × multiple), \( N \) the payments retained, \( f \) the fee rate.

A. Ledger integrity

CheckFormula
A1Every posting debits one account and credits another, so \( \sum_{\text{accounts}} \Delta = 0 \) exactly.
A2Holder outflow = Σ postings from Owner; A2b: equal to Σ PurchasePrice postings, the only outflow kind.
A3Holder inflow = Σ postings to Owner.
A4Origination take = Σ OriginationFee postings (the first N payments, routed whole).
A5Servicing take = Σ FlowFee postings.
A6Total take = A4 + A5. No stop-sale share reaches BTC Now; the surplus above \( P \) is the Holder’s.
A7The revenue timeline re-derived from fee postings plus per-row paper spread at each origination month, every month within a cent, summing to take + spread within two cents.
A8Paper spread = Σ (purchase price − strike) over the rows.

B. Metrics from postings

CheckFormula
B1Net IRR: monthly net Owner flows rebuilt from postings; the \( r \) zeroing \( \sum_t \text{flow}_t / (1+r)^t \); annualized \( (1+r)^{12} - 1 \); within 0.01 pp (convention C3). If either side finds no IRR, both must.
B2WAL = \( \sum_t t \cdot \text{in}_t / \sum_t \text{in}_t \) on gross inflows, within 1e-6 months.
B3Undiscounted multiple = Σ gross inflows / Σ gross outflows, within 1e-9.
B4Payback = first month \( m > 0 \) with cumulative net cash ≥ 0.
B5@mCash recovery at marker month m (12, 24, 36 when reached) = gross cash through m ÷ total invested.
B6The cumulative net cash series ends at inflow − outflow.

C. Contract math per Agreement

CheckFormula
C1Convention C1: PMT = floor-to-cent(\( P / n \)); the last payment is \( P - (n-1),\mathrm{PMT} \); the schedule sums to \( P \) for every row.
C2Expected net = half-even\( \big((P - \text{first } N)(1 - f)\big) \).
C3Purchase price = half-even(strike × purchase percentage), per row, since strikes differ under dispersion.
C4Convention C2: lifetime fee = half-even(\( f \times \) delivered) exactly, though single postings wobble ±1 cent.
C5Delivered gross by outcome: completed, \( P - \text{first } N \); early completion, \( P \) less the retained payments made; open, the scheduled deliveries so far; stop, scheduled deliveries after the first N + \( (V - \text{refund}) \). Each row is also re-summed from its own postings.
C6Capital P&L = Holder net − purchase price.
C7Book shortfall = Σ row shortfalls.
C8Coin to Buyers = Σ row coin (early completions only; a stop returns none).
C9Σ row fees = Σ FlowFee postings.
C10Σ row origination = Σ OriginationFee postings.
C11Exit split: each of the seven counts re-tallied from row outcomes, summing to the book.
C12Per stop: refund = \( \min(A, \max(0, V + A - P)) \), shortfall = \( \max(0, (P - A) - V) \), surplus = \( \max(0, V - P) \), all exact.
C13Σ row refunds = Σ StopRefund postings; C13b: the book refund total = Σ row refunds.
C14Book surplus = Σ row surplus.

Checks A and B see only postings and headline outputs (A7 and A8 also read each row’s purchase price and strike); checks C see the rows and the configuration, so a defect in agreement_table that the ledger did not share fails C and passes A.

The audit harness

web/app/forwardflow/audit-harness.ts runs the same runAudit from Node against the engine at http://localhost:8080 on four adversarial configurations and exits non-zero on any failure:

ConfigWhat it stresses
base bridgeBASE_CONFIG: 24 cohorts × 10, 43% vol bridge, 40% prior, 5%, par, 25 bp, 18-day lag, dispersion on, seed 42.
everything on, awkward term 481.62× over 48 months, N = 3, 105% price, custom yearly shares, rational mode, conviction, a gated early completion, a 2% haircut, 50 bp, a shock with recovery, an origination stop, seed 1337.
replay from Nov 2013, N = 0, no defaultsHistorical replay from bar 21, no stops, 5% early-completion propensity.
micro-strike, term 2A $3.37 coin over two months with a 30% prior: every cent-rounding edge at once.

With the engine up: cd web && npx tsx app/forwardflow/audit-harness.ts. On 3 September 2026 it printed 32/32, 32/32, 32/32 and 29/29 ties on 240, 70, 240 and 15 Agreements (18,057, 1,041, 18,346 and 68 postings). The count varies because B5 is one check per cash-recovery marker reached.

Determinism

The engine is seeded end to end with ChaCha20. engine.rs::run seeds one generator from config.seed, and that generator draws the path alone. Since spec v1.6 every Agreement owns its own stream, engine.rs::agreement_rng(seed, id), keyed from the seed and the Agreement id through splitmix64; its strike dispersion draw at origination and its monthly stop and early-completion draws come from it, and the monthly draws are taken whether or not the price makes them matter. A price bump or a changed exit elsewhere in the book changes decisions, never the random numbers behind them.

Monte Carlo (outputs.rs::run_monte_carlo) runs \( n \) simulations at seeds \( \text{seed}, \ldots, \text{seed}+n-1 \) in parallel, so run \( k \) is reproducible alone as a single simulate call at seed + k. The API memoizes identical Monte Carlo requests on the run count and the config JSON, which is sound only because of this.

One consequence: the same seed draws differently across engine versions when the draw structure changes. The archived Model Card records that v1.6’s per-Agreement streams moved the single-vintage replay median of the same morning from 38.2% to 47.9%. A figure is reproducible against the engine build and data stamped on it.

Reproducing the archived Model Card examples

The archived v2.1 Model Card §8 names w0108_refresh and the suite; the crate’s other examples print the related sets. These commands preserve the historical research recipe, not a promise that later engine versions reproduce its numbers. From backend/forwardflow:

CommandPrints
cargo run --release --example w0108_refreshEvery §6 row and the sensitivity cells at seed 42: the zero-drift ensembles, the 70% prior, the full-history stress, the paced replay over the 90 feasible starts, the single-vintage replay, and the auxiliary WAL, payback and month-12 cash.
cargo run --release --example deck_reproThe deck figure set with the zero-drift regime pinned by year, plus sensitivity cells.
cargo run --release --example co8_numbersThe trailing-24-month regime family and month-12 collections.
cargo run --release --example trailing_regimeThe unpinned zero-drift bootstrap at current volatility for the trailing 24 and 36 months, printing the series end and bar count first.
cargo test --release --workspaceThe suite.

All four examples build the same SimConfig ($60,000, 1.475×, 60 months, N = 1, 5%, par, drawdown multipliers on, 2.5% early-completion propensity, seed 42) and vary only the regime and the prior. The archived Model Card’s production table is that config at 1 × 200 Agreements over 1,200 paths for the single vintage and 24 × 20 over 600 paths for the paced book. The current risk endpoint (POST /api/forwardflow/risk) returns the exposure layer for any configuration. Its response headers identify the engine build and data; the workspace retains that answer’s identity and request in its completed record and evidence export. A historical specification label alone is not a current result identity.

The tornado golden check

tornado_golden_check_vs_memo_reference is marked #[ignore] and runs only on request:

cargo test --release -p forwardflow -- --ignored golden --nocapture

It builds the memo configuration (zero-drift bootstrap from January 2017, N = 0, 105% price, 40% prior, 24 × 20, seed 42), takes the median IRR over 400 runs, and repeats for five stresses: an origination stop at month 3, a permanent −70% crash over three months with flow continuing, a 90% lifetime prior, a sale at 20% of spot (haircut \( -\ln 0.2 \)), and the behavioral floor (X = 0, Y = 2). It prints each delta beside the July 2026 reference deltas (−0.2, +2.4, −9.6, −4.3, −21.6 pp).

“Directional, non-blocking” means three things. Absolute medians are never compared, because the reference predates the v1.2 residual-coin rule, the v1.4 t+1 clock and the v1.5 stop. Only what should survive engine generations is asserted: the credit stress within ±1 pp, negative signs on the three credit and severity rows, the ordering floor ≫ prior ≫ haircut, the two flow rows under 3 pp, and the crash row negative. And the test is ignored, so no build waits on 2,400 Monte Carlo runs and no memo number can turn the suite red. Its companion memo_neighborhood_zero_drift prints the median against the memo’s 9.41% and asserts nothing.

The test’s comment records the July 2026 observation under spec v1.4: base 11.21%, the 90% prior at −9.96 pp. Run on 3 September 2026 under spec v1.6 it printed the following and failed its first gate:

RowEngine, 2026-09-03Reference, July 2026
base median11.97%9.41%
origination stop at month 3+1.24 pp−0.17 pp
crash −70% over 3 months, flow continues−2.04 pp+2.43 pp
90% lifetime prior−5.13 pp−9.59 pp
sale at 20% of spot−10.73 pp−4.31 pp
behavioral floor (X = 0, Y = 2)−32.86 pp−21.55 pp

The ±1 pp credit gate and the ordering (the haircut row now exceeds the prior row) do not survive the September waterfall. The 90% prior costs about half of what it cost the July engine, consistent with archived Model Card §6: under the ruled waterfall a stop whose sale covers the remaining schedule costs the Holder only future yield, and a stop in the money hands the surplus to the Holder, whereas the reference capped every recovery at the schedule. memo_neighborhood_zero_drift printed 14.64% over 200 runs the same day. Neither touches the build. Both say the July reference is no longer a neighborhood of the September product and the gates are due for a recut on the current engine. That is what non-blocking is for: the number is on the record and the suite stays green while the product moves.

Reproducing a workspace result

Save the completed run package from the workspace. Keep its request arguments, responses, engine identity and any warnings together. Research comparisons and specialist analyses need their own endpoint arguments: a simulation configuration alone does not reproduce a surface, hedge or monthly analysis.

  1. Confirm the engine source build and historical-data digest match the original response. A specification label alone does not identify a build.
  2. Use the completed request, including endpoint options, surface and hedge structure where applicable. Do not substitute a draft edited after the run.
  3. For a distribution, retain the seed sequence and number of runs. For a historical analysis, retain its start range and overrides.
  4. Compare the numerical result and its missing/ambiguous-result flags. Use the independent ledger tie-out where postings are available.

Legacy chart and CSV exports can carry a short sha8 configuration fingerprint. It is not an encoding of the configuration and is not the full run identity. A chart image alone cannot reproduce an analysis. MCP supplies a separate canonical replay object and result digest; see Read the result.

Deterministic reproduction is scoped to the same build, data and arguments. Floating-point identity across different compilers or hardware is not established by the repository’s tests.

Completed Research, preview and drift-sensitivity exports use recognized evidence package kinds with reporting.objective, capital/selection conventions and reporting.calculation_version. The verifier accepts bounded packages up to 64 MiB; a JSON draft import is a separate operation and does not restore completed results. Keep the request, raw observations and client calculation version together so another analyst can reproduce the table, not only the backend response. Old unsupported exports must not be relabeled as a verified new package without checking their contents.

What the suite does not prove

The invariant tests check the specified arithmetic and identities for the cases they exercise. They do not prove that the hazard shape, the multipliers, the early-completion propensity or the rational Buyer’s belief are right; the repository does not include an observed Agreement dataset that validates those priors. The tornado reference is stale and has not been recut. The auditor re-derives the run it is given and says nothing about whether that run’s assumptions describe the world. The limits chapter takes those up.

Entry-funded Coin checks in 0.6.0

tests/coin_funding.rs independently checks 240 par Agreements require 240 BTC with entry dispersion on or off, premium/discount funding, actual cohort counts, original-flow IRR residuals, cohort gross reconciliation, and matching unhedged/hedged funding. The same test preserves the original USD ledger return. Coin lifecycle regressions cover stopped collateral with and without refunds, final-payment stops and signed surplus exposure; independent Gaussian quadrature checks the known-stop delta. Long-sale-tail tests preserve posting dates at 18, 45, 90 and 120 days.

The September 13 allocator audit and implementation evidence live under docs/reviews/2026-09-13-coin-allocator-audit/ in the repository. A historical golden for a deliberately corrected Coin hedge changes with the release; unchanged Agreement cash and surface-node controls remain tested separately.

Monthly Coin sensitivity checks in 0.6.1

tests/coin_monthly_age.rs checks performing receipt sensitivity against independently differentiated fixed receipts at every monthly age, including the final receipt, several term lengths and different origination-payment counts. It also checks half and opposite futures sizing and zero exposure after receipt booking. The performing surface now has a node at every payment age; a three-month interpolation must not spread a receipt transition over three months.

The correction deliberately changes Coin-delta hedge cash. The lifecycle and extended-domain tests reconcile the hedge’s actual monthly cash and independently solve the resulting original BTC flow IRRs, rather than replacing unexplained aggregate goldens. Existing quarterly surface nodes and unhedged numerical controls were compared separately. Monthly construction takes approximately three times the work of the former quarterly core grid; route admission includes a retained-surface allowance, but this is not a production-capacity certification.

The frozen audit is docs/reviews/2026-09-13-separate-workspace-audit/; its separate remediation/ folder records the correction, numerical controls and browser checks. Those records preserve their original build identities. Deployment acceptance is recorded separately in docs/releases/2026-09-14-coin-usd-audit/RELEASE.md.

Versions and rulings

This chapter records the early rewrite and program rulings through specification v1.6, dated 3 September 2026. It is a historical record, not the current release manifest. Later model and audit work is documented in the relevant methods chapters and repository; the running engine reports its specification, build and data identity at /api/forwardflow/health. Read it when a number in an older document does not match what the engine prints today, or when a term in an older document is not in the glossary.

Two rules govern what follows. The source defines the implemented calculation; a disagreement with the documented intent is a review finding. Each formula below names its implementing function. The spec is the intent: the version table is drawn from the change blocks at the top of the specification, which record what changed and why, in the order it happened.

How to read the dates

A spec version is a number the specification carries (the table here covers v1.0 through v1.6). It changes when the engine’s definition changes. A ruling is a decision by Marc on a program term or a modelling choice, cited here by date and decider, for example “Marc, 2026-08-22”. A ruling is applied by a spec version, sometimes weeks later. The gap matters: from 22 August to 3 September the engine still priced the July product while the program had moved on. The v1.5 change block is where the September rulings caught up with the code.

A Model Card is a third kind of document. It stamps one configuration and one set of figures for a data room. It cites the spec version it was run on, and records whether the engine owner has accepted it; a draft is not a stamp. The lineage is at the end of this chapter.

Current release card

The v3.3 model card, updated 14 September 2026, documents engine/API 0.7.0 guided MCP, authorized by Marc for research release that day. It retains the 0.6.1 USD/BTC measurement boundaries and model mathematics. Deployment verification is recorded separately; actual Claude conversation acceptance remains pending manual testing, and independent model approval has not been recorded. The preceding authorized 0.6.1 release has its own deployment record. The historical card sections and numerical stances below describe prior configurations, not new workspace defaults. The repository archives v2.1 under docs/model-cards/.

Spec versions

VersionDateWhat changedWhy
v1.02026-07-10The rewrite itself. One job: the economics of a Holder buying BPA paper of any term. Hybrid verdict: keep the double-entry ledger, the exact rust_decimal money math, the Brownian-bridge path generator and the API; scratch-build the domain (Agreement, exits, fees). M0 closed-form fixtures done the same day.The legacy simulation modelled a different program (1.92×, 120 months, warehouse, bonds). Its semantics would have fought every line.
v1.12026-07-10Term length a true parameter: payment, fee rate, hazard shape and outputs all derive from it. “Payment 1 to BTC Now” generalised to the first N payments (origination_payments, default 1). The Python actuarial engine demoted from oracle to directional reference; acceptance rests on invariants.Marc: “5yr” is a default, not an assumption. Reproduction of a differently specified model is not a test.
v1.22026-07-11Haircut and market-sale cost promoted to inputs #15 and #16. The stop sale (then liquidation) corrected: sized to the remaining schedule, never the whole coin; residual coin back to the Buyer; no sale excess to BTC Now. Per-Agreement shortfall in dollars in the drill-down.Marc, 2026-07-11: the paper’s owner is entitled only to the cash component, capped at the Agreement. Superseded for the residual on 2026-09-03 (see below); “BTC Now takes fees only” survives.
v1.32026-07-11Inputs #17 shock designer, #18 origination window and runoff, #19 inverse price solver. The Assumption Tornado with a golden reference. The Scenario Shelf (S1 to S12, plus S13).Merged from a parallel spec session. Cleaner crash instruments than moving bridge endpoints; the tornado is the one-picture sensitivity a reader asks for first.
v1.42026-07-12 to 07-13No static haircut (default 0); the forced sale priced at the following month’s mark. Par as the default purchase price (purchase_pct_of_strike 1.00, $60,000; was 1.05, $63,000). Input #20 intramonth strike dispersion. Input #22 early-completion take-profit gate. Custom per-year stop timing. The tie-out audit (an independent TypeScript re-derivation of every figure). Input #23 drawdown-scaled hazard, which ties the deterministic crash grid to the Python oracle within ±0.3 pp. Break-even heatmaps, stamped exports, the tornado golden check. The pooled-vehicle seat (input #21) built, then removed on 2026-07-12. Cockpit defaults 1%/yr fee and dispersion ON. Hosting hardening with byte-identical outputs.Marc, 2026-07-12: par is the neutral anchor for the negotiation; a one-coin sale has no market impact, so the clock is priced by time on the path, not by a haircut. Marc, 2026-07-12, on input #21: recycling multiplies wealth, never the rate, so the paper’s IRR is the analysis and a second engine mode carries no information.
v1.52026-09-03The September program. The stop replaces the old sale: day 16 is the Stop Date, the coin is sold for dollars 18 days after the missed payment (input #24 stop_sale_lag_days), the R-1033 waterfall pays the Holder first, refunds the Buyer up to what he paid, and leaves the surplus with the Holder. The fee is a flat 5% of every delivered dollar (servicing_fee_rate replaces base_fee_pa). Early completion is cash-only. Horizon + 1. Vocabulary locked. The Behavior Engine parked.Marc’s rulings of 2026-08-22, 08-31, 09-02 and 09-03, applied as Phase 0 of the build plan. The engine had been seven weeks behind the program.
v1.62026-09-03The exposure layer: the two lines, coverage, the exposure ladder, PD·LGD·EAD per vintage, Greeks by bump-and-revalue, the rational walk-away frontier as the fifth behavior mode (input #25). One seeded draw stream per Agreement (engine.rs::agreement_rng). POST /api/forwardflow/risk and the Risk desk page.Marc’s “start” on Phase 1. The bumps are sensitivities only if a bump changes decisions and never the random numbers behind them, hence the per-Agreement streams.

Two versions deserve a sentence each beyond the table.

v1.4 is the longest change block because it contains a reversal. The pooled-vehicle seat (change 3), the evergreen view (change 8) and the evergreen framing of that vehicle (change 9) were built and then removed in the second change 11, committed 2026-07-12, after the IRR-versus-wealth discussion: recycling compounds wealth, but it cannot raise the rate above the paper’s own. The removed implementation lives in git history. Nothing in the current engine models a pooled vehicle. The paper’s own IRR is the Holder’s rate, and a wrapper returns that rate less idle-cash drag.

v1.5 is the version that changed the product rather than the method. The Model Card v2.0 draft says so in its first heading. Every figure in a document dated before 3 September 2026 was produced under v1.4’s economics and should be read with the translation table below in hand.

Rulings the engine implements

Each row names the ruling, the decider and date, and the spec version that applied it. Where a ruling was later superseded, the row says so.

DateRulingDeciderApplied by
2026-07-10Term is a model parameter; the first N payments go to BTC Now, N = 1 by default.Marcv1.1
2026-07-11The residual-coin correction: on a stop the sale is sized to the remaining schedule; coin beyond that returns to the Buyer; there is no sale excess to BTC Now.Marcv1.2. Residual rule superseded 2026-09-03; “fees only” kept.
2026-07-12No static haircut. The delay between the missed payment and the sale is priced by selling at the next month’s mark.Marcv1.4. Refined by v1.5 (sale at day 18, interpolated).
2026-07-12Par is the default purchase price: $60,000 on the base coin, not $63,000. The premium remains a free input.Marcv1.4
2026-07-12Buyers may stop on a custom per-year timing; Buyers complete early only above a take-profit threshold when the gate is on.Marcv1.4
2026-07-12The pooled-vehicle seat (input #21) is removed: a vehicle holding the paper needs no model of its own, because the paper’s rate is the vehicle’s rate less idle-cash drag.Marcv1.4 change 11 (the second), committed 2026-07-12
2026-07-13Cockpit base case: fee 1%/yr (was 0.75%), strike dispersion ON.Marcv1.4 change 12. Fee form superseded 2026-08-31.
2026-08-05The pricing stance is a zero-drift bootstrap at trailing-24-month volatility; the 2016-regime construction is superseded. Model Card v1.0 stamped.MarcModel Card v1.0 and v1.1 (no engine change)
2026-08-05The late window is 15 days, not 25: the Agreement already said so, and the engine’s 25-day wording was conformed to it. The engine notes the window as descriptive at monthly resolution.Marc, confirming the Agreement’s text; restated as R-1035 on 2026-08-22Model Card v1.1. Made exact by v1.5 (input #24).
2026-08-06Model Card v1.1 stamped after the price series refresh through July 2026.MarcModel Card v1.1
2026-08-22The stop: a recorded sale for dollars; refund = min(paid, max(0, proceeds + paid − Purchase Price)); the Buyer never receives more than he paid. Day 16 after a missed payment is the Stop Date (R-1035). No eligibility check and no sizing. The sale within two business days (R-1037).Marcv1.5
2026-08-31The servicing fee is 5% of every dollar sent to the Holder; the send is the fee event, termination sends included.Marcv1.5
2026-09-02Vocabulary: Buyer and Holder, numbered from 0; Transfer and Sale; Partner.Marcv1.5
2026-09-03The surplus above the Purchase Price on a stop sale stays with the Holder; BTC Now takes its 5% of every delivered dollar and nothing else.Marcv1.5
2026-09-03The Holder’s stop-sale delivery is in dollars; there is no in-kind residual term. A Holder who wants coin buys it with the proceeds.Marcv1.5 change 8. A reinvest-into-coin view is planned as the Holder’s own act.
2026-09-03The Behavior Engine v0 is parked on its own branch; its rational boundary is lifted into the main engine; its four-channel decomposition goes to the memorandum as prose.Marcv1.5 change 7, v1.6 change 6
2026-09-03Lockouts reinstated: seven days after two lapsed windows, six months after a stop.MarcNoted in v1.5 change 8. Not modelled.
2026-09-03Phase 1 of the build plan: the exposure layer.Marcv1.6

Two items are flagged in the spec as term-sheet questions rather than rulings, and the engine has taken a position on each pending the answer. Whether payment 1 sits in the Buyer’s refund base is modelled as yes, on Marc’s phrase “the maximum he paid”. The sale standard (venue, deadline, price against the index) is not yet written into the Agreement; the engine assumes a market sale at the path price less 25 bp. Both are listed in limits.

Three rulings in numbers

The base terms throughout: a $60,000 coin, multiple 1.475×, Purchase Price $88,500 (contract.rs::ContractTerms::terminal), 60 monthly payments of $1,475.00 (contract.rs::ContractTerms::schedule), payment 1 to BTC Now, 5% servicing on every dollar delivered to the Holder, the Holder buys at par, the stop sale 18 days after the missed payment, 25 bp sale cost.

The par default of v1.4

Before v1.4 the default purchase price was 105% of the strike, $63,000. From v1.4 it is par, $60,000 (engine.rs::SimConfig::purchase_pct_of_strike, default 1.00). The M0 fixtures were generated at 105% and their tests pin that value, so a fixture figure and a cockpit figure at the same inputs differ by the $3,000 premium and nothing else. A reader of the July file set’s “Buyer Pack 105” document is reading the pre-v1.4 anchor.

The fee at send

From v1.0 through v1.4 the fee was a rate per year multiplied by the term:

\[ \text{fee rate} = \text{base_fee_pa} \times \frac{\text{term months}}{12} . \]

At 1%/yr and 60 months that is 5%, and at 0.75%/yr it is 3.75% ($55.31 per payment). At 36 months the same 1%/yr gave 3%. Since v1.5 the rate is the input itself, servicing_fee_rate, flat at 5% whatever the term (contract.rs::ContractTerms::fee_rate). At the base terms nothing moved: 5% of $1,475.00 is $73.75 to BTC Now and $1,401.25 to the Holder on every delivered payment (fees.rs::FeeState::split, with cumulative rounding so the lifetime total is exact to the cent). At any other term the fee did move, which is why the acceptance item that once read “total fee rises linearly in term” (spec §6.5) now asserts the opposite; the test that carries it is term_parametricity_sweep_is_smooth_and_monotone. The M0 fixtures keep their historical basis by pinning the flat rate per term: 2.25%, 3%, 3.75%, 5.25% and 7.5% for 36, 48, 60, 84 and 120 months.

What the 2026-08-31 ruling added, beyond the flat form, is scope. The fee is on every send, the stop-sale delivery included. The engine had charged the fee on sale deliveries since v1.0 (spec §3.2: “in the plus we also get our fee”); the September rule makes that the program’s own term rather than a modelling convention.

The residual-coin correction and the waterfall that replaced it

Take a Buyer who makes twelve payments and misses the thirteenth. He has paid in \(A = 12 \times 1{,}475 = 17{,}700\), payment 1 included. The remaining schedule is \(R = P - A = 70{,}800\). The missed date is month 13; the sale is 18 days later; the cash posts at month 14, which is \(\mathrm{round}(13 + 18/30.4375)\). The proceeds are the interpolated path price less the sale cost, \(V = S \times (1 - 0.0025)\) (engine.rs::stop_sale). The waterfall is

\[ \text{refund} = \min\big(A,\ \max(0,\ V + A - P)\big), \qquad \text{delivered} = V - \text{refund}, \]

with the Holder receiving the delivery less 5%, the shortfall \(\max(0, R - V)\) and the surplus \(\max(0, V - P)\) recorded per Agreement.

Coin at $30,000Coin at $120,000
Proceeds \(V\)$29,925.00$119,700.00
Refund to the Buyer$0$17,700.00 (capped at \(A\))
Delivered to the Holder, gross$29,925.00$102,000.00
Fee to BTC Now on the delivery$1,496.25$5,100.00
Holder receives$28,428.75$96,900.00
Shortfall against the schedule$40,875.00$0
Surplus above the Purchase Price$0$31,200.00

Both columns were produced by the live engine on a one-Agreement custom path that steps at month 12 and stays flat: the left by a conviction walk at month 13, the right by a hazard draw at age 12 under custom per-year timing, since the conviction rule cannot fire while the coin is in the money. Every cell reproduces from the four formulas above. The test stop_sale_regime_one_exact_cents pins a sibling case (a walk at month 7 on a path that drops to 60%) cent by cent, timing included; stop_sale_waterfall_identities_across_regimes checks the identities across all three regimes on one path.

Under v1.2 through v1.4 the right-hand column read differently. The sale was sized to the remaining schedule: only enough coin to raise $70,800 was sold (at the next month’s mark from v1.4; on the missed date, less the haircut, in v1.2 and v1.3), and the coin beyond that (worth about $49,200 at that price, before the sale cost) went back to the Buyer as coin. The Holder received $70,800 less the fee. The v1.2 rule was itself a correction: before it, the whole coin was sold and the excess above the schedule went to the paper’s owner. The September ruling returns the surplus to the Holder, but in a different shape. The Buyer is now refunded in dollars up to what he paid, so the Buyer’s position in the up case is $17,700 of refund rather than $49,200 of coin, and the Holder’s is $102,000 gross rather than $70,800.

The left-hand column barely changed between the two rules. When the coin is worth less than the schedule, both rules sell it all and both leave the same shortfall. That is why the Model Card’s zero-drift medians moved by a tenth of a point between v1.1 and v2.0 while the paced replay median moved from 12.9% to 21.5% and the single-vintage replay median from 19.7% to 47.9%. The difference between the rules is the up case, and on Bitcoin’s own history the up case is large.

One founder invariant needs restating because the v1.2 wording no longer holds. “We can never keep more than we are owed” described the v1.2 sale and is superseded for the Holder. What stays true and stays tested is narrower: BTC Now takes fees only, the first N payments and the flat 5%, never a share of a stop sale. No input may give it one.

What a reader of an older document must translate

The July memorandum, the diligence Q&A, the drawdown exhibit and every engine document dated before 3 September 2026 describe the earlier product. Six terms recur. Each has a current equivalent.

Older document saysToday it meansWhere it changed
The 25-day clockDay 16 after a missed payment is the Stop Date; the sale is 18 days after the missed date, input #24, priced on the path by interpolation.15 days confirmed against the Agreement 2026-08-05 and restated as R-1035 on 2026-08-22; made exact by v1.5.
$63,000The Holder buys at par, $60,000. The 105% premium is a free input, not the anchor.v1.4, Marc 2026-07-12.
The made-whole lockoutEarly completion is the Buyer paying the remaining schedule in cash and taking the coin. A stop is refunded by formula. Lockouts exist in the program (seven days, six months) and are outside the engine.v1.5; lockouts reinstated 2026-09-03, not modelled.
The advance structure, “45% advance”There is no advance. The Holder buys the whole receivable at par and carries the full coin basis from day one. The two lines in exposure.rs::two_lines are the replacement exhibit.Never an engine input: the July exhibit priced an alternative structure the program did not offer. Redrafted for the par Holder on 2026-09-03; the two lines are v1.6.
“Purchaser”, “Purchaser Analytics”The Holder; the report page is now Scenario report.Vocabulary, Marc 2026-09-02.
“the fund”, “forward-flow buyer”The Holder. There is no pooled vehicle in the engine.Input #21 removed 2026-07-12; word retired 2026-09-02.

Some detail on each.

The 25-day clock. From v1.0 the engine’s comments said 25 days, and from v1.4 the delay was priced by selling at the following month’s mark, one full month after the missed payment. On 5 August 2026 Marc pointed out that the Agreement’s window was 15 days, not 25 (a payment up to 15 days late carries no fee and no penalty); the engine’s documents were conformed, the 22 August ruling restated the window as R-1035, and Model Card v1.1 recorded the engine’s clock as descriptive at monthly resolution, since a 15-day and a 25-day clock both sat below the model’s step. v1.5 made the timing exact: the sale lands at stop_sale_lag_days after the missed date, default 18 (day 16 plus two business days), and the price is interpolated log-linearly between the path’s monthly marks, so \(\text{lag} = 0\) sells at the missed date’s mark and \(\text{lag} \approx 30.4\) at the next. At monthly resolution the difference between 18 and 30 days is a fraction of one month’s price move, and the test above pins the ordering of the three prices.

The $63,000 price. The July diligence script deflects a request to lend against the receivables with “the offer is whole-receivable flow at $63,000”. The number is the 105% default of v1.0 through v1.3. The structure (“whole receivables, one structure”) still holds; the price does not. Every current figure is at par, and the inverse price solver (input #19) is the tool for any other price.

The made-whole lockout. The July diligence answer to “why won’t Buyers churn” was that a profitable re-entry mathematically implies an exit that was not made whole, which triggered a six-month lockout. In the September program the exit is a stop with a refund by formula, so the arithmetic of that answer has to be re-run against the waterfall. The engine’s lockouts are out of scope; what it offers instead is the rational walk-away frontier of v1.6 (boundary.rs::rational_frontier), which prices the walk against continuing or completing early, with a walk cost as an input. The current view of churn is a question for the behavior chapter, not a closed answer.

The advance structure. The July drawdown exhibit’s first finding, that a 30% drop in Bitcoin costs nothing, was true of a senior advance at 45% of the coin’s day-one value, an alternative structure the program never offered. It is not true of a Holder who pays par. The v2.0 draft of that exhibit says so and retires the sentence: at par a permanent −30% costs yield, not capital; capital loss begins near −60% inside a year; the 2021–22 shape is the worst cell. The engine’s own statement of the same fact is the capital line, which starts at 1.05 of entry at month 1 (payment 1 went to BTC Now and every delivered dollar carries 5%) and reaches zero at month 44. Those two values are pinned by test in v1.6.

Purchaser and the fund. Two vocabularies had grown, one in the legal drafts and one in the developer spec, and “trade” alone named three different things. The 2026-09-02 ruling ended it: the paying side is the Buyer, the receiving side the Holder, Holder 0 is always BTC Now, the paying side changes hands by Transfer and the receiving side by Sale, and a Partner is anyone paid a cut of BTC Now’s fees. The words Purchaser, customer, obligor, forward-flow buyer, fund, liquidation and non-performance left every user-visible string in v1.5. Engine identifiers did not change: Owner is the Holder’s ledger entity, Obligor the Buyer’s, non_performance an exit tag, MakeWholeDelivery the ledger kind of an early-completion payoff, and base_fee_pa survives only in the M0 fixture file and in the spec’s history. A reader of the code should map them and not be alarmed.

Model Card lineage

Three cards exist. Each states a full configuration, a hazard model, a path process and a results table at seed 42, sufficient for reproduction. The results are Holder IRRs.

CardDateStampSpecThe pricing constructionPaced zero-drift medianSingle vintage zero-drift medianSingle vintage replay median
v1.05 August 2026Stamped by Marc, 2026-08-05v1.4Zero-drift bootstrap from the 2016-onward regime, about 68% realized; the 25-day clock, next month’s mark, capped at the schedule, excess to the Buyer.9.8%13.7%19.8% (100 starts)
v1.16 August 2026Stamped by Marc, 2026-08-06v1.4Zero-drift bootstrap at trailing-24-month volatility (July 2024 to July 2026, 41.4% realized) after the series refresh to 174 bars; the 15-day window, descriptive at monthly resolution.13.1%15.0%19.7% (113 starts)
v2.0 DRAFT3 September 2026Pending Marc’s stampv1.6Same construction; the September program: the R-1033 waterfall with the surplus to the Holder, 5% flat, cash-only early completion, 84-month paced horizon.13.1%14.9%47.9% (113 starts)

What moved between v1.0 and v1.1 was the volatility regime, not the product. Marc ruled on 5 August that the modern-regime window included market microstructure that no longer exists, so the pricing stance became current volatility with the full-history construction kept as the printed stress. The v1.0 stamp did not carry across the construction swap, which is why v1.1 needed its own.

What moved between v1.1 and v2.0 was the product. The card’s own reading is the right one: on a driftless path the September waterfall and the July sale deliver almost the same cash, so the zero-drift medians moved by a tenth of a point (the v1.5 morning run printed 13.0% and 14.8%). Every right tail and every replay row moved a lot, because a Buyer who stops while the coin is worth more than the Purchase Price now hands the surplus to the Holder, and the engine produces such stops at half the baseline hazard through the ×0.5 in-the-money multiplier. How often a Buyer with a winning coin stops is the least-evidenced behavior in the model, and it now drives the upside. The card attaches that sentence to every replay figure.

The v2.0 draft carries one more note a reader should not mistake for a product change. Its figures were re-run on v1.6, which gave every Agreement its own seeded draw stream so that bump-and-revalue Greeks are sensitivities rather than draw noise. The same seed therefore draws differently from the v1.5 run of the same morning: the zero-drift medians moved by a tenth of a point and the single-vintage replay median from 38.2% to 47.9%. That is the in-the-money-stop sensitivity restated. On Bitcoin’s own history a handful of such stops per vintage moves a median by ten points.

The stamp is the act. Until Marc stamps v2.0, the figures a Holder may be shown are v1.1’s, and v1.1 describes a product that no longer exists. The verification chapter gives the one command that reproduces each card’s table.

MCP technical reference

For analysts and client developers who want the tool details, result metadata and limits. To get connected, use the three-step connection guide.

Available analysis

The interface provides eight tools. Every calculation requires preparation with an explicit objective: "dollar" or "coin". Technical validation can still disclose legacy defaults; it does not establish the Holder’s intent or authorise a calculation. One connection and token serve both objectives. The MCP preparation requirement changes neither the access-token setup nor the REST request contract.

ToolSupported return objective and result
describe_modelDefaults, named presets, analyst guide, units, capabilities, measurement_contract and limitations.
prepare_analysisResolve the declared brief and proposed inputs without calculating. Returns up to three missing questions, an unsupported explanation, or canonical inputs with a signed preparation and next_call.
validate_scenarioValidate a planned Dollar run or a single Coin path without computing outcomes.
run_scenarioDollar: one path or 2–256 seeds. Coin: one path, with complete research.outcome and research.conventions. For Coin Monte Carlo use hedge_risk_samples.
compare_scenariosDollar: one or many seeds; Coin: one path per side. Both sides retain matched seed and horizon rules.
solve_purchase_priceUSD cash-flow IRR only. A Coin objective is refused; no BTC price solver is implemented.
compare_hedgesUSD headline distributions. A Coin request is refused with instructions to call hedge_risk_samples; it is not automatically rerouted.
hedge_risk_samplesPaired Dollar/BTC observations, with an explicit objective and complete Coin root diagnostics, capital bridge and mark conventions. Up to 32 seeds per call, or 8 with monthly arrays.

For a Coin question, use run_scenario with objective: "coin" for one path. Read result.research.outcome.coin for the BTC IRR and its matching diagnostics, and coin_holding for the amounts. Supporting USD fields cannot replace missing BTC values. For Monte Carlo, collect raw hedge_risk_samples observations using matching assumptions, engine identity and valuation seed; combine raw samples rather than averaging batch percentiles.

Prepare an analysis

prepare_analysis takes question, analysis: {tool, arguments} and context. Context records objective, capital_basis, benchmark, defaults_policy, assumptions_note, horizon_note and, when hedges are selected, hedge_constraints.

  • capital_basis: "dated_agreement_flows" describes the supported research. "fixed_wallet" is refused: reserves, recycling, borrowing limits and daily margin do not become a funded portfolio simulation merely by choosing an Agreement count.
  • Benchmarks are holding_matched_btc for Coin, usd_cash_flows for Dollar, usd_discounted_hurdle for Dollar hedge_risk_samples, and unhedged_agreements for either objective.
  • defaults_policy: "program_defaults" requires an explicitly accepted named preset: dollar-research-v1, coin-research-v1 or legacy-engine-bridge-v1. Their values come from the server’s shared preset catalogue. "explicit_inputs" requires complete numerical inputs. Supplied overrides and remaining defaults are disclosed separately.

The result.status is needs_clarification, unsupported or ready. Clarification returns at most three questions plus a count of questions still remaining; the assistant should ask only what has not already been answered. A ready response supplies brief, assumptions_preview, canonical_analysis, provenance and next_call. Show the material assumptions before calling next_call.tool with next_call.arguments unchanged.

All five outcome-producing tools require a preparation object beside their canonical arguments. It is signed, valid for one hour and bound to the tool, inputs, brief and engine identity. Missing, changed, expired or incompatible preparations are refused. The receipt proves server preparation, not human approval, calibration or funding feasibility, and is not an access token. An assistant must not invent answers to missing questions to obtain it.

The optional MCP prompt start_analysis takes no arguments. Resources forwardflow://guides/analyst-v1 and forwardflow://presets/research-v1 expose the shared analyst guide and preset catalogue. describe_model also returns the guide. Clients choose how to surface prompts and resources; connecting does not install a local skill or guarantee automatic guide ingestion.

Ask and inspect

For example:

Use the illustrative legacy-engine-bridge-v1 preset, with a permanent 40% Bitcoin shock starting in month six over one month, and a 60% lifetime baseline stop rate over the Agreement term. Use the Dollar objective and dated Agreement flows against contributed USD, retaining the preset’s term, origination pacing and horizon. Run 128 seeds and show USD cash-loss frequency beside USD effective annual IRR. State all other assumptions before calculating.

The assistant can first call validate_scenario with:

{
  "config": {
    "shock": {
      "start_month": 6,
      "drop_pct": 0.4,
      "duration_months": 1,
      "recover_to_pct": null
    },
    "scenario": { "BaselineCurve": { "lifetime": 0.6 } }
  },
  "runs": 128,
  "objective": "dollar"
}

This is a technical validation example, not a calculation-ready request. The assistant then sends the question, proposed run_scenario arguments and established context to prepare_analysis, names the accepted preset, explains the preview and uses its next_call. A supplied nested object replaces that entire object. Unknown fields, including misspellings within nested objects, are refused. The assistant should clarify material ambiguities such as annual versus lifetime stop rates, the time of a shock, its recovery, USD versus BTC, or nominal versus effective returns.

compare_scenarios takes baseline and alternative objects. The alternative inherits the resolved baseline and changes only its supplied top-level fields. Both sides use the same seed and a common market horizon large enough for both books; conflicting explicit horizons or seeds are refused. The canonical inputs disclose this normalization. Changing book dimensions or Buyer behavior can change the mapping of random draws to Agreements even with the same seed. Side-by-side percentiles are not a confidence interval for the difference.

Read the result

Each successful answer contains:

  • result: the existing engine outputs, or model/validation metadata. Single simulations retain conservation status and currency-specific scalar output flags. Coin runs also include the complete research outcome, Coin root diagnostics and conversion/benchmark conventions. Monte Carlo retains cash-loss frequency, missing-IRR counts, ambiguity counts and the engine’s explanatory note. Hedge outputs retain surface warnings and conventions.
  • warnings and defaulted_fields: interpretation cautions and which assumptions were filled in.
  • engine: model specification, API version, source build and historical-data digest.
  • analysis_brief: on calculations, the verified prepared brief and its assumptions/provenance. Its caller statements are not independently verified human consent.
  • replay: the tool name, complete resolved arguments and engine identity.
  • run_id: SHA-256 of the canonical replay object; result_sha256: SHA-256 of the returned result object. Canonical JSON uses recursively sorted object keys, no extra whitespace, and the Rust serializer’s numeric representation.

Save replay, analysis_brief and the result in the client. Replay contains the complete numerical arguments without the expiring preparation. Send those saved arguments and the original brief through prepare_analysis, then execute the new next_call. The same complete numerical inputs, engine build, data and runtime reproduce the numerical result. Check the original identity before interpreting a replay: another build or data set is a new comparison. run_id is a content identifier, not a secret, saved-run URL, or lookup token. There is no get_run store. Preparation and default-disclosure metadata can differ; the numerical result and its digest remain the same.

Set include_series: true on scenario/validation/comparison tools to request monthly exhibits when running. Otherwise the response omits the ordinary price/cash series and USD Monte Carlo histograms; the Coin research outcome retains its reconciliation data and conventions. Raw postings and individual Agreement rows are never sent through these MCP tools. The API remains available for those exhibits.

Rates are fractions: 0.12 is 12%. Decimal money-related inputs such as purchase_pct_of_strike are strings: "0.95" is 95% of strike. haircut is a log discount, not a percentage subtraction. Null IRRs remain unavailable, and ambiguous IRRs retain their flags. Model outcomes and stylised option surfaces are not live quotes or forecasts; MCP access does not itself establish the model’s suitability for an investment decision.

Capacity and lifecycle

backend/src/mcp.rs enforces these bounds in addition to the existing engine validation and shared capacity limits:

LimitValue
Agreements in a book2,000
Agreement term120 months
Market horizon240 months
Scenario/Monte Carlo runs1–256
Comparison sides2
Hedge structures4 for compare_hedges; 8 for hedge_risk_samples
Estimated work per tool call500,000 Agreement-runs, including both comparison sides
Estimated peak memory per constituent run request128 MiB
Request body / request deadline128 KiB / 120 seconds

Each named key has 120 requests per rolling minute and four concurrent slots for MCP, including protocol calls. A party’s REST and MCP requests share its allowance. These counters and the compute/memory budgets are per server process; multiple machines do not share a distributed quota. Explicit open mode counts anonymous MCP callers by IP in a separate rate state; a shared AI gateway IP may group several people. All MCP calculations use the existing global gate of three active heavy computations and at most eight queued requests. Every executed request reserves memory from the same process budget as the API. Calculations for comparison sides run sequentially. A cached Monte Carlo answer requires no new computation.

Rate/concurrency refusals use HTTP 429 and Retry-After. Engine capacity refusals inside a tool return MCP isError: true with a structured error including status 503 and the engine’s actual retry_after_secs (5 for memory pressure, 10 for a full compute queue). Invalid assumptions return a tool error with status 400. Authentication and transport failures use HTTP statuses. Retrying a completed request is safe because these tools have no persistent side effects, though retries may repeat computation.

The official Rust MCP SDK provides Streamable HTTP, protocol negotiation, discovery and JSON tool responses. Legacy protocol sessions are disabled; there is no per-connection session store to grow. A disconnect or deadline drops the API future and requests cooperative cancellation. A running seed or single simulation finishes its current engine unit before compute resources are released. A single simulation retains its memory reservation while the typed result is converted for MCP; this is not a reservation for the complete subsequent MCP envelope or its transport lifetime. No asynchronous job polling, saved scenarios, OAuth accounts, external data feeds, or trade execution are implemented here.

ChatGPT

Client documentation checked 14 September 2026. ChatGPT desktop supports ForwardFlow’s access-token connection. Set Authorization to Bearer YOUR_ACCESS_TOKEN under Headers, using your own token. See the desktop connection guide or the Codex CLI and IDE option.

The Bearer token env var field expects an environment-variable name, not the token value. The Headers from environment variables fields also read their values from the environment. Leave both empty when using the static Authorization header above. The desktop app, Codex CLI and IDE extension share MCP configuration on the same Codex host. See OpenAI’s MCP documentation.

ChatGPT web uses a separate connection setup. It does not read the desktop app’s local MCP configuration. Hosted ChatGPT Work chats can use tools supplied by installed plugins. The separate developer-mode guide describes OAuth, no authentication and mixed OAuth/no-auth connections. ForwardFlow requires a static access key and does not implement OAuth, so that developer-mode flow is not a documented direct connection for this service. Choosing no authentication will not bypass the key requirement. Saving the desktop connection does not enable it on the web.

GPT Actions supports API-key authentication over REST as another web integration. A custom GPT must use selected operations from the hosted OpenAPI schema, with the party’s key saved in the Action’s authentication settings. There is no curated, tested Actions or hosted-plugin package in this release. Availability depends on the ChatGPT account and workspace. The documented direct setups are local ChatGPT desktop/Codex clients and Claude Code.

Connection help

What you seeWhat to check
401, missing key or invalid keyUse your ForwardFlow access token in the authentication header. Check for extra spaces and confirm with BTC Now that the key is active.
An OAuth sign-in screenForwardFlow uses an access token. Choose a client configuration that supports an authentication header.
404 or no toolsUse https://btcnow-forwardflow.fly.dev/mcp, and select HTTP transport.
429Wait for the indicated retry interval; the party’s request or concurrency allowance is in use.
A tool error with status 400Ask the assistant to validate and correct the named assumption or reduce the requested work.
A tool error with status 503The engine is at capacity. Wait for the reported retry interval and try again.

Opening the MCP URL in a browser is not a connection test: the browser does not supply your access token or perform MCP discovery. Use the client’s status panel and the first question in the connection guide. The public engine health page reports the running build; it does not validate your key.

For key issuance and deployment, see MCP service operations.

hedge_risk_samples

Paired research observations for 0–8 strategies and 1–32 seeds per call (at most 8 with include_monthly: true). Supports config, structures, surface, option_rate, put_skew_points, exec_cost_bps, discount_rate_annual, valuation_seed, and objective (dollar or coin). Defaults are disclosed in the reply. Every seed reuses one Agreement book across its overlays. The result includes dollar/coin economic outcomes, NPV, cash requirements and paper exposure peaks; exact request and engine identity accompany the result. structures: [] requests only the unhedged book. Cross-book collars are excluded.

Join raw observations from non-overlapping batches only when assumptions and engine/data identity match. Use the same seed ranges for strategy/stress comparisons, and different ranges for a separate sampling check. Report tail sample size and uncertainty; cash allowance exceedance is a monthly screen without daily collateral or forced closure. Definitions and workflow.

Choose the objective before comparing strategies. For example, “Use the Coin objective. Compare these hedges with the unhedged Agreements and holding the same dated BTC contributions. Show BTC shortfall, sampling uncertainty and the separate USD cash requirement.” MCP calculations require an explicit prepared objective. An unresolved objective returns clarification before a calculation is issued. A single connection and access token serves both objectives.

coin_holding and versioned conventions identify the same benchmark as the browser. BTC shortfall is max(0, -net_gain_coins); average the worst 5% of nonnegative shortfall sample mass for tail loss. Pair strategies with unhedged on each identical seed using BTC surplus differences for Coin, or USD NPV differences for Dollar. Never discount BTC with the supplied USD rate. Contributions are strategy-specific net monthly economic outlays, including modeled hedge costs and horizon marks; reserves and borrowed capital are excluded. Economic recovery is not a realized wallet balance. The cash-retained USD screen and BTC conversion are separate readings, without jointly funded wallet/margin feasibility. Benchmark definitions.

BTC amounts and return language

BTC cash-flow IRR is a useful annual return on dated investment flows after the included costs. A single 100 BTC contribution and 110 BTC recovery means 10% total return; it is not necessarily 10% annualized IRR. State the dates and intermediate flows. Ratios are ×, amounts are BTC equivalents, and annualized rates are %. A missing Coin IRR keeps its own reason and must not hide a known BTC loss.

Gross Agreement purchases differ from net monthly contributions, and economic recovery can include unpaid horizon value. Show realized-cash contribution/recovery separately from signed derivative marks. Holder receipts already reflect their contractual deductions; do not deduct those fees again. Additional conversion costs, reserves and omitted collateral are not silently included. Reporting in BTC does not claim BTC settlement or a funded wallet. Research holding retains each strategy’s matching dated contributions; legacy one-coin shelf references are not equal-budget rankings.

MCP service operations

This page is for the team operating ForwardFlow. Invited users connect to the existing hosted service using ChatGPT setup, Claude Code setup or the general connection guide.

Production service

The live MCP endpoint is https://btcnow-forwardflow.fly.dev/mcp. The research website is ff.btcnow.com; its API proxy does not serve MCP. Keep clients connected directly to the engine endpoint.

The hosted configuration in backend/fly.toml sets FF_MCP_ACCESS=keys. The server refuses to start if FF_API_KEYS is empty. Preserve this setting when deploying. The website’s own key belongs to the server and must not be distributed to invited parties.

Operator configuration and verification

Invite a small group

Access requests go to info@btcnow.com. Issue one named key per invited person or organisation and deliver it privately. The user guides call this an access token; it is the existing API key, not a new account or login system.

Start the pilot with two or three invited parties. Keep a private record of each key’s name, recipient and status; store its value in the secret manager. Do not publish a list of tokens or hand several parties the same key. Separate keys allow individual revocation and separate request allowances. Retain the website’s own key separately.

Issue, replace or revoke a token

  1. Generate a different random key for each party using a password manager or openssl rand -hex 32 on the operator’s machine. Keep the key in a secret manager; do not commit it or paste it into a conversation. Key names use letters, digits, -, _ or ., with at most 64 characters; keys must contain at least 24 characters.
  2. Set FF_API_KEYS in the engine’s deployment secrets to the complete list of named keys. For example, placeholders only: web:REPLACE_ME_web_key_0000000001,party-alpha:REPLACE_ME_alpha_key_00000002,party-beta:REPLACE_ME_beta_key_000000003. Preserve the cockpit’s key and all other active entries when adding a party. Keep FF_MCP_ACCESS=keys.
  3. Give the party the HTTPS endpoint and only its own key through a private channel. The party configures its client’s credential settings once. The engine logs the key’s name, never its value. A key grants the existing model operations; there are no per-tool permission tiers or saved private records in this release.
  4. To rotate a key, replace that party’s secret and update its client. To revoke it, remove that named entry while retaining the others. Apply the new secrets and restart or roll every engine instance; new requests use the new key set after each instance restarts. Already running work is not retroactively cancelled. Existing cockpit websocket tokens have their own short expiry; MCP access keys are issued by the operator and remain valid until removed or replaced.

Shared capacity

The service accepts multiple users. Each request supplies its scenario; there is no single-user model session or shared editable scenario on the engine. Separate keys do not create separate machines.

The shared compute gate allows up to three active heavy calculations and eight waiting requests per process, subject to the shared memory budget. Each named key also has 120 requests per rolling minute and four concurrent heavy-route requests, shared across REST and MCP; every MCP request counts toward that concurrency limit. These limits are implemented in backend/src/api/forwardflow_api.rs and backend/src/access.rs.

The deployed configuration in backend/fly.toml uses two shared CPUs, 2 GB of memory and a 1.2 GB calculation-memory budget. A memory refusal or full queue returns a busy response with a retry interval. Website use consumes the same engine capacity. Limits protect the service; they do not establish a measured number of simultaneous users or a response-time guarantee. Check latency, busy responses and resource use during the pilot before expanding access. Multiple instances have independent counters, not a distributed quota. See capacity and lifecycle for precise error behavior.

Deployment controls and verification

FF_MCP_ALLOWED_HOSTS is a comma-separated exact host/authority allowlist, replacing the defaults localhost,127.0.0.1,[::1],btcnow-forwardflow.fly.dev. Add the real public hostname for a custom domain or tunnel. FF_MCP_ALLOWED_ORIGINS replaces the browser-origin defaults http://localhost:8080,http://127.0.0.1:8080,https://btcnow-forwardflow.fly.dev. Requests without an Origin header are allowed; a supplied Origin must match. Empty entries and wildcards are refused at startup. Configure FF_CORS_ORIGINS consistently for browser clients; CORS permission does not bypass MCP origin validation. These controls protect against browser-origin and DNS-rebinding abuse.

The production dependency is the official rmcp SDK, locked in backend/Cargo.lock. It requires Rust 1.88 or later; the Docker builder uses Rust 1.91. The invitation setup reuses the existing FF_API_KEYS deployment secret; it needs no account service, database or volume.

backend/tests/mcp.rs tests the real router and an official SDK client over a loopback HTTP listener. It covers numerical parity with the API, replay, comparison normalization, missing IRRs, unknown inputs, required keys, independent party revocation and rate allowances, host/origin/body limits, shared admission and cancellation. backend/tests/access.rs also verifies REST Bearer authentication. backend/tests/fixtures/mcp_prompts.json supplies 30 representative analyst prompts: 25 authored argument cases exercised by the server tests and five clarification/unsupported-action cases for evaluation in a real assistant. These fixtures do not claim measured natural-language accuracy. Run the tests with the full backend suite:

cd backend
cargo test --release --workspace

After each service change, deploy the tested source revision, verify its build header, connect a supported client such as Claude Code to the deployed HTTPS URL, and evaluate interpretation and follow-up questions. Local protocol tests do not validate a particular account’s permissions, proxy timeouts, or an assistant’s natural-language interpretation.

Local development only

To work on the engine itself, the development endpoint is http://localhost:8080/mcp. It refers to the developer’s own machine. Hosted clients should use the production endpoint above.

FF_MCP_ACCESS=inherit is the application default for development: it follows FF_API_KEYS, including anonymous access when unset. FF_MCP_ACCESS=open explicitly opens MCP even when REST is keyed. Neither is the production invitation setting.