Un portefeuille est un vecteur de poids
Un portefeuille se décrit par un poids par actif : la fraction de sa valeur qui y est investie. Les poids somment à 1, et un poids négatif est une position vendeuse qui finance les autres. Son rendement sur une période est la moyenne pondérée des rendements des actifs, donc le rendement espéré est un produit scalaire w @ mu. Le rendement est la moitié facile : il est linéaire, sans interaction entre actifs. Le risque non.
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 variance est une forme quadratique
Pour deux actifs, la variance de w1 * A + w2 * B est w1**2 * var(A) + w2**2 * var(B) + 2 * w1 * w2 * cov(A, B) : la variance propre de chaque actif, plus un terme croisé. Avec n actifs, il y a n * n termes, w_i * w_j * cov[i, j], et la somme entière est la forme quadratique w @ cov @ w, une ligne en NumPy. Les termes croisés sont l'essentiel : quand deux actifs ne sont pas parfaitement corrélés, ils se compensent en partie, et le portefeuille est moins risqué que la moyenne pondérée des risques.
# 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.1D'où vient le risque ?
Un gérant veut savoir quelles positions font le risque. La contribution au risque de l'actif i est w_i * (cov @ w)_i / sigma_p : son poids multiplié par l'intensité de sa co-évolution avec l'ensemble du portefeuille, rapporté à la volatilité sigma_p du portefeuille. Les contributions s'additionnent exactement en sigma_p, ce qui découpe le total en parts. Un actif de petit poids mais très covariant avec le reste peut contribuer bien plus que son poids ne le laisse penser, et une couverture peut contribuer négativement.
# 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 et cov sont des estimations, et la variance est la plus fiable des deux : les rendements espérés sont très bruités, les covariances bien moins. Les poids, mu et cov doivent aussi porter sur la même période : une covariance quotidienne avec des rendements annuels est une erreur courante ; on multiplie la variance par le nombre de périodes, la volatilité par sa racine.- Le rendement du portefeuille est linéaire :
w @ mu. La variance est la forme quadratiquew @ cov @ w, avecn * ntermes et des termes croisés qui font qu'une corrélation inférieure à 1 réduit le risque. - La volatilité est
sqrt(variance * periods); l'annualisation doit correspondre à la période decov. - Les contributions au risque
w_i * (cov @ w)_i / sigma_prépartissent exactement la volatilité par actif ; une couverture peut contribuer négativement.
À suivre : le portefeuille de variance minimale, choisir les poids qui rendent ce risque aussi petit que possible.
Lab : ce qui est noté
- Additionner le rendement attendu avec
portfolio_return25 pts - Mesurer le risque avec
portfolio_variance25 pts - Exprimer la volatilité avec
portfolio_vol25 pts - Répartir le risque par actif avec
risk_contributions25 pts