Chaque nombre de l'article, et d'où il vient
Assez de méthode pour refaire les calculs à la main, ou pour reconstruire une étude équivalente à partir des mêmes données publiques et sous licence.
Données et univers
Deux panels, tous deux en barres de 15 minutes et d'une heure de janvier 2021 à juillet 2026. La crypto est constituée de l'OHLCV des perpétuels Binance margés en USDT, avec le spread coté mesuré par symbole-mois et chaque événement de funding à son horodatage de règlement. Les actions sont des barres consolidées ajustées des splits, limitées aux transactions éligibles au NBBO pendant la séance régulière, lots impairs et déclarations de transactions hors bourse exclus, indexées par un identifiant permanent de titre plutôt que par le ticker et portant un spread NBBO mesuré, pondéré par le temps, pour chaque barre.
L'univers a été figé le 31 décembre 2022 sur la seule liquidité d'octobre à décembre 2022, en retenant les dix noms les plus liquides de chaque marché. MATIC cesse de coter en septembre 2024 et reste absent ensuite plutôt que d'être remplacé, car un remplaçant choisi plus tard utiliserait une information sur les contrats qui ont survécu.
| Marché | Instruments |
|---|---|
| Crypto | BTCUSDT, ETHUSDT, DOGEUSDT, XRPUSDT, BNBUSDT, SOLUSDT, MATICUSDT, LTCUSDT, CHZUSDT, ETCUSDT |
| Actions | TSLA, AAPL, NVDA, AMZN, MSFT, AMD, META, GOOGL, NFLX, XOM |
Bibliothèque de règles
Quatorze familles, trente paramétrages chacune, soit 420 règles. Pour chaque famille, le générateur énumère la grille ci-dessous, écarte les réglages invalides et les doublons, puis retient 30 configurations space-filling avec la graine 20260804. La bibliothèque est sérialisée de façon canonique puis hachée, et chaque runner refuse de démarrer si le hash diffère.
| Famille | Signal à la clôture de la barre t | Grille |
|---|---|---|
| Suivi de tendance | Côté d'une tendance mobile, si sa force dépasse un seuil | lookback 12, 24, 48, 96 ; force 0, 0.5, 1 unités de vol ; détention 1, 2, 4, 8, 16 |
| Retour à la moyenne | À l'inverse d'un écart standardisé à une moyenne mobile | lookback 12, 24, 48, 96 ; z d'entrée 0.5, 1, 1.5, 2 ; détention 1, 2, 4, 8 |
| Breakout | Clôture au-delà de l'extrême de clôture glissant précédent | lookback 6 à 96 ; marge 0, 5, 10 bp ; détention 1, 2, 4, 8 |
| Momentum | Signe d'un rendement cumulé retardé au-dessus d'une force | lookback 3 à 96 ; force 0, 0.25, 0.5, 1 unités de vol ; détention 1, 2, 4, 8 |
| Expansion de volatilité | Direction de court terme quand la vol courte dépasse la vol longue | courte 6, 12 ; longue 24, 48, 96 ; ratio 1.25, 1.5, 2 ; direction 1, 3 ; détention 1, 2, 4 |
| Contraction de volatilité | Direction de court terme pendant une compression | courte 6, 12 ; longue 24, 48, 96 ; ratio 0.4, 0.6, 0.8 ; direction 1, 3 ; détention 1, 2, 4 |
| Croisement de moyennes mobiles | Croisement EMA rapide/lente sur barre terminée | rapide 3 à 24 ; lente 12 à 96, rapide inférieure à lente ; détention 1 à 16 |
| Breakout de canal | Clôture au-delà d'une moyenne plus ou moins un canal d'ATR | lookback 12 à 96 ; largeur 0.5, 1, 1.5, 2 ATR ; détention 1, 2, 4, 8 |
| Retournement RSI | Acheter la survente, vendre le surachat | RSI 6, 14, 28 ; bornes 20/80, 30/70, 40/60 ; détention 1, 2, 4, 8 |
| Retournement Bollinger | À l'inverse d'une clôture hors de la bande | lookback 12, 20, 48, 96 ; largeur 1 à 2.5 écarts-types ; détention 1, 2, 4, 8 |
| Breakout de Donchian | Clôture au-delà du canal plus haut/plus bas précédent | lookback 12 à 96 ; détention 1 à 16 ; confirmation optionnelle sur une barre |
| Confirmation par le volume | Momentum seulement quand le volume normalisé dépasse un ratio | momentum 3 à 24 ; lookback de volume 12 à 96 ; ratio 1, 1.5, 2 ; détention 1, 2, 4, 8 |
| Breakout d'opening range | Cassure des premières barres de la journée de marché | range 1, 2, 4 barres ; marge 0, 5, 10 bp ; entrée au plus tard à 0.5, 0.75 de la journée ; détention 1, 2, 4 |
| Basée sur l'heure | Tranche de séance et direction fixes | fraction de séance 0, 0.25, 0.5, 0.75 ; long ou short ; masque de jours de semaine ; détention 1, 2, 4, 8 |
Un signal est une transition d'entrée : il se déclenche quand une règle entre pour la première fois dans une condition longue ou courte, et les barres successives dans la même condition ne sont pas de nouveaux signaux. Dans chaque fenêtre d'entraînement de 24 mois, une règle n'est écartée que si elle compte moins de 120 trades terminés, trade moins de 5 instruments ou produit exactement le même ensemble de signaux qu'une règle de numéro inférieur, et aucune statistique de rentabilité ne sert jamais à en écarter une.
Sept réglages d'expansion de volatilité ne tradent jamais, car le rapport exigé entre volatilité courte et longue est hors d'atteinte pour ces longueurs de fenêtre, et le contrôle de doublons compare les signaux sans la durée de détention, si bien que les règles qui ne diffèrent que par leur durée de détention se réduisent à celle de plus petit numéro. En moyenne, 392 à 396 des 420 règles sont éligibles dans une fenêtre donnée.
Exécution
La décision est prise une fois la barre t terminée. La position entre à l'ouverture de la barre t+1 et sort à la clôture de la h-ième barre détenue, où h est la durée de détention de la règle en barres observées. Il n'y a ni stop ni objectif, donc l'ordre du haut et du bas d'une barre ne décide jamais d'une sortie.
Chaque paire règle-instrument détient au plus une position candidate, et un signal qui arrive alors qu'une position est ouverte est ignoré ; un modèle qui saute un trade ne crée jamais de remplacement.
entry = signal_idx + 1 exit = signal_idx + hold # close of the hold-th held bar gross_bps = direction * log(close[exit] / open[entry]) * 1e4 net_bps = gross_bps - spread_bps - fee_bps - slippage_bps + funding_bps
Features
Soixante-deux features de prix et de volume calculées à la barre t dans l'historique propre de chaque instrument : rendements retardés et momentum sur 1 à 24 barres, range haut-bas, fractions de corps et de mèches de bougie, gap clôture-ouverture, volatilité, distance à la moyenne mobile, position dans le range et distance aux plus hauts et plus bas sur 6 à 96 barres, plus le volume normalisé. Sept autres s'y ajoutent : la force de tendance (distance absolue à la moyenne mobile sur 24 barres rapportée à la volatilité sur 24 barres), la position dans la séance et le jour de la semaine en paires sinus et cosinus, les barres restantes dans la journée de marché et le log de la clôture de la barre t.
La règle apporte sa famille sous forme de vecteur one-hot, ses paramètres ramenés à leur position dans la grille avec des indicateurs de paramètre manquant, la direction et la force du signal, la durée de détention prévue et le nombre de barres depuis son signal précédent. La performance récente est la moyenne, la somme, les gains et les pertes des 20 derniers trades terminés de la règle sur cet instrument, à côté de la moyenne, des gains et des pertes de ses 100 derniers trades terminés sur l'ensemble des instruments, en ne comptant que les trades sortis au plus tard à la barre de décision.
# strategy-level trailing 100: only trades that have already exited count
for p in trades_sorted_by_entry:
while exits[cursor].exit_ts <= p.signal_ts:
ring.push(exits[cursor].net_bps); cursor += 1
p.recent_100_mean = ring.mean()Les données d'exécution (l'ouverture de la barre suivante, chaque colonne de coût et le funding) sont stockées sur chaque ligne et retirées de la matrice du modèle. La position dans la séance et les barres restantes utilisent le nombre de barres de la journée, qui reflète le trou les jours où des barres manquent ; ces jours portent entre 0.002% et 0.33% des trades selon la cellule, et les retirer fait passer le meilleur book de +0.748% à +0.761%.
Un trade entré dans la fenêtre d'entraînement mais sorti après elle est exclu à la fois de l'entraînement et de l'ensemble hors échantillon, il disparaît donc aussi de l'historique des 20 trades, ce qui rend cet historique légèrement en retard mais jamais en avance sur la décision.
Modèles et recherche
Trois familles, chacune ajustée séparément pour la cible de rendement net et la cible de profit, chacune avec 30 configurations fixes. La cible de régression est écrêtée à ses quantiles d'entraînement de 0.5% et 99.5% puis standardisée. Les configurations en échec, constantes ou qui ne convergent pas comptent parmi les 30 et ne peuvent pas être choisies.
| Famille | Implémentation | Plage de la grille sur les 30 configurations |
|---|---|---|
| Linéaire | Ridge pour du L2 pur, elastic net sinon ; elastic net logistique pour le profit | alpha 0.0001 à 0.03 ; ratio L1 0 à 1 |
| Arbres boostés | LightGBM, 150 arbres, sans sous-échantillonnage de lignes ni de colonnes | profondeur 2 à 6 ; feuilles 4 à 31 ; learning rate 0.0054 à 0.091 ; min child 104 à 4,645 ; L2 0.0014 à 68 |
| Réseau de neurones | MLP GELU, 6 epochs, batch de 8,192 | profondeur 1 à 3 ; largeur 16 à 128 ; learning rate 0.0001 à 0.0028 ; weight decay 1.5e-7 à 0.0094 |
Dans chaque fenêtre, les lignes d'entraînement sont découpées chronologiquement en 80/20 entre entraînement interne et validation, avec 16 barres de temps d'horloge retirées avant la coupure. Les modèles de rendement net sont sélectionnés sur l'IC de rang de Spearman en validation et les modèles de profit sur le ROC AUC en validation, les égalités allant au plus petit numéro de configuration. Le gagnant est réajusté sur les 24 mois complets, avec un prétraitement ajusté sur ces seules lignes.
Walk-forward
Quarante-trois fenêtres mensuelles hors échantillon, de janvier 2023 à juillet 2026, dans chacune des quatre cellules marché-horizon, soit 172 fenêtres. Chaque fenêtre s'entraîne sur exactement les 24 mois calendaires précédents, en n'utilisant que les trades dont l'entrée et la sortie tombent toutes deux à l'intérieur, puis prédit une seule fois chaque trade éligible entré au cours du mois calendaire suivant. Un modèle par marché, horizon, famille et cible regroupe toutes les règles et tous les instruments.
for cell in ["equity_1h", "equity_15m", "crypto_1h", "crypto_15m"]:
for month in months("2023-01", "2026-07"):
is_rows = trades(entry >= month - 24M, exit < month, eligible_rules(month))
oos_rows = trades(month <= entry < month + 1M, eligible_rules(month))
for family in ("linear", "gbt", "mlp"):
for target in ("net_return", "profit"):
best = select(family, target, is_rows, n_configs=30) # 80/20 inner split
model = refit(best, is_rows)
oos_rows[f"pred_{family}_{target}"] = model.predict(oos_rows)Cela donne 1,032 modèles retenus sur 30,960 configurations tentées, dont 30,394 se sont ajustées avec succès, et 239,452,104 prédictions hors échantillon sur 39,908,684 trades candidats.
Politiques de trading
Chaque paire règle-instrument reçoit le même poids de base, w0 = 1 / (S × 10), où S est le nombre de règles éligibles dans la fenêtre, si bien que prendre tous les signaux ne peut jamais dépasser 100% d'exposition brute.
baseline w = w0
simple filter w = w0 if at least 2 of { vol_24, trend_strength_24, vol_norm_24 }
exceed the instrument's median over the 24 training months
ML filter w = w0 if prediction > 0 bp (net return) or > 0.5 (profit)
ML sizing q = rank of prediction in the window's sorted validation predictions
w = w0 * (0.25 + 1.5 * q)
ML ranking at each signal timestamp keep ceil(20%) of the new signals, at least one,
by prediction; w = w0 * min(5, n_candidates / n_kept)Les médianes du filtre simple viennent des barres de marché uniques de la fenêtre d'entraînement, et non des lignes de trades, si bien qu'une barre portant de nombreux signaux n'est pas comptée de nombreuses fois. Un filtre qui ne prend aucun trade dans un mois laisse ce mois en cash.
Coûts
| Marché | Spread | Frais par exécution | Slippage par exécution | Funding |
|---|---|---|---|---|
| Crypto | La moitié du spread mesuré à l'entrée plus la moitié à la sortie | 5 bp | 2 bp | Chaque événement réel de la barre d'entrée à la barre de sortie, signé |
| Actions | La moitié du spread mesuré par barre à l'entrée plus la moitié à la sortie | 0.5 bp | 0 | Aucun |
Un funding positif débite les positions longues et crédite les courtes, et les événements de funding sont affectés à la barre qui les contient, de sorte qu'un trade entrant à l'ouverture d'une barre de règlement paie ce règlement. Le funding reste faible à côté des frais : prendre tous les signaux crypto une heure donne -0.28% du capital en funding net sur la période, contre 174.59% payés en frais. Les coûts sont identiques pour chaque politique, c'est pourquoi la barre de coût par trade de l'article bouge à peine alors que la barre brute bouge.
Inférence et critères de validation
Les résultats économiques viennent du ledger hors échantillon concaténé et d'une série de rendements quotidiens non chevauchants, le P&L étant comptabilisé à la sortie de chaque trade. L'article rapporte des rendements quotidiens cumulés par somme, car la composition colle chaque book crypto près de -100% ; le critère enregistré sur le rendement net utilise la série composée.
Chaque contraste est une politique moins son comparateur sur la série quotidienne. L'incertitude vient d'un bootstrap apparié qui rééchantillonne des semaines entières, 10,000 réplications avec la graine 20260804, et Benjamini-Hochberg s'applique sur l'ensemble des 144 contrastes : 4 cellules, 18 politiques de modèle et 2 comparateurs, échecs compris.
weeks = daily_delta.groupby(week) # week-clustered resampling
boot = [weeks.sample(n_weeks, replace=True).sum() for _ in range(10_000)]
p = two_sided(boot)
q = benjamini_hochberg(p_all_144)
# rotation null: same timestamps, same instrument, same count, same exposure budget
for r in range(1_000):
shifted = circular_shift(scores, offset=nonzero, within=(cell, month, instrument))
null[r] = net_of_policy(select(shifted))
passes_rotation = policy_net > quantile(null, 0.95)Un contraste est crédité quand les sept critères tiennent tous : rendement net supérieur au comparateur, rendement net par unité d'exposition brute moyenne supérieur au comparateur, q inférieur à 0.05, plus de la moitié des 43 mois gagnés, rapport rendement/risque mensuel supérieur à 1, une avance positive après suppression du meilleur mois et un résultat au-dessus du 95e percentile du nul de rotation. Quand une politique gagne tous les mois, le ratio rendement/risque est indéfini et le critère échoue ; douze contrastes sont dans ce cas, six en actions 15 minutes et six en crypto une heure.
L'affirmation générale exige un résultat crédité sur les deux marchés, les deux horizons et au moins deux des trois familles de modèles. Les chiffres hebdomadaires absolus cités dans l'article pour des books isolés viennent d'un bootstrap distinct, en clusters de semaines, du P&L hebdomadaire propre à chaque book.
Contrôles
Avant qu'aucun nombre hors échantillon n'existe, la suite au niveau du panel a confirmé que tronquer le futur laisse inchangés toutes les features, signaux et trades antérieurs (différence maximale 0.0), qu'une fuite de cible plantée fait monter l'IC de validation de 0.017 à 0.9999, qu'aucune colonne de coût ou de funding n'atteint la matrice du modèle, que le funding est daté par événement avec les positions longues débitées quand le taux est positif, que les splits connus sur actions sont ajustés et que les cibles à la barre suivante s'alignent à 1.7e-6 bp près. Une référence Python a reproduit le moteur de signaux compilé sur 57,648 trades, à partir d'une configuration de chaque famille, sur 50,000 barres réelles de BTCUSDT.
Après l'exécution, chacune des 39,908,684 lignes candidates hors échantillon a été vérifiée contre les panels source : entrée une barre après le signal, sortie après exactement la durée de détention, prix d'entrée égal à l'ouverture du panel et prix de sortie à sa clôture, rendement brut, spread, frais, slippage et funding recalculés depuis les entrées brutes, identité du net, entrée à l'intérieur du mois évalué et feature de prix égale au log de la clôture de la barre du signal. Chaque poids de filtre, de dimensionnement, de classement et de filtre simple a été reconstruit à partir des prédictions et des médianes d'entraînement stockées, et les totaux agrégés des 92 books de politiques se reconstruisent à 5.3e-15 près.
Les spreads sur actions sont les valeurs par barre figées avec l'étude. Revaloriser chaque trade sur actions avec un nouvel extrait ultérieur des mêmes spreads ne déplace aucune politique de plus de 0.0024 du capital et ne change aucun signe. Les p-values du bootstrap, les q-values BH et les dépassements du nul de rotation se reproduisent aussi exactement à partir de la série quotidienne.
Hashs
| Artefact | SHA-256 |
|---|---|
| Bibliothèque de règles | 501f04df5537f881709027e8755eade85d7ca667acb0051d1fcd4b9dc18bb14a |
| Grilles de modèles | 6c6d89b6cfd435ce875876820443405f5d383956a5673ea38f5d2d410b490e47 |
| Politiques | df469ad083dde73c7be52ed5d33ad4d434faa684a74e7777c200b6d5b988bf1d |
| Features | eedebd594bfa8aa9c56fb282e7d065eef06c99b63a0d07f974668a526862cc02 |
| Configuration combinée | 6fc808d41d71d9930b17eb81a0b6b0d2189ba9b7e084896e70c61a3020c94f0f |
| Configuration de l'analyse | 1e3af923775cf8fca30f2c28da0ecab7803320454cfafd15e9101a33d53b5735 |
| Implémentation de l'analyse | 86f433e42fe14560425f6da690754e5b603509b4b9581bba56d6bd1bd447fb3b |
Chaque nombre que dessinent les graphiques de l'article se trouve dans un seul fichier JSON. Le pipeline complet, de la génération des signaux jusqu'aux scripts d'audit en passant par les ajustements walk-forward, est disponible sur demande à daniel@daru.finance.

