09 · Validation statistique¶
Le chapitre Données historiques et backtest donnait les sept biais à éviter. Celui-ci donne la méthodologie qui permet de prouver qu'il reste quelque chose une fois les biais retirés.
Elle vient essentiellement des travaux de Marcos López de Prado. Le fil rouge : les recettes standard de validation supposent des observations indépendantes et identiquement distribuées. Les données financières ne le sont pas. Les recettes standard y sont donc fausses par construction, pas approximativement justes.
Pourquoi ce chapitre est indispensable ici
Le chapitre Ce que dit la recherche établit qu'aucun motif d'order flow n'a de validation publiée. Vous devez donc produire la vôtre. Une validation faite avec les mauvais outils statistiques ne vaut pas mieux que pas de validation — elle vaut moins, car elle donne confiance.
Le code Python pur de toutes les formules de ce chapitre est en
annexe, fichier metrics.py, testé.
Étiqueter : la triple barrière¶
La méthode naïve étiquette un signal par « le prix a-t-il monté après N barres ? ». Elle ignore le chemin parcouru : un vrai trade touche son stop bien avant l'échéance.
La triple barrière pose trois lignes autour de chaque entrée :
| Barrière | Rôle |
|---|---|
| Haute | Objectif de gain, souvent proportionnel à la volatilité récente |
| Basse | Stop |
| Verticale | Limite de temps |
Le label est donné par la première barrière touchée : \(+1\), \(-1\), ou \(0\) à l'échéance.
def triple_barrier(prices, entry_idx, side, tp_ticks, sl_ticks, max_bars,
tick_size):
entry = prices[entry_idx][0] if side > 0 else prices[entry_idx][1]
tp = entry + side * tp_ticks * tick_size
sl = entry - side * sl_ticks * tick_size
mfe = mae = 0.0
for i in range(entry_idx + 1, min(entry_idx + 1 + max_bars, len(prices))):
hi, lo = prices[i]
fav = (hi - entry) if side > 0 else (entry - lo)
adv = (entry - lo) if side > 0 else (hi - entry)
mfe = max(mfe, fav / tick_size)
mae = max(mae, adv / tick_size)
touched_sl = (lo <= sl) if side > 0 else (hi >= sl)
touched_tp = (hi >= tp) if side > 0 else (lo <= tp)
# Conservateur : si les deux barrières sont touchées dans la même barre,
# on suppose le stop d'abord.
if touched_sl:
return -1, i - entry_idx, mfe, mae
if touched_tp:
return 1, i - entry_idx, mfe, mae
return 0, min(max_bars, len(prices) - 1 - entry_idx), mfe, mae
L'hypothèse conservatrice sur la barre ambiguë
Quand une barre touche le stop et l'objectif, on ne sait pas lequel est venu en premier sans données intrabarre. Supposer le stop sous-estime la stratégie. C'est la seule direction d'erreur acceptable.
Sur des barres de volume courtes, ce cas est rare. Sur des barres de 5 minutes, il est fréquent — raison de plus pour travailler en barres de volume, comme recommandé au chapitre Delta, footprint et CVD.
Des barrières trop serrées n'étiquettent que du bruit
Si la distance au stop est du même ordre que le grésillement normal des prix, les labels ne portent aucune information. Calibrez les barrières sur la volatilité récente, pas sur un nombre de ticks fixe.
La fonction retourne aussi la MFE et la MAE, qui servent directement à calibrer objectif et stop — voir Métriques de l'edge.
Le méta-étiquetage¶
Architecture en deux étages, particulièrement adaptée à l'order flow :
Modèle primaire → direction (acheter/vendre)
règle codée, réglée pour être généreuse
↓
Modèle secondaire → prendre ce signal ? à quelle taille ?
apprend à prédire si le signal primaire sera gagnant
On remplace un problème très dur — prédire le marché — par un problème plus facile : trier les signaux d'une règle existante. La probabilité fournie par le méta-modèle sert directement à doser la taille de la position.
C'est le pont naturel entre ce rapport et l'apprentissage automatique
Vos motifs d'order flow (absorption, imbalances empilées) sont le modèle primaire. Ils sont déjà écrits, testés et interprétables. Le méta-modèle apprend le contexte dans lequel ils fonctionnent — heure, volatilité, régime, distance au VWAP — c'est-à-dire exactement les filtres du chapitre Filtres de contexte et risque, mais appris plutôt que devinés.
C'est nettement plus prometteur que d'entraîner un réseau à prédire le prix à partir du carnet brut, et cela reste explicable.
Le méta-modèle hérite des défauts du primaire
Il doit être validé avec la même rigueur, et le filtrage a un coût : on renonce à certains trades gagnants pour éviter beaucoup de perdants. Ce compromis se mesure, il ne se suppose pas.
Valider : purge, embargo, CPCV¶
La validation croisée standard découpe les données en \(k\) blocs et suppose les observations indépendantes. En finance, c'est faux : un label triple-barrière s'étale sur plusieurs barres, donc deux observations voisines partagent de l'information. Le modèle est testé sur des données qu'il a partiellement vues.
Trois correctifs, cumulatifs :
| Correctif | Ce qu'il fait |
|---|---|
| Purge | Retire de l'entraînement toute observation dont le label chevauche la période de test |
| Embargo | Retire en plus une fenêtre juste après le bloc de test (~1 % des données) |
| CPCV | Génère toutes les combinaisons de blocs entraînement/test, purge et embargo à chaque fois |
Le gain du CPCV : on obtient une distribution de la performance sur de nombreux chemins de backtest, au lieu d'un chiffre unique facile à sur-optimiser.
L'ordre de grandeur de l'erreur
Un modèle testé en validation croisée naïve peut afficher 58 % de prédictions correctes et tomber à 51 % avec purge et embargo. Les 7 points d'écart ne sont pas du talent : c'est de l'information qui fuit des blocs d'entraînement vers le bloc de test.
Sept points, sur un signal d'order flow, c'est la différence entre une stratégie rentable et une stratégie qui paie des commissions.
Implémentation : purgedcv, compatible scikit-learn.
Juger : le Sharpe dégonflé¶
C'est le cœur du chapitre, et le point le moins connu des praticiens.
Le problème¶
La recherche elle-même gonfle le ratio de Sharpe. Testez 500 variantes d'une stratégie sur le même historique, et la meilleure affichera un Sharpe impressionnant même si toutes sont sans valeur. C'est le problème des tests multiples.
Le Sharpe maximal attendu par pur hasard¶
| Symbole | Signification |
|---|---|
| \(N\) | nombre d'essais effectués pendant la recherche |
| \(V[SR]\) | variance des Sharpe observés sur l'ensemble des essais |
| \(\gamma\) | constante d'Euler-Mascheroni, \(\approx 0{,}5772\) |
| \(\Phi^{-1}\) | quantile de la loi normale centrée réduite |
Valeurs calculées par metrics.py, avec
\(V[SR] = 0{,}5\) :
| Essais | Sharpe attendu par hasard |
|---|---|
| 10 | 1,11 |
| 50 | 1,61 |
| 100 | 1,79 |
| 500 | 2,16 |
| 1 000 | 2,30 |
| 5 000 | 2,61 |
Lisez ce tableau attentivement
Après 1 000 essais, un Sharpe de 2,30 est le résultat attendu du pur hasard. Si votre meilleure variante affiche 2,0 après avoir balayé une grille de paramètres, vous n'avez rien trouvé — vous avez trouvé moins que ce que le hasard produit.
Un balayage de grille sur 5 paramètres à 6 valeurs chacun, c'est 7 776 essais. Personne ne compte ses essais honnêtement, et c'est précisément pour cela que la plupart des backtests publiés ne valent rien.
PSR et DSR¶
Le Probabilistic Sharpe Ratio est la probabilité que le vrai Sharpe dépasse un seuil, en tenant compte de la longueur de l'historique et de la forme des rendements :
où \(\hat\gamma_3\) est l'asymétrie et \(\hat\gamma_4\) l'aplatissement des rendements.
Le Deflated Sharpe Ratio est simplement le PSR dont le seuil \(SR^{*}\) est le Sharpe maximal attendu par hasard, calculé ci-dessus.
def deflated_sharpe(returns, n_trials, var_sharpe):
"""Compter TOUS les essais, brouillons compris."""
return probabilistic_sharpe(returns,
expected_max_sharpe(n_trials, var_sharpe))
Le seul paramètre difficile est le comptage des essais
Ne comptez pas seulement les variantes « sérieuses ». Chaque combinaison de paramètres regardée compte, y compris celles jetées après trois secondes.
Conséquence pratique : tenez un compteur d'essais dans votre code de recherche, incrémenté automatiquement à chaque backtest lancé. C'est cinq lignes, et c'est la seule façon d'avoir un chiffre honnête. Le compter de mémoire à la fin donne systématiquement un chiffre trop bas d'un ordre de grandeur.
La longueur minimale d'historique¶
Corollaire brutal de la formule précédente : avec assez d'essais sur un historique court, on trouve toujours une stratégie au Sharpe séduisant.
Années d'historique nécessaires, calculées :
| Essais | SR visé 1,0 | SR visé 1,5 | SR visé 2,0 |
|---|---|---|---|
| 10 | 2,5 ans | 1,1 an | 0,6 an |
| 50 | 5,2 ans | 2,3 ans | 1,3 an |
| 100 | 6,4 ans | 2,8 ans | 1,6 an |
| 500 | 9,3 ans | 4,1 ans | 2,3 ans |
| 1 000 | 10,6 ans | 4,7 ans | 2,6 ans |
Confrontation avec la réalité de ce rapport
Vous disposez au mieux de quelques mois de données enregistrées, ou d'un an de MBO Databento. Pour qu'un Sharpe annuel de 1,5 soit crédible sur 2,3 ans d'historique, vous ne pouvez pas dépasser 50 essais.
Cela impose une discipline très inhabituelle : formuler peu d'hypothèses, et les tester une fois. Le balayage de grille est structurellement incompatible avec la quantité de données dont dispose un retail sur les futures.
C'est probablement la contrainte la plus sous-estimée de tout ce rapport.
Les autres garde-fous¶
Probability of Backtest Overfitting (PBO)¶
La probabilité que la configuration choisie comme meilleure sur le passé soit en réalité sous la médiane sur données nouvelles. Elle se calcule par une variante combinatoire de la validation croisée.
Un PBO au-delà d'environ 50 % signifie que votre sélection n'a rien capturé de réel.
White's Reality Check et SPA test¶
Deux tests formels du problème des tests multiples, par rééchantillonnage (bootstrap) :
- Reality Check (White, 2000) : la meilleure règle d'un univers bat-elle la référence, compte tenu de tout l'univers testé ?
- SPA test (Hansen, 2005) : version plus puissante, moins faussée quand l'univers contient des règles franchement mauvaises.
Appliquer le test aux seules survivantes annule le test
Gardez la trace des 300 combinaisons essayées, pas seulement des 5 survivantes. C'est précisément le biais que le test est censé corriger.
Poids d'échantillons¶
Quand les labels triple-barrière se chevauchent, les exemples se répètent partiellement. On pondère chaque exemple par son unicité : la part de la période qu'il est seul à couvrir.
Pourquoi c'est important en order flow
Vos signaux se déclenchent en grappes : plusieurs absorptions dans la même demi-heure de marché agité. Sans pondération, le modèle voit dix exemples indépendants alors qu'il n'y a qu'un seul épisode de marché. Il sur-apprend cet épisode, et ses probabilités deviennent mal calibrées — un « 70 % de confiance » qui n'en vaut que 55.
Le pipeline complet¶
1. Étiqueter en triple barrière
2. Pondérer par unicité
3. Préparer des features stationnaires
4. Valider en CPCV (purge + embargo)
5. Filtrer par méta-étiquetage
6. Juger au DSR, PBO et SPA
avec comptage HONNÊTE des essais
C'est lourd. C'est le prix d'un avantage réel — et c'est la seule réponse sérieuse au fait qu'aucun motif d'order flow n'a de validation publiée.
La version allégée, pour commencer
Si ce pipeline vous paraît hors de portée, faites au minimum ceci, qui couvre l'essentiel du risque :
- Comptez vos essais (compteur automatique).
- Étiquetez en triple barrière plutôt qu'en rendement à horizon fixe.
- Calculez le DSR avec le nombre d'essais réel.
- Comparez à un tirage aléatoire de même distribution horaire (chapitre Backtest).
Ces quatre étapes tiennent dans metrics.py plus vingt lignes, et elles
éliminent la grande majorité des faux positifs.
Résumé¶
- Les recettes de validation standard sont fausses par construction sur données financières.
- La triple barrière étiquette ce qu'un vrai trade aurait vécu, stop compris.
- Le méta-étiquetage est le pont naturel entre motifs d'order flow et apprentissage automatique.
- Purge + embargo peuvent retirer 7 points de précision apparente.
- Après 1 000 essais, un Sharpe de 2,30 est le résultat attendu du hasard.
- Avec 2 ans de données, vous ne pouvez pas dépasser ~50 essais : le balayage de grille est incompatible avec vos données.
- Version minimale : compter les essais, triple barrière, DSR, test du hasard.
Chapitre précédent : La boîte à outils · Partie suivante : Économie