/ Proyectos / Finanzas / Markowitz con ML
Markowitz con ML para carteras de inversión: LSTM + CAPM + Optimización
Pipeline cuantitativo completo: predicción de precios de activos con redes LSTM, selección de acciones con CAPM y construcción de la Frontera Eficiente con la Teoría Moderna de Carteras de Markowitz.
Punto de partidaBrief del proyecto
El problema clásico de construcción de portafolios tiene tres subproblemas que hay que resolver en secuencia: ¿qué rendimiento espero de cada activo?, ¿qué activos incluyo?, y ¿cuánto peso le asigno a cada uno?. Este proyecto los resuelve con herramientas modernas: LSTM para el primero, CAPM para el segundo y Markowitz para el tercero.
Qué está en juego
Una mala estimación de rendimientos esperados puede generar una cartera óptima en papel que falla en producción. Si usas medias históricas como proxy del futuro, asumes que el mercado no cambia — una premisa débil. La decisión de cómo estimar rendimientos esperados es la decisión de mayor impacto en todo el pipeline.
Mapa del proyecto
Resultados de aprendizajeLo que aprendes aplicando este caso
Disciplinas integradas: Deep Learning, teoría financiera y optimización numérica en un solo pipeline.
Acciones reales de alta capitalización con datos históricos desde 2020 vía Yahoo Finance.
Indicadores técnicos construidos a mano: SMA, Bollinger, RSI y autocorrelación.
Decisión clave que lo cambia todo: cómo estimar rendimientos esperados para Markowitz.
Al terminar este proyecto sabrás hacer esto
No son conceptos abstractos. Son decisiones que podrás tomar y justificar en una tesis, en una entrevista o en un proyecto real.
Resumen del proyecto¿Qué construyes?
¿Qué encontrarás en este proyecto?
- Descarga de precios históricos de 10 acciones tecnológicas y financieras vía Yahoo Finance API.
- Predicción del precio futuro de cada activo con redes LSTM entrenadas con indicadores técnicos (SMA, Bollinger, RSI, autocorrelación).
- Filtrado de activos con el modelo CAPM: beta, costo de equity y selección de acciones con rendimiento esperado atractivo.
- Optimización del portafolio con la Teoría Moderna de Carteras de Markowitz: Frontera Eficiente y portafolio de mínima volatilidad.
Este proyecto integra tres pilares de las finanzas cuantitativas modernas en un solo pipeline: predicción de series temporales con Deep Learning, selección de activos basada en riesgo sistemático y optimización matemática de carteras. El resultado es un portafolio con pesos óptimos que maximiza el retorno ajustado al riesgo según las predicciones del modelo LSTM.
El universo de inversión son 10 acciones de alta capitalización bursátil (AAPL, MSFT, GOOGL, AMZN, TSLA, META, NFLX, NVDA, JPM, JNJ), con datos históricos desde el 1 de enero de 2020. La red neuronal predice el precio para los próximos 30 días usando los precios de cierre ajustados — un precio que incorpora dividendos, splits y consolidaciones, permitiendo comparaciones históricas precisas.
Decisión clave antes de empezar
¿Esto es un problema de regresión o de clasificación?
Predecir el precio futuro de una acción es regresión — el target es un valor continuo (el precio). Algunos proyectos convierten esto en clasificación (¿sube o baja?), lo cual simplifica pero pierde información clave para Markowitz, que necesita magnitudes, no solo dirección.
Se eligió regresión: el precio predicho se convierte directamente en el rendimiento esperado que entra al optimizador de Markowitz.
Insight de experto
La pregunta más importante de todo el pipeline no es técnica — es conceptual: ¿los rendimientos esperados son correctos? Markowitz es tan bueno como los inputs que recibe. Si los rendimientos esperados son malos, el portafolio óptimo también lo es. Aquí es donde el LSTM aporta su valor diferencial sobre el enfoque clásico de medias históricas.
Arquitectura del pipeline
- Obtención de datos: descarga de precios ajustados de cierre con
yfinancepara 10 acciones desde 2020 a la fecha actual. - Predicción LSTM: entrenamiento de un modelo por acción con indicadores técnicos como features. Los últimos 30 días son el conjunto de prueba; el modelo predice los precios futuros que se usarán como rendimientos esperados.
- Selección CAPM: cálculo de betas con regresión lineal sobre el S&P 500, aplicación del CAPM y filtrado de acciones cuyo retorno esperado ajustado por riesgo supera el exceso de retorno de mercado.
- Optimización Markowitz: cálculo de la matriz de covarianza, trazado de la Frontera Eficiente y obtención de los pesos óptimos que minimizan la volatilidad del portafolio.
Stack tecnológico
- Python 3.10+ — lenguaje principal
- yfinance — descarga de datos históricos de Yahoo Finance
- TensorFlow / Keras — arquitectura LSTM con capas de Dropout y regularización L2
- scikit-learn — MinMaxScaler, StandardScaler, mean_squared_error
- scipy — optimización numérica con minimize (SLSQP) y regresión lineal (stats.linregress)
- pandas / numpy — manipulación de datos y álgebra matricial
- matplotlib — visualización de precios, curvas de pérdida y Frontera Eficiente
Setup del entornoCómo correr este proyecto
El proyecto puede ejecutarse en Google Colab o localmente. Se recomienda Colab por el acceso a GPU gratuita para acelerar el entrenamiento de los modelos LSTM, especialmente cuando se entrena un modelo por acción.
Error común — setup
Usar la versión incorrecta de TensorFlow. La API de Keras cambió en TF 2.x. Si ves errores como module 'keras' has no attribute 'Sequential', verifica que estás usando tensorflow >= 2.10 y que importas desde tensorflow.keras, no desde keras directamente.
Instalación de dependencias
pip install yfinance tensorflow scikit-learn scipy pandas numpy matplotlib
Descarga de datos con yfinance
import yfinance as yf
import pandas as pd
from datetime import datetime
symbols = ['AAPL','MSFT','GOOGL','AMZN','TSLA','META','NFLX','NVDA','JPM','JNJ']
fecha_inicial = '2020-01-01'
fecha_final = datetime.today().strftime('%Y-%m-%d')
# Descargar datos históricos
main_data = yf.download(symbols, start=fecha_inicial, end=fecha_final)
# Extraer precios de cierre ajustados
closing_prices = main_data['Adj Close']
print(f"Período: {fecha_inicial} → {fecha_final}")
print(f"Shape: {closing_prices.shape}")
print(closing_prices.tail(3))
import matplotlib.pyplot as plt
closing_prices.plot(figsize=(12, 6))
plt.title('Precios de Cierre Ajustados — 10 Acciones (2020 a la fecha)')
plt.xlabel('Fecha')
plt.ylabel('Precio de Cierre Ajustado (USD)')
plt.legend(loc='upper left', fontsize=8)
plt.tight_layout()
plt.show()
Checkpoint — Setup
- El DataFrame tiene 10 columnas (una por acción) sin NaN en el rango temporal
- Los precios son de cierre ajustado, no de cierre simple
- La visualización muestra tendencias coherentes con lo que conoces del mercado 2020–2024
Deep Learning para series temporalesCómo preparar datos para este problema
Las redes LSTM (Long Short-Term Memory) son especialmente adecuadas para series temporales financieras porque pueden capturar dependencias de largo plazo entre observaciones — algo que las redes neuronales estándar no logran. Se entrena un modelo LSTM independiente por cada acción del universo.
Decisión clave — features del modelo
¿Solo precio de cierre o con indicadores técnicos?
Solo precio: simple, menos overfitting, pero pierde señales de momentum y volatilidad.
Con indicadores: más información, mejor poder predictivo — pero hay que calcularlos correctamente y manejar los NaN que generan las ventanas móviles.
Se eligió: 4 indicadores técnicos (SMA 20d, Bollinger, RSI 14d, autocorrelación lag-1). La ganancia en predicción justifica la complejidad adicional.
División de datos e indicadores técnicos
Los datos de entrenamiento incluyen todos los registros excepto los últimos 30 días. Los últimos 30 días forman el conjunto de prueba. Sobre cada serie se calculan cuatro indicadores técnicos que enriquecen las features del modelo:
- SMA de 20 días — suaviza las fluctuaciones e identifica la tendencia general. Un valor alto o bajo puede indicar niveles de soporte o resistencia.
- Bandas de Bollinger (20 días, 2σ) — miden la volatilidad del mercado. Precios que tocan las bandas sugieren condiciones de sobrecompra o sobreventa.
- RSI de 14 días — oscila entre 0 y 100. RSI > 70 indica sobrecompra; RSI < 30 indica sobreventa. Útil para anticipar cambios de tendencia.
- Autocorrelación lag-1 — mide si el precio de hoy está correlacionado con el de ayer. Detecta patrones repetitivos y ciclos.
import pandas as pd
import numpy as np
from sklearn.preprocessing import MinMaxScaler
def calculate_technical_indicators(data, stock_name):
"""Calcula SMA, Bandas de Bollinger, RSI y autocorrelación."""
data = pd.DataFrame(data.copy())
# SMA de 20 días
data['SMA_20'] = data[stock_name].rolling(window=20).mean()
# Bandas de Bollinger (20 días, desviación estándar de 2)
rolling_mean = data[stock_name].rolling(window=20).mean()
rolling_std = data[stock_name].rolling(window=20).std()
data['BB_upper'] = rolling_mean + 2 * rolling_std
data['BB_lower'] = rolling_mean - 2 * rolling_std
# RSI de 14 días
delta = data[stock_name].diff()
gain = (delta.where(delta > 0, 0)).rolling(window=14).mean()
loss = (-delta.where(delta < 0, 0)).rolling(window=14).mean()
RS = gain / loss
data['RSI_14'] = 100 - (100 / (1 + RS))
# Autocorrelación lag-1 (valor escalar — se usa como referencia)
autocorr_value = data[stock_name].autocorr()
return data.dropna()
def preprocess_data(data):
"""Elimina NaNs y normaliza con MinMaxScaler al rango [0, 1]."""
data.dropna(inplace=True)
scaler = MinMaxScaler(feature_range=(0, 1))
scaled_data = scaler.fit_transform(data)
return scaled_data, scaler
Error común — preprocesamiento
Aplicar MinMaxScaler sobre todo el dataset antes de dividir en train/test. Esto genera data leakage: el scaler aprende los valores del conjunto de test, que en producción no existirían todavía. Siempre ajusta el scaler solo sobre los datos de entrenamiento y úsalo para transformar el test.
Arquitectura de la red LSTM
La red usa un modelo secuencial de Keras con tres capas LSTM apiladas y capas de Dropout para reducir el sobreajuste. La capa densa de salida tiene regularización L2 para penalizar pesos grandes.
Decisión clave — arquitectura
¿Cuántas capas LSTM y cuántas unidades?
Una sola capa: simple, rápida, pero puede no capturar patrones complejos.
Tres capas: más capacidad de representación — con el riesgo de sobreajustar si no se regulariza.
Se eligió: 3 capas con 50 unidades cada una + Dropout(0.2) entre capas. El Dropout actúa como regularizador que obliga a la red a no depender de neuronas específicas, mejorando la generalización.
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import LSTM, Dense, Dropout
from tensorflow.keras.regularizers import L2
def build_lstm_model(input_shape):
model = Sequential([
# Primera capa LSTM — devuelve secuencias para la siguiente capa
LSTM(units=50, return_sequences=True, input_shape=input_shape),
# Segunda capa LSTM con Dropout del 20%
LSTM(units=50, return_sequences=True),
Dropout(0.2),
# Tercera capa LSTM — solo devuelve la última salida
LSTM(units=50, return_sequences=False),
Dropout(0.1),
# Capa densa de salida con regularización L2
Dense(units=1, kernel_regularizer=L2(0.01))
])
model.compile(optimizer='adam', loss='mean_squared_error')
return model
Checkpoint — LSTM
- El scaler se ajustó solo sobre datos de entrenamiento
- La validation loss desciende junto con la training loss (sin divergencia severa)
- Los 30 días de test no se usaron en ningún momento durante el entrenamiento
- Las predicciones tienen el mismo orden de magnitud que los precios reales tras la inversa del scaler
Filtro de riesgo sistemáticoSelección de activos con CAPM
El CAPM (Capital Asset Pricing Model) es un modelo financiero que calcula el rendimiento esperado de un activo en función de su exposición al riesgo de mercado (beta). Se usa como filtro para seleccionar las acciones que pasarán al optimizador de Markowitz.
Decisión clave — por qué CAPM antes de Markowitz
¿Incluir todos los activos en Markowitz o filtrar antes?
Todos los activos: Markowitz puede asignar peso cero a los malos — pero es más inestable y sensible a errores de estimación cuando hay muchas acciones.
Filtrar con CAPM: reduce el universo a activos que tienen premio por riesgo justificado, haciendo que el optimizador trabaje con un subconjunto más robusto.
Se eligió: filtrar primero. Las acciones cuyo costo de equity supera el exceso de retorno de mercado se excluyen antes de la optimización.
Error común — CAPM
Usar la tasa libre de riesgo nominal en lugar de real. Si usas la tasa de bonos del Tesoro a 10 años sin ajustar por inflación, el CAPM puede excluir activos atractivos porque el umbral de selección está inflado. En períodos de alta inflación (como 2022–2023), este error sesga el filtro de forma significativa.
Checkpoint — CAPM
- El beta de cada acción se calculó con regresión sobre el S&P 500, no estimado de memoria
- La tasa libre de riesgo refleja el periodo de análisis (no un valor genérico fijo)
- Al menos 3 acciones pasan el filtro — si ninguna pasa, revisar el umbral o el periodo
Teoría Moderna de CarterasOptimización con Markowitz
La Teoría Moderna de Carteras de Markowitz busca el portafolio que minimiza la volatilidad para un nivel de retorno dado — o equivalentemente, maximiza el retorno para un nivel de riesgo dado. El resultado es la Frontera Eficiente: el conjunto de portafolios óptimos.
Decisión clave — función objetivo
¿Minimizar volatilidad o maximizar ratio de Sharpe?
Mínima volatilidad: más conservador, más estable numéricamente, fácil de interpretar. El portafolio resultante es el punto más a la izquierda de la Frontera Eficiente.
Máximo Sharpe: busca el mejor balance retorno/riesgo — pero es más sensible a los inputs de rendimientos esperados.
Se eligió: minimizar volatilidad como objetivo principal. Es más robusto cuando los rendimientos esperados tienen incertidumbre (que siempre la tienen).
Insight de experto
Markowitz no te dice qué acciones son buenas — te dice cómo combinarlas. Una acción con alta volatilidad individual puede ser valiosa en el portafolio si tiene baja correlación con las demás. La diversificación matemática es el núcleo del modelo: no es elegir los mejores activos, sino los que mejor se complementan entre sí.
Checkpoint — Markowitz
- Los pesos óptimos suman 1 (restricción de presupuesto verificada)
- Ningún peso es negativo (si permites short-selling, esto cambia — documéntalo)
- La Frontera Eficiente tiene forma de "bala" — si es recta o cóncava, hay un error en la matriz de covarianza
- El portafolio de mínima volatilidad está en el extremo izquierdo de la frontera
Interpretación de resultadosEvaluación
Decisión clave — qué métrica reportar
¿RMSE, MAE o ratio de Sharpe?
RMSE del LSTM: mide el error de predicción de precios — útil para validar el modelo, pero no conecta directamente con la calidad del portafolio.
Ratio de Sharpe out-of-sample: mide si el portafolio resultante realmente superó al benchmark en un período de validación — esto es lo que importa al cliente.
Reporta ambos: RMSE para el componente técnico, Sharpe para el componente financiero. Son para audiencias distintas.
Error común — evaluación
Evaluar el portafolio solo en datos de entrenamiento. El backtesting debe hacerse en un período que el modelo no vio. Si optimizas con datos 2020–2024 y evalúas con los mismos datos, los resultados son ilusoriamente buenos. Separa al menos 6 meses como período de validación out-of-sample.
Resultados visualesFrontera Eficiente y distribución del portafolio
La visualización final muestra dos gráficos: la Frontera Eficiente con el portafolio óptimo marcado y la distribución de pesos en un gráfico de torta.
import matplotlib.pyplot as plt
fig, axes = plt.subplots(1, 2, figsize=(16, 6))
# Frontera Eficiente
axes[0].plot([p[1] for p in efficient_frontier], [p[0] for p in efficient_frontier],
'b-', linewidth=2, label='Frontera Eficiente')
axes[0].scatter(optimal_portfolio['Volatilidad'], optimal_portfolio['Retorno'],
color='red', s=200, zorder=5, label='Portafolio Óptimo')
axes[0].set_xlabel('Volatilidad (Riesgo)')
axes[0].set_ylabel('Retorno Esperado')
axes[0].set_title('Frontera Eficiente de Markowitz')
axes[0].legend()
# Gráfico de torta: distribución del portafolio
axes[1].pie(w_optimal, labels=selected_stocks, autopct='%1.1f%%',
startangle=90, pctdistance=0.85)
axes[1].set_title('Distribución del Portafolio')
plt.tight_layout()
plt.show()
Checkpoint — Visualización
- El portafolio óptimo (punto rojo) está sobre la Frontera Eficiente, no por debajo
- Los pesos del gráfico de torta suman 100%
- La distribución tiene al menos 2–3 activos con peso significativo (no concentración total en uno)
Conclusiones y trabajo futuroHallazgos
Hallazgos principales
LSTM captura patrones de corto plazo con precisión razonable
La pérdida de validación desciende de 0.154 a 0.047 en 50 épocas sin sobreajuste significativo. La diferencia entre training loss y validation loss se mantiene pequeña, lo que indica que la arquitectura de tres capas LSTM con Dropout tiene capacidad de generalización adecuada para horizontes de 30 días. Los indicadores técnicos (especialmente RSI y Bandas de Bollinger) enriquecen el poder predictivo respecto a usar solo el precio de cierre.
El CAPM elimina activos con riesgo sistemático no compensado
El filtro CAPM reduce el universo de 10 a 4–7 acciones según las condiciones de mercado. Las acciones con beta muy alto (como TSLA o NVDA en períodos de alta volatilidad) pueden ser excluidas si su exceso de retorno predicho no justifica el riesgo que añaden. Esto protege la cartera de concentrar peso en activos altamente volátiles cuya prima de riesgo no está siendo compensada.
Markowitz concentra el peso en activos de baja covarianza
El optimizador de mínima volatilidad tiende a asignar pesos altos a pares de acciones con baja correlación entre sí — por ejemplo, una acción tecnológica (MSFT o GOOGL) junto con una financiera o defensiva (JPM o JNJ). Esta diversificación matemática es la ventaja central de Markowitz: no se trata de elegir los mejores activos individualmente, sino los que combinados reducen el riesgo global.
Usar predicciones LSTM como rendimientos esperados mejora la relevancia del modelo
El enfoque clásico de Markowitz usa medias históricas de rendimientos como estimadores de retornos esperados futuros — lo cual asume que el pasado predice el futuro directamente. Sustituir esas medias históricas por las predicciones del modelo LSTM introduce una estimación dinámica que reacciona a condiciones recientes del mercado, lo que puede generar portafolios más adaptados al contexto actual.
Limitaciones y trabajo futuro
- El modelo LSTM predice solo precio, no distribución de probabilidad — una extensión con predicción de intervalo de confianza (Bayesian LSTM o Monte Carlo Dropout) cuantificaría la incertidumbre de los rendimientos esperados.
- El horizonte de predicción es fijo en 30 días. Una arquitectura multi-step (predicción de múltiples pasos futuros de forma directa) permitiría ajustar el horizonte del portafolio de manera más flexible.
- No se incorpora rebalanceo periódico del portafolio. En producción, el pipeline debería ejecutarse periódicamente (mensual o trimestralmente) para actualizar predicciones LSTM y pesos óptimos.
- El universo se limita a 10 acciones. Ampliar a un índice completo (S&P 500) requeriría técnicas de clustering para agrupar acciones y entrenar modelos por cluster, reduciendo el costo computacional.
- SHAP aplicado sobre el modelo LSTM permitiría explicar qué indicadores técnicos contribuyen más a cada predicción individual, mejorando la interpretabilidad para el analista.
Lo que realmente aprendiste
Qué te llevas: la diferencia entre estimar rendimientos esperados con medias históricas vs. con un modelo predictivo — y por qué eso cambia la calidad del portafolio.
Qué repetirías: el filtro CAPM antes de Markowitz. Reduce inestabilidad numérica y hace más robusto el resultado.
Qué evitarías: optimizar con el mismo período de datos con el que evaluarás. El backtesting fuera de muestra es obligatorio antes de presentar cualquier resultado.