fire:一个逐日 FIRE 模拟器

在高薪岗位上工作多少年,你才能 coast、退休,或者退休且永远不让你的养老金缩水。

提醒一下:这是一个 vibe-coded 的 FIRE 工具。它是在与 Claude(一个 AI)的对话中构建的:有人描述了他们想要什么,Claude 编写了代码、测试和设计日志。没有任何金融专业人士审阅过它。它是不构成财务建议。用它来探索想法,任何重要的事情都要向以此为生的真人核实。

这是什么?

它回答一个问题:我需要在高薪岗位上工作多久才能停止、coast 或退休?

为了找出答案,它模拟你的资金逐日到 112 岁以实际(今天的)美元计算。每一天,工资在扣除所得税后到账,当天的支出流出,任何盈余都被投资。当出现短缺时,它从投资组合中卖出并支付该笔卖出的税款。每项支出都加了一个安全系数(1.1×)。然后一个二分求解器找出在高薪(你的收入计划中的X)下维持余额在每一天都大于或等于 $0 的最短时间。

每个计划都针对三个目标求解:退休到 $0(钱能撑到 112 岁),coast 到 60 岁(一份能支付你核心支出直到 60 岁的工作),以及从 100 岁起持平(投资组合在 100 岁后永不缩水)。

你会得到什么:

  • 对于每个目标,停止年龄(精确到天)以及该年龄时的投资组合
  • 一张收入计划表(每个薪资段,税前和税后)以及终期图表(净资产、按类别划分的支出、现金流)。
  • 假设分析表:如果你在 Y 岁时多存 $A,你能提前多少周停止。
  • 搬迁城市:在某个年龄之前按一个城市的计划生活,之后按另一个城市的;支出和税收在该年龄切换(plan.move_to(other, at_age);示例sf_to_austin:在 SF 赚钱,35 岁搬到 Austin)。
  • 网格扫描跨计划、收入阶梯、伴侣收入、税务设置和账户类型,汇总在一张表中。
  • 401(k) + Roth + 529账户,默认开启;摘要还会显示不含它们时的答案(--no-accounts会关闭它们)。

它看起来是什么样

uv run fire.py --plan sf_family(一个已发布的示例:SF,一个孩子,120 → 175 → 230k 的阶梯):

Summary: stop age per goal, with and without tax-advantaged accounts, and the income plan Net worth by age for each goal Expenses per year by category 401(k), Roth, 529 and taxable balances by age

假设分析表和现金流

What-if: weeks sooner you can stop for extra savings at each age Cash flow: gross, take-home, expenses and portfolio sales by age

用以下命令重新生成这些uv run docs/screenshots.py(它只使用已发布的示例计划)。

与 Claude Code 一起使用

这些计划是纯 Python,所以Claude Code作为接口效果很好。用平实的英文描述你的生活(“我赚 $180k,想在 30 岁出头要两个孩子,35 岁可能搬到更便宜的地方;我什么时候能躺平?”),让它用custom_plans/写出计划,运行fire.py/grid.py,然后把结果并排放在一起。它也适合回答“如果我伴侣停止工作会怎样?”或“保险对夫妻真的会翻倍吗?”这类问题,用于核对开支估算,以及记录你选择的假设。把个人数字放在custom_plans/(gitignored)里,这样它们就不会进入仓库。

快速开始

需要uv。依赖只有richplotext

uv sync                                                #安装
uv run fire.py --list                                  #你可以运行的计划和网格
uv run fire.py --plan sf_family                        #求解每个目标:摘要、假设情景、图表
uv run fire.py --plan sf_family --no-plot --no-whatif  #仅摘要和收入计划(约 1 秒)
uv run fire.py --plan austin_family --budget           #打印每一行开支并退出
uv run fire.py --plan sf_to_austin                     #旧金山到 35 岁,然后奥斯汀价格和得克萨斯州税
uv run grid.py grid_cities                             #扫描 SF/Austin × 阶梯 × 伴侣 × 账户

主要fire.py标志(--help用于全部)。uv run grid.py [NAME]运行一个网格(默认DEFAULT_GRID)。

