fire-planner/README.md
Viktor Barzin 6efe3b0c31
Some checks failed
Build and Push / lint-and-test (push) Has been cancelled
Build and Push / build (push) Has been cancelled
Build and Push / deploy (push) Has been cancelled
Build and Push / notify-failure (push) Has been cancelled
docs(fire-planner): document FIRE countdown; flag stale playbook
README: add spend_model/geo/fire_target modules, the recompute-fire-targets
command, and the fire_target table. PLAYBOOK_VIKTOR.md: add a superseded-notice
header — it anchors on Cyprus/£1.5M/£60k, but the base case moved to Bulgaria,
real NW is ~£1.13M, and the simulator now drains tax (so its regime-invariant
claim is false). Point readers to the live FIRE Countdown dashboard.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-28 11:53:41 +00:00

62 lines
2.5 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# fire-planner
Risk-adjusted, tax-minimised FIRE retirement planner. Consumes today's
portfolio, savings rate, and RSU vest schedule from sibling services
(`wealthfolio`, `payslip-ingest`, `hmrc-sync`) and returns the after-tax
probability of success for each combination of jurisdiction, withdrawal
strategy, and "year you break UK tax residency".
## Layout
- `fire_planner/` — package
- `tax/` — per-jurisdiction tax engines (UK, nomad, Malaysia, Thailand,
Cyprus, Bulgaria)
- `returns/` — Shiller 1871+ data + block bootstrap sampler
- `strategies/` — Trinity 4% SWR, Guyton-Klinger guardrails, VPW
- `ingest/` — pulls from `wealthfolio` / `payslip-ingest` / `hmrc-sync`
- `simulator.py` — vectorised NumPy MC engine
- `scenarios.py` — Cartesian product over (jurisdiction × strategy ×
leave-UK-year × glide)
- `spend_model.py` — per-Case real-GBP spend, COL-scaled per country (the
FIRE-countdown cases: Solo / Household / Family)
- `geo.py` — COL city → tax jurisdiction
- `fire_target.py` — solves each Case's "FIRE number" (smallest liquid NW
where Guyton-Klinger hits the 99% bar); see `docs/adr/0001`
- `app.py` — FastAPI on-demand `/recompute`
- `__main__.py``click` CLI: `ingest`, `simulate`, `recompute-all`,
`recompute-fire-targets`, `migrate`
## Common commands
```bash
poetry install
pytest -v
mypy .
ruff check .
yapf --recursive .
# Run migrations against the local DB:
DB_CONNECTION_STRING=postgresql+asyncpg://... alembic upgrade head
# CLI
DB_CONNECTION_STRING=... python -m fire_planner ingest
DB_CONNECTION_STRING=... python -m fire_planner simulate --scenario=cyprus-vpw-leave-y3
DB_CONNECTION_STRING=... python -m fire_planner recompute-all
# Solve the FIRE-countdown targets (per Case × country) for the wealth
# Grafana dashboard's "FIRE Countdown" section:
DB_CONNECTION_STRING=... python -m fire_planner recompute-fire-targets --countries all
```
## Schema
Core tables in `fire_planner` schema on `pg-cluster-rw`:
- `account_snapshot` — daily NW per account (Wealthfolio)
- `scenario` — Cartesian-product scenario definition
- `mc_run` — execution metadata + summary stats per (scenario, run_at)
- `mc_path` — sparse storage (top decile, bottom decile, median)
- `projection_yearly` — deterministic point projection per scenario
- `scenario_summary` — denormalised fast-read for Grafana
- `fire_target` — solved FIRE number per (Case × country × with-home) for the
"FIRE Countdown" section of the wealth Grafana dashboard