Skip to content

[QC][Explorer] Évaluer « Multi-Horizon ETF Momentum Rotation » (676) sous frais du courtier #19174

Closed

Description

@jsboige

Source

QuantConnect Strategy Explorer, stratégie publique 676 « Multi-Horizon ETF Momentum Rotation Strategy » (auteur affiché : Jon Thibodeaux), version 1.1.0 du 01/10/2026. Fiche lue le 2026-10-04 par l'API publique POST /api/v2/strategies/read (projet à cloner : 34803853, backtest de référence : 828ad393d1ac88255bac5a44e80fe3eb, discussion 21181).

Description publiée : rotation mensuelle entre 20 ETF (actions US et internationales, obligations, matières premières, devises). Score de momentum = moyenne de cinq taux de variation sur 5, 21, 63, 126 et 252 séances, chacun mesuré avec un saut de 21 séances. Le premier jour de bourse du mois, les cinq ETF au meilleur score sont retenus ; si les cinq scores sont négatifs, tout passe en liquidités. Poids inversement proportionnels à l'écart-type des rendements journaliers sur 63 séances. Toutes les positions sont liquidées avant la pose des nouvelles cibles. La version 1.1.0 a ajouté le saut de 21 séances et cinq ETF à l'univers (UUP, FXF, AIA, DBA, RLY).

Chiffres affichés par la fiche (non vérifiés, Sharpe au sens de QuantConnect) : CAGR 5 ans 8,07 %, pire baisse 5 ans 15,6 %, Sharpe 1 an 0,914. Aucun historique hors échantillon : la version évaluée date du 01/10/2026.

Pourquoi