标志 作用
--plan NAME-p 要运行哪个计划(默认:DEFAULT_PLAN
--no-accounts 仅应税经纪账户:没有 401(k) / Roth / 529(config.NO_ACCOUNTS
--budget-b 打印每一行开支($/月,$/年)并退出
--compare-taxes 在每个TAX_VARIANTS条目下求解并比较
--whatif A@AGE-w 如果在 AGE 额外存 $A,能提前多少(可重复)
--x N 跳过求解:模拟这一个 X 并打印逐年表格
--sweep 同时显示全年 X 扫描表和图表
-s NAME 仅运行名称包含 NAME 的目标(可重复)
--table-t 为每个解打印逐年表格
--no-plot/--no-whatif 跳过图表 / 假设情景网格

让它成为你的

default_plans/存放随附示例。你自己的计划放在custom_plans/,它被 gitignore,会先被搜索,并优先于同名默认值。

mkdir -p custom_plans
cp default_plans/sf_family.py custom_plans/my_plan.py      #然后编辑数字
cat > custom_plans/__init__.py <<'EOF'
DEFAULT_PLAN = "my_plan"
DEFAULT_GRID = "my_grid"
EOF

custom_plans/需要那个__init__.py才能被找到。之后,一个裸的uv run fire.py会运行你的计划。 一个网格(例如custom_plans/my_grid.py)是一个模块,带有一个GRID轴字典,每个{label: value}。 每种组合都并行求解,并打印为一张表:

from config import NO_ACCOUNTS, TAX_ADVANTAGED
from custom_plans.my_plan import PLAN
from default_plans.ladders import LADDERS

GRID = {
    "plans":    {"me": PLAN},                                         # 必需
    "ladders":  LADDERS,                                              # 替换 plan.career
    "partners": {"solo": None, "+ $70k": 70_000},                     # 通过 plan.with_partner
    "taxes":    {"CA": "CA, MFJ after wedding", "TX": "TX, MFJ after wedding"},
    "accounts": {"taxable": NO_ACCOUNTS, "401k+Roth+529": TAX_ADVANTAGED},
}

SF vs Austin:税收

来自税率表,在config.py(2026 联邦税、2025 加州税;请核实):到手收入和有效总税率(联邦 + FICA + 州税,含加州的 1.3% SDI),针对仅工资收入,单一收入者,标准扣除,无 401(k)。"MFJ" 指配偶收入为 $0(最佳情形,如married()中的config.py)。

总工资 加州单身 加州 MFJ 德州单身 德州 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%)

下一美元工资的边际税率(C = 加州单身,t = 德州单身)。在 $185k 附近的下降是社会保障工资基数。超过 $200k 后,加州大约增加 10 个百分点。

           Total marginal rate (fed + FICA + state), single, one earner
  ┌────────────────────────────────────────────────────────────────────────────┐
50┤ CC CA single                                           CCCCCCCCCCCCCCCCCCCC│
  │ tt TX single                            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
                                 gross wages ($k)

保持 SF 价格不变,仅替换税收设置(uv run grid.py grid_taxes)显示了税制本身的价值。单元格是停止年龄(停止时的投资组合):

方案 税收 退休到 $0 Coast(核心支出→60) 退休,从 100 起持平
SF 家庭 加州,婚后 MFJ 46.0 ($2.26M) 41.8 ($1.65M) 47.1 ($2.46M)
SF 家庭 加州,单身 49.6 ($2.28M) 46.5 ($1.87M) 51.3 ($2.52M)
SF 家庭 德州,婚后 MFJ 42.7 ($2.30M) 38.2 ($1.58M) 43.6 ($2.46M)
SF 家庭 德州,单身 44.4 ($2.31M) 40.5 ($1.72M) 45.5 ($2.50M)
SF 家庭 伊利诺伊州,婚后 MFJ 45.1 ($2.33M) 40.9 ($1.69M) 46.2 ($2.52M)
SF 家庭 科罗拉多州,婚后 MFJ 44.8 ($2.33M) 40.6 ($1.68M) 45.9 ($2.52M)

示例支出

示意性的,粗略 2026 年 SF 价格,不是任何人的真实预算:sf_family是你在 2BR 中的份额,无车,28 岁时 $60k 婚礼,33 岁时一个孩子。在职业工作期间每年,未乘 ×1.1 安全系数之前:

类别 主要项目 25 岁 35 岁(孩子 2 岁)
住房 租金 $1,900/月,水电,网络,租客保险 $24,900 $24,900
孩子 + 定期寿险 日托 $2,250/月,额外卧室 $1,800/月,婴儿费用,保险 $59,340
食品 杂货 $90/周,外出就餐 $70/周 $8,320 $8,320
旅行 旅行 $600/月 $7,200 $7,200
健康 雇主计划,牙科 + 视力,健身房 $3,600 $3,600
其他一切 社交 $60/周,科技,衣服,礼物,手机,个人护理,... $8,900 $8,900
交通 公交 + 网约车 $180/月,自行车 $2,560 $2,560
总计 $55,480 $114,820

其他行稍后开启:个人健康计划从RETIRE到 65 岁(在 50 岁和 58 岁上调),办公室不再供餐后每周买菜,65 岁开始 Medicare,88 岁起晚年护理每月 $3,000,UC 每年 $45k。

austin_family复用所有这些,只覆盖其AUSTINdict 中的行(租金 $950,日托 $1,650,UT Austin 而非 UC,更便宜的健康保险,……)。它增加了 4 岁的付费学前班(德州没有免费 TK)以及带孩子时的额外网约车,并使用TX, MFJ after wedding。25 岁时约为每年 $45k。

示例结果

uv run grid.py grid_profiles:不同的人生,同样的“big tech 120 → 175 → 230×X”阶梯(单位 $k/年):

方案 退休到 $0 Coast(核心支出→60) 退休,从 100 起持平
单身,精简 35.1($1.42M) 30.7($0.74M) 35.9($1.55M)
SF,一个孩子 46.0($2.26M) 41.8($1.65M) 47.1($2.46M)
SF,三个孩子 67.3($1.95M) 67.3($1.95M) 70.9($2.57M)
有车的夫妻 55.1($2.47M) 52.1($2.05M) 57.4($2.93M)

uv run grid.py grid_cities,仅应税行。伴侣赚 55k,从 32 岁起赚 90k,并将税后收入的一半投入共享池:

城市 阶梯 伴侣 退休到 $0 Coast(核心支出→60) 退休,从 100 起持平
SF big tech 120 → 175 → 230×X 单人 46.0($2.26M) 41.8($1.65M) 47.1($2.46M)
SF big tech 120 → 175 → 230×X + 55k → 32 岁时 90k 41.1($2.03M) 36.6($1.36M) 42.1($2.20M)
SF 快速通道 200×3 → 320×X 单人 37.2($2.42M) 33.3($1.71M) 37.8($2.55M)
SF 快速通道 200×3 → 320×X + 55k → 32 岁时 90k 34.3($2.09M) 30.7($1.27M) 34.9($2.20M)
Austin big tech 120 → 175 → 230×X 单人 38.4($2.02M) 33.9($1.31M) 39.1($2.14M)
Austin big tech 120 → 175 → 230×X + 55k → 32 岁时 90k 34.3($1.62M) 29.9($0.77M) 34.8($1.71M)
Austin 快速通道 200×3 → 320×X 单人 32.8($2.13M) 29.4($1.19M) 33.2($2.23M)
Austin 快速通道 200×3 → 320×X + 55k → 32 岁时 90k 30.3($1.54M) 26.9($0.74M) 30.6($1.63M)

完整网格的401k+Roth+529行比这些早 0.1–1.2 年停止,大多在 SF(401(k) 也节省了 CA 税)。

布局

  • fire.py:CLI(表格、图表、标志)。grid.py:扫描 CLI;其 docstring 记录了各轴。tests/:pytest 测试套件。DESIGN.md:设计日志。
  • config.py:市场、税收和安全设置。它有联邦/FICA/CA 税率表,TAX_VARIANTS(婚后 CA/TX 单身或 MFJ,IL/CO MFJ;每个都是方案的函数),TAX_ADVANTAGED(2026 年限额:401(k) $24,500,Roth $7,500,529 $16,000),实际增长(60 岁前 5%,之后 4%),END_AGEEXPENSE_SAFETY_FACTOR,what-if 网格,以及make_config(plan, taxes, accounts)
  • default_plans/(随附示例):sf_familyaustin_family(覆盖模式),single_leanthree_kidscouple_with_carsf_to_austin(35 岁时搬家);ladders.pyLADDERS) 和partners.pyPARTNERS)用于网格;这些网格grid_citiesgrid_taxesgrid_profilesgrid_ladders__init__.py集合DEFAULT_PLAN/DEFAULT_GRID
  • custom_plans/(gitignored):你的计划和网格,相同布局,优先搜索。
  • docs/:README 截图(SVG)和screenshots.py,它会重新生成它们。
  • firemodel/:引擎。schema.py(dataclasses,X/RETIRE,periods,Accounts),plan.pyLifePlan,coast salary,with_partner),tax.py(brackets,regime()),sim.py(逐日模拟,accounts),solve.py(bisection,what-if,sweep),grid.py(并行求解),loader.py(查找计划和网格)。

