Files
ilaandClaude Sonnet 4.6 d558da51a6 add project docs and research materials; update arch doc with WSL2 dev setup
- docs/: 6 development documents (PRD, tech selection, architecture, risks, roadmap, design workflow)
- research/: FIRE background materials, personal plan, Excel calculator
- 03-架构设计.md: add Section 8 documenting WSL2 → Windows browser dev environment (Vite host config, access URLs, mobile debugging tip)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-08 09:21:04 +08:00

137 lines
6.5 KiB
Python
Raw Permalink 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.
# -*- coding: utf-8 -*-
"""生成可调参数的 FIRE(Barista 半退休) 测算表。改黄色参数格,结果用 Excel 公式自动重算。"""
from openpyxl import Workbook
from openpyxl.styles import Font, PatternFill, Alignment, Border, Side
from openpyxl.utils import get_column_letter
wb = Workbook()
ws = wb.active
ws.title = "FIRE测算"
# ---- 样式 ----
title_font = Font(name="微软雅黑", size=14, bold=True, color="FFFFFF")
title_fill = PatternFill("solid", fgColor="2F5597")
head_font = Font(name="微软雅黑", size=11, bold=True, color="FFFFFF")
head_fill = PatternFill("solid", fgColor="4472C4")
sect_font = Font(name="微软雅黑", size=11, bold=True, color="2F5597")
input_fill = PatternFill("solid", fgColor="FFF2CC") # 黄色=可改
input_font = Font(name="微软雅黑", size=11, bold=True, color="C00000")
calc_fill = PatternFill("solid", fgColor="E2EFDA") # 绿色=自动算
normal_font = Font(name="微软雅黑", size=11)
ok_fill = PatternFill("solid", fgColor="C6E0B4")
thin = Side(style="thin", color="BFBFBF")
border = Border(left=thin, right=thin, top=thin, bottom=thin)
center = Alignment(horizontal="center", vertical="center")
left = Alignment(horizontal="left", vertical="center")
def put(cell, value, font=normal_font, fill=None, align=left, fmt=None, bd=True):
c = ws[cell]
c.value = value
c.font = font
if fill: c.fill = fill
c.alignment = align
if fmt: c.number_format = fmt
if bd: c.border = border
return c
# ---- 标题 ----
ws.merge_cells("A1:F1")
put("A1", "FIRE 半退休 (Barista) 可调参数测算表", title_font, title_fill, center, bd=False)
ws.row_dimensions[1].height = 28
ws.merge_cells("A2:F2")
put("A2", "用法:只改【黄色格子】里的数字,右侧/下方结果会自动重算。", Font(name="微软雅黑", size=9, italic=True, color="808080"), align=left, bd=False)
# ---- 参数区 ----
put("A3", "① 可修改参数(改这里)", sect_font, bd=False)
params = [
("起始本金(元)", 70000, "#,##0"),
("每月定投(元)", 1500, "#,##0"),
("年开支(元)", 65000, "#,##0"),
("兼职年收入(元)", 40000, "#,##0"),
("安全提取率", 0.035, "0.0%"),
("收益率-保守", 0.04, "0.0%"),
("收益率-中性", 0.06, "0.0%"),
("收益率-乐观", 0.08, "0.0%"),
]
r = 4
for name, val, fmt in params:
put(f"A{r}", name, normal_font, input_fill, left)
put(f"B{r}", val, input_font, input_fill, center, fmt)
r += 1
# 记下行号
ROW = {"本金":4, "月投":5, "开支":6, "兼职":7, "提取率":8, "保守":9, "中性":10, "乐观":11}
# ---- 自动计算:目标本金 ----
put("A13", "② 自动计算结果", sect_font, bd=False)
put("A14", "年缺口(开支-兼职,元)", normal_font, calc_fill)
put("B14", f"=B{ROW['开支']}-B{ROW['兼职']}", normal_font, calc_fill, center, "#,##0")
put("A15", "目标本金(元)", Font(name="微软雅黑", bold=True), calc_fill)
put("B15", f"=B14/B{ROW['提取率']}", Font(name="微软雅黑", bold=True, color="C00000"), calc_fill, center, "#,##0")
put("A16", "目标本金(万元)", Font(name="微软雅黑", bold=True), calc_fill)
put("B16", "=B15/10000", Font(name="微软雅黑", bold=True, color="C00000"), calc_fill, center, "0.0")
# ---- 逐年测算表 ----
put("A18", "③ 逐年资产测算(含分红复投;第一个出现 ✅ 的年份≈达成 Barista FIRE)", sect_font, bd=False)
headers = ["年数", "保守(元)", "中性(元)", "乐观(元)", "中性(万)", "是否达标(中性)"]
hr = 19
for i, h in enumerate(headers):
put(f"{get_column_letter(i+1)}{hr}", h, head_font, head_fill, center)
start = hr + 1
for y in range(1, 31):
rr = start + (y - 1)
put(f"A{rr}", y, normal_font, None, center, "0")
# FV(年化/12, 年数*12, -月投, -起始本金) -> 正数
put(f"B{rr}", f"=FV($B${ROW['保守']}/12, A{rr}*12, -$B${ROW['月投']}, -$B${ROW['本金']})", normal_font, None, center, "#,##0")
put(f"C{rr}", f"=FV($B${ROW['中性']}/12, A{rr}*12, -$B${ROW['月投']}, -$B${ROW['本金']})", normal_font, None, center, "#,##0")
put(f"D{rr}", f"=FV($B${ROW['乐观']}/12, A{rr}*12, -$B${ROW['月投']}, -$B${ROW['本金']})", normal_font, None, center, "#,##0")
put(f"E{rr}", f"=C{rr}/10000", normal_font, None, center, "0.0")
put(f"F{rr}", f'=IF(C{rr}>=$B$15,"✅达标","")', Font(name="微软雅黑", bold=True, color="548235"), None, center)
# ---- 列宽 ----
widths = [22, 14, 14, 14, 12, 16]
for i, w in enumerate(widths):
ws.column_dimensions[get_column_letter(i+1)].width = w
# ---- 说明页 ----
ws2 = wb.create_sheet("说明")
notes = [
"【这张表怎么用】",
"1. 在『FIRE测算』页,只改黄色格子里的数字(本金、月投、开支、兼职收入、提取率、收益率)。",
"2. 绿色格子和下方表格会自动重算,不用动公式。",
"",
"【目标本金怎么来的】",
"目标本金 =(年开支 − 兼职年收入)÷ 安全提取率",
"例:开支6.5万、兼职4万、提取率3.5% → (65000-40000)/3.5% = 约71万。",
"兼职每多赚1万,目标本金大约少29万。",
"",
"【提取率为什么用 3.5%】",
"美国常用4%,但中国低利率、市场波动大、社保有空窗,打折到3%~3.5%更稳健。",
"想更保守可把提取率改成 0.03。",
"",
"【收益率怎么理解】",
"已包含分红再投资(总回报口径)。参考组合:红利ETF/工行50-60% + 宽基指数30% + 货基/国债10-15%。",
"保守4% / 中性6% / 乐观8% 为假设值,不是承诺。",
"",
"【达成年限怎么看】",
"看『是否达标(中性)』列,第一个出现 ✅ 的年份,就是按中性收益大约达成的年数。",
"想看保守/乐观,可对比 保守(元)、乐观(元) 两列与目标本金B15的大小。",
"",
"【加速的关键】",
"把『每月定投』从1500改成3000试试,会看到达成年限大幅缩短——提高储蓄率比纠结收益率更有效。",
"",
"⚠️ 本表为个人理性测算与学习参考,不构成投资建议,收益率为假设值、不保证实现。",
]
for i, line in enumerate(notes, start=1):
c = ws2[f"A{i}"]
c.value = line
if line.startswith("【") or line.startswith("⚠️"):
c.font = Font(name="微软雅黑", bold=True, color="2F5597")
else:
c.font = Font(name="微软雅黑", size=10)
ws2.column_dimensions["A"].width = 95
out = "/mnt/d/opc_project/fire_goal/FIRE可调测算表.xlsx"
wb.save(out)
print("saved:", out)