σμ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.8·~1 hAvancé

Pipelines scikit-learn pour les rendements

Position dans le parcoursModule 8 sur 11 · Sujet 8 sur 10
8.8 Pipelines scikit-learn pour les rendements
—Score du lab— / 100XP total—Série—

L'apprentissage automatique sur les rendements

L'apprentissage supervisé ajuste une fonction des variables X vers une cible y. En finance, la cible est typiquement un rendement futur et les variables sont des choses connues avant : rendements passés, volatilité, volumes. scikit-learn offre une interface uniforme : chaque modèle a fit(X, y) et predict(X). Une différence clé avec les problèmes de manuel est le rapport signal/bruit : les rendements sont presque du pur bruit, donc même un bon modèle n'en explique qu'une infime part, et une mise en place négligente trouve des régularités qui n'existent pas.

import numpy as np

returns = np.array([0.01, -0.02, 0.015, 0.005, -0.01, 0.02])
n_lags = 2

# Row i holds the two returns before the target; y[i] is the return that follows them
windows = np.lib.stride_tricks.sliding_window_view(returns, n_lags)
X, y = windows[:-1], returns[n_lags:]
print(X)
print(y)                                             # the targets are one step ahead of the features

Des variables à partir des retards

Les variables les plus simples d'un rendement sont les rendements qui le précèdent. Construisez une matrice de retards : la ligne i contient returns[i : i + n_lags], et la cible est returns[i + n_lags], la valeur suivante. Une vue en fenêtre glissante la donne sans boucle. Tout ce qui figure dans une ligne doit être connu au moment de la prévision, c'est la règle d'anticipation appliquée à un modèle : une variable décalée d'une position dans le mauvais sens laisserait le modèle voir la réponse.

import numpy as np
from sklearn.linear_model import Ridge
from sklearn.pipeline import Pipeline
from sklearn.preprocessing import StandardScaler

# Pipeline: scale each feature, then a ridge regression (a linear model with a penalty on large coefficients)
pipe = Pipeline([("scale", StandardScaler()), ("model", Ridge(alpha=10.0))])
print([name for name, _ in pipe.steps])

rng = np.random.default_rng(1)
returns = np.zeros(400)
noise = rng.normal(0, 0.01, size=400)
for t in range(1, 400):
    returns[t] = 0.3 * returns[t - 1] + noise[t]                    # a series with a little memory

windows = np.lib.stride_tricks.sliding_window_view(returns, 3)
X, y = windows[:-1], returns[3:]
pipe.fit(X[:300], y[:300])                                          # fit on the first 300 rows
print(np.round(pipe.named_steps["model"].coef_, 4))                 # the last lag weighs most

Pipelines et fuite d'information

Un Pipeline regroupe le normalisateur et le modèle de sorte que fit apprend les statistiques de mise à l'échelle (moyenne et écart-type de chaque variable) sur les seules lignes d'entraînement et que predict les applique telles quelles aux nouvelles lignes. Normaliser tout le jeu de données avant de le découper ferait fuir moyenne et dispersion de la période de test dans l'entraînement : le même biais d'anticipation, à un nouvel endroit. Dans une série temporelle, le découpage est aussi chronologique : entraîner sur les premières lignes, tester sur les suivantes, jamais un tirage aléatoire.

import numpy as np
from sklearn.linear_model import Ridge
from sklearn.pipeline import Pipeline
from sklearn.preprocessing import StandardScaler

def build_pipeline(alpha):
    return Pipeline([("scale", StandardScaler()), ("model", Ridge(alpha=alpha))])

rng = np.random.default_rng(1)
returns = np.zeros(600)
noise = rng.normal(0, 0.01, size=600)
for t in range(1, 600):
    returns[t] = 0.3 * returns[t - 1] + noise[t]

windows = np.lib.stride_tricks.sliding_window_view(returns, 3)
X, y = windows[:-1], returns[3:]
model = build_pipeline(10.0).fit(X[:400], y[:400])                  # chronological split: early rows to train
pred = model.predict(X[400:])                                       # later rows to test
print(round(float(np.mean(np.sign(y[400:]) == np.sign(pred))), 3))  # direction accuracy on unseen data

Juger le résultat honnêtement

Pour un signal de trading, la direction compte souvent le plus, donc un score simple est la précision directionnelle : la part des jours où le rendement prévu et le rendement réel ont le même signe. 50 % équivaut à pile ou face. Les vrais prédicteurs de rendements atteignent 51 à 53 %, et 60 % sur des données de test est plus probablement une fuite qu'une découverte. Comparez aux références triviales (toujours prévoir à la hausse, prévoir zéro) avant de croire quoi que ce soit.

import numpy as np

y_true = np.array([0.01, -0.02, 0.015, -0.005, 0.02, -0.01])
y_pred = np.array([0.004, 0.001, 0.002, -0.001, 0.003, 0.002])        # the model is right on 4 of 6 signs

accuracy = float(np.mean(np.sign(y_true) == np.sign(y_pred)))
always_up = float(np.mean(np.sign(y_true) == 1))                      # the trivial baseline: always predict a rise
print(round(accuracy, 3), round(always_up, 3))                        # compare the model with the baseline
Des modèles simples, une validation honnêteAvec autant de bruit, les modèles linéaires régularisés (ridge, lasso) battent généralement les modèles flexibles comme les arbres profonds ou les réseaux de neurones, qui mémorisent la période d'entraînement. La pénalité alpha est un réglage à ajuster, mais sur une période de validation, jamais sur la période de test. Les sujets suivants construisent les variables et le test glissant (walk-forward) qui rendent cela honnête.
  • Une matrice de retards (sliding_window_view) donne des variables à partir des rendements précédant chaque cible ; tout ce qui est dans une ligne doit être connu au moment de la prévision.
  • Un Pipeline([("scale", StandardScaler()), ("model", Ridge(alpha=alpha))]) apprend la mise à l'échelle sur les seules lignes d'entraînement ; découpez chronologiquement, jamais au hasard.
  • Notez la direction (part de signes égaux) face à des références triviales ; 51 à 53 % est réaliste, 60 % suggère une fuite ; les modèles linéaires régularisés conviennent aux données bruitées.

À suivre : l'ingénierie de variables pour les données financières, construire de meilleures entrées que les rendements retardés bruts.

Lab : ce qui est noté

  • Transformer une série en variables avec lag_matrix25 pts
  • Enchaîner les étapes avec build_pipeline25 pts
  • Entraîner sur le passé, prévoir la suite avec fit_predict25 pts
  • Noter les directions avec direction_accuracy25 pts
Explorateur · lab-109-1
▾ lab-109-1
…
bash · dans le navigateur
learner@quantcraft:~/lab-109-1$
Python pas encore chargéPythonLn 1, Col 1Spaces: 4UTF-8

Réinitialiser ce sujet ?

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