Skip to content

Fix(qc,#22496): le DM de research_turn_of_month porte des moments bruts et la condition de signe - #23557

Merged
jsboige merged 4 commits into
mainfrom
fix/turn-of-month-dm-sign
Oct 31, 2026
Merged

jsboige merged 4 commits into
mainfrom
fix/turn-of-month-dm-sign

Conversation

@jsboige

@jsboige jsboige commented Oct 30, 2026 •

Copy link
Copy Markdown
Owner

Grain: MED/qc — lane myia-po-2024:CoursIA-2 — prev: DEEP/qc #22579

Correction du test de Diebold-Mariano de research_turn_of_month.ipynb (carnet mergé par #22496), demandée par le coordinateur dans son DM du 30/10 15:50Z (point 1). Cette PR ne crée aucun carnet : elle corrige un carnet existant, et ferme #22700 (doublon de #22496).

Le défaut mesuré

La cellule 6 (dm_test) estimait la variance sur la série centrée tout en plaçant le numérateur sur l'écart moyen :

def _autocov(lag):
    dev = d - d.mean()          # dispersion instantanée
    return float((dev.iloc[lag:] * dev.iloc[:n - lag]).sum()) / n
gamma0 = _autocov(0)
gammas = sum(_autocov(lag) for lag in range(1, h))   # h = 1 -> liste vide
var = (gamma0 + 2 * gammas) / n                     # variance i.i.d.
stat = d.mean() / np.sqrt(var)

Deux conséquences, mesurées :

  1. L'estimateur est celui de la dispersion, pas celui de l'écart moyen. La variance de long-run d'un différentiel n'est pas sa variance centrée : c'est l'estimation HAC des moments bruts. Un h = 1 ne corrigeait rien — la boucle range(1, 1) est vide.
  2. La significativité était anti-conservatrice. Le différentiel est persistant : la stratégie est à plat les deux tiers du temps, elle perd donc à chaque journée de marché. La dispersion centrée d'un tel différentiel est mécaniquement plus faible que sa variance de long-run → le seuil p < 0.05 était atteint par construction.

Le test comparait bien le différentiel de rendement (et non des pertes quadratiques) — mais l'estimateur de variance en faisait un instrument qui ne pouvait pas ne pas sonner.

Le correctif

Portage de l'organe de la série (research_st_reversal.ipynb, dm_test_hac) : variance HAC Newey-West, noyau Bartlett (maxlag = 5) sur les moments bruts, numérateur dbar non re-centré, test toujours posé sur le différentiel de rendement (stratégie − B&H).

Ajout de la condition de signe : stat > 0 = la stratégie est significativement meilleure ; stat < 0 = elle est significativement pire. Une significativité négative n'est plus lue comme une victoire — l'ancien affichage annonçait SIGNIFICATIF (p<0.05) quel que soit le signe.

Mesures fraîches (re-exécution complète : 10 cellules code, 0 erreur, execution_count 1→10, données au 2026-10-29) :

actif stat p ancien p lecture
SPY −1.029 0.3036 0.012 non significatif
QQQ −1.197 0.2313 0.004 non significatif
EFA −0.643 0.5202 0.115 non significatif

Ce qui change dans la conclusion : le DM ne conclut plus rien sur l'échantillon complet. Le verdict NO BEATS tient — il repose désormais sur la rentabilité nette (Sharpe TOM négatif : −0.03 à −0.07, contre 0.17 à 0.43 pour le B&H) et sur l'instabilité out-of-sample (l'écart change de signe d'un fold à l'autre), et non plus sur une « défaite significative » que l'instrument ne savait pas produire.

Diagnostic dérive

Cause : (c) moteur upstream — l'instrument du test lui-même, pas les données ni l'environnement. Le carnet mergé par #22496 portait une valeur de p produite par un estimateur qui sous-estimait la variance de long-run d'un différentiel persistant : la valeur était fausse, et la prose alignée dessus l'était aussi.

Verdict : CAUSE_FIXED — la cause est dans la source (cellule 6), elle est corrigée, et le carnet est re-exécuté : aucune sortie de cellule n'a été éditée à la main (C.2). La prose (tableau récapitulatif, sensibilité k sur EFA, conclusion) est réalignée sur les sorties fraîches. La fenêtre de données a également glissé d'un jour de bourse (2026-10-29), ce qui explique les écarts de Sharpe minimes (QQQ −0.020 → −0.027).

Organe invoqué (règle organ-first)

question réponse
Quelle série possède la sémantique ? research/research_st_reversal.ipynb (dm_test_hac, même dossier)
Peut-on l'invoquer ? Non — l'organe vit dans un notebook, non importable ; ML-Training-Pipeline/scripts/dm_test.py attend des erreurs de prévision et reproduirait le défaut sur des rendements (loss_fn="mse") → copie déclarée
Que faudrait-il exporter ? Extraire dm_test_hac vers un module commun de la série — hors périmètre ici, signalé
Quel témoin négatif ? L'instrument défaillant franchit p < 0.05 par construction sur un différentiel persistant ; le corriger rend p = 0.30 / 0.23 / 0.52 — il peut ne pas sonner
Qui vérifie indépendamment ? La jambe Sharpe (rentabilité nette, sans le DM) et la stabilité out-of-sample ; le module canonique dm_test.py et sa suite

Portée

  • research_turn_of_month.ipynb : 4 cellules source modifiées (12 code, 13, 15, 19), les 19 autres identiques à main ; ré-exécution complète, sorties réécrites.
  • research_vrp_putwrite.ipynb : 1 ligne de Navigation (+1 / −1), markdown seul, aucune ré-exécution due (C.3).
  • Aucun carnet créé, aucune sortie de cellule éditée à la main.

Le lien de navigation et #22579

research_turn_of_month est un orphan_entry sur main (rien ne pointe vers lui). Comme cette PR touche le carnet, l'organe lui imputait le finding. La ligne de Navigation de research_vrp_putwrite porte donc le lien >> research_turn_of_month — vers un carnet qui existe déjà sur main, donc sans lien 404.

Le lien vers research_st_reversal n'est pas porté ici : il reste porté par research_risk_parity, sa sœur déclarée, conformément au geste du coordinateur (DM du 30/10 17:10Z) — il est livré par #22579. Chaque carnet de la chaîne garde ainsi un prédécesseur unique et réciproque : vrp_putwrite → turn_of_month d'un côté, risk_parity → st_reversal de l'autre.

research_month_of_year n'existe que sur #22579 : il reste hors de cette ligne. Si #22579 et cette PR se fusionnent, dans un ordre ou dans l'autre, la résolution du conflit sur cette ligne est une union (>> research_turn_of_month + >> research_month_of_year) — déterminée, sans ambiguïté.

Preuve : python scripts/notebook_tools/check_notebook_nav_chain.py --check --diff-base origin/main → rc=0 (OK: 0 NEW finding vs baseline), tête 4290d3f573.

Voir #22700, #22496.

🤖 Generated with Claude Code

jsboige and others added 2 commits October 30, 2026 17:27
…ts et la condition de signe

Le test de Diebold-Mariano de la cellule 6 estimait la variance sur la serie
**centree** (la dispersion instantanee du differentiel) tout en plaquant le
numérateur sur l'ecart **moyen**. Deux consequences mesurees :

- la variance estimee est celle des *fluctuations*, pas la variance de long-run
  du differentiel -- qui est persistant (la strategie est a plat les deux tiers
  du temps, elle perd donc a chaque journee de marche) ;
- la significativite obtenue est donc **anti-conservatrice** : l'ecart type du
  differentiel centre est mecaniquement plus petit que celui du marche.

Correction portee de l'organe de la serie (`research_st_reversal.ipynb`,
`dm_test_hac`) : variance HAC Newey-West a noyau Bartlett (maxlag 5) sur les
**moments bruts**, numerateur `dbar` non re-centre, teste sur le differentiel
de **rendement** (strategie - B&H). Le verdict exige en outre le **signe**
(`stat > 0` = la strategie est significativement meilleure) : une
significativite negative est une defaite significative, pas une victoire.

**Mesures fraichees** (re-execution complete : 10 cellules code, 0 erreur,
`execution_count` 1->10, donnees au 2026-10-29) :

| actif | DM stat | DM p | lecture |
|---|---|---|---|
| SPY | -1.029 | 0.3036 | non significatif |
| QQQ | -1.197 | 0.2313 | non significatif |
| EFA | -0.643 | 0.5202 | non significatif |

La previous lecture « significativement negative (SPY p=0.012, QQQ p=0.004) »
etait un artefact de l'estimateur : le DM **ne conclut rien** sur l'echantillon
complet. Le verdict NO BEATS reste, mais il repose desormais sur la
**rentabilite nette** (Sharpe TOM negatif contre B&H positif) et sur
l'instabilite out-of-sample -- pas sur une significativite que l'instrument ne
sait pas produire ici.

Prose realignee sur les sorties fraiches : tableau recapitulatif (cellule 15),
sensibilite k sur EFA (cellule 13), et premisse de l'Exercice 2 (cellule 19),
qui décrivait un test « à un seul lag » -- le test porte maintenant un noyau
Bartlett a 5 lags, l'exercice demande de choisir h par la regle de Newey-West.

Cellules source modifiees : 12 (code), 13, 15, 19 -- les 19 autres sont
identiques a main. Re-execution complete, aucune sortie de cellule editee a la
main (C.2).

Voir #22496, #22700.

Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
Sur `main`, `research_turn_of_month` et `research_st_reversal` sont deux
`orphan_entry` au sens de `check_notebook_nav_chain.py` : aucun carnet ne pointe
vers eux. `research_vrp_putwrite` est le dernier maillon de la chaine de la
serie -- sa ligne de Navigation le relie a `research_rl_tactical_overlay` en
amont, et rien en aval.

Cette PR touche `research_turn_of_month`, donc l'organe lui imputait le
finding : la ligne de Navigation de `research_vrp_putwrite` porte desormais les
deux liens sortants qui donnent enfin a ces deux carnets un lien entrant.

**Un seul lien change** (+1 / -1, markdown seul, aucune re-execution due) :
les deux cibles existent deja sur `main`, donc aucun lien 404 n'est introduit.
`research_month_of_year` -- qui n'existe que sur #22579 -- reste hors de cette
ligne : la resolution avec #22579 est une **union** des deux lignes (les trois
liens), quelle que soit l'ordre de merge.

Preuve : `check_notebook_nav_chain.py --check --diff-base origin/main` -> rc=0.

Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
@jsboige

jsboige commented Oct 30, 2026 •

Copy link
Copy Markdown
Owner Author

Preuves d'exécution et de chaîne de navigation, rejouées sur la tête 4290d3f573 de cette PR (le lien vers research_st_reversal y reste porté par research_risk_parity, conformément au geste du coordinateur — DM du 30/10 17:10Z — et n'est donc plus porté par research_vrp_putwrite).

