σμ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.14·~40 minIntermédiaire

Fenêtres glissantes

Position dans le parcoursModule 2 sur 11 · Sujet 14 sur 20
2.14 Fenêtres glissantes
—Score du lab— / 100XP total—Série—

Une fenêtre qui glisse

Le prix du jour signifie peu à lui seul ; les traders le comparent au passé récent. Une fenêtre glissante garde les n dernières observations et avance d'un pas à la fois, si bien que chaque date reçoit une statistique calculée sur son propre passé récent : une moyenne mobile, une volatilité récente, un plus haut récent. Avec pandas, series.rolling(n) décrit la fenêtre et une statistique comme .mean() la calcule à chaque position d'un coup.

import pandas as pd

prices = pd.Series(
    [100.0, 102, 101, 105, 107, 106, 110],
    index=pd.bdate_range("2026-03-02", periods=7),
    name="close",
)

moving_average = prices.rolling(3).mean()
print(moving_average.round(2).tolist())
# [nan, nan, 101.0, 102.67, 104.33, 106.0, 107.67]

print(prices.rolling(3).max().tolist())   # [nan, nan, 102.0, 105.0, 107.0, 107.0, 110.0]   the 3-day high

Le début de la série

Les n - 1 premières positions n'ont pas encore de fenêtre complète, le résultat y est donc NaN. C'est honnête : la moyenne de deux prix n'est pas une moyenne sur 3 jours. Si vous voulez vraiment une valeur plus tôt, min_periods=1 calcule avec ce qui est disponible, et expanding() utilise tout depuis le début au lieu d'une fenêtre fixe. Les statistiques qui demandent plusieurs points, comme l'écart type, restent NaN tant qu'il n'y a pas assez d'observations. Une série qui commence déjà par NaN, comme les rendements simples, repousse d'un cran la première fenêtre valide.

# min_periods=1: use whatever is available at the start
print(prices.rolling(3, min_periods=1).mean().round(2).tolist())   # [100.0, 101.0, 101.0, 102.67, 104.33, 106.0, 107.67]

# expanding(): everything since the first day, not a fixed window
print(prices.expanding().mean().round(2).tolist())   # [100.0, 101.0, 101.0, 102.0, 103.0, 103.5, 104.43]

# Rolling volatility of the returns: the first return is NaN, so three returns exist only from the 4th day
returns = prices.pct_change()
print(returns.rolling(3).std().round(4).tolist())   # [nan, nan, nan, 0.0249, 0.0248, 0.0246, 0.0237]

Des fenêtres aux signaux

Les statistiques glissantes deviennent des signaux de trading quand on les compare. Un z-score dit à quelle distance le prix du jour se trouve de sa propre moyenne récente, en unités de son écart type récent : (price - rolling mean) / rolling std. Construisez l'objet glissant une fois (r = prices.rolling(3)) et utilisez-le deux fois. Un croisement de moyennes mobiles compare une moyenne rapide à une lente : tant que la rapide est au-dessus, la tendance récente est haussière. Une comparaison donne des booléens, et astype("int64") les transforme en signal 0/1. Les comparaisons avec NaN valent False, donc le signal vaut 0 tant que les deux moyennes n'existent pas.

# z-score of each price against its own 3-day window
rolling = prices.rolling(3)
zscore = (prices - rolling.mean()) / rolling.std()
print(zscore.round(2).tolist())   # [nan, nan, 0.0, 1.12, 0.87, 0.0, 1.12]
# Moving-average crossover: 1 while the 2-day average is above the 4-day average
fast = prices.rolling(2).mean()
slow = prices.rolling(4).mean()
signal = (fast > slow).astype("int64")
print(signal.tolist())   # [0, 0, 0, 1, 1, 1, 1]

# Trade on the next day: the signal of today's close cannot be traded before tomorrow
print(signal.shift(1).fillna(0).astype("int64").tolist())   # [0, 0, 0, 0, 1, 1, 1]
Ne regardez pas dans le futurUne fenêtre glissante se termine à la ligne courante : elle n'utilise que le passé. center=True place la ligne courante au milieu de la fenêtre et utilise donc des prix futurs : c'est superbe sur un graphique et impossible à trader. Un signal calculé sur la clôture du jour doit être appliqué demain, d'où shift(1).
  • series.rolling(n).mean() (ou std(), max()...) calcule une statistique sur les n dernières observations à chaque position.
  • Les n - 1 premières entrées sont NaN ; min_periods et expanding() changent cela volontairement.
  • Les z-scores et les croisements de moyennes mobiles transforment les fenêtres en signaux ; décalez un signal avant de l'exploiter et évitez center=True.

À suivre : la gestion des données manquantes, que faire des valeurs NaN que contiennent toujours les vrais tableaux de prix.

Lab : ce qui est noté

  • Lisser les prix avec moving_average25 pts
  • Mesurer le risque récent avec rolling_volatility25 pts
  • Standardiser chaque prix par rapport à sa fenêtre avec rolling_zscore25 pts
  • Transformer deux moyennes en signal avec ma_crossover25 pts
Explorateur · lab-47-1
▾ lab-47-1
…
bash · dans le navigateur
learner@quantcraft:~/lab-47-1$
Python pas encore chargéPythonLn 1, Col 1Spaces: 4UTF-8

Réinitialiser ce sujet ?

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