Módulo 2: Casos Prácticos Resueltos
Fundamentos Matemáticos — Laboratorio en Python
📋 Introducción
Estos ejercicios convierten las matemáticas del Módulo 2 en código que puedes ejecutar. Verás cómo los momentos, las matrices de covarianza y el movimiento Browniano cobran vida con datos reales y simulaciones. Ejecuta cada bloque y experimenta cambiando los parámetros.
Requisitos Previos
pip install numpy pandas matplotlib scipy yfinance
🧪 Caso Práctico 1: Los Cuatro Momentos en Acción
Objetivo
Calcular e interpretar los cuatro momentos de un activo real y comparar con una normal teórica.
Código
import yfinance as yf
import numpy as np
from scipy import stats
import matplotlib.pyplot as plt
# Descargar datos
datos = yf.download("AAPL", start="2019-01-01", end="2024-01-01")
retornos = np.log(datos["Close"] / datos["Close"].shift(1)).dropna()
# Los cuatro momentos
media = retornos.mean()
volatilidad = retornos.std()
asimetria = stats.skew(retornos)
curtosis = stats.kurtosis(retornos) # exceso de curtosis (normal = 0)
print("=== LOS CUATRO MOMENTOS DE AAPL (diario) ===")
print(f"1. Media: {media:.5f} ({media*252:.2%} anualizado)")
print(f"2. Volatilidad: {volatilidad:.5f} ({volatilidad*np.sqrt(252):.2%} anualizado)")
print(f"3. Asimetría: {asimetria:.3f}")
print(f"4. Curtosis: {curtosis:.3f} (normal = 0)")
# Interpretación automática
print("\n=== INTERPRETACIÓN ===")
print(f"Asimetría: {'cola izquierda más larga (pérdidas extremas)' if asimetria < 0 else 'cola derecha más larga'}")
print(f"Curtosis: {'colas pesadas, eventos extremos frecuentes' if curtosis > 0 else 'colas ligeras'}")
Interpretación
Resultados típicos:
- Asimetría a menudo negativa → las caídas bruscas son más comunes que las subidas bruscas
- Exceso de curtosis claramente positivo → confirma colas pesadas
Lección: la media y la volatilidad (momentos 1 y 2) son lo que casi todos miran. Pero la asimetría y la curtosis (momentos 3 y 4) revelan el riesgo de cola que esos dos no capturan.
🧪 Caso Práctico 2: Comparar Distribuciones — Normal vs. t-Student
Objetivo
Ver visualmente por qué la t-Student modela mejor los retornos que la normal.
Código
import numpy as np
from scipy import stats
import matplotlib.pyplot as plt
retornos_std = (retornos - retornos.mean()) / retornos.std() # estandarizar
x = np.linspace(-5, 5, 500)
pdf_normal = stats.norm.pdf(x)
pdf_t = stats.t.pdf(x, df=4) # t-Student con 4 grados de libertad
plt.figure(figsize=(12, 6))
plt.hist(retornos_std, bins=80, density=True, alpha=0.5,
color="gray", label="Retornos reales (estandarizados)")
plt.plot(x, pdf_normal, "b-", linewidth=2, label="Normal")
plt.plot(x, pdf_t, "r-", linewidth=2, label="t-Student (df=4)")
plt.xlim(-5, 5)
plt.title("Retornos reales vs. Normal vs. t-Student")
plt.xlabel("Retorno estandarizado")
plt.ylabel("Densidad")
plt.legend()
plt.grid(True, alpha=0.3)
plt.tight_layout()
plt.show()
# Comparar probabilidad de un evento extremo (caída de -3 desviaciones)
prob_normal = stats.norm.cdf(-3)
prob_t = stats.t.cdf(-3, df=4)
print(f"P(retorno < -3σ) según Normal: {prob_normal:.4%}")
print(f"P(retorno < -3σ) según t-Student: {prob_t:.4%}")
print(f"La t-Student asigna {prob_t/prob_normal:.1f}x más probabilidad al evento extremo")
Interpretación
- En las colas, la t-Student se ajusta mejor a los datos reales que la normal
- La normal asigna a un evento de -3σ una probabilidad ínfima; la t-Student lo considera bastante más probable
- Esto explica el “Lunes Negro” de 1987: bajo la normal era imposible; bajo colas pesadas, solo raro
🧪 Caso Práctico 3: Construir una Matriz de Covarianza
Objetivo
Calcular la matriz de covarianza de una cartera real y entender su estructura.
Código
import yfinance as yf
import numpy as np
import pandas as pd
# Cartera de 4 activos diversos
tickers = ["AAPL", "JPM", "XOM", "GLD"] # tech, banco, petróleo, oro
datos = yf.download(tickers, start="2020-01-01", end="2024-01-01")["Close"]
retornos = np.log(datos / datos.shift(1)).dropna()
# Matriz de covarianza ANUALIZADA (×252)
matriz_cov = retornos.cov() * 252
print("=== MATRIZ DE COVARIANZA (anualizada) ===")
print(matriz_cov.round(4))
# Matriz de correlación (más interpretable)
matriz_corr = retornos.corr()
print("\n=== MATRIZ DE CORRELACIÓN ===")
print(matriz_corr.round(3))
# La diagonal de la covarianza son las varianzas → volatilidades
print("\n=== VOLATILIDADES INDIVIDUALES (anualizadas) ===")
for ticker in tickers:
vol = np.sqrt(matriz_cov.loc[ticker, ticker])
print(f"{ticker}: {vol:.2%}")
Interpretación
- La diagonal de la matriz de covarianza contiene las varianzas (volatilidad²) de cada activo
- Fuera de la diagonal están las covarianzas entre pares
- En la matriz de correlación, busca pares con correlación baja o negativa → son los mejores diversificadores
- El oro (GLD) suele mostrar correlación baja con las acciones → de ahí su rol de refugio
🧪 Caso Práctico 4: La Fórmula Maestra del Riesgo (wᵀΣw)
Objetivo
Calcular el riesgo de una cartera usando álgebra matricial, comprobando la diversificación.
Código
import numpy as np
# Usamos la matriz de covarianza del caso anterior
Sigma = matriz_cov.values # convertir a array de numpy
# Pesos iguales (25% cada activo)
w = np.array([0.25, 0.25, 0.25, 0.25])
# LA FÓRMULA MAESTRA: σ²ₚ = wᵀ Σ w
varianza_cartera = w.T @ Sigma @ w
vol_cartera = np.sqrt(varianza_cartera)
print(f"Volatilidad de la cartera (wᵀΣw): {vol_cartera:.2%}")
# Comparar con la media simple de volatilidades (lo que sería SIN diversificación)
vols_individuales = np.sqrt(np.diag(Sigma))
vol_media_simple = (w * vols_individuales).sum()
print(f"Media ponderada de volatilidades: {vol_media_simple:.2%}")
print(f"\nBENEFICIO DE DIVERSIFICACIÓN: {vol_media_simple - vol_cartera:.2%}")
print(f"Reducción de riesgo: {(1 - vol_cartera/vol_media_simple):.1%}")
Interpretación
El resultado clave: la volatilidad de la cartera (wᵀΣw) es menor que la media ponderada de las volatilidades individuales. Esa diferencia es el beneficio de la diversificación, capturado matemáticamente.
Por qué funciona: la fórmula wᵀΣw incluye los términos de covarianza cruzada. Cuando los activos no están perfectamente correlacionados, sus movimientos se cancelan parcialmente, reduciendo el riesgo total. Esta es la matemática exacta detrás del “único almuerzo gratis en finanzas”.
🧪 Caso Práctico 5: Simular Movimiento Browniano Geométrico
Objetivo
Implementar el GBM y simular trayectorias de precios — el motor de Monte Carlo.
Código
import numpy as np
import matplotlib.pyplot as plt
np.random.seed(42)
# Parámetros del GBM
S0 = 100 # precio inicial
mu = 0.08 # deriva anual (8%)
sigma = 0.20 # volatilidad anual (20%)
T = 1.0 # horizonte (1 año)
n_pasos = 252 # días de trading
dt = T / n_pasos
n_simulaciones = 50
# Simular trayectorias
plt.figure(figsize=(12, 6))
precios_finales = []
for i in range(n_simulaciones):
precios = [S0]
for t in range(n_pasos):
# Fórmula discreta del GBM
Z = np.random.normal()
siguiente = precios[-1] * np.exp((mu - 0.5*sigma**2)*dt + sigma*np.sqrt(dt)*Z)
precios.append(siguiente)
precios_finales.append(precios[-1])
plt.plot(precios, linewidth=0.8, alpha=0.6)
plt.axhline(S0, color="black", linestyle="--", alpha=0.5, label="Precio inicial")
plt.title(f"{n_simulaciones} trayectorias GBM (μ={mu}, σ={sigma})")
plt.xlabel("Día")
plt.ylabel("Precio")
plt.legend()
plt.grid(True, alpha=0.3)
plt.tight_layout()
plt.show()
# Estadísticas de los precios finales
precios_finales = np.array(precios_finales)
print(f"Precio inicial: {S0}")
print(f"Precio final medio: {precios_finales.mean():.2f}")
print(f"Precio teórico esperado (S0·e^(μT)): {S0*np.exp(mu*T):.2f}")
print(f"Rango: [{precios_finales.min():.2f}, {precios_finales.max():.2f}]")
Interpretación
Qué observas:
- Cada trayectoria es distinta (el azar del término σS dW)
- Todas comparten la misma tendencia alcista de fondo (la deriva μS dt)
- Los precios nunca son negativos (gracias a la formulación exponencial/lognormal)
- El precio final medio se aproxima a S₀·e^(μT)
Conexión teórica: has implementado la versión discreta de dS = μS dt + σS dW. Este código es la base de la simulación Monte Carlo que usarás para valorar opciones (Módulo 4) y estimar VaR (Módulo 5).
🧪 Caso Práctico 6: El Teorema Central del Límite Visualizado
Objetivo
Comprobar el TCL empíricamente y entender sus límites en finanzas.
Código
import numpy as np
import matplotlib.pyplot as plt
np.random.seed(42)
# Partimos de una distribución MUY no normal (uniforme)
fig, axes = plt.subplots(2, 2, figsize=(13, 9))
tamanios_muestra = [1, 2, 10, 30]
for ax, n in zip(axes.flat, tamanios_muestra):
# Promediar n variables uniformes, 10000 veces
medias = [np.random.uniform(0, 1, n).mean() for _ in range(10000)]
ax.hist(medias, bins=50, density=True, alpha=0.7, color="teal")
ax.set_title(f"Media de n={n} variables uniformes")
ax.set_xlabel("Valor medio")
ax.grid(True, alpha=0.3)
plt.suptitle("Teorema Central del Límite en acción", fontsize=14)
plt.tight_layout()
plt.show()
print("Observa cómo, al promediar más variables, la distribución")
print("se vuelve cada vez más parecida a una campana (normal),")
print("AUNQUE partíamos de una distribución uniforme (plana).")
Interpretación
Lo que demuestra:
- Con n=1, ves la distribución original (uniforme, plana)
- A medida que n crece, la distribución de las medias se acerca a una normal
- Esto es el TCL: sumas/promedios de variables tienden a la normalidad
El matiz crítico en finanzas:
- El TCL asume independencia entre las variables
- Los retornos financieros NO son del todo independientes (clustering de volatilidad, autocorrelación)
- En crisis, los activos se mueven juntos (la “independencia” desaparece)
- Por eso, aunque el TCL justifica usar la normal en muchos casos, falla precisamente cuando más importa: en los eventos extremos
🎓 Proyecto del Módulo
Enunciado
Construye un análisis matemático completo de una cartera de 3 activos a tu elección:
- Descarga 3 años de datos
- Calcula los cuatro momentos de cada activo
- Construye la matriz de covarianza anualizada
- Calcula el riesgo de la cartera con pesos iguales usando wᵀΣw
- Compara con la media ponderada de volatilidades (mide la diversificación)
- Simula una trayectoria GBM para uno de los activos usando su μ y σ reales
- Escribe un párrafo interpretando los resultados
Solución de Referencia (Plantilla)
import yfinance as yf
import numpy as np
from scipy import stats
# 1. Elige tus activos
mis_tickers = ["MSFT", "KO", "GLD"] # cámbialos
datos = yf.download(mis_tickers, start="2021-01-01", end="2024-01-01")["Close"]
retornos = np.log(datos / datos.shift(1)).dropna()
# 2. Cuatro momentos por activo
print("=== MOMENTOS ===")
for t in mis_tickers:
r = retornos[t]
print(f"\n{t}:")
print(f" Media anual: {r.mean()*252:.2%}")
print(f" Vol anual: {r.std()*np.sqrt(252):.2%}")
print(f" Asimetría: {stats.skew(r):.3f}")
print(f" Curtosis: {stats.kurtosis(r):.3f}")
# 3. Matriz de covarianza
Sigma = retornos.cov().values * 252
# 4. Riesgo de cartera (pesos iguales)
n = len(mis_tickers)
w = np.array([1/n] * n)
vol_cartera = np.sqrt(w.T @ Sigma @ w)
print(f"\n=== CARTERA ===")
print(f"Volatilidad de cartera: {vol_cartera:.2%}")
# 5. Comparar con media simple
vol_media = (w * np.sqrt(np.diag(Sigma))).sum()
print(f"Media ponderada de vols: {vol_media:.2%}")
print(f"Beneficio diversificación: {vol_media - vol_cartera:.2%}")
# 6. Simular GBM del primer activo
np.random.seed(1)
mu = retornos[mis_tickers[0]].mean() * 252
sigma = retornos[mis_tickers[0]].std() * np.sqrt(252)
S0, dt, pasos = 100, 1/252, 252
precio = S0
for _ in range(pasos):
precio *= np.exp((mu - 0.5*sigma**2)*dt + sigma*np.sqrt(dt)*np.random.normal())
print(f"\nPrecio simulado tras 1 año ({mis_tickers[0]}): {precio:.2f}")
# 7. Tu interpretación aquí
Criterios de Evaluación
- Código funcional: se ejecuta sin errores (25%)
- Momentos y matriz correctos: cálculos bien hechos (30%)
- Fórmula wᵀΣw aplicada: riesgo de cartera correcto (20%)
- Interpretación: análisis razonado de diversificación y riesgo de cola (25%)
📝 Resumen del Módulo Práctico
Has aprendido a:
✓ Calcular e interpretar los cuatro momentos con scipy.stats
✓ Comparar la normal con la t-Student y entender las colas pesadas
✓ Construir matrices de covarianza y correlación con numpy/pandas
✓ Aplicar la fórmula maestra del riesgo (wᵀΣw) con álgebra matricial
✓ Simular trayectorias de precios con el GBM
✓ Visualizar el TCL y comprender sus límites en finanzas
“La matemática que acabas de programar es la misma que usan los fondos más sofisticados del mundo. La diferencia no está en las fórmulas, sino en la disciplina con que las aplicas y la humildad con que reconoces sus límites.”
En el Módulo 3 daremos el salto a la estadística aplicada: estacionariedad, regresión, y los modelos de series temporales que predicen (o intentan predecir) el comportamiento de los mercados.
Fin de los Casos Prácticos del Módulo 2