Passer au contenu

Voir l’original

BtcSpotDualTimeframeMomentumConfluenceLong

Hypothèses

BTC Spot Confluence de Momentum Double Timeframe, LONG-ONLY 1x (BTCUSDT.BINANCE_SPOT, 4H Primaire + Confirmation 1D, Long Uniquement Quand les BOTH Timeframes Pointent UP, FLAT vers Cash Sinon, Sortie Suiveuse ATR + Rupture de Régime Journalier, Paramètres Optimisés à la Source Conservés)

Hypothèses

Un portage LONG-ONLY, 1x, compte CASH de la stratégie promue la plus performante de la factory (BtcDualTimeframeMomentumConfluenceLS, Sharpe 1.99, 458 trades, +$4,774 sur 29 jours de paper trading) sur BTCUSDT.BINANCE_SPOT, créé spécifiquement en tant que FIRST LIVE-TRADING CANDIDATE car la venue live est spot uniquement. Mécanisme inchangé, pur-OHLCV, mono-instrument : jambe FAST = signe du rendement suiveur sur 18 barres sur des barres 4-HOUR ; jambe SLOW = signe du rendement suiveur sur 15 barres sur des barres DAILY (lu via on_extra_bar comme jambe de contexte, jamais comme barrière de synchronisation). Les deux doivent AGREE. Le seul changement structurel que le compte CASH force est de réduire {LONG/FLAT/SHORT} à {LONG/FLAT} : là où le parent futures passe SHORT en cas de confluence baissière conjointe, la version spot conserve 100% USDT. Tout le reste est préservé mot pour mot : stop suiveur ATR(14) à 3.033x ATR depuis l'extrême courant, sortie sur rupture de régime journalier, dimensionnement relatif au capital ajusté à la volatilité (risk_pct 1.011% / distance de stop 3.033xATR), plafond de notionnel maximal 49.7%, et le vecteur de paramètres exact sélectionné par l'optimisation source (fast_lookback 18, slow_lookback 15, trail_atr_mult 3.033). Trois paramètres ajustables, pas de données supplémentaires, pas de shorts, pas de levier. Changements de code spécifiques au spot : venue BINANCE_SPOT CASH (pas de multiplicateur de levier dans position_size), size_precision 5 / price_precision 2, min_notional $10, ventes dimensionnées uniquement selon la quantité de base détenue. Données vérifiées présentes : BTCUSDT.BINANCE_SPOT barres 4-HOUR et 1-DAY s'exécutent en continu depuis 2017-08-17 (~9 ans, ~19,800 barres 4H).

Hypothèses

Portage direct du parent futures promu BtcDualTimeframeMomentumConfluenceLS sur la venue spot, car la venue live prévue est spot uniquement. Mécanisme préservé mot pour mot : les deux mêmes jambes de momentum, la même porte AGREE-ou-plat, le même stop suiveur ATR(14) 3.033x depuis l'extrême courant, la même sortie sur rupture de régime journalier, le même dimensionnement risk_pct / distance de stop ajusté à la volatilité avec un plafond notionnel 49.7%, et le vecteur de paramètres exact sélectionné par l'optimisation source (fast_lookback 18, slow_lookback 15, trail_atr_mult 3.033). Le compte CASH force exactement un changement structurel, comme le spécifie l'hypothèse : {LONG,FLAT,SHORT} se réduit à {LONG,FLAT} — should_enter retourne BUY uniquement en cas de hausse conjointe et None sinon, donc la baisse conjointe donne 100% USDT plutôt qu'un short. Modifications spécifiques au spot : venue BINANCE_SPOT avec levier 1.0 et aucun terme de levier dans position_size, size_precision 5 / price_precision 2 (BTCUSDT spot), min_notional relevé à $10, et toutes les sorties acheminées via le close_position reduce-only du template de base, de sorte qu'un SELL ne peut jamais que réduire la quantité de base détenue (un short spot nu est impossible par construction, et le garde-fou _would_open_spot_short du template de base n'est jamais déclenché). calculate_signal retourne le rendement continu 18-bar 4H à chaque barre (il varie de barre en barre) ; la porte de confluence discrète se trouve dans should_enter, comme requis. Le coût par barre est O(1) : le ATR utilise une fenêtre fixe de 15 barres et le rendement rapide est une simple consultation indexée, donc il n'y a pas de travail qui augmente avec l'historique. Une mise en garde honnête pour l'Analyste : le coût d'aller-retour spot est d'environ ~0.20% contre ~0.10% sur le parent futures, donc l'edge par trade doit couvrir le double de friction — la structure de sortie suiveuse de tendance (trail + rupture de régime, pas de take-profit fixe) est ce qui rend la durée de détention moyenne suffisamment longue pour que cela soit plausible, mais c'est la métrique à surveiller en priorité.

