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.05La 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))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áticaw @ cov @ w, conn * nté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 decov. - Las contribuciones al riesgo
w_i * (cov @ w)_i / sigma_preparten 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