Methodik des Simulators

Technische Transparenz zu Pfaden, Perzentilen und erweiterten Werkzeugen.

Zuletzt aktualisiert: Mai 2026

1. Überblick: Was der Simulator leistet

My FIRE Simulator projiziert Pläne für finanzielle Unabhängigkeit (FIRE) mit tausenden monatlichen Pfaden (Monte-Carlo oder historisch), konfigurierbaren Steuern und Entnahmestrategien. Der numerische Kern ist in C++ geschrieben und läuft in Ihrem Browser als WebAssembly (WASM) in einem Web Worker: Ihre Hauptplan-Daten werden nicht an unsere Server gesendet, um zu rechnen.

Datenschutz by Design: Nur wenn Sie einen Plan speichern, einen Link teilen oder den KI-Berater nutzen, senden wir Daten an Supabase oder andere in der Datenschutzerklärung beschriebene Dienste.

Diese Seite beschreibt die Logik für Nutzer, die den Motor verstehen — oder gedanklich prüfen — möchten. Sie ersetzt keine professionelle Finanzberatung.

2. Architektur im Browser

Der technische Ablauf des Hauptsimulators:

  1. Oberfläche (HTML + JavaScript): liest das Formular, prüft Phasen, Steuern und Portfolio; erzeugt ein Konfigurations-JSON (store.js).
  2. Web Worker (simulation_worker.js): empfängt das JSON und delegiert an das passende WASM-Binary, ohne die UI zu blockieren.
  3. WASM-Module aus C++ kompiliert: Hauptsimulation, inverse Ziele, Heatmap und SWR-Suche.
  4. Ergebnisse: Perzentile, Diagramme (Chart.js), Jahrestabelle und Erbschaftshistogramm werden clientseitig gerendert.

Die schnelle Rechner auf der Landingpage („Berechne dein Ziel“) ist die Ausnahme: nur deterministisches JavaScript mit Zinseszins, ohne WASM, für eine sofortige Antwort.

3. Haupt-Simulationsmotor

Quelldatei: src-wasm/simulate.cpp → Worker-WASM. Standardmäßig 5.000 unabhängige Iterationen (iterations im Payload), jeweils gleiche Konfiguration, unterschiedliche Zufallspfade.

Zeitliche Granularität

Die Simulation schreitet monatlich über den definierten Horizont voran. Jeden Monat: Einzahlungen und Entnahmen nach Phasen, Portfolio-Rendite, Inflation, Gebühren, Steuern und Stressereignissen, falls konfiguriert.

Statistische Ausgaben

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. Datenquellen: individuell vs historisch

Individuell (reines Monte-Carlo)

Erzeugt zufällige Monatsrenditen mit Normalverteilung (Mittelwert mu, Volatilität sigma) plus konstanter Inflation.

Historisch (Block-Sampling)

Nutzt public/js/data/historical_series.js (Jordà–Schularick–Taylor, moderne Ära 1950–2020). Aufeinanderfolgende Jahresblöcke mit koordinierten realen Renditen.

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. Portfolio, Volatilität und Glidepath

Im individuellen Modus schätzt die UI mu und sigma mit Markowitz-Heuristiken (calculatePortfolioStats).

Der Glidepath interpoliert Gewichte monatlich. Stressereignisse wenden Schocks in gewählten Jahren an.

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. Besteuerung und Kapital-Buckets

Die dynamische Besteuerung liegt in src-wasm/simulate.cpp und läuft nur im vollen Monatsloop (needsDynamicCalc): nicht-feste Entnahme, historische Datenquelle, aktive Steuern, konfigurierte Tranches oder FIFO. Der Plan-JSON enthält generalCapital, taxFreeCapital, deferredCapital, taxStrategy (average | fifo), withdrawalOrder, taxBrackets[], flatTaxRate und latentGainsPct (anfängliche Steuerkostenbasis des General-Buckets).

Englische Bezeichner: alle technischen Namen in sind englisch. JSON (store.js) und C++ nutzen weiterhin spanische Schlüssel (z. B. generalCapital in der Doku = capGeneral im Code).

Drei Kapital-Buckets

