Metodología del simulador
Transparencia técnica para entender trayectorias, percentiles y herramientas avanzadas.
Última actualización: mayo de 2026
1. Resumen: qué hace el simulador
My FIRE Simulator proyecta planes de independencia financiera (FIRE) mediante miles de trayectorias mensuales (Montecarlo o histórico), impuestos configurables y estrategias de retiro. El núcleo numérico está escrito en C++ y se ejecuta en tu navegador como WebAssembly (WASM) dentro de un Web Worker: tus cifras del plan principal no se envían a nuestros servidores para calcular.
Esta página describe la lógica para usuarios que quieren entender —o auditar mentalmente— el motor. No sustituye asesoramiento financiero profesional.
2. Arquitectura en el navegador
El flujo técnico del simulador principal es:
- Interfaz (HTML + JavaScript): lee el formulario, valida fases, impuestos y cartera; construye un JSON de configuración (
store.js). - Web Worker (
simulation_worker.js): recibe el JSON y delega en el binario WASM adecuado sin bloquear la UI. - Módulos WASM compilados desde C++: simulación principal, objetivos inversos, mapa de calor y búsqueda de SWR.
- Resultados: percentiles, gráficos (Chart.js), tabla anual e histograma de herencia se renderizan en el cliente.
La calculadora rápida de la landing («Calcula tu meta») es la excepción: usa solo JavaScript con interés compuesto determinista, sin WASM, para una respuesta instantánea.
3. Motor principal de simulación
Por defecto se ejecutan 5.000 iteraciones independientes (iterations en el payload), cada una con la misma configuración pero trayectorias aleatorias distintas.
Granularidad temporal
La simulación avanza mes a mes durante el horizonte definido. En cada mes se aplican, en este orden: selección de retorno, crecimiento de cartera, comisión del broker, amortización de deudas, compensación aportes/retiros, aportaciones netas, retiros fiscales y registro de capital.
Retorno mensual — modo personalizado (GBM log-normal)
En modo personalizado el motor genera un rendimiento mensual usando un Movimiento Browniano Geométrico (Geometric Brownian Motion). El generador de números aleatorios es un Mersenne Twister (mt19937) con semilla del hardware (std::random_device):
R_mensual = exp( (μ − σ²/2) × Δt + σ × √Δt × Z ) − 1
donde:
Δt = 1/12 (paso mensual)
Z ~ N(0, 1) (distribución normal estándar)
μ = rentabilidad anual esperada de la cartera
σ = volatilidad anual de la carteraRetorno mensual — modo histórico (block sampling)
En modo histórico, cada año del simulador se extrae un bloque consecutivo de la serie JST. Las rentabilidades anuales se convierten a mensuales y se combinan según los pesos de cartera:
R_mes_eq = (1 + R_anual_eq)^(1/12) − 1
R_mes_bond = (1 + R_anual_bond)^(1/12) − 1
R_mes_fx = (1 + R_anual_fx)^(1/12) − 1
R_portfolio = w_acciones × R_mes_eq + w_bonos × R_mes_bond + w_cripto × R_cripto_lognormal
Si NO hedged (divisa sin cubrir):
R_final = (1 + R_portfolio) × (1 + R_mes_fx) − 1Salidas estadísticas
- P10 / P50 / P90: percentiles interpolados del capital (nominal y real) en cada mes y al final.
- Probabilidad de éxito / riesgo de ruina: fracción de trayectorias que agotan el capital antes del horizonte.
- Desglose fiscal (P50): comisiones e impuestos acumulados en escenario mediano cuando el motor calcula impuestos dinámicamente.
Cálculo de percentiles interpolados
No asumimos distribución normal del capital final. Los percentiles se calculan con estadística de orden directa sobre los vectores de 5.000 resultados:
índice = (N − 1) × p / 100
lower = ⌊índice⌋
frac = índice − lower
percentil = arr[lower] × (1 − frac) + arr[lower + 1] × frac
(selección parcial con std::nth_element, complejidad O(N))4. Fuentes de datos: personalizado vs histórico
Personalizado (Montecarlo puro)
Genera rendimientos mensuales aleatorios con distribución log-normal (GBM) usando la media mu y la volatilidad sigma configuradas. La inflación es constante durante toda la simulación.
Histórico (block sampling — Jordà–Schularick–Taylor)
El motor usa la base de datos Jordà–Schularick–Taylor (JST), una de las fuentes macro-financieras académicas más citadas. Cada registro anual contiene cuatro campos por país:
eq— Rentabilidad total de acciones (precio + dividendos).bond— Rentabilidad de bonos soberanos a largo plazo.inf— Tasa de inflación del año.fx— Variación del tipo de cambio frente a la divisa del usuario.
Regiones disponibles: EEUU, Europa (proxy Alemania pre-1999), Japón, Reino Unido. Periodo por defecto: era moderna 1950–2020. Botón Pánico: extiende a 1870+ incluyendo guerras mundiales y la Gran Depresión.
Algoritmo de muestreo por bloques
Para preservar la autocorrelación entre años consecutivos, el motor extrae bloques de N años (configurable como tamanoBloque, por defecto 5). Cada bloque empieza en un año aleatorio de la serie y avanza secuencialmente, envolviendo al final:
Al inicio de cada bloque:
índice_inicio = aleatorio entre [0, longitud_serie − 1]
Dentro del bloque (años consecutivos):
índice_actual = (índice_inicio + offset) % longitud_serie
Cuando se agotan los años del bloque → nuevo índice aleatorioCubrir divisa (Hedged): si la opción está activa, el impacto de fx se ignora. Si no, el retorno se ajusta: (1 + R_portfolio) × (1 + R_fx) − 1.
5. Cartera, volatilidad y glidepath
En modo personalizado, la UI estima mu y sigma con heurísticas tipo Markowitz. Las constantes usadas en el motor para cada clase de activo son:
| Activo | μ esperada | σ volatilidad |
|---|---|---|
| Acciones | 9 % | 15 % |
| Bonos | 4 % | 5 % |
| Cripto | 40 % | 75 % |
| Correlación | ρ |
|---|---|
| Acciones – Bonos | 0.05 |
| Acciones – Cripto | 0.20 |
| Bonos – Cripto | 0.05 |
Fórmula de varianza de cartera
μ_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 (transición de cartera)
Cuando se activa, los pesos de acciones/bonos/cripto se interpolan linealmente mes a mes entre los puntos de control definidos por el usuario. En cada mes t, el motor recalcula mu y sigma con la fórmula Markowitz anterior usando los pesos interpolados de ese mes.
Eventos de estrés
Los eventos de estrés aplican un factor multiplicador al retorno del mes elegido: factorEstres = 1 − caída. Una caída del 40% equivale a factorEstres = 0.6, simulando un crash bursátil puntual.
6. Fiscalidad y buckets de capital
La fiscalidad dinámica vive en src-wasm/simulate.cpp y solo entra en juego cuando el motor necesita el bucle mensual completo (needsDynamicCalc): retiro distinto de fijo, fuente histórica, impuestos activos, tramos configurados o modo FIFO. El JSON del plan incluye generalCapital, taxFreeCapital, deferredCapital, taxStrategy (average | fifo), withdrawalOrder, taxBrackets[], flatTaxRate y latentGainsPct (coste fiscal inicial del bucket general).
… están en inglés para lectura internacional. El JSON que envía store.js y el motor C++ conservan las claves históricas en español (p. ej. generalCapital en la documentación = capGeneral en el código).Tres buckets de capital
- General (broker): aportes en
cashFlowPhasesconaccount: "general". Es el único bucket con coste fiscal modelado (promedio o FIFO). - Libre:
account: "libre". En retiros se asume 100 % de ganancia imponible (ratio = 1.0); no lleva FIFO. - Diferido:
account: "diferido". Misma lógica fiscal que libre (ganancia íntegra al retirar).
Orden de retiro (withdrawalOrder)
- Óptimo (por defecto): secuencia fija
general → libre → diferido(en código: índices 0 → 2 → 1). - Prorrateado (
prorated): reparte el retiro neto entre buckets según su peso en el capital total; si uno no alcanza, el déficit pasa al siguiente en la misma secuencia.
Modo promedio (taxStrategy: "average")
Se mantiene un coste fiscal agregado (generalCostBasis) en el bucket general. Al retirar, la plusvalía imponible es proporcional. Los impuestos pueden ser un tipo plano (flatTaxRate) o tramos progresivos (taxBrackets con max y rate). El beneficio fiscal se acumula en annualGainYTD y se reinicia cada año natural.
ratio_beneficio = (valorMercado − costeBase) / valorMercado
plusvalía = retiroBruto × ratio_beneficio
Tipo plano: impuesto = plusvalía × flatTaxRate
Tramos: Para cada tramo con (max, rate):
capacidad = tramo.max − beneficioAcumuladoAño
chunk = min(plusvalíaRestante, capacidad)
impuesto += chunk × tramo.rate
El motor resuelve el bruto necesario para un retiro neto:
bruto = neto / (1 − ratio × tasa_marginal)Modo FIFO (taxStrategy: "fifo")
Solo aplica al bucket general. Cada aporte crea un lote { cost, units } en un deque; un precio índice (indexPrice) se actualiza cada mes con el retorno de cartera y la comisión del broker. Al vender, se consumen lotes por orden de entrada (FIFO). Si el deque supera 120 lotes, se fusionan los 12 más antiguos en uno para limitar memoria.
Actualización mensual del precio índice:
precioÍndice *= (1 + R_portfolio) × (1 − comisiónBroker)
Por cada lote consumido (orden FIFO):
valorMercado_lote = unidades × precioÍndice
coste_por_unidad = coste_original / unidades_original
ganancia = (precioÍndice − coste_por_unidad) × unidades_vendidas
impuesto += aplicar_tramos(ganancia)7. Estrategias de retiro
La estrategia se envía como withdrawalStrategy: fixed (por defecto), guyton o vpw. Los retiros programados en fases llegan como valores negativos en cashFlowPhases[].val; el motor los convierte en flujo de caja mensual dentro del bucle dinámico.
Fija (fixed)
Cada mes con retiro planificado: withdrawal = baseWithdrawals[t] × withdrawalInflationFactor. Si adjustFlowsForInflation está activo, withdrawalInflationFactor se multiplica por (1 + annualInflation) una vez al año (igual que los aportes con generalInflationFactor). Equivale a la regla clásica: mismo poder adquisitivo inicial, revalorizado por inflación.
Guyton–Klinger (guyton)
Parámetros UI: gkThreshold (%) y gkAdjustment (%). El motor calcula la tasaRetiroInicial la primera vez que hay retiro. Cada enero aplica dos reglas de guardia:
tasaRetiroInicial = retiroAnualPlanificado / capitalTotal (solo al 1er retiro)
Cada enero:
1. Regla de prosperidad:
Si capitalTotal ≥ capitalInicioAño → aplicar inflación al gasto
Si no → congelar gasto (no revalorizar)
2. Reglas de guardia:
tasaActual = gastoAnual / capitalTotal
Si tasaActual > tasaInicial × (1 + threshold) → factor × (1 − adjustment) [recorte]
Si tasaActual < tasaInicial × (1 − threshold) → factor × (1 + adjustment) [subida]VPW — Variable Percentage Withdrawal (vpw)
Parámetro vpwRate (tasa anual esperada, p. ej. 4 %). Cada enero (o al inicio del retiro) recalcula una anualidad sobre el capital vivo. No es un % fijo del capital cada mes, sino una renta variable tipo préstamo inverso:
añosRestantes = (mesesTotales − t + 1) / 12
Si vpwRate > 0:
pmtRate = vpwRate / (1 − (1 + vpwRate)^(−añosRestantes))
Si vpwRate = 0:
pmtRate = 1 / añosRestantes
Retiro_mensual = capitalTotal × pmtRate / 128. Herramientas avanzadas
Las tres herramientas del panel «Herramientas avanzadas» (public/js/controllers/simulation.js) no reutilizan el formulario principal: construyen payloads propios y llaman al Web Worker (simulation_worker.js) con tipos goals, heatmap y swr.
Objetivos (simulate_goals.cpp)
mode: "time": ¿cuántos años hasta alcanzartargetconinitialy aportación mensualmonthly? Simula hasta 80 años por trayectoria; si no llega, devuelve 80.mode: "money": ¿qué aportación mensual constante (en euros de hoy) hace falta enyearspara llegar a la meta?- Siempre 5.000 iteraciones Monte Carlo / histórico. Rentabilidad real: en custom, log-normal menos inflación constante; en histórico, bootstrap de un año aleatorio y avance secuencial por la serie inyectada desde
historical_series.js. - Percentiles con convención «pesimista = peor»: en modo tiempo, P10 = percentil 90 de años (tarda más); P90 = percentil 10. En modo dinero, P10 = ahorro alto (peor para ti).
Mapa de calor (simulate_heatmap.cpp)
- Modelo simplificado respecto al motor principal: sin fases, impuestos, glidepath, deudas ni estrategias GK/VPW.
- Rejilla: edad de retiro (
ageMin…ageMax, pasoageStep) × gasto mensual (expMin…expMax, pasoexpStep). Por celda: capital fijo del panel, horizonte95 − edadaños, gasto anual = gasto×12 revalorizado por inflación cada año, retorno anual (Monte Carlo log-normal con μ/σ heurísticos de cartera, o histórico con avance de índice año a año). - 5.000 simulaciones por celda; éxito = capital > 0 al final. Resultado:
rate = éxitos / 5000 × 100. - Límite UI:
filas × columnas ≤ 120(controllers/simulation.js); paso mínimo gasto 10 €, edad paso ≥ 1.
SWR — tasa segura de retiro
public/js/api.jsclona el payload principal pero fuerza escenario puro:withdrawalStrategy: fixed, sin fases extra, deudas, crisis ni hipotecas.- En el worker: búsqueda binaria entre 1 % y 15 % (7 iteraciones). Cada prueba inyecta una única fase de retiro mensual =
(initialCapital × rate/100) / 12durante todo el horizonte (years). - Cada prueba llama al mismo
simulate_wasmdel plan principal (impuestos, cartera, histórico, etc.). Objetivo: ≥ 95 % de trayectorias sin ruina ((iterations − bankruptcies) / iterations). - No modela aportes futuros, pensiones públicas ni ingresos extraordinarios — solo «jubilación hoy con retiro fijo revalorizado».
simulate_goals.cpp, simulate_heatmap.cpp, simulation_worker.js (función handleSWR).9. Qué sí pasa en servidor
Las simulaciones principales se ejecutan íntegramente en tu navegador (WASM + Web Worker). Solo viajan datos al servidor en estos casos:
- Cuenta Google / Supabase: si guardas un plan en la nube, compartes un enlace o participas en la galería comunitaria (comentarios, likes).
- Asesor IA: envía un resumen de tu plan a Google Gemini vía Netlify Function. Los datos se descartan tras generar la respuesta.
- Formulario de contacto: Formspree (solo nombre, email y mensaje).
- Analítica: Simple Analytics (sin cookies, sin datos personales).
- Logs de error: si el motor WASM falla, se registra el error (sin datos del plan) para depuración.
10. Limitaciones y supuestos
Toda simulación es una simplificación de la realidad. Es importante conocer los supuestos del modelo:
- El futuro puede ser peor que el P10 histórico. La serie JST no cubre escenarios de hiperinflación extrema, confiscación de activos o colapso sistémico global sin precedentes.
- Correlaciones constantes. Las correlaciones entre activos (acciones–bonos, acciones–cripto) son fijas. En la realidad, las correlaciones cambian en crisis (tienden a subir).
- Rebalanceo implícito sin costes. El motor asume que la cartera mantiene sus pesos objetivo cada mes sin modelar costes de transacción por rebalanceo.
- Inflación constante en modo personalizado. En modo custom, la inflación no varía entre iteraciones. En modo histórico sí fluctúa año a año.
- Normativa fiscal y comisiones cambian. Los tramos impositivos y comisiones de broker que configures son estáticos durante toda la simulación.
- No modela riesgo de secuencia avanzado. El block sampling preserva autocorrelación dentro de cada bloque, pero no captura regímenes de mercado de larga duración.
- Herramienta educativa, no recomendación de inversión. Consulta siempre con un asesor financiero profesional antes de tomar decisiones sobre tu patrimonio.