如何自定义

  • 支出Expense(name, amount, per, start, end=None, every=1, category="")
    • perDAYWEEKMONTHYEARONCE(一次性收取,在start)。
    • 年龄是半开区间[start, end)end=None表示永远。every=10每 10 年收取一次(一辆车)。
    • startend可以是RETIRE,“你离开高薪的那一天”:雇主健康保险持续到RETIRE,个人计划从RETIRE到 65 岁。
    • 这些类别kidseventsbig-ticket被排除在 coast salary 之外。
  • 收入阶梯Income(amount, years=… | until=…, start=…)段,首尾相接。恰好有一段持有Xyears=X求解多长时间,amount=X(例如Income(X, years=12))求解薪水。
    职业=[收入(120_000, =3), 收入(175_000, =2), 收入(230_000, =X)]
    alt_careers={"tag": [...]}针对相同的支出运行额外的收入路径。
  • 税收:将plan.taxes设置为一个TAX_VARIANTS名称。要添加一个,写一个关于计划的函数,返回regime(start_age, end_age, federal, fica, state, joint=...)段。flat_state(rate)给出一个统一税率的州。
  • 搬城市SF.move_to(AUSTIN, 35)在 35 岁前使用第一个计划,之后使用第二个。每个计划的支出行在搬迁时被截断,税收设置也会切换;搬迁前的职业、金钱和事件来自第一个计划。参见default_plans/sf_to_austin.py。用网格对计划比较几个搬迁年龄。
  • 伴侣plan.with_partner(70_000)(固定薪水,工作 25–55 岁)或with_partner([Income(...), ...])。默认情况下,他们税后收入的 50% 进入共享资金池,他们自己的支出来自其余部分。对于完全共同的家庭,传入share=1.0couple={line name or category: factor}:每条支出行对两个成年人 vs 一个成年人的成本(共享租金 1.0,杂货 1.5,健康保险 2.0,...),来自together_from。每条支出行都必须被覆盖。他们的收入在婚礼后计入联合申报。
  • 账户默认开启(config.ACCOUNTS);--no-accounts,或accounts网格轴,给出一个应税经纪账户。家庭有一个 401(k) 和一个 Roth:每个有工作的收入者(你,以及薪水被汇入的伴侣)添加自己的年度限额。每个账户只被资助到它能匹配的需求。401(k)(然后是Roth)只获得计划在 59½ 岁后支出所需的部分,向后计算并前置加载;529获取“大学”支出行的现值,这部分支出免税支付。供款只在仍能覆盖 59½ 岁之前发生的所有未来需求(婚礼、过渡年份……)时才离开应税账户。59½ 岁之前从应税账户支付;之后按 401(k) → Roth → 应税账户的顺序。提前支取需额外支付 +10%。参见 DESIGN.md D74。
  • 安全系数EXPENSE_SAFETY_FACTOR = 1.1config.py。这是模型中最大的杠杆。

