📝

Draft article

This draft is visible to admins and superusers only. Sign in with an authorized account.

← Retour aux articles
July 23, 2026
5 min de lecture

Courbes de slippage, pas constantes de slippage : des modèles de coûts qui survivent au contact du trading réel

Courbes de slippage, pas constantes de slippage : des modèles de coûts qui survivent au contact du trading réel
#slippage
#coûts de transaction
#impact de marché
#backtest
#exécution
#tca
#loi racine carrée
#modèles de coûts

La ligne la plus coûteuse de la plupart des backtests est slippage_bps = 5. Non pas parce que 5 est un mauvais chiffre — moyenné sur une année de fills, il peut même être juste — mais parce qu'une constante est fausse conditionnellement, et que votre processus de sélection de stratégies est une machine à trouver et exploiter des erreurs conditionnelles. Le slippage est une fonction de la taille de l'ordre, du spread, de la volatilité et de la liquidité disponible. Une constante, c'est cette fonction évaluée en un seul point puis extrapolée partout, y compris lors de la cascade de liquidations de 3 heures du matin où votre signal de retour à la moyenne se déclenche le plus fort et où le carnet n'a plus qu'un dixième de sa profondeur habituelle. Le backtest facture 5 bps là aussi. Le trading réel, lui, ne le fera pas.

Cet article porte sur le modèle de coûts à l'intérieur de la boucle de backtest : la fonction cost(size, market_state) qui est soustraite de chaque trade simulé. Il ne s'agit délibérément pas de prédire l'impact ex ante pour planifier une exécution — c'est le problème jumeau, traité côté modèle dans la modélisation de l'impact de prix et côté planification optimale dans Almgren-Chriss. Ici, la question est plus étroite et, pour quiconque sélectionne ses stratégies par backtest, plus urgente : quelle fonction devrait se trouver là où se trouve actuellement votre constante, comment l'ajuster à partir de données dont vous disposez réellement, et dans quelle mesure la réponse change-t-elle la stratégie que vous finissez par exécuter. Cette dernière partie a une réponse empirique nette : reclassez 20 variantes d'une même famille de stratégies sous quatre modèles de coûts et regardez le classement s'inverser.

L'échelle des modèles de coûts

Échelle à quatre échelons des modèles de coûts, de la constante en bps à la participation en racine carrée

Comme l'échelle de simulation des fills, les modèles de coûts forment une échelle de fidélité où chaque échelon nécessite plus de données et élimine un biais systématique précis. Les échelons sont cumulatifs : chacun conserve les termes de celui du dessous et en ajoute un.

M0 : constante en bps

C0=fee+cC_0 = \text{fee} + c

Un seul chiffre, appliqué à chaque trade. Les trois modes de défaillance, par ordre croissant de dégâts :

  1. Aveugle à la taille. Un clip de 10 000 etunordreparentde5M et un ordre parent de 5 M paient le même prix. Toute comparaison de stratégies entre niveaux de capacité est dénuée de sens.
  2. Aveugle au régime. Un mardi après-midi calme et l'effondrement de FTX paient le même prix. L'erreur n'est pas du bruit ; elle est anti-corrélée exactement avec les états où les stratégies event-driven concentrent leur trading.
  3. Toxique pour la sélection. Quand vous balayez 200 combinaisons de paramètres, l'optimiseur découvre que sous un coût plat, le turnover est bon marché par rapport à la réalité, et oriente toute la recherche vers des variantes haute fréquence. L'erreur de coût devient une force directionnelle sur la sélection de modèle, et non une force symétrique sur le PnL.

M1 : proportionnel au spread

C1=fee+st2C_1 = \text{fee} + \frac{s_t}{2}

Facturer le demi-spread mesuré à l'horodatage de la décision (plus le fee, qui est déterministe et devrait toujours constituer sa propre ligne). C'est le premier modèle qui réagit tant soit peu au marché : les spreads s'élargissent lors des annonces, à l'ouverture des sessions, sur les altcoins peu liquides, donc le coût co-évolue désormais avec au moins une variable de liquidité. Ce qu'il continue de manquer : le spread évalue le prix d'un fill d'un seul clip de top-of-book. Tout ce qui est plus gros traverse le carnet, et cette traversée est invisible pour sts_t.

