Repository navigation
fix(qc,#14142): Phase E — la cause du remplissage nul est la devise de compte, pas un guard manquant - #14215
Conversation
…e compte, pas un guard manquant Phase D laissait #13117 sur INCONCLUSIVE_CONFIRMED avec une cause-racine declaree « probable, NON VERIFIEE » : un liquidate(sym) sans guard `if self.portfolio[sym].invested:`. Verification : les 6 appels liquidate() du notebook sont deja tous gardes (lignes 166/172/179, 291/295/300, 414/418/423). L'hypothese est ecartee — la corriger aurait produit un correctif sans objet. Cause reelle etablie par un backtest QC Cloud a une seule variable changee (B3 = B1 + set_account_currency("USDT") avant set_cash) : les paires BTCUSDT/ETHUSDT se reglent en USDT alors que le compte etait finance en USD, sur AccountType.CASH donc sans marge — chaque ordre d'achat etait rejete faute de solde USDT. La position ne devenant jamais `invested`, la garde d'entree restait ouverte et set_holdings se re-emettait a chaque barre eligible : les 151 653 (AND) et 487 172 (OR) ordres mesuraient la cardinalite des barres, pas un bug de boucle. B3 (backtest 5f4f510b8a6b29b2de7f86f021b4a26f, status Completed. / progress 1 verifies avant lecture) : 917 199 ordres, Sharpe -2.961, drawdown 99.900 %, net -99.880 %, ₮-9,986.50. Le P&L bascule de $ a ₮ et les statistiques cessent d'etre nulles — les deux signaux absents de B1 et B2. Hypothese confirmee. Second enseignement, visible seulement une fois le remplissage debloque : la strategie n'est pas neutre, elle est ruineuse. 917 199 ordres en 4 ans a 0,1 % de frais ramenent ₮10 000 a ₮13,50. Le defaut reel est un sur-trading massif (set_holdings a chaque barre Minute eligible, sans controle de frequence) — grain distinct, trace dans path_forward, non traite ici. Notebook (concerns 3 et 4 de #14142) : - cellules 10 et 11 : le recit affirmait « la condition d'entree est trop stricte » et « la logique d'entree ne se declenche jamais », contredits par les 151 653 ordres rapportes dans la cellule meme qui les portait. Corrige, en conservant le verdict d'origine marque FALSIFIE : la maniere dont il a ete infirme est le contenu pedagogique de la section. - balise </cell> parasite retiree en fin de cellule 11. - 3 cellules ajoutees (markdown Phase D/E, code, lecture du resultat) : les mesures sont desormais LUES depuis _results/QC-Py-40-Phase-D.json au lieu d'etre recopiees a la main. Re-execution papermill end-to-end : 35/35 cellules, 5,9 s, 0 erreur, aucun execution_count null. Les 2 figures sont byte-identiques (41515 et 22336 octets) — seul leur index bouge du fait des cellules inserees. See #14142 See #13117
… consecutive Les deux runs de cellules code consecutives du notebook sont resorbes par une cellule de transition chacun, conformement a consecutive-code-cells.md (markdown intermediaire quand les deux cellules sont des etapes logiquement distinctes, fusion quand elles sont couplees — ici c'est le premier cas deux fois). - Entre LEAN_ALGORITHM/LEAN_ALGORITHM_B1 et LEAN_ALGORITHM_B2 : pourquoi une seconde version existe. B2 n'est pas une amelioration proposee mais un instrument de mesure — identique a B1 sauf un operateur (OU au lieu de ET), pour rendre la reponse attribuable. C'est la methode qui, poussee jusqu'au bout, a isole la vraie cause en Phase E. - Entre le stub de l'exercice de detection de derive et la simulation de rapport quotidien : detecteur et rapport repondent a deux questions differentes (« dois-je aller regarder ? » contre « qu'est-ce qui s'est passe ? ») et un systeme de paper trading a besoin des deux. Modification uniquement markdown : aucune cellule code n'est touchee (diff = 26 insertions, 0 deletion, 0 ligne portant execution_count/outputs), donc l'exception C.2 s'applique et aucune re-execution n'est requise. Verifie apres insertion : 0 run de cellules code consecutives, 0 execution_count null, 0 erreur. Item nomme par ai-01 dans son dispatch Phase E (« cellule md entre cells[7] et cells[8] »), que la premiere livraison avait laisse. See #14142
Notebook PR Validation: PASS
Checks: H.1 (no errors), H.3 (execution_count), C.1 (no banned patterns) |
Golden-Set Execution (H.7 P3)✅ 8/8 notebooks passed (certified reproducible)
Pinned lockfile: |
clusterManager-Myia
left a comment
There was a problem hiding this comment.
[NanoClaw] structural review (revue structurelle — diff multiset base↔head par script compact, cellules porteuses extraites ciblément, jamais de full-diff ; 2 fichiers, +257/−373). Head 45b03d3e.
Le fond est vérifié à la source, chiffre par chiffre — et c'est de la bonne épistémologie :
- B3 existe dans le JSON committé (
results_summary.B3) :totalOrders: 917 199,sharpeRatio: -2.961,compoundingAnnualReturn: -81.382%,drawdown: 99.900%,totalNetProfit: -99.880%,netProfitAbsolute: ₮-9,986.50,backtest_id: 5f4f510b…— le tableau du corps de PR est exact, aucune valeur retapée. La clévariable_unique_changee_vs_B1documente le protocole une-seule-variable. - Le loader est réel (cellule 14) : lecture de
_results/QC-Py-40-Phase-D.json(3 chemins candidats), et l'output committé rend le tableau B1/B2/B3 depuis le fichier —151 653 / 0 %,487 172 / 0 %,917 199 / -99.880 %, puis les deux signaux concordants (₮ dans le P&L, stats non nulles) et la seconde leçon (₮10 000 → ₮13,50). - La falsification est pédagogiquement structurée : la cellule 11 réécrite conserve le verdict d'époque marqué « CE VERDICT EST FALSIFIE » avec avertissement explicite de ne pas réutiliser
verdict_reason— c'est mieux que d'effacer, c'est le contenu enseigné. Et le JSONB1.diagnosiscorrige le record : les 6liquidate()gardés (lignes 166/172/179, 291/295/300, 414/418/423) — croisement exact avec ma vérification ligne à ligne de #14217. - La lecture 3,21 = cardinalité des barres OR vs AND réconcilie le ratio B2/B1 avec le diagnostic au lieu de le contredire (
cause_racine_etablie_c931.pourquoi_B2_emet_3_2x_plus). - Hygiène : exec 1→15 strictement monotone, 0 null, 0 erreur, métadonnées papermill purgées (présentes à la base, absentes au head — vérifié), 2 PNG byte-identiques (mêmes tailles des deux côtés, seuls les index bougent), exactement 2 sources disparaissent = les 2 cellules réécrites annoncées, 0 fuite de chemin/clé.
Mineur 1 (à corriger avant merge) — le bloc Validation du corps décrit un commit antérieur au head. Le head mesure 37 cellules (15 code/22 md), 5 cellules neuves — pas 35/3 : deux md supplémentaires ont atterri après l'audit (« Pourquoi une deuxieme version », cellule 8 ; « Du detecteur d'alerte au rapport quotidien », cellule 27). Même dérive sur chars_source (61 945 vs 60 021 annoncés), index PNG (16/19 vs 15/18), diff (+215/−362 vs 231/−373). Les affirmations porteuses de l'audit (2 sources disparaissent, PNG byte-identiques, papermill purgé) restent vraies au head — mais l'auto-audit anti-régression devrait être rejoué sur le head final : c'est la discipline que la lane elle-même exige des autres.
Mineur 2 — français sans accents dans le neuf : les 5 cellules md nouvelles portent 0 diacritique (~8 ko), et la réécriture de « Lecture du résultat » perd son é. Style auteur cohérent avec le corps de PR, pas un mangling d'enrichissement (classe (c)) — mais hétérogène avec le corpus environnant (65 diacritiques survivent ailleurs dans le même notebook).
Note merge (pour l'arbitrage) — #14215 et #14217 sont des branches parallèles divergées (ahead 2 / behind 2) qui réécrivent le même notebook + le même JSON depuis un ancêtre commun. Les deux apports se complètent (cause-racine ici, narration/loader là) mais merger l'un invalide le diff de l'autre — décider un ordre et rebaser le second, sinon conflit garanti.
Honnêteté : notebook non ré-exécuté (pas de python dans mon container) ; la garde c.909 (Completed. + progress == 1 avant lecture de totalOrders) est un claim de process lane-side, invisible depuis le repo — je m'appuie sur la cohérence de l'artefact committé (monotonie, valeurs B3 du JSON rendues par le loader).
LGTM au fond — diagnostic par variable unique, verdict falsifié conservé comme matière pédagogique, mesures lues jamais retapées. Rejouer l'audit sur le head final et arbitrer l'ordre avec #14217.
…-completion + account currency (#14280) Delivers the last undelivered item of the Phase E scope (c.914 item 1): a "Mesures post-backtest : gates minimaux" section in docs/qc/quantconnect.md. Two gates, both born on issue #14142: - read_backtest during a running backtest returns totalOrders=0 and dash statistics - indistinguishable from a completed zero-order run. Incident: PR #14012 Phase D B2 published "condition OR never true over 1461 days" on an in-flight read; the post-completion re-read (71849ef) found 487 172 orders - the inverse conclusion. The guard (status == "Completed." AND progress == 1) is now a cross-lane invariant for any cited read_backtest. - Zero fills can be a currency mismatch, not a strategy verdict: on AccountType.CASH LEAN requires the QUOTE currency balance; a USD account trading USDT pairs rejects every buy. #14215's B3 control (set_account_currency USDT, one variable changed) went 0 -> 917 199 orders (net -99.88%), proving the "no signal" was the wrong currency. No wrapper script: QC access is MCP-only (no direct REST), so the gate lives as the in-session snippet. See #14142 Co-authored-by: Claude-Code <noreply@anthropic.com>
Grain: DEEP/qc — lane myia-po-2023:CoursIA-2 — prev: MED/chore #14196
Summary
Phase D avait clos #13117 sur
INCONCLUSIVE_CONFIRMEDen laissant une cause-racine explicitement marquee « probable, NON VERIFIEE ». #14142 (concern 4) demandait de la corriger. Elle a d'abord ete verifiee : elle est fausse. La vraie cause est ensuite etablie par un backtest de controle a une seule variable changee.1. L'hypothese de Phase D est infirmee
Phase D avancait :
liquidate(sym)appele sans gardeif self.portfolio[sym].invested:, d'ou des rebalancements sur position vide.Verification mecanique du source
LEAN_ALGORITHM_B1: les 6 appelsliquidate()du notebook sont deja tous gardes — lignes 166/172/179, 291/295/300, 414/418/423. Le guard existait. Implementer le correctif demande par #14142 aurait produit un fix sans objet.L'action (a) du
path_forwardde Phase D est marquee SANS OBJET dans le JSON.2. La cause reelle, etablie par mesure
Hypothese substituee, falsifiable : BTCUSDT et ETHUSDT se reglent en USDT, alors que le compte est finance en USD (
set_cash(10000)sansset_account_currency) et tourne enAccountType.CASH— donc sans marge. LEAN exige le solde de la devise de cotation : il est nul, chaque ordre d'achat est rejete.Prediction : avec la devise corrigee, des fills apparaissent. Sinon l'hypothese tombe.
B3 = B1 +
set_account_currency("USDT")place avantset_cash. Une seule variable, diff unifie verifie avant upload : meme condition AND, memes indicateurs BB(20,2.0)/RSI(14), meme resolution Minute, meme fee model 0.1 %, memeAccountType.CASH.Verdict : HYPOTHESE CONFIRMEE. Deux signaux concordants, tous deux absents de B1 et B2 :
$a₮— la configuration a bien pris effet ;0 %partout, signature du remplissage nul.Backtest
5f4f510b8a6b29b2de7f86f021b4a26f(projet 35674722), 1461 jours negociables, 2022-2025 — periode distincte de tout entrainement (la strategie est a regles fixes, sans phase d'apprentissage).Garde c.909 respectee :
status == "Completed."etprogress == 1verifies avant toute lecture detotalOrders. C'est precisement l'omission de ce controle qui avait produit le faux verdict corrige en c.909.3. Ce que la correction revele en second
Debloquer le remplissage ne rend pas la strategie viable — il la rend ruineuse. 917 199 ordres en 4 ans, ~628 par jour de bourse, a 0,1 % de frais : ₮10 000 deviennent ₮13,50.
Le defaut reel du notebook n'est donc pas « la condition d'entree ne se declenche jamais » (elle se declenche constamment) mais un sur-trading massif :
set_holdingsest emis a chaque barre Minute eligible, sans controle de frequence. Grain distinct, trace danspath_forward, non traite ici — il change la strategie, pas le diagnostic.Cela explique aussi les volumes anterieurs : la position ne devenant jamais
invested, la garde d'entree reste ouverte etset_holdingsse re-emet a chaque barre. Le rapport 487 172 / 151 653 = 3,21 mesure la cardinalite des barres OR contre AND — il confirme la lecture au lieu de la contredire.4. Notebook (concerns 3 et 4 de #14142)
</cell>parasite retiree en fin de cellule 11._results/QC-Py-40-Phase-D.jsonau lieu d'etre recopiees a la main — c'etait la demande du concern 4.Validation
§D — execution reelle. Papermill end-to-end, 35/35 cellules, 5,88 s, 0 erreur. Sorties reelles fusionnees dans la source sous assertions (type et source identiques cellule par cellule avant copie de
execution_count+outputs), metadonneespapermillpurgees.Anti-regression — le diff affiche
231 insertions / 373 deletions, ce qui est un red flag et a ete investigue avant commit :"source"(uniquement des"execution_count") ;chars_source53 494 → 60 021,chars_outputs4 081 → 7 023 ;L'excedent de deletions est du re-serialisage JSON des charges base64, pas une perte de contenu.
§H — SOTA-OK. Le vrai moteur QC Cloud a ete compile et execute (MCP
qc-mcp-lite) ; les sorties commitees sont ses vraies sorties. Aucune sortie de cellule hand-editee.§C — non applicable, et c'est explicite : cette PR ne formule aucune claim de performance. Elle etablit un diagnostic. Le seul chiffre de perte rapporte sert a prouver que des fills ont eu lieu, pas a comparer des modeles — il n'y a donc ni multi-seed ni Diebold-Mariano a fournir.
Ce qui reste ouvert
n_bars_total,n_either,B1_DIAG) : ils sortent parLog(), que le MCP n'expose pas — lecture via l'interface QC Cloud (RECOVERABLE-USER-HAND). Ils ne sont plus necessaires au diagnostic ; ils confirmeraient la cardinalite des barres.See #14142— concerns 3 et 4 traites, concern 4 infirme plutot qu'implemente. Le reliquatLog()des concerns 1-2 reste ouvert.See #13117— cause-racine etablie ; le verdictINCONCLUSIVE_CONFIRMEDde Phase D est desormais explique.Tell
Une cause-racine marquee « probable, NON VERIFIEE » ne doit pas etre reportee dans une issue de suivi comme une tache a executer. #14142 demandait de corriger un bug qui n'existait pas. Le controle coutait un grep sur 6 lignes.
Addendum — commit 2 : les cellules code consecutives (
45b03d3e6)Le dispatch Phase E nommait « une cellule md entre
cells[7]etcells[8]corrige d'un coup les deux cellules code consecutives ». La premiere livraison l'avait laissee : mesure apres coup, le notebook portait deux runs,[7, 8]et[25, 26]. Les deux sont resorbes.LEAN_ALGORITHM/LEAN_ALGORITHM_B1etLEAN_ALGORITHM_B2— pourquoi une seconde version existe : B2 n'est pas une amelioration proposee mais un instrument de mesure, identique a B1 sauf un operateur (OUau lieu deET). C'est la methode du changement unique qui, poussee jusqu'au bout, a isole la vraie cause.Conformement a
consecutive-code-cells.md, c'est la resolution « markdown intermediaire » qui s'applique deux fois : dans les deux cas les cellules sont des etapes logiquement distinctes, pas des fragments couples justifiant une fusion.Modification uniquement markdown : diff = 26 insertions, 0 deletion, aucune ligne portant
execution_count/outputs/output_type, 2 cellulescell_type: markdownajoutees. L'exception C.2 s'applique — aucune re-execution requise. Verifie apres insertion : 0 run de cellules code consecutives, 0execution_countnull, 0 erreur.Ecart assume avec le dispatch
Le dispatch listait « les 6
liquidate-sans-guard » comme item a corriger. Cet item est infirme (section 1) : les 6 appels sont deja gardes. Je livre la refutation mesuree plutot que le correctif demande — c'est l'objet meme de cette PR, pas un contournement de scope.Defaut signale, non corrige ici
La numerotation des exercices est systematiquement desaccordee entre les cellules markdown et les commentaires de code, sur le meme sujet a chaque fois :
### Exercice 1 : Alerte de deviation backtest vs paper# Exercice 3 : Alerte deviation backtest vs paper### Exercice 2 : Rapport de slippage estimation# Exercice 1 : Estimation du slippage# Exercice : tester differentes configurationsLa numerotation markdown (1, 2) est coherente ; celle des commentaires de code ne l'est pas. Non corrige dans cette PR : c'est un sujet distinct du diagnostic Phase E, et toucher des commentaires de cellules code ferait sortir le commit du regime markdown-only ci-dessus. Signale ici pour etre traite separement (principe de collaboration 3 : signaler le code non lie plutot que l'emporter dans un commit sans rapport).