关键假设与局限

完整列表参见 DESIGN.md §2(决策)和 §3(搁置的工作)。

  • 实际美元(税率档次保持今天的数值)以及确定性回报:没有蒙特卡洛模拟,也没有365 天的年份;账单按天均匀累积,一次性项目作为整笔计入。
  • 无社会保障(保守)。你可以将其作为一项Income添加,start=67
  • 账户经过简化:没有 RMD、雇主匹配、HSA,或 Roth 转换阶梯 / 55 岁规则。伴侣的账户合并到家庭账户中(两人共用一个 59½ 岁日期)。应税账户使用平均成本基础,所有收益均按长期资本利得征税。
  • 没有子女税收抵免,没有分项扣除。MFJ 仅对计划中的收入征税,因此没有with_partner的 MFJ 相当于 $0 收入的配偶。
  • 支出数字是粗略的 2026 年估算(旧金山和奥斯汀),并非报价。请对照你自己的实际生活进行核对。
  • 求解器假设更多的 X 永远不会有害,因此最终工资应至少等于 coast 工资。

测试

PYTHONPATH= uv run pytest -q

PYTHONPATH=前缀会从PYTHONPATH中清除 ROS 安装。没有它,pytest 会加载一个 ROS pytest 插件并失败。约 190 个测试(几秒钟)大多使用你可以手工验证的微型闭式场景:零税、0% 增长、金额为 $365 的倍数。一个冒烟测试还会检查每个已发布的计划都能求解。

DESIGN.md是完整日志:每个请求、Claude 做出的每个决策(以及原因),以及逐步的编码过程。