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

La frontière efficiente

Position dans le parcoursModule 5 sur 11 · Sujet 5 sur 5
5.5 La frontière efficiente
—Score du lab— / 100XP total—Série—

Le risque pour chaque niveau de rendement

Le sujet précédent trouvait, pour un rendement cible, les poids de variance minimale. Faites-le pour chaque cible et placez la volatilité en abscisse et le rendement en ordonnée : les points forment une courbe, la frontière efficiente. Tout portefeuille de ces actifs se trouve sur elle ou à l'intérieur, à droite. Un portefeuille à l'intérieur est inefficace, puisqu'un autre offre le même rendement avec moins de risque, ou plus de rendement pour le même risque. L'idée de Markowitz : l'investisseur ne doit regarder que la courbe.

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]])

def target_return_weights(mu, cov, target):
    ones = np.ones(len(mu))
    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
    return ((c - b * target) * s1 + (a * target - b) * sm) / d

for target in (0.05, 0.06, 0.07, 0.08, 0.09, 0.10):
    w = target_return_weights(mu, cov, target)
    print(target, round(float(np.sqrt(w @ cov @ w)), 4))

Sa forme et sa pointe

La frontière est une parabole dans le plan (rendement, variance), et une hyperbole si l'on trace la volatilité. La variance pour un rendement cible t vaut (a * t**2 - 2 * b * t + c) / (a * c - b**2), avec les mêmes quatre nombres a, b, c qu'avant. Son point le plus bas est le portefeuille de variance minimale globale, de rendement b / a : la pointe de la courbe, qui est le portefeuille de variance minimale d'il y a deux sujets. Seule la partie de la pointe vers le haut est la frontière efficiente ; la branche basse est son image miroir, avec moins de rendement pour le même risque.

# The tip of the curve: the minimum-variance portfolio and its return
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)
x = np.linalg.solve(cov, ones)
w = x / x.sum()
print(np.round(w, 4), round(float(w @ mu), 4), round(float(np.sqrt(w @ cov @ w)), 4))     # the tip: return, volatility

def closed_form_vol(target):
    s1, sm = x, np.linalg.solve(cov, mu)
    a, b, c = ones @ s1, ones @ sm, mu @ sm
    return float(np.sqrt((a * target ** 2 - 2 * b * target + c) / (a * c - b ** 2)))

for target in (0.05, 0.0642, 0.07):
    print(target, round(closed_form_vol(target), 4))

Points efficients et inefficaces

Le même risque apparaît pour deux rendements sur la courbe, un sur chaque branche, et seul le plus élevé vaut la peine d'être détenu. Un objectif est donc efficient exactement quand il est au moins égal au rendement de variance minimale. Le test est une simple comparaison, mais il décide quels objectifs figurent sur un graphique de choix. Chercher le meilleur compromis sur la frontière revient alors à choisir un point, et une réponse courante est celui qui rapporte le plus de rendement par unité de risque au-dessus du taux sans risque.

# The point with the highest Sharpe ratio among candidate targets
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
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

targets = np.linspace(0.05, 0.10, 11)
vols = np.sqrt((a * targets ** 2 - 2 * b * targets + c) / d)
sharpe = (targets - rf) / vols
for t, v, s in zip(targets, vols, sharpe):
    print(round(float(t), 3), round(float(v), 4), round(float(s), 4))
print("best target:", round(float(targets[np.argmax(sharpe)]), 3))
# Efficient or not: compare a target with the return of the minimum-variance portfolio
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]])
x = np.linalg.solve(cov, np.ones(3))
tip = float((x / x.sum()) @ mu)
for target in (0.05, 0.06, 0.07, 0.08):
    print(target, "efficient" if target >= tip else "inefficient")
Une frontière estimée est une bande floueLa courbe est tracée à partir de mu et cov, tous deux estimés, donc c'est elle-même une estimation, et une estimation flatteuse : l'optimiseur a choisi, parmi tous les portefeuilles, ceux dont les rendements estimés sont élevés et les risques faibles par hasard. Les résultats réels se situent à droite et en dessous de la ligne visible. Rééchantillonner les données, ou comparer la frontière de la première moitié d'un échantillon avec sa performance sur la seconde, montre la part de chance de la courbe. Voyez la frontière comme une carte des compromis, pas comme une promesse.
  • La frontière efficiente donne, pour chaque rendement cible, la volatilité minimale : sqrt(w @ cov @ w) avec w = target_return_weights(...) ; les portefeuilles à l'intérieur sont inefficaces.
  • Sa pointe est le portefeuille de variance minimale globale, de rendement b / a ; seuls les objectifs au moins égaux à ce rendement sont efficients.
  • Choisissez un point selon un critère comme le ratio de Sharpe maximal (target - rf) / volatility ; rappelez-vous que la courbe vient d'estimations et est optimiste.

À suivre : un backtester vectorisé, tester une règle de trading sur des prix historiques.

Lab : ce qui est noté

  • Tracer la frontière avec frontier_vols25 pts
  • Trouver la pointe de la courbe avec min_variance_return25 pts
  • Distinguer la bonne moitié de la mauvaise avec is_efficient25 pts
  • Choisir le meilleur point avec max_sharpe_target25 pts
Explorateur · lab-86-1
▾ lab-86-1
…
bash · dans le navigateur
learner@quantcraft:~/lab-86-1$
Python pas encore chargéPythonLn 1, Col 1Spaces: 4UTF-8

Réinitialiser ce sujet ?

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