Portfolio — Optimización Cuantitativa de Portafolios
Este skill implementa 3 enfoques de optimización de portafolios desde el material del curso
(notebook Clase_08_teoria_2025_portafolio.ipynb y PDF Portafolios 2025 Ucema.pdf):
- Markowitz / Media-Varianza — Optimización convexa vía
scipy.optimize- simulación Monte Carlo + frontera eficiente + CML.
- Black-Litterman — Combinación bayesiana de retornos de equilibrio de mercado (CAPM inverso) con views del inversor, incluyendo matriz de incertidumbre Ω (método Idzorek).
- HRP / HERC / NCO — Construcción jerárquica de portafolios mediante clustering (single/complete/average/ward), risk parity y NCO con restricciones.
Todos los scripts usan solo numpy, pandas y scipy. Sin dependencias pesadas.
Este skill es autónomo: funciona sin skills/backtesting.
Para ratios de performance post-optimización (Sharpe, Sortino, VaR, drawdowns, etc.)
consultar el skill hermana:
skills/backtesting.
Part of the Gauss314 Skills Repository.
File Map
skills/portfolio/
├── SKILL.md ← Este archivo
├── references/
│ ├── PORTFOLIO_THEORY.md ← MPT, Markowitz, frontera eficiente (ES)
│ ├── BLACK_LITTERMAN.md ← BL: prior, views, posterior, omega (ES)
│ ├── HIERARCHICAL.md ← HRP, HERC, NCO, clustering (ES)
│ └── RISK_MEASURES.md ← VaR, CVaR, MAD, MSV, DR, MDD (ES)
├── assets/
│ ├── sample_prices.csv ← Precios multi-activo para ejemplos
│ ├── sample_returns.csv ← Retornos multi-activo
│ ├── sample_mcaps.json ← Market caps para Black-Litterman
│ └── defaults.json ← Parámetros default
├── scripts/
│ ├── __init__.py
│ ├── portfolio.py ← Core: Markowitz, Sharpe, Monte Carlo, frontera
│ ├── black_litterman.py ← BL: prior, posterior, omega, views
│ ├── hierarchical.py ← HRP/HERC/NCO: clustering, risk parity, constraints
│ ├── risk_measures.py ← VaR, CVaR, MAD, MSV, MDD, DR
│ ├── covariance.py ← Covarianza: hist, ledoit-wolf, oas, ewma
│ └── cli.py ← CLI unificada (12 modos)
└── tests/
├── __init__.py
└── test_portfolio.py ← Tests + validación contra notebook
Qué hace cada script
| Script | Rol | Funciones clave |
|---|---|---|
portfolio.py |
Core de optimización Markowitz | max_sharpe_optim, min_variance_optim, random_portfolios, efficient_frontier, cml_portfolio, asset_stats |
black_litterman.py |
Black-Litterman completo | market_implied_risk_aversion, market_implied_prior_returns, bl_posterior_returns, omega_idzorek |
hierarchical.py |
HRP / HERC / NCO | hrp_portfolio, herc_portfolio, nco_portfolio, nco_with_constraints, hrp_constraints |
risk_measures.py |
Medidas de riesgo | var_historic, cvar, max_drawdown, cdar, diversification_ratio, risk_contribution |
covariance.py |
Estimación de covarianza | cov_hist, cov_ledoit_wolf, cov_oas, cov_ewma |
Quick Start
Markowitz (scipy.optimize)
# Max Sharpe con 3 activos
py scripts/cli.py markowitz --assets assets/sample_returns.csv
# Con tasa libre de riesgo personalizada
py scripts/cli.py markowitz --assets assets/sample_returns.csv --rf 0.05
# Estadísticas individuales
py scripts/cli.py stats --assets assets/sample_returns.csv
Monte Carlo
# Simular 10.000 carteras aleatorias
py scripts/cli.py montecarlo --assets assets/sample_returns.csv
# Guardar frontera a CSV
py scripts/cli.py montecarlo --assets assets/sample_returns.csv --save frontier.csv
Frontera Eficiente
py scripts/cli.py frontier --assets assets/sample_returns.csv --n 50
CML — Leverage y Deleverage
El portafolio tangente (máximo Sharpe) se combina con el activo libre de riesgo para obtener cualquier punto sobre la Capital Market Line (CML), manteniendo el mismo Sharpe ratio.
# Portafolio tangente puro (w=1)
py scripts/cli.py cml --assets assets/sample_returns.csv --weight 1.0
# Deleverage: 60% en tangencia, 40% en Rf (menos riesgo, mismo Sharpe)
py scripts/cli.py cml --assets assets/sample_returns.csv --weight 0.6
# Leverage: pide prestado 50% a Rf, invierte 150% en tangencia (más riesgo, mismo Sharpe)
py scripts/cli.py cml --assets assets/sample_returns.csv --weight 1.5
Black-Litterman
# Prior: retornos implícitos de mercado (CAPM inverso)
py scripts/cli.py bl-prior --assets assets/sample_returns.csv --market-prices assets/sample_prices.csv --mcaps assets/sample_mcaps.json
# BL completo con views + optimización
py scripts/cli.py bl --assets assets/sample_returns.csv --market-prices assets/sample_prices.csv --mcaps assets/sample_mcaps.json --views '{"BMA": 0.25, "LOMA": 0.4, "MELI": -0.1}' --confidences "0.3,0.5,0.8" --optimize
HRP / HERC / NCO
# Hierarchical Risk Parity
py scripts/cli.py hrp --assets assets/sample_returns.csv
# Nested Clustered Optimization
py scripts/cli.py nco --assets assets/sample_returns.csv --clusters 3
# NCO con restricciones
py scripts/cli.py nco-con --assets assets/sample_returns.csv --constraints assets/sample_constraints.csv --classes assets/sample_classes.csv
Riesgo
# Todas las medidas de riesgo
py scripts/cli.py risk --prices assets/sample_prices.csv
# Medida específica
py scripts/cli.py risk --prices assets/sample_prices.csv --measure var
Usar como Librería
from scripts.portfolio import *
from scripts.black_litterman import *
from scripts.hierarchical import *
import numpy as np
# --- Markowitz ---
rets = pd.read_csv('assets/sample_returns.csv', index_col=0)
result = max_sharpe_optim(rets, rf=0.045)
print(result['weights'], result['sharpe']) # pesos óptimos, Sharpe
# --- CML: leverage/deleverage ---
# 60% en tangencia, 40% en Rf (deleverage)
cml = cml_portfolio(rets, rf=0.045, weight_tangency=0.6)
print(cml['ret'], cml['vol'], cml['sharpe']) # mismo Sharpe que el tangente
# Leverage: 150% en tangencia (pide prestado 50% a Rf)
cml2 = cml_portfolio(rets, rf=0.045, weight_tangency=1.5)
print(cml2['ret'], cml2['vol'], cml2['sharpe']) # mismo Sharpe
# --- Monte Carlo ---
port_df = random_portfolios(rets, n_portfolios=10000, rf=0.045)
best = port_df.loc[port_df['sharpe'].idxmax()]
print(best['weights']) # mejor combinación Monte Carlo
# --- Black-Litterman ---
import json
with open('assets/sample_mcaps.json') as f:
mcaps = json.load(f)
spy = pd.read_csv('assets/sample_prices.csv')['SPY'].pct_change().dropna()
bl_result = bl_pipeline(rets, spy.values, mcaps,
view_dict={'BMA': 0.25, 'LOMA': 0.4},
view_confidences=[0.3, 0.5], rf=0.045)
print(bl_result['posterior']) # retornos a posteriori
# --- HRP ---
hrp_result = hrp_portfolio(rets, linkage_method='ward')
print(hrp_result['weights']) # pesos HRP
Dependencias
| Librería | Requerida | Uso |
|---|---|---|
numpy |
✅ | Cómputo vectorizado, álgebra lineal |
pandas |
✅ | CSV I/O, DataFrames |
scipy |
✅ | optimize (Markowitz), cluster.hierarchy (HRP/NCO), stats |
No requiere Riskfolio-Lib, PyPortfolioOpt, sklearn, cvxpy ni arch.
Para visualización (dendrogramas, frontera eficiente) se puede usar matplotlib
opcionalmente. Ejemplos de plots están en el notebook de referencia.
Referencias Teóricas
- Markowitz (1952): "Portfolio Selection", Journal of Finance.
- Black & Litterman (1992): "Global Portfolio Optimization", Financial Analysts Journal.
- Idzorek (2005): "A Step-by-Step Guide to the Black-Litterman Model".
- Lopez de Prado (2016): "Building Diversified Portfolios that Outperform Out of Sample" (HRP).
- De Prado (2019): "Nested Clustered Optimization", SSRN 3469961.
- Pfitzinger & Katzke (2019): "NCO with Constraints", SSRN 4409173.
- Meucci (2006): "Beyond Black-Litterman: Views on Non-Normal Markets", SSRN 1213325.
- Avramov (2004): "Bayesian Variable Selection in Portfolio Analysis", SSRN 3326617.
Para profundizar en ratios de performance (30+ métricas: Sharpe, Sortino,
VaR, cVaR, Kelly, Rachev, Profit Factor, etc.) y backtesting de estrategias:
skills/backtesting.
Notebook de referencia
El contenido teórico y ejemplos numéricos de este skill están basados en:
temp/Clase_08_teoria_2025_portafolio.ipynb— Implementaciones en Python de Markowitz, Monte Carlo, NCO (Riskfolio-Lib), Black-Litterman (PyPortfolioOpt).temp/Portafolios 2025 Ucema.pdf— Marco teórico: MPT, CAPM, Fama-French, clustering, NCO, Black-Litterman.
Las implementaciones flat numpy en scripts/ replican los resultados de esos
notebooks sin depender de las librerías mencionadas.