σμ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 3 · Probabilités et simulation Monte-Carlo›Sujet 3.7·~40 minIntermédiaire

Le ratio de Sharpe

Position dans le parcoursModule 3 sur 11 · Sujet 7 sur 12
3.7 Le ratio de Sharpe
—Score du lab— / 100XP total—Série—

La récompense par unité de risque

Une stratégie qui rapporte 12 % avec 20 % de volatilité n'est pas meilleure qu'une qui rapporte 6 % avec 5 %. Le ratio de Sharpe les met sur une même échelle : le rendement moyen au-dessus du taux sans risque divisé par la volatilité, (mean - rf) / std, annualisé par sqrt(périodes). Vous avez rencontré la formule dans le sujet sur la moyenne et la variance. Ce sujet porte sur son usage : comment lire un ratio de Sharpe, l'améliorer et surtout dans quelle mesure lui faire confiance. Un Sharpe de 0,5 est médiocre, 1 est bon, 2 est excellent et suspect.

La volatilité compte autant la hausse que la baisse, or personne ne se plaint d'un gain surprise. Le ratio de Sortino ne garde que le mauvais côté : il remplace l'écart type par l'écart à la baisse, sqrt(mean(min(r - mar, 0)²)), où mar est le rendement minimal acceptable. Les jours au-dessus de mar contribuent pour zéro mais comptent quand même dans la moyenne. Une stratégie avec de gros sauts positifs et de petites pertes obtient un bien meilleur score en Sortino qu'en Sharpe.

import math
import numpy as np
import pandas as pd

returns = np.array([0.004, -0.002, 0.001, 0.003, -0.004, 0.002, 0.006, -0.001, -0.010, 0.004, 0.002, -0.003])

sharpe = returns.mean() / returns.std(ddof=1) * math.sqrt(252)
print(round(sharpe, 3))                                  # 0.598

mar = 0.0                                                # a day below zero is a bad day
downside = math.sqrt(np.mean(np.minimum(returns - mar, 0) ** 2))
sortino = (returns.mean() - mar) / downside * math.sqrt(252)
print(round(downside, 5), round(sortino, 3))             # 0.00329 0.804   the up days no longer count as risk

La voir évoluer

Un seul ratio de Sharpe pour tout l'historique cache l'histoire. Un ratio de Sharpe glissant le recalcule sur les window derniers rendements, ce qui montre les bons et mauvais régimes de la stratégie. Avec pandas, c'est la moyenne glissante divisée par l'écart type glissant, annualisée. Remarquez à quel point les fenêtres courtes sautent : un mois de données est loin de suffire pour classer des stratégies.

series = pd.Series(returns)
rolling = series.rolling(5)
rolling_sharpe = rolling.mean() / rolling.std() * math.sqrt(252)
print(rolling_sharpe.round(2).tolist())
# [nan, nan, nan, nan, 1.89, 0.0, 6.96, 4.97, -3.66, 0.51, 0.51, -4.69]   a 5-day Sharpe ratio is mostly noise

Quelle part de chance ?

Un ratio de Sharpe est une estimation, et les estimations comportent une erreur. Pour un Sharpe par période sr mesuré sur n rendements, l'erreur standard vaut environ sqrt((1 + 0,5 * sr²) / n). Avec nos 12 jours, elle est de 0,29 par jour pour un Sharpe de 0,04 : l'erreur vaut sept fois l'estimation. Annualisés, ces mêmes 12 jours donnent 0,6 ± 4,6. Pour un Sharpe annuel S observé sur years années, la statistique t vaut S * sqrt(years) ; il faut donc (1,96 / S)² années pour être sûr à 95 % qu'il n'est pas nul : 15 ans pour un Sharpe de 0,5, 4 ans pour un Sharpe de 1. De courts backtests de stratégies modestes ne prouvent rien.

per_period = returns.mean() / returns.std(ddof=1)            # the per-day Sharpe ratio
std_error = math.sqrt((1 + 0.5 * per_period ** 2) / len(returns))
print(round(per_period, 4), round(std_error, 4))            # 0.0376 0.2888   the error is 7 times the estimate
print(round(std_error * math.sqrt(252), 3))                 # 4.584           annualised: 0.6 +/- 4.6

# Years needed for an annual Sharpe ratio to reach t = 1.96
for annual in (0.5, 1.0, 2.0):
    print(annual, round((1.96 / annual) ** 2, 1))
# 0.5 15.4
# 1.0 3.8
# 2.0 1.0
# A yearly Sharpe of 1.0 observed over 4 years (1,008 days): how precise is it?
per_day = 1.0 / math.sqrt(252)
error_per_day = math.sqrt((1 + 0.5 * per_day ** 2) / 1008)
print(round(per_day, 4), round(error_per_day, 4), round(error_per_day * math.sqrt(252), 3))   # 0.063 0.0315 0.5   so 1.0 +/- 0.5
Le meilleur backtest est le moins fiableSi vous essayez 100 variantes d'une stratégie et gardez celle au meilleur ratio de Sharpe, le chiffre du gagnant est gonflé par la sélection : même des stratégies sans avantage produisent un Sharpe de 1 ou plus par hasard. Divisez votre enthousiasme par le nombre de variantes essayées, testez sur des données que la sélection n'a jamais vues, et préférez les idées qui ont une raison économique.
  • Sharpe = (mean - rf) / std * sqrt(périodes) ; Sortino remplace l'écart type par l'écart à la baisse sqrt(mean(min(r - mar, 0)²)).
  • Un Sharpe glissant (rolling.mean() / rolling.std()) montre l'évolution du ratio ; les fenêtres courtes sont surtout du bruit.
  • Son erreur standard est sqrt((1 + 0,5 * sr²) / n) par période ; la statistique t d'un Sharpe annuel est S * sqrt(years) : un Sharpe de 1 demande environ 4 ans.

À suivre : les drawdowns, les pertes depuis un sommet que les investisseurs ressentent réellement.

Lab : ce qui est noté

  • Ne pénaliser que les mauvais jours avec sortino_ratio25 pts
  • Suivre l'évolution du ratio dans le temps avec rolling_sharpe25 pts
  • Mesurer la part de chance d'un ratio de Sharpe avec sharpe_standard_error25 pts
  • Se demander si c'est réel avec sharpe_t_stat25 pts
Explorateur · lab-60-1
▾ lab-60-1
…
bash · dans le navigateur
learner@quantcraft:~/lab-60-1$
Python pas encore chargéPythonLn 1, Col 1Spaces: 4UTF-8

Réinitialiser ce sujet ?

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