Lo que promete la campana
Muchos modelos de riesgo suponen que los rendimientos siguen una distribución normal, porque permite calcularlo todo con una media y una desviación típica. La normal es muy precisa sobre los extremos. La probabilidad de estar a más de k desviaciones típicas de la media, en cualquier dirección, es erfc(k / sqrt(2)): 31,7 % para 1, 4,55 % para 2, 0,27 % para 3, 0,0063 % para 4. Un día de 4 sigmas ocurriría entonces una vez cada 15 800 días bursátiles, más o menos una vez en 63 años, y un día de 5 sigmas una vez en varios miles de años.
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 daysLos mercados tienen colas gruesas
Los mercados reales producen días de 4 y 5 sigmas muchas veces a lo largo de una carrera, en 1987, 2008, 2020. Sus distribuciones de rendimientos tienen colas gruesas: más días extremos de los que permite la campana (y un centro más alto y estrecho). Una forma sencilla de verlo es el ratio de cola: la proporción observada de rendimientos más allá de k desviaciones típicas dividida por la que daría una normal. Un ratio cercano a 1 significa colas normales; 5, extremos cinco veces demasiado frecuentes; con datos diarios y k = 4 puede llegar a cientos. Los modelos de riesgo que suponen normalidad subestiman justo los días que más importan.
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)El test de Jarque-Bera
Una prueba formal pregunta si la muestra podría provenir plausiblemente de una distribución normal. El estadístico de Jarque-Bera combina las dos medidas de forma del tema anterior: JB = n / 6 * (S² + K² / 4), con S la asimetría y K la curtosis en exceso. En una muestra normal ambos están cerca de 0 y JB es pequeño. Bajo normalidad, JB sigue una distribución chi-cuadrado con 2 grados de libertad, cuya cola tiene la forma cerrada exp(-JB / 2): es el valor p. Si es menor que 0,05 se rechaza la normalidad. El valor crítico es JB = 5,99. Nuestra muestra de 10 días con un desplome tiene JB = 22,4 y un 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- Una variable normal está a más de k desviaciones con probabilidad
erfc(k / sqrt(2)): 4,55 % para 2, 0,0063 % para 4. - El ratio de cola, proporción observada dividida por la normal, muestra las colas gruesas: los mercados están muy por encima de 1 a 4 sigmas.
- Jarque-Bera:
n / 6 * (S**2 + K**2 / 4); el valor p esexp(-JB / 2); por debajo de 0,05 se rechaza la normalidad (JB sobre 5,99).
A continuación: las pruebas de hipótesis: la prueba t, para decidir si un rendimiento medio es realmente distinto de cero.
Lab: qué se califica
- Medir cuánto se aleja una muestra de la normal con
jarque_bera25 pts - Convertir el estadístico en un valor p con
normality_p_value25 pts - Calcular lo que promete la campana con
normal_tail_probability25 pts - Contar cuánto más gruesas son las colas con
tail_ratio25 pts