Calculation catalog › Spending and withdrawals
Display guardrail balance thresholds
The cut and raise balance thresholds of a risk-based guardrail policy in today's dollars, on the base the ledger acts on (today's investable balances, the ledger's first-year real portfolio).
Field: Risk-based guardrail dollar thresholds Unit: US dollars Basis: today's dollars (real) Computed by: UI transformation Status: Complete chain
Appears in: results-table (Risk-based guardrails callout "cut below $X / raise above $Y"); monte-carlo-page (Risk-based dollar guardrails note "cut below $X · raise above $Y"); assumptions-card (Spending card, risk-based guardrails callout "cut spending if the portfolio falls below $X; raise if it rises above $Y")
Calculation records
-
Risk-based guardrail thresholds in dollars Composition record, justified by derivation
Purpose: The portfolio levels at which a risk-based guardrail policy cuts or raises spending, in today's dollars on the base the ledger acts on.
Statement: montecarlo/riskBasedGuardrails.ts#guardrailThresholdDollars publishes, for a risk-based guardrail policy with persisted percents p_L and p_U, (p_L / 100) × B and (p_U / 100) × B in that association, with B = startingInvestableOf(plan), the ledger's first-year real portfolio (the same rows added in the same order, at an inflation factor of exactly 1). With no persisted percent the status is unsolved; with B = 0 the status is no-starting-portfolio and only the percents are published, because the ledger then anchors on the first year the portfolio has a balance; acts is false when both thresholds exist and the cut threshold is not below the raise threshold, where the ledger holds spending every year. The solver persists its fraction as balancePct = balanceThresholdPct(fraction) = round(fraction × 10,000) / 100 (BALANCE_THRESHOLD_PCT_DECIMALS is 2). For the solver's worked edges 1.403671875 and 1.901171875 on a $500,000 portfolio the percents are 140.37 and 190.12 and the thresholds $701,850 and $950,600. Units: today's dollars; percent. Rounding: the percents carry two decimals; the dollars none.
Formula:
lower = (p_L / 100) · B; upper = (p_U / 100) · B; p = round(f · 10000) / 100-
p_L, p_U: Persisted cut and raise thresholds (unit: percent of the starting portfolio) (domain: > 0, two decimals) -
B: Today's investable balances (startingInvestableOf), the ledger's first-year real portfolio (unit: today USD) (domain: >= 0) -
f: The solver's balance fraction at a band edge (unit: 1) (domain: 0.02 to 4)
Timing: from the plan; the same in every year of every path, because the anchor is fixed in the first year
Rounding: the percents to 0.01 by balanceThresholdPct; the dollars none
Limits:
- The published base equals the ledger's anchor only because the ledger's first year has an inflation factor of exactly 1 and adds the same rows in the same order; an evidence case pins the ledger's first-year action on either side of B (a cut at 100.01 percent, a hold at 99.99).
- At a zero starting balance the anchor is known only by simulating (and varies by path in Monte Carlo), so no dollars are published.
- The percents were solved for the balances at the time of the solve; after a balance edit they still act, as percents of the new balance, until the thresholds are solved again.
- The rounding is floating point: at three of the solver's 1,024 lattice points the product lands just below a half and rounds down (76.62 where the exact 76.625 rounds half up to 76.63), at most 0.01 percent of the balance, about a fortieth of the solver's own step.
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: display-guardrail-balance-thresholds.md Mutation receipt: display-guardrail-balance-thresholds.mutation.md
Evidence tests: riskBasedGuardrails.thresholdDollars.evidence.test.ts
Implemented in: riskBasedGuardrails.ts
Is the number of: display-guardrail-balance-thresholds
-
Records that feed this number
-
Risk-based guardrail: one year's cut, raise or hold against real-balance thresholds Model record, justified by derivation
Purpose: The decision that rations discretionary spending when the deflated portfolio leaves its solved balance band.
Statement: adj = (adjustmentPct ?? 10)/100; prev = clamp(prevMultiplier) into [0, max(0, maxMultiplier)]. When startingBalance is not > 0, or currentRealBalance is not finite or is negative: hold at prev. lower = (lowerBalanceThresholdPct/100) x startingBalance when that percent is supplied and > 0, else null; upper likewise from upperBalanceThresholdPct. When both are set and lower >= upper: hold. currentRealBalance < lower: multiplier = clamp(prev - adj), action "cut" when it fell by more than 1e-9, else "hold". currentRealBalance > upper: multiplier = clamp(prev + adj), action "raise" when it rose by more than 1e-9, else "hold". Otherwise hold. Units: balances in real (deflated) dollars; thresholds as percent of the starting real portfolio; the multiplier a fraction. Rounding: none. annualGuardrailFunding.ts deflates the start-of-year portfolio by the year's inflation factor and anchors startingBalance at the first solvent year's real portfolio.
Formula:
L = l/100 x S, U = u/100 x S; m = clamp(prev - adj) if B < L; m = clamp(prev + adj) if B > U; m = prev otherwise, and always when L or U is unset or L >= U-
B: This year's real start-of-year portfolio (unit: usd, real) (domain: B >= 0 and finite) -
S: The starting real portfolio the thresholds are scaled from (unit: usd, real) (domain: S > 0) -
l, u: Lower and upper thresholds as percent of S (unit: percent) (domain: each > 0 when set; l < u) -
adj: Step size as a fraction of the full discretionary layer (unit: 1) (domain: default 0.1) -
m: This year's discretionary multiplier (unit: 1) (domain: 0 <= m <= maxMultiplier)
Timing: once per projection year, before the funding fixed point
Rounding: none
Limits:
- Thresholds come from the shared-path solver (montecarlo/riskBasedGuardrails.ts); a policy whose thresholds are absent, zero or inverted holds, so the mode never acts on unsolved numbers
- The dollar thresholds the pages print (display-guardrail-balance-thresholds) are this step's L and U on the first-year anchor, published by montecarlo/riskBasedGuardrails.ts#guardrailThresholdDollars (the record guardrail-threshold-dollars); at a zero starting balance the anchor is the first year the portfolio has a balance, which only a simulation knows
- Same fixed step and clamp semantics as the withdrawal-rate step; clampRange is module-private and reached through nextBalanceGuardrailMultiplier
- The Monte Carlo action counts and cut-depth percentiles aggregate this decision's action and multiplier exactly as they do for the withdrawal-rate mode
Provenance: derived by
codex, implemented byclaude-subagent.Independent review:
grokGates (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: balance-risk-guardrail-step.md Mutation receipt: balance-risk-guardrail-step.mutation.md
Evidence tests: guardrails.evidence.test.ts
Implemented in: guardrails.ts
Is the number of: spending-guardrail-factor-annual
Feeds: monte-carlo-guardrail-action-counts, monte-carlo-max-cut-depth-percentiles, display-guardrail-balance-thresholds
-
-
Risk-based guardrail thresholds and suggested adjustments by bisection Model record, justified by derivation
Purpose: Finds the portfolio levels where the plan's Monte Carlo success meets each edge of its success band, and the spending change that brings success back to the middle of the band there.
Statement: Let S(f, m) be the success rate of the plan with fixed target spending, with every investable balance scaled by f and target spending by m, on shared seeded Monte Carlo paths (or the value of the optional successProbe(f, m), which replaces that run). The band is L/U percent (the plan's policy, else 70/95) and the recovery target is (L + U)/200. For each edge p = L/100 and U/100: if S(0.02, 1) >= p the outcome is always-above-band; if S(4, 1) < p it is never-reaches-band; otherwise ten bisections on [0.02, 4] move hi to the midpoint when S(mid, 1) >= p and lo otherwise, and the edge is hi, a point 0.02 + k · 3.98/1024 with k a whole number from 1 to 1024 (the smallest such point meeting p), published with balancePct = round(hi · 10,000) / 100, the percent the planner persists. At a solved lower edge f_L the suggested cut bisects m eight times on [max(0.3, required/base), 1], moving lo when S(f_L, m) >= the recovery target, and returns lo, provided the best case S(f_L, lowest m) reaches the target; at a solved upper edge the raise does the same on [1, 2], and reports m = 2 when even S(f_U, 2) stays above the target. annualDollars = |1 − m| · base spending and monthlyDollars = annualDollars / 12. For S(f, m) = min(1, f/(2m)), one $500,000 taxable account, base spending $40,000, no required spending and a 70/95 band: successAtCurrent is 0.5; the edges are k = 356 and 484, balanceFrac 1.403671875 and 1.901171875, balancePct 140.37 and 190.12; the cut multiplier is 0.849609375 ($6,015.625 a year, $501.3020833333333 a month, success after 0.8260689655172412) and the raise multiplier 1.1484375 ($5,937.50 a year, $494.7916666666667 a month, success after 0.8277210884353741); the probe is called 40 times. Units: fractions of starting investable, multipliers, today's dollars. Rounding: none; absolute tolerance 1e-12 on fractions and multipliers and 1e-6 on dollars.
Formula:
edge(p) = min { 0.02 + k · 3.98/1024 : k = 1..1024, S(·, 1) >= p } by ten bisections; m_cut = max { m on the eight-step lattice of [m_min, 1] : S(f_L, m) >= (L + U)/200 }; m_raise likewise on [1, 2]; monthly = |1 − m| · base / 12-
S(f, m): Fixed-target success rate at balance scale f and spending multiplier m (unit: 1) (domain: [0, 1], nondecreasing in f and nonincreasing in m; worksheet min(1, f/(2m))) -
L, U: Success band edges (unit: percent) (domain: L < U; worksheet 70 and 95) -
k: Lattice index of a solved edge (unit: 1) (domain: whole number 1 to 1024; worksheet 356 and 484) -
m: Spending multiplier of a suggested adjustment (unit: 1) (domain: cut [max(0.3, required/base), 1], raise [1, 2]; worksheet 0.849609375 and 1.1484375) -
base: Base annual target spending (unit: usd) (domain: > 0; worksheet 40,000) -
B: Starting investable (risk-based-starting-investable) (unit: usd) (domain: worksheet 500,000)
Timing: once per solve, on today's balances and today's spending
Rounding: none
Limits:
- Monotonicity of success in f and m is assumed, not checked; with a curve that is not monotone the result is a lattice point but not a meaningful edge
- The Spending card always solves under the lognormal model at 12 percent volatility around the plan's own expected returns (allocated accounts use their asset classes' volatilities), with 200 paths and the engine's default seed (monte-carlo-default-seed), whatever model the Monte Carlo page shows; the solve stores the seed beside the thresholds, and thresholds saved before schema v6 carry none and were solved on a seed taken from the plan id, which the card says
- Successes are cached by the balance scale rounded to 1e-6; the lattice step is about 3.9e-3, so two lattice points never share a key. The spending phase is not cached, so successAfter re-evaluates a multiplier the bisection has usually tried already
- With a single path S is 0 or 1, so the two band edges are the same test and return the same point
- balancePct rounds in floating point: at three of the 1,024 lattice points (k = 192, 448 and 704) the product lands just below a half and rounds down, persisting 76.62, 176.12 and 275.62 where the exact values round half up to 76.63, 176.13 and 275.63; the callouts print the persisted percent of today's balances (guardrail-threshold-dollars)
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: risk-based-guardrail-threshold-solver.md Mutation receipt: risk-based-guardrail-threshold-solver.mutation.md
Evidence tests: riskBasedGuardrails.evidence.test.ts
Implemented in: riskBasedGuardrails.ts
Is the number of: risk-based-guardrail-solved-balance-thresholds, risk-based-guardrail-suggested-adjustment-monthly
Feeds: display-guardrail-balance-thresholds, spending-guardrail-factor-annual
-
-
Starting investable: sum of listed account balances Formula record, justified by derivation
Purpose: The dollar base the risk-based guardrail fractions and SWR initial spend apply to.
Statement: Sum current balances of accounts whose type is taxable, equityComp, traditional, Roth, HSA, or cash. Property (home) and other noninvestable types are excluded, including when they carry a value rather than a balance. For taxable $100,000, cash $20,000 and home/property $300,000 the sum is $120,000. Units: dollars. Rounding: none. Exact dollars.
Formula:
B = sum { balance(a) : type(a) in {taxable, equityComp, traditional, roth, hsa, cash} }-
a: A plan account (unit: account) (domain: finite nonnegative balances) -
B: Starting investable (unit: usd) (domain: >= 0)
Timing: once at the start of a solve, on today's balances
Rounding: none
Limits:
- The listed investable set is the same dollar base market paths can fund; property is excluded by type and because it stores value rather than balance
- Basis fields are not added; only the balance field of an investable account contributes
Provenance: derived by
codex, implemented bygrok.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: risk-based-starting-investable.md Mutation receipt: risk-based-starting-investable.mutation.md
Evidence tests: riskBasedGuardrails.evidence.test.ts
Implemented in: riskBasedGuardrails.ts
Feeds: display-guardrail-balance-thresholds, monte-carlo-success-rate, swr-rule-result-initial-annual-spend
-
Transformations
- (persisted percent / 100) × startingInvestableOf(plan), the ledger's first-year anchor, in montecarlo/riskBasedGuardrails.ts#guardrailThresholdDollars (R3); with no starting balance only the percents are published, and acts is false when the cut threshold is not below the raise threshold.
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.