Aller au contenu

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 :

  1. 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.
  2. 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.

\[ c_k = \text{contrats absorbés au niveau } k \text{ avant sa chute} \]

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