σμ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.2·~45 minIntermedio

Rendimiento y varianza de una cartera

Posición en la rutaMódulo 5 de 11 · Tema 2 de 5
5.2 Rendimiento y varianza de una cartera
—Puntuación del lab— / 100XP total—Racha—

Una cartera es un vector de pesos

Una cartera se describe con un peso por activo: la fracción de su valor invertida allí. Los pesos suman 1, y un peso negativo es una posición corta que financia las demás. Su rendimiento en un periodo es la media ponderada de los rendimientos de los activos, así que el rendimiento esperado es un producto escalar w @ mu. El rendimiento es la mitad fácil: es lineal, sin interacción entre activos. El riesgo no.

import numpy as np

weights = np.array([0.6, 0.4])             # 60% in asset A, 40% in asset B
mu = np.array([0.08, 0.05])                # expected annual returns

print(weights.sum())                       # 1.0
print(weights @ mu)                        # 0.068   = 0.6 * 0.08 + 0.4 * 0.05

La varianza es una forma cuadrática

Para dos activos, la varianza de w1 * A + w2 * B es w1**2 * var(A) + w2**2 * var(B) + 2 * w1 * w2 * cov(A, B): la varianza propia de cada activo más un término cruzado. Con n activos hay n * n términos, w_i * w_j * cov[i, j], y la suma entera es la forma cuadrática w @ cov @ w, una línea en NumPy. Los términos cruzados son lo esencial: cuando dos activos no están perfectamente correlacionados se compensan en parte, y la cartera es menos arriesgada que la media ponderada de los riesgos.

# The variance two ways: the explicit formula and the matrix form
import numpy as np

weights = np.array([0.6, 0.4])
cov = np.array([[0.04, 0.006], [0.006, 0.01]])      # variances 0.04 and 0.01 (volatilities 20% and 10%), covariance 0.006

explicit = weights[0] ** 2 * cov[0, 0] + weights[1] ** 2 * cov[1, 1] + 2 * weights[0] * weights[1] * cov[0, 1]
matrix_form = weights @ cov @ weights
print(round(explicit, 6), round(matrix_form, 6))     # 0.01888 0.01888
print(round(float(np.sqrt(matrix_form)), 4))         # 0.1374   a 13.7% volatility, below 0.6*20% + 0.4*10% = 16%
# Diversification: 50/50 in two assets of 20% volatility, for several correlations
import numpy as np

weights = np.array([0.5, 0.5])
for rho in (1.0, 0.5, 0.0, -0.5):
    cov = 0.2 * 0.2 * np.array([[1.0, rho], [rho, 1.0]])
    print(rho, round(float(np.sqrt(weights @ cov @ weights)), 4))      # 1.0: 0.2, 0.5: 0.1732, 0.0: 0.1414, -0.5: 0.1

¿De dónde viene el riesgo?

Un gestor quiere saber qué posiciones generan el riesgo. La contribución al riesgo del activo i es w_i * (cov @ w)_i / sigma_p: su peso por lo fuerte que se mueve con la cartera entera, dividido por la volatilidad sigma_p de la cartera. Las contribuciones suman exactamente sigma_p, de modo que reparten el total en partes. Un activo de poco peso pero con alta covarianza con el resto puede contribuir mucho más de lo que sugiere su peso, y una cobertura puede contribuir de forma negativa.

# Risk contributions of a three-asset portfolio: they add up to the volatility
import numpy as np

weights = np.array([0.5, 0.3, 0.2])
cov = np.array([[0.0400, 0.0060, 0.0100],
                [0.0060, 0.0100, 0.0020],
                [0.0100, 0.0020, 0.0225]])

variance = weights @ cov @ weights
contributions = weights * (cov @ weights) / np.sqrt(variance)
print(np.round(contributions, 4), round(float(contributions.sum()), 4))
print(round(float(np.sqrt(variance)), 4))
Los datos importan más que la fórmulaLas fórmulas son exactas, pero mu y cov son estimaciones, y la varianza es la más fiable de las dos: los rendimientos esperados son muy ruidosos, las covarianzas mucho menos. Pesos, mu y cov también deben compartir periodo: una covarianza diaria con rendimientos anuales es un error común; se escala la varianza por el número de periodos y la volatilidad por su raíz.
  • El rendimiento de la cartera es lineal: w @ mu. La varianza es la forma cuadrática w @ cov @ w, con n * n términos y términos cruzados que hacen que una correlación menor que 1 reduzca el riesgo.
  • La volatilidad es sqrt(variance * periods); la anualización debe coincidir con el periodo de cov.
  • Las contribuciones al riesgo w_i * (cov @ w)_i / sigma_p reparten exactamente la volatilidad por activo; una cobertura puede contribuir negativamente.

A continuación: la cartera de mínima varianza, elegir los pesos que hacen este riesgo lo más pequeño posible.

Lab: qué se califica

  • Sumar el rendimiento esperado con portfolio_return25 pts
  • Medir el riesgo con portfolio_variance25 pts
  • Expresar la volatilidad con portfolio_vol25 pts
  • Repartir el riesgo por activo con risk_contributions25 pts
Explorador · lab-83-1
▾ lab-83-1
…
bash · en el navegador
learner@quantcraft:~/lab-83-1$
Python aún no cargadoPythonLn 1, Col 1Spaces: 4UTF-8

¿Restablecer este tema?

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