zfin/docs/reference/config/projections-srf.md
Emil Lerch 74fc219afd
All checks were successful
Generic zig build / build (push) Successful in 5m48s
Generic zig build / publish-macos (push) Successful in 11s
Generic zig build / deploy (push) Successful in 23s
add docs/guides
2026-06-22 14:53:53 -07:00

8.6 KiB

projections.srf reference

projections.srf configures zfin projections: the Monte-Carlo-style retirement simulation run over the Shiller dataset (1871-present). zfin loads it from the same directory as the portfolio. It is optional -- without it, the command runs with sensible defaults (20/30/45-year horizons, 90/95/99% confidence, no accumulation phase).

This page is the field reference. For how the simulation actually works see The retirement projection model; for a guided setup see Plan for retirement.

Record types

Every line starts with a type:: discriminator:

type:: Purpose
config A single configuration field (allocation, horizon, retirement target, contributions, spending).
birthdate A household member's birthdate (drives ages, horizon_age, retirement_age, life-event timing).
event A life event: recurring income or expense (Social Security, pension, tuition, healthcare).
#!srfv1
type::config,target_stock_pct:num:80
type::config,horizon:num:25
type::config,horizon_age:num:95
type::birthdate,date::1981-04-12
type::event,name::Social Security,start_age:num:70,amount:num:38400

config fields

Field Type Description
target_stock_pct num Asset-allocation target (0-100). Sets the simulation's stock/bond blend.
horizon num Distribution-phase length in years. Repeat the line for multiple horizons.
horizon_age num Horizon expressed as an age; resolves to target_age - oldest_current_age. Repeatable.
retirement_age num Age the oldest configured person must reach to retire.
retirement_at date Absolute retirement date (YYYY-MM-DD). Wins over retirement_age if both set.
annual_contribution num Yearly accumulation-phase contribution, in today's dollars.
contribution_inflation_adjusted bool If true (default), contributions grow with CPI year over year.
target_spending num Desired retirement spending, in today's dollars.
target_spending_inflation_adjusted bool If true (default), target spending grows with CPI during distribution.
retirement_target num Annotation on a horizon/horizon_age line that overrides the earliest-retirement promotion rule. Allowed: 90, 95, 99.

birthdate fields

Field Type Description
date date Birthdate (YYYY-MM-DD).
person num Household member (1 default, 2 for a partner).
type::birthdate,date::1981-04-12
type::birthdate,date::1983-09-08,person:num:2

event fields

Life events modify annual cash flow in both phases. Positive amounts are income (offset withdrawals); negative amounts are expenses (added to withdrawals).

Field Type Description
name string Label shown in the Life Events table.
amount num Annual amount. Positive = income, negative = expense.
start_age num Age (of person) at which the event begins.
duration num Optional length in years. Omit for a permanent event.
person num Whose age start_age refers to (1 default, 2 for a partner).
inflation_adjusted bool If true (default), the amount grows with CPI. Set false for a fixed nominal amount.
# Permanent income starting at age 70
type::event,name::Social Security,start_age:num:70,amount:num:38400

# 4-year expense starting at age 50
type::event,name::College Tuition,start_age:num:50,duration:num:4,amount:num:-55000

# A partner's pension
type::event,name::Pension,start_age:num:65,person:num:2,amount:num:24000

The two retirement-planning inputs

projections.srf answers a different question depending on which inputs you set. This is the single most important thing to understand about the file:

You set... zfin answers... Display
A target date (retirement_age / retirement_at) "Given my date, what can I spend?" Accumulation-phase block with a dated headline.
A target spending (target_spending) "Given my spending, when can I retire?" Earliest-retirement grid; one cell is promoted to the headline.
Both Both, back to back Configured date wins the headline; grid renders below for comparison.
Neither Already-retired view "Years until possible retirement: none".

When target_spending is set, the earliest-retirement grid shows, for each (horizon x confidence) pair, the earliest year that sustains the spending. The default promotion rule picks the headline cell by walking horizons longest-to-shortest at 99% confidence, preferring the longest horizon that keeps the oldest person under age 100. Override it with a retirement_target annotation on one horizon line:

# use the 35yr x 95% cell as the headline
type::config,horizon:num:35,retirement_target:num:95

At most one horizon may carry the annotation; configuring more than one drops them all and falls back to the default rule. If the promoted cell is infeasible (no accumulation length <= 50 years sustains the spending), the headline reads "not feasible" and the grid still renders so you can pick a workable anchor.

The example configurations

The five bundled examples are fully-configured walkthroughs of each combination:

examples/... Inputs
pre-retirement-age target date only
pre-retirement-spending target spending only
pre-retirement-spending-target target spending + an explicit (infeasible) anchor
pre-retirement-both target date + target spending
post-retirement neither (distribution-only)
ZFIN_HOME=examples/pre-retirement-both zfin projections

See also


Documentation home