Metodologia del simulatore

Trasparenza tecnica su traiettorie, percentili e strumenti avanzati.

Ultimo aggiornamento: maggio 2026

1. Riepilogo: cosa fa il simulatore

My FIRE Simulator proietta piani di indipendenza finanziaria (FIRE) con migliaia di traiettorie mensili (Monte Carlo o storico), tasse configurabili e strategie di prelievo. Il nucleo numerico è in C++ e gira nel browser come WebAssembly (WASM) in un Web Worker: i dati del piano principale non vengono inviati ai nostri server per il calcolo.

Privacy by design: inviamo dati a Supabase o ad altri servizi descritti nella informativa sulla privacy solo se salvi un piano, condividi un link o usi il consulente IA.

Questa pagina descrive la logica per chi vuole capire — o verificare mentalmente — il motore. Non sostituisce una consulenza finanziaria professionale.

2. Architettura nel browser

Il flusso tecnico del simulatore principale:

  1. Interfaccia (HTML + JavaScript): legge il modulo, valida fasi, tasse e portafoglio; costruisce un JSON di configurazione (store.js).
  2. Web Worker (simulation_worker.js): riceve il JSON e delega al binario WASM senza bloccare l'UI.
  3. Moduli WASM compilati da C++: simulazione principale, obiettivi inversi, heatmap e ricerca SWR.
  4. Risultati: percentili, grafici (Chart.js), tabella annuale e istogramma eredità renderizzati lato client.

La calcolatrice rapida della landing (« Calcola il tuo obiettivo ») è l'eccezione: solo JavaScript deterministico con interesse composto, senza WASM, per una risposta immediata.

3. Motore principale di simulazione

File sorgente: src-wasm/simulate.cpp → WASM del worker. Di default 5.000 iterazioni indipendenti (iterations nel payload), stessa configurazione, traiettorie casuali diverse.

Granularità temporale

La simulazione avanza mese per mese sull'orizzonte definito. Ogni mese: versamenti e prelievi secondo le fasi, rendimento del portafoglio, inflazione, commissioni, tasse ed eventi di stress se configurati.

Output statistici

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. Fonti dati: personalizzato vs storico

Personalizzato (Monte Carlo puro)

Genera rendimenti mensili casuali con distribuzione normale (media mu, volatilità sigma) più inflazione costante.

Storico (campionamento a blocchi)

Usa public/js/data/historical_series.js (Jordà–Schularick–Taylor, era moderna 1950–2020). Blocchi di anni consecutivi con rendimenti reali coordinati.

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. Portafoglio, volatilità e glidepath

In modalità personalizzata, l'UI stima mu e sigma con euristiche Markowitz (calculatePortfolioStats).

Il glidepath interpola i pesi mese per mese. Gli eventi di stress applicano shock negli anni scelti.

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à e bucket di capitale

La fiscalità dinamica è in src-wasm/simulate.cpp e gira solo nel loop mensile completo (needsDynamicCalc): prelievo non fisso, fonte storica, imposte, scaglioni o FIFO. Il JSON include generalCapital, taxFreeCapital, deferredCapital, taxStrategy (average | fifo), withdrawalOrder, taxBrackets[], flatTaxRate e latentGainsPct (base fiscale iniziale del bucket generale).

Identificatori in inglese: tutti i nomi tecnici in sono in inglese. Il JSON (store.js) e il C++ usano ancora chiavi spagnole (es. generalCapital nella doc = capGeneral nel codice).

Tre bucket di capitale

Ordine di prelievo (withdrawalOrder)

Modalità media (taxStrategy: "average")

Costo fiscale aggregato generalCostBasis; plusvalenza proporzionale ratio = (valore_mercato − costo) / valore_mercato. Aliquota fissa o scaglioni taxBrackets. annualGainYTD azzerato ogni anno solare. Il motore calcola il lordo per un netto obiettivo.

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)

Modalità FIFO (taxStrategy: "fifo")

Solo bucket generale: lotti { cost, units } in deque, indexPrice aggiornato ogni mese. Vendite FIFO; se > 120 lotti, fusione dei 12 più vecchi.

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)
Template per paese: approssimazioni didattiche, non consulenza fiscale (wash sale, esenzioni, ecc.).

7. Strategie di prelievo

Strategia in withdrawalStrategy: fixed, guyton o vpw. Prelievi pianificati come valori negativi in cashFlowPhases[].val nel loop dinamico.

Fissa (fixed)

withdrawal = baseWithdrawals[t] × withdrawalInflationFactor; con adjustFlowsForInflation, inflazione annua sul fattore (regola classica indicizzata).

Guyton–Klinger (guyton)

gkThreshold, gkAdjustment. initialGKWithdrawalRate al primo prelievo. Ogni gennaio: inflazione sulle spese solo se totalCapital ≥ capitalAtYearStart; guardrail se il tasso di prelievo supera o scende sotto le soglie rispetto al tasso iniziale.

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; rendita su capitale vivo: pmtRate = vpwRate / (1 − (1+vpwRate)−remainingYears); mensile = totalCapital × pmtRate / 12. Flag isVPWMeta in fiscalità.

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. Strumenti avanzati

I tre strumenti in «Strumenti avanzati» (controllers/simulation.js) non leggono il modulo principale: payload propri e worker goals, heatmap, swr.

Obiettivi (simulate_goals.cpp)

Heatmap (simulate_heatmap.cpp)

SWR — tasso di prelievo sicuro

Per auditare: simulate_goals.cpp, simulate_heatmap.cpp, simulation_worker.js (handleSWR).

9. Cosa avviene sul server

Le notifiche e-mail non sono nell'interfaccia attuale.

10. Limitazioni

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

Apri il simulatore