σμ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 AcademyRoteiros
Ctrl K
AO
+1,2 mil alunos
EntrarParticipar ↗
Roteiros
AO
+1,2 mil alunos
Entrar
  • English
  • Français
  • Español
  • العربية
  • Português
  • Deutsch
  • 日本語
  • 中文
Roteiro de Python›Módulo 3 · Probabilidade e simulação Monte Carlo›Tópico 3.7·~40 minIntermediário

O índice de Sharpe

Posição no roteiroMódulo 3 de 11 · Tópico 7 de 12
3.7 O índice de Sharpe
—Nota do lab— / 100XP total—Sequência—

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 risk

Vê-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 noise

Quanto é 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
O melhor backtest é o menos confiávelSe você testa 100 variantes de uma estratégia e fica com a de maior índice de Sharpe, o número da vencedora é inflado pela seleção: até estratégias sem vantagem produzem Sharpe de 1 ou mais por sorte. Divida seu entusiasmo pelo número de variantes testadas, teste em dados que a seleção nunca viu e prefira ideias com uma razão econômica.
  • Sharpe = (mean - rf) / std * sqrt(períodos); Sortino troca o desvio padrão pelo desvio de baixa sqrt(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
Explorador · lab-60-1
▾ lab-60-1
…
bash · no navegador
learner@quantcraft:~/lab-60-1$
Python ainda não carregadoPythonLn 1, Col 1Spaces: 4UTF-8

Redefinir este tópico?

Tudo em lab-60-1 volta aos arquivos iniciais e o terminal é limpo. Sua solução atual será perdida — isso não pode ser desfeito.

Será redefinido
  • main.py → código inicial
  • Histórico do terminal e saída dos testes
  • Alterações não enviadas
Será mantido
  • Melhor nota do lab
  • Seu XP e sua sequência
  • Progresso da aula e anotações

Entre para executar o seu código.

Todas as aulas são gratuitas para ler. Crie uma conta ou entre para executar código, rodar os testes e enviar este lab.

  • IDE no navegador com terminal bash — nada para instalar
  • Correção instantânea, XP e o seu lugar no roteiro
Criar uma contaEntrar
OU
Continuar com o GitHub