Une stratégie communautaire de notre corpus de parité
nommée IES restait en parité moderate vis-à-vis de TradingView depuis des semaines. Les
décomptes coïncidaient. Les prix d'entrée, au centime près. Les prix de sortie, idem. Mais le
PnL cumulé dérivait de 29 281 $ sur 2 632 opérations déjà appariées.
Quand nous avons publié les chiffres du corpus en mai, celui-ci comptait 167 stratégies de référence, dont 165 en parité stricte trade-for-trade avec TradingView. Il a grossi depuis. Le corpus compte désormais 252 stratégies — 251 en parité « excellente » (99,6 %), une anomalie documentée, zéro échec.
Le chiffre qu'on met en avant, c'est la partie ennuyeuse. La partie intéressante, c'est ce qui a émergé en chemin : trois comportements du broker TradingView qu'on ne modélisait pas, chacun découvert parce qu'une stratégie réelle ou synthétique a exercé un chemin de code que les 167 précédentes ne touchaient pas. Voici trois d'entre eux, en détail.
Où en est le corpus
corpus/validation_report.md (2026-06-29)
Total probes: 252
Excellent: 251 (99.6%)
Anomaly: 1
Strong/Moderate/Weak/Minimal/Fail: 0
TV trades: 389,590
Engine trades: 389,688
Matched: 389,468Le moteur émet 98 trades de plus que TradingView sur l'ensemble du corpus — un écart de 0,025 % sur 389 590 trades de référence. C'est l'agrégat ; par stratégie, la quasi-totalité de cet écart vient de la seule anomalie ci-dessous, pas d'un bruit étalé sur les 251 autres.
Les six derniers probes forment une catégorie drawing, ajoutée quand on a livré un runtime
capable de lire les objets de dessin Pine (line.get_price, géométrie des labels/box) comme
des données utilisables en backtest, et pas seulement comme des annotations de graphique. Les
stratégies qui utilisent une ligne tracée comme état de calcul — une trendline ancrée
manuellement et utilisée comme niveau de support dynamique, par exemple — exigent que le moteur
continue de servir la géométrie d'objets déjà supprimés par le script, exactement comme le fait
TradingView. Les six probes sont passés sans accroc.
L'unique anomalie, nommée sans détour
On ne filtre pas la seule stratégie qui ne matche pas. anomaly-equity-mirror-strategy-equity-01
se situe exactement à la frontière 1× equity pour l'admission en marge. L'émulateur de broker
de TradingView est non monotone juste à cette frontière — il admet certaines entrées en
sur-equity et rejette certaines entrées en sous-equity, de façon incohérente, bar après bar.
Notre moteur, lui, est déterministe : même equity, même décision d'admission, à chaque fois.
C'est correct au regard de la sémantique de marge documentée de Pine. C'est aussi, de façon
démontrable, différent de ce que fait réellement le broker TradingView à cette frontière
précise. Il faudrait introduire notre propre non-déterminisme pour matcher ce comportement, donc
on ne le fait pas. L'écart est documenté, pas caché, et c'est le seul sur 252.
Bug n°1 : la position que TradingView aurait liquidée d'office
Le broker de TradingView n'attend pas que la logique de votre stratégie décide de sortir d'une
position qui a dépassé son exigence de marge — il liquide. Si l'equity d'une position à effet
de levier ou short chute suffisamment pour que la marge requise dépasse l'equity du compte,
TradingView liquide la position d'office à la barre suivante, peu importe les conditions de
strategy.exit() remplies ou non.
Le moteur ne modélisait pas ça. Il conservait la position et laissait la logique propre de la stratégie la clôturer plus tard — ce qui, sur une stratégie sans stop serré, pouvait représenter des dizaines de barres avec une position que TradingView avait déjà liquidée. Sur les stratégies du corpus à fort effet de levier, ça produisait des courbes d'equity radicalement différentes en période de stress, même quand la logique d'entrée et de sortie matchait parfaitement partout ailleurs.
Le correctif ajoute une liquidation forcée dès que l'equity du compte franchit l'exigence de marge, en calculant le prix de liquidation de la même façon que le broker de TradingView, puis en arrondissant la quantité liquidée au lot step du symbole — TradingView ne liquide pas de lots fractionnaires plus petits que ce que permet l'exchange, et nous non plus désormais.
Ça concerne surtout ceux qui backtestent des stratégies à effet de levier ou short sur des perpetuals. Si votre courbe d'equity semblait trop lisse pendant un drawdown, c'était probablement ça.
Bug n°2 : le contrôle d'affordabilité qui ignorait le FX
Une stratégie Pine peut déclarer une devise de compte différente de la devise de cotation du
symbole — currency.INR qui trade une paire USDT, par exemple. Avant de vérifier si vous pouvez
vous permettre un ordre, le broker de TradingView convertit la valeur notionnelle de l'ordre dans
la devise de votre compte au taux de change en vigueur, puis compare ce montant à l'equity dans
cette même devise.
Le filtre d'affordabilité du moteur sautait cette conversion. Il comparait directement le
notionnel en USDT à une equity libellée en INR comme si le taux de change était de 1:1 — ce qui
faisait qu'une stratégie déclarant currency.INR paraissait environ 83× plus abordable qu'elle
ne l'était réellement (puisque 1 USDT vaut à peu près ce nombre de roupies). Sur les scripts
concernés, ça doublait à peu près le nombre de trades que le moteur admettait alors que le broker
de TradingView les aurait rejetés comme inabordables.
Le correctif applique le taux de change de la devise de compte à la marge requise avant que le contrôle d'affordabilité ne s'exécute, exactement au même point du pipeline d'ordre que TradingView. Si vous backtestez avec une devise de compte autre que l'USD contre un symbole coté en USD ou en USDT, c'est ce bug qui gonflait discrètement votre nombre de trades.
Bug n°3 : le trailing stop qui ne s'armait jamais
C'est celui qu'on aurait le plus aimé qu'un utilisateur repère avant nous, parce qu'il échoue en
silence. strategy.exit() propose deux façons de spécifier un trailing stop : trail_points,
une distance relative au prix d'entrée, et trail_price, un prix d'activation absolu.
// armed correctly today; trail_price-only calls used to never arm at all
strategy.exit("TS", from_entry = "Long", trail_price = close * 1.05, trail_offset = 10)La condition d'armement du moteur ne vérifiait que trail_points. Un exit trailing spécifié
uniquement via trail_price compilait sans erreur, tournait sans erreur, et ne s'armait jamais
— la position restait là, sans aucun trailing stop actif, en silence, pendant tout le backtest.
Aucune exception, aucune ligne de log, aucun trade. La stratégie avait l'air protégée. Elle ne
l'était pas.
On a trouvé ce bug parce qu'un probe du corpus isole spécifiquement l'activation via
trail_price seul — exactement le genre de stratégie étroite et synthétique pour laquelle existe
la catégorie validation, comme expliqué dans le billet sur la composition du corpus :
pas rentable, pas réaliste, construite pour exercer une seule fonctionnalité Pine de façon
isolée afin qu'une régression comme celle-ci ne puisse pas se cacher derrière « les chiffres
globaux avaient l'air bons ».
Le correctif arme le trailing stop dès que trail_points ou trail_price est présent. Si vous
avez déjà écrit une stratégie Pine utilisant trail_price et que la courbe d'equity ressemblait
suspicieusement à une absence totale de trailing stop — c'était bien le cas, sur toute version du
moteur antérieure à celle-ci.
Pourquoi c'est précisément l'intérêt d'un corpus
Aucun de ces trois bugs n'a été trouvé par revue de code. Ils ont été trouvés parce qu'une stratégie de la bonne forme est passée dans le moteur et a produit un chiffre qui ne matchait pas celui de TradingView. C'est toute la thèse derrière le fait de maintenir 167 stratégies, puis 252, et ça continue comme un corpus versionné et mis à l'écart, plutôt qu'une poignée de smoke tests : des bugs qui ne changent ni le nombre de trades ni le prix d'exécution peuvent quand même changer le chiffre qui compte le plus — ce que la stratégie a gagné ou perdu — et la seule façon de les attraper, c'est de continuer à faire tourner plus de formes de stratégies dans le même diff contre la même vérité de référence.
On continuera d'ajouter des probes à mesure qu'on trouve des trous, et de publier les chiffres — les bons comme l'unique anomalie — tels qu'ils sont.
Pour aller plus loin
- Parcourir la galerie — les 252 stratégies, palier de parité et nombre de trades sur chaque carte.
- Lire comment on a trouvé un bug de marge similaire — un bug différent, une frontière différente, la même méthode : isoler, diffuser, corriger.
- Essayer l'API de codegen — transpilez une stratégie Pine et backtestez-la sur vos propres OHLCV depuis Claude, Cursor, ou n'importe quel client MCP.
- Profiter d'un accès anticipé — offre gratuite, 100 transpilations par mois.
