Etapa 2 de 4 · Fundamentos en Python
Simulación de Monte Carlo en finanzas con Python
Qué es la simulación de Monte Carlo y cómo usarla en finanzas: el movimiento browniano geométrico, simular miles de trayectorias de un precio en Python, probabilidades de pérdida y valorar una opción.
En este artículo
En física de partículas usamos Monte Carlo a diario: cuando un problema tiene demasiadas piezas aleatorias para resolverlo con papel y lápiz, lo simulamos miles de veces y miramos la estadística del resultado. En finanzas pasa exactamente lo mismo, y por eso es una de las herramientas más útiles que puede aprender un quant.
En este artículo vas a entender la idea, el modelo de precio que hay detrás y a programarlo en Python para responder preguntas reales: ¿qué probabilidad hay de perder más de un 20% en un año? o ¿cuánto vale una opción?
Qué es el método de Monte Carlo
La idea cabe en una línea: si no sabes calcular algo, simúlalo muchas veces y promedia. La ley de los grandes números garantiza que el promedio de muchas simulaciones se acerca al valor verdadero.
Un ejemplo sin finanzas: ¿cuál es la probabilidad de sacar al menos un seis en cuatro tiradas de un dado? Se puede calcular (\( 1 - (5/6)^4 \approx 0{,}518 \)), pero también simular:
import numpy as np
rng = np.random.default_rng(42)
tiradas = rng.integers(1, 7, size=(1_000_000, 4))
prob = (tiradas == 6).any(axis=1).mean()
print(f"Simulado: {prob:.4f} Exacto: {1 - (5/6)**4:.4f}")Con un millón de simulaciones el error es de unas milésimas. En finanzas el «dado» es el precio de un activo, y lo que simulamos son sus posibles trayectorias futuras.
El modelo: movimiento browniano geométrico
Para simular un precio necesitamos un modelo de cómo se mueve. El estándar, el mismo que usa Black-Scholes, es el movimiento browniano geométrico (GBM):
\[ dS_t = \mu S_t\,dt + \sigma S_t\,dW_t \]
Se lee así: en cada instante el precio tiene una tendencia \( \mu \) (la deriva) y un componente aleatorio de tamaño \( \sigma \) (la volatilidad), ambos proporcionales al precio. Por eso es «geométrico»: se mueve en porcentajes, no en euros, y el precio nunca puede ser negativo.
La ecuación tiene solución exacta, que es la que usaremos para simular:
\[ S_{t+\Delta t} = S_t \exp\!\left[\left(\mu - \tfrac{1}{2}\sigma^2\right)\Delta t + \sigma\sqrt{\Delta t}\,Z\right], \qquad Z \sim \mathcal{N}(0,1) \]
¿De dónde sale el \( -\tfrac12\sigma^2 \)? Del lema de Itô: al pasar a logaritmos, la volatilidad «resta» rentabilidad. Intuitivamente, perder un 50% y ganar después un 50% no te deja igual, te deja en un 75%. Cuanta más volatilidad, más se nota ese efecto.
Simular trayectorias en Python
Simulemos una acción que hoy vale 100 €, con una rentabilidad esperada del 8% anual y una volatilidad del 25%, durante un año de 252 sesiones. En lugar de un bucle, generamos todos los números aleatorios de golpe con numpy, que es cientos de veces más rápido:
S0, mu, sigma = 100.0, 0.08, 0.25 # precio inicial, deriva y volatilidad anuales
T, pasos, n_sim = 1.0, 252, 10_000 # horizonte (años), pasos y simulaciones
dt = T / pasos
rng = np.random.default_rng(7)
Z = rng.standard_normal((n_sim, pasos))
incrementos = (mu - 0.5 * sigma**2) * dt + sigma * np.sqrt(dt) * Z
log_trayectorias = np.cumsum(incrementos, axis=1)
S = S0 * np.exp(np.hstack([np.zeros((n_sim, 1)), log_trayectorias]))
print(S.shape) # (10000, 253): 10.000 caminos de 253 preciosCada fila de S es un futuro posible. Dibujemos unos cuantos:
import matplotlib.pyplot as plt
plt.figure(figsize=(10, 5))
plt.plot(S[:50].T, lw=0.8, alpha=0.7)
plt.title("50 trayectorias simuladas (GBM)")
plt.xlabel("Días"); plt.ylabel("Precio (€)")
plt.show()Responder preguntas con las simulaciones
Aquí está la potencia del método: una vez tienes los 10.000 futuros, cualquier pregunta se responde contando.
final = S[:, -1]
print(f"Precio medio a 1 año: {final.mean():.2f} €")
print(f"Mediana: {np.median(final):.2f} €")
print(f"Prob. de acabar en pérdidas: {(final < S0).mean():.1%}")
print(f"Prob. de perder más de un 20%: {(final < 0.8 * S0).mean():.1%}")
print(f"Intervalo del 90%: {np.percentile(final, 5):.1f} € – {np.percentile(final, 95):.1f} €")Fíjate en que la media es mayor que la mediana: la distribución del precio final es lognormal, con una cola larga hacia arriba. Unos pocos caminos muy buenos tiran de la media, pero el caso «típico» es más modesto. Es una lección importante para cualquier inversor.
También podemos mirar la trayectoria entera, no solo el final. Por ejemplo, la probabilidad de que en algún momento del año el precio caiga por debajo de 80 €, aunque luego se recupere:
toca_80 = (S.min(axis=1) < 80).mean()
print(f"Prob. de tocar 80 € en algún momento: {toca_80:.1%}")Esta probabilidad es mucho mayor que la de acabar por debajo de 80 €. Preguntas así (dependientes del camino) son difíciles de resolver con fórmulas y triviales con Monte Carlo.
Valorar una opción con Monte Carlo
Una opción call europea da derecho a comprar la acción a un precio fijo \( K \) en la fecha \( T \). Si al vencimiento la acción vale \( S_T \), la opción paga \( \max(S_T - K, 0) \).
La teoría de valoración dice que su precio es el valor esperado del pago, descontado, pero calculado en el llamado mundo neutral al riesgo: se sustituye la deriva \( \mu \) por el tipo sin riesgo \( r \). Veremos por qué en el siguiente artículo; de momento, apliquémoslo:
\[ C = e^{-rT}\,\mathbb{E}\!\left[\max(S_T - K, 0)\right] \]
def call_montecarlo(S0, K, T, r, sigma, n=1_000_000, seed=0):
rng = np.random.default_rng(seed)
Z = rng.standard_normal(n)
ST = S0 * np.exp((r - 0.5 * sigma**2) * T + sigma * np.sqrt(T) * Z)
pagos = np.maximum(ST - K, 0)
precio = np.exp(-r * T) * pagos.mean()
error = np.exp(-r * T) * pagos.std() / np.sqrt(n)
return precio, error
precio, error = call_montecarlo(S0=100, K=105, T=1, r=0.03, sigma=0.25)
print(f"Call: {precio:.3f} € ± {1.96 * error:.3f} (95%)")Como solo nos importa el precio final, no hace falta simular la trayectoria día a día: un único paso basta. El resultado debería salir en torno a 9,1 €, y en el artículo de Black-Scholes comprobarás que coincide con la fórmula cerrada.
La precisión de Monte Carlo
El error estándar de un estimador de Monte Carlo decrece como \( 1/\sqrt{N} \). Eso tiene una consecuencia práctica muy clara: para ganar un decimal de precisión necesitas 100 veces más simulaciones.
for n in [1_000, 10_000, 100_000, 1_000_000]:
p, e = call_montecarlo(100, 105, 1, 0.03, 0.25, n=n)
print(f"N = {n:>9,} precio = {p:.4f} error = {e:.4f}")Por eso existen las técnicas de reducción de varianza. La más sencilla son las variables antitéticas: por cada número aleatorio \( Z \) se usa también \( -Z \). Las dos trayectorias están negativamente correlacionadas y sus errores se compensan en parte:
def call_antitetica(S0, K, T, r, sigma, n=500_000, seed=0):
rng = np.random.default_rng(seed)
Z = rng.standard_normal(n)
a = (r - 0.5 * sigma**2) * T
b = sigma * np.sqrt(T)
pagos = 0.5 * (np.maximum(S0 * np.exp(a + b * Z) - K, 0)
+ np.maximum(S0 * np.exp(a - b * Z) - K, 0))
return np.exp(-r * T) * pagos.mean(), np.exp(-r * T) * pagos.std() / np.sqrt(n)
p, e = call_antitetica(100, 105, 1, 0.03, 0.25)
print(f"Antitética: {p:.4f} ± {1.96 * e:.4f}")Con el mismo número de números aleatorios, el error baja de forma apreciable. En los bancos se combinan varias técnicas de este tipo, porque cada simulación cuesta tiempo de cálculo.
Limitaciones que debes conocer
- Monte Carlo es tan bueno como su modelo. El GBM supone volatilidad constante y rentabilidades normales. Los mercados reales tienen colas más gruesas y volatilidad que cambia con el tiempo (lo trata el modelo GARCH).
- La deriva \( \mu \) es muy difícil de estimar. Con diez años de datos, el error en la rentabilidad media es enorme. Para valorar opciones no importa (se usa \( r \)), pero para planificar inversiones, sí.
- Converge despacio. Para problemas con pocas dimensiones suele haber métodos numéricos más rápidos; Monte Carlo brilla cuando hay muchos activos o el pago depende del camino.
Resumen
- Monte Carlo = simular muchos escenarios y hacer estadística del resultado.
- El precio se modela con un movimiento browniano geométrico, que se simula con su solución exacta.
- Con las trayectorias puedes calcular probabilidades de pérdida, intervalos y precios de opciones.
- El error cae como \( 1/\sqrt{N} \); las variables antitéticas ayudan gratis.
Siguiente paso: entender por qué una opción se valora con el tipo sin riesgo y de dónde sale la fórmula más famosa de las finanzas en Black-Scholes explicado desde la intuición.