Calculation catalog › Monte Carlo
Monte carlo max cut depth percentiles
Among paths that cut at least once, the median and 90th-percentile deepest cut as a fraction of the discretionary layer.
Field: Deepest cut, median and p90 Unit: percent Basis: no dollar basis Computed by: Engine Status: Complete chain
Appears in: monte-carlo-page (Adjustment tile "Typical / worst-case cut depth")
Calculation records
-
Guardrail adjustment shares, depths, durations and ending surplus Formula record, justified by derivation
Purpose: How likely a mid-course spending cut is, how deep and how long it runs among the paths that cut, and how often the plan still ends with something left.
Statement: pathsWithCut is the share of all paths with at least one cut year and pathsWithRaise the share with at least one raise action. Among the cut paths only: averageCutYears is their mean guardrailCutYears, p90CutYears its interpolated 90th percentile, medianMaxCutDepth and p90MaxCutDepth the interpolated 50th and 90th of their maxGuardrailCutDepth, and averageLongestCutSpellYears their mean longestGuardrailCutSpellYears. probEndingSurplus is the share of all paths whose ending after-tax estate is strictly positive; probEndingAboveBequestTarget is the share of the paths carrying a target that cleared it, and exactly null when no path carries one. For the worksheet's five paths: 0.6, 0.6, 14/3, 7.2, 0.3, 0.54, 3 years, 0.6 and 0.6. Units: shares in [0, 1], years, and depth as a fraction of the discretionary layer. Rounding: none; absolute 1e-9, with the no-target null compared exactly.
Formula:
pathsWithCut = |{p : cutYears_p > 0}| / N; among those paths, averageCutYears = mean(cutYears), p90 by interpolation at 0.9(m-1); probEndingSurplus = |{p : estate_p > 0}| / N-
N: All paths (unit: paths) (domain: worksheet 5) -
m: Paths with at least one cut year (unit: paths) (domain: worksheet 3) -
cutYears_p, spell_p, depth_p: One path's cut years, longest consecutive cut spell and deepest cut (unit: years, years, fraction) (domain: worksheet [2,4,8], [1,3,5], [0.10,0.30,0.60] among cut paths) -
estate_p: One path's ending after-tax estate (unit: usd) (domain: worksheet 0, 100, 200, 0, 50)
Timing: once per aggregation, over the completed path set
Rounding: none
Limits:
- Every cut statistic divides by the cut paths, not by all paths: over all five, average cut years would be 2.8 and the average longest spell 1.8 years
- A zero ending estate is not a surplus; probEndingSurplus takes a strictly positive estate, so counting zeros would give 1 rather than 0.6
- probEndingAboveBequestTarget divides by the paths whose comparison is non-null, and is null only when no path carries a target; all five worksheet paths carry one, so that denominator and the path count coincide here
- The cut statistics are all zero for a plan with no guardrail policy, which is not the same claim as a plan that cut nothing
- guardrailActionCounts is summed elsewhere (monte-carlo-guardrail-action-counts); this record publishes path shares and conditional magnitudes only
- The evidence supplies the guardrail fields, endingAfterTaxEstate and endingAboveBequestTarget only; every other MonteCarloPath field is at the test’s neutral (no depletion or shortfall, floor and target met, attainment 1, no goal activity)
Provenance: derived by
codex, implemented byclaude.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: monte-carlo-guardrail-adjustments.md Mutation receipt: monte-carlo-guardrail-adjustments.mutation.md
Evidence tests: run.evidence.test.ts
Implemented in: run.ts
Is the number of: monte-carlo-paths-with-cut-share, monte-carlo-paths-with-raise-share, monte-carlo-cut-years, monte-carlo-max-cut-depth-percentiles, monte-carlo-average-longest-cut-spell-years, monte-carlo-prob-ending-surplus, monte-carlo-prob-ending-above-bequest-target
-
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
-
-
Withdrawal-rate guardrail: one year's cut, raise or hold of the discretionary multiplier Model record, justified by derivation
Purpose: The Guyton-Klinger-style decision that rations discretionary spending when the withdrawal rate leaves its band.
Statement: upper = (upperGuardrailPct ?? 120)/100; lower = (lowerGuardrailPct ?? 80)/100; adj = (adjustmentPct ?? 10)/100; prev = clamp(prevMultiplier) into [0, max(0, maxMultiplier)] with maxMultiplier defaulting to 1 and a non-finite prev read as min(1, that ceiling). When startingRate is not > 0, or currentRate is not finite or is negative: hold at prev. Otherwise ratio = currentRate/startingRate. ratio > upper: multiplier = clamp(prev - adj), action "cut" when it fell by more than 1e-9, else "hold". ratio < lower: multiplier = clamp(prev + adj), action "raise" when it rose by more than 1e-9, else "hold". Otherwise hold. Units: the multiplier is a fraction of the discretionary layer; the rates are ratios of target spending to the start-of-year portfolio whose units cancel. Rounding: none.
Formula:
ratio = current/starting; m = clamp(prev - adj) if ratio > upper; m = clamp(prev + adj) if ratio < lower; m = prev otherwise; clamp into [0, maxMultiplier]-
prev: Last year's discretionary multiplier (unit: 1) (domain: clamped into [0, maxMultiplier]) -
current, starting: This year's and the first year's withdrawal rate (unit: 1) (domain: starting > 0, current >= 0 and finite) -
upper, lower: Band edges as fractions of the starting rate (unit: 1) (domain: defaults 1.2 and 0.8) -
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:
- The signal is gross recurring target spending over the start-of-year portfolio, not the net portfolio draw: a documented simplification that keeps the rule non-circular with the spending it decides
- The step is a fixed adjustmentPct of the full discretionary layer, not a percentage of the previous multiplier
- A cut or raise the clamp absorbs is reported as hold, so downstream counts reflect real moves only
- clampRange is module-private; the evidence reaches it through nextGuardrailMultiplier
- The Monte Carlo counts each year's action (run.ts) and takes 1 - guardrailFactor as the year's cut depth, so both aggregates are fed by this decision
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: withdrawal-rate-guardrail-step.md Mutation receipt: withdrawal-rate-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
-
Engine source: engine/src/montecarlo/run.ts
(MonteCarloSummary.adjustments.medianMaxCutDepth / p90MaxCutDepth)
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.