模拟器方法论

技术透明:理解路径、百分位数与高级工具。

最后更新:2026 年 5 月

1. 概述:模拟器做什么

My FIRE Simulator 通过数千条月度路径(蒙特卡洛或历史数据)、可配置税收和取款策略,投影财务独立(FIRE)计划。数值核心用 C++ 编写,在浏览器中以 WebAssembly (WASM) 在 Web Worker 中运行:主计划数据不会发送到我们的服务器进行计算。

隐私设计:仅在您保存计划、分享链接或使用 AI 顾问时,才会向 Supabase 或隐私政策中描述的其他服务发送数据。

本页面向希望理解或在心里审查引擎逻辑的用户。不能替代专业财务建议。

2. 浏览器内架构

主模拟器的技术流程:

  1. 界面(HTML + JavaScript):读取表单,验证阶段、税收和投资组合;构建配置 JSON(store.js)。
  2. Web Workersimulation_worker.js):接收 JSON 并委托给相应 WASM 二进制,不阻塞 UI。
  3. WASM 模块由 C++ 编译:主模拟、逆向目标、热力图和 SWR 搜索。
  4. 结果:百分位数、图表(Chart.js)、年度表和遗产直方图在客户端渲染。

首页快速计算器(「计算你的目标」)是例外:仅使用确定性复利 JavaScript,无 WASM,即时响应。

3. 主模拟引擎

源文件:src-wasm/simulate.cpp → Worker WASM。默认运行 5,000 次独立迭代(payload 中的 iterations),配置相同,随机路径不同。

时间粒度

模拟在定义的时间范围内按月推进。每月根据阶段应用供款和取款、组合收益、通胀、费用、税收及已配置的压力事件。

统计输出

In custom mode the engine generates a monthly return using Geometric Brownian Motion (GBM). The random number generator is a Mersenne Twister (mt19937) seeded from hardware (std::random_device):

R_monthly = exp( (μ − σ²/2) × Δt  +  σ × √Δt × Z ) − 1

where:
  Δt = 1/12   (monthly step)
  Z  ~ N(0, 1) (standard normal distribution)
  μ  = expected annual portfolio return
  σ  = annual portfolio volatility

Monthly return — historical mode (block sampling)

In historical mode, each simulated year draws a consecutive block from the JST series. Annual returns are converted to monthly and combined by portfolio weights:

R_month_eq   = (1 + R_annual_eq)^(1/12) − 1
R_month_bond = (1 + R_annual_bond)^(1/12) − 1
R_month_fx   = (1 + R_annual_fx)^(1/12) − 1

R_portfolio = w_stocks × R_month_eq  +  w_bonds × R_month_bond  +  w_crypto × R_crypto_lognormal

If NOT hedged (unhedged currency):
  R_final = (1 + R_portfolio) × (1 + R_month_fx) − 1

Statistical outputs

Interpolated percentile calculation

We do not assume normality of final capital. Percentiles are computed with order statistics directly on the 5,000-result vectors:

index  = (N − 1) × p / 100
lower  = ⌊index⌋
frac   = index − lower

percentile = arr[lower] × (1 − frac) + arr[lower + 1] × frac

(partial sort via std::nth_element, O(N) complexity)

4. 数据来源:自定义 vs 历史

自定义(纯蒙特卡洛)

正态分布(均值 mu、波动率 sigma)生成随机月收益,外加恒定通胀。

历史(块抽样)

使用 public/js/data/historical_series.js(Jordà–Schularick–Taylor,现代时期 1950–2020)。抽取连续年份块,协调实际收益。

Available regions: US, Europe (Germany proxy pre-1999), Japan, United Kingdom. Default period: modern era 1950–2020. Panic button: extends to 1870+ including world wars and the Great Depression.

Block sampling algorithm

To preserve autocorrelation between consecutive years, the engine draws blocks of N years (configurable as blockSize, default 5). Each block starts at a random year in the series and advances sequentially, wrapping around:

At block start:
  start_index = random in [0, series_length − 1]

Within block (consecutive years):
  current_index = (start_index + offset) % series_length

When block years exhausted → new random index

Currency hedge (Hedged): when enabled, the fx impact is ignored. Otherwise, return is adjusted: (1 + R_portfolio) × (1 + R_fx) − 1.

5. 投资组合、波动率与 glidepath

自定义模式下,UI 用 Markowitz 式启发法估计 musigmacalculatePortfolioStats)。

Glidepath 按月插值权重。压力事件在选定年份施加冲击。

Correlationρ
Stocks – Bonds0.05
Stocks – Crypto0.20
Bonds – Crypto0.05

Portfolio variance formula

μ_portfolio = w_s × μ_s + w_b × μ_b + w_c × μ_c

σ²_portfolio = w_s² × σ_s²  +  w_b² × σ_b²  +  w_c² × σ_c²
             + 2 × w_s × w_b × σ_s × σ_b × ρ_sb
             + 2 × w_s × w_c × σ_s × σ_c × ρ_sc
             + 2 × w_b × w_c × σ_b × σ_c × ρ_bc

Glidepath (portfolio transition)

When enabled, stock/bond/crypto weights are linearly interpolated month by month between user-defined control points. At each month t, the engine recalculates mu and sigma with the Markowitz formula above using that month's interpolated weights.

Stress events