M2 : ajusté sur la volatilité

C2=fee+st2+cσσtC_2 = \text{fee} + \frac{s_t}{2} + c_\sigma \, \sigma_t

Ajoutez un terme proportionnel à la volatilité à court horizon. La justification est à la fois empirique et structurelle : les spreads et la profondeur sont fixés par des market makers dont le risque de sélection adverse et le risque d'inventaire évoluent avec σ\sigma (la logique de cotation d'Avellaneda-Stoikov exécutée à l'envers), donc le coût de la demande de liquidité se dénombre naturellement en unités de volatilité. Quand vous n'avez aucune donnée de carnet — des barres journalières, un historique long — cσσtc_\sigma \sigma_t avec cσc_\sigma autour de 0,05 à 0,1 fois la vol journalière est déjà un modèle nettement meilleur que n'importe quelle constante, car il respire au moins avec le régime.

M3 : racine carrée en participation

C3=fee+st2+Yσt(QVt)δ,δ0.40.7,Y=O(1)C_3 = \text{fee} + \frac{s_t}{2} + Y \, \sigma_t \left(\frac{Q}{V_t}\right)^{\delta}, \qquad \delta \approx 0.4\text{–}0.7, \quad Y = O(1)

Le terme de taille. QQ est la taille de votre ordre, VtV_t le volume de marché sur la fenêtre pertinente, σt\sigma_t la volatilité sur cette même fenêtre. Cette forme fonctionnelle n'est pas une commodité ; c'est l'un des résultats empiriques les plus répliqués de la microstructure de marché, et il vaut la peine d'être précis sur son origine.

Le point de départ théorique est Kyle (1985), « Continuous auctions and insider trading » (Econometrica 53(6), 1315–1335). Dans le modèle de Kyle, un market maker fixe le prix comme une fonction linéaire du flux d'ordres net, Δp=λq\Delta p = \lambda q, avec

λ=12Σ0σu\lambda = \frac{1}{2}\frac{\sqrt{\Sigma_0}}{\sigma_u}

— impact par unité de flux proportionnel à l'incertitude sur la valeur, rapportée au volume de trading bruité. Le lambda de Kyle a donné au domaine son unité de liquidité (la profondeur est 1/λ1/\lambda) et sa première affirmation testable : le coût par dollar échangé dépend du ratio entre information et liquidité, pas d'une constante. Mais le modèle est linéaire en taille, et les données disent le contraire.

Les données disent concave. Almgren, Thum, Hauptmann et Li (2005), « Direct estimation of equity market impact » (Risk, juillet 2005), ont ajusté environ 700 000 ordres actions US issus des desks de Citigroup et ont trouvé un impact temporaire croissant comme la participation à la puissance 3/53/5 — ils ont explicitement testé et rejeté à la fois le linéaire et la racine carrée pure au profit de δ=0.6\delta = 0.6 — tandis que l'impact permanent ressortait à 0.89±0.100.89 \pm 0.10, statistiquement indissociable du linéaire (ce qui est rassurant, puisque Huberman et Stanzl (2004) ont montré qu'un impact permanent non linéaire permet la manipulation). Tóth et al. (2011), « Anomalous price impact and the critical nature of liquidity in financial markets » (Physical Review X 1, 021006), en utilisant près de 500 000 méta-ordres de Capital Fund Management sur les marchés à terme, ont trouvé E[ΔP]YσdQ/Vd\mathbb{E}[\Delta P] \approx Y \sigma_d \sqrt{Q/V_d} avec YY d'ordre un — et ont fourni l'explication désormais standard : le carnet visible ne représente qu'une fraction infime de la liquidité latente, laquelle est localement linéaire en prix autour du mid, ce qui fait que le coût pour la traverser évolue comme la racine carrée. Des travaux ultérieurs affinent les bords : Zarinelli, Treccani, Farmer et Lillo (2015), « Beyond the square root » (Market Microstructure and Liquidity 1(2)), ont montré sur sept millions de méta-ordres institutionnels ANcerno que la racine carrée s'ajuste bien sur environ deux décades de taille d'ordre, mais qu'une forme logarithmique s'étend sur cinq ; Bucci et al. (2019, Physical Review Letters 122, 108302) ont documenté le passage d'un impact linéaire (à la Kyle) à très faible participation vers un impact en racine carrée au-delà. Et Frazzini, Israel et Moskowitz (2018), « Trading Costs » (SSRN 3229719), en utilisant 1 700 milliards de dollars d'exécutions réelles d'AQR sur 19 ans, ont trouvé des coûts réalisés d'un ordre de grandeur inférieurs aux estimations académiques antérieures — un avertissement : des coefficients ajustés sur le flux d'autres personnes, avec une urgence et un contenu informationnel différents, ne se transposent pas au vôtre.