Implémentation

LONG-ONLY 1x BTCUSDT spot (BINANCE_SPOT, CASH) confluence de momentum double timeframe. Jambe FAST = signe du rendement suiveur sur 18 barres sur des barres 4-HOUR (primaire) ; jambe SLOW = signe du rendement suiveur sur 15 barres sur des barres 1-DAY, consommée via on_extra_bar comme jambe de contexte à timeframe plus lente (timeframe différente, donc la barrière d'alignement inter-jambes du template de base ne diffère jamais le signal 4H). Les deux jambes doivent pointer UP pour détenir BTC ; la baisse conjointe (où le parent futures short) et le désaccord signifient tous deux détenir 100% USDT. Sorties : stop suiveur ATR(14) à 3.033x ATR en dessous du pic courant de la position, OR le retournement à la baisse de la jambe journalière sous la position longue (rupture de régime à timeframe supérieure). Le dimensionnement est ajusté à la volatilité et relatif au capital : equity * 1.011% / (3.033 * ATR), plafonné à 49.7% du solde USDT en notionnel, avec multiplicateur de levier NO (le compte CASH ne peut pas emprunter), quantifié à size_precision 5 et rejeté en dessous du notionnel minimum spot de $10. Trois paramètres ajustables, OHLCV pur, pas de données supplémentaires, pas de shorts.

Résultats de vérification

Soit utiliser self._atr (courant) dans la comparaison de trail de should_exit pour correspondre exactement au parent, soit indiquer explicitement dans la description que le trail est ancré à ATR de la barre d'entrée, afin que l'analyste n'attribue pas la divergence par rapport au parent au changement de venue.

Résultats de vérification

Le ATR du stop suiveur est FROZEN à l'entrée (self._entry_atr, capturé dans should_enter / lors de l'adoption de la position) et jamais rafraîchi, donc la distance du trail est 3.033 x ATR-à-l'entrée plutôt que 3.033 x ATR(14) courant. Le parent futures promu (BtcDualTimeframeMomentumConfluenceLS4H) ajuste son trail avec le CURRENT ATR à chaque barre. La formulation de l'hypothèse ('stop suiveur ATR(14) à 3.033x ATR depuis l'extrême courant') est satisfaite dans les deux cas, et comme _extreme est monotone, le niveau du stop ne fait toujours que remonter, donc ce n'est pas un défaut de correction — mais lors d'une expansion de volatilité après l'entrée, le stop effectif est plus serré que celui du parent, ce qui est une réelle différence de comportement par rapport à la stratégie que ceci prétend porter 'mot pour mot'.

Résultats de vérification

Définir l'état du trade à partir du fill (on_order_filled / première barre avec une position ouverte) plutôt qu'à l'intérieur de should_enter, ou le réinitialiser de manière défensive en haut de calculate_signal avant la recherche de position.

Résultats de vérification

should_enter() modifie l'état du trade (_in_long = True, _extreme, _entry_atr) BEFORE que le template de base a dimensionné l'ordre et appliqué la porte de notionnel minimum. Si position_size() retourne 0 ou que la vérification de notionnel minimum de $10 rejette l'entrée, la stratégie reste marquée comme in-long avec un trail armé alors qu'elle est plate. En pratique, cela s'autorégule à la barre suivante (calculate_signal voit que la position est None et réinitialise les drapeaux, et should_exit n'est jamais atteint alors qu'elle est plate), donc il n'y a pas de bug de comportement observable — mais l'invariant est fragile si le chemin de sortie est un jour modifié.

Résultats de vérification

Aucun correctif requis pour la correction. Si une consommation exacte sur la même barre est souhaitée, recalculer _slow_dir paresseusement à partir de self._extra_bars[...] à l'intérieur de calculate_signal plutôt que de le mettre en cache dans on_extra_bar.

Résultats de vérification

Latence d'une barre sur la jambe journalière : la barre DAILY et la dernière barre 4H du jour partagent le même horodatage de clôture. Si le moteur livre d'abord la barre primaire 4H, _maybe_process_primary() s'exécute et enregistre _last_processed_ts, et la mise à jour _slow_dir de la barre journalière (arrivant au même ts) n'est consommée qu'à partir de la NEXT barre 4H suivante. C'est conservateur (cela ne peut que retarder un signal, jamais divulguer des données futures), mais cela signifie que la sortie sur rupture de régime journalier peut se déclencher jusqu'à 4 heures après la clôture journalière dans environ la moitié des cas, et la parité backtest/paper dépend de la stabilité de l'ordre de livraison.

Résultats de vérification

Implémentation fidèle, mais trois éléments que l'analyste devrait évaluer sur l'historique complet : (1) l'aller-retour spot est d'environ ~0.20% contre ~0.10% pour le parent futures, donc le même edge brut se traduit par environ la moitié en net — le sandbox montre déjà une commission de 10.03% du brut ; (2) le vecteur de paramètres (rapide 18 / lent 15 / trail 3.033) a été sélectionné par une exécution d'optimisation sur une DIFFERENT venue, un mix de timeframes et un ensemble de directions (LONG/SHORT futures), donc il s'agit d'un transfert hors échantillon, pas d'une configuration optimisée — le Sharpe du sandbox est 0.527 avec sharpe_ci_low -0.233 et information_ratio -0.760 par rapport au benchmark, c'est-à-dire un livre BTC long uniquement qui sous-performe par rapport à la détention de BTC sur la fenêtre de smoke test ; (3) l'abandon de la jambe short supprime environ la moitié de la population de trades du parent (250 trades, tous longs, exposition 43.6%), donc le Sharpe réalisé devrait être attendu bien en dessous des 1.99 du parent plutôt que proche. Premier point à vérifier sur le backtest réel : avg_trade_return_pct par rapport au plancher spot 0.25% (le sandbox montre 0.82%, mais cette fenêtre exclut 2017 et ne fait que 2401 jours) et si le tercile de volatilité 'normale' (-8.77% dans le sandbox) reste négatif sur l'historique complet.

Revue du backtest

314 trades clôturés sur 3146 jours (2017-08 → 2026-03), metrics_reliable=true, entry_diag 314/314 entrées soumises avec 0 rejetées.

Revue du backtest

Les trades mettent en œuvre l'hypothèse : tous 314 LONG / 0 short (réduction CASH correcte du LONG/FLAT/SHORT du parent), notionnel/equity max 0.50 contre le plafond 0.497, détention moyenne 4j8h et exposition 43.6% reproduisant le parent futures.

Revue du backtest

avg_trade_return_pct 1.20% contre un aller-retour 0.20% BINANCE_SPOT d'environ ~ ; commissions seulement 8.97% du brut, impact 4.33% — pas marginal sur les frais.

Revue du backtest

max_drawdown 10.3% (sous le défaut spot 15%), PF 1.41, payoff 2.07 pour un taux de réussite de 40.4% (forme de tendance correcte), tercile de volatilité stressé +28.0%.

Revue du backtest

Concentration extrême : les 10 premiers sur 314 trades = 93.8% du rendement cumulé par trade, les 20 premiers = 141% (trades 21–314 net négatif).

Revue du backtest

Seulement 6/10 années civiles positives — exactement au plancher strict — et les deux dernières années négatives (2025 −10.5%, 2026 −9.9%).

Revue du backtest

Sharpe 0.664 avec sharpe_ci_low 0.0042 et PSR 0.924 (<0.95) : déjà marginal avant la barre de sélection d'environ ~225 essais.

Revue du backtest

L'inquiétude de QA est partiellement confirmée : le tercile de volatilité 'normale' reste négatif (−7.70%, 16.1% DD), information_ratio −0.757, alpha de seulement +2.4%/an — cela sous-performe par rapport à la détention de BTC, et benchmark_meaningful=true donc ce manque à gagner est réel.

Revue du backtest

Les paramètres sont un transfert hors échantillon depuis une autre venue/ensemble de directions — non ajustés plutôt que surajustés, mais aussi non testés ici.
Rapport de stratégie

Stratmill est un outil de recherche et de paper trading, pas un conseil financier ni un courtier. Les résultats de backtest et de paper trading sont hypothétiques. Le trading comporte un risque de perte.