MéthodeméthodetransparenceMIwalk-forward

J'ai perdu 368 USDT en 5 jours. Voici comment j'ai trouvé les 4 cellules mortelles de ma matrice MI

Entre le 6 avril et le 17 mai, ma flotte de bots APEX faisait +945 USDT net sur 466 trades. Bons mois, edge mesurable, courbe d'équité qui montait.

Puis le 17 mai, quelque chose a basculé.

Sur les 9 jours suivants (17 → 26 mai), 200 nouveaux trades fermés. P&L net : −368 USDT. Pas un crash, pas un bug — un retournement de régime que mon filtre n'avait pas anticipé. Les shorts continuaient de saigner, les longs prenaient des SL gros et des TP serrés. La matrice MI v1 voyait bien le régime BEAR mais ne bloquait rien — elle réduisait le sizing, c'est tout.

Cette session, j'ai passé une journée à comprendre exactement se trouvaient les fuites, et à construire un filtre qui les ferme dur. Le résultat : un module que j'ai déployé en mode shadow sur le VPS, baptisé Hard-Gate v1. Si je l'avais eu en place le 17 mai, le backtest dit que j'aurais fait +59 USDT sur la fenêtre au lieu de −368 — un delta de +427 USDT en 5 jours, que je n'ai pas mesuré sur du live mais sur un re-jeu walk-forward de chaque trade existant.

Voici comment j'en suis arrivé là.

Phase 1 — Comprendre où ça saigne

Avant de coder quoi que ce soit, j'ai sorti la totalité des trades fermés depuis le lancement (1 002 trades, dont 666 signal trades hors carry/funding) et je les ai croisés avec les snapshots du service Market Intelligence (464 snapshots horaires, matchés à chaque trade dans une fenêtre ±2h, couverture 75 %).

J'ai cherché des cellules mortelles : des conditions sous lesquelles un certain type de trade perdait systématiquement.

Le pivot du 15 mai

Premier découpage : long vs short, semaine par semaine.

Période
LONG (n / WR / P&L)
SHORT (n / WR / P&L)
Mai S1 (01-07)
132 / 72 % / +978
53 / 7.5 % / −430
Mai S2 (08-14)
87 / 65 % / +895
25 / 8 % / −285
Mai S3 (15-21)
215 / 70 % / −378
66 / 62 % / +230
Mai S4 (22-26)
291 / 77 % / −177
69 / 43 % / −65

Le retournement saute aux yeux. Première moitié de mai : longs en mode parfait, shorts massacrés (WR 7-8 %, du quasi pile-ou-face mais payé en pertes). Semaine du 15-21 : inversion complète — shorts gagnent, longs commencent à perdre. Puis semaine du 22-26 : les deux saignent. Régime sideways/choppy, ni vrai bear ni vrai bull.

J'ai tout l'historique de la flotte à 1 ligne par trade. Donc je peux faire mieux que "le régime a changé".

Cross-tab des features MI

J'ai découpé chaque trade par buckets de features MI. Quelques résultats parlants (filtre à au moins 10 trades par cellule, all-time) :

LONG en btc_vs_ema [-5,-2)%
test EMA200 par le bas
285
n
65 %
WR
0.08
PF
−540 USDT
P&L
LONG en trend_regime=TRANSITION
35
n
2.9 %
WR
0.06
PF
−347 USDT
P&L
SHORT en btc_vs_ema <-5%
zone très oversold
61
n
26 %
WR
0.35
PF
−212 USDT
P&L
LONG en composite_score [-20,-10)
281
n
75 %
WR
0.47
PF
−149 USDT
P&L
SHORT en btc_vs_ema [-5,-2)%
zone test EMA depuis bas
78
n
64 %
WR
4.54 OOS
PF
+218 USDT
P&L
LONG en composite_score [-10,-3)
pullback léger
203
n
87 %
WR
2.59
PF
+182 USDT
P&L
LONG en btc_vs_ema <-5%
BTC très oversold, mean-rev
223
n
86 %
WR
1.11
PF
+14 USDT
P&L

