Simulación con el Método Monte Carlo

Introducción

Este documento presenta implementaciones en Python para análisis de Monte Carlo en contextos actuariales y financieros, cada una acompañada de su contexto teórico y aplicado.



1. Estimación de π mediante Monte Carlo

Contexto: Este es el ejemplo clásico que demuestra el poder de los métodos Monte Carlo. Se basa en la relación geométrica entre un cuadrado unitario y un círculo inscrito. La probabilidad de que un punto aleatorio caiga dentro del círculo es π/4, lo que permite estimar π mediante simulación.

Aplicación: Aunque aparentemente simple, este método ilustra el principio fundamental de Monte Carlo: usar probabilidades y muestreo aleatorio para resolver problemas deterministas.

Código Python

import random

def estimar_pi(n_simulaciones):
    dentro_circulo = 0
    for _ in range(n_simulaciones):
        x, y = random.random(), random.random()
        if x**2 + y**2 <= 1:
            dentro_circulo += 1
    return 4 * dentro_circulo / n_simulaciones

print(f"π estimado: {estimar_pi(100000):.4f}")

Explicación Concepto: Utiliza el método de aceptación-rechazo en un cuadrado unitario

Método: Genera puntos aleatorios y cuenta cuántos caen dentro del círculo unitario

Fórmula: π ≈ 4 × (puntos dentro del círculo) / (total de puntos)

Aplicación: Demostración básica del poder de los métodos Monte Carlo



2. Valor Esperado de e^X donde X ~ N(0,1)

Contexto: En matemáticas financieras y actuariales, frecuentemente necesitamos calcular valores esperados de funciones no lineales de variables aleatorias. Este ejemplo muestra cómo estimar E[e^X] cuando X sigue una distribución normal estándar.

Aplicación: Este tipo de cálculo es fundamental en pricing de derivados financieros, valoración de opciones reales, y en modelos de tasas de interés donde se modela el crecimiento exponencial.

Código Python

import numpy as np

def valor_esperado_mc(n_sim, funcion):
    muestras = np.random.normal(0, 1, n_sim)
    return np.mean(funcion(muestras))

resultado = valor_esperado_mc(100000, lambda x: np.exp(x))
print(f"E[e^X] = {resultado:.4f} (Teórico: {np.exp(0.5):.4f})")

Explicación Propiedad matemática: Si X ~ N(0,1), entonces E[e^X] = e^(0.5)

Método: Simula muestras de la distribución normal y calcula la media empírica

Importancia: Ilustra cómo estimar valores esperados de funciones complejas



3. Cálculo de Prima de Seguro

Contexto: En la práctica actuarial, las primas de seguros se calculan considerando tanto la frecuencia como la severidad de los siniestros. Este modelo utiliza un proceso de Poisson compuesto, donde la frecuencia sigue Poisson y la severidad sigue una distribución exponencial.

Aplicación: Este enfoque se utiliza extensivamente en seguros de propiedad, responsabilidad civil, y otros ramos donde se necesita modelar el riesgo agregado considerando deducibles y límites de cobertura.

Código Python

def prima_seguro_mc(n_sim, lambda_poisson, media_siniestro, deducible, limite):
    primas = []
    for _ in range(n_sim):
        n_siniestros = np.random.poisson(lambda_poisson)
        if n_siniestros > 0:
            siniestros = np.random.exponential(media_siniestro, n_siniestros)
            siniestros_netos = np.clip(siniestros - deducible, 0, limite)
            primas.append(np.sum(siniestros_netos))
        else:
            primas.append(0)
    return np.mean(primas)

prima = prima_seguro_mc(10000, 0.1, 50000, 10000, 200000)
print(f"Prima estimada: ${prima:,.0f}")

Explicación Modelo: Proceso de Poisson compuesto para número de siniestros

Componentes:

Frecuencia: Distribución de Poisson (λ = 0.1)

Severidad: Distribución exponencial (media = 50,000)

Deducible: 10,000 (pérdida mínima antes de que el seguro pague)

Límite: 200,000 (pago máximo por siniestro)

Aplicación: Cálculo de primas puras en seguros de propiedad



4. Value at Risk (VaR)

Contexto: El Value at Risk es una métrica fundamental en la gestión de riesgo financiero que cuantifica la pérdida potencial máxima de una cartera en un horizonte temporal y nivel de confianza dados. El VaR 95% representa la pérdida que no será excedida con 95% de probabilidad.

Aplicación: Ampliamente utilizado por instituciones financieras, fondos de inversión, y empresas para medir y controlar su exposición al riesgo de mercado, cumpliendo con regulaciones como Basel III en banca.

Código Python

def calcular_var_mc(rendimientos, alpha=0.95):
    return np.percentile(rendimientos, 100 * (1 - alpha))

rendimientos = np.random.normal(0.001, 0.02, 100000)
var_95 = calcular_var_mc(rendimientos, 0.95)
print(f"VaR 95%: {var_95:.4f}")

