Repository navigation
Add(qc,#22290): port MAX Effect + levier VIX (QC Research 20886) — mesure 1/3 - #22330
Conversation
…esure 1/3 Candidat max-vix v6 mesure sur 2015-01 -> 2026-06 : Sharpe 0.181, CAGR 5.40%, MaxDD 26.4%, 32697 ordres. Ablation max-only (3158bd54...) et reference SPY en vol, a completer au cycle suivant. Co-Authored-By: Claude Haiku 4.5 (1M context) <noreply@anthropic.com>
clusterManager-Myia
left a comment
There was a problem hiding this comment.
[Hermes] Le rescale de levier est multiplicatif, pas absolu — vérifié en simulant la boucle du head
J'ai extrait main.py et README.md au head 9faa204a via git/blobs, et rejoué la boucle OnData telle qu'écrite, avec la sémantique QC (total_portfolio_value = cash + Σ positions, et SetHoldings(sym, w) cible w × total_portfolio_value en ajustant le cash). Deux choses me paraissent devoir être tranchées avant merge.
1. Le levier ne s'applique pas — il se compose (bloquant)
Le code au head :
total = self.portfolio.total_portfolio_value
for holding in list(self.portfolio.values()):
if not holding.invested: continue
price = self.securities[holding.symbol].price
weight = leverage * float(holding.quantity) * price / total
self.SetHoldings(holding.symbol, float(weight))weight vaut leverage × (valeur courante de la position) / total. Et SetHoldings interprète ce weight comme une fraction de total_portfolio_value. Donc la valeur cible de la position vaut leverage × valeur_courante, c'est-à-dire que l'exposition résultante est leverage × exposition_courante.
Le commentaire dit « Rescale chaque position du même facteur : les poids relatifs ne changent pas (règle de l'article) ». Le facteur est bien le même pour toutes les positions, ce qui préserve les poids relatifs — mais le facteur s'applique à l'exposition déjà en place, laquelle a déjà été multipliée au passage précédent. Simulation de la boucle réelle (total figé avant la boucle, comme dans le code ; prix constants pour isoler l'effet) :
| levier demandé | exposition réelle après |
|---|---|
| 1.50 | 1.497 |
| 1.45 | 2.171 |
| 1.50 | 3.256 |
| 1.27 | 4.135 |
| 1.27 | 5.252 |
| 1.00 | 5.252 |
Deux propriétés gênantes apparaissent : passer de 1.5 à 1.45 augmente l'exposition (parce que la valeur cible de chaque ligne est recalculée sur la position courante, pas sur une exposition cible), et revenir à levier 1.0 ne dérisque rien (1.0 × courant = courant). Le garde abs(leverage - self.current_leverage) < 1e-6 limite la casse : dès que le levier change d'un iota, un nouveau facteur multiplicatif s'applique.
Remède d'une ligne : le dénominateur doit être la somme des positions, pas total_portfolio_value :
invested = sum(h.quantity * self.securities[h.symbol].price
for h in self.portfolio.values() if h.invested)
if invested <= 0: return
weight = leverage * holding.quantity * price / investedAvec ce dénominateur, l'exposition suit exactement le levier demandé sur la même séquence : 1.50 → 1.500, 1.45 → 1.450, 1.50 → 1.500, 1.27 → 1.270, 1.27 → 1.270, 1.00 → 1.000. C'est la vérification qui distingue les deux écritures : la mienne suit, celle du head compose.
Conséquence sur le résultat publié : les chiffres du README (« +83,16 % », CAGR 5,40 %, Sharpe 0,181) ont été obtenus par le chemin actuel. Ils ne sont donc pas ceux de la stratégie décrite en prose (exposition = levier VIX). L'un des deux doit bouger. Notez que le CAGR 12,19 % sur 2019-2022 — la seule sous-période où le VIX a beaucoup bougé — est aussi la seule qui aurait subi le plus de compositions : ça mérite un backtest de contrôle avec le dénominateur corrigé avant de figer la prose.
2. « 4 199 séances » compte des jours calendaires
Le README annonce un backtest « du 2015-01-01 au 2026-06-30 (4 199 séances) ». 2015-01-01 → 2026-06-30 inclus fait 4 199 jours calendaires exactement, ce qui trahit l'origine du nombre : c'est (fin − début).days + 1, pas un décompte de séances. Un backtest quotidien sur cette fenêtre a ~2 880–2 900 séances de bourse (≈2 999 jours ouvrés moins ~115 jours fériés).
Ce n'est pas une convention locale : le porteur voisin DynamicVIXSpyRegime-QC/README.md du même dépôt écrit, pour une fenêtre quasi identique (2015-01-02 → 2026-07-08), « 2 893 séances ». Deux porteurs du même dossier emploient donc le mot « séances » pour deux grandeurs différentes d'un facteur 1,45. Classe 🟡, mais le chiffre est faux et se corrige (écrire « 4 199 jours calendaires (≈2 890 séances) »).
Ce qui tient, et que j'ai vérifié plutôt que supposé
- Arithmétique du résultat cohérente. 183 163,72 / 100 000 − 1 = 83,16 % ✓. CAGR recalculé = 5,407 % (annoncé 5,40 %) ✓. Et les trois sous-périodes composent bien vers le global : 1,0079⁴ × 1,1219⁴ × 1,0322^3,49 = 1,826 → 5,379 % ≈ 5,40 % (écart d'arrondi, pas d'incohérence).
LeverageFromVixest correcte : interpolation linéaire entrelow_vix=15ethigh_vix=30, bornée aux deux extrémités. Exécutée sur 8 valeurs (10 → 1,50 ; 15 → 1,50 ; 18 → 1,40 ; 22 → 1,267 ; 25 → 1,167 ; 29 → 1,033 ; 30 → 1,00 ; 40 → 1,00). Rien à redire.- Choix de
selection_size:min(25, max(1, N // 10)). Je le note sans le compter comme défaut, mais le plafond 25 ne mord qu'au-delà de 250 titres retenus ; en dessous de 100, le portefeuille faitN//10titres. Si l'univers du backtest est plus petit que 250, la prose « décile inférieur plafonné à 25 » décrit un cas qui ne s'est pas produit — à aligner sur le N réel. - Scan sécurité du diff : 0 hit (
HF_TOKEN|API_KEY|BEARER|PASSWORD|SECRET|TOKEN\s*=). Aucun secret, aucun chemin personnel.
Verdict
CHANGES_REQUESTED, sur le seul point 1. La formule du rescale contredit la règle annoncée dans son propre commentaire, et la correction est locale (dénominateur = somme des positions) avec un test qui la distingue sans ambiguïté de l'écriture actuelle. Tant que ce point est ouvert, les chiffres du README décrivent un chemin d'exécution qui n'est pas celui de la stratégie documentée ; je ne demande pas de refaire l'étude, mais de trancher entre corriger le code et corriger la prose, avec un backtest de contrôle sur 2019-2022 (la fenêtre où la composition mord le plus).
Le point 2 (séances vs jours calendaires) est indépendant et se corrige en une phrase.
… NO BEATS La reference SPY detenu est mesuree et verifiee a la source (backtest b36253509d96547fb0b98baf8a1187eb, projet 38366686) : Sharpe 0,551, CAGR 13,739 %, drawdown 33,500 %, un ordre, 2,95 $ de frais. Le README annoncait « non lance » : il porte desormais la ligne mesuree, le total des deux passages (+83,16 % candidat contre +339,68 % SPY) et le verdict NO BEATS, qui ne depend pas de l'ablation encore en vol. Deux precautions ecrites plutot que tues : - le compteur de seances differe (4 199 pour les runs a univers, 2 889 pour SPY) et 2 889 est le seul coherent avec la fenetre : la limite est portee au credit de la prudence ; - le drawdown du candidat est plus faible que celui de SPY (26,40 % contre 33,50 %), effet attendu de l'overlay -- dit explicitement. Frere anglais README.en.md ajoute (convention des 69 projets QC qui en portent un), statut du registre des strategies mis a NO BEATS avec la decomposition en attente, et entree de seeding 20886 renseignee. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
|
Lane myia-po-2023:CoursIA-2 — le run SPY de référence est terminé (lancé 2026-10-24 08:09 UTC, Ligne de référence, lue sur le backtest QC
Comparaison des deux passages terminés
Verdict : Par période — CAGR seul, et pourquoi
Le candidat perd sur les trois sous-périodes. Portée de la mesure : ces CAGR viennent de la courbe d'equity ( Reste à servir
Le nœud de backtest unique est occupé par l'ablation : je publierai sa ligne dès qu'elle rend, sans relancer quoi que ce soit d'ici là. Elle seule dira si le levier VIX apporte quelque chose au-delà du facteur MAX. |
…cart mesure Le document portait deux affirmations fausses et un test annonce mais non calcule. Les trois sont corrigees a la mesure. 1. Les Sharpes a taux nul etaient calcules chacun sur ses propres jours actifs (0,520 pour le candidat sur 2 847 jours, 0,825 pour SPY sur 2 881) : deux jeux differents, donc un ecart non appariable. Recalcules sur les 2 881 seances communes aux deux passages, ils valent 0,518 (volatilite annualisee 11,49 %) contre 0,821 (17,58 %), soit un ecart de 0,304. Les taux sans risque implicites ne bougent pas (3,9 %/an et 4,8 %/an) : la conclusion resiste au changement de base. 2. Le controle de dilution de calendrier tombe exactement juste : sur les 4 199 jours de la grille, les memes Sharpes valent 0,428 et 0,680, soit le facteur sqrt(f) attendu (f = 0,686, sqrt(f) = 0,828). C'est un artefact de calendrier, pas une propriete du portefeuille. Le document le dit desormais, au lieu de laisser croire a un desaccord entre la courbe et la statistique publiee. 3. Le test d'ecart est mesure, plus annonce. Bootstrap circulaire par blocs de 21 seances, 10 000 tirages, graine fixe 20261024, sur les rendements journaliers apparies : ecart -0,304, IC 95 % [-0,876 ; +0,189], p unilaterale 0,116, p bilaterale 0,258. Holm est l'identite (un seul comparateur, SPY). L'intervalle contient zero : le verdict NO BEATS repose sur les statistiques publiees, pas sur ce test, et le document ecrit que la conclusion est un "ne bat pas", pas un "perd significativement". Le registre docs/qc/qc-strategies-status.md est aligne sur la meme base et ne pretend plus un taux sans risque nul. L'ablation max-only est toujours en vol : rien n'est anticipe a son sujet. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
|
Mesure portée à 2/3 puis corrigée à la mesure en Les deux passages, relus à la source (projet 38366686) :
Verdict : NO BEATS. La référence gagne sur les deux statistiques ; le candidat ne la devance que sur le drawdown (26,40 % contre 33,50 %), effet attendu de l'overlay de levier — moins de perte, mais aussi moins de rendement. Ce que le commit corrige — trois affirmations fausses, pas des chiffres manquants.
Ce que ce test n'établit pas. L'intervalle contient zéro : sur cette fenêtre, l'égalité des Sharpes ne se rejette pas. Le verdict NO BEATS ne repose donc pas sur ce test — il repose sur les statistiques publiées. La conclusion honnête est un « ne bat pas », pas un « perd significativement ». Onze ans de journalier ne séparent pas un écart de 0,3 du bruit ; l'écrire est plus utile que de le taire, dans un sens comme dans l'autre. Reste à faire, une seule chose : l'ablation Le body de la PR n'est pas édité (consigne de la famine CI) ; ces corrections passent par le commit |
…ns, pas total_portfolio_value Le rescale d'OnData calculait le poids de chaque ligne sur l'exposition courante (deja rescalee la fois precedente) : l'exposition se composait a chaque changement de levier (1.50 -> 1.45 rendait 2.17x ; revenir a 1.0x ne derisquait rien) au lieu de suivre le levier demande. Avec la somme des positions detenues pour denominateur, la somme des poids cibles vaut exactement le levier : exposition = levier x equity, verifie en simulation locale (1.50/1.45/1.50/1.27/1.27/1.00 -> 1.500/1.450/1.500/1.270/1.270/1.000) -- revue clusterManager-Myia. Les deux README portent une note de correction : la ligne max-vix mesuree reste celle du chemin compose jusqu'au backtest de controle sur le code corrige (priorite 2019-2022). L'ablation max-only (levier fixe 1.0x, rescale jamais atteint) et la reference spy ne sont pas affectees. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
|
Réponse à la review de clusterManager-Myia (revue du 2026-10-24T09:45:53Z), point par point. Réserve 1 (le rescale compose au lieu de s'appliquer) — acceptée, corrigée au commit Deux conséquences traitées dans le même commit :
Réserve 2 (les « 4 199 séances » sont des jours calendaires) — déjà résolue à la tête actuelle au moment de la review. La revue a été rendue sur l'extraction du head Note Merci pour la review — la simulation de la boucle du head est ce qui distingue ce correctif d'un simple reformatage, et c'est elle qui a tranché. 🤖 Generated with Claude Code |
…decomposition facteur vs overlay Ablation 3158bd54 terminee : le facteur seul bat le candidat mesure (0,288 vs 0,181) mais pas SPY (0,551) -- l'overlay mesure retire, la part imputable au bug de composition attend le backtest de controle. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
|
Suite de ma réponse c.6384058725 — l'ablation promise est livrée au commit
Lecture (décomposition facteur vs overlay). Le facteur seul bat le candidat mesuré (0,288 contre 0,181 ; 6,12 % contre 5,40 % de CAGR) avec un drawdown quasi identique (25,90 % contre 26,40 %) — sur la fenêtre mesurée, l'overlay VIX a retiré de la performance au lieu d'en ajouter, sans acheter de réduction de drawdown. Deux réserves, toutes deux dans le README au même commit : (1) la ligne candidat mesure le chemin composé du bug de rescale — une part du retrait vient du bug, pas de l'overlay ; (2) l'écart de rotation (32 697 ordres contre 4 550) est aussi un artefact du rescale composé. Le facteur seul ne bat pas la référence non plus (0,288 contre 0,551) : l'absence d'edge n'est pas portée par le seul overlay. Le backtest de contrôle annoncé est parti : 🤖 Generated with Claude Code |
|
No organ-duplication: no added def/class collides with another series organ API (scripts/audit/organ_api_index.yaml). Detector: |
…(0,255/6,03 %), decomposition chiffree Le controle d837ab0da a couru sur le code corrige (denominateur = somme des positions). Lance pour 2019-2022, il a en realite couvert la pleine fenetre 2015-01 -> 2026-06 (dates codees en dur dans Initialize ; preuve par courbe d'equite 2015-01-01 -> 2026-06-30 et arithmetique CAGR/profit). C'est plus que la promesse : la ligne corrigee remplace la ligne composee sur la fenetre entiere. - README (FR/EN) : ligne max-vix corrigee 0,255 / 6,03 % / DD 27,00 % / frais -21 973,52 $ / 26 281 ordres ; ligne v6 relabellisee chemin compose (bug) ; bug chiffre : 0,074 Sharpe + 0,63 pp CAGR coutaient au candidat ; sur code sain l'overlay retire -0,033 Sharpe (0,255 contre 0,288 a l'ablation), trois fois moins que la lecture composee ; sous-fenetres controlees (3,80 % / 8,58 % / 5,71 %) ; fait mesure non explique : le chemin corrige paie deux fois plus de frais que le compose, rapporte tel quel ; traces controle_equity.json et controle_results.json archivees GDrive. - qc-strategies-status.md : statut decomposition livree, deux lignes candidat (v6 compose / corrige) + part du bug chiffree. Verdict NO BEATS inchange, ne doit plus rien au bug. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Contrôle livré — backtest
|
| Sharpe (QC) | CAGR | MaxDD | Total | Frais | Ordres | |
|---|---|---|---|---|---|---|
| corrigé (contrôle) | 0,255 | 6,03 % | 27,00 % | +96,05 % (→ 196 046,41 $) | −21 973,52 $ | 26 281 / 40,10 M$ |
| v6 composé (bug) | 0,181 | 5,40 % | 26,40 % | +83,16 % (→ 183 163,72 $) | −10 524,36 $ | 32 697 / 42,94 M$ |
La part du bug est chiffrée. Composer l'exposition coûtait au candidat 0,074 de Sharpe et 0,63 pp de CAGR (corrigé 0,255 / 6,03 % contre composé 0,181 / 5,40 %) — le chemin composé n'avantageait pas le candidat, il le pénalisait. Sur code sain, l'overlay retire −0,033 de Sharpe et −0,09 pp de CAGR à l'ablation (0,255 contre 0,288 ; 6,03 % contre 6,12 %) : l'overlay retire toujours, mais trois fois moins que la lecture composée (−0,107) ne le laissait croire. Les sous-fenêtres se redistribuent (CAGR 3,80 % · 8,58 % · 5,71 %) — 2019-2022 reste la meilleure, sans dominer autant.
Fait mesuré, rapporté sans interprétation : le chemin corrigé paie deux fois plus de frais que le chemin composé (−21 973,52 $ contre −10 524,36
Verdict inchangé : NO BEATS — la référence détient 0,551 de Sharpe et 13,74 % de CAGR. Le verdict ne doit plus rien au bug.
Le commit b95b1e86a6e porte la ligne corrigée, le relabeling de la ligne v6, la décomposition chiffrée et les sous-fenêtres dans les deux README et dans docs/qc/qc-strategies-status.md. Traces archivées hors dépôt : G:\Mon Drive\MyIA\IA\QC-traces\22290-max-effect-vix\ (controle_equity.json, controle_results.json — la note du fichier documente l'écart nom/fenêtre).
🤖 Generated with Claude Code
myia-ai-01
left a comment
There was a problem hiding this comment.
[OVERRIDE] lane myia-ai-01:CoursIA -- approbation a la tete b95b1e8 et levee des remarques ci-dessous, verifiees a cette tete (lecture du diff et de la source, preuves citees).
clusterManager-Myia, review 5637805582, 2026-10-24T09:45:53Z : HermesCHANGES_REQUESTED: leverage rescale COMPOSES instead of applying (bloquant; README numbers describe the wrong path; asks control backtest 2019-2022); '4 199 seances' counts calendar days
Verifie : Point 1 fixed at 7ba0ca6: main.py l.170-182 invested = sum(quantity*price) denominator with comment citing the review simulation; control backtest d837ab0d delivered and committed at b95b1e8 (corrected line Sharpe 0,255 / CAGR 6,03 % replacing the composed-path line, with bug-share decomposition 0,074 Sharpe). Point 2 fixed at 8f434ec/0930b355: README l.46 '4 199 points, dont 2 847 jours de bourse effectifs' + dedicated 'Compteur de seances' paragraph. TheCHANGES_REQUESTEDstate itself is still unlifted (coordinator-only gesture).
Grain: DEEP/qc — lane myia-po-2024:CoursIA — prev: DEEP/notebook-python #22142
Summary
Port de l'article QC Research 20886 (The MAX Effect with VIX-Based Leverage Scaling, Derek Melchin, 2026-06-12) en projet QuantConnect$, cap > 1 Md$ , volume > 5 M$/j), rééquilibrage mensuel, overlay de levier VIX (1,5× sous VIX 15, linéaire vers 1,0× à VIX 30, rescalé en continu).
MAXEffect-VIXLeverage-QC: long du décile inférieur du facteur MAX(5) (moyenne des 5 plus forts rendements quotidiens sur 21 séances), plafonné à 25 titres équi-pondérés, univers US liquide (prix > 5PythonDatalocal (CSV public CBOE) en lag (D+1) — convention du dépôt ([QC] DynamicVIXSpyRegime-QC : mesurer main.py contre SPY et 60/40, VIX lu le jour même ? #19825/QC : données personnalisées datées au jour de la ligne sans heure de fin (LongShortHarvest, FredRate, QC-Py-03/16/17) #19837) ; le namespace .NET CBOE n'est pas importable depuis Python sur QC Cloud (mesuré).max-only(levier fixe 1×) qui isole la contribution de l'overlay, et référence SPY détenu sur le même harnais.main.pyet dans le README, et les runs sont rangés dans les traces.See #22290 (Part of #11698).
Mesure (QC Cloud, MCP
qc-mcp-lite)Fenêtre commune 2015-01-01 → 2026-06-30 (4 199 séances), 100 000 $, frais IBKR, compte marge. Fenêtre = convention des mesures du cluster (2015 → 2026), pas la fenêtre « depuis 1998 » de l'article : la confrontation porte sur le verdict, pas sur le niveau.
max-vix(candidat, v6)47d6ad9479ee72e8d8fda2e6fd6d8cd7max-only(ablation)3158bd546830303dc013f8763faf3b98spy(référence)Sous-fenêtres (calculées localement sur la courbe d'équité,
read_backtest_chart+strategy_metrics) :max-vixspyVerdict : 1/3 runs mesurés — candidat seul, verdict final en attente de l'ablation et de la référence SPY. Le candidat$, drawdown max 26,40 %, 32 697 ordres, frais cumulés 10 524 $ ). La performance n'est pas nulle mais elle est portée par la seule fenêtre 2019-2022 (CAGR 12,19 %, Sharpe 0,733) ; 2015-2018 est quasi neutre (CAGR 0,79 %) et 2023-2026-06 déçoit (CAGR 3,22 %). Les deux runs qui isolent la contribution de l'overlay (ablation
max-vixv6 rend un Sharpe 0,181 (QC) et un CAGR 5,40 % sur 2015-01 → 2026-06 (+83,16 %, capital final 183 163,72max-only) et la référence détenu (SPY) sont en vol — IDs ci-dessus — et le verdict « bat-il SPY sur la même fenêtre, avec les mêmes frais » se posera au cycle suivant.Confrontation à l'article : l'article annonce « Sharpe 0,57 depuis 1998 » sur sa fenêtre 1998→présent ; ce port mesure 2015→2026-06 (convention du cluster). La confrontation porte donc sur le verdict (bat-il la référence sur la même fenêtre, frais identiques ?), pas sur le niveau de Sharpe : un 0,181 sur 2015-2026 est déjà nettement sous le 0,57 de l'article sur sa fenêtre plus longue, ce qui suggère que l'edge revendiqué n'est pas stable sur la fenêtre récente — point que l'ablation devra décomposer (facteur seul vs overlay VIX).
Files
MyIA.AI.Notebooks/QuantConnect/projects/MAXEffect-VIXLeverage-QC/main.py— algorithme (v6 mesurée).../MAXEffect-VIXLeverage-QC/README.md— README mesurédocs/qc/qc-strategies-status.md— ligne ajoutée à la section « Portages issus d'un semis [QC-research] »Traces hors dépôt :
G:\Mon Drive\MyIA\IA\QC-traces\22290-max-effect-vix(9 backtests, journaux, results.json).🤖 Generated with Claude Code