C'est là que je vois la vraie structure du marché vue par mes bots :

  • Quand BTC teste EMA200 par le dessous (-5 à -2 % en dessous), les longs perdent presque toutes leurs SL alors que le WR reste à 65 %. C'est-à-dire qu'ils prennent des TP serrés mais 35 % de SL qui sont gros — PF 0.08 c'est-à-dire les pertes valent 12× les gains. Single biggest leak de la flotte.
  • Quand BTC est très oversold (<-5 %), c'est exactement l'inverse : les shorts se font piéger (zone mean-rev statistique) et les longs commencent à gagner.
  • Le régime TRANSITION est rare mais quand il se déclenche, 0 trade LONG sur 35 ne gagne.

Auto-corrélation per-bot — trouvaille majeure

Découpage différent : pour chaque trade, qu'est-ce qui s'est passé sur le trade précédent du même bot ?

Après 1 victoire
282
n
56 %
WR
+1 067
P&L
+3.78
avg
Après 1 perte
359
n
34 %
WR
−457
P&L
−1.27
avg
Après ≥ 3 pertes consécutives
147
n
26 %
WR
−476
P&L
−3.24
avg
Après ≥ 4 pertes consécutives
102
n
23 %
WR
−428
P&L
−4.20
avg

Surprise. Indépendamment du régime de marché, un bot qui vient de prendre 3 SL d'affilée a 26 % de chances de gagner le 4ème, contre 50 % pour un bot "frais". C'est massif : sur les 147 occurrences observées, ce 4ème trade a coûté en moyenne −3.24 €. Cumul : −476 USDT, soit autant que la cellule TRANSITION du bucket MI.

C'est cohérent avec ce que je retrouve dans la littérature quant (papiers Mulvey sur les CTAs) : un bot qui a perdu plusieurs fois indique probablement que ses paramètres ne matchent plus le régime courant — mieux vaut le mettre en pause que de lui donner une 4ème chance.

Phase 2 — Walk-forward, ou comment ne pas se mentir

Trouver des corrélations dans 1 002 trades, c'est facile. Trouver des corrélations qui tiennent hors échantillon, c'est ce qui sépare un edge d'un artefact.

Je viens d'ailleurs d'apprendre cette leçon à mes dépens : il y a 4 jours, j'ai publié une autopsie d'une stratégie ORB+FVG sur l'or qui semblait avoir PF 1.40 sur 2 ans de backtest — et qui n'a tenu aucune de ses 5 meilleures configs en walk-forward (0/5 OOS). Plus jamais ça.

Donc j'ai découpé mes 666 trades signal en 5 folds chronologiques (~133 trades chacun) et j'ai testé chaque cellule mortelle sur chaque fold, séparément.

Ce qui a tenu — 4 cellules

Cellule
Folds actifs
Verdict
LONG btc_vs_ema [-5,-2)%
3, 4, 5 → 3/3 négatifs
✅ structurel
SHORT btc_vs_ema <-5%
4, 5 → 1 break-even + 1 catastrophe
⚠️ partial
LONG trend_regime=TRANSITION
3, 4 → 2/2 négatifs (dormant)
✅ sentinelle dormante
Streak per-bot ≥ 3 pertes
1-5 → 5/5 négatifs
✅ stable

Trois cellules sur quatre tiennent 100 % en walk-forward. La quatrième (SHORT en zone très oversold) tient en moyenne mais avec une distribution bimodale (un fold break-even, un fold catastrophe). Je l'ai gardée mais flaguée "monitor" pour surveiller son comportement live.

Ce que j'ai rejeté

