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.0526La 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 aboveAlcanzar 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))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 essolve(cov, mu) / lambda, y duplicarlambdala 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)ysolve(cov, mu); el resultado vale lo que valga elmuestimado.
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