σμimplied vol · KKmax(S−K,0)udβρᵢⱼfat tailsPnL · drawdownyield · TdSₜ = μSₜ dt + σSₜ dWₜC = S·N(d₁) − K·e⁻ʳᵀ·N(d₂)σₚ² = wᵀΣwd₁ = [ln(S/K) + (r + σ²⁄2)T] ⁄ σ√T∂V/∂t + ½σ²S²·∂²V/∂S² + rS·∂V/∂S − rV = 0Sharpe = (Rₚ − R_f) ⁄ σₚVaR₉₉ = μ − 2.33σρ = Cov(X,Y) ⁄ σₓσᵧE[Rₚ] = Σ wᵢ·E[Rᵢ]φ(x) = e^(−x²/2) ⁄ √2πE[Rᵢ] = R_f + βᵢ(E[Rₘ] − R_f)dXₜ = θ(μ − Xₜ)dt + σ dWₜσ²ₜ = ω + α·ε²ₜ₋₁ + β·σ²ₜ₋₁C − P = S − K·e⁻ʳᵀΔ = ∂V/∂S Γ = ∂²V/∂S²p = (e^(rΔt) − d) ⁄ (u − d)rₜ = ln(Pₜ ⁄ Pₜ₋₁)E[Xₜ₊₁ | ℱₜ] = Xₜf* = (bp − q) ⁄ bz = (x − μ) ⁄ σP(N=k) = λᵏe^(−λ) ⁄ k!Θ = −∂V/∂Tν = S·φ(d₁)·√Tmin wᵀΣw s.t. wᵀμ = μ*∫Σ∂∞λ
σμimplied vol · KKmax(S−K,0)udβρᵢⱼfat tailsPnL · drawdownyield · TdSₜ = μSₜ dt + σSₜ dWₜC = S·N(d₁) − K·e⁻ʳᵀ·N(d₂)σₚ² = wᵀΣwd₁ = [ln(S/K) + (r + σ²⁄2)T] ⁄ σ√T∂V/∂t + ½σ²S²·∂²V/∂S² + rS·∂V/∂S − rV = 0Sharpe = (Rₚ − R_f) ⁄ σₚVaR₉₉ = μ − 2.33σρ = Cov(X,Y) ⁄ σₓσᵧE[Rₚ] = Σ wᵢ·E[Rᵢ]φ(x) = e^(−x²/2) ⁄ √2πE[Rᵢ] = R_f + βᵢ(E[Rₘ] − R_f)dXₜ = θ(μ − Xₜ)dt + σ dWₜσ²ₜ = ω + α·ε²ₜ₋₁ + β·σ²ₜ₋₁C − P = S − K·e⁻ʳᵀΔ = ∂V/∂S Γ = ∂²V/∂S²p = (e^(rΔt) − d) ⁄ (u − d)rₜ = ln(Pₜ ⁄ Pₜ₋₁)E[Xₜ₊₁ | ℱₜ] = Xₜf* = (bp − q) ⁄ bz = (x − μ) ⁄ σP(N=k) = λᵏe^(−λ) ⁄ k!Θ = −∂V/∂Tν = S·φ(d₁)·√Tmin wᵀΣw s.t. wᵀμ = μ*∫Σ∂∞λ
uantCraft AcademyRutas
Ctrl K
AO
+1,2 k estudiantes
Iniciar sesiónUnirse ↗
Rutas
AO
+1,2 k estudiantes
Iniciar sesión
  • English
  • Français
  • Español
  • العربية
  • Português
  • Deutsch
  • 日本語
  • 中文
Ruta de Python›Módulo 5 · Optimización de carteras›Tema 5.4·~55 minIntermedio

Optimización media-varianza

Posición en la rutaMódulo 5 de 11 · Tema 4 de 5
5.4 Optimización media-varianza
—Puntuación del lab— / 100XP total—Racha—

Rendimiento y riesgo en un solo número

La mínima varianza ignora los rendimientos. La optimización media-varianza (Markowitz) usa ambos: un inversor quiere un rendimiento esperado w @ mu alto y una varianza w @ cov @ w baja. Una forma simple de equilibrarlos es la utilidad w @ mu - 0.5 * lambda * w @ cov @ w, donde lambda es la aversión al riesgo: cuanto mayor, más cuesta una unidad de varianza. Dos inversores con las mismas expectativas pero distinto lambda eligen carteras distintas, y la mejor cartera de cada uno es el máximo de una función suave y cóncava.

import numpy as np

mu = np.array([0.08, 0.05, 0.10])                         # expected annual returns
cov = np.array([[0.0400, 0.0060, 0.0100],
                [0.0060, 0.0100, 0.0020],
                [0.0100, 0.0020, 0.0225]])

def utility(weights, mu, cov, risk_aversion):
    return weights @ mu - 0.5 * risk_aversion * weights @ cov @ weights

equal = np.full(3, 1 / 3)
print(round(float(equal @ mu), 4), round(float(equal @ cov @ equal), 4))      # 0.0767 0.0121
print(round(float(utility(equal, mu, cov, 4.0)), 4))                          # 0.0526

La mejor posición para una aversión dada

Sin restricción presupuestaria (el resto del dinero rinde el tipo libre de riesgo, tomado aquí como cero en exceso), la mejor posición es donde el gradiente mu - lambda * cov @ w se anula: w = cov^-1 * mu / lambda. En código, np.linalg.solve(cov, mu) / lambda. Escala como dicta el sentido común: el doble de aversión, la mitad de posición. La suma de los pesos es la que haga falta; el resto, 1 - w.sum(), es efectivo.