Pour la boucle de backtest, le résumé pratique tient en trois affirmations. L'impact évolue avec la volatilité. L'impact est concave en taille, avec un exposant que vous devriez traiter comme 0.50.5 jusqu'à ce que vos propres données plaident pour 0.60.6. Le préfacteur YY est d'ordre un mais varie selon la plateforme, l'actif et — surtout — votre propre style de trading, d'où le fait que la section suivante porte sur son ajustement plutôt que sur sa copie. Le calibrage approfondi (noyaux de décroissance, décomposition transitoire/permanent, impact croisé) relève de l'article sur la modélisation de l'impact ; le backtest n'a besoin que de la courbe.

Un chiffre calculé, pour fixer les ordres de grandeur. Perpétuel BTC, vol journalière σd=250\sigma_d = 250 bps, volume journalier de la plateforme Vd=6V_d = 6 Md,, Y = 0.75,, \delta = 0.5$ :

Taille d'ordre QQ Q/VdQ/V_d Q/Vd\sqrt{Q/V_d} Terme d'impact + demi-spread (0,5 bp)
50 000 $ 0.0008% 0.0029 0.5 bp 1.0 bp
500 000 $ 0.008% 0.0091 1.7 bp 2.2 bp
5 M$ 0.083% 0.0289 5.4 bp 5.9 bp
50 M$ 0.83% 0.0913 17.1 bp 17.6 bp

Trois ordres de grandeur en taille couvrent un facteur d'environ 18 en coût. Aucune constante ne couvre ce tableau, et toute comparaison de stratégies qui fait varier la taille de position en maintenant slippage_bps fixe compare silencieusement des pommes à 50 000 aˋdesorangesaˋ5M à des oranges à 5 M.

Ajuster la courbe : vos fills, ou ceux de personne

À partir de vos propres fills

Les bonnes données sont votre propre relevé TCA : une ligne par ordre parent avec le mid à l'arrivée, le VWAP de fill réalisé, la taille de l'ordre et l'état du marché au moment de la décision — exactement le jeu de données que produit accessoirement la mesure de l'implementation shortfall. Le shortfall en bps par rapport à l'arrivée, normalisé par la volatilité contemporaine, régressé sur la participation, vous donne directement YY et δ\delta.

Le piège, c'est le bruit. Le shortfall d'un ordre unique est la somme de l'impact et du bruit de prix sur la fenêtre d'exécution, et le bruit domine : pour une exécution de 10 minutes à 250 bps de vol journalière, le terme de bruit est σd10/144021\sigma_d\sqrt{10/1440} \approx 21 bps face à un signal d'impact d'environ 3 bps. Un rapport signal/bruit de 0,15 par observation explique pourquoi Almgren et al. ont eu besoin de 700 000 ordres, et pourquoi vous devez ajuster sur des médianes de bucket, jamais sur des points bruts :

import numpy as np
import pandas as pd

def fit_sqrt_curve(orders: pd.DataFrame) -> tuple[float, float]:
    """orders: one row per parent order.
       is_bps    -- implementation shortfall vs arrival mid, bps, cost-positive
       sigma_bps -- volatility at arrival (same window used at simulation time)
       q_over_v  -- parent size / market volume over the execution window
    Fits I = Y * sigma * (Q/V)^delta on participation-bucket medians."""
    df = orders.query("q_over_v > 0 and sigma_bps > 0").copy()
    df["i_norm"] = df.is_bps / df.sigma_bps            # impact in vol units
    df["bucket"] = pd.qcut(np.log10(df.q_over_v), 12, duplicates="drop")
    b = (df.groupby("bucket", observed=True)
           .agg(i=("i_norm", "median"), qv=("q_over_v", "median")))
    b = b[b.i > 0]               # noisy buckets can go negative; drop, don't clip
    delta, log_y = np.polyfit(np.log(b.qv), np.log(b.i), 1)
    return float(np.exp(log_y)), float(delta)

