O que o sino promete
Muitos modelos de risco supõem que os retornos seguem uma distribuição normal, porque ela torna tudo calculável com uma média e um desvio padrão. A normal é muito precisa sobre os extremos. A probabilidade de estar a mais de k desvios padrão da média, em qualquer direção, é erfc(k / sqrt(2)): 31,7% para 1, 4,55% para 2, 0,27% para 3, 0,0063% para 4. Um dia de 4 sigmas aconteceria então cerca de uma vez em 15 800 dias de negociação, mais ou menos uma vez em 63 anos, e um dia de 5 sigmas uma vez em vários milhares de anos.
import math
import numpy as np
for k in (1, 2, 3, 4, 5):
probability = math.erfc(k / math.sqrt(2)) # P(|Z| > k) for a normal variable
print(k, f"{probability:.2e}")
# 1 3.17e-01
# 2 4.55e-02
# 3 2.70e-03
# 4 6.33e-05
# 5 5.73e-07
print(round(1 / math.erfc(4 / math.sqrt(2)))) # 15787 trading days between 4-sigma days, if returns were normal
print(round(1 / math.erfc(5 / math.sqrt(2)) / 252)) # 6922 years between 5-sigma daysOs mercados têm caudas pesadas
Os mercados reais produzem dias de 4 e 5 sigmas várias vezes numa carreira, em 1987, 2008, 2020. Suas distribuições de retornos têm caudas pesadas: mais dias extremos do que o sino permite (e um centro mais alto e estreito). Uma forma simples de ver isso é a razão de cauda: a parcela observada de retornos além de k desvios padrão dividida pela parcela que uma normal daria. Uma razão perto de 1 significa caudas normais; 5, extremos cinco vezes mais frequentes; em dados diários com k = 4 pode chegar a centenas. Modelos de risco que supõem normalidade subestimam justamente os dias que mais importam.
returns = np.array([0.010, 0.012, 0.008, 0.011, 0.009, 0.010, 0.012, -0.060, 0.010, 0.009])
def tail_ratio(x, k):
observed = np.mean(np.abs(x - x.mean()) > k * x.std(ddof=1)) # share beyond k standard deviations
return observed / math.erfc(k / math.sqrt(2)) # divided by what a normal would give
print(round(float(tail_ratio(returns, 2)), 2)) # 2.2 the one crash is 2.2 times too frequent at 2 sigma (a tiny sample, though)O teste de Jarque-Bera
Um teste formal pergunta se a amostra poderia plausivelmente vir de uma distribuição normal. A estatística de Jarque-Bera combina as duas medidas de forma do tópico anterior: JB = n / 6 * (S² + K² / 4), com S a assimetria e K a curtose em excesso. Numa amostra normal as duas ficam perto de 0 e JB é pequeno. Sob normalidade, JB segue uma qui-quadrado com 2 graus de liberdade, cuja cauda tem a forma fechada exp(-JB / 2): é o valor-p. Se for menor que 0,05, rejeita-se a normalidade. O valor crítico é JB = 5,99. Nossa amostra de 10 dias com uma queda tem JB = 22,4 e valor-p de 0,00001.
x = returns
n = len(x)
deviations = x - x.mean()
m2, m3, m4 = (np.mean(deviations ** k) for k in (2, 3, 4))
skew, kurt = m3 / m2 ** 1.5, m4 / m2 ** 2 - 3
jb = n / 6 * (skew ** 2 + kurt ** 2 / 4)
p_value = math.exp(-jb / 2)
print(round(float(jb), 2), p_value < 0.05) # 22.37 True normality rejected
print(f"{p_value:.1e}") # 1.4e-05# The p-value falls fast as the statistic grows: 5.99 is the 5% critical value
for jb in (0, 2, 5.99, 10, 22.37):
print(jb, round(math.exp(-jb / 2), 5))
# 0 1.0
# 2 0.36788
# 5.99 0.05004
# 10 0.00674
# 22.37 1e-05- Uma variável normal está a mais de k desvios com probabilidade
erfc(k / sqrt(2)): 4,55% para 2, 0,0063% para 4. - A razão de cauda, parcela observada dividida pela normal, mostra as caudas pesadas: os mercados ficam muito acima de 1 a 4 sigmas.
- Jarque-Bera:
n / 6 * (S**2 + K**2 / 4); o valor-p éexp(-JB / 2); abaixo de 0,05, rejeita-se a normalidade (JB acima de 5,99).
A seguir: os testes de hipóteses: o teste t, para decidir se um retorno médio é realmente diferente de zero.
Lab: o que é avaliado
- Medir o quanto uma amostra se afasta da normal com
jarque_bera25 pts - Transformar a estatística em valor-p com
normality_p_value25 pts - Calcular o que o sino promete com
normal_tail_probability25 pts - Contar quanto mais pesadas são as caudas com
tail_ratio25 pts