Plus intéressant encore : ce que j'ai testé et qui n'a PAS tenu en walk-forward.

  • Per-bot blacklist sur les 3 bots qui concentrent 50 % du leak SHORT (x1-wvborb-bf7, comboemarsimacd-bf11, x3-emakeltner-bf22). En IS leurs shorts perdent 316 €. En OOS, sur les 5 derniers jours, leurs mêmes shorts gagnent +15 €. Overfit pur. Si j'avais blacklisté ces bots, j'aurais sacrifié un edge légitime.
  • Day-of-week guard SHORT Mercredi+Samedi (n=44 Saturday WR 20 %, n=25 Wednesday WR 16 % en IS). En OOS, 0 trade Wed/Sat dans la fenêtre. Non-testable sur le dataset actuel. Je garde l'idée en backlog pour ré-évaluer dans 60 jours.
  • US session 12-18 UTC SHORT : −292 € sur la période, mais n=15 en OOS, puissance statistique trop faible.
  • derivatives_score < -2 → LONG : IS WR 72 %, OOS WR 33 %. Retournement complet OOS. Classique : la feature derivatives capte un signal IS mais pas robuste sur la suite.
  • Scoring composite multi-feature (5 features, threshold tunable) : équivalent net OOS aux règles simples (+23 vs +50), avec plus de complexité, plus d'opacité. Cas typique où ajouter des paramètres ne fait que sur-fitter.

Toutes ces pistes sont documentées dans le backlog du spec pour ré-évaluation après 60 jours de data live supplémentaires.

Phase 3 — Pourquoi un gate binaire et pas un sizing

À ce stade j'avais identifié les fuites. Mais il fallait choisir comment les fermer.

J'avais déjà déployé une matrice MI qui module le sizing (long_mult × short_mult, 0.3× à 1.0× selon la cellule régime). Cette matrice tourne en prod depuis le 12 mai. Elle est même en train d'être complétée par un Direction Edge Detector v2 (en shadow depuis le 18 mai), qui ajoute une couche de sizing dynamique basée sur l'auto-corrélation de mon P&L sur 3 jours.

Question : est-ce que ces leaks pouvaient se fermer simplement en réduisant encore plus le sizing ?

Réponse : non.

Le sizing est continu et a un floor à 0.3×. Quand BTC est en [-5,-2)% et qu'un bot ouvre un LONG, même à 0.3× il prend quand même un SL — juste avec une perte 3× plus petite. Sur la cellule TRANSITION (35 trades, 0 winner sur 35), même à 0.3× sizing, le bot perd toujours sur 35 trades.

La bonne couche manquante, c'est un gate binaire : si le bot est dans la death cell, il ne trade pas du tout, point.

C'est la distinction classique en gestion d'actifs quantitative :

Couche
Question
Mon layer
Hard gate (binaire)
Est-ce qu'on trade ?
Hard-Gate v1 (nouveau)
Sizing modulator (continu)
Combien ?
DED v2 (shadow) + matrice v1 (prod)

Les deux couches sont complémentaires. Le gate dit si, le sizing dit combien. Pattern bien établi chez Man AHL, Mulvey, et la plupart des fonds CTA — pas mon invention, je suis juste la littérature.

Phase 4 — Calibration du cooldown du streak guard

Pour les 3 cellules MI (L1 + L2), aucune calibration de paramètre — elles sont définies par les buckets observés.

Pour le streak guard (L3), il fallait choisir une condition de reset. Si je bloque un bot après 3 SL d'affilée, comment il sort de cette pause ?

Deux options :

  1. Win-based : il faut qu'il gagne 1 trade pour briser la série. Problème : si je bloque tous ses trades, il ne peut jamais gagner. Pause permanente.
  2. Time-based : après X heures, je laisse passer un "trial trade". S'il gagne, série brisée ; s'il perd, nouvelle pause.

Donc reset = win OU cooldown temporel. Reste à choisir le cooldown.

J'ai simulé 4 valeurs (12h / 24h / 48h / 72h) plus "no time limit", avec une discipline real-time stricte : pour chaque trade candidat, je regarde seulement les trades déjà fermés au moment de son ouverture (pas un look-ahead bias possible).

Cooldown
Trades bloqués
FP rate
Δ baseline
12h ✅
111
39 %
+34 USDT
24h
123
38 %
+32
48h
148
39 %
+22
72h
150
39 %
+36
No limit
154
40 %
+11

