141 lines
6.2 KiB
Markdown
141 lines
6.2 KiB
Markdown
# The retirement projection model
|
|
|
|
[`zfin projections`](../reference/cli/projections.md) simulates your
|
|
retirement portfolio against real market history. This page explains
|
|
the model so you can trust -- and correctly distrust -- its output. For
|
|
how to configure it, see the
|
|
[`projections.srf` reference](../reference/config/projections-srf.md)
|
|
and [Plan for retirement](../guides/plan-retirement.md).
|
|
|
|
## Historical simulation, not a formula
|
|
|
|
Rather than assume a single average return, zfin replays your portfolio
|
|
through actual historical sequences drawn from the **Shiller dataset**
|
|
(US equity total returns and CPI back to 1871). Each simulated run uses
|
|
a real historical path of returns and inflation, so the spread of
|
|
outcomes reflects real sequences -- including bad-timing sequences like
|
|
retiring into 1929, 1973, or 2000. This is the same family of method
|
|
as FIRECalc.
|
|
|
|
## Two phases
|
|
|
|
Every projection runs the same two phases in order:
|
|
|
|
1. **Accumulation** -- contributions added each year, no spending. Its
|
|
length comes from your retirement-date input. With no input, it's
|
|
zero years (an already-retired view).
|
|
2. **Distribution** -- annual spending withdrawn (CPI-adjusted by
|
|
default), no contributions. Its length is the configured `horizon`.
|
|
|
|
[Life events](../reference/config/projections-srf.md#event-fields)
|
|
(Social Security, pensions, tuition, healthcare) adjust the cash flow
|
|
in both phases.
|
|
|
|
## How inflation is handled
|
|
|
|
Inflation isn't a fixed assumption. Each historical cycle uses that
|
|
start year's **actual CPI sequence** alongside its actual returns (the
|
|
Shiller dataset carries both), so a cycle beginning in 1966 replays
|
|
1966's stagflation while one beginning in 2009 replays low-inflation
|
|
years.
|
|
|
|
The simulation runs in **nominal dollars**, which means the output
|
|
mixes two units -- and knowing which is which is the difference between
|
|
a sensible plan and a badly misread one:
|
|
|
|
- **Flows are entered in today's dollars and inflated forward.** Your
|
|
`annual_contribution`, `target_spending`, and inflation-adjusted life
|
|
events are amounts in *today's* dollars; each simulated year the model
|
|
multiplies them by that cycle's cumulative CPI, holding their
|
|
purchasing power constant. Set `contribution_inflation_adjusted`,
|
|
`target_spending_inflation_adjusted`, or an event's
|
|
`inflation_adjusted` to `false` to pin a flow at a flat nominal amount
|
|
instead (e.g. a fixed pension with no COLA).
|
|
- **Safe-withdrawal figures are in today's dollars.** "You could spend
|
|
~$264k/yr at 99%" means ~$264k of *today's* purchasing power, with the
|
|
actual dollar amount rising each retirement year to keep pace with
|
|
inflation.
|
|
- **Portfolio and terminal values are nominal (future dollars).** The
|
|
"Median portfolio at retirement" and the
|
|
`Terminal Portfolio Value (nominal, ...)` percentiles are **not**
|
|
inflation-adjusted. A ~$244M median balance 50 years out is heavily
|
|
inflated dollars, not $244M of today's purchasing power -- judge it
|
|
against the inflated spending it has to support, never against today's
|
|
prices.
|
|
|
|
This split is deliberate and matches FIRECalc: you plan spending in real
|
|
(today's) terms while the balance compounds in nominal terms.
|
|
|
|
## Percentile bands
|
|
|
|
Across all the historical runs, zfin reports the distribution of
|
|
outcomes rather than a single number:
|
|
|
|
- **p10 (pessimistic)** -- only 10% of histories did worse.
|
|
- **p50 (median)** -- the middle outcome.
|
|
- **p90 (optimistic)** -- only 10% did better.
|
|
|
|
```
|
|
Terminal Portfolio Value (nominal, at 99% withdrawal rate)
|
|
25 Year 35 Year
|
|
Pessimistic (p10) $6,739,560.02 $11,597,557.94
|
|
Median (p50) $30,023,255.68 $66,794,741.87
|
|
Optimistic (p90) $103,184,321.05 $279,372,182.75
|
|
```
|
|
|
|
The wide spread is the point: it shows sequence-of-returns risk
|
|
honestly instead of hiding it behind an average.
|
|
|
|
## Confidence and safe withdrawal
|
|
|
|
The **Safe Withdrawal** table answers "how much could I spend and still
|
|
not run out?" at chosen confidence levels (90/95/99%). A 99% safe
|
|
withdrawal is the spending level that survived 99% of historical
|
|
sequences over that horizon -- the most conservative. Higher confidence
|
|
and longer horizons both lower the safe number.
|
|
|
|
## The earliest-retirement search
|
|
|
|
When you set a `target_spending` instead of a date, zfin inverts the
|
|
question: for each (horizon x confidence) cell it searches for the
|
|
**earliest** accumulation length (up to 50 years) that sustains your
|
|
spending, and renders the grid of answers. One cell is promoted to the
|
|
headline (see
|
|
[promotion rules](../reference/config/projections-srf.md#the-two-retirement-planning-inputs)).
|
|
If no length within the cap works, the cell is **infeasible** -- shown
|
|
honestly rather than fudged.
|
|
|
|
## The caveat that matters most
|
|
|
|
zfin states this loudly by design, and so does this page:
|
|
|
|
> The actuals overlay and evaluation views
|
|
> (`--overlay-actuals`, `--convergence`, `--return-backtest`) tell you
|
|
> whether the model was **directionally honest** -- did your real
|
|
> trajectory fall within the bands it would have drawn. They do **not**
|
|
> tell you whether a safe-withdrawal claim is **accurate**. An SWR
|
|
> claim is a 30-year claim; there is at most ~12 years of weekly
|
|
> history and a year or two of native snapshots to check it against.
|
|
> No one will have data to validate a full-retirement SWR within our
|
|
> lifetimes.
|
|
|
|
Treat the projection as a disciplined way to compare scenarios and
|
|
visualize sequence risk -- not as a promise about your specific future.
|
|
|
|
## Assumptions to keep in mind
|
|
|
|
- **Allocation** is a single stock/bond blend (`target_stock_pct`), not
|
|
your exact holdings.
|
|
- **Inflation** comes from each historical cycle's own CPI; flows are
|
|
real (today's-dollar) and balances are nominal. See
|
|
[How inflation is handled](#how-inflation-is-handled).
|
|
- **Taxes** are not modeled. Withdrawal figures are pre-tax.
|
|
- **Imported-value overlays** scale today's allocation to a historical
|
|
total when lot-level history isn't available, because a `liquid::`
|
|
row can't reconstruct past composition.
|
|
|
|
## See also
|
|
|
|
- [Plan for retirement](../guides/plan-retirement.md) -- the guided walkthrough.
|
|
- [`projections.srf` reference](../reference/config/projections-srf.md) -- every input.
|
|
- [`zfin projections`](../reference/cli/projections.md) -- flags and evaluation views.
|