σμ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 AcademyParcours
Ctrl K
AO
+1,2 k apprenants
ConnexionRejoindre ↗
Parcours
AO
+1,2 k apprenants
Connexion
  • English
  • Français
  • Español
  • العربية
  • Português
  • Deutsch
  • 日本語
  • 中文
Parcours Python›Module 3 · Probabilités et simulation Monte-Carlo›Sujet 3.4·~45 minIntermédiaire

Tests d'hypothèses : le test t

Position dans le parcoursModule 3 sur 11 · Sujet 4 sur 12
3.4 Tests d'hypothèses : le test t
—Score du lab— / 100XP total—Série—

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 ».

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

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.589

De 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.513

Intervalles 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.0015
Significatif ne veut pas dire important, et beaucoup de tests trichentUn avantage minuscule peut être très significatif avec assez de données, et un gros peut ne pas l'être avec peu. Lisez toujours la taille de l'effet à côté de la p-valeur. Et si vous testez 100 stratégies, environ 5 passeront à 5 % par pur hasard : plus vous essayez de variantes sur les mêmes données, moins une p-valeur isolée signifie quelque chose.
  • t = (mean - mu0) / (s / sqrt(n)) : la moyenne en unités de son erreur standard s / 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 intervalle mean -/+ 1,96 * se contenant 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
Explorateur · lab-57-1
▾ lab-57-1
…
bash · dans le navigateur
learner@quantcraft:~/lab-57-1$
Python pas encore chargéPythonLn 1, Col 1Spaces: 4UTF-8

Réinitialiser ce sujet ?

Tout le contenu de lab-57-1 revient aux fichiers de départ et le terminal est vidé. Votre solution actuelle sera perdue : cette action est irréversible.

Sera réinitialisé
  • main.py → code de départ
  • Historique du terminal et sortie des tests
  • Modifications non soumises
Sera conservé
  • Meilleur score du lab
  • Votre XP et votre série
  • Progression du cours et notes

Connectez-vous pour exécuter votre code.

Tous les cours sont gratuits à lire. Créez un compte ou connectez-vous pour exécuter du code, lancer les tests et soumettre ce lab.

  • IDE dans le navigateur avec terminal bash — rien à installer
  • Correction instantanée, XP et votre place dans le parcours
Créer un compteSe connecter
OU
Continuer avec GitHub