Explicación Definición: El VaR 95% representa la pérdida máxima que no será excedida con 95% de probabilidad

Método: Simula rendimientos y calcula el percentil correspondiente

Interpretación: Un VaR de -0.0319 significa que hay 5% de probabilidad de perder más del 3.19%



5. Probabilidad de Ruina Anual

Contexto: La teoría de la ruina es un pilar de la matemática actuarial que estudia la probabilidad de que una compañía de seguros agote su capital debido a la ocurrencia de siniestros. Este modelo simula la evolución mensual del capital considerando primas cobradas y siniestros pagados.

Aplicación: Fundamental para la gestión de solvencia, determinación de capital económico, y cumplimiento de regulaciones como Solvencia II en el sector asegurador.

Código Python

def probabilidad_ruina_mc(capital_inicial, primas, lambda_siniestros, media_siniestro, n_sim):
    ruinas = 0
    for _ in range(n_sim):
        capital = capital_inicial
        for _ in range(12):  # 12 meses
            capital += primas
            n_siniestros = np.random.poisson(lambda_siniestros)
            if n_siniestros > 0:
                capital -= np.sum(np.random.exponential(media_siniestro, n_siniestros))
            if capital <= 0:
                ruinas += 1
                break
    return ruinas / n_sim

prob = probabilidad_ruina_mc(1000000, 50000, 2, 40000, 10000)
print(f"Probabilidad de ruina anual: {prob:.2%}")

Explicación Escenario: Simula 12 meses de operación para una compañía de seguros

Parámetros:

Capital inicial: $1,000,000

Primas mensuales: $50,000

Siniestros mensuales: Poisson(λ=2)

Severidad de siniestros: Exponencial(media=40,000)

Resultado: Probabilidad de que el capital se agote durante el año



6. Integración Monte Carlo

Contexto: Los métodos Monte Carlo pueden utilizarse para calcular integrales definidas, especialmente en dimensiones altas donde métodos tradicionales como Simpson o trapezoidal son computacionalmente costosos. Se basa en el teorema del valor esperado.

Aplicación: Útil en valoración de opciones exóticas, cálculo de probabilidades en distribuciones multivariadas, y en problemas de física e ingeniería con dominios complejos.

Código Python

def integral_mc(funcion, a, b, n_sim):
    x_aleatorio = np.random.uniform(a, b, n_sim)
    return (b - a) * np.mean(funcion(x_aleatorio))

resultado = integral_mc(lambda x: x**2, 0, 1, 100000)
print(f"∫x² dx [0,1] = {resultado:.4f} (Teórico: 0.3333)")

Explicación Teoría: ∫[a,b] f(x) dx ≈ (b-a) × promedio(f(x_i))

Ventaja: Útil para integrales multidimensionales o funciones complejas

Ejemplo: ∫₀¹ x² dx = 1/3 ≈ 0.3333



7. IBNR (Incurred But Not Reported)

Contexto: En seguros, los siniestros IBNR representan obligaciones que han ocurrido pero no han sido reportados a la aseguradora. Estimar adecuadamente estas reservas es crítico para la solvencia de la compañía.

Aplicación: Métodos actuariales para reservas técnicas, cumplimiento de normas contables, y gestión financiera en compañías de seguros. El percentil 95% proporciona una estimación conservadora para capital regulatorio.

Código Python

def ibnr_mc(n_sim, siniestros_reportados, factor_desarrollo_media, factor_desarrollo_std):
    factores = np.random.normal(factor_desarrollo_media, factor_desarrollo_std, n_sim)
    ibnr_simulado = siniestros_reportados * (factores - 1)
    return np.mean(ibnr_simulado), np.percentile(ibnr_simulado, 95)

media_ibnr, percentil_95 = ibnr_mc(10000, 5000000, 1.15, 0.05)
print(f"IBNR estimado: ${media_ibnr:,.0f}, Percentil 95%: ${percentil_95:,.0f}")

Explicación Contexto: Siniestros que han ocurrido pero no han sido reportados

Método: Usa factores de desarrollo con incertidumbre

Salidas:

Valor esperado del IBNR

Percentil 95% para reservas conservadoras



8. Proceso de Poisson Compuesto

Contexto: El proceso de Poisson compuesto es el modelo fundamental para pérdidas agregadas en matemática actuarial. Combina una distribución de Poisson para el número de eventos con una distribución de severidad para los montos individuales.

Aplicación: Base para modelos de riesgo operacional, seguros de propiedad y casualty, y análisis de pérdidas agregadas en banca y finanzas.

Código Python

def proceso_poisson_compuesto_mc(T, lambda_poisson, distribucion_siniestros, n_sim):
    perdidas_totales = []
    for _ in range(n_sim):
        n_eventos = np.random.poisson(lambda_poisson * T)
        if n_eventos > 0:
            montos = distribucion_siniestros(n_eventos)
            perdidas_totales.append(np.sum(montos))
        else:
            perdidas_totales.append(0)
    return perdidas_totales