Stress events apply a multiplier factor to the return in the chosen month: stressFactor = 1 − drop. A 40% drop equals stressFactor = 0.6, simulating a point-in-time market crash.

6. 税收与资本桶

动态税收逻辑位于 src-wasm/simulate.cpp,仅在需要完整月度循环时运行(needsDynamicCalc):非固定取款、历史数据源、启用税收、配置税率档或 FIFO。计划 JSON 包含 generalCapitaltaxFreeCapitaldeferredCapitaltaxStrategyaverage | fifo)、withdrawalOrdertaxBrackets[]flatTaxRatelatentGainsPct(一般桶的初始税务成本基础)。

英文标识符:文档中 内的技术名称均为英文。实际 JSON(store.js)与 C++ 仍使用西班牙语历史字段(例如文档 generalCapital = 代码中的 capGeneral)。

三个资本桶

取款顺序(withdrawalOrder

平均成本模式(taxStrategy: "average"

一般桶维护汇总 税务成本基础generalCostBasis)。取款时应税增值按比例:ratio = (市值 − 成本) / 市值。可为单一税率(flatTaxRate)或 累进档taxBracketsmaxrate)。年度收益累计于 annualGainYTD,每自然年(12 个月)重置。引擎计算实现目标净额的 所需毛取款

gain_ratio   = (marketValue − costBasis) / marketValue
taxable_gain = grossWithdrawal × gain_ratio

Flat tax:    tax = taxable_gain × flatTaxRate
Brackets:    For each bracket with (max, rate):
               capacity = bracket.max − annualGainYTD
               chunk    = min(remainingGain, capacity)
               tax     += chunk × bracket.rate

Engine solves gross needed for a target net withdrawal:
  gross = net / (1 − gain_ratio × marginal_rate)

FIFO 模式(taxStrategy: "fifo"

仅适用于 general 桶。每次供款在 deque 中增加批次 { cost, units }指数价格indexPrice)每月随组合收益与券商费用更新。卖出按 FIFO 消耗批次;对实现收益征税。若批次超过 120,合并最旧 12 个以限制内存。

Monthly index price update:
  indexPrice *= (1 + R_portfolio) × (1 − brokerFee)

For each lot consumed (FIFO order):
  lot_marketValue = units × indexPrice
  cost_per_unit   = original_cost / original_units
  gain            = (indexPrice − cost_per_unit) × units_sold
  tax            += apply_brackets(gain)
界面中的国家模板为教学近似,请对照实际税法;引擎未涵盖所有细节(洗售、个人免税额等)。

7. 取款策略

策略字段 withdrawalStrategyfixed(默认)、guytonvpw。计划取款在 cashFlowPhases[].val 中以负值传入,在动态月度循环中应用。

固定(fixed

有计划取款月份:withdrawal = baseWithdrawals[t] × withdrawalInflationFactor。若 adjustFlowsForInflation 开启,每年将因子乘以 (1 + 年通胀)(与供款的 generalInflationFactor 类似)。即经典通胀调整取款规则。

Guyton–Klinger(guyton

界面参数 gkThresholdgkAdjustment(%)。首次取款时计算 initialGKWithdrawalRate = 计划年取款 / 总资本。每年一月:(1)仅当 totalCapital ≥ capitalAtYearStart 才对支出加通胀;(2)若当前取款率高于初始率的 (1 + 阈值),将取款因子乘以 (1 − 调整);低于 (1 − 阈值) 则乘以 (1 + 调整)

initialWithdrawalRate = plannedAnnualWithdrawal / totalCapital  (1st withdrawal only)

Each January:
  1. Prosperity rule:
     If totalCapital ≥ capitalAtYearStart → apply inflation to spending
     Otherwise → freeze spending (no inflation bump)

  2. Guardrail rules:
     currentRate = annualSpending / totalCapital
     If currentRate > initialRate × (1 + threshold) → factor × (1 − adjustment)  [cut]
     If currentRate < initialRate × (1 − threshold) → factor × (1 + adjustment)  [raise]

VPW(vpw

参数 vpwRate(预期年率,如 4%)。每年一月在 活资本 上重算年金:剩余年数 remainingYears = (months − t + 1) / 12pmtRate = vpwRate / (1 − (1+vpwRate)−remainingYears)(为 0 时用 1/remainingYears)。月取款 = totalCapital × pmtRate / 12。税务使用 isVPWMeta(按毛额目标,非固定净额)。

remainingYears = (totalMonths − t + 1) / 12

If vpwRate > 0:
  pmtRate = vpwRate / (1 − (1 + vpwRate)^(−remainingYears))

If vpwRate = 0:
  pmtRate = 1 / remainingYears

Monthly_withdrawal = totalCapital × pmtRate / 12

8. 高级工具

「高级工具」面板中的三项工具(public/js/controllers/simulation.js不读取主表单:自建载荷并调用 Web Worker(simulation_worker.js)的 goalsheatmapswr

目标(simulate_goals.cpp

热力图(simulate_heatmap.cpp

SWR — 安全取款率

审计:simulate_goals.cppsimulate_heatmap.cppsimulation_worker.jshandleSWR)。

9. 在服务器上运行的内容

当前界面无电子邮件通知。

10. 局限性

Every simulation simplifies reality. It is important to know the model's assumptions:

打开模拟器