Méthodologie du simulateur

Transparence technique pour comprendre trajectoires, percentiles et outils avancés.

Dernière mise à jour : mai 2026

1. Résumé : ce que fait le simulateur

My FIRE Simulator projette des plans d'indépendance financière (FIRE) via des milliers de trajectoires mensuelles (Monte-Carlo ou historique), des impôts configurables et des stratégies de retrait. Le cœur numérique est en C++ et s'exécute dans votre navigateur en WebAssembly (WASM) dans un Web Worker : les chiffres de votre plan principal ne sont pas envoyés à nos serveurs pour le calcul.

Confidentialité par conception : nous n'envoyons des données à Supabase ou à d'autres services décrits dans la politique de confidentialité que si vous enregistrez un plan, partagez un lien ou utilisez le conseiller IA.

Cette page décrit la logique pour les utilisateurs qui veulent comprendre — ou vérifier mentalement — le moteur. Elle ne remplace pas un conseil financier professionnel.

2. Architecture dans le navigateur

Le flux technique du simulateur principal :

  1. Interface (HTML + JavaScript) : lit le formulaire, valide phases, impôts et portefeuille ; construit un JSON de configuration (store.js).
  2. Web Worker (simulation_worker.js) : reçoit le JSON et délègue au binaire WASM adapté sans bloquer l'UI.
  3. Modules WASM compilés depuis C++ : simulation principale, objectifs inverses, carte de chaleur et recherche SWR.
  4. Résultats : percentiles, graphiques (Chart.js), tableau annuel et histogramme d'héritage rendus côté client.

Le calculateur rapide de la page d'accueil (« Calculez votre objectif ») est l'exception : JavaScript déterministe avec intérêts composés uniquement, sans WASM, pour une réponse instantanée.

3. Moteur principal de simulation

Fichier source : src-wasm/simulate.cpp → WASM du worker. Par défaut 5 000 itérations indépendantes (iterations dans le payload), même configuration, trajectoires aléatoires différentes.

Granularité temporelle

La simulation avance mois par mois sur l'horizon défini. Chaque mois : apports et retraits selon les phases, rendement du portefeuille, inflation, frais, impôts et événements de stress si configurés.

Sorties statistiques

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. Sources de données : personnalisé vs historique

Personnalisé (Monte-Carlo pur)

Génère des rendements mensuels aléatoires avec une distribution normale (moyenne mu, volatilité sigma) plus inflation constante.

Historique (échantillonnage par blocs)

Utilise public/js/data/historical_series.js (Jordà–Schularick–Taylor, ère moderne 1950–2020). Blocs d'années consécutives avec rendements réels coordonnés.

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. Portefeuille, volatilité et glidepath

En mode personnalisé, l'UI estime mu et sigma avec des heuristiques type Markowitz (calculatePortfolioStats).

Le glidepath interpole les poids mois par mois. Les événements de stress appliquent des chocs aux années choisies.

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. Fiscalité et compartiments de capital

La fiscalité dynamique est dans src-wasm/simulate.cpp et ne s'exécute que dans la boucle mensuelle complète (needsDynamicCalc) : retrait non fixe, source historique, impôts, tranches ou FIFO. Le JSON inclut generalCapital, taxFreeCapital, deferredCapital, taxStrategy (average | fifo), withdrawalOrder, taxBrackets[], flatTaxRate et latentGainsPct.

Identifiants en anglais : tous les noms techniques dans sont en anglais. Le JSON (store.js) et le C++ gardent des clés historiques en espagnol (ex. generalCapital ici = capGeneral dans le code).

Trois compartiments de capital

Ordre de retrait (withdrawalOrder)

Mode moyenne (taxStrategy: "average")

Coût fiscal agrégé generalCostBasis ; plus-value proportionnelle : ratio = (valeur_marché − coût) / valeur_marché. Taux plat ou tranches taxBrackets. annualGainYTD remis à zéro chaque année civile. Le moteur calcule le brut pour un net cible.

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)

Mode FIFO (taxStrategy: "fifo")

Uniquement sur general : lots { cost, units } en deque, indexPrice mis à jour chaque mois. Ventes FIFO ; fusion des 12 plus anciens lots si > 120 lots.

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)
Modèles par pays = approximations pédagogiques, pas de conseil fiscal. Le moteur ne couvre pas tous les cas (wash sales, abattements, etc.).

7. Stratégies de retrait

Stratégie : withdrawalStrategyfixed, guyton ou vpw. Retraits planifiés en valeurs négatives dans cashFlowPhases[].val.

Fixe (fixed)

withdrawal = baseWithdrawals[t] × withdrawalInflationFactor ; revalorisation annuelle si adjustFlowsForInflation (règle classique inflation-indexée).

Guyton–Klinger (guyton)

gkThreshold, gkAdjustment. initialGKWithdrawalRate au premier retrait. Chaque janvier : inflation sur les dépenses seulement si capital ≥ début d'année ; garde-fous si la taux de retrait dépasse ou descend sous les seuils par rapport au taux initial.

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 ; annuité sur capital vivant : remainingYears, pmtRate = vpwRate / (1 − (1+vpwRate)−remainingYears) ; mensuel = totalCapital × pmtRate / 12. Fiscalité : 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. Outils avancés

Les trois outils « Outils avancés » (controllers/simulation.js) n'utilisent pas le formulaire principal : payloads dédiés vers le worker (goals, heatmap, swr).

Objectifs (simulate_goals.cpp)

Carte de chaleur (simulate_heatmap.cpp)

SWR — taux de retrait sécurisé

Pour auditer : simulate_goals.cpp, simulate_heatmap.cpp, simulation_worker.js (handleSWR).

9. Ce qui passe par le serveur

Les notifications par e-mail ne sont pas dans l'interface actuelle.

10. Limitations

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

Ouvrir le simulateur