σμ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 2 · NumPy et pandas pour les données de marché›Sujet 2.16·~40 minIntermédiaire

Détecter les valeurs aberrantes

Position dans le parcoursModule 2 sur 11 · Sujet 16 sur 20
2.16 Détecter les valeurs aberrantes
—Score du lab— / 100XP total—Série—

Une valeur qui ne peut pas être juste

Un rendement quotidien de +30 % sur une grande capitalisation est soit une OPA, soit une erreur de données comme un fractionnement oublié ou une virgule mal placée. Une valeur aberrante est assez éloignée des autres pour exiger une décision : la garder, la corriger ou la retirer. Ne la supprimez jamais simplement parce qu'elle gêne, car les vrais chocs sont précisément ce que les modèles de risque doivent voir. La première étape est toujours de les rendre visibles avec une règle explicite et reproductible.

La règle du z-score, et pourquoi elle peut échouer

La règle classique signale les valeurs situées à plus de 2 ou 3 écarts types de la moyenne : z = (x - mean) / std. Elle est simple et fonctionne sur des données propres. Mais la valeur aberrante gonfle elle-même la moyenne et surtout l'écart type, et elle se cache donc de la règle. Dans l'exemple ci-dessous, la journée à +30 % n'obtient qu'un z-score de 2,85 et, avec un seuil de 3, elle ne serait pas signalée. Plus l'erreur est extrême, plus elle se protège.

import pandas as pd

returns = pd.Series([0.004, -0.002, 0.001, 0.003, -0.004, 0.002, 0.30, -0.001, 0.0, 0.002])

z = (returns - returns.mean()) / returns.std()
print(z.round(2).tolist())
# [-0.28, -0.34, -0.31, -0.29, -0.36, -0.3, 2.85, -0.33, -0.32, -0.3]

print(returns.mean().round(4), returns.std().round(4))   # 0.0305 0.0947   one day moved the mean tenfold
print(returns.drop(6).std().round(4))                    # 0.0026          the std without that day

Règles robustes : IQR et MAD

Les statistiques robustes sont à peine déplacées par les valeurs aberrantes qu'elles traquent. L'écart interquartile IQR = Q3 - Q1 mesure la dispersion de la moitié centrale des données, et les valeurs au-delà de Q1 - 1.5 * IQR ou Q3 + 1.5 * IQR sont hors des bornes d'une boîte à moustaches. Le MAD (écart absolu médian) remplace l'écart type par une médiane, et le z-score modifié 0.6745 * (x - median) / MAD se lit comme un z-score normal (3,5 est un seuil courant). Les deux repèrent sans peine la journée à +30 %.

# The fences of a box plot
q1, q3 = returns.quantile(0.25), returns.quantile(0.75)
spread = q3 - q1
lower, upper = q1 - 1.5 * spread, q3 + 1.5 * spread
print(round(lower, 4), round(upper, 4))                    # -0.006 0.008
print(returns[(returns < lower) | (returns > upper)].to_dict())   # {6: 0.3}

# The modified z-score, built on the median
median = returns.median()
mad = (returns - median).abs().median()
modified_z = 0.6745 * (returns - median) / mad
print(modified_z.round(1).tolist())   # [0.8, -1.2, -0.2, 0.5, -1.9, 0.2, 100.7, -0.8, -0.5, 0.2]

Winsorisation : plafonner plutôt que supprimer

Supprimer une ligne supprime aussi l'information de ses autres colonnes et casse l'espacement des dates. La winsorisation garde toutes les lignes et ramène les valeurs extrêmes à des quantiles choisis, par exemple ceux de 1 % et 99 % : tout ce qui est sous le quantile à 1 % devient ce quantile, tout ce qui est au-dessus de celui à 99 % devient celui-ci. Avec pandas, c'est clip(lower, upper) avec les bornes tirées de quantile. La forme de la série subsiste et les extrêmes perdent leur emprise sur les moyennes et les régressions.

low, high = returns.quantile(0.1), returns.quantile(0.9)
print(round(low, 4), round(high, 4))             # -0.0022 0.0336

clipped = returns.clip(lower=low, upper=high)
print(clipped.round(4).tolist())
# [0.004, -0.002, 0.001, 0.003, -0.0022, 0.002, 0.0336, -0.001, 0.0, 0.002]
#                                 ^ raised                  ^ the 30% day pulled back to the 90% quantile
# Flag with a rule, then look at what is flagged before deciding anything
mask = z.abs() > 2
print(returns[mask].to_dict())                 # {6: 0.3}        the +30% day
print(returns[~mask].std().round(4))           # 0.0026          the volatility of the remaining days
Décidez avec une règle, et notez-laUne règle de valeurs aberrantes est un choix de modélisation, pas un fait. Vérifiez ce que sont les jours signalés (une nouvelle, un fractionnement, un mauvais tick) avant de modifier quoi que ce soit, gardez la colonne d'origine et notez le seuil employé. Un autre seuil donne un autre backtest.
  • La règle du z-score abs((x - mean) / std) > k est simple, mais les valeurs aberrantes gonflent la moyenne et l'écart type et peuvent se cacher.
  • Les règles robustes utilisent quantiles et médianes : bornes IQR (Q1 - 1.5 IQR, Q3 + 1.5 IQR) et z-score modifié fondé sur le MAD.
  • clip(lower, upper) avec des bornes en quantiles (winsorisation) garde les lignes ; ne supprimez jamais un choc sans savoir ce que c'est.

À suivre : les opérations sur titres, les fractionnements et dividendes qui rendent un ancien prix incomparable avec celui d'aujourd'hui.

Lab : ce qui est noté

  • Repérer les rendements éloignés avec zscore_outliers25 pts
  • Calculer les bornes de la boîte à moustaches avec iqr_bounds25 pts
  • Repérer les valeurs aberrantes avec un score robuste grâce à mad_outliers25 pts
  • Dompter les extrêmes avec winsorize25 pts
Explorateur · lab-49-1
▾ lab-49-1
…
bash · dans le navigateur
learner@quantcraft:~/lab-49-1$
Python pas encore chargéPythonLn 1, Col 1Spaces: 4UTF-8

Réinitialiser ce sujet ?

Tout le contenu de lab-49-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