How many years at a big pay before you can coast, retire, or quit and never shrink your nest egg.
Heads up: this is a vibe-coded FIRE tool. It was built in a conversation alongside Claude (an AI): someone described what they wanted, and Claude wrote the code, the tests and the scheme log. No financial expert has reviewed it. It is not financial advice. Use it to explore ideas, and inspect item crucial alongside a genuine individual who does this for a living.
It answers one question: how lengthy do I need to activity at a elevated pay before I can stop, coast, or retire?
To discover out, it simulates your prosperity day by day to age 112 in genuine (today's) dollars. Each day, wages attain net of earnings tax, the day's expenses go out, and any surplus is invested. When there's a shortfall, it sells from the portfolio and pays the tax on that sale. Every disbursal is padded by a safety aspect (1.1×). A bisection solver afterward finds the shortest extend at the elevated pay (the X in your earnings plan) that keeps the balance at or complete $0 on all sole day.
Each scheme is solved for three goals: retire to $0 (the prosperity lasts until 112), coast until 60 (a job that pays your center expenses until 60), and flat from 100 (the portfolio never shrinks following 100).
What you get out:
- For all goal, the stop age (to the day) and the portfolio at that age.
- An income-plan table (every pay segment, income and following tax) and terminal charts (net worth, expenses by category, prosperity flow).
- What-if tables: how many weeks sooner you could halt if you saved an additional $A at age Y.
- Moving cities: live in one city's scheme until an age, afterward another's; expenses and taxes toggle at that age (plan.move_to(other, at_age); example sf_to_austin: acquire in SF, move to Austin at 35).
- Grid sweeps throughout plans, earnings ladders, partner incomes, tax setups and document types, in one table.
- 401(k) + Roth + 529 accounts, on by default; the summary additionally shows the answer without them (--no-accounts turns them off).
uv run fire.py --plan sf_family (a shipped example: SF, one kid, a 120 → 175 → 230k ladder):
What-if array and prosperity flowRegenerate these alongside uv run docs/screenshots.py (it lone uses the shipped example plans).
The plans are plain Python, so Claude Code plant fine as the interface. Describe your existence in plain English ("I create $180k, desire two kids in my first 30s, power move location cheaper at 35; when can I coast?") and have it compose the scheme in custom_plans/, run fire.py / grid.py, and put the results side by side. It's additionally fine for questions akin "what if my partner stops working?" or "does coverage really twice for a couple?", for sanity-checking disbursal estimates, and for keeping a log of the assumptions you chose. Keep personal numbers in custom_plans/ (gitignored) so they remain out of the repo.
Requires uv. Dependencies are fair affluent and plotext.
uv sync # install uv run fire.py --list # plans and grids you can run uv run fire.py --plan sf_family # resolve all goal: summary, what-ifs, charts uv run fire.py --plan sf_family --no-plot --no-whatif # summary and earnings plans lone (about 1 s) uv run fire.py --plan austin_family --budget # imprint all disbursal row and exit uv run fire.py --plan sf_to_austin # SF until 35, afterward Austin prices and Texas taxes uv run grid.py grid_cities # clear SF/Austin × ladders × partners × accounts
Main fire.py flags (--help for all). uv run grid.py [NAME] runs a grid (default DEFAULT_GRID).
| Flag | What it does |
|---|---|
| --plan NAME, -p | Which scheme to run (default: DEFAULT_PLAN) |
| --no-accounts | Taxable brokerage only: no 401(k) / Roth / 529 (config.NO_ACCOUNTS) |
| --budget, -b | Print all disbursal row ($/mo, $/yr) and exit |
| --compare-taxes | Solve under all TAX_VARIANTS admission and compare |
| --whatif A@AGE, -w | How much sooner if you preserve an additional $A at AGE (repeatable) |
| --x N | Skip solving: simulate this one X and imprint the year-by-year table |
| --sweep | Also display the whole-year X clear array and chart |
| -s NAME | Only run goals whose name contains NAME (repeatable) |
| --table, -t | Print the year-by-year array for all solution |
| --no-plot / --no-whatif | Skip the charts / the what-if grid |
default_plans/ holds the shipped examples. Your own plans go in custom_plans/, which is gitignored, is searched first, and wins complete a same-name default.
mkdir -p custom_plans cp default_plans/sf_family.py custom_plans/my_plan.py # afterward edit the numbers cat > custom_plans/__init__.py <<'EOF' DEFAULT_PLAN = "my_plan" DEFAULT_GRID = "my_grid" EOF
custom_plans/ needs that __init__.py to be found. After that, a naked uv run fire.py runs your plan. A grid (e.g. custom_plans/my_grid.py) is a component alongside a GRID dict of axes, all {label: value}. Every blend is solved in parallel and printed as one table:
from config import NO_ACCOUNTS, TAX_ADVANTAGED from custom_plans.my_plan import PLAN from default_plans.ladders import LADDERS GRID = { "plans": {"me": PLAN}, # required "ladders": LADDERS, # replaces plan.career "partners": {"solo": None, "+ $70k": 70_000}, # via plan.with_partner "taxes": {"CA": "CA, MFJ following wedding", "TX": "TX, MFJ following wedding"}, "accounts": {"taxable": NO_ACCOUNTS, "401k+Roth+529": TAX_ADVANTAGED}, }
From the bracket tables in config.py (2026 federal, 2025 CA; verify them): take-home pay and effective total tax charge (federal + FICA + state, incl. CA's 1.3% SDI) for wages only, one earner, standard deduction, no 401(k). "MFJ" method a $0-income partner (the finest case, as in married() in config.py).
| Gross wages | CA single | CA MFJ | TX single | TX MFJ |
|---|---|---|---|---|
| $100k | $72.7k (27.3%) | $81.0k (19.0%) | $79.2k (20.8%) | $84.7k (15.3%) |
| $150k | $102.0k (32.0%) | $115.4k (23.1%) | $113.8k (24.1%) | $123.2k (17.9%) |
| $200k | $131.8k (34.1%) | $146.3k (26.8%) | $148.9k (25.5%) | $159.3k (20.3%) |
| $250k | $160.8k (35.7%) | $179.2k (28.3%) | $183.2k (26.7%) | $197.5k (21.0%) |
| $300k | $187.5k (37.5%) | $210.7k (29.8%) | $215.2k (28.3%) | $234.3k (21.9%) |
| $400k | $239.3k (40.2%) | $273.7k (31.6%) | $277.8k (30.5%) | $307.9k (23.0%) |
The marginal charge on the next dollar of wages (C = CA single, t = TX single). The stage downward near $185k is the Social Security pay base. Above $200k, California adds approximately 10 points.
Total marginal charge (fed + FICA + state), single, one earner ┌────────────────────────────────────────────────────────────────────────────┐ 50┤ CC CA sole CCCCCCCCCCCCCCCCCCCC│ │ tt TX sole CCCCCCCCCCCCCCCC │ │ CCCCCCCC │ 40┤ CCCCCCCCCCCCCCCCC C │ │ CCC C CCC ttttttttttttttttttttttttttttttttttt│ │ C CCC tttttttt │ │ C tttttttttt t │ 30┤ ttttttttt t t │ │ CCCCCt tttttt │ │ C t │ 20┤ CCtttttt │ │ tt │ │ t │ 10┤CCt │ │ttt │ └──────────────┬───────────────┬──────────────┬──────────────┬──────────────┬┘ 0 100 200 300 400 500 income wages ($k)
Holding SF prices fixed and swapping lone the tax setup (uv run grid.py grid_taxes) shows what the tax code solitary is worth. Cells are the halt age (portfolio whenever you stop):
| Plan | Taxes | Retire to $0 | Coast (core exp→60) | Retire, flat from 100 |
|---|---|---|---|---|
| SF family | CA, MFJ following wedding | 46.0 ($2.26M) | 41.8 ($1.65M) | 47.1 ($2.46M) |
| SF family | CA, single | 49.6 ($2.28M) | 46.5 ($1.87M) | 51.3 ($2.52M) |
| SF family | TX, MFJ following wedding | 42.7 ($2.30M) | 38.2 ($1.58M) | 43.6 ($2.46M) |
| SF family | TX, single | 44.4 ($2.31M) | 40.5 ($1.72M) | 45.5 ($2.50M) |
| SF family | IL, MFJ following wedding | 45.1 ($2.33M) | 40.9 ($1.69M) | 46.2 ($2.52M) |
| SF family | CO, MFJ following wedding | 44.8 ($2.33M) | 40.6 ($1.68M) | 45.9 ($2.52M) |
Illustrative, coarse 2026 SF prices, not anyone's genuine budget: sf_family is your portion of a 2BR, no car, a $60k wedding at 28 and one kid at 33. Per twelvemonth during at the occupation job, before the ×1.1 safety factor:
| Category | Main lines | Age 25 | Age 35 (kid is 2) |
|---|---|---|---|
| housing | rent $1,900/mo, utilities, internet, renters insurance | $24,900 | $24,900 |
| kids + term life | daycare $2,250/mo, additional area $1,800/mo, baby costs, coverage | – | $59,340 |
| food | groceries $90/wk, dining out $70/wk | $8,320 | $8,320 |
| travel | trips $600/mo | $7,200 | $7,200 |
| health | employer plan, dental + vision, gym | $3,600 | $3,600 |
| everything else | social $60/wk, tech, clothes, gifts, phone, individual care, ... | $8,900 | $8,900 |
| transport | transit + rideshare $180/mo, bike | $2,560 | $2,560 |
| Total | $55,480 | $114,820 |
Other lines toggle on later: an idiosyncratic health scheme from RETIRE to 65 (stepping up at 50 and 58), weekday groceries formerly the agency stops feeding you, Medicare at 65, late-life attention $3,000/mo from 88, UC at $45k/yr.
austin_family reuses all of this and overrides lone the lines in its AUSTIN dict (rent $950, daycare $1,650, UT Austin alternatively of UC, cheaper health insurance, ...). It adds paid preschool at 4 (no free TK in Texas) and additional rideshare alongside kids, and uses TX, MFJ following wedding. That's concerning $45k/yr at 25.
uv run grid.py grid_profiles: distinct lives, identical "big tech 120 → 175 → 230×X" ladder (in $k/yr):
| Plan | Retire to $0 | Coast (core exp→60) | Retire, flat from 100 |
|---|---|---|---|
| single, lean | 35.1 ($1.42M) | 30.7 ($0.74M) | 35.9 ($1.55M) |
| SF, one kid | 46.0 ($2.26M) | 41.8 ($1.65M) | 47.1 ($2.46M) |
| SF, three kids | 67.3 ($1.95M) | 67.3 ($1.95M) | 70.9 ($2.57M) |
| couple alongside car | 55.1 ($2.47M) | 52.1 ($2.05M) | 57.4 ($2.93M) |
uv run grid.py grid_cities, subject rows only. The partner earns 55k, afterward 90k from 32, and puts fractional of their take-home into the shared pool:
| City | Ladder | Partner | Retire to $0 | Coast (core exp→60) | Retire, flat from 100 |
|---|---|---|---|---|---|
| SF | big tech 120 → 175 → 230×X | solo | 46.0 ($2.26M) | 41.8 ($1.65M) | 47.1 ($2.46M) |
| SF | big tech 120 → 175 → 230×X | + 55k → 90k at 32 | 41.1 ($2.03M) | 36.6 ($1.36M) | 42.1 ($2.20M) |
| SF | fast track 200×3 → 320×X | solo | 37.2 ($2.42M) | 33.3 ($1.71M) | 37.8 ($2.55M) |
| SF | fast track 200×3 → 320×X | + 55k → 90k at 32 | 34.3 ($2.09M) | 30.7 ($1.27M) | 34.9 ($2.20M) |
| Austin | big tech 120 → 175 → 230×X | solo | 38.4 ($2.02M) | 33.9 ($1.31M) | 39.1 ($2.14M) |
| Austin | big tech 120 → 175 → 230×X | + 55k → 90k at 32 | 34.3 ($1.62M) | 29.9 ($0.77M) | 34.8 ($1.71M) |
| Austin | fast track 200×3 → 320×X | solo | 32.8 ($2.13M) | 29.4 ($1.19M) | 33.2 ($2.23M) |
| Austin | fast track 200×3 → 320×X | + 55k → 90k at 32 | 30.3 ($1.54M) | 26.9 ($0.74M) | 30.6 ($1.63M) |
The complete grid's 401k+Roth+529 rows halt 0.1–1.2 years before than these, most in SF (the 401(k) saves CA tax too).
- fire.py: the CLI (tables, charts, flags). grid.py: the clear CLI; its docstring documents the axes. tests/: the pytest suite. DESIGN.md: the scheme log.
- config.py: markets, taxes and safety settings. It has the federal/FICA/CA bracket tables, TAX_VARIANTS (CA/TX sole or MFJ following the wedding, IL/CO MFJ; all a function of the plan), TAX_ADVANTAGED (2026 limits: 401(k) $24,500, Roth $7,500, 529 $16,000), genuine growth (5% until 60, afterward 4%), END_AGE, EXPENSE_SAFETY_FACTOR, the what-if grid, and make_config(plan, taxes, accounts).
- default_plans/ (shipped examples): sf_family, austin_family (the override pattern), single_lean, three_kids, couple_with_car, sf_to_austin (a move at 35); ladders.py (LADDERS) and partners.py (PARTNERS) for grids; the grids grid_cities, grid_taxes, grid_profiles, grid_ladders; __init__.py sets DEFAULT_PLAN / DEFAULT_GRID.
- custom_plans/ (gitignored): your plans and grids, identical layout, searched first.
- docs/: the README screenshots (SVG) and screenshots.py, which regenerates them.
- firemodel/: the engine. schema.py (dataclasses, X/RETIRE, periods, Accounts), plan.py (LifePlan, coast salary, with_partner), tax.py (brackets, regime()), sim.py (the day-by-day simulation, accounts), solve.py (bisection, what-if, sweep), grid.py (parallel solving), loader.py (finds plans and grids).
- Expenses: Expense(name, amount, per, start, end=None, every=1, category="").
- per is DAY, WEEK, MONTH, YEAR or ONCE (charged once, at start).
- Ages are half-open [start, end). end=None method forever. every=10 charges it all 10 years (a car).
- start or end can be RETIRE, "the day you depart the elevated salary": director health coverage runs until RETIRE, and an idiosyncratic scheme from RETIRE to 65.
- The categories kids, events and big-ticket are remaining out of the shoreline salary.
- Income ladders: Income(amount, years=… | until=…, start=…) segments, laid end to end. Exactly one holds X: years=X solves for how long, amount=X (e.g. Income(X, years=12)) solves for the salary. alt_careers={"tag": [...]} runs additional earnings paths against the identical spending.
career=[Income(120_000, years=3), Income(175_000, years=2), Income(230_000, years=X)]
- Taxes: set plan.taxes to a TAX_VARIANTS name. To add one, compose a function of the scheme that returns regime(start_age, end_age, federal, fica, state, joint=...) segments. flat_state(rate) gives a flat-rate state.
- Moving cities: SF.move_to(AUSTIN, 35) lives the archetypal scheme until 35 and the second following it. Each plan's expense lines are clipped at the move, and the tax setup switches too; career, prosperity and events before the move come from the archetypal plan. See default_plans/sf_to_austin.py. Compare a few move ages alongside a grid complete plans.
- Partner: plan.with_partner(70_000) (a flat salary, worked 25–55) or with_partner([Income(...), ...]). By default 50% of their take-home goes into the shared pool, and their own spending comes from the rest. For a completely shared household, continue share=1.0 and couple={line name or category: factor}: what all line costs for two adults vs one (shared rent 1.0, groceries 1.5, health coverage 2.0, ...), from together_from. Every row must be covered. Their earnings is on the shared come back following the wedding.
- Accounts are on by default (config.ACCOUNTS); --no-accounts, or the accounts grid axis, gives one taxable brokerage account. The family has one 401(k) and one Roth: all operating earner (you, and a partner whose pay is pooled) adds their own annual limit. Each document is funded lone up to a need it can match. The 401(k) (then Roth) gets fair what the plan's after-59½ spending needs, computed backward and frontloaded; the 529 gets the current value of the "college" disbursal lines, which it pays tax-free. Contributions only leave subject during it can motionless shield all forthcoming need that happens before 59½ (the wedding, the extend years, ...). Taxable pays before 59½; following it, 401(k) → Roth → taxable. Early draws pay +10%. See DESIGN.md D74.
- Safety factor: EXPENSE_SAFETY_FACTOR = 1.1 in config.py. It's the biggest lever in the model.
See DESIGN.md §2 (decisions) and §3 (tabled work) for the complete list.
- Real dollars (tax brackets remain at today's values) and deterministic returns: no Monte Carlo and no sequence-of-returns risk. 365-day years; bills accrue evenly per day, one-off items as lumps.
- No Social Security (conservative). You can add it as an Income alongside start=67.
- Accounts are simplified: no RMDs, director match, HSA, or Roth conversion ladder / regulation of 55. A partner's accounts are merged into the household's (one 59½ date for both). Taxable uses average-cost basis, and all acquire is taxed as long-term.
- No kid tax credit, no itemized deductions. MFJ taxes lone the incomes in the plan, so MFJ without with_partner acts akin a $0-income spouse.
- The disbursal figures are coarse 2026 estimates (SF and Austin), not quotes. Check them against your own life.
- The solver assumes additional X never hurts, so the final pay have to be at smallest the shoreline salary.
PYTHONPATH= uv run pytest -q
The PYTHONPATH= prefix clears a ROS instal from PYTHONPATH. Without it, pytest loads a ROS pytest plugin and fails. The ~190 tests (a few seconds) mostly use small closed-form scenarios you can inspect by hand: zero tax, 0% growth, amounts in multiples of $365. A fume test additionally checks that all shipped scheme solves.
DESIGN.md is the complete log: all request, all decision Claude made (and why), and the coding procedure stage by step.