σμ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 AcademyParcours
Ctrl K
AO
+1,2 k apprenants
ConnexionRejoindre ↗
Parcours
AO
+1,2 k apprenants
Connexion
  • English
  • Français
  • Español
  • العربية
  • Português
  • Deutsch
  • 日本語
  • 中文
Parcours Python›Module 5 · Optimisation de portefeuille›Sujet 5.2·~45 minIntermédiaire

Rendement et variance d'un portefeuille

Position dans le parcoursModule 5 sur 11 · Sujet 2 sur 5
5.2 Rendement et variance d'un portefeuille
—Score du lab— / 100XP total—Série—

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.05

La 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.1

D'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))
Les données comptent plus que la formuleLes formules sont exactes, mais 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 quadratique w @ cov @ w, avec n * n termes 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 de cov.
  • Les contributions au risque w_i * (cov @ w)_i / sigma_p ré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
Explorateur · lab-83-1
▾ lab-83-1
…
bash · dans le navigateur
learner@quantcraft:~/lab-83-1$
Python pas encore chargéPythonLn 1, Col 1Spaces: 4UTF-8

Réinitialiser ce sujet ?

Tout le contenu de lab-83-1 revient aux fichiers de départ et le terminal est vidé. Votre solution actuelle sera perdue : cette action est irréversible.

Sera réinitialisé
  • main.py → code de départ
  • Historique du terminal et sortie des tests
  • Modifications non soumises
Sera conservé
  • Meilleur score du lab
  • Votre XP et votre série
  • Progression du cours et notes

Connectez-vous pour exécuter votre code.

Tous les cours sont gratuits à lire. Créez un compte ou connectez-vous pour exécuter du code, lancer les tests et soumettre ce lab.

  • IDE dans le navigateur avec terminal bash — rien à installer
  • Correction instantanée, XP et votre place dans le parcours
Créer un compteSe connecter
OU
Continuer avec GitHub