1. Chaîne de navigation — python scripts/notebook_tools/check_notebook_nav_chain.py --check --diff-base origin/main, exit code capturé sans tube :

WARN: baseline sans provenance (ecrit avant #22121) -- le re-deriver sur main propre active le controle.
WARN: 1 NEW finding(s) hors diff (non imputables a cette PR, ne bloquent pas):
  [orphan_entry] MyIA.AI.Notebooks/GameTheory/GameTheory-06i-Ensembles-Limites-Poincare-Bendixson-Python.ipynb
INFO: 33 finding(s) resolus depuis le baseline (mettre le baseline a jour).
OK: 0 NEW finding vs baseline (256 connus, 1705 notebook(s) au graphe).

rc=0. Le WARN résiduel porte sur GameTheory-06i-Ensembles-Limites-Poincare-Bendixson-Python.ipynb — hors diff, hors périmètre, et signalé comme non bloquant par l'organe lui-même.

2. Exécution réelle (H.1 / H.3 / C.1) — python scripts/notebook_tools/validate_pr_notebooks.py origin/main --json → passed: true, 2 carnets, 16 cellules code, 0 échec :

{
  "notebooks": [
    {
      "path": "MyIA.AI.Notebooks/QuantConnect/research/research_turn_of_month.ipynb",
      "kernel": "python3",
      "total_code": 10,
      "passed": true,
      "errors": [],
      "forensic_verdict": "EXEC_PROVED"
    },
    {
      "path": "MyIA.AI.Notebooks/QuantConnect/research/research_vrp_putwrite.ipynb",
      "kernel": "python3",
      "total_code": 6,
      "passed": true,
      "errors": [],
      "forensic_verdict": "EXEC_PROVED"
    }
  ],
  "total_notebooks": 2,
  "total_code_cells": 16,
  "passed": true,
  "failed_count": 0
}

3. Prose et activité — check_prose_quantitative_claims.py --diff origin/main...HEAD → [OK] aucun compteur quantitatif en prose ; check_activity_leak.py --diff origin/main...HEAD → [OK] aucun passage d'activite dense ajoute.

4. Périmètre — research_turn_of_month.ipynb : 4 cellules source modifiées (12, 13, 15, 19), les 19 autres identiques à main, sorties réécrites par la ré-exécution (10 cellules code, execution_count 1→10, 0 erreur). research_vrp_putwrite.ipynb : +1 / −1 sur la ligne de Navigation, markdown seul (le lien >> research_turn_of_month), aucune ré-exécution due (C.3). Aucune sortie de cellule éditée à la main.

@github-actions

Copy link
Copy Markdown
Contributor

⚠️ Prose/output review needed in the notebooks this PR changed: a numeric value is not anchored, an explicit relation is contradicted, or its evidence is missing. These cases remain distinct in the JSON report; the signal is advisory, NOT a merge gate.

Scope = notebooks CHANGED in this PR, not the whole corpus. Explicit claim-check relations resolve only against named CLAIM_METRICS from the local output window and are classified SUPPORTED, CONTRADICTED, or UNPROVEN.
The markdown-claims-output-report run artifact contains the structured JSON report. See python scripts/check_markdown_claims_output.py --help for re-running locally.
Detector rationale: c.290 / c.331 / PR #11435 numeric pathology, extended with low-noise relational evidence.

@github-actions

Copy link
Copy Markdown
Contributor

⚠️ Stale-claim review needed: a markdown cell claims a measurement value that appears in NO committed output of the notebook. Advisory, NOT a merge gate — triage against the JSON artifact.

Scope = notebooks CHANGED in this PR, not the whole corpus. The stale-claim-report run artifact holds the structured JSON.
Rationale: the sibling detector above only compares a claim to the outputs of the cells that PRECEDE it; a claim written in a cell that precedes its code (App-5-Timetabling c.2/c.4) is invisible to it, and a value imported from a twin notebook is never produced locally. See python scripts/check_stale_claims.py --help.

@github-actions

Copy link
Copy Markdown
Contributor

✅ No factual mislabel detected in the notebooks this PR changed (entity counts and tuple formulas checked against nearby committed streams).

Scope = notebooks CHANGED in this PR, not the whole corpus. The factual-mislabel-report run artifact holds the structured JSON.
Rationale: pure ABSENCE of a claimed value is the sibling stale-claim detector's job; this one only reports CONTRADICTIONS between an adjacent code cell's stream and the markdown that describes it. See python scripts/check_factual_mislabel.py --help.

@github-actions

Copy link
Copy Markdown
Contributor

No organ-duplication: no added def/class collides with another series organ API (scripts/audit/organ_api_index.yaml).

Detector: python scripts/audit/detect_organ_duplication.py --base <merge-base> --body-file <pr body>
Rationale: #16776 / #13564 (rule merged in #16778).

@github-actions

Copy link
Copy Markdown
Contributor

Notebook outputs-required (H.4 schema): PASS (every code cell carries an outputs: list)

@github-actions

Copy link
Copy Markdown
Contributor

Notebook PR Validation: PASS

  • Notebooks checked: 2
  • Code cells validated: 16
  • Result: All passed

Checks: H.1 (no errors), H.3 (execution_count), C.1 (no banned patterns)
Non-Python kernels (.NET/Lean): C.1 + errors only (execution_count advisory)
QuantConnect notebooks: C.1 + errors only (require QC Cloud for execution)

Le lien sortant vers `research_st_reversal` reste porte par
`research_risk_parity` -- sa soeur declaree -- conformement au geste du
coordinateur (DM du 30/10 17:10Z). La ligne de Navigation de
`research_vrp_putwrite` ne porte donc plus que le lien vers
`research_turn_of_month` (+1 / -1, markdown seul).

Chaque carnet de la chaine garde ainsi un predecesseur unique et reciproque :
`vrp_putwrite -> turn_of_month` d'un cote, `risk_parity -> st_reversal` de
l'autre.

Organe rejoue sur cette tete : `check_notebook_nav_chain.py --check --diff-base
origin/main` -> **rc=0** (`OK: 0 NEW finding vs baseline`).

Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
@github-actions

github-actions Bot commented Oct 30, 2026 •

Copy link
Copy Markdown
Contributor

Golden-Set Execution (H.7 P3)

✅ 9/9 notebooks passed (certified reproducible)

Notebook Status Time
2.1-Workflow-ML.ipynb ✅ SUCCESS 3.2s
2.2-Descente-de-gradient.ipynb ✅ SUCCESS 2.9s
2.3-Regression-lineaire-logistique.ipynb ✅ SUCCESS 3.2s
2.4-Arbres-Forets-Ensembles.ipynb ✅ SUCCESS 3.0s
Search-01-StateSpace.ipynb ✅ SUCCESS 2.4s
SL-1-LogicalLearning.ipynb ✅ SUCCESS 1.6s
RL-04-Bandits-Manchots-Python.ipynb ✅ SUCCESS 14.7s
GameTheory-04c-NashExistence-Python.ipynb ✅ SUCCESS 2.1s
GameTheory-13d-Optimistic-CFR-Python.ipynb ✅ SUCCESS 8.6s

Pinned lockfile: scripts/notebook_tools/golden_set.lock.txt (H.7 P3, axe A #4208)

…search_vrp_putwrite

Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
@jsboige

jsboige commented Oct 30, 2026

Copy link
Copy Markdown
Owner Author

Conflit avec main levé (lane myia-po-2024:CoursIA-2).

Merge de origin/main — un seul conflit, additif (ligne de navigation dans research_vrp_putwrite.ipynb). Résolution par union : la version main est un sur-ensemble (ajoute le lien [research_month_of_year >>], le dé-orphelinage déjà sur main) ; c'est elle qui est conservée. Le reste du fichier s'est fusionné proprement (les modifs de la PR préservées).

Re-contrôle local check_notebook_nav_chain.py --check --diff-base origin/main → rc 0, 0 NEW finding à la tête ce9e04b3da. PR revenue BLOCKED (attente re-agrégation du PR gate sur la tête fraîche — pool runners saturé).

@github-actions

Copy link
Copy Markdown
Contributor

Path-collision (organ #13359/#13615)

Cette PR #23557 (Fix(qc,#22496): le DM de research_turn_of_month porte des moments bruts et la condition de signe) touche au moins un chemin de fichier aussi modifie par d'autres PRs ouvertes. Risque de double-livraison (meme fichier livre deux fois, 2x le travail et 2x les runs CI). Advisory : parfois legitime (tranches coordonnees, partition paths: explicite, PRs empilees exclues) -- l'organe rend visible, il ne bloque pas.

Le verdict terminal (#15578) signale qu'un cote de la paire est deja sur main. L'organe mesure un recouvrement de chemins ; il ne compare pas le contenu des deux livraisons, donc il ne conclut PAS a une redondance (#15768) : deux PRs peuvent toucher le meme fichier pour des raisons disjointes. L'arbitrage reste a la lane ou au coordinateur.

@jsboige
jsboige merged commit cb75e7f into main Oct 31, 2026
90 of 91 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant