Le rendement moyen est-il réel ?
Un backtest affiche un rendement moyen de +0,14 % par jour. La stratégie est-elle bonne, ou a-t-elle eu de la chance ? Les rendements sont bruités, donc tout échantillon a une moyenne, et même une stratégie à pile ou face en a une positive une fois sur deux. Un test d'hypothèse pose une question précise : si la vraie moyenne était mu0 (souvent 0), à quel point une moyenne d'échantillon aussi éloignée de mu0 serait-elle surprenante ? La réponse est une p-valeur. Une petite p-valeur dit que les données seraient très improbables sous l'hypothèse « il ne se passe rien ».
La statistique t mesure la moyenne en unités de sa propre incertitude. L'incertitude d'une moyenne est l'erreur standard s / sqrt(n) : elle diminue quand l'échantillon grandit, c'est pourquoi davantage de données rendent la même moyenne plus convaincante. Mettre tous les rendements à l'échelle change autant la moyenne que l'erreur standard, donc t ne change pas. En règle empirique, |t| supérieur à 2 a peu de chances d'être dû à la chance.
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 à la p-valeur
Sous l'hypothèse nulle, t se comporte comme une variable normale centrée réduite quand l'échantillon est grand : la p-valeur bilatérale est la probabilité de tomber à plus de |t| de 0, dans un sens ou dans l'autre : erfc(|t| / sqrt(2)). Avec t = 1,59, cela fait 11 % : un résultat aussi bon arrive une fois sur neuf par hasard, on ne peut donc pas revendiquer un avantage réel. Le seuil habituel est 5 % (|t| = 1,96). Pour de petits échantillons, la vraie référence est la loi t de Student, qui a des queues plus épaisses : la p-valeur normale est un peu trop petite ; avec moins de 30 points, utilisez une vraie table de t.
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.513Intervalles de confiance et échantillons appariés
Un intervalle de confiance en dit plus qu'un test oui/non : moyenne -/+ 1,96 * erreur standard est une plage qui contient la vraie moyenne environ 95 % du temps. Si l'intervalle contient 0, la moyenne n'est pas significative à 5 %. Notre intervalle, de -0,03 % à +0,32 % par jour, enjambe zéro et est large comparé à la moyenne. Pour comparer deux stratégies mesurées sur les mêmes jours, testez leurs différences quotidiennes contre 0. L'appariement supprime le bruit que les deux partagent (les bons et mauvais jours du marché), et détecte donc un avantage bien plus nettement que deux tests séparés.
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)): la moyenne en unités de son erreur standards / sqrt(n); plus de données, |t| plus grand.- La p-valeur bilatérale vaut
erfc(|t| / sqrt(2))pour de grands échantillons ; |t| > 1,96 signifie p < 5 % ; un intervallemean -/+ 1,96 * secontenant 0 signifie non significatif. - Comparez des stratégies sur les mêmes jours avec la statistique t des différences quotidiennes ; méfiez-vous des tests sur de nombreuses variantes.
À suivre : la régression linéaire (MCO), pour ajuster une droite qui explique un rendement par un autre.
Lab : ce qui est noté
- Mesurer la moyenne en unités de son bruit avec
t_statistic25 pts - Transformer t en p-valeur avec
two_sided_p25 pts - Encadrer la vraie moyenne avec
confidence_interval25 pts - Comparer deux stratégies jour par jour avec
paired_t_statistic25 pts