07 · Lire le carnet et le tape¶
Les chapitres précédents traitaient des données agrégées : footprint, delta, OFI. Ce chapitre descend d'un cran, au niveau du carnet vivant et du flux brut de transactions. C'est là que se trouvent les signaux les plus difficiles à automatiser — et les plus difficiles à falsifier par un backtest naïf.
Pourquoi ce chapitre existe
Une bonne partie de la littérature d'order flow — celle des lecteurs de DOM professionnels, école Jigsaw en particulier — décrit des comportements dynamiques du carnet que le footprint ne capture pas. Plusieurs de ces comportements sont pourtant parfaitement calculables. Ce chapitre les formalise.
Le tape est la seule preuve¶
Le DOM affiche des intentions annulables. Le tape n'affiche que ce qui s'est réellement échangé. Toute la hiérarchie de fiabilité en découle :
| Donnée | Fiabilité | Pourquoi |
|---|---|---|
| Transactions exécutées (tape) | Élevée | Un contrat échangé ne s'annule pas |
| Volume par prix (footprint) | Élevée | Dérivé du tape |
| Tailles affichées au carnet | Faible | Annulables, manipulables |
| Évolution des tailles affichées | Moyenne | Le comportement informe plus que la photo |
La règle qui résume tout
Une intention affichée qui ne s'exécute pas n'est pas un signal, c'est du décor. Toute lecture de carnet doit être confirmée par le tape.
Le piège de l'agrégation MDP 3.0¶
C'est un point technique décisif, et presque toujours ignoré des tutoriels.
Depuis le passage au protocole MDP 3.0 en 2015, le CME publie ses transactions sous forme de messages Trade Summary : un message représente l'ensemble des ordres qui se sont appariés contre un seul ordre agressif, et les transactions sont agrégées par niveau de prix. Un même appariement peut de surcroît être réparti sur plusieurs paquets UDP quand il ne tient pas dans un seul (CME Group, MDP 3.0 — Trade Summary).
Conséquences pratiques :
Le tape brut sous-estime les gros ordres
Un ordre agressif de 400 contrats qui consomme 12 ordres passifs peut apparaître, selon le traitement du flux, comme une série de lignes de tailles variées plutôt que comme un unique print de 400.
Un bot qui filtre « transactions ≥ 100 contrats » pour détecter les gros acteurs rate donc une partie de ce qu'il cherche, et ce de façon non aléatoire : il rate d'autant plus que l'ordre était gros et qu'il a traversé plusieurs niveaux.
La correction s'appelle le tape reconstruit : regrouper les exécutions appartenant au même événement d'appariement. Les outils professionnels le font — c'est la fonction phare de Jigsaw daytradr, dont la Reconstructed Tape recolle les exécutions fragmentées à leur taille d'origine (Jigsaw).
Pour un bot, deux voies :
- Utiliser les champs d'agrégation du flux quand ils sont exposés (nombre d'ordres impliqués dans l'appariement) plutôt que de reconstruire à la main.
- Regrouper par fenêtre temporelle très courte (quelques millisecondes) et par sens : approximation grossière mais qui capte l'essentiel.
Vérifiez ce que votre flux vous donne avant de coder
Un bot construit sur l'hypothèse « une ligne du tape = un ordre » produira une distribution des tailles fausse, et donc une détection de « gros acteurs » fausse. C'est le genre d'erreur qui ne se voit jamais dans un backtest, parce que le backtest hérite du même biais.
L'épaississement (thickening up)¶
Le signal le plus intéressant de la lecture de carnet, parce qu'il est mesurable et rarement automatisé.
L'idée : au lieu de regarder les tailles affichées, on compte, tick après tick, combien de contrats il a fallu pour faire céder chaque niveau. Ce nombre n'est pas du « volume vendeur » : c'est la quantité que les acheteurs passifs ont accepté d'absorber avant de céder.
Si la suite \((c_k)\) croît en fin de repli, les acheteurs passifs deviennent plus gourmands : le marché s'épaissit, la baisse s'essouffle.
def thickening(cost_per_level):
"""Retourne la pente moyenne. > 0 = épaississement."""
if len(cost_per_level) < 3:
return 0.0
deltas = [cost_per_level[i] - cost_per_level[i - 1]
for i in range(1, len(cost_per_level))]
return sum(deltas) / len(deltas)
Exemple typique : les ticks baissiers successifs coûtent 37, 45, 69, 72 puis 103 contrats. Puis des vagues de plusieurs centaines de contrats ne font plus céder les niveaux. Le plancher se confirme — sans qu'aucun mur spectaculaire ne soit affiché, et avant que quoi que ce soit n'apparaisse sur un graphique.
Pourquoi c'est intéressant pour un bot
Ce signal ne demande ni MBO, ni faible latence : il se calcule à partir du L1 (meilleur bid/ask et tailles) plus le tape. Son horizon est de plusieurs dizaines de secondes. Il tombe donc exactement dans la fenêtre accessible au retail, décrite au chapitre Frictions, coûts et latence.
N'espérez pas le tick exact
Après l'épaississement, le marché oscille souvent 20 à 30 secondes entre deux niveaux — et redescend parfois d'un ou deux ticks — avant de partir. Un stop d'un tick sera pris. Cette respiration doit être intégrée au dimensionnement du stop, pas combattue.
Les quatre formes de retournement¶
Une classification utile, parce qu'elle borne ce qu'un bot peut espérer détecter :
| Forme | Lisible au carnet ? |
|---|---|
| Épaississement progressif | Oui |
| Épuisement des vendeurs après climax | Partiellement |
| Bascule molle, sans signal | Non |
| Arrivée soudaine d'acheteurs en taille | Oui |
Renoncer à la moitié des retournements est la bonne décision
Deux formes sur quatre sont détectables. Un bot qui essaie d'attraper les quatre finit par se déclencher sur du bruit dans les deux cas où il n'y a rien à voir. Coder explicitement « je ne détecte que ces deux formes » est un choix de conception, pas une limitation subie.
Icebergs et rechargement¶
La signature d'un iceberg¶
Un iceberg est un ordre dont seule une tranche est visible ; chaque exécution recharge automatiquement une nouvelle tranche au même prix.
Signature calculable : le volume traité à un niveau dépasse massivement la taille affichée, sans que l'affichage diminue.
def iceberg_ratio(traded_volume, displayed_size):
"""Un ratio élevé (> 5) est suspect."""
if displayed_size <= 0:
return float("inf") if traded_volume > 0 else 0.0
return traded_volume / displayed_size
Cas typique : 40 contrats affichés au bid, 1 800 contrats exécutés à ce prix, et l'affichage reste à 40. Ratio de 45. Un gros acheteur se remplit tranche par tranche ; tant qu'il recharge, le niveau est un vrai plancher.
La cessation du rechargement¶
C'est le moment de bascule, et il est plus exploitable que la détection de l'iceberg lui-même.
def replenishment_stopped(history, tolerance=0.3):
"""history : tailles affichées après chaque vague de consommation."""
if len(history) < 3:
return False
passe = sorted(history[:-1])
mediane = passe[len(passe) // 2]
return mediane > 0 and history[-1] < tolerance * mediane
L'iceberg s'arrête sans prévenir
Un plancher tenu par un iceberg disparaît d'un coup, pas progressivement. Une stratégie qui s'appuie sur ce plancher doit sortir au moment où le rechargement cesse, pas attendre la confirmation en prix — à ce moment-là, le prix est déjà plusieurs ticks plus bas.
Le retournement après remplissage complet¶
Prolongement contre-intuitif : un iceberg acheteur entièrement rempli prépare souvent le mouvement inverse. Les vendeurs, rassurés par un niveau qui ne cassait pas malgré tout ce qui s'y vendait, se croient en position de force. Quand le prix décolle, leurs rachats en panique et les stops massés au-dessus alimentent une jambe haussière rapide.
L'absorption d'aujourd'hui fabrique le stop run de tout à l'heure. C'est un scénario à surveiller, pas une mécanique automatique.
Un gros acteur ou une foule ?¶
La façon dont un niveau est consommé renseigne sur qui agit :
| Motif | Interprétation |
|---|---|
| Bid balayé d'un seul coup, taille exactement égale à l'affichage, niveau après niveau | Un seul acteur, moyens importants |
| Frappes progressives, paquets irréguliers | Une foule de participants |
Règle empirique des lecteurs de DOM : on trade plus volontiers contre la foule que contre le gros.
La texture des deux sens complète la lecture. Une descente en frappes uniques et rapides suivie d'une remontée lente et graduelle oppose un gros vendeur pressé à des acheteurs dispersés.
Information bonus
Si le prix repasse durablement au-dessus de la zone où un gros vendeur a opéré, ce vendeur devra probablement se racheter. Sa zone devient une cible.
Les rafales (bursts)¶
Une rafale est une salve d'exécutions quasi simultanées. C'est la version mesurable du « sweep » du chapitre Imbalance, absorption, épuisement, et la spécification issue des scanners professionnels est meilleure que la mienne : trois filtres au lieu d'un.
| Filtre | Rôle |
|---|---|
| Volume minimal dans une fenêtre de quelques secondes | Élimine le bruit |
| Delta de la zone | Donne le sens |
| Basis ratio : part de la rafale dans le volume des dernières minutes | Normalise par l'activité du moment |
Le troisième est la vraie trouvaille. Sur ES, un basis ratio au-delà d'environ 10 % est déjà élevé.
def burst(trades, window_ns, volume_recent, min_volume, min_basis_ratio=0.10):
if not trades:
return False, 0, 0, 0.0
span = trades[-1][0] - trades[0][0]
vol = sum(s for _, s, _ in trades)
delta = sum(side * s for _, s, side in trades)
ratio = vol / volume_recent if volume_recent else 0.0
est = (span <= window_ns and vol >= min_volume and ratio >= min_basis_ratio)
return est, vol, delta, ratio
Le basis ratio résout le problème de calibration
Un seuil de volume absolu valable en séance américaine produit des faux signaux en séance européenne, où le volume est bien moindre. Un ratio relatif au volume récent s'adapte tout seul. C'est le même principe que les seuils en percentiles recommandés au chapitre Imbalance, absorption, épuisement, appliqué au flux plutôt qu'au footprint.
Ce qui suit la rafale¶
La rafale n'est pas le signal — la suite l'est :
- rafale au franchissement d'un extrême, puis retour immédiat : chasse aux stops, la liquidité a été prise, le mouvement n'a pas de suite ;
- rafale qui lance une extension avec continuation du flux : vraie cassure.
Après un stop run avorté, la cible naturelle est la zone de valeur (VWAP, POC de séance) — pas un objectif lointain.
La vitesse du tape¶
La vitesse de défilement est une information en soi, et elle se quantifie (transactions par seconde). Un raffinement utile : mesurer séparément la vitesse des achats et des ventes.
| Observation | Lecture |
|---|---|
| Cassure + accélération du tape | Cassure crédible |
| Cassure sans accélération | Personne ne suit, méfiance |
| Prix qui monte, tape qui ralentit | Fin de course |
| Accélération des deux côtés, prix immobile | Bataille sur place = absorption |
Chaque instrument a sa vitesse normale
Elle est plus lente à midi et avant les week-ends. C'est l'écart à cette base qui informe, pas le chiffre brut. Comme partout dans ce rapport : le seuil doit être relatif à une distribution récente du même créneau horaire.
Ce qui invalide chaque lecture¶
Tableau de synthèse — à implémenter comme conditions de sortie, pas comme commentaires :
| Lecture | Invalidée quand |
|---|---|
| Rejet attendu sur un mur affiché | Le mur est retiré à l'approche sans avoir traité (leurre) |
| Plancher tenu par un iceberg | Le rechargement cesse — sortir avant la confirmation en prix |
| Niveau qui se recharge | La vague où plus rien ne revient : la cassure devient dominante |
| Absorption au carnet | Le prix franchit le niveau absorbé en clôture |
| Épaississement | La suite \((c_k)\) cesse de croître, ou un niveau saute sur une frappe unique en taille |
| Rejet par absence d'acheteurs | Au contact suivant, les acheteurs traitent en taille dans l'extrême |
| Toute lecture de carnet | Le tape ne confirme pas |
L'invalidation est un comportement daté, pas un prix
C'est la différence entre ce chapitre et les précédents. Ailleurs, le stop est un niveau de prix. Ici, l'invalidation est un événement observable : le mur tombe, le rechargement cesse, la suite cesse de croître.
Un bot peut surveiller ces événements ; il sort alors sur information, pas sur perte. C'est structurellement supérieur — à condition d'écrire la condition avant l'entrée.
Résumé¶
- Le tape est la seule preuve ; le carnet affiche des intentions annulables.
- MDP 3.0 agrège les transactions : le tape brut sous-estime les gros ordres, de façon non aléatoire.
- L'épaississement se calcule en L1 + tape, sur un horizon compatible avec la latence retail. C'est le signal de carnet le plus intéressant pour un bot.
- Deux formes de retournement sur quatre sont détectables : renoncer aux deux autres est un choix, pas une limitation.
- La cessation du rechargement est plus exploitable que la détection d'iceberg.
- Le basis ratio résout la calibration des rafales entre sessions.
- Chaque lecture a une invalidation qui est un événement, pas un prix.
Chapitre précédent : Frictions, coûts et latence · Partie suivante : Infrastructure