C'est la première stratégie de rotation d'ETF multi-classes du Strategy Explorer évaluée ici, après deux stratégies sur actions individuelles (#18904, #18905). Deux de ses briques existent déjà dans le dépôt : le momentum multi-horizons et le dimensionnement par l'inverse de la volatilité (#19072). La question est donc moins « l'idée tient-elle ? » que « apporte-t-elle quelque chose face aux allocations d'ETF du dépôt, et à quel coût en frais ? ».

Deux points de vigilance :

  • la liquidation complète chaque mois fait payer deux fois les frais des lignes conservées ;
  • un univers de 20 lignes et cinq horizons laisse beaucoup de degrés de liberté à une stratégie publiée il y a quelques jours.

Protocole

Même protocole que #18904, même règle de verdict, mêmes références :

  1. Lire le projet publié ; à défaut, réimplémentation déclarée d'après la description publique, chaque point laissé ouvert tranché dans le README.
  2. Backtest QC avec les frais du courtier (modèle par défaut de Lean) sur la fenêtre principale 2018-01-01 → 2026-09-25, puis sur la fenêtre de la fiche (écart de reproduction seulement).
  3. Mesures : Sharpe, CAGR, pire baisse, rotation annuelle, frais annuels, Sharpe à frais doublés ; comparaison au SPY détenu et au 60/40 SPY/IEF ; corrélations hebdomadaires avec les paniers proxys des allocations d'ETF du dépôt.
  4. Verdict BEATS / NO BEATS / INCONCLUSIVE, avec robustesse par sous-période, à frais doublés et sur des paramètres voisins (grille fixée avant le calcul).
  5. Point propre à cette stratégie : ce que coûte la liquidation complète, mesuré contre la même règle qui ne traite que les écarts.
  6. Geler le code à la date du verdict et l'inscrire au registre du suivi en ombre ([ML-Pipeline][#18907 5c] Suivi en ombre : geler les candidats à une date D et les rejouer chaque mois, historique hors temps sans biais de sélection #18923).

Livrable

Un projet sous MyIA.AI.Notebooks/QuantConnect/projects/ avec attribution à l'auteur et lien vers la fiche. Backtests annoncés sur le dashboard (limite de 10 appels/min partagée par la flotte).

See #18904.

Activity

  1. jsboige commented on Oct 4, 2026

    @jsboige
    OwnerAuthor

    [CLAIMED] lane myia-po-2024:CoursIA-3 -- réimplémentation déclarée, backtests QC et verdict selon un pré-enregistrement posté avant le premier run -- paths: MyIA.AI.Notebooks/QuantConnect/projects/MultiHorizonMomentum676/**

  2. jsboige commented on Oct 4, 2026

    @jsboige
    OwnerAuthor

    [PRÉ-ENREGISTREMENT] lane myia-po-2024:CoursIA-3 — évaluation de la 676 en réimplémentation déclarée. Posé avant le premier backtest : aucun run de ce projet n'existe à l'heure de ce commentaire (son horodatage fait foi).

    1. Ce qui est évalué

    Le projet publié 34803853 n'est pas lisible par le compte de la flotte (files/read : « You do not own this project »), et la page de la discussion 21181 ne rend pas son contenu sans navigateur. J'écris donc une réimplémentation déclarée d'après la description publique de la fiche, comme pour #18904. Aucune ligne du code d'origine n'est reprise.

    Projet, sur une branche à venir : projects/MultiHorizonMomentum676/. Références : celles de #18904 (voir §4).

    Point laissé ouvert Choix déclaré
    Univers (20 ETF) les 5 ajouts nommés par la version 1.1.0 (UUP, FXF, AIA, DBA, RLY) et 15 lignes choisies ici : actions US SPY, QQQ, IWM, VNQ ; actions hors US EFA, EEM, EWJ, VGK ; obligations TLT, IEF, SHY, LQD, TIP ; matières premières GLD, DBC
    Taux de variation avec saut P[t−21] / P[t−21−h] − 1 pour h ∈ {5, 21, 63, 126, 252}, sur les clôtures journalières ajustées
    Score moyenne simple des cinq taux de variation
    Moment du calcul à l'ouverture du premier jour de bourse du mois, sur les clôtures jusqu'à la séance précédente
    « Si les cinq scores sont négatifs » lecture littérale : liquidités si les cinq retenus sont négatifs ; sinon les cinq lignes sont détenues, y compris celles de score négatif
    Liquidités dollars non investis, rendement nul (pas d'ETF monétaire)
    Poids 1 / écart-type (ddof 1) des 63 derniers rendements journaliers, normalisés à une somme de 1 ; aucun levier
    Liquidation lecture littérale : tout est vendu, puis les cinq cibles sont achetées, même pour une ligne conservée (paramètre liquidate=1)
    Exécution ordres au marché, événement 30 minutes après l'ouverture ; frais du courtier, modèle par défaut de Lean, mis à l'échelle par fee_mult

    Conséquence : un écart à la fiche peut venir de ces choix, surtout de l'univers, autant que de la stratégie. Il est rapporté, il n'entre pas dans le verdict.

    Chiffres de la fiche, relus le 2026-10-04 : CAGR 5 ans 8,07 %, pire baisse 5 ans 15,6 %, Sharpe 1 an 0,914 au sens de QuantConnect ; backtest de référence 828ad393….

    2. Fenêtres

    Fenêtre Dates Rôle
    Principale 2018-01-01 → 2026-09-25 verdict (même fenêtre que #18904 et #18905)
    Fiche 2021-10-01 → 2026-09-30 écart de reproduction seulement. Je suppose que la fiche couvre les 5 ans précédant sa publication (01/10/2026) ; l'hypothèse n'est pas vérifiable sans le code
    Sous-périodes 2018-2020, 2021-2023, 2024 → 2026-09-25 robustesse ; tranchées dans le run principal, sans nouveau backtest

    3. Mesures

    Comme #18904 : valeur du portefeuille à chaque clôture (graphique shadow, séries e0..e4, contrat du rejeu en ombre de #18923) ; Sharpe à taux sans risque nul (rendements journaliers, ddof 1, √252), CAGR et pire baisse par strategy_metrics.py ; rotation annuelle et frais annuels par les séries turnover et fees. Les statistiques du rapport QuantConnect ne sont pas utilisées. Chaque rebalancement est écrit dans le journal de l'algorithme (lignes retenues, poids, mois en liquidités).

    4. Règle de verdict

    Identique à #18904. Deux comparaisons dans la fenêtre principale :

    • Sharpe(676) − Sharpe(SPY détenu) ;
    • Sharpe(676) − Sharpe(60/40 SPY/IEF).

    Références réutilisées, pas relancées : les runs spy et 6040 de #18904 (projet FourSleeve774Benchmarks, main.py de sha256 8c4088c51967…, PR #19139) couvrent exactement la même fenêtre, avec les mêmes frais et le même rebalancement le premier jour de bourse du mois. Les séries sont alignées par date ; les séances manquantes d'un côté sont comptées et rapportées.

    Test : voltarget_strategy_verdict.circular_block_diff, bootstrap circulaire par blocs de 21 séances, 10 000 tirages, graine 18921, mêmes indices pour les deux séries. La p-valeur unilatérale est la part des tirages où la différence est ≤ 0. Correction de Holm sur les deux comparaisons.

    Verdict Condition, pour chaque référence
    NO BEATS différence observée ≤ 0
    BEATS p Holm < 0,05 et les trois conditions de robustesse ci-dessous
    INCONCLUSIVE tout autre cas

    Conditions de robustesse :

    1. La différence reste > 0 à frais doublés (fee_mult=2).
    2. La différence est > 0 sur au moins 2 des 3 sous-périodes.
    3. La différence est > 0 sur au moins 3 des 4 points de grille.

    Grille, un paramètre à la fois : top ∈ {3, 7} ; skip=0 (sans saut) ; weights=equal. Elle n'est lancée que si le test principal rend p Holm < 0,05 pour au moins une référence : sinon BEATS est déjà exclu et la grille ne peut plus changer le verdict (leçon du complément 1 de #18904, posée ici d'avance).

    5. Point propre à la 676 : le coût de la liquidation complète

    Un run liquidate=0 dans la fenêtre principale : mêmes cibles, mais seuls les écarts sont traités. Rapportés, hors verdict : différence de frais annuels, de rotation annuelle et de CAGR entre liquidate=1 et liquidate=0.

    Corrélations hebdomadaires, Pearson sur les rendements du vendredi, sans test, sur la fenêtre principale : avec les paniers proxys vt2 (SPY/QQQ/IEF/GLD à poids égaux) et aw (SPY 0,30 / IEF 0,30 / GLD 0,30 / XLP 0,10) de #18904, réutilisés eux aussi, et avec la 774.

    6. Ordre des runs

    Un backtest à la fois, annoncé sur le dashboard, lu seulement à completed: true :

    1. base principale ;
    2. frais doublés ;
    3. fenêtre de la fiche ;
    4. liquidate=0 ;
    5. grille, seulement sous la condition du §4.

    Les séries brutes sont rangées hors dépôt, et leur chemin sera cité dans le résultat.

    7. Après le verdict

    Le code est gelé au SHA du verdict et inscrit au registre du suivi en ombre (#18923), puis rejoué chaque mois sur la période écoulée.

    Ce pré-enregistrement ne sera pas modifié. Tout changement de règle ou de grille sera posté en complément daté, avant le run qu'il concerne.

  3. jsboige commented on Oct 4, 2026

    @jsboige
    OwnerAuthor

    [COMPLÉMENT daté 2026-10-04, avant toute relance] lane myia-po-2024:CoursIA-3 — défaut d'implémentation dans main.py en mode liquidate=1 ; trois des quatre runs pré-enregistrés sont annulés et relancés.

    Ce qui a été vu

    Les quatre runs pré-enregistrés ont tourné avec main.py sha256 0d683f60da11… : principal, frais doublés, fenêtre de la fiche, liquidate=0. Le run principal et le run liquidate=0 écrivent les mêmes lignes sel chaque mois, donc les mêmes cibles. Pourtant leurs pires baisses ne se ressemblent pas : −10,8 % pour le premier, −22,7 % pour le second. Les mêmes cibles ne peuvent pas produire deux expositions aussi différentes. J'ai donc lu le journal des ordres des deux backtests (backtests/orders/read, 1 026 et 646 ordres).

    Cause

    En données journalières, un ordre passé à 10 h s'exécute à la clôture. Après liquidate(), l'appel set_holdings(cibles) calcule donc ses quantités contre des positions qui ne sont pas encore vendues. Il n'envoie que les écarts.

    Exemple du 2018-03-01. Le run principal vend tout : AIA −234, EEM −359, EWJ −392, QQQ −124, SPY −118. Il n'achète ensuite que SPY −24, AIA +53, EEM +61, EWJ +18 et QQQ −2. Ce sont exactement les ordres du run liquidate=0 le même jour.

    Sur l'ensemble de la fenêtre, j'ai reconstruit les positions à partir des ordres et comparé l'exposition brute après chaque rebalancement à celle du run liquidate=0, mois par mois sur 104 dates communes :

    • l'exposition médiane du run principal vaut 0,71 fois la cible ;
    • elle tombe sous la moitié 21 mois ;
    • le portefeuille porte au moins une petite position à découvert 61 mois.

    Le run principal ne mesure donc pas la règle déclarée.

    Conséquence

    Run sha 0d683f60da11… Statut
    principal liquidate=1 annulé
    frais doublés liquidate=1 annulé
    fenêtre de la fiche liquidate=1 annulé
    liquidate=0 un seul set_holdings avec liquidate_existing_holdings non touché

    Mesures et décisions de la v1 :

    • J'ai calculé les mesures v1 du run principal avant de trouver le défaut. Elles donnaient un verdict provisoire INCONCLUSIVE contre les deux références, que je n'ai pas publié.
    • La v1 ne sert à rien d'autre.
    • Les traces v1 (séries, journaux d'ordres, mesures) sont déposées hors dépôt dans le sous-dossier v1 du dossier des traces de l'évaluation. Rien n'est effacé.

    Correctif

    Il ne touche que la mécanique d'ordre. La règle, l'univers, les fenêtres, les références, la règle de verdict et la grille conditionnelle ne changent pas.

    Avec liquidate=1, les quantités cibles absolues sont fixées avant la liquidation : position actuelle plus l'ordre qui mène à la cible. La liquidation et les achats sont ensuite envoyés ensemble. Tout s'exécute à la même clôture, ventes d'abord.

    Nouveau main.py : sha256 a5f8a4248526… (6 787 octets).

    Relance

    Les quatre runs sont relancés sous ce code, avec les mêmes paramètres, un à la fois : principal, frais doublés, fenêtre de la fiche, liquidate=0. Le run liquidate=0 est relancé pour que les quatre runs partagent le même code.

    Contrôle prévu avant toute mesure : relire le journal des ordres du nouveau run principal et vérifier qu'au 2018-03-01 il revend tout puis rachète les cibles en entier.

    Déclaration (pas un changement)

    Janvier 2018 n'est investi ni par la 676 ni par les références de #18904. En données journalières, aucun prix n'est encore connu à 10 h le 2 janvier, et le premier investissement a lieu le 1er février. La mécanique est la même des deux côtés ; je la conserve pour garder la symétrie de la comparaison.

  4. jsboige commented on Oct 5, 2026

    @jsboige
    OwnerAuthor

    [RÉSULTAT] lane myia-po-2024:CoursIA-3 — évaluation de la 676 en réimplémentation déclarée, selon le pré-enregistrement #19174 (comment), posé avant le premier backtest.

    Les runs viennent du code corrigé décrit dans le complément #19174 (comment) (main.py sha256 a5f8a4248526…). Ce complément a été posté avant leur relance. La règle de verdict et la grille n'ont pas changé.

    Verdict : NO BEATS contre les deux références

    Fenêtre principale 2018-01-01 → 2026-09-25, 2 194 rendements journaliers alignés, aucune séance manquante d'un côté ni de l'autre.

    Méthode :

    • Sharpe à taux sans risque nul (ddof 1, √252), sur la valeur du portefeuille à chaque clôture ;
    • circular_block_diff, blocs de 21 séances, 10 000 tirages, graine 18921 ;
    • correction de Holm.
    Comparaison Différence de Sharpe p brute p Holm IC 95 % Verdict
    676 − SPY détenu −0,110 0,703 1,000 [−0,600 ; 0,347] NO BEATS
    676 − 60/40 SPY/IEF −0,140 0,727 1,000 [−0,638 ; 0,358] NO BEATS

    Les deux différences sont négatives : c'est la condition de NO BEATS du §4.

    Robustesse. Elle est rapportée pour information ; elle ne peut plus changer le verdict.

    Condition 676 − SPY 676 − 60/40
    Frais doublés −0,128 −0,157
    Sous-période 2018-2020 −0,371 −0,637
    Sous-période 2021-2023 −0,013 +0,225
    Sous-période 2024 → 2026-09-25 −0,117 −0,110
    Sous-périodes positives 0 sur 3 1 sur 3

    Grille non lancée, comme le prévoyait le §4 : aucune référence n'atteint p Holm < 0,05.

    Mesures par run

    Sharpe à taux sans risque nul. Frais annuels en % du capital de départ.

    Run Sharpe CAGR Pire baisse Rotation / an Frais / an Ordres
    676 (liquidate=1) 0,675 8,48 % −22,72 % 23,6 0,27 % 1 035
    676, frais doublés 0,658 8,23 % −22,76 % 23,6 0,54 % 1 035
    676, liquidate=0 0,683 8,59 % −22,74 % 9,0 0,14 % 646
    676, fenêtre de la fiche (2021-10-01 → 2026-09-30) 0,776 9,58 % −12,15 % 23,3 0,25 % 585
    SPY détenu (réutilisé, #18904) 0,785 13,92 % −33,61 % 0,11 0,00 % —
    60/40 SPY/IEF (réutilisé, #18904) 0,815 8,87 % −21,19 % 0,32 0,02 % —

    Coût de la liquidation complète (§5, hors verdict)

    Mêmes cibles chaque mois, seule la façon de les atteindre change :

    Mesure liquidate=1 liquidate=0 Écart
    Rotation / an 23,6 9,0 +14,6
    Frais / an 0,272 % 0,141 % +0,13 point
    CAGR 8,48 % 8,59 % −0,11 point
    Sharpe 0,675 0,683 −0,008

    Vendre puis racheter les lignes conservées multiplie la rotation par 2,6 et presque double les frais. Cela coûte environ 0,1 point de rendement par an, sans rien changer à l'exposition.

    Le run liquidate=0 relancé sous le nouveau code donne une série identique, octet pour octet, à celle de la v1. Le correctif ne touche donc que le chemin liquidate=1.

    Écart à la fiche (hors verdict)

    Sur la fenêtre de 5 ans supposée de la fiche (2021-10-01 → 2026-09-30, hypothèse déclarée au §2) :

    Mesure Ce run Fiche
    CAGR 9,58 % 8,07 %
    Pire baisse −12,15 % −15,6 %

    Le rendement est plus haut ici d'environ 1,5 point, et la pire baisse plus faible. L'écart peut venir de l'univers (15 lignes sur 20 sont les nôtres) autant que d'un autre point laissé ouvert, et il n'est pas attribuable sans le code d'origine. Le Sharpe 1 an de la fiche (0,914) est calculé au sens de QuantConnect, avec taux sans risque : il n'est pas comparé.

    Ce que fait la règle, mois par mois

    Source : journal de l'algorithme, 105 rebalancements de janvier 2018 à septembre 2026. Les lignes sel de la v1 et de la v2 sont identiques, car la sélection ne dépend pas de la mécanique d'ordre.

    • Aucun mois en liquidités. La condition « les cinq scores négatifs » ne s'est jamais réalisée dans la fenêtre.
    • Lignes de score négatif. 8 mois sur 105 en détiennent au moins une (14 positions en tout) ; c'est la lecture littérale déclarée.
    • Lignes reconduites. En moyenne 3,5 lignes sur 5 sont reconduites d'un mois sur l'autre. La liquidation complète les vend puis les rachète quand même : c'est le coût mesuré plus haut.
    • Lignes les plus souvent retenues : QQQ (70 mois), SPY (53), GLD (52), AIA (43), DBC (34). Les obligations longues et intermédiaires le sont rarement (TLT 17, IEF 10), et FXF et TIP presque jamais (3 et 2).

    Corrélations hebdomadaires (sans test)

    Pearson sur les rendements du vendredi, fenêtre principale :

    Panier Corrélation avec la 676 Sharpe du panier
    vt2 0,76 1,048
    aw 0,73 1,033
    774 0,73 0,978

    Ces trois paniers ont un Sharpe nettement supérieur à celui de la 676 (0,675) sur la même fenêtre.

    Version du code et traces

    • Code. projects/MultiHorizonMomentum676/main.py, sha256 a5f8a4248526… (6 787 octets). L'empreinte du fichier du projet QC a été revérifiée à chaque lecture de run. Un seul compile pour les 4 runs.
    • Contrôle du correctif. Le journal des ordres du run principal a été relu avant toute mesure. Le 2018-03-01, il revend les cinq lignes, puis rachète les cibles en entier : AIA 287 = 234 + 53, SPY 94 = 118 − 24. Les 1 035 ordres sont tous remplis, aucun n'est invalide.
    • Runs annulés. Les runs v1 (0d683f60da11…) annulés par le complément restent déposés dans le sous-dossier v1.
    • Références. Runs spy, 6040, vt2, aw et base de [QC][Explorer] Évaluer « Four-Sleeve Adaptive Growth » (774) sous frais IBKR #18904 (FourSleeve774Benchmarks, main.py sha256 8c4088c51967… ; 774 9b4ff36fc02d…), lus dans leurs dossiers, pas relancés.
    • Mesures. strategy_metrics.py et voltarget_strategy_verdict.circular_block_diff du main de CoursIA au commit f1794486ae. Ces deux fichiers n'ont pas changé depuis d0103b51a7.
    • Séries et rapports bruts, un dossier par run : G:\Mon Drive\MyIA\IA\QC-traces\19174-multi-horizon-676\ (hors dépôt).

    Suite

    PR à venir avec le projet projects/MultiHorizonMomentum676/ : code, README et cette section de résultats. Après le merge, comme annoncé au §7 :

  5. added a commit that references this issue on Oct 5, 2026
  6. added a commit that references this issue on Oct 5, 2026
  7. added 2 commits that reference this issue on Oct 5, 2026
  8. added 2 commits that reference this issue on Oct 5, 2026
  9. myia-ai-01 commented on Oct 7, 2026

    @myia-ai-01
    Collaborator

    Fermeture par le coordinateur. Livrée par #19182 et #19250 :

    — ai-01, cycle 07/10 06:13Z

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    qcquantconnect-researchMoissonnage QC research — distillation intelligente

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions