σμ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.1·~45 minIntermédiaire

Matrices de covariance et de corrélation

Position dans le parcoursModule 5 sur 11 · Sujet 1 sur 5
5.1 Matrices de covariance et de corrélation
—Score du lab— / 100XP total—Série—

D'un actif à plusieurs

Tout ce qui précède portait sur une seule série de rendements. Un portefeuille contient de nombreux actifs, et son risque dépend non seulement du risque de chacun, mais de la façon dont ils évoluent *ensemble*. La covariance de deux actifs est le produit moyen de leurs écarts à la moyenne : positive s'ils montent et baissent ensemble, négative si l'un monte quand l'autre baisse. Pour n actifs, il y a n * n covariances, rangées dans une matrice de covariance, symétrique, avec les variances sur la diagonale.

import numpy as np

# Five days of returns for two assets: shape (days, assets)
returns = np.array([[0.01, 0.02], [-0.02, -0.01], [0.03, 0.01], [0.00, -0.02], [0.01, 0.03]])

centered = returns - returns.mean(axis=0)            # subtract each column's mean
cov = centered.T @ centered / (len(returns) - 1)     # (assets x days) @ (days x assets) -> (assets x assets)
print(np.round(cov, 6))
print(np.allclose(cov, np.cov(returns, rowvar=False)))     # True

La matrice de covariance empirique

Avec un tableau de rendements de forme (days, assets), toute la matrice tient en une ligne d'algèbre linéaire. On soustrait la moyenne de chaque colonne (axis=0, moyenne sur les jours), et on multiplie la transposée du tableau centré par lui-même : l'entrée (i, j) de centered.T @ centered est la somme sur les jours de x_i * x_j. On divise par days - 1, comme pour une variance d'échantillon. C'est ce que calculent np.cov(returns, rowvar=False) et DataFrame.cov() ; notez le rowvar=False, car np.cov attend les actifs en lignes.

La corrélation : la covariance sans unités

Une covariance de 0.0004 dit peu : elle dépend de la volatilité des deux actifs. Diviser par le produit des deux écarts-types enlève l'échelle et donne la corrélation, toujours entre -1 et 1 : corr[i, j] = cov[i, j] / (std[i] * std[j]), avec std la racine de la diagonale. En code, np.outer(std, std) construit le tableau des produits et une division remet toute la matrice à l'échelle, avec des 1 sur la diagonale. La corrélation dit à quel point deux actifs évoluent ensemble ; la covariance, qui garde l'échelle, est ce dont les formules de risque ont besoin.

# From covariance to correlation and annualised volatilities
import numpy as np

returns = np.array([[0.01, 0.02], [-0.02, -0.01], [0.03, 0.01], [0.00, -0.02], [0.01, 0.03]])
cov = np.cov(returns, rowvar=False)

std = np.sqrt(np.diag(cov))
corr = cov / np.outer(std, std)
print(np.round(corr, 4))
print(np.round(np.sqrt(np.diag(cov) * 252), 4))      # daily variances scaled to a year: volatility grows with sqrt(252)

Cette matrice peut-elle être réelle ?

Un tableau de nombres quelconque n'est pas une matrice de covariance. Elle doit être symétrique et semi-définie positive : pour tout vecteur de poids w, le nombre w' C w est la variance d'un portefeuille, donc il ne peut pas être négatif. De façon équivalente, aucune valeur propre n'est négative. Les matrices estimées passent, mais celles modifiées à la main, ou un assemblage de corrélations de sources différentes (par exemple A~B = 0.9, B~C = 0.9, A~C = -0.9), échouent. On vérifie avec np.linalg.eigvalsh, pour les matrices symétriques, qui renvoie des valeurs propres réelles, avec une petite tolérance pour les arrondis.

# A validity check: symmetry and no negative eigenvalue
import numpy as np

def is_valid_cov(matrix, tol=1e-10):
    symmetric = bool(np.all(np.abs(matrix - matrix.T) <= tol))
    return symmetric and bool(np.linalg.eigvalsh(matrix).min() >= -tol)

good = np.array([[1.0, 0.5, 0.2], [0.5, 1.0, 0.3], [0.2, 0.3, 1.0]])
bad = np.array([[1.0, 0.9, -0.9], [0.9, 1.0, 0.9], [-0.9, 0.9, 1.0]])
print(is_valid_cov(good), np.round(np.linalg.eigvalsh(good), 3))
print(is_valid_cov(bad), np.round(np.linalg.eigvalsh(bad), 3))
# Few days, many assets: the sample covariance matrix is singular
import numpy as np

rng = np.random.default_rng(0)
returns = rng.normal(0, 0.01, size=(5, 8))              # 5 days, 8 assets
cov = np.cov(returns, rowvar=False)
print(np.linalg.matrix_rank(cov), "of", cov.shape[0])  # 4 of 8: some portfolios seem to have zero variance
Peu d'observations, beaucoup d'actifsUne matrice pour 500 actions a 125 250 entrées distinctes, estimées à partir d'environ 250 jours. Avec moins de jours que d'actifs, la matrice empirique est singulière : un portefeuille semble avoir une variance nulle, ce qu'aucun optimiseur ne devrait croire. Les praticiens la rétrécissent vers une cible simple ou utilisent un modèle à facteurs. L'estimation est aussi une moyenne du passé : les périodes calmes et de crise ont des covariances très différentes.
  • La matrice de covariance d'un tableau (days, assets) est centered.T @ centered / (days - 1) : symétrique, variances sur la diagonale.
  • La corrélation est cov / np.outer(std, std) : sans unité, entre -1 et 1, des 1 sur la diagonale ; on annualise les variances par periods, les volatilités par sa racine.
  • Une matrice de covariance valide est symétrique sans valeur propre négative (np.linalg.eigvalsh) ; des estimations sur trop peu de jours, ou retouchées à la main, peuvent l'enfreindre.

À suivre : le rendement et la variance d'un portefeuille, transformer les poids et cette matrice en risque de portefeuille.

Lab : ce qui est noté

  • Construire le tableau des covariances avec cov_matrix25 pts
  • Le ramener en corrélations avec corr_from_cov25 pts
  • Lire la volatilité de chaque actif avec annualised_vols25 pts
  • Tester si elle peut être réelle avec is_valid_cov25 pts
Explorateur · lab-82-1
▾ lab-82-1
…
bash · dans le navigateur
learner@quantcraft:~/lab-82-1$
Python pas encore chargéPythonLn 1, Col 1Spaces: 4UTF-8

Réinitialiser ce sujet ?

Tout le contenu de lab-82-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