You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
QC Cloud-SectorRotation-Momentum : un rééquilibrage tombé sur une demi-séance passe tout en SHY (garde d'historique suspectée) #20286
En mesurant #20264 sur Cloud-SectorRotation-Momentum (backtests QC du 2026-10-10, fenêtre codée 2018-01-01 → 2025-01-01), un second motif est apparu dans les deux runs, avant et après le correctif de la tranche de minuit. Quand le compteur de 21 séances tombe sur une demi-séance (clôture américaine à 13:00, New York), la stratégie vend tout et passe à 100 % en SHY, alors que le marché est en hausse franche.
Aucun autre rééquilibrage de ces runs ne tombe sur une demi-séance. Les deux qui y tombent passent en défensif.
Pourquoi c'est un défaut et pas un signal
_go_defensive() ne se déclenche que si aucun des cinq titres n'est qualifié (prix au-dessus de la SMA200 et momentum 126 séances positif). Le 2019-07-03, SPY est loin au-dessus de sa SMA200 et son momentum 126 séances est nettement positif (point bas de fin décembre 2018). Quatre titres détenus sortent ensemble : la cause est donc commune à la tranche, pas propre à un titre.
Restent trois sorties de la boucle de qualification (main.py, lignes 65-82) qui ne dépendent pas du marché :
symbol not in data ;
history(...) vide, ou KeyError ;
len(closes) < self.momentum_lookback.
Le garde de #20264 (data.bars.contains_key sur les cinq titres) est passé ce jour-là dans le run corrigé, donc des barres étaient présentes. Hypothèse à vérifier : sur une demi-séance, history([symbol], 126, Resolution.DAILY) rend 125 barres, et la garde de longueur écarte tous les titres. Rien n'est encore mesuré de l'intérieur de l'algorithme.
Protocole proposé
Un backtest de diagnostic sur la fenêtre codée, qui journalise à chaque rééquilibrage len(closes), symbol in data, le prix et la SMA de chaque titre. Le lire aux deux dates ci-dessus.
Recenser les autres projets QC qui combinent une garde len(history) < N à un rééquilibrage pouvant tomber sur une demi-séance.
Portée
Distinct de #20264 (tranche de minuit sans barre) : ici la tranche porte des barres, à une heure de clôture anticipée. Trouvé en mesurant #20264, non corrigé dans sa PR pour garder un sujet par PR.
Traces des deux runs : G:\Mon Drive\MyIA\IA\QC-traces\20264-midnight-slice\Cloud-SectorRotation-Momentum\ (journaux d'ordres complets).
[CLAIMED] lane myia-po-2024:CoursIA-3 -- backtest de diagnostic (longueur d'historique et présence des données à chaque rééquilibrage), puis correctif et mesure avant/après si l'hypothèse 3 tient. paths: MyIA.AI.Notebooks/QuantConnect/projects/Cloud-SectorRotation-Momentum/**
Pré-enregistrement (lane myia-po-2024:CoursIA-3). Écrit avant tout backtest, et suit les étapes 1 et 2 du protocole de l'issue.
Code mesuré (sha256 de main.py, 12 caractères)
Variante
Empreinte
Contenu
avant
2a1ce95cac64
main à 68972f1. Backtest existant a8e7809afb021ce8b42e8b59d61e9e1b (projet 37630571), même empreinte : il n'est pas relancé.
diag
e3e61eebe939
avant avec des journaux en plus, logique de trading inchangée. Code de mesure seulement, il ne sera pas commité.
fix
f853395e1587
avant plus deux lignes : history(..., lookback + 5, ...), puis les 126 dernières clôtures. La garde de longueur ne bouge pas.
Ce que journalise diag
Une ligne par titre à chaque rééquilibrage : présent ou non dans la tranche, prix et SMA200, longueur d'historique avec ses première et dernière dates, momentum. La sortie de boucle est nommée parmi les trois de l'issue.
Exécution : nœuds de backtest QC, fenêtre codée 2018-01-01 → 2025-01-01, diag et fix en parallèle.
Prédictions
diag rend les mêmes ordres que avant (mêmes dates, mêmes titres). Sinon les journaux ont perturbé le run, et la lecture de diag ne vaut plus pour avant.
L'hypothèse 3 tient si deux conditions sont réunies :
au rééquilibrage du 2019-07-03, les cinq titres sont dans la tranche, et ceux qui passent la SMA200 sortent tous sur len125 ;
les longueurs de 125 tombent sur les séances qui ne ferment pas à 16:00, et seulement sur elles.
Elle est réfutée si la sortie du 2019-07-03 est nodata, empty ou key, ou si la longueur y vaut 126. Dans ce cas, pas de correctif dans cette PR : je publie la cause mesurée.
Si elle tient, avec fix :
zéro passage en SHY sur un rééquilibrage de demi-séance ;
un calendrier de rééquilibrage identique à avant, puisque le compteur de 21 séances ne dépend pas des positions.
Ce qui sera publié
Les lignes de diagnostic aux dates concernées, et le résumé de fin de run.
Le nombre de passages en SHY, en séparant demi-séances et séances pleines.
Sharpe, CAGR, pire baisse, ordres et frais de avant et de fix, avec les identifiants de backtest.
Ces chiffres iront dans le README du projet (FR/EN). La ligne SectorRotation de docs/qc/qc-strategies-status.md prendra les chiffres de fix, puisque c'est le code qui serait sur main.
Seuil fixé d'avance : si le Sharpe QC bouge d'au moins 0,05 entre avant et fix, je le signale dans le README. Pas de verdict de stratégie : c'est la correction d'un défaut, sur une fenêtre déjà vue. Le NO-BEATS de #20264 reste en l'état.
Étape 3 (recensement) : un relevé des autres projets QC qui combinent une garde len(history) < N et un rééquilibrage pouvant tomber sur une demi-séance est en cours. Il sera publié avec le résultat. Les éventuels autres projets touchés restent hors de cette PR, un sujet par PR.
Résultat du premier tour, et pré-enregistrement du second (lane myia-po-2024:CoursIA-3). Les deux runs ont été lus à completed: true.
Tour 1 : diag et fix (pré-enregistrement c.6105254259)
Variante
Backtest QC
Ordres
Sharpe
Rééquilibrage du 2019-07-03
avant (2a1ce95cac64)
a8e7809afb021ce8b42e8b59d61e9e1b
309
0,224
100 % SHY
diag (e3e61eebe939)
921283996fe873976fdd9e4adbb45dd5
309
0,224
100 % SHY
fix (f853395e1587)
5bf8331b90febecf6032d057cd11b488
309
0,224
100 % SHY
Prédiction 1 tenue : diag rend exactement les ordres de avant (309 ordres, même date, même titre, même quantité, aucune différence). Sa lecture vaut donc pour avant.
Prédiction 2 : le mécanisme tient, le chiffre est réfuté.
Au rééquilibrage du 2019-07-03 13:00, les cinq titres sont dans la tranche et passent leur SMA200. Ils sortent tous sur la garde de longueur, mais avec 105 barres, pas 125 : QQQ:len105[2019-02-04 16:00..2019-07-03 13:00] SPY:len105[...] EFA:len105[...] GLD:len105[...] IWM:len105[...]
Sur la fenêtre, il y a 15 séances qui ne ferment pas à 16:00. Elles sont toutes courtes, avec 104 à 106 barres pour 126 demandées. Aucune séance pleine n'est courte (résumé de fin de run : non16h=15 court_non16h=15 court_16h=0).
L'historique d'une demi-séance commence environ un mois trop tard : le 2019-02-04 au lieu de début janvier. Il manque une vingtaine de barres, pas une.
La sortie 3 de l'issue est donc la bonne, et la cause est commune aux cinq titres, comme supposé. En revanche, « 125 barres » était faux : je ne l'avais pas mesuré.
Prédiction 3 réfutée pour ce correctif.
fix demande 131 barres. Il en reçoit encore moins de 126 sur une demi-séance, et le run est identique à avant : mêmes 309 ordres, mêmes statistiques, même passage en SHY le 2019-07-03.
Le correctif pré-enregistré ne corrige rien. Il ne sera pas commité.
Pourquoi Lean raccourcit la requête un jour de clôture anticipée n'est pas établi ici. Seul l'effet est mesuré.
L'autre rééquilibrage en SHY de cette période, le 2018-11-29 16:00, est un vrai signal : les cinq titres y sont sous leur SMA200 (ligne DIAG REB du même journal).
Tour 2 : fix2, pré-enregistré avant le run
Code : fix2 = avant + history(..., 2 * lookback, ...), puis les 126 dernières clôtures, sha256 7510c24e4f58. La garde de longueur ne bouge pas. Même fenêtre, nœuds QC.
Prédictions
Ordres identiques avant la demi-séance. Jusqu'au 2019-07-03 exclu, les ordres de fix2 sont identiques à ceux de avant (date, titre, quantité). Sur une séance pleine, les 126 dernières barres d'une requête de 252 sont les mêmes que celles d'une requête de 126.
Pas de défensif à tort. Le 2019-07-03, fix2 ne passe pas en SHY : le journal porte REBALANCE: N assets, et aucun achat de SHY n'est daté d'une demi-séance.
Même calendrier. Les 83 rééquilibrages tombent aux mêmes dates que dans avant.
Écart de trajectoire, descriptif seulement. Après le 2019-07-03, les positions diffèrent : l'écart de trajectoire est publié tel quel.
Si 1 ou 3 échoue, le correctif change autre chose que la demi-séance : il n'est pas commité, et je dirai ce qui a bougé. Seuil et absence de verdict de stratégie inchangés : un écart de Sharpe d'au moins 0,05 est signalé dans le README.
Étape 3, recensement : ce que la mesure change
Un relevé des projets QC qui combinent un history(N) et une garde de longueur avait été fait sous l'hypothèse « une barre de moins ». Il classait comme protégés les projets qui demandent N+5 ou N+10 barres, comme ceux qui gardent sur N*0.8.
Avec un déficit mesuré de 16 à 17 %, ce classement ne tient plus. Une requête de N+5 ou N+10 barres est aussi exposée, comme l'a montré fix ici. Le relevé sera repris avec ce seuil et publié avec le résultat du tour 2. Les corrections éventuelles iront dans d'autres PRs.
Résultat du second tour (lane myia-po-2024:CoursIA-3), lu à completed: true. Pré-enregistrement : c.6105308987.
Tour 2 : fix2 (sha256 7510c24e4f58)
Variante
Backtest QC
Ordres
Sharpe
CAGR
Pire baisse
PSR
avant (2a1ce95cac64)
a8e7809afb021ce8b42e8b59d61e9e1b
309
0,224
6,84 %
27,6 %
0,64 %
fix2 (7510c24e4f58)
5661dc625175652677c63daa90074862
307
0,196
6,29 %
27,6 %
0,50 %
Tenue. Jusqu'au 2019-07-03 exclu, les 54 ordres de fix2 sont identiques à ceux de avant (date, titre, quantité).
Tenue. Le 2019-07-03, le journal porte REBALANCE: 5 assets | Top: QQQ (29.2%) | Total mom: 97.00%. Aucun achat de SHY n'est daté d'une demi-séance. Les deux achats de SHY du run (2018-10-30 et 2022-07-05) tombent sur des séances pleines.
Tenue. Les 83 rééquilibrages tombent aux mêmes dates que dans avant.
Écart de trajectoire, descriptif. Les rendements annuels sont identiques, sauf 2019 (+7,1 % → +3,4 %) et un dixième en 2020-2021. En 2019, le passage en SHY à tort a fait mieux que la position investie : c'est un effet de trajectoire, pas une propriété de la règle. L'écart de Sharpe (−0,028) est sous le seuil de 0,05 fixé d'avance. Pas de verdict de stratégie : le NO-BEATS du README tient.
Le correctif est donc commité (PR à suivre).
Calendrier des demi-séances
Sur 2018-2024, les 15 séances qui ne ferment pas à 16:00 sont les 3 juillet, les lendemains de Thanksgiving et les 24 décembre. Toutes rendent 104 à 106 barres pour 126 demandées. Aucune n'est la première séance d'un mois. Deux sont la dernière séance d'un mois : 2019-11-29 et 2024-11-29.
Un rééquilibrage par date_rules.month_start ne tombe donc jamais sur une demi-séance sur cette fenêtre. Un calcul à date fixe en fin de mois, quotidien ou par compteur de séances (comme ici), peut y tomber.
Recensement repris (lecture des lignes de requête et de garde, non exhaustif)
Requête N, garde N, exposés comme ici si un calcul tombe sur une demi-séance (calendrier de chaque projet à vérifier) :
FuturesTrend main.py:106-107 et 131-132 (contrats à terme : l'effet n'est mesuré que sur des actions US)
Requête N+5, garde N, classée à tort comme protégée au premier relevé : FamaFrench main.py:76-77.
Requête N+5, garde 0,8 N, rééquilibrage month_start : DualMomentum, Trend-Following, VolTarget-Momentum, RiskParity, GlobalMacro-Regime. Ces cinq projets ne sont pas exposés en pratique, puisqu'aucune demi-séance n'est un premier jour de mois. Le code reste fragile pour autant. Sur une demi-séance, environ 0,83 (N+5) barres passent la garde 0,8 N, puis iloc[-N] sort des bornes.
Requête N+5, garde 0,8 N, calcul quotidien : MeanReversion main.py:69-70. La garde passe, mais l'usage de la série reste à vérifier.
Pas repris à ce tour : MomentumStrategy, DualMomentumNoTLT, composite-c1-multiasset, Framework_Composite_FamaFrenchAllWeather, BlackLitterman, ForexCarry (marché des changes : l'effet n'y est pas établi), Paradox46, MultiHorizonMomentum676, la famille HAR-RV et Vol-* (gardes len >= 2).
Ce relevé est une lecture du code, pas une mesure. Avant toute correction, chaque projet suivra le même protocole qu'ici : vérifier sur le backtest publié qu'un calcul tombe bien sur une demi-séance. Les corrections iront dans des PRs séparées. L'issue reste ouverte pour cela.
Pré-enregistrement, suite du recensement c.6105344807 (lane myia-po-2024:CoursIA-3). Écrit avant tout backtest.
Projets : Framework_Composite_MomentumRegime (MR) et MomentumRegime-AdaptiveWeights (AW). Les deux alpha_models.py portent le même code ; ils ne diffèrent que par les poids par défaut de SectorMomentumAlpha.
Lecture du code (main à 68972f1a6b)
SectorMomentumAlpha._composite_momentum (l. 101-103) : history(symbol, 252), garde len < 252. Émission mensuelle, à la première tranche du mois. Aucune demi-séance n'est un premier jour de mois : je ne l'attends pas exposé.
RegimeSwitchingAlpha._apply_momentum (l. 230-231) : history(symbol, 68), garde len < 63. Il est appelé à la première tranche du mois et le jour d'un changement de régime vers bull, qui peut tomber n'importe quel jour. Si ce jour est une demi-séance, l'historique rendu serait d'environ 47 barres (c.6105308987 : environ 21 barres de moins), SPY et QQQ seraient écartés et _apply_momentum rendrait []. Comme _need_rebalance est déjà remis à faux, le passage en bull ne serait pas appliqué avant l'émission du mois suivant.
Mesure : copies de diagnostic hors dépôt, sans changement de logique : compteur par alpha, heure de la tranche et longueur rendue ; une ligne de journal par appel court ou par tranche ni de 16:00 ni de minuit ; une ligne par jour de changement de régime ; une ligne DIAG RS LOST quand le calcul ne retient aucun titre. Mode intent (défaut), fenêtre codée de chaque source (MR 2015-2025, AW 2018-2024), un backtest par projet sur les nœuds QC.
Fichier (sha256, 12 car.)
main
diag
MR alpha_models.py
896d0843fcdd
889177bc2155
MR main.py
b25f2790cdae
55b57ba4a986
AW alpha_models.py
16dfd8dfad78
25e7910ca0c6
AW main.py
33169b0a9842
676f11e015d4
Prédictions
SectorMomentumAlpha : aucun appel d'historique sur une tranche de demi-séance (13:00, New York), dans les deux projets.
RegimeSwitchingAlpha : un appel sur une demi-séance seulement un jour de changement de régime. S'il y en a au moins un : longueur sous 63 et ligne DIAG RS LOST le même jour.
Au moins une émission perdue, ou un appel court qui écarte un seul des deux titres (le partage 70/30 n'est alors plus choisi) : correctif identique à fix(qc,#20286): SectorRotation -- demi-seances, history() en double fenetre #20330 (demander le double, garder les N dernières barres), une PR pour les deux projets, mesurée orig contre fix sur les mêmes fenêtres. Si le Sharpe QC bouge d'au moins 5 centièmes, les lignes du README concernées sont signalées pour réécriture. Pas de verdict de stratégie : c'est la correction d'un défaut, sur des fenêtres déjà vues.
Aucun : pas de correctif. Je mets à jour le recensement ici, et les deux projets passent dans « non exposés en pratique ».
Prédictions : 1 tenue (aucun appel de SectorMomentumAlpha sur une demi-séance). 2 tenue au sens strict, mais sans exemple : aucun changement de régime n'est tombé sur une demi-séance, donc aucun appel de RegimeSwitchingAlpha à 13:00. 3 tenue : toutes les longueurs rendues égalent la demande, à 16:00 comme à minuit.
Décision (règle fixée d'avance) : aucune émission perdue, donc pas de correctif.
Le chemin reste exposable. Un passage en bull un jour de demi-séance ferait perdre l'émission jusqu'au mois suivant. Sur ces fenêtres, c'était environ une chance sur cinq (MR : 23 passages en bull sur environ 2 760 séances, 23 demi-séances) et une sur sept (AW), et c'est resté sans exemple. Ces deux projets passent dans « exposables, non atteints sur les fenêtres publiées ». Le correctif de #20330 s'y transposerait tel quel le jour où l'un d'eux tourne sur une autre fenêtre.
Descriptif, à verser à #20264 : la première tranche du mois est une tranche de minuit 120 mois sur 132 en MR (la tranche d'avis de dividende décrite en #20264). L'émission mensuelle de SectorMomentumAlpha y a donc lieu. MR passe 202 ordres sur 683 à minuit (heure de New York), AW 108 sur 362. Ce n'est pas le défaut de #20264 : les alphas lisent le dernier prix connu, les longueurs d'historique sont pleines, et l'ordre part à l'ouverture suivante comme depuis une tranche de 16:00. Je le note sans le qualifier davantage.
Recensement, mis à jour
Lecture des 17 appels d'historique pris en décision de trading, déléguée puis vérifiée ligne à ligne sur les cas qui changent la liste. Elle corrige c.6105344807 sur trois points.
Mesuré, non atteint : Framework_Composite_MomentumRegime, MomentumRegime-AdaptiveWeights (ci-dessus).
Exposés par la même mécanique (appel dans on_data, sur la tranche de 13:00), non mesurés :
Sparse-Index-Tracking-QC (main.py:254-257, compteur de 63 jours ; un appel court reporte le rééquilibrage au trimestre suivant, car le compteur a déjà avancé) ;
SectorMomentum (main.py:88-94, sortie quotidienne quand SPY passe sous sa SMA200 ; un appel court retarde la rotation défensive d'un jour, puisque la condition reste vraie le lendemain).
Appels depuis un événement planifié à 10:00 New York (after_market_open("SPY", 30)) :
MeanReversion et FuturesTrend tous les jours ;
ML-XGBoost et ML-TextClassification le lundi.
Le défaut n'a été mesuré qu'à 13:00, sur la tranche de clôture d'une demi-séance. Qu'un appel à 10:00 le même jour rende aussi moins de barres n'est pas établi, et c'est la prochaine mesure. MeanReversion serait le cas le plus fin : sa garde (len < 0,8 N) rend un rendement nul au lieu d'écarter le titre, ce qui neutraliserait le filtre de force relative ce jour-là.
Corrigé, non exposés :
Cloud-VolTargeting (month_start, main.py:60-64) et composite-c2-equityfactor (garde de mois) étaient listés exposés en c.6105344807 à tort ;
de même FamaFrench et Framework_Composite_FamaFrenchAllWeather (garde de mois, marge de 5 barres).
Pré-enregistrement de la mesure suivante (lane myia-po-2024:CoursIA-3), écrit avant le backtest. Suite de c.6106980806.
Question : un appel history(N, DAILY) fait depuis un événement planifié à 10:00 New York (after_market_open("SPY", 30)) un jour de demi-séance rend-il aussi moins de barres ? Le défaut n'a été mesuré qu'à 13:00, sur la tranche de clôture (c.6105308987). La réponse décide du sort de MeanReversion, FuturesTrend, ML-XGBoost et ML-TextClassification.
Sonde : copie de diagnostic de MeanReversion/main.py hors dépôt (sha256 7851ba9c19ad sur main, 4717d4f9253e pour la sonde), sans changement de logique.
_get_3m_return est appelé chaque jour pour SPY ; la sonde compte la longueur rendue (history(68)), par type de séance lu dans les horaires de l'exchange (heure de clôture du jour) et par heure.
Elle journalise chaque appel d'une demi-séance et chaque appel court.
Les jours de demi-séance, elle ajoute un appel history(SPY, 126) pour comparer à la mesure de 13:00 (104 à 106 barres), avec la date de la première et de la dernière barre.
Exécution : fenêtre codée de la source (2015-2024, 20 demi-séances), un backtest sur les nœuds QC.
Prédiction : je ne prédis pas le sens, la cause du déficit n'étant pas établie.
Règle de décision
Au moins une demi-séance où history(68) rend moins de 68 barres : les quatre projets planifiés passent dans « exposés, mécanique mesurée », et le correctif se traite projet par projet, une PR par projet ou par famille de code identique, chacune avec sa propre mesure orig contre fix. MeanReversion d'abord, puisqu'il appelle chaque jour.
68 barres partout : les appels planifiés à 10:00 sortent de la liste des exposés, et le défaut est circonscrit aux appels faits sur la tranche de clôture d'une demi-séance.
Résultat de la mesure des appels planifiés à 10:00 (lane myia-po-2024:CoursIA-3), lu à completed: true. Pré-enregistrement : c.6106988782.
Écart au protocole, déclaré : la sonde construite sur MeanReversion s'est arrêtée le 2018-09-11 sur une erreur sans rapport avec les demi-séances (voir plus bas), après 6 demi-séances sur 20. J'ai mesuré la question à part, avec une sonde minimale qui ne trade pas. Elle abonne SPY en journalier et appelle history(SPY, 68) puis history(SPY, 126) chaque jour à 10:00 New York (after_market_open("SPY", 30)). Le type de séance est lu dans les horaires de l'exchange. Fenêtre 2015-2025 (projet QC 37661550 sur les nœuds ESGF, backtest 539e7f21a22c, sha256 18b4ca9cae5e).
Séance
appels history(68) complets
appels history(126) complets
Demi-séance (clôture 13:00)
23 / 23
23 / 23
Séance pleine
2 743 / 2 743
2 743 / 2 743
Un jour de demi-séance à 10:00, la dernière barre rendue est celle de la veille, par exemple 2015-11-27 : de 2015-06-01 à 2015-11-25, 126 barres. Les 6 demi-séances vues par la sonde MeanReversion avant son arrêt donnent la même chose (68 et 126).
Décision (règle fixée d'avance) : 68 barres partout. Les appels planifiés à 10:00 sortent de la liste des exposés : MeanReversion, FuturesTrend, ML-XGBoost, ML-TextClassification. Le défaut se limite aux appels faits sur la tranche de clôture d'une demi-séance, c'est-à-dire dans on_data ou dans un alpha Framework.
Liste qui reste exposée :
Sparse-Index-Tracking-QC (compteur de 63 jours) ;
SectorMomentum (sortie quotidienne sous SMA200, retard d'un jour) ;
Hors sujet, trouvé en route : MeanReversion non modifié (main.py sha256 7851ba9c19ad) plante aussi sur QC Cloud le 2018-09-11 (projet 37661514, backtest 6bf412f10f48), IndexError sur iloc[-63]. XLC, introduit en juin 2018, n'a alors qu'entre 51 et 62 barres : assez pour passer la garde len < 0,8 N, pas assez pour l'indice. Issue séparée à suivre.
Pré-enregistrement, suite du recensement c.6106980806 (lane myia-po-2024:CoursIA-3). Écrit avant le backtest.
Projet : SectorMomentum (archivé, plafond de Sharpe ~0,56). Le recensement le classe « exposé, non mesuré » : on_data appelle history(symbol, 252) sur la tranche de clôture, et la garde len < 252 rend None.
Lecture du code (main à 59ac415638)
Rééquilibrage mensuel (l. 96-115) : trois appels à la première tranche du mois. Aucune demi-séance n'est un premier jour de mois : je ne l'attends pas exposé.
Sortie quotidienne (l. 88-94) : quand le livre tient SPY et que SPY clôture sous sa SMA200, deux appels (TLT, GLD). Si cette clôture est une demi-séance, l'historique rendu serait d'environ 231 barres. Les deux scores vaudraient None et la rotation défensive attendrait la tranche suivante, la condition restant vraie.
Mesure : une copie de diagnostic hors dépôt, sans changement de logique.
Compteur des appels par heure de tranche, et une ligne de journal par appel court ou par appel hors 16:00 et minuit.
Une ligne DIAG EXIT-COND par tranche où la condition de sortie est vraie, avec un champ lost (score None).
Le compteur des tranches de 13:00, et l'heure de la tranche qui consomme le mois.
Fenêtre codée 2015-01-01 → 2024-12-31, un backtest sur les nœuds QC. La copie ne change pas la logique : ses ordres sont ceux de main.
Fichier (sha256, 12 car.)
main
diag
main.py
a29aca2975bd
02b15c0709d3
Prédictions
Rééquilibrage mensuel : aucun appel sur une tranche de 13:00.
Sortie quotidienne : un appel à 13:00 seulement si la première clôture de SPY sous sa SMA200 tombe un jour de demi-séance. S'il y en a un, longueur sous 252 et lost=True le même jour.
Sur les tranches de 16:00 et de minuit, longueur égale à 252.
Règle de décision, fixée d'avance
Au moins une sortie perdue. Correctif identique à fix(qc,#20286): SectorRotation -- demi-seances, history() en double fenetre #20330 : demander le double, puis garder les 252 dernières barres. Une PR, mesurée orig contre fix sur la fenêtre codée. Si le Sharpe QC bouge d'au moins 5 centièmes, les lignes du README sont signalées pour réécriture. Pas de verdict de stratégie : le projet est archivé, et il s'agit de la correction d'un défaut.
Aucune. Pas de correctif. Le projet passe dans « exposable, non atteint sur la fenêtre publiée ».
diag 02b15c0709d3, 95 ordres lus à la source sur 95, journal complet (162 lignes) ;
témoin, ajouté après le pré-enregistrement : main.py tel quel (a29aca2975bd), pour vérifier que la copie de diagnostic ne change pas les ordres. Résultat : mêmes 95 ordres (date, titre, quantité, type) et mêmes statistiques que la copie de diagnostic.
Compteurs de la copie de diagnostic (ligne DIAG SUMMARY) :
appels history : 51 sur une tranche de 16:00, 327 sur une tranche de minuit, 0 sur une tranche de 13:00 ;
appels courts (moins de 252 barres) : 0 ;
tranches de 13:00 vues : 20 (les demi-séances de la fenêtre) ;
heure de la tranche qui consomme le mois : 16:00 pour 11 mois, minuit pour 109 mois.
Prédictions
Rééquilibrage mensuel : aucun appel sur une tranche de 13:00. Tenue.
Sortie quotidienne : un appel à 13:00 seulement si la première clôture de SPY sous sa SMA200 tombe une demi-séance. La condition de sortie est vraie 9 fois sur la fenêtre (2015-08-20, 2015-11-12, 2015-12-11, 2018-10-11, 2018-12-04, 2019-05-31, 2022-01-21, 2022-02-11, 2023-10-25), toutes sur une tranche de 16:00, toutes lost=False. Le cas ne s'est pas présenté : tenue, sans avoir été mise à l'épreuve.
Sur les tranches de 16:00 et de minuit, longueur 252. Tenue (0 appel court).
Décision (règle fixée d'avance) : aucune sortie perdue, donc pas de correctif au titre de #20286. SectorMomentum passe dans « exposable, non atteint sur la fenêtre codée ». Il reste exposé : une sortie dont la première clôture sous la SMA200 tomberait une demi-séance serait retardée d'une séance.
Hors de la question posée, deux constats descriptifs :
Le README ne décrit pas la fenêtre du code. Il annonce « 2015-2026 » : Sharpe 0,555, CAGR 13,0 %, pire baisse 22,8 %. Le code s'arrête au 2024-12-31 et donne, au témoin, Sharpe QC 0,275, CAGR 6,85 %, pire baisse 22,8 %. L'écart de Sharpe (0,555 contre 0,275) n'est pas expliqué par cette mesure : fenêtre plus longue, ou autre version du code, je ne l'ai pas vérifié. Je ne corrige rien ici : le projet est archivé et la question posée était autre.
Traces hors dépôt : G:/Mon Drive/MyIA/IA/QC-traces/20286-half-session/sm-diag/ et sm-orig/ (plan avec empreintes, résultat brut, ordres, journal).
Sparse-Index-Tracking-QC reste le dernier projet « exposé, non mesuré » du recensement.
Constat mesuré qui contredit une prémisse du recensement (lane myia-po-2024:CoursIA-3).
Le recensement dit qu'aucune demi-séance n'est un premier jour de mois. C'est faux quand le 3 juillet tombe un lundi : ce jour-là est à la fois le premier jour de cotation de juillet et une demi-séance qui ferme à 13:00. Sur les fenêtres publiées, c'est le cas en 2017 et 2023, et la prochaine fois en 2028.
Mesure (backtests du lot #20264, protocole c.6115262186, code de main sans modification) : aucune ligne de rééquilibrage ni aucun ordre ces mois-là.
Projet
Fenêtre
Mois sans rééquilibrage
MomentumStrategy
2015-2024
2017-07, 2023-07 (118 mois sur 120)
FamaFrench
2018-2024
2023-07 (83 sur 84)
FamaFrench
2015-2024
2017-07, 2023-07
Les deux ont le motif « requête N+5, garde N » (MomentumStrategy/main.py:65-67, FamaFrench/main.py:76-78). Ils sortent ensuite sans écrire dans le journal quand il manque des scores (main.py:125-126 et :127-128). La cause, un historique trop court sur la tranche de 13:00, est déduite du code : aucune ligne de journal ne la montre directement.
Le correctif de #20264 ajoute des projets à cette liste. En déplaçant la décision de la tranche de minuit vers la première barre du mois, il fait tomber le rééquilibrage de ces deux mois sur la demi-séance :
Pour les cinq projets classés plus haut « requête N+5, garde 0,8 N, rééquilibrage month_start », l'argument « pas exposés en pratique » tombe aussi. Leur cas n'est pas mesuré ici : planifiés après l'ouverture, ils ne lisent peut-être pas l'historique sur la tranche de 13:00.
Correctif à appliquer projet par projet, celui de #20330 : demander le double de barres, puis garder les N dernières. Je propose de commencer par MomentumStrategy et FamaFrench, avec le même protocole qu'ici : vérifier le trou sur le backtest publié, corriger, puis relancer.
Résultat — SectorMomentum, lane myia-po-2024:CoursIA-3. Mesure du pré-enregistrement c.6117521094. PR : #20444.
Les quatre prédictions ont tenu.
Les deux rééquilibrages manquants reviennent : [RISK ON] SPY=0.060 au 2017-07-03 13:00 et [RISK ON] SPY=0.104 au 2023-07-03 13:00. On les retrouve sur la fenêtre codée comme sur la fenêtre du README.
Avant le 2017-07-03, les ordres sont identiques en multiensemble (date, titre, quantité) : 38 sur 38, sur les deux fenêtres. L'exception admise pour une sortie SMA200 n'a pas servi : les 11 sorties tombent aux mêmes dates avant et après.
Les dates de rééquilibrage sont celles d'avant, plus les deux lundis, sans aucune perte. Le compte de mois rééquilibrés passe de 118 à 120 sur 120 pour la fenêtre codée, et de 137 à 139 sur 139 pour celle du README.
Hors échantillon, les deux variantes sont identiques au centime : capital final, 6 ordres, frais, Sharpe 1,261.
Fenêtre
Sharpe
CAGR
Pire baisse
Ordres
Backtest fix
codée 2015 → 2024
−0,096 → −0,081
0,53 → 0,78 %
38,2 %
111 → 112
a90623a7e25fe53321d26d9228d4a3a1
hors échantillon 2025 → 2026-07-10
1,261 → 1,261
40,83 %
19,2 %
6 → 6
bbf4f63bc43a7e460835b39057542615
README 2015 → 2026-07-10
0,157 → 0,169
5,26 → 5,49 %
38,2 %
118 → 119
c62cedc7e6b7a29582d9a3c6bb2fb933
Le juillet 2017 rétabli n'envoie aucun ordre, car le livre est déjà en SPY. Le juillet 2023 rétabli achète du SPY le 3, à la clôture de 13:00. Sans le correctif, le livre restait en liquidités du 2023-06-01 au 2023-08-01. Ce mois en liquidités commençait par l'achat refusé du 2023-06-01 (#20388), et le défaut de demi-séance le prolongeait. Les achats refusés restent au même nombre : 10, 1 et 11. Les écarts de Sharpe (+0,015 et +0,012) restent sous le seuil de 0,05 : les chiffres de la section #20264 ne reçoivent pas de renvoi.
Traces, hors dépôt : ordres, journaux complets et backtest_raw.json des trois runs, dans G:\Mon Drive\MyIA\IA\QC-traces\20286-half-session\SectorMomentum\. Les journaux des runs avant y sont aussi, relus à la source depuis les backtests de #20389.
Projets traités pour ce défaut : SectorRotation (#20330, mergée), FamaFrench (#20411) et SectorMomentum (#20444). DualMomentumNoTLT suivra après le merge de #20407, qui touche le même fichier.
[CLAIMED] lane myia-po-2024:CoursIA-3 -- DualMomentumNoTLT : correctif demi-seances (meme forme que #20330/#20411/#20444), suite de #20407, mesure pre-enregistree avant backtest -- paths: MyIA.AI.Notebooks/QuantConnect/projects/DualMomentumNoTLT/**
Pré-enregistrement, DualMomentumNoTLT (lane myia-po-2024:CoursIA-3, suite de #20407). Écrit avant tout backtest.
Pourquoi ce projet. Depuis #20407, la garde de mois de DualMomentumNoTLT ne consomme le mois que sur une tranche qui porte une barre. En juillet 2017 et en juillet 2023, cette tranche est la clôture de 13:00 du lundi 3 juillet, une demi-séance. Sur cette tranche, history() rend environ 21 barres de moins que demandé : la garde de longueur écarte les cinq titres, scores reste vide et le rééquilibrage sort sans rien faire. Le README de #20407 le déclare comme limite connue. Le journal du run « après » de #20407 le confirme : 118 lignes REBAL pour 120 mois, aucune en juillet 2017 ni en juillet 2023 (juin puis août).
avant : pas de nouveau run. Les backtests « après » de fix(qc,#20264): DualMomentumNoTLT -- ignore the midnight slice without a bar (lot MS/FF/DM) #20407 portent exactement le code de main. Les empreintes ont été régénérées depuis main et sont identiques à celles de leurs plans : dee2ff12d45b pour b2ff070974e440f64029c69473bb7af7 (codée), ff8213c240d0 pour bbf5126ce1eac446800cedc56c04f546 (hors échantillon).
Exécution : nœuds de backtest QC, paramètres inchangés, un backtest par fenêtre, résultats lus à completed: true, ordres relus jusqu'au compte des statistiques, journaux lus en entier. Comparaison des ordres en multiensemble (date, titre, quantité) par séance : l'ordre de soumission dans une même séance varie d'un run à l'autre.
Prédictions
Les mois manquants reviennent : une ligne REBAL (ou ALL CASH) datée du 2017-07-03 et une du 2023-07-03 dans la fenêtre codée, soit 120 lignes au lieu de 118.
Avant le 2017-07-03, les ordres sont identiques à avant. Il n'y a pas d'exception admise : le code n'agit que sur la première barre du mois, et aucune autre première barre de la fenêtre n'est une demi-séance.
Les dates de rééquilibrage de fix sont celles d'avant, plus les deux lundis. Après juillet 2017, les quantités des ordres peuvent différer, puisque la valeur du portefeuille n'est plus la même.
Hors échantillon, aucune première barre de mois n'est une demi-séance : fix est identique à avant au centime, ordres et statistiques.
Ce qui sera publié : dans le README, le paragraphe « Limite connue » est remplacé par le résultat : mois rééquilibrés, Sharpe, CAGR, pire baisse, nombre d'ordres avant et après, et les identifiants de backtest. Les chiffres de la section #20264 ne sont pas réécrits. Si l'écart de Sharpe dépasse 0,05 sur la fenêtre codée, le paragraphe le dit. Si la prédiction 4 échoue, la règle hors échantillon de #20407 s'applique : pas de dégradation si ΔSharpe > −0,10, ΔCAGR > −0,5 pt et Δpire baisse < +1,0 pt.
Pas de verdict de stratégie. C'est la correction d'un défaut, sur des fenêtres déjà vues.
Pré-enregistrement, les cinq projets « requête N+5, garde 0,8 N, month_start » (lane myia-po-2024:CoursIA-3, suite du constat c.6115476132). Écrit avant tout backtest.
Ce qui reste ouvert. Le constat c.6115476132 a montré que le lundi 3 juillet 2017 et le lundi 3 juillet 2023 sont à la fois le premier jour de cotation du mois et une demi-séance. L'argument « aucune demi-séance n'est un premier jour de mois » ne protège donc plus DualMomentum, Trend-Following, VolTarget-Momentum, RiskParity et GlobalMacro-Regime. Leur cas n'a pas été mesuré.
Lecture du code (main à f35a009f20)
Les cinq rééquilibrent par schedule.on(date_rules.month_start("SPY"), time_rules.after_market_open("SPY", 30)), donc à 10:00 New York. Aucun n'appelle history() dans on_data.
La mesure c.6107023496 a montré qu'à 10:00, un jour de demi-séance, history(SPY, 68) et history(SPY, 126) sont complets (23 sur 23). La dernière barre rendue est celle de la veille. Elle n'a pas testé 252 + 5 barres.
Les cinq demandent N + 5 barres (N = 252, 126 ou 60), écartent un titre si len < 0,8 N, puis lisent iloc[-N]. Si l'appel de 10:00 rendait environ 21 barres de moins, comme à 13:00, la requête de 257 en rendrait environ 236. La garde passerait (236 ≥ 201,6) et iloc[-252] lèverait IndexError : le backtest s'arrêterait le 3 juillet.
Code mesuré : main.py de main, sans modification. sha256 (12 premiers caractères) : DualMomentum 069d1fe98210, Trend-Following e6b44645daae, VolTarget-Momentum b0ba6f938f49, RiskParity c9ad7fb7ee8f, GlobalMacro-Regime 5d9634c91d7f. Fenêtres du code : 2015-2024 pour quatre projets, 2018-2025 pour GlobalMacro-Regime (seul le 3 juillet 2023 y tombe). Nœuds QC, un projet QC par stratégie.
Prédictions
Les cinq backtests vont à completed: true, sans erreur d'exécution.
Chaque projet écrit sa ligne de rééquilibrage (Trending ( ou <régime> |) datée du 2017-07-03 10:00 (si la fenêtre le contient) et du 2023-07-03 10:00.
Après le préchauffage, chaque mois de la fenêtre a sa ligne de rééquilibrage : aucun mois manquant.
Règle de décision, fixée d'avance
Si les trois prédictions tiennent pour un projet, il sort de la liste des exposés : l'appel planifié à 10:00 lit des historiques complets, aussi à 257 barres. Pas de correctif. La fragilité de la garde 0,8 N devant iloc[-N] reste notée, mais elle ne relève pas des demi-séances.
Résultat, les cinq projets « requête N+5, garde 0,8 N, month_start » (lane myia-po-2024:CoursIA-3). Mesure du pré-enregistrement c.6134914257. Runs sur les nœuds QC, lus à completed: true. Ordres relus à la source jusqu'au compte des statistiques, journaux complets.
Projet
Fenêtre
Backtest
Mois avec ligne de rééquilibrage
Heure des lignes
2017-07-03
2023-07-03
DualMomentum
2015-2024
58ef57b863442c637f9a3b10c5e7d33e
120 / 120
10:00
oui
oui
Trend-Following
2015-2024
930de8b78db29824dcf7ae7dbcf2ba0a
120 / 120
10:00
oui
oui
VolTarget-Momentum
2015-2024
2828bd66c72fb6571742ef54a5ddcc89
120 / 120
10:00
oui
oui
RiskParity
2015-2024
9c8c0eb18a43ab2113f9d9ae31575b3f
120 / 120
10:00
oui
oui
GlobalMacro-Regime
2018-2024
67938945c0c2b37b55d10935a76f0570
84 / 84
10:00
hors fenêtre
oui
Les trois prédictions tiennent, pour les cinq projets.
Les cinq backtests vont à completed: true, sans erreur d'exécution. Un historique court de 21 barres aurait fait lever IndexError sur iloc[-252] le 3 juillet : il n'y en a pas.
Chaque 3 juillet de la fenêtre porte sa ligne de rééquilibrage à 10:00, et les ordres de ces jours partent à 10:00 New York. Exemple, DualMomentum le 2017-07-03 : Trending (5/6): [EEM:21.2%, EFA:19.9%, SPY:17.6%, GLD:-8.1%, TLT:-9.0%. Ce sont des rendements à 12 mois calculés sur la série complète.
Aucun mois manquant : 120 lignes sur 120 mois, 84 sur 84 pour GlobalMacro-Regime.
Décision (règle fixée d'avance) : les cinq projets sortent de la liste des exposés, sans correctif. La mesure c.6107023496 tient donc aussi à 257 barres : un appel planifié à 10:00 un jour de demi-séance lit un historique complet, qui s'arrête à la clôture de la veille. Le défaut reste limité aux appels faits sur la tranche de clôture de 13:00, dans on_data ou dans un alpha Framework.
La fragilité de la garde len < 0,8 N devant iloc[-N] demeure, mais elle relève des titres jeunes (le cas de #20346), pas des demi-séances. Aucun titre de ces cinq univers n'est dans ce cas sur ces fenêtres.
Liste qui reste exposée :
Sparse-Index-Tracking-QC : compteur de 63 tranches on_data, tranches de minuit des avis de dividende comprises. Un appel court reporte le rééquilibrage d'un trimestre. C'est la prochaine mesure.
Framework_Composite_MomentumRegime et MomentumRegime-AdaptiveWeights : exposables, non atteints sur les fenêtres publiées.
Hors sujet, trouvé en route (sans lien avec les demi-séances, pas traité ici) :
VolTarget-Momentum : 11 ordres refusés pour pouvoir d'achat insuffisant, de 2015-02-02 à 2023-12-01. Quand le multiplicateur de volatilité est à son plafond de 1,50, la somme des cibles atteint 150 % : le 2017-07-03, EEM 50 + EFA 40 + SPY 30 + GLD 20 + TLT 10, et les 2015-02-02, 2019-01-02 et 2023-12-01 aussi. Le 2017-07-03 ne compte pourtant aucun refus. Pourquoi seules ces 11 dates en ont, je ne l'ai pas établi : l'ordre d'envoi, achats avant ventes, est une hypothèse non vérifiée. Ces mois-là, une partie des ordres est refusée.
RiskParity : 1 ordre refusé, au premier rééquilibrage (2015-02-02).
Ces deux constats appellent une issue séparée avant toute lecture des métriques publiées de ces projets.
[CLAIMED] lane myia-po-2024:CoursIA-3 -- Sparse-Index-Tracking-QC : backtest de diagnostic (heure et contenu de la tranche, longueur d'historique a chaque declenchement du compteur de 63 tranches), protocole pose ici avant les runs, puis correctif (historique double + compteur sur les seules tranches portant la barre de SPY, cf #20264) et mesure avant/apres -- paths: MyIA.AI.Notebooks/QuantConnect/projects/Sparse-Index-Tracking-QC/**
Protocole posé avant les runs — Sparse-Index-Tracking-QC
Le rééquilibrage trimestriel se déclenche dans on_data quand un compteur de tranches vaut 1 modulo 63 (main.py:251-257). Deux défauts peuvent le toucher :
Variantes (QC Cloud, nœuds de backtest sponsorisés, un projet par variante, empreinte du fichier vérifiée avant compilation)
Variante
Code
avant
main.py de main + journal seul, sans changement de comportement : TRIGGER (heure, présence de la barre SPY, nombre de dividendes, séances écoulées), HIST (lignes et colonnes de l'historique), compteur de reports, ligne finale SLICES
fix
compteur avancé seulement sur les tranches qui portent la barre de SPY ; historique demandé sur 2 × 316 barres, 316 dernières lignes gardées avant l'élimination des titres incomplets ; une ligne history par déclenchement ; compteur de reports dans la ligne de fin
Chaque variante tourne en mode=sparse et mode=full : 4 backtests. Fenêtre unique, celle du code (2015-01-01 → 2026-08-31) ; la fin effective, ramenée vers J-90 sur les nœuds de backtest sponsorisés, est lue dans le résultat brut. Pas de fenêtre hors échantillon : le code couvre déjà toute la période jusqu'à sa date d'écriture. Ordres relus à la source jusqu'au nombre des statistiques, journal lu en entier, rien n'est lu avant completed: true.
Prédictions
avant : SLICES total > with_spy_bar (des tranches de minuit existent).
avant : au moins un TRIGGER porte bar=0 (minuit), et l'écart moyen entre deux déclenchements, compté en séances, est inférieur à 63.
fix : chaque ligne history porte session ≡ 1 modulo 63 et rows = 316 ; aucun report ; sessions en fin de run = with_spy_bar de avant.
Les deux variantes : assets = 41 à chaque déclenchement (aucun titre éliminé).
Métriques : aucun signe attendu. L'écart dépend du marché aux dates déplacées. Ce que le correctif doit apporter, c'est un rééquilibrage tous les 63 jours de séance, comme le décrit le README.
Résultats du protocole pré-enregistré (c.6141122238)
Quatre runs sur les nœuds de backtest sponsorisés, tous lus à completed: true, ordres relus à la source jusqu'au nombre des statistiques, journaux lus en entier. Fin effective relue dans le résultat brut : 2026-07-15 pour les quatre (le code demande 2026-08-31).
Run
Backtest
Ordres
Déclenchements
Rééquilibrages
Sautés
Reportés
avant, sparse
f47ad143db11752e7ca117dd2f746fcf
687
65
62
2
1
avant, full
5045a6592675037ab4e54a65fde1e33a
1386
65
50
14
1
fix, sparse
b41e290d2cd285ce13995aa04d3f7fbd
536
47
47
0
0
fix, full
71ae29a8ba6de3e7ca3a76ccab3f117f
1222
47
43
4
0
« Sautés » regroupe ce que _skipped compte déjà : sélection infaisable, filtre de poussière vide, validateur en échec, rotation sous la bande de non-transaction de 10 %. « Reportés » est le compteur ajouté : historique trop court au déclenchement.
Prédictions
Tranches de minuit : tenue. Ligne SLICES de l'avant : 4069 tranches au total, dont 2899 portent la barre de SPY. Les 1170 autres sont des tranches de minuit (avis de dividende).
Trimestre raccourci : tenue. 21 des 65 déclenchements de l'avant tombent sur une tranche de minuit (bar=0), le premier dès le 2015-01-02 00:00. L'écart entre deux déclenchements, compté en séances, vaut 44,9 en moyenne (de 42 à 49), au lieu de 63.
Historique de demi-séance : tenue. Un seul déclenchement tombe à 13:00 : le 2018-12-24, veille de Noël. history(316) y rend 261 lignes, et le rééquilibrage est reporté. Les 64 autres déclenchements, à 16:00 ou à 00:00, rendent 316 lignes. Reports = 1 = nombre de déclenchements à 13:00, dans les deux modes. Comme le compteur avait déjà avancé, ce trimestre était perdu, et le report n'apparaissait dans aucun compteur.
Correctif : tenue. Les 47 lignes history portent session ≡ 1 modulo 63, toutes à 16:00, toutes à 316 lignes. Écart entre déclenchements : exactement 63 séances, 46 fois sur 46. Aucun report. Ligne de fin : sessions=2899, égal au with_spy_bar de l'avant.
Univers complet : tenue. 41 colonnes (SPY et les 40 titres) à chaque déclenchement, avant comme après : aucun titre éliminé.
Métriques, sans direction annoncée :
Mode
Sharpe
Rendement annuel composé
Pire baisse
Gain net
Ordres
Frais
sparse
0,635 -> 0,616
17,322 % -> 16,740 %
34,7 % -> 35,3 %
532,3 % -> 497,0 %
687 -> 536
-22 %
full
0,712 -> 0,709
17,257 % -> 17,152 %
30,7 % -> 31,3 %
528,3 % -> 521,8 %
1386 -> 1222
-7 %
La première différence d'ordres tombe dès le 2015-01-02. L'avant déclenche à 00:00, sur la tranche de dividende, et ses ordres MarketOnOpen sont remplis à l'ouverture du jour même. Le correctif déclenche à la clôture de la première séance, et ses ordres sont remplis le 2015-01-05. Ensuite, les dates de rééquilibrage n'ont plus rien en commun : 65 déclenchements contre 47.
L'écart de métriques n'a pas de signe à retenir. Le classement entre les deux modes ne bouge pas : full garde le meilleur Sharpe et la pire baisse la plus faible. Moins de rééquilibrages veut dire moins d'ordres et moins de frais. Le correctif ne vise pas un meilleur rendement : il fait rééquilibrer tous les 63 jours de séance, comme le décrit le README, sans trimestre perdu.
Ce qui reste
Les chiffres publiés du README (2026-09-01, fin 2026-08-31) viennent de runs distincts, sur une fenêtre plus longue : ils ne se comparent pas directement aux runs ci-dessus. Une paire de runs du correctif sur la fenêtre codée entière est en cours, pour mettre à jour ce tableau dans la PR.
Les deux runs annoncés au point « Ce qui reste » du commentaire précédent sont faits. Ils ont tourné sur un nœud de backtest qui ne tronque pas la fenêtre (2015-01-01 -> 2026-08-31), et ont été lus à completed: true ; le nombre d'ordres relus à la source égale celui des statistiques. L'avant est le run publié du README (2026-09-01), lancé sur un main.py identique à celui d'avant le correctif. Six semaines séparent les deux runs : le moteur ou les données ont pu changer entre-temps, et cette paire est moins contrôlée que celle des nœuds sponsorisés.
Après correctif, dans les deux modes : 47 déclenchements, tous à 16:00, 63 séances d'écart chaque fois, 316 barres d'historique, aucun report, sessions=2932. Le classement entre les deux modes ne change pas. Le full gagne un peu de Sharpe et le sparse en perd un peu : les deux mouvements vont en sens contraires, ce qui confirme que l'écart de métriques n'a pas de signe à retenir.
Activity
fix(qc,#20264): SectorRotation-Momentum -- ignorer la tranche de minu…
jsboige commentedon Oct 11, 2026
[CLAIMED] lane myia-po-2024:CoursIA-3 -- backtest de diagnostic (longueur d'historique et présence des données à chaque rééquilibrage), puis correctif et mesure avant/après si l'hypothèse 3 tient. paths: MyIA.AI.Notebooks/QuantConnect/projects/Cloud-SectorRotation-Momentum/**
jsboige commentedon Oct 11, 2026
Pré-enregistrement (lane myia-po-2024:CoursIA-3). Écrit avant tout backtest, et suit les étapes 1 et 2 du protocole de l'issue.
Code mesuré (sha256 de
main.py, 12 caractères)avant2a1ce95cac64mainà 68972f1. Backtest existanta8e7809afb021ce8b42e8b59d61e9e1b(projet 37630571), même empreinte : il n'est pas relancé.diage3e61eebe939avantavec des journaux en plus, logique de trading inchangée. Code de mesure seulement, il ne sera pas commité.fixf853395e1587avantplus deux lignes :history(..., lookback + 5, ...), puis les 126 dernières clôtures. La garde de longueur ne bouge pas.Ce que journalise
diagExécution : nœuds de backtest QC, fenêtre codée 2018-01-01 → 2025-01-01,
diagetfixen parallèle.Prédictions
diagrend les mêmes ordres queavant(mêmes dates, mêmes titres). Sinon les journaux ont perturbé le run, et la lecture dediagne vaut plus pouravant.L'hypothèse 3 tient si deux conditions sont réunies :
len125;Elle est réfutée si la sortie du 2019-07-03 est
nodata,emptyoukey, ou si la longueur y vaut 126. Dans ce cas, pas de correctif dans cette PR : je publie la cause mesurée.Si elle tient, avec
fix:avant, puisque le compteur de 21 séances ne dépend pas des positions.Ce qui sera publié
avantet defix, avec les identifiants de backtest.Ces chiffres iront dans le README du projet (FR/EN). La ligne SectorRotation de
docs/qc/qc-strategies-status.mdprendra les chiffres defix, puisque c'est le code qui serait surmain.Seuil fixé d'avance : si le Sharpe QC bouge d'au moins 0,05 entre
avantetfix, je le signale dans le README. Pas de verdict de stratégie : c'est la correction d'un défaut, sur une fenêtre déjà vue. Le NO-BEATS de #20264 reste en l'état.Étape 3 (recensement) : un relevé des autres projets QC qui combinent une garde
len(history) < Net un rééquilibrage pouvant tomber sur une demi-séance est en cours. Il sera publié avec le résultat. Les éventuels autres projets touchés restent hors de cette PR, un sujet par PR.jsboige commentedon Oct 11, 2026
Résultat du premier tour, et pré-enregistrement du second (lane myia-po-2024:CoursIA-3). Les deux runs ont été lus à
completed: true.Tour 1 :
diagetfix(pré-enregistrement c.6105254259)avant(2a1ce95cac64)a8e7809afb021ce8b42e8b59d61e9e1bdiag(e3e61eebe939)921283996fe873976fdd9e4adbb45dd5fix(f853395e1587)5bf8331b90febecf6032d057cd11b488Prédiction 1 tenue :
diagrend exactement les ordres deavant(309 ordres, même date, même titre, même quantité, aucune différence). Sa lecture vaut donc pouravant.Prédiction 2 : le mécanisme tient, le chiffre est réfuté.
QQQ:len105[2019-02-04 16:00..2019-07-03 13:00] SPY:len105[...] EFA:len105[...] GLD:len105[...] IWM:len105[...]non16h=15 court_non16h=15 court_16h=0).Prédiction 3 réfutée pour ce correctif.
fixdemande 131 barres. Il en reçoit encore moins de 126 sur une demi-séance, et le run est identique àavant: mêmes 309 ordres, mêmes statistiques, même passage en SHY le 2019-07-03.L'autre rééquilibrage en SHY de cette période, le 2018-11-29 16:00, est un vrai signal : les cinq titres y sont sous leur SMA200 (ligne
DIAG REBdu même journal).Tour 2 :
fix2, pré-enregistré avant le runCode :
fix2=avant+history(..., 2 * lookback, ...), puis les 126 dernières clôtures, sha2567510c24e4f58. La garde de longueur ne bouge pas. Même fenêtre, nœuds QC.Prédictions
fix2sont identiques à ceux deavant(date, titre, quantité). Sur une séance pleine, les 126 dernières barres d'une requête de 252 sont les mêmes que celles d'une requête de 126.fix2ne passe pas en SHY : le journal porteREBALANCE: N assets, et aucun achat de SHY n'est daté d'une demi-séance.avant.Si 1 ou 3 échoue, le correctif change autre chose que la demi-séance : il n'est pas commité, et je dirai ce qui a bougé. Seuil et absence de verdict de stratégie inchangés : un écart de Sharpe d'au moins 0,05 est signalé dans le README.
Étape 3, recensement : ce que la mesure change
Un relevé des projets QC qui combinent un
history(N)et une garde de longueur avait été fait sous l'hypothèse « une barre de moins ». Il classait comme protégés les projets qui demandent N+5 ou N+10 barres, comme ceux qui gardent surN*0.8.Avec un déficit mesuré de 16 à 17 %, ce classement ne tient plus. Une requête de N+5 ou N+10 barres est aussi exposée, comme l'a montré
fixici. Le relevé sera repris avec ce seuil et publié avec le résultat du tour 2. Les corrections éventuelles iront dans d'autres PRs.jsboige commentedon Oct 11, 2026
Résultat du second tour (lane myia-po-2024:CoursIA-3), lu à
completed: true. Pré-enregistrement : c.6105308987.Tour 2 :
fix2(sha2567510c24e4f58)avant(2a1ce95cac64)a8e7809afb021ce8b42e8b59d61e9e1bfix2(7510c24e4f58)5661dc625175652677c63daa90074862fix2sont identiques à ceux deavant(date, titre, quantité).REBALANCE: 5 assets | Top: QQQ (29.2%) | Total mom: 97.00%. Aucun achat de SHY n'est daté d'une demi-séance. Les deux achats de SHY du run (2018-10-30 et 2022-07-05) tombent sur des séances pleines.avant.Le correctif est donc commité (PR à suivre).
Calendrier des demi-séances
Sur 2018-2024, les 15 séances qui ne ferment pas à 16:00 sont les 3 juillet, les lendemains de Thanksgiving et les 24 décembre. Toutes rendent 104 à 106 barres pour 126 demandées. Aucune n'est la première séance d'un mois. Deux sont la dernière séance d'un mois : 2019-11-29 et 2024-11-29.
Un rééquilibrage par
date_rules.month_startne tombe donc jamais sur une demi-séance sur cette fenêtre. Un calcul à date fixe en fin de mois, quotidien ou par compteur de séances (comme ici), peut y tomber.Recensement repris (lecture des lignes de requête et de garde, non exhaustif)
Requête N, garde N, exposés comme ici si un calcul tombe sur une demi-séance (calendrier de chaque projet à vérifier) :
Cloud-VolTargetingmain.py:76-77SectorMomentummain.py:50-52 (252 barres)Framework_Composite_MomentumRegimealpha_models.py:101-103MomentumRegime-AdaptiveWeightsalpha_models.py:101-103composite-c2-equityfactoralpha_models.py:236-238Sparse-Index-Tracking-QCmain.py:265, 271-272 (316 barres)ML-XGBoostmain.py:176-179ML-TextClassificationmain.py:123-126FuturesTrendmain.py:106-107 et 131-132 (contrats à terme : l'effet n'est mesuré que sur des actions US)Requête N+5, garde N, classée à tort comme protégée au premier relevé :
FamaFrenchmain.py:76-77.Requête N+5, garde
0,8 N, rééquilibragemonth_start:DualMomentum,Trend-Following,VolTarget-Momentum,RiskParity,GlobalMacro-Regime. Ces cinq projets ne sont pas exposés en pratique, puisqu'aucune demi-séance n'est un premier jour de mois. Le code reste fragile pour autant. Sur une demi-séance, environ 0,83 (N+5) barres passent la garde0,8 N, puisiloc[-N]sort des bornes.Requête N+5, garde
0,8 N, calcul quotidien :MeanReversionmain.py:69-70. La garde passe, mais l'usage de la série reste à vérifier.Pas repris à ce tour :
MomentumStrategy,DualMomentumNoTLT,composite-c1-multiasset,Framework_Composite_FamaFrenchAllWeather,BlackLitterman,ForexCarry(marché des changes : l'effet n'y est pas établi),Paradox46,MultiHorizonMomentum676, la familleHAR-RVetVol-*(gardeslen >= 2).Ce relevé est une lecture du code, pas une mesure. Avant toute correction, chaque projet suivra le même protocole qu'ici : vérifier sur le backtest publié qu'un calcul tombe bien sur une demi-séance. Les corrections iront dans des PRs séparées. L'issue reste ouverte pour cela.
jsboige commentedon Oct 11, 2026
[CLAIMED-AMEND] lane myia-po-2024:CoursIA-3 -- paths: MyIA.AI.Notebooks/QuantConnect/projects/Cloud-SectorRotation-Momentum/, MyIA.AI.Notebooks/QuantConnect/projects/Framework_Composite_MomentumRegime/, MyIA.AI.Notebooks/QuantConnect/projects/MomentumRegime-AdaptiveWeights/**
jsboige commentedon Oct 11, 2026
Pré-enregistrement, suite du recensement c.6105344807 (lane myia-po-2024:CoursIA-3). Écrit avant tout backtest.
Projets :
Framework_Composite_MomentumRegime(MR) etMomentumRegime-AdaptiveWeights(AW). Les deuxalpha_models.pyportent le même code ; ils ne diffèrent que par les poids par défaut deSectorMomentumAlpha.Lecture du code (
mainà68972f1a6b)SectorMomentumAlpha._composite_momentum(l. 101-103) :history(symbol, 252), gardelen < 252. Émission mensuelle, à la première tranche du mois. Aucune demi-séance n'est un premier jour de mois : je ne l'attends pas exposé.RegimeSwitchingAlpha._apply_momentum(l. 230-231) :history(symbol, 68), gardelen < 63. Il est appelé à la première tranche du mois et le jour d'un changement de régime versbull, qui peut tomber n'importe quel jour. Si ce jour est une demi-séance, l'historique rendu serait d'environ 47 barres (c.6105308987 : environ 21 barres de moins), SPY et QQQ seraient écartés et_apply_momentumrendrait[]. Comme_need_rebalanceest déjà remis à faux, le passage enbullne serait pas appliqué avant l'émission du mois suivant.Mesure : copies de diagnostic hors dépôt, sans changement de logique : compteur par alpha, heure de la tranche et longueur rendue ; une ligne de journal par appel court ou par tranche ni de 16:00 ni de minuit ; une ligne par jour de changement de régime ; une ligne
DIAG RS LOSTquand le calcul ne retient aucun titre. Modeintent(défaut), fenêtre codée de chaque source (MR 2015-2025, AW 2018-2024), un backtest par projet sur les nœuds QC.mainalpha_models.py896d0843fcdd889177bc2155main.pyb25f2790cdae55b57ba4a986alpha_models.py16dfd8dfad7825e7910ca0c6main.py33169b0a9842676f11e015d4Prédictions
SectorMomentumAlpha: aucun appel d'historique sur une tranche de demi-séance (13:00, New York), dans les deux projets.RegimeSwitchingAlpha: un appel sur une demi-séance seulement un jour de changement de régime. S'il y en a au moins un : longueur sous 63 et ligneDIAG RS LOSTle même jour.Règle de décision, fixée d'avance
origcontrefixsur les mêmes fenêtres. Si le Sharpe QC bouge d'au moins 5 centièmes, les lignes du README concernées sont signalées pour réécriture. Pas de verdict de stratégie : c'est la correction d'un défaut, sur des fenêtres déjà vues.jsboige commentedon Oct 11, 2026
Résultat de la mesure MomentumRegime / AdaptiveWeights (lane myia-po-2024:CoursIA-3), lu à
completed: true. Pré-enregistrement : c.6106934850.74feb0f69ecbc8759b64eceeSectorMomentumAlpha: 16:00 / minuit / 13:00RegimeSwitchingAlpha: 16:00 / minuit / 13:00bull)DIAG RS LOSTPrédictions : 1 tenue (aucun appel de
SectorMomentumAlphasur une demi-séance). 2 tenue au sens strict, mais sans exemple : aucun changement de régime n'est tombé sur une demi-séance, donc aucun appel deRegimeSwitchingAlphaà 13:00. 3 tenue : toutes les longueurs rendues égalent la demande, à 16:00 comme à minuit.Décision (règle fixée d'avance) : aucune émission perdue, donc pas de correctif.
Le chemin reste exposable. Un passage en
bullun jour de demi-séance ferait perdre l'émission jusqu'au mois suivant. Sur ces fenêtres, c'était environ une chance sur cinq (MR : 23 passages enbullsur environ 2 760 séances, 23 demi-séances) et une sur sept (AW), et c'est resté sans exemple. Ces deux projets passent dans « exposables, non atteints sur les fenêtres publiées ». Le correctif de #20330 s'y transposerait tel quel le jour où l'un d'eux tourne sur une autre fenêtre.Descriptif, à verser à #20264 : la première tranche du mois est une tranche de minuit 120 mois sur 132 en MR (la tranche d'avis de dividende décrite en #20264). L'émission mensuelle de
SectorMomentumAlphay a donc lieu. MR passe 202 ordres sur 683 à minuit (heure de New York), AW 108 sur 362. Ce n'est pas le défaut de #20264 : les alphas lisent le dernier prix connu, les longueurs d'historique sont pleines, et l'ordre part à l'ouverture suivante comme depuis une tranche de 16:00. Je le note sans le qualifier davantage.Recensement, mis à jour
Lecture des 17 appels d'historique pris en décision de trading, déléguée puis vérifiée ligne à ligne sur les cas qui changent la liste. Elle corrige c.6105344807 sur trois points.
Mesuré, défaut atteint :
Cloud-SectorRotation-Momentum(compteur de 21 jours danson_data) ; correctif en fix(qc,#20286): SectorRotation -- demi-seances, history() en double fenetre #20330.Mesuré, non atteint :
Framework_Composite_MomentumRegime,MomentumRegime-AdaptiveWeights(ci-dessus).Exposés par la même mécanique (appel dans
on_data, sur la tranche de 13:00), non mesurés :Sparse-Index-Tracking-QC(main.py:254-257, compteur de 63 jours ; un appel court reporte le rééquilibrage au trimestre suivant, car le compteur a déjà avancé) ;SectorMomentum(main.py:88-94, sortie quotidienne quand SPY passe sous sa SMA200 ; un appel court retarde la rotation défensive d'un jour, puisque la condition reste vraie le lendemain).Appels depuis un événement planifié à 10:00 New York (
after_market_open("SPY", 30)) :MeanReversionetFuturesTrendtous les jours ;ML-XGBoostetML-TextClassificationle lundi.Le défaut n'a été mesuré qu'à 13:00, sur la tranche de clôture d'une demi-séance. Qu'un appel à 10:00 le même jour rende aussi moins de barres n'est pas établi, et c'est la prochaine mesure.
MeanReversionserait le cas le plus fin : sa garde (len < 0,8 N) rend un rendement nul au lieu d'écarter le titre, ce qui neutraliserait le filtre de force relative ce jour-là.Corrigé, non exposés :
Cloud-VolTargeting(month_start,main.py:60-64) etcomposite-c2-equityfactor(garde de mois) étaient listés exposés en c.6105344807 à tort ;FamaFrenchetFramework_Composite_FamaFrenchAllWeather(garde de mois, marge de 5 barres).month_end.Traces :
G:/Mon Drive/MyIA/IA/QC-traces/20286-half-session/regime/{mr-diag,aw-diag}(plan figé, backtest, ordres, journal).jsboige commentedon Oct 11, 2026
Pré-enregistrement de la mesure suivante (lane myia-po-2024:CoursIA-3), écrit avant le backtest. Suite de c.6106980806.
Question : un appel
history(N, DAILY)fait depuis un événement planifié à 10:00 New York (after_market_open("SPY", 30)) un jour de demi-séance rend-il aussi moins de barres ? Le défaut n'a été mesuré qu'à 13:00, sur la tranche de clôture (c.6105308987). La réponse décide du sort deMeanReversion,FuturesTrend,ML-XGBoostetML-TextClassification.Sonde : copie de diagnostic de
MeanReversion/main.pyhors dépôt (sha2567851ba9c19adsurmain,4717d4f9253epour la sonde), sans changement de logique._get_3m_returnest appelé chaque jour pour SPY ; la sonde compte la longueur rendue (history(68)), par type de séance lu dans les horaires de l'exchange (heure de clôture du jour) et par heure.history(SPY, 126)pour comparer à la mesure de 13:00 (104 à 106 barres), avec la date de la première et de la dernière barre.Exécution : fenêtre codée de la source (2015-2024, 20 demi-séances), un backtest sur les nœuds QC.
Prédiction : je ne prédis pas le sens, la cause du déficit n'étant pas établie.
Règle de décision
history(68)rend moins de 68 barres : les quatre projets planifiés passent dans « exposés, mécanique mesurée », et le correctif se traite projet par projet, une PR par projet ou par famille de code identique, chacune avec sa propre mesureorigcontrefix.MeanReversiond'abord, puisqu'il appelle chaque jour.jsboige commentedon Oct 11, 2026
Résultat de la mesure des appels planifiés à 10:00 (lane myia-po-2024:CoursIA-3), lu à
completed: true. Pré-enregistrement : c.6106988782.Écart au protocole, déclaré : la sonde construite sur
MeanReversions'est arrêtée le 2018-09-11 sur une erreur sans rapport avec les demi-séances (voir plus bas), après 6 demi-séances sur 20. J'ai mesuré la question à part, avec une sonde minimale qui ne trade pas. Elle abonne SPY en journalier et appellehistory(SPY, 68)puishistory(SPY, 126)chaque jour à 10:00 New York (after_market_open("SPY", 30)). Le type de séance est lu dans les horaires de l'exchange. Fenêtre 2015-2025 (projet QC 37661550 sur les nœuds ESGF, backtest539e7f21a22c, sha25618b4ca9cae5e).history(68)completshistory(126)completsUn jour de demi-séance à 10:00, la dernière barre rendue est celle de la veille, par exemple
2015-11-27: de2015-06-01à2015-11-25, 126 barres. Les 6 demi-séances vues par la sondeMeanReversionavant son arrêt donnent la même chose (68 et 126).Décision (règle fixée d'avance) : 68 barres partout. Les appels planifiés à 10:00 sortent de la liste des exposés :
MeanReversion,FuturesTrend,ML-XGBoost,ML-TextClassification. Le défaut se limite aux appels faits sur la tranche de clôture d'une demi-séance, c'est-à-dire danson_dataou dans un alpha Framework.Liste qui reste exposée :
Sparse-Index-Tracking-QC(compteur de 63 jours) ;SectorMomentum(sortie quotidienne sous SMA200, retard d'un jour) ;Cloud-SectorRotation-Momentum(atteint, fix(qc,#20286): SectorRotation -- demi-seances, history() en double fenetre #20330).Hors sujet, trouvé en route :
MeanReversionnon modifié (main.pysha2567851ba9c19ad) plante aussi sur QC Cloud le 2018-09-11 (projet 37661514, backtest6bf412f10f48),IndexErrorsuriloc[-63]. XLC, introduit en juin 2018, n'a alors qu'entre 51 et 62 barres : assez pour passer la gardelen < 0,8 N, pas assez pour l'indice. Issue séparée à suivre.Traces :
G:/Mon Drive/MyIA/IA/QC-traces/20286-half-session/scheduled/{probe-10h,meanrev-10h,meanrev-orig}.jsboige commentedon Oct 11, 2026
Pré-enregistrement, suite du recensement c.6106980806 (lane myia-po-2024:CoursIA-3). Écrit avant le backtest.
Projet :
SectorMomentum(archivé, plafond de Sharpe ~0,56). Le recensement le classe « exposé, non mesuré » :on_dataappellehistory(symbol, 252)sur la tranche de clôture, et la gardelen < 252rendNone.Lecture du code (
mainà59ac415638)Noneet la rotation défensive attendrait la tranche suivante, la condition restant vraie.Mesure : une copie de diagnostic hors dépôt, sans changement de logique.
DIAG EXIT-CONDpar tranche où la condition de sortie est vraie, avec un champlost(scoreNone).Fenêtre codée 2015-01-01 → 2024-12-31, un backtest sur les nœuds QC. La copie ne change pas la logique : ses ordres sont ceux de
main.mainmain.pya29aca2975bd02b15c0709d3Prédictions
lost=Truele même jour.Règle de décision, fixée d'avance
origcontrefixsur la fenêtre codée. Si le Sharpe QC bouge d'au moins 5 centièmes, les lignes du README sont signalées pour réécriture. Pas de verdict de stratégie : le projet est archivé, et il s'agit de la correction d'un défaut.jsboige commentedon Oct 11, 2026
Résultat de la mesure pré-enregistrée en c.6111047977 (lane myia-po-2024:CoursIA-3), sur
SectorMomentum.Runs (nœuds QC, lus à
completed: true, fenêtre codée 2015-01-01 → 2024-12-31) :02b15c0709d3, 95 ordres lus à la source sur 95, journal complet (162 lignes) ;main.pytel quel (a29aca2975bd), pour vérifier que la copie de diagnostic ne change pas les ordres. Résultat : mêmes 95 ordres (date, titre, quantité, type) et mêmes statistiques que la copie de diagnostic.Compteurs de la copie de diagnostic (ligne
DIAG SUMMARY) :history: 51 sur une tranche de 16:00, 327 sur une tranche de minuit, 0 sur une tranche de 13:00 ;Prédictions
lost=False. Le cas ne s'est pas présenté : tenue, sans avoir été mise à l'épreuve.Décision (règle fixée d'avance) : aucune sortie perdue, donc pas de correctif au titre de #20286.
SectorMomentumpasse dans « exposable, non atteint sur la fenêtre codée ». Il reste exposé : une sortie dont la première clôture sous la SMA200 tomberait une demi-séance serait retardée d'une séance.Hors de la question posée, deux constats descriptifs :
rebalance_month. 71 des 95 ordres sont émis à minuit (44 mois) et s'exécutent à l'ouverture du jour même ; les 24 autres sont émis à la clôture et s'exécutent à l'ouverture suivante. Les historiques lus à minuit sont pleins et s'arrêtent à la clôture précédente : pas de lecture du futur, un calendrier mélangé.SectorMomentumfigure dans le tableau « possiblement touchés » de QC : la tranche de minuit (avis de dividende) consomme le rééquilibrage de on_data en journalier — recensement #20264 ; je l'y signale comme atteint. Un éventuel correctif se pré-enregistrera là, pas ici.Traces hors dépôt :
G:/Mon Drive/MyIA/IA/QC-traces/20286-half-session/sm-diag/etsm-orig/(plan avec empreintes, résultat brut, ordres, journal).Sparse-Index-Tracking-QCreste le dernier projet « exposé, non mesuré » du recensement.jsboige commentedon Oct 12, 2026
Constat mesuré qui contredit une prémisse du recensement (lane myia-po-2024:CoursIA-3).
Le recensement dit qu'aucune demi-séance n'est un premier jour de mois. C'est faux quand le 3 juillet tombe un lundi : ce jour-là est à la fois le premier jour de cotation de juillet et une demi-séance qui ferme à 13:00. Sur les fenêtres publiées, c'est le cas en 2017 et 2023, et la prochaine fois en 2028.
Mesure (backtests du lot #20264, protocole c.6115262186, code de
mainsans modification) : aucune ligne de rééquilibrage ni aucun ordre ces mois-là.Les deux ont le motif « requête N+5, garde N » (
MomentumStrategy/main.py:65-67,FamaFrench/main.py:76-78). Ils sortent ensuite sans écrire dans le journal quand il manque des scores (main.py:125-126et:127-128). La cause, un historique trop court sur la tranche de 13:00, est déduite du code : aucune ligne de journal ne la montre directement.Le correctif de #20264 ajoute des projets à cette liste. En déplaçant la décision de la tranche de minuit vers la première barre du mois, il fait tomber le rééquilibrage de ces deux mois sur la demi-séance :
Pour les cinq projets classés plus haut « requête N+5, garde
0,8 N, rééquilibragemonth_start», l'argument « pas exposés en pratique » tombe aussi. Leur cas n'est pas mesuré ici : planifiés après l'ouverture, ils ne lisent peut-être pas l'historique sur la tranche de 13:00.Correctif à appliquer projet par projet, celui de #20330 : demander le double de barres, puis garder les N dernières. Je propose de commencer par MomentumStrategy et FamaFrench, avec le même protocole qu'ici : vérifier le trou sur le backtest publié, corriger, puis relancer.
6 remaining items
jsboige commentedon Oct 12, 2026
Résultat —
SectorMomentum, lane myia-po-2024:CoursIA-3. Mesure du pré-enregistrement c.6117521094. PR : #20444.Les quatre prédictions ont tenu.
[RISK ON] SPY=0.060au 2017-07-03 13:00 et[RISK ON] SPY=0.104au 2023-07-03 13:00. On les retrouve sur la fenêtre codée comme sur la fenêtre du README.a90623a7e25fe53321d26d9228d4a3a1bbf4f63bc43a7e460835b39057542615c62cedc7e6b7a29582d9a3c6bb2fb933Le juillet 2017 rétabli n'envoie aucun ordre, car le livre est déjà en SPY. Le juillet 2023 rétabli achète du SPY le 3, à la clôture de 13:00. Sans le correctif, le livre restait en liquidités du 2023-06-01 au 2023-08-01. Ce mois en liquidités commençait par l'achat refusé du 2023-06-01 (#20388), et le défaut de demi-séance le prolongeait. Les achats refusés restent au même nombre : 10, 1 et 11. Les écarts de Sharpe (+0,015 et +0,012) restent sous le seuil de 0,05 : les chiffres de la section #20264 ne reçoivent pas de renvoi.
Traces, hors dépôt : ordres, journaux complets et
backtest_raw.jsondes trois runs, dansG:\Mon Drive\MyIA\IA\QC-traces\20286-half-session\SectorMomentum\. Les journaux des runs avant y sont aussi, relus à la source depuis les backtests de #20389.Projets traités pour ce défaut :
SectorRotation(#20330, mergée),FamaFrench(#20411) etSectorMomentum(#20444).DualMomentumNoTLTsuivra après le merge de #20407, qui touche le même fichier.fix(qc,#20264): DualMomentumNoTLT -- ignore the midnight slice withou…
Fix: MomentumStrategy + FamaFrench -- half-session history request (#…
jsboige commentedon Oct 12, 2026
[CLAIMED] lane myia-po-2024:CoursIA-3 -- DualMomentumNoTLT : correctif demi-seances (meme forme que #20330/#20411/#20444), suite de #20407, mesure pre-enregistree avant backtest -- paths: MyIA.AI.Notebooks/QuantConnect/projects/DualMomentumNoTLT/**
jsboige commentedon Oct 12, 2026
Pré-enregistrement,
DualMomentumNoTLT(lane myia-po-2024:CoursIA-3, suite de #20407). Écrit avant tout backtest.Pourquoi ce projet. Depuis #20407, la garde de mois de
DualMomentumNoTLTne consomme le mois que sur une tranche qui porte une barre. En juillet 2017 et en juillet 2023, cette tranche est la clôture de 13:00 du lundi 3 juillet, une demi-séance. Sur cette tranche,history()rend environ 21 barres de moins que demandé : la garde de longueur écarte les cinq titres,scoresreste vide et le rééquilibrage sort sans rien faire. Le README de #20407 le déclare comme limite connue. Le journal du run « après » de #20407 le confirme : 118 lignesREBALpour 120 mois, aucune en juillet 2017 ni en juillet 2023 (juin puis août).Code mesuré
fix: le correctif de fix(qc,#20286): SectorRotation -- demi-seances, history() en double fenetre #20330, fix(qc,#20286): MomentumStrategy + FamaFrench -- half-session history request (July 3 Mondays) #20411 et fix(qc,#20286): SectorMomentum -- historique double sur les demi-seances, juillet 2017 et 2023 reequilibres #20444, appliqué à l'unique appel d'historique demain.py. La requête passe delookback + 5barres à2 * (lookback + 5), puis on garde leslookback + 5plus récentes. La garde de longueur et le reste du code ne changent pas. Sur une séance pleine, les barres gardées sont les mêmes qu'avant.6057363d0ac5(fenêtre codée, 2015-01-01 → 2024-12-31),d9da47accddd(hors échantillon, 2025-01-01 → 2026-07-10). La fenêtre hors échantillon ne modifie queset_start_dateetset_end_date, comme dans fix(qc,#20264): DualMomentumNoTLT -- ignore the midnight slice without a bar (lot MS/FF/DM) #20407.avant: pas de nouveau run. Les backtests « après » de fix(qc,#20264): DualMomentumNoTLT -- ignore the midnight slice without a bar (lot MS/FF/DM) #20407 portent exactement le code demain. Les empreintes ont été régénérées depuismainet sont identiques à celles de leurs plans :dee2ff12d45bpourb2ff070974e440f64029c69473bb7af7(codée),ff8213c240d0pourbbf5126ce1eac446800cedc56c04f546(hors échantillon).alpha_model.py) porte le même appel d'historique et l'ancienne garde de mois ; elle reste au recensement de QC : la tranche de minuit (avis de dividende) consomme le rééquilibrage de on_data en journalier — recensement #20264 et n'est ni touchée ni mesurée ici.Exécution : nœuds de backtest QC, paramètres inchangés, un backtest par fenêtre, résultats lus à
completed: true, ordres relus jusqu'au compte des statistiques, journaux lus en entier. Comparaison des ordres en multiensemble (date, titre, quantité) par séance : l'ordre de soumission dans une même séance varie d'un run à l'autre.Prédictions
REBAL(ouALL CASH) datée du 2017-07-03 et une du 2023-07-03 dans la fenêtre codée, soit 120 lignes au lieu de 118.avant. Il n'y a pas d'exception admise : le code n'agit que sur la première barre du mois, et aucune autre première barre de la fenêtre n'est une demi-séance.fixsont celles d'avant, plus les deux lundis. Après juillet 2017, les quantités des ordres peuvent différer, puisque la valeur du portefeuille n'est plus la même.fixest identique àavantau centime, ordres et statistiques.Ce qui sera publié : dans le README, le paragraphe « Limite connue » est remplacé par le résultat : mois rééquilibrés, Sharpe, CAGR, pire baisse, nombre d'ordres avant et après, et les identifiants de backtest. Les chiffres de la section #20264 ne sont pas réécrits. Si l'écart de Sharpe dépasse 0,05 sur la fenêtre codée, le paragraphe le dit. Si la prédiction 4 échoue, la règle hors échantillon de #20407 s'applique : pas de dégradation si ΔSharpe > −0,10, ΔCAGR > −0,5 pt et Δpire baisse < +1,0 pt.
Pas de verdict de stratégie. C'est la correction d'un défaut, sur des fenêtres déjà vues.
jsboige commentedon Oct 12, 2026
Résultat,
DualMomentumNoTLT(lane myia-po-2024:CoursIA-3) — PR #20509. Mesure du pré-enregistrement c.6131650205.b2ff070974e440f64029c69473bb7af7a32a1a24f317c96e5d0fe2bbb00ca583bbf5126ce1eac446800cedc56c04f54691eae7e7741ff46af96b5cd83dbccc5aLes quatre prédictions tiennent :
REBALdu 2017-07-03 13:00 et du 2023-07-03 13:00 reviennent, soit 120 au lieu de 118.Le 3 juillet 2023, le rééquilibrage vend GLD et passe sur QQQ et SPY un mois plus tôt. |ΔSharpe| = 0,005 sur la fenêtre codée.
Pas de verdict de stratégie. La variante Framework (
alpha_model.py) n'est ni touchée ni mesurée ; elle reste au recensement de #20264.Traces :
G:\Mon Drive\MyIA\IA\QC-traces\20286-half-session\DualMomentumNoTLT\.jsboige commentedon Oct 12, 2026
Pré-enregistrement, les cinq projets « requête N+5, garde
0,8 N,month_start» (lane myia-po-2024:CoursIA-3, suite du constat c.6115476132). Écrit avant tout backtest.Ce qui reste ouvert. Le constat c.6115476132 a montré que le lundi 3 juillet 2017 et le lundi 3 juillet 2023 sont à la fois le premier jour de cotation du mois et une demi-séance. L'argument « aucune demi-séance n'est un premier jour de mois » ne protège donc plus
DualMomentum,Trend-Following,VolTarget-Momentum,RiskParityetGlobalMacro-Regime. Leur cas n'a pas été mesuré.Lecture du code (
mainàf35a009f20)schedule.on(date_rules.month_start("SPY"), time_rules.after_market_open("SPY", 30)), donc à 10:00 New York. Aucun n'appellehistory()danson_data.history(SPY, 68)ethistory(SPY, 126)sont complets (23 sur 23). La dernière barre rendue est celle de la veille. Elle n'a pas testé 252 + 5 barres.N + 5barres (N = 252, 126 ou 60), écartent un titre silen < 0,8 N, puis lisentiloc[-N]. Si l'appel de 10:00 rendait environ 21 barres de moins, comme à 13:00, la requête de 257 en rendrait environ 236. La garde passerait (236 ≥ 201,6) etiloc[-252]lèveraitIndexError: le backtest s'arrêterait le 3 juillet.XLCcomme QC MeanReversion : le code de main plante le 2018-09-11 (IndexError, XLC trop jeune) et le README décrit la v4.0 #20346).Code mesuré :
main.pydemain, sans modification. sha256 (12 premiers caractères) : DualMomentum069d1fe98210, Trend-Followinge6b44645daae, VolTarget-Momentumb0ba6f938f49, RiskParityc9ad7fb7ee8f, GlobalMacro-Regime5d9634c91d7f. Fenêtres du code : 2015-2024 pour quatre projets, 2018-2025 pour GlobalMacro-Regime (seul le 3 juillet 2023 y tombe). Nœuds QC, un projet QC par stratégie.Prédictions
completed: true, sans erreur d'exécution.Trending (ou<régime> |) datée du 2017-07-03 10:00 (si la fenêtre le contient) et du 2023-07-03 10:00.Règle de décision, fixée d'avance
0,8 Ndevantiloc[-N]reste notée, mais elle ne relève pas des demi-séances.Pas de verdict de stratégie : c'est une mesure de défaut. Traces :
G:/Mon Drive/MyIA/IA/QC-traces/20286-half-session/five-10h/.jsboige commentedon Oct 13, 2026
Résultat, les cinq projets « requête N+5, garde
0,8 N,month_start» (lane myia-po-2024:CoursIA-3). Mesure du pré-enregistrement c.6134914257. Runs sur les nœuds QC, lus àcompleted: true. Ordres relus à la source jusqu'au compte des statistiques, journaux complets.58ef57b863442c637f9a3b10c5e7d33e930de8b78db29824dcf7ae7dbcf2ba0a2828bd66c72fb6571742ef54a5ddcc899c8c0eb18a43ab2113f9d9ae31575b3f67938945c0c2b37b55d10935a76f0570Les trois prédictions tiennent, pour les cinq projets.
completed: true, sans erreur d'exécution. Un historique court de 21 barres aurait fait leverIndexErrorsuriloc[-252]le 3 juillet : il n'y en a pas.DualMomentumle 2017-07-03 :Trending (5/6): [EEM:21.2%, EFA:19.9%, SPY:17.6%, GLD:-8.1%, TLT:-9.0%. Ce sont des rendements à 12 mois calculés sur la série complète.Décision (règle fixée d'avance) : les cinq projets sortent de la liste des exposés, sans correctif. La mesure c.6107023496 tient donc aussi à 257 barres : un appel planifié à 10:00 un jour de demi-séance lit un historique complet, qui s'arrête à la clôture de la veille. Le défaut reste limité aux appels faits sur la tranche de clôture de 13:00, dans
on_dataou dans un alpha Framework.La fragilité de la garde
len < 0,8 Ndevantiloc[-N]demeure, mais elle relève des titres jeunes (le cas de #20346), pas des demi-séances. Aucun titre de ces cinq univers n'est dans ce cas sur ces fenêtres.Liste qui reste exposée :
Sparse-Index-Tracking-QC: compteur de 63 trancheson_data, tranches de minuit des avis de dividende comprises. Un appel court reporte le rééquilibrage d'un trimestre. C'est la prochaine mesure.Framework_Composite_MomentumRegimeetMomentumRegime-AdaptiveWeights: exposables, non atteints sur les fenêtres publiées.Cloud-SectorRotation-Momentum(fix(qc,#20286): SectorRotation -- demi-seances, history() en double fenetre #20330),MomentumStrategyetFamaFrench(fix(qc,#20286): MomentumStrategy + FamaFrench -- half-session history request (July 3 Mondays) #20411),SectorMomentum(fix(qc,#20286): SectorMomentum -- historique double sur les demi-seances, juillet 2017 et 2023 reequilibres #20444),DualMomentumNoTLT(fix(qc,#20286): DualMomentumNoTLT -- historique double sur les demi-seances (118 -> 120 mois) #20509).Hors sujet, trouvé en route (sans lien avec les demi-séances, pas traité ici) :
VolTarget-Momentum: 11 ordres refusés pour pouvoir d'achat insuffisant, de 2015-02-02 à 2023-12-01. Quand le multiplicateur de volatilité est à son plafond de 1,50, la somme des cibles atteint 150 % : le 2017-07-03, EEM 50 + EFA 40 + SPY 30 + GLD 20 + TLT 10, et les 2015-02-02, 2019-01-02 et 2023-12-01 aussi. Le 2017-07-03 ne compte pourtant aucun refus. Pourquoi seules ces 11 dates en ont, je ne l'ai pas établi : l'ordre d'envoi, achats avant ventes, est une hypothèse non vérifiée. Ces mois-là, une partie des ordres est refusée.RiskParity: 1 ordre refusé, au premier rééquilibrage (2015-02-02).Ces deux constats appellent une issue séparée avant toute lecture des métriques publiées de ces projets.
Traces :
G:/Mon Drive/MyIA/IA/QC-traces/20286-half-session/five-10h/(plan, backtest, ordres, journal,five_check.json).jsboige commentedon Oct 13, 2026
[CLAIMED] lane myia-po-2024:CoursIA-3 -- Sparse-Index-Tracking-QC : backtest de diagnostic (heure et contenu de la tranche, longueur d'historique a chaque declenchement du compteur de 63 tranches), protocole pose ici avant les runs, puis correctif (historique double + compteur sur les seules tranches portant la barre de SPY, cf #20264) et mesure avant/apres -- paths: MyIA.AI.Notebooks/QuantConnect/projects/Sparse-Index-Tracking-QC/**
jsboige commentedon Oct 13, 2026
Protocole posé avant les runs —
Sparse-Index-Tracking-QCLe rééquilibrage trimestriel se déclenche dans
on_dataquand un compteur de tranches vaut 1 modulo 63 (main.py:251-257). Deux défauts peuvent le toucher :history(316, DAILY)rend environ 21 barres de moins._get_returnsrend alorsNone, le rééquilibrage est « reporté », mais le compteur a déjà avancé : le trimestre est perdu. Ce report n'est pas compté dansskipped(ligne de fin).Variantes (QC Cloud, nœuds de backtest sponsorisés, un projet par variante, empreinte du fichier vérifiée avant compilation)
avantmain.pydemain+ journal seul, sans changement de comportement :TRIGGER(heure, présence de la barre SPY, nombre de dividendes, séances écoulées),HIST(lignes et colonnes de l'historique), compteur de reports, ligne finaleSLICESfixhistorypar déclenchement ; compteur de reports dans la ligne de finChaque variante tourne en
mode=sparseetmode=full: 4 backtests. Fenêtre unique, celle du code (2015-01-01 → 2026-08-31) ; la fin effective, ramenée vers J-90 sur les nœuds de backtest sponsorisés, est lue dans le résultat brut. Pas de fenêtre hors échantillon : le code couvre déjà toute la période jusqu'à sa date d'écriture. Ordres relus à la source jusqu'au nombre des statistiques, journal lu en entier, rien n'est lu avantcompleted: true.Prédictions
avant:SLICES total>with_spy_bar(des tranches de minuit existent).avant: au moins unTRIGGERportebar=0(minuit), et l'écart moyen entre deux déclenchements, compté en séances, est inférieur à 63.avant: un déclenchement à 13:00 rendrows< 316 et un report ; un déclenchement à 16:00 ou à 00:00 rendrows= 316. Nombre de reports = nombre de déclenchements à 13:00. Si aucun ne tombe à 13:00, le défaut QC Cloud-SectorRotation-Momentum : un rééquilibrage tombé sur une demi-séance passe tout en SHY (garde d'historique suspectée) #20286 est latent pour ce projet sur cette fenêtre, et sera écrit tel quel.fix: chaque lignehistoryportesession≡ 1 modulo 63 etrows= 316 ; aucun report ;sessionsen fin de run =with_spy_bardeavant.assets= 41 à chaque déclenchement (aucun titre éliminé).jsboige commentedon Oct 13, 2026
Résultats du protocole pré-enregistré (c.6141122238)
Quatre runs sur les nœuds de backtest sponsorisés, tous lus à
completed: true, ordres relus à la source jusqu'au nombre des statistiques, journaux lus en entier. Fin effective relue dans le résultat brut : 2026-07-15 pour les quatre (le code demande 2026-08-31).sparsef47ad143db11752e7ca117dd2f746fcffull5045a6592675037ab4e54a65fde1e33asparseb41e290d2cd285ce13995aa04d3f7fbdfull71ae29a8ba6de3e7ca3a76ccab3f117f« Sautés » regroupe ce que
_skippedcompte déjà : sélection infaisable, filtre de poussière vide, validateur en échec, rotation sous la bande de non-transaction de 10 %. « Reportés » est le compteur ajouté : historique trop court au déclenchement.Prédictions
SLICESde l'avant : 4069 tranches au total, dont 2899 portent la barre de SPY. Les 1170 autres sont des tranches de minuit (avis de dividende).bar=0), le premier dès le 2015-01-02 00:00. L'écart entre deux déclenchements, compté en séances, vaut 44,9 en moyenne (de 42 à 49), au lieu de 63.history(316)y rend 261 lignes, et le rééquilibrage est reporté. Les 64 autres déclenchements, à 16:00 ou à 00:00, rendent 316 lignes. Reports = 1 = nombre de déclenchements à 13:00, dans les deux modes. Comme le compteur avait déjà avancé, ce trimestre était perdu, et le report n'apparaissait dans aucun compteur.historyportentsession≡ 1 modulo 63, toutes à 16:00, toutes à 316 lignes. Écart entre déclenchements : exactement 63 séances, 46 fois sur 46. Aucun report. Ligne de fin :sessions=2899, égal auwith_spy_barde l'avant.sparsefullLa première différence d'ordres tombe dès le 2015-01-02. L'avant déclenche à 00:00, sur la tranche de dividende, et ses ordres
MarketOnOpensont remplis à l'ouverture du jour même. Le correctif déclenche à la clôture de la première séance, et ses ordres sont remplis le 2015-01-05. Ensuite, les dates de rééquilibrage n'ont plus rien en commun : 65 déclenchements contre 47.L'écart de métriques n'a pas de signe à retenir. Le classement entre les deux modes ne bouge pas :
fullgarde le meilleur Sharpe et la pire baisse la plus faible. Moins de rééquilibrages veut dire moins d'ordres et moins de frais. Le correctif ne vise pas un meilleur rendement : il fait rééquilibrer tous les 63 jours de séance, comme le décrit le README, sans trimestre perdu.Ce qui reste
G:\Mon Drive\MyIA\IA\QC-traces\20286-sparse-index\.jsboige commentedon Oct 13, 2026
Complément : fenêtre codée entière
Les deux runs annoncés au point « Ce qui reste » du commentaire précédent sont faits. Ils ont tourné sur un nœud de backtest qui ne tronque pas la fenêtre (2015-01-01 -> 2026-08-31), et ont été lus à
completed: true; le nombre d'ordres relus à la source égale celui des statistiques. L'avant est le run publié du README (2026-09-01), lancé sur unmain.pyidentique à celui d'avant le correctif. Six semaines séparent les deux runs : le moteur ou les données ont pu changer entre-temps, et cette paire est moins contrôlée que celle des nœuds sponsorisés.sparsea9bb668b764d55155f6a074f84627757->8d5874334173ee85fea7affb5e2ae899full8e647c58edff52af9836b25b80ff433d->4f5c12db8ea30104fbf46f248758b780Après correctif, dans les deux modes : 47 déclenchements, tous à 16:00, 63 séances d'écart chaque fois, 316 barres d'historique, aucun report,
sessions=2932. Le classement entre les deux modes ne change pas. Lefullgagne un peu de Sharpe et lesparseen perd un peu : les deux mouvements vont en sens contraires, ce qui confirme que l'écart de métriques n'a pas de signe à retenir.PR : #20532. Traces :
G:\Mon Drive\MyIA\IA\QC-traces\20286-sparse-index\fix-{sparse,full}-perso\.fix(qc,#20286): SectorMomentum -- historique double sur les demi-sean…
fix(qc,#20286): DualMomentumNoTLT -- historique double sur les demi-s…
fix(qc,#20286): Sparse-Index-Tracking -- compteur de séances et histo…