# zfin examples Realistic-but-fictional portfolio configurations for spot-checking zfin features and showing users how the configuration files fit together. Run any zfin command against an example by setting `ZFIN_HOME` to its directory: ```sh ZFIN_HOME=examples/pre-retirement-both zfin projections ZFIN_HOME=examples/pre-retirement-age zfin projections ZFIN_HOME=examples/pre-retirement-spending zfin projections ZFIN_HOME=examples/pre-retirement-spending-target zfin projections ZFIN_HOME=examples/post-retirement zfin projections ZFIN_HOME=examples/pre-retirement-both zfin --tui ``` All names, share counts, account numbers, prices, and life events in these examples are fictional. Do not interpret them as advice. ## Available examples The five scenarios share the same fictional couple and balance sheet (~$1.3M, age ~45, contributing $80k/yr) for the four pre-retirement variants, and a separate retired couple for the distribution example. Only the `projections.srf` configuration differs across the pre-retirement variants — making it easy to see how each retirement-planning input shapes the output. ### Background: how the projection accepts input The simulation always runs the same two-phase model: an **accumulation phase** (contributions in, no spending) followed by a **distribution phase** (spending out, no contributions). What the user configures in `projections.srf` decides which questions the display answers: - **Target retirement date** (`retirement_age` or `retirement_at`) — "Given my retirement date, what can I spend?" Produces the Accumulation phase block: median portfolio at retirement, p10–p90 range, and the dated headline retirement line. - **Target spending** (`target_spending`) — "Given my desired spending, when can I retire?" Produces the Earliest retirement grid (one cell per horizon × confidence) and promotes one cell into the Accumulation phase block as the headline. - **Both** — both blocks render back-to-back. The configured retirement date wins for the headline; the grid is the side-by-side comparison. - **Neither** — distribution-only mode. The Accumulation phase block reduces to a soft "Years until possible retirement: none" line. ### `pre-retirement-age/` **Input: target retirement date only.** `retirement_age:num:65` is set, `target_spending` is not. Output renders: - **Accumulation phase** block — median portfolio at the configured retirement date, p10–p90 range, and the `Years until possible retirement: 19 (2046-04-12, ages 65/62)` line showing both partners' ages at retirement. - Standard Safe Withdrawal table for the configured horizons. - No "Earliest retirement" grid (no spending target to search against). ### `pre-retirement-spending/` **Input: target spending only.** `target_spending:num:80000` is set, `retirement_age`/`retirement_at` are not. Output renders: - **Earliest retirement** grid — one cell per (horizon × confidence) showing the earliest year the household can retire and sustain $80k/yr at that confidence over that distribution horizon. - The **Accumulation phase** block is populated by **promoting one cell** from the grid into the headline retirement line, plus the median portfolio at retirement and p10-p90 range. The default promotion rule walks horizons longest → shortest and picks the longest one whose end year keeps the oldest configured person under age 100, at 99% confidence (most conservative). If even the shortest horizon overshoots, it's used anyway. - The grid stays rendered for transparency — the user can see how the headline cell compares to the rest of the matrix. ### `pre-retirement-spending-target/` Same household and balance sheet as `pre-retirement-spending/`, but with a much higher `target_spending` ($2.4M/yr) AND an explicit `retirement_target:num:99` annotation on the `horizon_age:num:95` record. That combination demonstrates two things at once: - The **explicit override** mechanism: instead of the default promotion rule (longest horizon at 99% confidence, capped at age 100), the user explicitly anchors the headline to the resolved `horizon_age:95 × 99%` cell. The override survives age-resolution — it rides on `horizon_age` records too, not just `horizon`. - The **"not feasible" rendering path**: the annotated cell turns out to be infeasible at this spending level (no value of `accumulation_years` ≤ 50 sustains $2.4M/yr at 99% over a 50-year retirement). The headline line renders "Years until possible retirement: not feasible" instead of a date, and the contribution / median lines below it are suppressed. The full Earliest retirement grid still renders so the user can see which (horizon × confidence) cells DO work and pick a different anchor. Use this variant when the user has a preferred planning anchor that's different from "longest feasible horizon at maximum conservatism." Allowed `retirement_target` values are 90, 95, 99. At most one horizon may carry the annotation; configuring two or more drops them all (warning logged) and falls back to the default rule. ### `pre-retirement-both/` **Inputs: both target retirement date AND target spending.** Both `retirement_age` and `target_spending` are set. Output renders both blocks back-to-back so the user can compare "what I planned" (target retirement date) against "what's earliest feasible" (target spending). The configured retirement date wins for the headline line; the Earliest retirement grid is rendered below for comparison. ### `post-retirement/` A retired couple, ~age 68, ~$2M total. Distribution-only mode (no `target_spending`, no `retirement_age`/`retirement_at`). Demonstrates the legacy projection behavior, including the soft "Years until possible retirement: none" line that confirms the model isn't projecting any pre-retirement growth. Includes a late-life healthcare expense modeled as a life event starting at age 80, plus Social Security already in pay status. Asset allocation target: 60/40 (more bond-heavy than the pre-retirement examples). ## Configuration file map Every example contains: - **`portfolio.srf`** — open lots, one per line. The source of truth for shares, cost basis, and account assignment. - **`accounts.srf`** — tax type and institution metadata for each account name referenced by `portfolio.srf`. - **`metadata.srf`** — sector / geography / asset-class classifications for each symbol. - **`projections.srf`** — retirement projection configuration: birthdates, target allocation, horizons, life events, and (in the pre-retirement variants) the accumulation-phase / earliest-retirement fields. This is the only file that differs across the four pre-retirement variants. There's no `watchlist.srf` in either example. Add one if you're spot-checking watchlist features. ## Symbol selection The examples use symbols whose candle data is commonly cached and kept fresh by ongoing cron jobs: - **Equity ETFs:** SPY, VTI, QQQ, SCHD - **Bond ETF:** AGG If you add a symbol not in this list, run `zfin quote ` once inside the example directory to populate the cache before running analytics commands.