σμ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.5·~55 minIntermedio

La frontera eficiente

Posición en la rutaMódulo 5 de 11 · Tema 5 de 5
5.5 La frontera eficiente
—Puntuación del lab— / 100XP total—Racha—

El riesgo para cada nivel de rendimiento

El tema anterior encontraba, para un rendimiento objetivo, los pesos de menor varianza. Hazlo para cada objetivo y dibuja la volatilidad en el eje horizontal y el rendimiento en el vertical: los puntos forman una curva, la frontera eficiente. Toda cartera de los activos está sobre ella o dentro, a la derecha. Una cartera dentro es ineficiente, pues otra da el mismo rendimiento con menos riesgo, o más rendimiento con el mismo riesgo. La idea de Markowitz: los inversores solo deben mirar la curva.

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]])

def target_return_weights(mu, cov, target):
    ones = np.ones(len(mu))
    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
    return ((c - b * target) * s1 + (a * target - b) * sm) / d

for target in (0.05, 0.06, 0.07, 0.08, 0.09, 0.10):
    w = target_return_weights(mu, cov, target)
    print(target, round(float(np.sqrt(w @ cov @ w)), 4))

Su forma y su punta

La frontera es una parábola en (rendimiento, varianza), y una hipérbola si se dibuja la volatilidad. La varianza para un rendimiento objetivo t es (a * t**2 - 2 * b * t + c) / (a * c - b**2), con los mismos números a, b, c de antes. Su punto más bajo es la cartera de mínima varianza global, con rendimiento b / a: la punta de la curva, que es la cartera de mínima varianza de hace dos temas. Solo la parte de la punta hacia arriba es la frontera eficiente; la rama inferior es su imagen especular, con menos rendimiento para el mismo riesgo.

# The tip of the curve: the minimum-variance portfolio and its return
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)
x = np.linalg.solve(cov, ones)
w = x / x.sum()
print(np.round(w, 4), round(float(w @ mu), 4), round(float(np.sqrt(w @ cov @ w)), 4))     # the tip: return, volatility

def closed_form_vol(target):
    s1, sm = x, np.linalg.solve(cov, mu)
    a, b, c = ones @ s1, ones @ sm, mu @ sm
    return float(np.sqrt((a * target ** 2 - 2 * b * target + c) / (a * c - b ** 2)))

for target in (0.05, 0.0642, 0.07):
    print(target, round(closed_form_vol(target), 4))

Puntos eficientes e ineficientes

El mismo riesgo aparece con dos rendimientos en la curva, uno en cada rama, y solo el mayor merece mantenerse. Por tanto un objetivo es eficiente exactamente cuando es al menos el rendimiento de mínima varianza. La prueba es una sola comparación, pero decide qué objetivos pertenecen a un gráfico de opciones. Buscar el mejor equilibrio en la frontera pasa a ser elegir un punto, y una respuesta habitual es el que rinde más por unidad de riesgo por encima del tipo libre de riesgo.

# The point with the highest Sharpe ratio among candidate targets
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
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

targets = np.linspace(0.05, 0.10, 11)
vols = np.sqrt((a * targets ** 2 - 2 * b * targets + c) / d)
sharpe = (targets - rf) / vols
for t, v, s in zip(targets, vols, sharpe):
    print(round(float(t), 3), round(float(v), 4), round(float(s), 4))
print("best target:", round(float(targets[np.argmax(sharpe)]), 3))
# Efficient or not: compare a target with the return of the minimum-variance portfolio
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]])
x = np.linalg.solve(cov, np.ones(3))
tip = float((x / x.sum()) @ mu)
for target in (0.05, 0.06, 0.07, 0.08):
    print(target, "efficient" if target >= tip else "inefficient")
Una frontera estimada es una banda difusaLa curva se dibuja con mu y cov, ambos estimados, así que ella misma es una estimación, y halagüeña: el optimizador eligió, entre todas las mezclas, las que casualmente tienen rendimientos estimados altos y riesgos bajos. Los resultados reales caen a la derecha y por debajo de la línea que se ve. Remuestrear los datos, o comparar la frontera de la primera mitad de una muestra con su desempeño en la segunda, muestra cuánto de la curva fue suerte. Trata la frontera como un mapa de equilibrios, no como una promesa.
  • La frontera eficiente da, para cada rendimiento objetivo, la menor volatilidad: sqrt(w @ cov @ w) con w = target_return_weights(...); las carteras dentro son ineficientes.
  • Su punta es la cartera de mínima varianza global, con rendimiento b / a; solo los objetivos iguales o superiores a ese rendimiento son eficientes.
  • Elija un punto con un criterio como el mayor ratio de Sharpe (target - rf) / volatility; recuerde que la curva sale de estimaciones y es optimista.

A continuación: un backtester vectorizado, probar una regla de trading con precios históricos.

Lab: qué se califica

  • Trazar la frontera con frontier_vols25 pts
  • Hallar la punta de la curva con min_variance_return25 pts
  • Distinguir la mitad buena de la mala con is_efficient25 pts
  • Elegir el mejor punto con max_sharpe_target25 pts
Explorador · lab-86-1
▾ lab-86-1
…
bash · en el navegador
learner@quantcraft:~/lab-86-1$
Python aún no cargadoPythonLn 1, Col 1Spaces: 4UTF-8

¿Restablecer este tema?

Todo lo que hay en lab-86-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