Sur quelques milliers d'ordres parents crypto, cet ajustement est déjà stable à environ ±30% sur YY et ±0,1 sur δ\delta — grossier, mais l'écart entre une courbe ajustée et une constante devinée n'est pas de 30%, c'est toute la forme. Réajustez trimestriellement ; la dérive de YY est elle-même un diagnostic (une hausse de YY à participation constante signifie soit que le régime de liquidité de la plateforme a changé, soit que votre flux d'ordres est devenu plus informé/plus détectable).

Démarrage à froid : pas encore de fills

Avant le premier ordre réel, vous n'avez que des données publiques, et deux options honnêtes.

Traverser le carnet en profondeur. Faites la moyenne du coût de traversée des snapshots L2 à chaque taille, exactement comme le fait le simulateur de fills par trade, mais agrégé en une courbe. C'est une borne inférieure dont le signe est connu : le snapshot montre la liquidité qui a survécu au flux de tout le monde, la dynamique de reconstitution est invisible, et vos propres ordres enfants antérieurs auront déplacé le carnet avant que les suivants n'arrivent.

def curve_from_book(snapshots, sizes_usd, safety=1.75):
    """snapshots: iterable of (mid, asks), asks = [(price, qty), ...] best-first.
    Depth-walk cost is a LOWER bound on realized cost -- scale it."""
    rows = []
    for q_usd in sizes_usd:
        costs = []
        for mid, asks in snapshots:
            rem, paid, got = q_usd / mid, 0.0, 0.0
            for p, q in asks:
                x = min(q, rem)
                paid += x * (p - mid); got += x; rem -= x
                if rem <= 0:
                    break
            if rem <= 0:
                costs.append(paid / got / mid * 1e4)
        rows.append((q_usd, safety * float(np.median(costs))))
    return pd.DataFrame(rows, columns=["q_usd", "cost_bps"])

Reconstituer l'empreinte des trades publics. Regroupez les trades agressifs par taille, mesurez le mouvement du mid quelques secondes après chacun, et déduisez-en une courbe d'impact empirique. Cela retrouve la forme concave et à peu près la bonne mise à l'échelle en σ\sigma, mais le niveau est biaisé dans une direction que vous ne pouvez pas déterminer a priori : les prints publics sont les décisions d'autres traders, donc les gros prints sont conditionnellement informés (biaisant l'impact mesuré à la hausse par rapport à votre rééquilibrage non informé), tandis que les traders les plus sensibles à la liquidité fractionnent précisément leurs ordres pour se cacher (le biaisant à la baisse). Utilisez-la pour la forme, pas pour le niveau.

Dans les deux cas, le protocole de démarrage à froid est le suivant : prenez la courbe issue des données publiques, appliquez un facteur de sécurité de 1,5 à 2, tradez petit, et commencez à remplir la table TCA qui la remplacera. La courbe publique est un échafaudage, pas un mur porteur.

Dépendance au régime : le même ordre, cinq fois le coût

Courbes de coûts qui s'écartent en éventail entre régimes de volatilité calme, normal et stressé

Chaque terme de C3C_3 dépend de l'état du marché, et lors d'un pic de volatilité, ils évoluent tous simultanément contre vous. Les spreads sont proportionnels à la vol à court horizon, donc sts_t s'élargit de 5 à 10 fois. σt\sigma_t intervient directement dans le terme d'impact. Le volume VtV_t augmente aussi — ce qui, naïvement, réduit la participation — mais il augmente moins que σ\sigma au numérateur, et cette compensation cache un effet plus vicieux : la profondeur cotée près du mid s'effondre bien plus vite que le volume tradé, de sorte que la courbe réalisée se durcit au-delà de ce que la formule ajustée prédit à partir des seules données stressées en entrée.

Concrètement, avec la courbe ajustée ci-dessus, un ordre BTC de 3 M$ :

  • Jour calme : σd=150\sigma_d = 150 bps, spread 1 bp, Vd=6V_d = 6 Md.Cou^t. Coût = 0.5 + 0.75 \times 150 \times \sqrt{0.0005} = 0.5 + 2.5 = 3.0$ bps.
  • Jour de crise (niveau LUNA, FTX) : σd=700\sigma_d = 700 bps, spread 6 bps, Vd=18V_d = 18 Md.Cou^t. Coût = 3.0 + 0.75 \times 700 \times \sqrt{0.000167} = 3.0 + 6.8 = 9.8$ bps.

La formule indique 3,3x. Le TCA réalisé lors des jours de stress dépasse systématiquement la formule — l'effondrement de la profondeur que le terme en racine carrée ne voit pas — et c'est là qu'un multiplicateur de stress mesuré trouve sa place. Dans nos fills, le résidu du bucket de crise se situe entre 1,4 et 1,8x au-dessus de la formule à entrées stressées, portant le coût total en stress à 4,5–6x le coût calme pour le même ordre. Voilà l'origine honnête du « le même ordre coûte 5x plus cher lors d'un pic de vol » : environ 2x issu de σ\sigma net du volume, 2x issu du spread, et le reste de la profondeur qui s'évapore plus vite que ne l'admettent les statistiques de volume.

L'implémentation est un modèle de coûts conditionnel : ajustez la courbe par bucket de régime, ou de manière équivalente ajustez une courbe unique et une table de multiplicateurs indexée sur un quantile de vol réalisée calculé au moment de la décision (fenêtre glissante uniquement — une étiquette de régime calculée sur l'échantillon complet est du look-ahead contrebandé via le modèle de coûts) :

Régime (quantile de vol glissante 1h) Fréquence Mult. spread Mult. courbe (mesuré)
Calme (< p25) 25% 0.6x 0.7x
Normal (p25–p75) 50% 1.0x 1.0x
Élevé (p75–p95) 20% 1.8x 1.6x
Stress (> p95) 5% 5–10x 3–6x

Frazzini, Israel et Moskowitz (2018) documentent le même phénomène dans leurs données institutionnelles : les coûts réalisés co-évoluent fortement avec la volatilité contemporaine, et un modèle de coûts sans terme de vol variable dans le temps sous-évalue précisément les queues de distribution. La raison pour laquelle ce tableau compte davantage que ne le suggère sa ligne de stress à 5% tient à la corrélation : les stratégies n'échantillonnent pas ces lignes uniformément. Un système de breakout sur volatilité fait l'essentiel de son trading dans les deux lignes du bas. Un market maker en marché calme gagne dans la ligne du haut et se fait sortir par la ligne du bas. Pondérer les trades de chaque stratégie par son propre histogramme de régime — plutôt que de facturer tout le monde à la moyenne inconditionnelle — est la plus grande correction de PnL de toute cette page pour les systèmes event-driven, plus importante que l'écart entre deux échelons adjacents de l'échelle.

L'expérience de sensibilité : quatre modèles de coûts, un classement chamboulé

Si le modèle de coûts changeait le PnL par un décalage constant, rien de tout cela n'importerait pour la sélection — chaque variante se déplacerait également et l'argmax survivrait. Ce n'est pas le cas, car la sensibilité aux coûts varie d'un ordre de grandeur selon les variantes. L'expérience qui concrétise cela :

Configuration. Une famille de stratégies — retour à la moyenne par z-score et breakout de Donchian sur les perpétuels BTC et ETH, barres 1 minute, 18 mois — avec une grille sur l'horizon de détention (15m / 1h / 4h / 24h) et le seuil d'entrée : 20 variantes, turnover unilatéral de 0,4x à 11x le book par jour, clips de 250 000 $ exécutés sur 5 minutes. Chaque variante est backtestée une fois par modèle de coûts :

  • M0 : fees seulement ;
  • M1 : fees + 5 bps constant ;
  • M2 : fees + demi-spread + 0.08σ5m0.08\,\sigma_{5m} ;
  • M3 : fees + courbe ajustée Y=0.75Y=0.75, δ=0.5\delta=0.5 avec la table de multiplicateurs de régime ci-dessus.

Mêmes fills, mêmes signaux, même chemin de code — le modèle de coûts est passé au backtester comme argument de constructeur, de la même manière que tout paramètre de première classe :

from dataclasses import dataclass

@dataclass(frozen=True)
class SqrtCost:
    y: float = 0.75        # fitted impact coefficient
    delta: float = 0.5     # fitted exponent
    fee_bps: float = 2.5   # taker fee, always separate
    def cost_bps(self, q_usd: float, st) -> float:
        part = q_usd / st.window_volume_usd
        return (self.fee_bps + st.stress_mult *
                (st.spread_bps / 2 + self.y * st.sigma_bps * part ** self.delta))

for model in [ZeroCost(), ConstCost(5.0), VolCost(0.08), SqrtCost()]:
    for variant in grid:
        results[(model, variant)] = backtest(variant, cost_model=model)

Résultats (run représentatif ; rendements nets annualisés) :

Variante Turnover/jour M0 M1 (5 bp) M2 M3 (courbe) Rang M1 → M3
MR-15m, z=1.0 11.4x +187% +61% +9% −14% 1 → 17
MR-15m, z=1.5 7.9x +141% +52% +15% −2% 2 → 13
BO-1h, k=2 3.1x +74% +43% +28% +19% 3 → 4
MR-1h, z=2.0 2.4x +58% +38% +25% +21% 4 → 2
BO-4h, k=3 0.9x +39% +33% +29% +26% 7 → 1

Graphique en bump chart montrant l'inversion des rangs de 20 stratégies selon quatre modèles de coûts

Corrélations de rang (Kendall τ\tau) entre classements : τ(M0,M1)=0.79\tau(\text{M0},\text{M1}) = 0.79 — une constante ne réordonne quasiment rien, c'est une taxe, pas un modèle. τ(M1,M3)=0.39\tau(\text{M1},\text{M3}) = 0.39 — six des dix meilleures variantes de M1 quittent le top dix sous M3, et la championne de M1 passe de +61% à négatif. τ(M2,M3)=0.74\tau(\text{M2},\text{M3}) = 0.74 — le seul terme de vol récupère l'essentiel du réordonnancement ; le terme de taille achève le travail pour les variantes qui tradent gros par rapport au volume de la fenêtre. Le mécanisme est exactement la toxicité de sélection de l'échelon M0 : un 5 bps plat subventionne les trades qui coûtent réellement 15 bps (gros, rapides, en stress) et surtaxe ceux qui coûtent 2 (petits, patients, en calme), de sorte que classer par M1 promeut systématiquement les variantes dont les coûts réels sont les plus sous-estimés. L'optimiseur n'a pas trouvé d'alpha ; il a trouvé le terme d'erreur de votre modèle de coûts.

C'est pourquoi le modèle de coûts appartient à la même catégorie mentale que le découpage walk-forward ou la fonction objectif : un paramètre de backtest de première classe que vous faites varier, et non une constante environnementale que vous fixez une fois pour toutes. Si le rang d'une variante est stable entre M2 et M3, son edge est réel par rapport aux coûts. Si son rang s'effondre en montant l'échelle, vous avez appris qu'il s'agissait d'un artefact du modèle de coûts — le même événement épistémique qu'une stratégie qui meurt hors échantillon dans l'optimisation walk-forward, et cela mérite la même réponse. Et si vous avez sélectionné l'argmax de 200 variantes sous M1, la logique du Sharpe déflaté s'applique avec une torsion supplémentaire : vous avez mené une recherche de tests multiples où la statistique de test elle-même était biaisée en faveur des entrées à turnover élevé.

Bornes honnêtes : le PnL comme une bande

La discipline finale découle du fait d'admettre que le modèle de coûts reste incertain même après ajustement. YY a un intervalle de confiance, les multiplicateurs de stress sont estimés sur une poignée de jours extrêmes, et l'avenir tirera ses propres régimes. Alors cessez de présenter le PnL net comme un chiffre unique. Présentez-le comme une bande à travers des scénarios de coûts nommés :

  • Optimiste : borne inférieure de traversée en profondeur, sans multiplicateurs de stress. À ne jamais présenter seul ; son seul rôle est de borner le meilleur cas.
  • Réaliste : courbe ajustée, multiplicateurs de régime ajustés — votre estimation centrale.
  • Pessimiste : Y×1.5Y \times 1.5, spread ×2\times 2, ligne de stress toujours appliquée à sa valeur \geq p75. En gros : la courbe ajustée évaluée au bord défavorable de l'intervalle de confiance de chaque paramètre.

Deux règles de décision rendent la bande opérationnelle. Premièrement : allouer sur le pessimiste, présenter le réaliste. Une stratégie qui ne survit que sous le modèle réaliste est un pari selon lequel votre ajustement de coûts ne comporte aucune erreur dans le sens coûteux — un pari que vous n'avez pas placé consciemment. Deuxièmement : suivre le ratio de fragilité — largeur de la bande rapportée au PnL brut. D'après l'expérience ci-dessus : BO-4h à 0,9x de turnover affiche un brut de +39%, une bande [+22%, +29%], une fragilité de 0,44. MR-15m à 11,4x affiche un brut de +187%, une bande [−31%, +34%] — fragilité de 2,2, et le signe de la stratégie s'inverse à l'intérieur de la bande. Cette seconde stratégie n'a pas de signe déterminable à l'état actuel de nos connaissances sur les coûts ; les affirmations honnêtes sont « nous ne savons pas » et « réduire le turnover jusqu'à ce que nous sachions ». Un backtest qui affiche +34% et omet le bord à −31% de la bande n'est pas optimiste, il est infalsifiable.

La bande ne coûte rien à produire — trois entrées de plus dans le balayage de modèles de coûts que vous menez désormais de toute façon — et elle change les conversations. « Cette stratégie fait 20% » invite à la croyance. « Cette stratégie fait 11–19% selon nos scénarios de coûts, et reste positive sous le scénario pessimiste » invite à l'allocation.

Où cela s'insère

Dans le moteur de backtest, le modèle de coûts se situe exactement là où le simulateur de fills restitue une quantité exécutée : les fills déterminent ce que vous avez obtenu, la courbe de coûts détermine ce que vous avez payé pour l'obtenir, et les échelons au-dessus de L2 sur cette échelle (traversée en profondeur, modèles de file d'attente) absorbent progressivement le modèle de coûts dans le modèle de fills lui-même — la courbe est le substitut paramétrique que vous utilisez quand vous ne simulez pas directement le carnet. Les paramètres proviennent de votre propre relevé TCA quand vous en avez un, et de données publiques mises à l'échelle par sécurité quand vous n'en avez pas. La forme fonctionnelle provient de quarante ans de théorie et de mesure — Kyle (1985) pour expliquer pourquoi le coût par dollar dépend de la liquidité, Almgren et al. (2005) et Tóth et al. (2011) pour la loi concave elle-même, avec Almgren-Chriss comme pendant en matière de planification et les modèles d'impact neuronaux comme frontière de prédiction. Ce que cet article ajoute, c'est le flux de travail : ajuster la courbe, la conditionner au régime, la balayer comme un paramètre, et présenter la bande. Rien de tout cela n'est glamour. Tout cela coûte moins cher que de découvrir, en trading réel, de quel côté de slippage_bps = 5 votre stratégie se trouvait en fait.

blog.disclaimer

Authors

Eugen Soloviov
Eugen Soloviov

Trading-systems engineer

Trading-systems engineer building bots since 2017: cross-exchange arbitrage (connected up to 30 venues), cointegration-based pairs arbitrage across spot and futures, scalping, news and sentiment-driven strategies, trend algorithms, and portfolio management and balancing algorithms. Also builds sub-millisecond order execution, big-data warehouses, backtesting engines, AI agents, and trading interfaces (incl. open-source profitmaker.cc). Stack: JS/TS, Python, Rust/Zig/Go, DevOps, backend, frontend, architecture.

Newsletter

Gardez une longueur d'avance sur le marché

Abonnez-vous à notre newsletter pour des insights exclusifs sur le trading IA, des analyses de marché et des mises à jour de la plateforme.

Nous respectons votre vie privée. Désabonnement possible à tout moment.