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 的阶梯):
用以下命令重新生成这些uv run docs/screenshots.py(它只使用已发布的示例计划)。
与 Claude Code 一起使用
这些计划是纯 Python,所以Claude Code作为接口效果很好。用平实的英文描述你的生活(“我赚 $180k,想在 30 岁出头要两个孩子,35 岁可能搬到更便宜的地方;我什么时候能躺平?”),让它用custom_plans/写出计划,运行fire.py/grid.py,然后把结果并排放在一起。它也适合回答“如果我伴侣停止工作会怎样?”或“保险对夫妻真的会翻倍吗?”这类问题,用于核对开支估算,以及记录你选择的假设。把个人数字放在custom_plans/(gitignored)里,这样它们就不会进入仓库。
快速开始
需要uv。依赖只有rich和plotext。
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_AGE,EXPENSE_SAFETY_FACTOR,what-if 网格,以及make_config(plan, taxes, accounts)。 -
default_plans/(随附示例):sf_family,austin_family(覆盖模式),single_lean,three_kids,couple_with_car,sf_to_austin(35 岁时搬家);ladders.py(LADDERS) 和partners.py(PARTNERS)用于网格;这些网格grid_cities,grid_taxes,grid_profiles,grid_ladders;__init__.py集合DEFAULT_PLAN/DEFAULT_GRID。 -
custom_plans/(gitignored):你的计划和网格,相同布局,优先搜索。 -
docs/:README 截图(SVG)和screenshots.py,它会重新生成它们。 -
firemodel/:引擎。schema.py(dataclasses,X/RETIRE,periods,Accounts),plan.py(LifePlan,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="")。per是DAY,WEEK,MONTH,YEAR或ONCE(一次性收取,在start)。- 年龄是半开区间
[start, end)。end=None表示永远。every=10每 10 年收取一次(一辆车)。 start或end可以是RETIRE,“你离开高薪的那一天”:雇主健康保险持续到RETIRE,个人计划从RETIRE到 65 岁。- 这些类别
kids,events和big-ticket被排除在 coast salary 之外。
- 收入阶梯:
Income(amount, years=… | until=…, start=…)段,首尾相接。恰好有一段持有X:years=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.0和couple={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.1在config.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 做出的每个决策(以及原因),以及逐步的编码过程。