Calculation catalog › Ladders and valuation
Funded ratio result unfunded PV
Essential-spending present value left for the portfolio after guaranteed income.
Field: unfundedPv Unit: US dollars Basis: today's dollars (real) Computed by: Engine Status: Complete chain
Appears in: income-floor (gap riding on the portfolio); insights (income-floor-funded card title and rationale (computeFundedRatio values))
Calculation records
-
Funded ratio: present values of essential spending and guaranteed income Composition record, justified by derivation
Purpose: How much of the essential-spending floor, valued on the TIPS curve, is covered by guaranteed income valued the same way.
Statement: From the deterministic ledger years with calendar year >= fromYear (default: the start year), each year's expenses.requiredSpending and the sum incomes.socialSecurity + pension + annuity + tipsLadder are deflated to today's dollars by the caller-supplied deflator and discounted as real flows at offset t = year - startYear on the TIPS par curve (ladder-real-present-value; t = 0 is undiscounted). essentialSpendingPv = E, guaranteedIncomePv = G, fundedRatioPct = 100·G/E, unfundedPv = max(0, E - G). Returns null when there are no years or E <= 0. Units: real dollars and percent. Rounding: none.
Formula:
E = sum_t d_t S_t/(1 + y(t)/100)^t; G = sum_t d_t I_t/(1 + y(t)/100)^t; ratio = 100 G/E; unfunded = max(0, E - G)-
t: Years from the start year to the ledger year (unit: years) (domain: integer t >= 0) -
d_t: Deflator from nominal year-t dollars to today's dollars (unit: 1) (domain: d_t > 0) -
S_t: Nominal required-floor spending in year t (unit: usd) (domain: S_t >= 0) -
I_t: Nominal guaranteed income in year t (Social Security + pension + annuity + TIPS ladder) (unit: usd) (domain: I_t >= 0) -
y(t): Interpolated par real yield at t years (unit: percent/year) (domain: y(t) > -100)
Timing: end of year t, annual; the start year is t = 0 and undiscounted
Rounding: none
Limits:
- Par yields are treated as spot rates: a planning convention, not a bootstrapped zero curve
- Guaranteed income counts Social Security, pensions, annuities and TIPS-ladder flows only; wages and portfolio income are excluded by design
- Years before fromYear are excluded from both sides; the horizon ends at the last ledger year
- The deflator is supplied by the caller; the record assumes it maps nominal ledger dollars to the same real basis as the curve
- Returns null rather than a ratio when the essential present value is not positive
Provenance: derived by
codex, implemented byclaude-subagent.Independent review:
cursorGates (6 of 6 pass): fixture registers a test: pass; pins resolve: pass; families exist: pass; worksheet exists: pass; mutation receipt exists: pass; provenance independent: pass
Worksheet: funded-ratio-hand-present-value.md Mutation receipt: funded-ratio-hand-present-value.mutation.md
Evidence tests: fundedRatio.evidence.test.ts
Implemented in: fundedRatio.ts
Is the number of: funded-ratio-result-essential-spending-pv, funded-ratio-result-guaranteed-income-pv, funded-ratio-result-funded-ratio-pct, funded-ratio-result-unfunded-pv
-
Records that feed this number
-
Funded ratio: the year the household floor starts to count Model record, justified by derivation
Purpose: The funded ratio counts from the household's later retirement and names whose it is, never from whoever is listed first.
Statement: ladder/fundedRatio.ts#fundedRatioStart returns fromYear = max(startYear, the household's later retirement), the person whose retirement that is, that person's retirement year and the rule that gave it, all from household-later-retirement (a retirement age gives ISO birth year plus that age, or the first year without the person's wages when a wage stream's end age keeps paying past it; a person with none retires in the first year without their wages, else in the start year; a person who never retires in the plan is left out and listed in notRetiring; a tie to the older person, then the smaller id; decision D-PEOPLE-ORDER and the independent review's M4 and N3). When nobody retires in the plan fromYear is null: wages carry the floor throughout, no ratio is counted, the card says so in plain words and the insight does not fire. The funded-ratio card (planner-ui IncomeFloorSection.tsx#FundedRatioCard, shown on Income floor and Results) and the income-floor-funded insight pass fromYear to ladder/fundedRatio.ts#computeFundedRatio, and for a couple name the person and call the ratio the household's. Units: calendar years. Rounding: none.
Formula:
fromYear = max(startYear, householdRetirement(plan, startYear).retirement.year), or null when nobody retires in the plan; person = its personId-
retirementAge: Each person's retirement age, or null (unit: years) (domain: 30 to 80, or null) -
startYear: The projection's first year (unit: calendar year) (domain: integer)
Timing: once per funded-ratio reading
Rounding: none
Limits:
- A convention, not a statute: the household floor is counted from the first year without the last earner's wages, so a couple with one person retired and one working shows the ratio from the later retirement, and wages paid past a retirement age (a wage stream's end age after it) count until they end
- A missing retirement age means the first year without that person's wages, else the start year, and a person who works through the plan is left out, the one rule the FI figures use (household-later-retirement); the card and the insight say which rule applied and who works through the plan
Provenance: derived by
claude, implemented byclaude.Independent review:
codexGates (6 of 6 pass): fixture registers a test: pass; pins resolve: pass; families exist: pass; worksheet exists: pass; mutation receipt exists: pass; provenance independent: pass
Worksheet: funded-ratio-household-start.md Mutation receipt: funded-ratio-household-start.mutation.md
Evidence tests: fundedRatioStart.evidence.test.ts
Implemented in: fundedRatio.ts; incomeFloorFunded.ts; householdRetirement.ts
Feeds: funded-ratio-result-essential-spending-pv, funded-ratio-result-guaranteed-income-pv, funded-ratio-result-funded-ratio-pct, funded-ratio-result-unfunded-pv
-
-
The household's later retirement, one rule for FI and the funded ratio Model record, justified by derivation
Purpose: The year the household stops earning, whose it is and which rule gave it, never whoever is listed first.
Statement: projection/householdRetirement.ts#personRetirement gives each person's retirement year, the first year without their work, and its rule. A wage stream of theirs with gross pay above 0 pays through birthYear + stopAge - 1, stopAge being its end age, else the retirement age (stopAge = endAge ?? retirementAge, as projection/internal/wageIncomeStreams.ts pays), and with neither through their last year alive at the planning age, never after it. With a retirement age the person retires in the later of the ISO birth year plus that age (retirementAge) and the year after their last wage year, when a stream's end age keeps paying past the retirement age (wagesPastRetirementAge); with none, in the year after their last wage year when that year is the start year or later (wagesEnd), otherwise in the start year (startYear). A person retires in the plan only when alive in max(year, startYear): one whose wages run through their last year alive works through the plan, a retirement age past the planning age is never reached, and a planning age ended before the start retires no one. projection/householdRetirement.ts#householdRetirement leaves those people out (notRetiring) and takes the latest of the rest, a tie going to the older person and then the smaller id by ordinal comparison, so list order never decides it; with nobody retiring in the plan there is no household retirement, and no FI figure, Coast-FIRE figure or funded ratio is priced (decision D-PEOPLE-ORDER; the independent review's M4 and N3). The FI figures (projection/compare.ts#summarizeProjection) and the funded ratio (ladder/fundedRatio.ts#fundedRatioStart) both read it, and projection/householdRetirement.ts#householdRetirementClause and #notRetiringClause word it for every page: they name the person, say which rule applied, and name anyone who works through the plan. Sam, born 1964 with no retirement age, planning age 95 and wages with no end age, works through the plan, so with Alex, born 1962 retiring at 66, the household retires in Alex's 2028; Gus, born 1966 with a retirement age of 65 and wages to age 75, is paid through 2040 and retires in 2041 (round-one review of #765). Units: calendar year. Rounding: none.
Formula:
year(p) = retirementAge !== null ? (lastWage(p) !== null ? max(birthYear + retirementAge, lastWage(p) + 1) : birthYear + retirementAge) : lastWage(p) !== null and lastWage(p) >= startYear ? lastWage(p) + 1 : startYear, lastWage(p) = max over p's paying wage streams of min(stopAge !== null ? birthYear + stopAge - 1 : birthYear + planningAge, birthYear + planningAge) with stopAge = endAge ?? retirementAge, and null when p has no paying wage stream; in(p) = max(year(p), startYear) <= birthYear + planningAge; household = argmax year over the people with in(p), ties to the earlier dob then the smaller id, none when nobody has in(p)-
year(p): A person's retirement year, the first year without their work (unit: calendar year) (domain: integer) -
lastWage(p): The last year one of their wage streams pays, null when none pays (unit: calendar year) (domain: integer or null) -
in(p): Whether the person is alive in the year their retirement would be priced (unit: boolean) (domain: true or false)
Timing: once per reading, from the plan (not a Monte Carlo path's sampled deaths)
Rounding: none
Limits:
- A convention, not a statute: a retirement age is the first year without wages unless a wage stream's end age runs past it, and then the wages decide; a person who never stops earning is said to work through the plan, not given a year
- A person who retires after the other person's planning age has ended still sets the household's retirement: the priced year is one they are alive in, whether or not their partner is
- Wages are the plan's only earned income; the rule reads the plan's wage streams and planning ages, not a Monte Carlo path's sampled death
Provenance: derived by
claude, implemented byclaude.Independent review:
codexGates (6 of 6 pass): fixture registers a test: pass; pins resolve: pass; families exist: pass; worksheet exists: pass; mutation receipt exists: pass; provenance independent: pass
Worksheet: household-later-retirement.md Mutation receipt: household-later-retirement.mutation.md
Evidence tests: householdRetirement.evidence.test.ts
Implemented in: householdRetirement.ts
Feeds: projection-summary-fi-number, projection-summary-fi-age, projection-summary-coast-fire-number, projection-summary-fi-year, projection-summary-average-pre-retirement-savings-rate-pct, funded-ratio-result-essential-spending-pv, funded-ratio-result-guaranteed-income-pv, funded-ratio-result-funded-ratio-pct, funded-ratio-result-unfunded-pv
-
-
Real present value of a cash-flow stream on the TIPS curve Formula record, justified by derivation
Purpose: What it would cost today to defease each future real dollar with a Treasury of matching maturity.
Statement: For flows (t, C_t) with t years from now and C_t in real dollars, PV = sum of C_t/(1 + y(t)/100)^t, where y(t) is the interpolated par real yield at maturity t with flat endpoints. A flow at t = 0 is added undiscounted; flows with t < 0 or C_t = 0 are skipped. Units: real (today's) dollars. Domain: finite flows, t >= 0, y(t) > -100%. Rounding: none.
Formula:
PV = sum_t C_t / (1 + y(t)/100)^t; the t = 0 term is C_0-
t: Years from now to the flow (unit: years) (domain: t >= 0) -
C_t: Real amount of the flow at t (unit: usd) (domain: finite) -
y(t): Interpolated par real yield at t years (unit: percent/year) (domain: y(t) > -100) -
PV: Present value of the stream (unit: usd) (domain: finite)
Timing: end of year t, annual; t = 0 is today
Rounding: none
Limits:
- Par yields are treated as spot rates; this is a planning approximation, not a bootstrapped zero curve
- Negative offsets and zero amounts are dropped silently rather than rejected
- A non-integer offset is discounted with a fractional exponent; no current caller supplies one
Provenance: derived by
codex, implemented byclaude-subagent.Independent review:
cursorGates (6 of 6 pass): fixture registers a test: pass; pins resolve: pass; families exist: pass; worksheet exists: pass; mutation receipt exists: pass; provenance independent: pass
Worksheet: ladder-real-present-value.md Mutation receipt: ladder-real-present-value.mutation.md
Evidence tests: ladderMath.evidence.test.ts
Implemented in: ladderMath.ts
Feeds: funded-ratio-result-essential-spending-pv, funded-ratio-result-guaranteed-income-pv, funded-ratio-result-funded-ratio-pct, funded-ratio-result-unfunded-pv, ladder-rung-cost
-
-
Par real yield at a maturity: linear interpolation with flat endpoints Formula record, justified by derivation
Purpose: The real yield the ladder engine reads off the TIPS curve at any maturity.
Statement: Given a curve of (maturity m_i years, par real yield y_i percent) points sorted ascending and a finite maturity m, the yield is y_first when m <= m_first, y_last when m >= m_last, and otherwise y_i + (m - m_i)(y_{i+1} - y_i)/(m_{i+1} - m_i) on the first segment with m <= m_{i+1}. An empty curve yields 0. Units: percent per year, real. Rounding: none; the production function returns a binary float.
Formula:
y(m) = y_i + (m - m_i)(y_{i+1} - y_i)/(m_{i+1} - m_i) for m_i <= m <= m_{i+1}; y(m) = y_first for m <= m_first; y(m) = y_last for m >= m_last-
m: Maturity, years from the anchor year (unit: years) (domain: finite m) -
m_i: Maturity of curve point i (unit: years) (domain: strictly ascending in i) -
y_i: Par real yield at curve point i (unit: percent/year) (domain: y_i > -100) -
y(m): Interpolated par real yield at m (unit: percent/year) (domain: between the yields of the bracketing points)
Timing: annual maturities; time-invariant lookup on one dated curve
Rounding: none
Limits:
- Endpoints are held flat, never extrapolated: a maturity beyond the last point reads the last yield, and one before the first point reads the first
- The curve must already be sorted ascending by maturity; the function neither sorts nor validates it
- An empty curve returns 0% rather than refusing
- Every caller consumes the interpolated par yield as a spot rate (the par-as-spot planning approximation documented in the ladderMath.ts header)
Provenance: derived by
codex, implemented byclaude-subagent.Independent review:
cursorGates (6 of 6 pass): fixture registers a test: pass; pins resolve: pass; families exist: pass; worksheet exists: pass; mutation receipt exists: pass; provenance independent: pass
Worksheet: ladder-real-yield-interpolation.md Mutation receipt: ladder-real-yield-interpolation.mutation.md
Evidence tests: ladderMath.evidence.test.ts
Implemented in: ladderMath.ts
Feeds: income-floor-ladder-yield-pct, ladder-rung-coupon-rate-pct, funded-ratio-result-essential-spending-pv, funded-ratio-result-guaranteed-income-pv, funded-ratio-result-funded-ratio-pct, funded-ratio-result-unfunded-pv
-
-
Embedded Treasury par real-yield curve, 2026-06-30 Data record, justified by dataset
Purpose: The offline TIPS real-yield snapshot every ladder quote and funded ratio discounts on.
Statement: Five par real yields in percent per year at 5, 7, 10, 20 and 30 years, dated 2026-06-30, taken from the U.S. Treasury Daily Par Real Yield Curve and stored as {asOfIso, source, points[]} sorted ascending by maturity. Consumers interpolate linearly between points and hold the endpoints flat (ladder-real-yield-interpolation). The stored row is the official 2026-06-30 row exactly as Treasury publishes it: 1.93, 2.06, 2.20, 2.54 and 2.73. Rounding: none; the yields keep the hundredth of a percent Treasury publishes.
Limits:
- Stored exactly as published, with no rounding to the nearest 5 basis points (decision D-TREASURY, 2026-09-25). Until 2026-09-27 the stored row was 1.85/2.05/2.25/2.55/2.70, which the worksheet showed was neither the official row nor its nearest 5 basis points: 8 basis points below the official row at 5 years, 1 below at 7, 5 above at 10, 1 above at 20 and 3 below at 30. Every ladder quote and funded ratio priced on the curve moved with the correction
- Maturities below 5 years read the 5-year yield and above 30 years the 30-year yield (flat endpoints)
- Par yields are consumed as spot rates by every ladder and funded-ratio calculation
- Refresh cadence is annual with the published parameters; the opt-in FedInvest fetch never replaces this snapshot
Dataset: U.S. Department of the Treasury, Daily Treasury Par Real Yield Curve Rates, row dated 06/30/2026 (5, 7, 10, 20 and 30 year columns) , as of 2026-06-30 , retrieved 2026-09-27
Provenance: derived by
claude, implemented byclaude-subagent.Independent review:
codexGates (6 of 6 pass): fixture registers a test: pass; pins resolve: pass; families exist: pass; worksheet exists: pass; mutation receipt exists: pass; provenance independent: pass
Worksheet: treasury-real-yield-curve-2026.md Mutation receipt: treasury-real-yield-curve-2026.mutation.md
Evidence tests: realYieldCurve2026.evidence.test.ts
Implemented in: realYieldCurve2026.ts
Feeds: income-floor-ladder-yield-pct, ladder-rung-cost, ladder-rung-coupon-rate-pct, ladder-build-total-cost, funded-ratio-result-essential-spending-pv, funded-ratio-result-guaranteed-income-pv, funded-ratio-result-funded-ratio-pct, funded-ratio-result-unfunded-pv
Engine source: engine/src/ladder/fundedRatio.ts
(FundedRatioResult.unfundedPv)
The evidence links on this page (each record's worksheet, mutation receipt, evidence tests
and implementation, and the source links) are pinned to engine commit
2624dbdba9fd, committed 2026-10-11.
The live calculation index and
output-family census links point at the repository's main
branch, so you can check this snapshot against them.