# The best position for several risk aversions: more aversion, smaller bets
import numpy as np

mu = np.array([0.08, 0.05, 0.10])
cov = np.array([[0.0400, 0.0060, 0.0100],
                [0.0060, 0.0100, 0.0020],
                [0.0100, 0.0020, 0.0225]])

for aversion in (5.0, 10.0, 20.0):
    w = np.linalg.solve(cov, mu) / aversion
    print(aversion, np.round(w, 3), "invested", round(float(w.sum()), 3))

La cartera tangente

Con un activo libre de riesgo, todos los inversores tienen la misma mezcla de riesgo, solo que en cantidades distintas. Esa mezcla es la de mayor ratio de Sharpe (w @ mu - rf) / sqrt(w @ cov @ w), la cartera tangente: w proporcional a cov^-1 * (mu - rf), reescalada para sumar 1. El inversor prudente pone solo parte del dinero ahí y el resto en efectivo; el audaz se endeuda para tener más. Decidir cuánto riesgo asumir y decidir qué activos arriesgados mantener son dos problemas separados.

# The tangency portfolio: no other mix of the three assets has a higher Sharpe ratio
import numpy as np

mu = np.array([0.08, 0.05, 0.10])
cov = np.array([[0.0400, 0.0060, 0.0100],
                [0.0060, 0.0100, 0.0020],
                [0.0100, 0.0020, 0.0225]])
rf = 0.02

x = np.linalg.solve(cov, mu - rf)
best = x / x.sum()

def sharpe(w):
    return (w @ mu - rf) / np.sqrt(w @ cov @ w)

rng = np.random.default_rng(1)
trials = rng.normal(size=(2000, 3))
trials = trials / trials.sum(axis=1, keepdims=True)
print(np.round(best, 4), round(float(sharpe(best)), 4))
print(round(max(float(sharpe(w)) for w in trials), 4))      # the best of 2000 random mixes comes close, never above

Alcanzar un rendimiento objetivo

Otra forma de plantear la pregunta: entre todas las carteras que suman 1 y tienen un rendimiento esperado dado, ¿cuál tiene menor varianza? Con dos restricciones, la solución es una combinación de los dos vectores resueltos s1 = cov^-1 * 1 y sm = cov^-1 * mu, con coeficientes de cuatro números a, b, c, d que resumen el mercado. Cada rendimiento objetivo da una cartera; dibujar el riesgo de todas es la frontera eficiente del próximo tema.

# Minimum-variance portfolios for three target returns
import numpy as np

mu = np.array([0.08, 0.05, 0.10])
cov = np.array([[0.0400, 0.0060, 0.0100],
                [0.0060, 0.0100, 0.0020],
                [0.0100, 0.0020, 0.0225]])
ones = np.ones(3)
s1, sm = np.linalg.solve(cov, ones), np.linalg.solve(cov, mu)
a, b, c = ones @ s1, ones @ sm, mu @ sm
d = a * c - b ** 2

for target in (0.06, 0.08, 0.09):
    w = ((c - b * target) * s1 + (a * target - b) * sm) / d
    print(target, np.round(w, 3), round(float(w @ mu), 4), round(float(np.sqrt(w @ cov @ w)), 4))
El optimizador maximiza tus erroresLos pesos media-varianza son muy sensibles a mu: un pequeño cambio en un rendimiento esperado puede mover la cartera de un activo a otro. El optimizador confía por completo en los datos, así que cualquier rendimiento sobreestimado se convierte en una posición grande. En la práctica los rendimientos se encogen, los pesos se acotan y los datos proceden de modelos o de opiniones, no de promedios pasados en bruto. La media-varianza es una buena herramienta para pensar y una peligrosa si se usa a ciegas.
  • La utilidad es w @ mu - 0.5 * lambda * w @ cov @ w; la mejor posición sin presupuesto es solve(cov, mu) / lambda, y duplicar lambda la reduce a la mitad.
  • Con un activo libre de riesgo, la mejor mezcla arriesgada es la cartera tangente solve(cov, mu - rf), reescalada para sumar 1: el mayor ratio de Sharpe.
  • Para un rendimiento objetivo, los pesos de mínima varianza combinan solve(cov, ones) y solve(cov, mu); el resultado vale lo que valga el mu estimado.

A continuación: la frontera eficiente, la curva del mejor riesgo para cada nivel de rendimiento.

Lab: qué se califica

  • Puntuar una cartera con utility25 pts
  • Hallar la mejor posición con utility_weights25 pts
  • Maximizar el ratio de Sharpe con tangency_weights25 pts
  • Alcanzar un objetivo de rendimiento con target_return_weights25 pts
Explorador · lab-85-1
▾ lab-85-1
…
bash · en el navegador
learner@quantcraft:~/lab-85-1$
Python aún no cargadoPythonLn 1, Col 1Spaces: 4UTF-8

¿Restablecer este tema?

Todo lo que hay en lab-85-1 vuelve a los archivos iniciales y se limpia el terminal. Tu solución actual se perderá: no se puede deshacer.

Se restablecerá
  • main.py → código inicial
  • Historial del terminal y salida de pruebas
  • Cambios sin enviar
Se conservará
  • Mejor puntuación del lab
  • Tu XP y tu racha
  • Progreso de la lección y notas

Inicia sesión para ejecutar tu código.

Todas las clases se pueden leer gratis. Crea una cuenta o inicia sesión para ejecutar código, lanzar las pruebas y enviar este lab.

  • IDE en el navegador con terminal bash — sin instalar nada
  • Corrección instantánea, XP y tu lugar en la ruta
Crear una cuentaIniciar sesión
O
Continuar con GitHub