12h est l'arbitrage optimal : à peu près le meilleur Δ, et la distribution d'âge des "fires" montre que 78 % des cascades se produisent dans les 12h après la 3ème perte. Au-delà, le bot reprend statistiquement en zone neutre (WR ~44 %, pnl légèrement positif), et un cooldown plus long ferait sacrifier ces winners.

48h+ est même contre-productif : ça bloque trop de trades dans la zone 24-48h où la moyenne est positive.

Phase 5 — Simulation finale et déploiement shadow

Combinaison des 3 layers (L1 btc_vs_ema + L2 TRANSITION + L3 streak guard) sur les 666 trades signal, walk-forward 5 folds anchored expanding :

Fold
Période
Baseline
Avec gate
Δ
1
06/04 → 01/05
−112
−42
+70
2
01-06/05
+975
+1013
+38
3
06-14/05
+30
+235
+206
4
14-20/05
+64
+195
+130
5
20-26/05
−380
+40
+420
Σ
+577
+1 441
+864

Décomposé par layer (cumul) :

  • L1 LONG test_ema : 114 trades bloqués, −541 USDT évités (le gros morceau)
  • L1 SHORT very_bear : 61 trades bloqués, −212 € évités
  • L2 LONG TRANSITION : 8 trades bloqués, −58 € évités
  • L3 streak ≥ 3 : 66 trades bloqués, −53 € évités (plus marginal mais préventif)

Le gate sauve l'écosystème surtout sur la fenêtre la plus récente. Sur les 5 derniers jours (mon OOS true dans le walk-forward 70/30) : baseline −368 USDT → gate +59 USDT. Sans cherry-picking ni look-ahead.

Déploiement shadow

J'ai déployé Hard-Gate v1 sur le VPS le 26 mai à 13:30 UTC, mais en mode shadow : le service MI calcule à chaque cycle ce que le gate aurait décidé, l'écrit dans un nouveau champ hard_gate du heartbeat JSON, mais les bots continuent d'utiliser la matrice v1 actuelle. Aucun changement de comportement live.

Le premier heartbeat post-déploiement a déjà fired la layer L1 : BTC vient justement de passer en zone test EMA200 par le bas (-3.x %). Le système observe ce qu'il fallait observer.

Pendant les 7 prochains jours, je vais comparer ce que le gate aurait bloqué vs ce qui s'est réellement passé sur la flotte. Si tout colle, je promeus en live début juin — les bots commenceront à lire le gate et à AND-combiner avec leur sizing actuel.

Ce que je ne sais pas encore

J'ai 1 002 trades, 50 jours, 1 seule transition de régime observée. Le walk-forward 5-fold + 70/30 me donne 2 vues OOS distinctes, mais 5 jours de OOS pure ce n'est pas une saison de marché complète.

Le vrai test, c'est forward live. Le mode shadow est conçu pour ça : observer 7 jours, voir si le gate continue de capter ce que j'ai calibré sans faux positifs systématiques, puis décider.

Si à la review du 2 juin le gate s'avère trop strict (sacrifie trop de winners en pratique), j'ai des feature flags par layer pour le désactiver granulairement. Si trop laxiste, je resserre.

Je documenterai la promotion ou le rollback ici dans 7 jours.

Pour creuser

  • Spécification complète : 469 lignes de design doc dans mon vault privé, qui détaille chaque décision et sa justification data
  • 51 tests unitaires TDD en Python, suite MI complète à 281/281 ✓
  • Pourquoi je garde DED v2 en parallèle plutôt que de le remplacer : architecture à 2 couches (gate binaire au-dessus, sizing continu en dessous), validée chacune indépendamment

Comme toujours : je montrerai la suite, qu'elle valide la méthode ou qu'elle l'invalide. C'est la seule façon que ça vaille quelque chose.

Suivre la recherche

Si cet article t’a servi : je publie mes backtests, mes rejets et les évolutions du labo. Laisse ton email pour recevoir les prochains write-ups. Pas de spam, désinscription sur simple réponse.