シミュレーターの方法論
パス、パーセンタイル、高度なツールを理解するための技術的透明性。
最終更新:2026年5月
1. 概要:シミュレーターの役割
My FIRE Simulator は、数千の月次パス(モンテカルロまたは歴史データ)、設定可能な税金と引出戦略で、経済的自立(FIRE)プランを投影します。数値コアは C++ で、ブラウザ内の Web Worker で WebAssembly (WASM) として実行されます。メインプランの数値は計算のために当社サーバーに送信されません。
本ページはエンジンのロジックを理解・心の中で検証したいユーザー向けです。専門的な金融アドバイスに代わるものではありません。
2. ブラウザ内アーキテクチャ
メインシミュレーターの技術フロー:
- UI(HTML + JavaScript):フォームを読み取り、フェーズ・税金・ポートフォリオを検証し、設定 JSON(
store.js)を構築。 - Web Worker(
simulation_worker.js):JSON を受け取り、UI をブロックせず適切な WASM に委譲。 - WASM モジュール(C++ からコンパイル):メインシミュレーション、逆目標、ヒートマップ、SWR 探索。
- 結果:パーセンタイル、Chart.js グラフ、年次表、相続ヒストグラムをクライアントで描画。
ランディングのクイック計算機(目標を計算)は例外:WASM なしの決定的複利 JavaScript のみで即時応答。
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 volatilityMonthly 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) − 1Statistical outputs
- P10 / P50 / P90:各月および期末の資本(名目・実質)の補間パーセンタイル。
- 成功確率 / 破綻リスク:ホライズン前に資本を使い果たすパスの割合。
- 税内訳(P50):動的税計算時の中央値シナリオでの累積手数料・税金。
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)を使用。連続年ブロックで実質リターンを協調。
- 地域:米国、欧州(1999 年前はドイツ代理)、日本、英国。
- パニックボタン(1870):歴史ソースのみ — 期間を拡張。
- 通貨ヘッジ(Hedged):歴史のみ — 通貨が系列と異なる場合 FX を調整。
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 indexCurrency hedge (Hedged): when enabled, the fx impact is ignored. Otherwise, return is adjusted: (1 + R_portfolio) × (1 + R_fx) − 1.
5. ポートフォリオ、ボラティリティ、グライドパス
カスタムモードでは UI が Markowitz 型ヒューリスティックで mu と sigma を推定(calculatePortfolioStats)。
グライドパスは月次でウェイトを補間。ストレスイベントは選択年にショックを適用。
| Correlation | ρ |
|---|---|
| Stocks – Bonds | 0.05 |
| Stocks – Crypto | 0.20 |
| Bonds – Crypto | 0.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 × ρ_bcGlidepath (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。ペイロード:generalCapital、taxFreeCapital、deferredCapital、taxStrategy(average | fifo)、withdrawalOrder、taxBrackets[]、flatTaxRate、latentGainsPct(一般バケットの初期税務コスト基準)。
… 内の技術名はすべて英語です。実際の JSON(store.js)と C++ はスペイン語のレガシーキーを使用します(例:ドキュメントの generalCapital = コードの capGeneral)。3 つの資本バケット
- 一般(証券口座):
cashFlowPhasesでaccount: "general"。税コストをモデル化する唯一のバケット(平均または FIFO)。 - 非課税(libre):引出時 100% 課税益(
ratio = 1.0);FIFO なし。 - 繰延(diferido):引出時の処理は libre と同様。
引出順序(withdrawalOrder)
- 最適(既定):
general → libre → diferido(インデックス 0 → 2 → 1)。 - 按分(
prorated):総資本に対する比率でネット引出を分配;不足分は同順で次バケットへ。
平均コスト(taxStrategy: "average")
一般バケットの集計 generalCostBasis。引出時の課税益比率 ratio = (時価 − コスト) / 時価。単一税率または taxBrackets の累進。annualGainYTD は毎年リセット。目標ネットに対する必要総引出を求解。
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. 引出戦略
withdrawalStrategy:fixed(既定)、guyton、vpw。計画引出は cashFlowPhases[].val の負の値で渡されます。
固定(fixed)
withdrawal = baseWithdrawals[t] × withdrawalInflationFactor。adjustFlowsForInflation 時は年次で (1 + 年インフレ) を乗算(古典的インフレ連動引出)。
Guyton–Klinger(guyton)
gkThreshold、gkAdjustment。初回引出で initialGKWithdrawalRate。毎年 1 月:資本が年初以上のときのみインフレ調整;引出率が初期比の上下限を超えたら withdrawalInflationFactor を増減。
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;生存資本に対する年金:pmtRate = vpwRate / (1 − (1+vpwRate)−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 / 128. 高度なツール
「高度なツール」の 3 機能(controllers/simulation.js)はメインフォームを読みません。専用ペイロードで Worker(goals、heatmap、swr)を呼び出します。
目標(simulate_goals.cpp)
mode: "time":targetまでの年数(パスあたり最大 80 年)。mode: "money":yearsで必要な実質月次貯蓄。- 5,000 反復。実質リターン(カスタムまたは
historical_series.jsのブートストラップ)。 - 悲観的 = 悪い結果:時間モード P10=年数の 90 パーセンタイル。
ヒートマップ(simulate_heatmap.cpp)
- メインエンジンより簡略:フェーズ・税・グライドパス・負債・GK/VPW なし。
- グリッド:退職年齢(
ageMin…ageMax、ageStep)× 月支出(expMin…expMax、expStep)。セルごとに固定資本、ホライズン95 − 年齢、インフレ調整の年間支出、年次リターン(MC または歴史)。 - セルあたり 5,000 回;成功 = 期末資本 > 0。
rate = 成功数/5000×100。 - UI 上限:
行×列 ≤ 120;支出ステップ最小 10、年齢ステップ ≥ 1。
SWR — 安全引出率
public/js/api.jsはメインペイロードを複製し、純粋シナリオを強制:withdrawalStrategy: fixed、追加フェーズ・負債・危機・住宅ローンなし。- Worker 内:二分探索 1–15%(7 回)。各試行で月次引出フェーズ =
(initialCapital × 率/100) / 12をyears全体に注入。 - 各試行は同じ
simulate_wasmを呼び出し。目標:≥95% の破綻なし((iterations − bankruptcies) / iterations)。 - 将来の拠出・公的年金・臨時収入は含みません。
simulate_goals.cpp、simulate_heatmap.cpp、simulation_worker.js(handleSWR)。9. サーバー側で行われること
現在の UI にメール通知はありません。
- Google アカウント / Supabase:プラン、コメント、リンク。
- AI アドバイザー:Gemini への要約送信。
- お問い合わせフォーム(Formspree)と Simple Analytics。
- 任意の匿名エラーログ。
10. 限界
Every simulation simplifies reality. It is important to know the model's assumptions:
- 将来は歴史的 P10 より悪くなる可能性があります。
- 税制と手数料は変わります。
- 教育用ツールであり、投資推奨ではありません。