Migrated from monorepo during Forgejo registry consolidation 2026-05-07
The recompute solved each Case's FIRE number with an independent binary search, so Monte-Carlo path noise + the coarse £15k tolerance made Family (Household + 2 kids) tie or even UNDERCUT Household (6 hard inversions + 5 exact ties across the 22 countries) — the ~£20k kids cost quantised to ~£0. Now solve the Cases in increasing-cost order and lower-bound each by the previous Case's target on the SAME return paths: a heavier Case (more spend / +kids / +home) can never need less net worth than a lighter one, so Solo <= Household <= Family <= Family+home holds by construction. tol tightened 15k -> 1k so the genuine but small kids/home increment resolves instead of snapping to the previous grid step. Kids/home were already modelled correctly (verified) — this is purely a solver-resolution + monotonicity fix. Found + verified via the fire-countdown flaw-hunt workflow. 346 tests pass. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> |
||
|---|---|---|
| .github/workflows | ||
| .woodpecker | ||
| alembic | ||
| docs | ||
| fire_planner | ||
| frontend | ||
| tests | ||
| .dockerignore | ||
| .gitignore | ||
| alembic.ini | ||
| CONTEXT.md | ||
| Dockerfile | ||
| PLAYBOOK_VIKTOR.md | ||
| poetry.lock | ||
| pyproject.toml | ||
| README.md | ||
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/— packagetax/— per-jurisdiction tax engines (UK, nomad, Malaysia, Thailand, Cyprus, Bulgaria)returns/— Shiller 1871+ data + block bootstrap samplerstrategies/— Trinity 4% SWR, Guyton-Klinger guardrails, VPWingest/— pulls fromwealthfolio/payslip-ingest/hmrc-syncsimulator.py— vectorised NumPy MC enginescenarios.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 jurisdictionfire_target.py— solves each Case's "FIRE number" (smallest liquid NW where Guyton-Klinger hits the 99% bar); seedocs/adr/0001app.py— FastAPI on-demand/recompute__main__.py—clickCLI:ingest,simulate,recompute-all,recompute-fire-targets,migrate
Common commands
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 definitionmc_run— execution metadata + summary stats per (scenario, run_at)mc_path— sparse storage (top decile, bottom decile, median)projection_yearly— deterministic point projection per scenarioscenario_summary— denormalised fast-read for Grafanafire_target— solved FIRE number per (Case × country × with-home) for the "FIRE Countdown" section of the wealth Grafana dashboard