σμ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 8 · Modèles stochastiques et apprentissage automatique›Sujet 8.9·~1 hAvancé

Ingénierie des variables pour les données financières

Position dans le parcoursModule 8 sur 11 · Sujet 9 sur 10
8.9 Ingénierie des variables pour les données financières
—Score du lab— / 100XP total—Série—

Donner au modèle des entrées comparables

Un prix de 250 ne signifie rien pour un modèle : il dépend de l'action et tend à monter avec le temps, si bien que les valeurs de 2020 ne ressemblent jamais à celles de 2010. L'ingénierie de variables transforme les données brutes en entrées comparables dans le temps et entre actifs : des rendements plutôt que des prix, de la volatilité plutôt que des étendues, des scores standardisés plutôt que des niveaux. De bonnes variables comptent plus dans le résultat que le choix du modèle, et chacune ne doit décrire que ce qui était connu le jour même.

import numpy as np

prices = np.array([100.0, 101.0, 99.0, 102.0, 104.0, 103.0, 106.0])

window = 3
momentum = np.full(len(prices), np.nan)
momentum[window:] = prices[window:] / prices[:-window] - 1          # the return over the last 3 days
print(np.round(momentum, 4))                                        # nan for the first 3 days: no price 3 days back

Des fenêtres qui regardent en arrière

La plupart des variables sont des statistiques d'une fenêtre glissante arrière : les n dernières valeurs jusqu'à aujourd'hui inclus. Le momentum compare aujourd'hui à il y a n jours ; la volatilité glissante est l'écart-type des n derniers rendements ; le z-score glissant dit de combien d'écarts-types de la fenêtre aujourd'hui s'écarte de la moyenne de la fenêtre. Avec sliding_window_view, on obtient toutes les fenêtres comme lignes d'une matrice et on calcule la statistique le long d'un axe, sans boucle. Les n - 1 premiers jours n'ont pas de fenêtre complète et restent nan.

import numpy as np

prices = np.array([100.0, 101.0, 99.0, 102.0, 104.0, 103.0, 106.0])
returns = np.diff(prices) / prices[:-1]
window = 3

windows = np.lib.stride_tricks.sliding_window_view(returns, window)        # every 3-day window as a row
vol = np.full(len(returns), np.nan)
vol[window - 1:] = windows.std(axis=1, ddof=1)
print(np.round(vol, 4))                                                    # nan nan then one value per complete window

p_windows = np.lib.stride_tricks.sliding_window_view(prices, window)
z = np.full(len(prices), np.nan)
z[window - 1:] = (prices[window - 1:] - p_windows.mean(axis=1)) / p_windows.std(axis=1, ddof=1)
print(np.round(z, 3))                                                      # how unusual each price is for its last 3 days
# Features put prices of different stocks on the same footing
import numpy as np

cheap = np.array([5.0, 5.2, 5.1, 5.4, 5.5])
dear = np.array([500.0, 520.0, 510.0, 540.0, 550.0])           # ten times the price, the same movements
print(cheap[-1] / cheap[-3] - 1, dear[-1] / dear[-3] - 1)        # identical momentum, about 0.0784 for both
print(dear[-1] - dear[-3], cheap[-1] - cheap[-3])                # price changes (40.0 and 0.4) would not be comparable

La cible regarde en avant

Ce que le modèle prédit est la cible, généralement le rendement sur les h jours suivants : prices[t + h] / prices[t] - 1. C'est la seule colonne autorisée à regarder en avant, et elle vaut nan pour les h derniers jours car l'avenir n'a pas eu lieu ; supprimez ces lignes avant l'entraînement. Une erreur fréquente est une variable qui contient la cible par accident, comme une fenêtre centrée, une statistique calculée sur toutes les données, ou un rendement futur réutilisé en entrée. Le test est simple : une variable au jour t ne doit pas changer quand on ajoute des jours ultérieurs.

import numpy as np

def forward_return(prices, horizon):
    out = np.full(len(prices), np.nan)
    out[:-horizon] = prices[horizon:] / prices[:-horizon] - 1
    return out

def momentum(prices, window):
    out = np.full(len(prices), np.nan)
    out[window:] = prices[window:] / prices[:-window] - 1
    return out

prices = np.array([100.0, 101.0, 99.0, 102.0, 104.0, 103.0, 106.0])
print(np.round(momentum(prices, 2), 4))                 # looks back: known on each day
print(np.round(forward_return(prices, 2), 4))           # looks forward: the target, nan for the last 2 days

# The prefix test: a feature computed on the first 5 days equals the first 5 values of the full computation
print(np.allclose(momentum(prices[:5], 2), momentum(prices, 2)[:5], equal_nan=True))      # True
Méfiez-vous des variables qui sont secrètement la cibleUne variable qui prédit trop bien est un signal d'alarme. Un momentum mesuré avec une fenêtre centrée, une volatilité incluant le rendement de demain, un z-score standardisé sur tout l'échantillon : chacun fait briller les backtests et échoue en réel. Construisez chaque variable de façon qu'elle puisse être calculée en production le matin du jour t, et vérifiez-la avec le test du préfixe ci-dessus.
  • Transformez les prix en variables comparables : momentum prices[t] / prices[t - n] - 1, volatilité glissante, z-score glissant, chacun sur une fenêtre arrière incluant aujourd'hui.
  • sliding_window_view donne chaque fenêtre en ligne, donc les statistiques n'ont pas besoin de boucle ; les n - 1 premiers jours restent nan.
  • Le rendement futur prices[t + h] / prices[t] - 1 est la cible, jamais une variable ; vérifiez chaque variable avec le test du préfixe.

À suivre : la validation glissante (walk-forward), tester un modèle dans l'ordre où le temps s'écoule réellement.

Lab : ce qui est noté

  • Mesurer la tendance avec momentum25 pts
  • Suivre le risque récent avec rolling_vol25 pts
  • Dire à quel point aujourd'hui est inhabituel avec rolling_zscore25 pts
  • Définir la cible avec forward_return25 pts
Explorateur · lab-110-1
▾ lab-110-1
…
bash · dans le navigateur
learner@quantcraft:~/lab-110-1$
Python pas encore chargéPythonLn 1, Col 1Spaces: 4UTF-8

Réinitialiser ce sujet ?

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