Rendement et risque en un seul nombre
La variance minimale ignore les rendements. L'optimisation moyenne-variance (Markowitz) utilise les deux : un investisseur veut un rendement espéré w @ mu élevé et une variance w @ cov @ w faible. Une façon simple de les arbitrer est l'utilité w @ mu - 0.5 * lambda * w @ cov @ w, où lambda est l'aversion au risque : plus elle est grande, plus une unité de variance coûte. Deux investisseurs ayant les mêmes anticipations mais un lambda différent choisissent des portefeuilles différents, et le meilleur portefeuille de chacun est le maximum d'une fonction lisse et concave.
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 meilleure position pour une aversion donnée
Sans contrainte budgétaire (le reste de l'argent rapporte le taux sans risque, pris ici nul en excès), la meilleure position est celle où le gradient mu - lambda * cov @ w s'annule : w = cov^-1 * mu / lambda. En code, np.linalg.solve(cov, mu) / lambda. Cela se met à l'échelle comme le bon sens le veut : aversion double, position moitié. La somme des poids est ce qu'elle doit être ; le reste, 1 - w.sum(), est en liquidités.
# 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))Le portefeuille tangent
Avec un actif sans risque, tous les investisseurs détiennent le même panier risqué, en quantités différentes. Ce panier est celui au ratio de Sharpe (w @ mu - rf) / sqrt(w @ cov @ w) maximal, le portefeuille tangent : w proportionnel à cov^-1 * (mu - rf), remis à l'échelle pour sommer à 1. L'investisseur prudent n'y met qu'une partie de son argent et le reste en liquidités ; l'audacieux emprunte pour en détenir davantage. Décider du niveau de risque et choisir les actifs risqués sont deux problèmes distincts.
# 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 aboveViser un rendement cible
Une autre façon de poser la question : parmi tous les portefeuilles de somme 1 ayant un rendement espéré donné, lequel a la plus petite variance ? Avec deux contraintes, la solution est une combinaison des deux vecteurs résolus s1 = cov^-1 * 1 et sm = cov^-1 * mu, avec des coefficients tirés de quatre nombres a, b, c, d qui résument le marché. Chaque rendement cible donne un portefeuille ; tracer le risque de tous est la frontière efficiente du prochain sujet.
# 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 petit changement d'un rendement espéré peut faire passer le portefeuille d'un actif à un autre. L'optimiseur fait entièrement confiance aux données, donc tout rendement surestimé devient une grosse position. En pratique, on rétrécit les rendements, on borne les poids, et les données viennent de modèles ou de vues plutôt que de moyennes passées brutes. La moyenne-variance est un bon outil de réflexion et un outil dangereux en aveugle.- L'utilité est
w @ mu - 0.5 * lambda * w @ cov @ w; la meilleure position sans budget estsolve(cov, mu) / lambda, et doublerlambdala divise par deux. - Avec un actif sans risque, le meilleur panier risqué est le portefeuille tangent
solve(cov, mu - rf), remis à l'échelle pour sommer à 1 : le ratio de Sharpe maximal. - Pour un rendement cible, les poids de variance minimale combinent
solve(cov, ones)etsolve(cov, mu); le résultat ne vaut que ce que vaut lemuestimé.
À suivre : la frontière efficiente, la courbe du meilleur risque pour chaque niveau de rendement.
Lab : ce qui est noté
- Noter un portefeuille avec
utility25 pts - Trouver la meilleure position avec
utility_weights25 pts - Maximiser le ratio de Sharpe avec
tangency_weights25 pts - Atteindre un objectif de rendement avec
target_return_weights25 pts