Vous m’avez envoyé le notebook la semaine dernière : même signal, même univers, les mêmes 14 mois de données perp BTCUSDT. Seule l’exécution avait changé. Vous avez cessé de franchir le spread et commencé à placer des bids passifs un tick à l’intérieur, et le Sharpe est passé de 0.42 à 2.14. Vous m’avez demandé si c’était réel ou si vous aviez cassé quelque chose.
Vous avez cassé quelque chose. Je veux vous montrer exactement où, parce que le bug tient à une ligne et que la leçon qu’elle cache va bien au-delà de cette ligne.
Voici votre règle d’exécution, copiée de votre moteur :
if bar.low <= limit_price: fill(limit_price)
Cela signifie : si le marché a enregistré une transaction à un prix inférieur ou égal au mien pendant cette minute, mon ordre a été exécuté à mon prix. En réalité, votre règle dit que le prix a touché votre niveau. Toucher n’est pas exécuter. Entre les deux, il y a une file d’attente, et vous l’avez modélisée comme si elle était vide.
Ce qui se trouve devant vous
Quand vous placez un bid à 84,120.0 sur le perp BTCUSDT, vous rejoignez la fin de la file des ordres déjà présents à ce niveau. Au meilleur prix de ce contrat, cela représente généralement entre 4 et 30 BTC selon l’heure ; la médiane de votre fenêtre d’échantillonnage tourne autour de 12 BTC. Votre ordre fait 0.4 BTC. Pour qu’il soit exécuté, les vendeurs doivent frapper ce niveau avec un volume cumulé suffisant pour servir tous ceux qui sont arrivés avant vous, et ils doivent le faire avant que les ordres à ce niveau soient annulés sous vos yeux ou que le marché reparte à la hausse.
La question que votre backtest devrait poser n’est donc pas « le prix a-t-il atteint 84,120.0 ? », mais « au moins 12.4 BTC de ventes au marché ont-ils été exécutés à 84,120.0 pendant que mon ordre était présent ? ». Ce sont des événements radicalement différents. Dans vos données, leur fréquence diffère d’un facteur d’environ trois.
Trois règles d’exécution, trois stratégies différentes
J’ai relancé votre signal avec les mêmes entrées et trois modèles d’exécution. Même alpha, mêmes frais, même funding. Seule la logique d’exécution a changé.
| Règle d’exécution | Exécutions | Edge moyen à 60 s après exécution | Sharpe |
|---|---|---|---|
Toucher : low <= limit | 4,180 | +2.6 bp | 2.14 |
Franchissement strict : low < limit - 1 tick | 1,712 | +0.4 bp | 0.61 |
| Simulation de file par volume au niveau | 1,306 | +0.9 bp | 0.77 |
La ligne du milieu correspond à la correction rudimentaire que tout le monde essaie en premier : ne compter une exécution que si le marché a négocié franchement au-delà de votre prix, en partant du principe que s’il l’a dépassé, il a forcément consommé votre ordre. L’idée va dans le bon sens et élimine l’essentiel de l’illusion. Mais elle introduit aussi un biais pernicieux, auquel je reviendrai.
La ligne du bas est celle que vous devriez construire. Elle ne nécessite ni flux L3 ni reconstitution de chaque ordre. Vous avez déjà presque tout ce qu’il faut.
Une simulation de file à construire à partir d’aggTrades
Récupérez le flux des transactions agrégées plutôt que les klines. Chaque transaction enregistrée contient le prix, la quantité, l’horodatage et l’indicateur du côté maker, qui vous indique si l’agresseur achetait ou vendait. C’est suffisant pour simuler correctement votre propre ordre passif :
- Au placement de votre ordre, relevez le volume présent à votre prix. Si vous n’avez qu’un flux de carnet toutes les 100ms, utilisez le dernier relevé ; l’erreur reste faible comparée à celle que vous corrigez.
- Définissez
queue_ahead = resting_size. Soyez pessimiste et partez du principe que vous êtes dernier. C’est le cas, sauf si c’est vous qui créez le niveau. - Parcourez le flux des transactions. Chaque vente agressive à votre prix ou en dessous décrémente
queue_aheaddu volume correspondant. Lorsqu’il devient négatif, votre ordre est exécuté à votre prix et à cet horodatage. - Si le prix s’éloigne d’un tick, ne remettez pas la file à zéro. Faites-la décroître. Certains ordres devant vous sont annulés quand le niveau devient obsolète, d’autres restent. Une décroissance de 30-40% par seconde complète passée loin du prix correspondait mieux à mes reconstitutions que l’un ou l’autre des scénarios extrêmes.
- Si votre stratégie annulerait l’ordre puis le replacerait, modélisez ce nouvel ordre en fin de file au nouveau niveau. C’est l’étape que les gens oublient, et c’est là que se cache le reste de l’illusion.
C’est sur l’étape 2 que vous aurez envie de me contredire. Oui, parfois vous êtes près du début de la file, parce que vous avez placé votre ordre au moment où le niveau s’est formé. Très bien : mesurez-le, ne le supposez pas. Consignez le volume présent au moment du placement et laissez les données vous dire quelle part de vos ordres arrive réellement tôt. Dans votre stratégie, c’était 11%, parce que votre signal se déclenche après un mouvement : le niveau que vous rejoignez existe donc déjà et il y a déjà du monde dessus.
Les exécutions que vous obtenez sont celles que vous auriez préféré éviter
Passons maintenant à ce qui compte vraiment et explique le biais de la règle du franchissement strict.
Demandez-vous dans quelles circonstances votre bid est entièrement exécuté. Cela arrive lorsque la pression vendeuse est assez forte pour absorber tout le volume du niveau. Autrement dit, par construction, c’est le moment où le marché traverse votre prix à la baisse. Les exécutions dont vous êtes le plus certain sont celles qui vous mettent immédiatement en perte latente.
Répartissez vos exécutions selon leur déroulement et mesurez le mark-out à 60 secondes :
| Type d’exécution | Part des exécutions | Mark-out à 60 s |
|---|---|---|
| Niveau négocié, rebond du prix | 38% | +3.1 bp |
| Niveau négocié, prix stable | 21% | +0.2 bp |
| Prix traversé d’au moins 2 ticks | 41% | −2.4 bp |
Votre backtest naïf vous donnait les trois catégories et les considérait toutes comme gratuites. La règle du franchissement strict ne retient presque que la troisième, ce qui explique pourquoi son edge s’est effondré encore davantage que celui de la simulation de file. Aucune des deux n’est juste. La simulation de file vous donne une répartition réaliste, et c’est toute la stratégie : l’exécution passive vous rapporte le spread et vous le fait payer en sélection adverse ; le rapport entre les deux constitue votre véritable stratégie.
La vieille formule des desks actions tient toujours : un ordre maker est une option gratuite que vous vendez au marché. Quelqu’un l’exerce quand cela l’arrange. Votre backtest encaissait la prime en oubliant que l’option comportait aussi une jambe de paiement.
Les transactions que vous n’avez pas obtenues changent la stratégie, pas seulement son coût
C’est le point auquel je veux le plus que vous réfléchissiez. Quand vous modélisez mal l’exécution taker, vous obtenez les bonnes transactions au mauvais prix, et une correction des frais règle l’essentiel du problème. Quand vous modélisez mal l’exécution maker, vous obtenez carrément un ensemble de transactions erroné. Environ 2,900 de vos 4,180 entrées n’ont jamais eu lieu. Certaines correspondaient à vos meilleurs signaux, sur des bougies qui ont bondi puis se sont retournées — exactement le type de mouvement où le marché est parti sans vous.
La branche « ordre non exécuté » de votre stratégie a donc besoin d’une vraie logique. Que fait-elle si l’entrée n’est pas exécutée avant que le signal devienne obsolète ? Vous poursuivez le marché avec un ordre taker et payez le spread plus l’impact ? Vous replacez l’ordre plus bas et acceptez un autre prix d’entrée ? Vous passez la transaction et restez à plat ? Chaque choix produit une courbe de capital sensiblement différente, et aucun ne consiste à « supposer que l’ordre a été exécuté ». Dans nos simulations, une règle de poursuite réaliste (franchir le spread après 20 secondes sans exécution, avec un slippage plafonné à 3 bp) a permis de récupérer environ un tiers des transactions manquantes et près de la moitié de l’écart de Sharpe entre le modèle naïf et la simulation de file. C’est un résultat vraiment intéressant, qui n’apparaît que lorsque le modèle d’exécution est assez réaliste pour que la question ait un sens.
Vérification rapide, en 10 minutes : prenez votre journal de paper trading en direct et votre backtest sur la même période. Comparez le taux d’exécution, pas le PnL. Si le backtest exécute 100% des ordres passifs et que le paper trading en exécute 34%, vous ne comparez pas des stratégies : vous avez un bug dans le modèle d’exécution. Corrigez-le avant de regarder le moindre chiffre de rendement.
Deux points secondaires, tant que je vous tiens
Rejets post-only. Si vous utilisez post-only pour garantir le niveau de frais maker et que le carnet bouge entre votre décision et l’accusé de réception de la plateforme, l’ordre est rejeté au lieu d’être placé dans le carnet. Dans nos journaux de paper trading, cela concerne 3-6% des tentatives sur BTCUSDT pendant les heures normales et plus de 15% dans la minute qui suit la publication de l’IPC américain. Un ordre rejeté n’est ni un ordre exécuté ni un ordre passif non exécuté : c’est une transaction qui n’a jamais existé. Si votre backtest ne prévoit pas cet état, le nombre de transactions est gonflé précisément dans les régimes qui vous intéressent le plus.
La prévention de l’auto-confrontation et votre propre empreinte. Avec 0.4 BTC, vous ne faites pas bouger BTCUSDT, donc vous pouvez laisser cela de côté. Mais vous avez dit vouloir lancer cette stratégie sur un perp d’altcoin à moyenne capitalisation, où le notionnel au meilleur prix est souvent inférieur à $15k. Là, votre ordre représente une part significative de la file, et le volume que vous relevez devant vous tient compte de la présence de votre dernier ordre. Dès que votre taille dépasse environ 10% du volume présent, la simulation doit tenir compte du fait que les autres participants réagissent à votre présence ; honnêtement, à ce stade, je ferais davantage confiance au paper trading qu’à toute simulation que vous ou moi pouvons écrire.
Relancez le backtest avec la simulation de file et envoyez-moi le tableau des mark-outs ventilé par type d’exécution. Si la catégorie des rebonds conserve l’essentiel de l’edge et que celle des franchissements ne l’absorbe pas entièrement, vous tenez peut-être quelque chose qui mérite d’être testé en paper trading. Si tout reposait sur les 2,900 exécutions que vous n’auriez jamais obtenues, mieux vaut le découvrir maintenant qu’après quatre semaines à regarder un compte de paper trading ne pas faire ce que le notebook promettait.
← Tous les articles


