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.

Privacidad por diseño: solo si guardas un plan, compartes un enlace o usas el asesor IA enviamos datos a Supabase u otros servicios descritos en la política de privacidad.

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:

  1. Interfaz (HTML + JavaScript): lee el formulario, valida fases, impuestos y cartera; construye un JSON de configuración (store.js).
  2. Web Worker (simulation_worker.js): recibe el JSON y delega en el binario WASM adecuado sin bloquear la UI.
  3. Módulos WASM compilados desde C++: simulación principal, objetivos inversos, mapa de calor y búsqueda de SWR.
  4. 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 cartera

Retorno 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) − 1

Salidas estadísticas

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:

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 aleatorio

Cubrir 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
Acciones9 %15 %
Bonos4 %5 %
Cripto40 %75 %

Correlaciónρ
Acciones – Bonos0.05
Acciones – Cripto0.20
Bonos – Cripto0.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 × ρ_bc

Glidepath (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).

Identificadores en inglés: en toda esta metodología los nombres técnicos dentro de 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

Orden de retiro (withdrawalOrder)

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)
Las plantillas por país en la UI son aproximaciones educativas. Verifica siempre contra tu normativa real; el motor no modela todos los matices (wash sales, exenciones personales, etc.).

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 / 12

8. 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)

Mapa de calor (simulate_heatmap.cpp)

SWR — tasa segura de retiro

Para auditar: objetivos y heatmap tienen WASM dedicados; el SWR reutiliza el motor completo con payload recortado. Archivos clave: 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:

10. Limitaciones y supuestos

Toda simulación es una simplificación de la realidad. Es importante conocer los supuestos del modelo:

Abrir el simulador