σμ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.4·~45 minIntermediário

Testes de hipóteses: o teste t

Posição no roteiroMódulo 3 de 11 · Tópico 4 de 12
3.4 Testes de hipóteses: o teste t
—Nota do lab— / 100XP total—Sequência—

O retorno médio é real?

Um backtest mostra retorno médio de +0,14% ao dia. A estratégia é boa ou teve sorte? Os retornos são ruidosos, então toda amostra tem uma média, e até uma estratégia de cara ou coroa a tem positiva metade das vezes. Um teste de hipótese faz uma pergunta precisa: se a média verdadeira fosse mu0 (normalmente 0), quão surpreendente seria uma média amostral tão distante de mu0? A resposta é um valor-p. Um valor-p pequeno diz que os dados seriam muito improváveis sob a hipótese de que "nada está acontecendo".

t = (mean - mu0) / (s / sqrt(n)) p = P(|Z| > |t|)

A estatística t mede a média em unidades da sua própria incerteza. A incerteza de uma média é o erro padrão s / sqrt(n): diminui quando a amostra cresce, por isso mais dados tornam a mesma média mais convincente. Reescalar todos os retornos muda igualmente a média e o erro padrão, então t não muda. Como regra prática, |t| acima de 2 dificilmente é sorte.

import math
import numpy as np

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

n = len(returns)
mean = returns.mean()
std_error = returns.std(ddof=1) / math.sqrt(n)         # the uncertainty of the mean
t = mean / std_error                                   # testing against mu0 = 0

print(round(mean, 5), round(std_error, 6))             # 0.00142 0.000892   the mean is only 1.6 standard errors from 0
print(round(t, 3))                                     # 1.589

De t ao valor-p

Sob a hipótese nula, t se comporta como uma normal padrão quando a amostra é grande, então o valor-p bilateral é a probabilidade de cair a mais de |t| de 0 em qualquer direção: erfc(|t| / sqrt(2)). Com t = 1,59 é 11%: um resultado tão bom acontece uma vez em nove por acaso, então não dá para afirmar uma vantagem real. O limite usual é 5% (|t| = 1,96). Para amostras pequenas a referência correta é a t de Student, de caudas mais pesadas, então o valor-p normal é um pouco pequeno demais; com menos de 30 pontos use uma tabela t de verdade.

p_value = math.erfc(abs(t) / math.sqrt(2))
print(round(p_value, 4))                      # 0.1121   an outcome this good happens by chance about 1 time in 9

print(round(math.erfc(1.96 / math.sqrt(2)), 4))   # 0.05     |t| = 1.96 is the usual 5% limit

# Testing against another value: is the mean different from 0.2% a day?
t_other = (mean - 0.002) / std_error
print(round(t_other, 3), round(math.erfc(abs(t_other) / math.sqrt(2)), 3))   # -0.654 0.513

Intervalos de confiança e amostras pareadas

Um intervalo de confiança diz mais que um teste sim/não: média -/+ 1,96 * erro padrão é uma faixa que contém a média verdadeira cerca de 95% das vezes. Se o intervalo contém 0, a média não é significativa a 5%. Nosso intervalo, de -0,03% a +0,32% ao dia, atravessa o zero e é largo comparado à média. Para comparar duas estratégias medidas nos mesmos dias, teste suas diferenças diárias contra 0. O pareamento remove o ruído que as duas compartilham (os dias bons e ruins do mercado), então detecta uma vantagem muito mais nitidamente que dois testes separados.

low, high = mean - 1.96 * std_error, mean + 1.96 * std_error
print(round(low, 5), round(high, 5))           # -0.00033 0.00316   the interval contains 0: not significant at 5%

# Paired: the same six days for two strategies
a = np.array([0.004, -0.002, 0.001, 0.003, -0.004, 0.002])
b = np.array([0.002, -0.003, 0.000, 0.001, -0.002, 0.0])
d = a - b
t_paired = d.mean() / (d.std(ddof=1) / math.sqrt(len(d)))
print(round(d.mean(), 5), round(t_paired, 3))   # 0.001 1.581   a daily edge of 0.1%, but only 6 days
# Same mean and spread, four times the data: t doubles and the p-value collapses
spread = returns.std(ddof=1)
for n in (12, 48):
    t_n = mean / (spread / math.sqrt(n))
    print(n, round(t_n, 3), round(math.erfc(abs(t_n) / math.sqrt(2)), 4))
# 12 1.589 0.1121
# 48 3.178 0.0015
Significativo não é importante, e muitos testes trapaceiamUma vantagem minúscula pode ser muito significativa com dados suficientes, e uma grande pode não ser com poucos. Leia sempre o tamanho do efeito junto com o valor-p. E se você testar 100 estratégias, cerca de 5 passarão a 5% só por sorte: quanto mais variantes você tenta nos mesmos dados, menos significa um valor-p isolado.
  • t = (mean - mu0) / (s / sqrt(n)): a média em unidades do erro padrão s / sqrt(n); mais dados, |t| maior.
  • O valor-p bilateral é erfc(|t| / sqrt(2)) para amostras grandes; |t| > 1,96 significa p < 5%; um intervalo mean -/+ 1,96 * se que contém 0 significa não significativo.
  • Compare estratégias nos mesmos dias com a estatística t das diferenças diárias; cuidado ao testar muitas variantes.

A seguir: a regressão linear (MQO), para ajustar uma reta que explique um retorno com outro.

Lab: o que é avaliado

  • Medir a média em unidades do seu ruído com t_statistic25 pts
  • Transformar t em valor-p com two_sided_p25 pts
  • Delimitar a média verdadeira com confidence_interval25 pts
  • Comparar duas estratégias dia a dia com paired_t_statistic25 pts
Explorador · lab-57-1
▾ lab-57-1
…
bash · no navegador
learner@quantcraft:~/lab-57-1$
Python ainda não carregadoPythonLn 1, Col 1Spaces: 4UTF-8

Redefinir este tópico?

Tudo em lab-57-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