Entnahmereihenfolge (withdrawalOrder)

Durchschnittsmodus (taxStrategy: "average")

Es gibt eine aggregierte Steuerkostenbasis (generalCostBasis) im General-Bucket. Bei Entnahme ist der steuerpflichtige Gewinnanteil proportional: ratio = (Marktwert − Kosten) / Marktwert. Steuer als Flatrate (flatTaxRate) oder progressive Tranches (taxBrackets mit max und rate). Gewinne sammeln sich in annualGainYTD und werden jedes Kalenderjahr (alle 12 Monate) zurückgesetzt. Der Motor berechnet die nötige Brutto-Entnahme für ein Ziel-Netto (Brutto ↔ Netto pro Tranche).

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-Modus (taxStrategy: "fifo")

Nur für den Bucket general. Jede Einzahlung erzeugt ein Lot { cost, units } in einer deque; ein Indexpreis (indexPrice) wird monatlich mit Portfolio-Rendite und Broker-Gebühr aktualisiert. Verkäufe verbrauchen Lots nach FIFO: Gewinn = Marktwert des Lots − Kosten; Steuer auf diesen Gewinn (Tranches oder Flatrate). Bei mehr als 120 Lots werden die 12 ältesten zu einem Lot zusammengeführt (Speicherlimit).

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)
Ländervorlagen in der UI sind pädagogische Näherungen. Prüfe immer deine reale Steuergesetzgebung; der Motor modelliert nicht alles (Wash-Sales, Freibeträge usw.).

7. Entnahmestrategien

Die Strategie wird als withdrawalStrategy gesendet: fixed (Standard), guyton oder vpw. Geplante Entnahmen kommen als negative Werte in cashFlowPhases[].val und werden im dynamischen Monatsloop verarbeitet.

Fix (fixed)

Jeden Monat mit geplanter Entnahme: withdrawal = baseWithdrawals[t] × withdrawalInflationFactor. Mit adjustFlowsForInflation wird withdrawalInflationFactor einmal jährlich mit (1 + Jahresinflation) multipliziert (wie Einzahlungen mit generalInflationFactor). Entspricht der klassischen inflationsindexierten Entnahmeregel.

Guyton–Klinger (guyton)

UI-Parameter: gkThreshold (%) und gkAdjustment (%). Der Motor setzt initialGKWithdrawalRate = geplante Jahresentnahme / Gesamtkapital beim ersten Entnahme-Monat. Jedes Januar: (1) Inflation auf die Ausgaben nur wenn totalCapital ≥ capitalAtYearStart; (2) Vergleich der aktuellen Entnahmerate mit der Anfangsrate — über rate × (1 + Schwelle) wird der Faktor mit (1 − Anpassung) multipliziert; darunter rate × (1 − Schwelle) mit (1 + Anpassung).

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 — Variable Percentage Withdrawal (vpw)

Parameter vpwRate (erwartete Jahresrate, z. B. 4 %). Jedes Januar (oder beim Entnahmestart) wird eine Annuität auf dem lebenden Kapital neu berechnet: Restjahre remainingYears = (months − t + 1) / 12; Tilgungsrate pmtRate = vpwRate / (1 − (1+vpwRate)−remainingYears) (bei 0: 1/remainingYears). Monatsentnahme = totalCapital × pmtRate / 12. Steuerlich mit isVPWMeta (brutto-orientiert, kein fixes Netto).

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. Erweiterte Werkzeuge

Die drei Tools im Panel «Erweiterte Werkzeuge» (public/js/controllers/simulation.js) lesen nicht das Hauptformular: sie bauen eigene Payloads und rufen den Web Worker (simulation_worker.js) mit goals, heatmap und swr auf.

Ziele (simulate_goals.cpp)

Heatmap (simulate_heatmap.cpp)

SWR — sichere Entnahmerate

Audit: simulate_goals.cpp, simulate_heatmap.cpp, simulation_worker.jshandleSWR.

9. Was auf dem Server läuft

E-Mail-Benachrichtigungen sind in der aktuellen Oberfläche nicht vorhanden.

10. Einschränkungen

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

Simulator öffnen