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".
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.589De 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.513Intervalos 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.0015t = (mean - mu0) / (s / sqrt(n)): a média em unidades do erro padrãos / sqrt(n); mais dados, |t| maior.- O valor-p bilateral é
erfc(|t| / sqrt(2))para amostras grandes; |t| > 1,96 significa p < 5%; um intervalomean -/+ 1,96 * seque 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