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 backDes 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 comparableLa 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)) # Truet, 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_viewdonne chaque fenêtre en ligne, donc les statistiques n'ont pas besoin de boucle ; lesn - 1premiers jours restentnan.- Le rendement futur
prices[t + h] / prices[t] - 1est 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