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.
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
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
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
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%
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
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
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
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
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
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
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
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.
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
Requiere poder computacional significativo
La convergencia puede ser lenta para algunos problemas
Depende de la calidad de los generadores de números aleatorios