perdidas = proceso_poisson_compuesto_mc(1, 10, lambda n: np.random.exponential(50000, n), 10000)
print(f"Pérdida esperada: ${np.mean(perdidas):,.0f}")

Explicación Modelo fundamental en matemáticas actuariales

Componentes:

N(t) ~ Poisson(λt) - número de eventos

X_i ~ Distribución de severidad - montos individuales

Aplicación: Modelar pérdidas agregadas en seguros



9. Precio de Opción Call Europea

Contexto: El modelo Black-Scholes proporciona una solución analítica para opciones europeas, pero Monte Carlo permite extender esta metodología a opciones con características exóticas o caminos dependientes.

Aplicación: Pricing de derivados financieros, gestión de riesgo de carteras de opciones, y valoración de instrumentos con payoffs complejos en mercados de capitales.

Código Python

def opcion_call_mc(S0, K, T, r, sigma, n_sim):
    Z = np.random.normal(0, 1, n_sim)
    ST = S0 * np.exp((r - 0.5 * sigma**2) * T + sigma * np.sqrt(T) * Z)
    payoff = np.maximum(ST - K, 0)
    return np.exp(-r * T) * np.mean(payoff)

precio_call = opcion_call_mc(100, 105, 1, 0.05, 0.2, 100000)
print(f"Precio call: ${precio_call:.2f}")

Explicación Modelo: Black-Scholes mediante simulación Monte Carlo

Fórmula: ST = S0 × exp((r - σ²/2)T + σ√T Z)

Payoff: max(ST - K, 0)

Descuento: Valor presente usando tasa libre de riesgo



10. Análisis de Sensibilidad

Contexto: El análisis de sensibilidad evalúa cómo cambian los resultados del modelo cuando se varían los parámetros de entrada. Esto es crucial para entender la robustez de las estimaciones y identificar los drivers principales del riesgo.

Aplicación: Gestión de modelos actuariales, validación de supuestos, comunicación de incertidumbre a stakeholders, y toma de decisiones bajo incertidumbre.

Código Python

def analisis_sensibilidad(parametros, n_sim_por_parametro):
    resultados = {}
    for param, valores in parametros.items():
        primas = []
        for valor in valores:
            prima = prima_seguro_mc(n_sim_por_parametro, valor, 50000, 10000, 200000)
            primas.append(prima)
        resultados[param] = primas
    return resultados

sensibilidad = analisis_sensibilidad({'lambda': [0.05, 0.1, 0.15, 0.2]}, 5000)
print("Sensibilidad a lambda:", sensibilidad)

Explicación Propósito: Entender cómo cambia la prima al variar parámetros clave

Parámetro analizado: λ (tasa de frecuencia de siniestros)

Resultado: Prima aumenta con λ, mostrando la sensibilidad del modelo



11. Métricas de Riesgo Completas

Contexto: Una gestión integral del riesgo requiere múltiples métricas que capturen diferentes aspectos del perfil de riesgo. Mientras el VaR mide un punto de la distribución, el TVaR (Tail Value at Risk) captura la severidad de las pérdidas extremas.

Aplicación: Cálculo de capital económico, optimización de carteras, gestión de riesgo enterprise-wide (ERM), y reporting regulatorio comprehensivo.

Código Python

def metricas_riesgo_completas(pérdidas):
    var_95 = np.percentile(pérdidas, 95)
    mask = pérdidas > var_95
    tvar_95 = np.mean(pérdidas[mask]) if np.any(mask) else var_95
    return {
        'Media': np.mean(pérdidas),
        'VaR_95': var_95,
        'TVaR_95': tvar_95,
        'Desviación': np.std(pérdidas),
        'Máximo': np.max(pérdidas)
    }

Explicación Métricas calculadas:

Media: Pérdida esperada

VaR 95%: Pérdida máxima con 95% de confianza

TVaR 95%: Pérdida promedio en el peor 5% de casos

Desviación: Volatilidad de las pérdidas

Máximo: Pérdida máxima simulada



Conclusión

Los métodos Monte Carlo representan una herramienta versátil y poderosa para el análisis cuantitativo en seguros y finanzas. Su capacidad para modelar sistemas complejos, incorporar incertidumbre, y proporcionar estimaciones robustas los hace indispensables en la práctica moderna de la gestión de riesgos.

Ventajas clave:

Flexibilidad para modelar distribuciones complejas

Capacidad de incorporar correlaciones y dependencias

Aplicable a problemas de alta dimensionalidad

Proporciona toda la distribución de resultados, no solo promedios

Limitaciones:

Requiere poder computacional significativo

La convergencia puede ser lenta para algunos problemas

Depende de la calidad de los generadores de números aleatorios