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 riskLa 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 noiseQuelle 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- Sharpe =
(mean - rf) / std * sqrt(périodes); Sortino remplace l'écart type par l'écart à la baissesqrt(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 estS * 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