A recompensa por unidade de risco
Uma estratégia que ganha 12% com 20% de volatilidade não é melhor que uma que ganha 6% com 5%. O índice de Sharpe as põe numa mesma escala: o retorno médio acima da taxa livre de risco dividido pela volatilidade, (mean - rf) / std, anualizado por sqrt(períodos). Você viu a fórmula no tópico sobre média e variância. Este tópico trata do seu uso: como ler um índice de Sharpe, melhorá-lo e, acima de tudo, quanto confiar nele. Um Sharpe de 0,5 é medíocre, 1 é bom, 2 é excelente e suspeito.
A volatilidade conta igualmente a alta e a queda, mas ninguém reclama de um ganho surpresa. O índice de Sortino guarda só o lado ruim: troca o desvio padrão pelo desvio de baixa, sqrt(mean(min(r - mar, 0)²)), em que mar é o retorno mínimo aceitável. Dias acima de mar contribuem com zero, mas ainda entram na média. Uma estratégia com grandes saltos positivos e pequenas perdas pontua muito melhor no Sortino que no 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 riskVê-lo mudar
Um único índice de Sharpe para todo o histórico esconde a história. Um Sharpe móvel o recalcula nos últimos window retornos, e assim se veem os bons e maus regimes da estratégia. No pandas é a média móvel dividida pelo desvio padrão móvel, anualizada. Note como as janelas curtas pulam: um mês de dados está longe de bastar para classificar estratégias.
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 noiseQuanto é sorte?
Um índice de Sharpe é uma estimativa, e estimativas têm erro. Para um Sharpe por período sr medido em n retornos, o erro padrão é cerca de sqrt((1 + 0,5 * sr²) / n). Com nossos 12 dias é 0,29 ao dia contra um Sharpe de 0,04: o erro é sete vezes a estimativa. Anualizados, esses mesmos 12 dias dão 0,6 ± 4,6. Para um Sharpe anual S observado por years anos, a estatística t é S * sqrt(years), então são necessários (1,96 / S)² anos para ter 95% de certeza de que não é zero: 15 anos para um Sharpe de 0,5, 4 anos para um Sharpe de 1. Backtests curtos de estratégias modestas não provam nada.
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(períodos); Sortino troca o desvio padrão pelo desvio de baixasqrt(mean(min(r - mar, 0)²)). - Um Sharpe móvel (
rolling.mean() / rolling.std()) mostra como o índice muda; janelas curtas são sobretudo ruído. - Seu erro padrão é
sqrt((1 + 0,5 * sr²) / n)por período; a estatística t de um Sharpe anual éS * sqrt(years): um Sharpe de 1 precisa de cerca de 4 anos.
A seguir: os drawdowns, as perdas a partir de um pico que os investidores realmente sentem.
Lab: o que é avaliado
- Penalizar só os dias ruins com
sortino_ratio25 pts - Acompanhar a mudança do índice no tempo com
rolling_sharpe25 pts - Medir a sorte em um índice de Sharpe com
sharpe_standard_error25 pts - Perguntar se é real com
sharpe_t_stat25 pts