Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 4 additions & 5 deletions MyIA.AI.Notebooks/QuantConnect/BOOK_MAPPING.md
Original file line number Diff line number Diff line change
Expand Up @@ -114,8 +114,8 @@ Algorithmes LEAN complets sur données de marché. Les exemples 04, 08, 18 et 19
| 17 | Head Shoulders Pattern Matching with CNN | [ML-HeadShoulders-CNN](projects/ML-HeadShoulders-CNN/) | COVERED | Vivant | |
| 18/01 | Amazon Chronos Model — Base Model | [ML-Chronos-Foundation](projects/ML-Chronos-Foundation/), [Chronos-Foundation-Forecasting](projects/Chronos-Foundation-Forecasting/) | COVERED | Needs-improvement (les deux, tranche 14) | |
| 18/02 | Amazon Chronos Model — Fine-Tuned Model | — | GAP | — | aucun ré-entraînement de Chronos dans le dépôt |
| 19/01 | FinBERT Model — Base Model | [ML-FinBERT-Sentiment](projects/ML-FinBERT-Sentiment/), [QC-Py-Cloud-01-FinBERT-Sentiment](Python/QC-Py-Cloud-01-FinBERT-Sentiment.ipynb) | COVERED | Needs-improvement (tranche 12) | le portage ne produit pas de transaction sur QC Cloud : [#18903](https://github.com/jsboige/CoursIA/issues/18903) |
| 19/02 | FinBERT Model — Fine-Tuned Model | — | GAP | — | aucun ré-entraînement de FinBERT dans le dépôt |
| 19/01 | FinBERT Model — Base Model | [ML-FinBERT-Sentiment](projects/ML-FinBERT-Sentiment/), [QC-Py-Cloud-01-FinBERT-Sentiment](Python/QC-Py-Cloud-01-FinBERT-Sentiment.ipynb) | COVERED | Needs-improvement (tranche 12) | le portage produit désormais ses rebalancements sur QC Cloud (v7, 2022-01/02, deux ordres, Sharpe -1,137) et son verdict `INCONCLUSIVE` est écrit (critère 4, §Métriques du README) ; [#18903](https://github.com/jsboige/CoursIA/issues/18903) se ferme au merge |
| 19/02 | FinBERT Model — Fine-Tuned Model | [ML-FinBERT-Sentiment](projects/ML-FinBERT-Sentiment/) (`main_finetuned.py`, `finetune/`) | COVERED | Non déployé (le ré-entraînement s'exécute hors QC) | ré-entraînement porté et mesuré hors échantillon sur quatre graines ; verdict **NO BEATS** (écart moyen -0,1054, -2,92 sigma, aucune graine gagnante) |

---

Expand Down Expand Up @@ -227,7 +227,7 @@ Pour 14 et 15, le texte ne donne pas de Sharpe, mais le balayage de paramètres
| 18/01 | aucun chiffre dans le texte ; courbes de prévision et table de rendements mensuels | 2019-01-01 → 2024-04-01, capital 100 k, 5 actions les plus liquides, Chronos pré-entraîné | Sharpe 0,277, CAGR 7,23 %, pire baisse 13,5 % | [ML-Chronos-Foundation](projects/ML-Chronos-Foundation/), 2015-01-01 → 2026-01-01 | `NON COMPARABLE` |
| 18/02 | — | Chronos ré-entraîné | — | aucune reproduction (`GAP`) | sans objet |
| 19/01 | idem 18/01 | 2022-01-01 → 2023-01-01, capital 100 k, 10 actions les plus liquides, FinBERT pré-entraîné | Sharpe 0,584, CAGR 22,10 %, pire baisse 43,0 % | [ML-FinBERT-Sentiment](projects/ML-FinBERT-Sentiment/), 2015-01-01 → 2026-01-01 | `NON COMPARABLE` |
| 19/02 | — | FinBERT ré-entraîné | — | aucune reproduction (`GAP`) | sans objet |
| 19/02 | — | FinBERT ré-entraîné | exactitude hors échantillon 0,3461 contre 0,4515 pour le modèle de base ; rendement cumulé de la stratégie −0,046 contre −0,037 | [ML-FinBERT-Sentiment](projects/ML-FinBERT-Sentiment/) (`finetune/`), hors échantillon 2019-07 → 2019-12, quatre graines | `NON COMPARABLE` : le livre ne publie aucun chiffre pour cet exemple, et le ré-entraînement n'est pas déployé sur QC |

**Rejeux** (projets QuantConnect séparés, une exécution chacun) :

Expand Down Expand Up @@ -300,10 +300,9 @@ Ces projets n'ont pas d'exemple correspondant dans le livre, mais illustrent des

| Exemple | Suite |
|---------|-------|
| 04/05, 04/18, 05/02, 05/15 | [#18957](https://github.com/jsboige/CoursIA/issues/18957) : écart interquartile, élimination récursive des variables, régression polynomiale, OPTICS (scripts courts sur données synthétiques, à porter dans QC-Py-18 à 20) |
| 06/02 | [#18958](https://github.com/jsboige/CoursIA/issues/18958) : régimes par prétraitement de facteurs |
| 06/08/01, 06/08/03 | [#18960](https://github.com/jsboige/CoursIA/issues/18960) : stop fixe de référence et couverture par put, dans [Stoploss-Volatility-ML](projects/Stoploss-Volatility-ML/) |
| 06/18/02, 06/19/02 | [#18962](https://github.com/jsboige/CoursIA/issues/18962) : ré-entraînement de Chronos et de FinBERT (calcul GPU) |
| 06/18/02 | [#18962](https://github.com/jsboige/CoursIA/issues/18962) : ré-entraînement de Chronos (calcul GPU) |
| 07/01 | [#18902](https://github.com/jsboige/CoursIA/issues/18902) |
| 08/01 | exclusion : le livre appelle l'API payante PredictNow.ai ; le dépôt garde une optimisation sans service externe |
| 08/02 | [#18901](https://github.com/jsboige/CoursIA/issues/18901) (méta-étiquetage sans l'API PredictNow.ai) |
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,43 @@ Portage fidèle du modèle de base de l'exemple 19 du livre (*Hands-On AI Tradin

**Le modèle tourne dans l'algorithme sur QC Cloud** (variante PyTorch, `local_files_only=True` comme le livre) — mesuré par les sondes `probe2-bitmask-finbert` et `probe3-tiingo-decode` du 2026-10-05 : `torch`, `transformers` et même `tensorflow` importent sur les nœuds, et l'inférence y rend ses probabilités.

## Modèle ré-entraîné (exemple 19/02)

L'exemple 19/02 du livre va plus loin : **ré-entraîner** FinBERT, à chaque rebalancement mensuel, sur les articles des 30 derniers jours étiquetés par la réaction du cours, avant de scorer le mois. Deux artefacts le portent :

- `main_finetuned.py` — portage fidèle du code du livre en PyTorch (écarts n1–n8 documentés en tête de fichier). Il **n'est pas exécutable en CI ni sur un nœud QC ordinaire** : le ré-entraînement tourne dans l'algorithme et demande un GPU.
- `finetune/run_finetune_finbert.py` — harnais **hors ligne** qui sort le ré-entraînement de l'algorithme pour l'exécuter sur un GPU local, et **compare modèle de base et modèle ré-entraîné hors échantillon**.

Le livre n'a **aucune séparation entraînement / hors échantillon** — il ré-entraîne et prédit sur les mêmes 100 échantillons du mois. Le harnais ajoute la séparation que l'issue demande : les six derniers mois sont retirés de l'entraînement.

### Corpus et étiquetage — deux écarts mesurés, pas supposés

**Source des articles.** Le livre lit Tiingo News sur QC Cloud ; hors ligne, le harnais lit **FNSPID** (`Stock_news/All_external.csv`, corpus public horodaté de même forme : date + ticker + titre). Le corpus est **CC BY-NC 4.0** et reste un **cache local hors dépôt** : seules les mesures sont committées. Sa couverture temporelle a été **mesurée** (échantillonnage de plages d'octets dans le fichier) avant de calibrer la fenêtre : ≈ 2004–2020, d'où le choix de 2015–2019.

**Fenêtre de réaction.** Le livre mesure la réaction du cours **entre deux parutions consécutives**, sur des clôtures à la seconde (`Resolution.SECOND`). À résolution quotidienne, cette règle s'effondre : deux parutions de la même séance n'ont aucune réaction résolvable et rendent un label exactement nul. Mesuré sur le corpus du harnais : **410 des 680 paires consécutives (60 %) tombent dans la même séance**, donc **429 labels (63 %) sont nuls exactement** — ce qui fabrique une classe neutre majoritaire à 75 %, que le livre ne connaît pas. Le harnais mesure donc la réaction **à la parution** (rendement de la première séance dont la clôture suit l'instant de parution). Sur le même corpus, la distribution des classes passe de **12 / 75 / 13** à **32 / 29 / 38**, contre les **37,5 / 25 / 37,5** que la règle du livre vise. L'intention du livre est préservée ; sa mécanique de mesure est adaptée à la résolution disponible, et l'écart est écrit en tête du harnais.

**Sélecteur d'actif et couverture du corpus.** Le livre retient le plus volatil des dix valeurs les plus liquides, sur un marché où la couverture d'actualité est **universelle** (Tiingo News sur QC Cloud). Hors ligne, la couverture de FNSPID est **très inégale** : le sélecteur du livre y désigne régulièrement un titre sans actualité observable. Mesure sur le corpus complet (13,06 M d'enregistrements parcourus, 22 496 articles retenus, 18 tickers) : la distribution va de HD 2 587 articles à **AMD 14**, et la tête de classement du livre *est* AMD — ce qui écartait **37 des 60 mois pour une raison de couverture, pas de stratégie**. Le harnais descend donc d'un rang quand le candidat n'est pas observable. Quand le premier choix du livre **est** couvert, il reste retenu à l'identique ; la même mesure passe alors à **1 948 échantillons sur 59 des 60 mois**.

### Résultat — 4 graines, et un verdict qui va contre le livre

Le harnais entraîne 1 680 échantillons sur 53 mois et évalue sur les **6 derniers mois tenus à l'écart** (268 échantillons, juillet-décembre 2019), quatre graines. Le modèle de base est le même pour toutes : exactitude hors échantillon **0,4515**.

| Graine | Exactitude ré-entraînée | Écart au modèle de base | McNemar p | Classes prédites [nég, neu, pos] |
| --- | --- | --- | --- | --- |
| 1 | 0,2948 | **−0,1567** | 0,0001 | [109, 14, 145] |
| 2 | 0,3694 | **−0,0821** | 0,0507 | [20, 6, 242] |
| 3 | 0,3731 | **−0,0784** | 0,0640 | [36, 0, 232] |
| 42 | 0,3470 | **−0,1045** | 0,0175 | [203, 4, 61] |
| *vérité* | — | — | — | [90, 75, 103] |

Écart moyen **−0,1054**, écart-type inter-graines 0,0361, soit **−2,92 σ**, et **aucune graine ne bat le modèle de base**. Verdict : **NO BEATS** — au sens strict, ce n'est pas « amélioration non démontrée » mais **dégradation démontrée**.

**Ce que la distribution des classes prédites ajoute à l'exactitude.** Le modèle de base prédit [70, 105, 93] : il sur-prédit le neutre, biais connu de FinBERT sur du texte court. Le modèle ré-entraîné prédit **0 à 14 neutres sur 268**, quelle que soit la graine — la classe neutre **disparaît**. Le signe du déséquilibre dépend en revanche de la graine (242 positifs pour la graine 2, 203 négatifs pour la graine 42) : ce n'est donc pas un effondrement sur une classe fixe, mais la perte du neutre **et** une instabilité de signe d'une graine à l'autre. Deux époques à 3e-5 sur 1 680 étiquettes bruitées suffisent à détruire la frontière du neutre d'un modèle pré-entraîné.

**Ce que ce verdict ne dit pas.** Il ne condamne pas la recette du livre, il condamne sa mesure **hors échantillon** dans ces conditions : le livre ré-entraîne et prédit sur les mêmes 100 échantillons du mois, donc sa mesure ne *peut pas* voir cette perte. C'est exactement l'écart que l'issue demande de mesurer.

**Portée.** Un univers de 24 grandes capitalisations, une fenêtre (2015-2019), un modèle de base, quatre graines. Le résultat est falsifiable via `finetune/measures/` (un fichier par graine plus l'agrégat) et reproductible par `finetune/run_finetune_finbert.py`.

## Historique du « 0 trade » (fermé le 2026-10-05)

Les portages v1/v2 appelaient `add_data(TiingoNews, "AAPL")` avec un **ticker en chaîne**. TiingoNews exige un `Symbol` d'action déjà mappé — l'appel lève `The custom data type TiingoNews requires mapping, but the provided ticker is not in the cache`, exception avalée par le `try/except` → zéro article → zéro trade. Le livre passe `security.symbol` (issu de l'univers) ; le portage fait de même désormais. Le diagnostic ancien « TF unavailable on QC Cloud » était faux sur les deux comptes.
Expand Down Expand Up @@ -53,14 +90,21 @@ Le livrable **complète et produit ses ordres** : 2 ordres = les 2 rebalancement

**Sur la sonde `probe9`** : elle porte 8191 ordres-marqueurs de plus (un `market_order` d'une action SPY par bit de son masque de diagnostic) et rend pourtant des statistiques **identiques à l'unité près** à celles de v7. Les marqueurs sont donc **neutres sur la mesure** — 8191 ordres d'écart ne déplacent ni le Sharpe, ni le rendement, ni le drawdown. Les deux exécutions se corroborent.

**Deux réserves, mesurées.** (1) La comparaison au livre est **à fenêtre inégale** : le livre couvre 2022 entier, le livrable le premier sixième (mur natif, cf. §Historique) — aucun jugement de performance n'est tiré de l'écart. (2) Ces chiffres **ne sont pas un verdict de stratégie** : deux mois, une seule configuration, aucune répétition multi-seed — le résultat est celui du portage fidèle, pas une évaluation du modèle.
**Verdict du modèle de base : `INCONCLUSIVE`.** Sur 40 séances et **2 ordres** — les deux rebalancements mensuels, exactement le rythme du livre — l'échantillon est trop court pour conclure quoi que ce soit sur la stratégie. Le mot est exigé par le critère 4 de #18903, et le seul défendable ici est celui-ci : ni `BEATS` ni `NO BEATS`, parce que ce backtest n'est pas une mesure de performance — c'est la vérification qu'un portage fidèle **complète et trade**. Le verdict de l'autre bras, le ré-entraînement FinBERT évalué hors échantillon sur quatre graines, est **`NO BEATS`** et vit dans la section « Résultat » ci-dessus.

**Deux bornes de mesure, qui encadrent ce verdict.** (1) La comparaison au livre est **à fenêtre inégale** : le livre couvre 2022 entier, le livrable le premier sixième (mur natif, cf. §Historique) — aucun jugement de performance n'est tiré de l'écart. (2) Une seule configuration, déterministe, sans répétition multi-seed : le résultat est celui du portage fidèle, pas une évaluation du modèle.

## Fichiers

- `main.py` — Stratégie : portage PyTorch du `FinbertBaseModelAlgorithm` du livre
- `main.py` — Stratégie : portage PyTorch du `FinbertBaseModelAlgorithm` du livre (exemple 19/01)
- `main_finetuned.py` — Stratégie : portage du `FinbertFineTunedModelAlgorithm` du livre (exemple 19/02), ré-entraînement dans l'algorithme
- `finetune/run_finetune_finbert.py` — Harnais hors ligne : corpus, étiquetage, ré-entraînement GPU, évaluation hors échantillon base contre ré-entraîné
- `finetune/measures/` — Mesures committées (une par graine + agrégat)
- `research.ipynb` — Évaluation du modèle de sentiment

## Références

- *Hands-On AI Trading*, Section 06, Exemple 19 (01 Base Model)
- *Hands-On AI Trading*, Section 06, Exemple 19 (01 Base Model, 02 Fine-Tuned Model)
- Repo du livre : `QuantConnect/HandsOnAITradingBook`, `06 Applied Machine Learning/19 FinBERT Model`
- `ProsusAI/finbert` — modèle de base (Hugging Face)
- FNSPID — corpus d'articles financiers horodatés, CC BY-NC 4.0 (cache local hors dépôt ; cf. `.claude/rules/bibliography-hygiene.md`)
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
{
"seed": 1,
"accuracy_base": 0.45149253731343286,
"accuracy_finetuned": 0.2947761194029851,
"delta": -0.15671641791044777,
"mcnemar": {
"b": 79,
"c": 37,
"n": 116,
"p_value": 0.00012034422931947828
},
"pred_base": [
70,
105,
93
],
"pred_finetuned": [
109,
14,
145
],
"vrai": [
90,
75,
103
],
"par_mois": {
"2019-07": {
"n": 63,
"symbole": "MU",
"exactitude_base": 0.42857142857142855,
"exactitude_finetuned": 0.38095238095238093,
"retour_base": -0.05415104548060723,
"retour_finetuned": -0.05415104548060723
},
"2019-08": {
"n": 38,
"symbole": "MU",
"exactitude_base": 0.3684210526315789,
"exactitude_finetuned": 0.2894736842105263,
"retour_base": 0.013431673822974943,
"retour_finetuned": 0.013431673822974943
},
"2019-09": {
"n": 25,
"symbole": "MU",
"exactitude_base": 0.64,
"exactitude_finetuned": 0.28,
"retour_base": 0.0018912427231083662,
"retour_finetuned": -0.007564970892433465
},
"2019-10": {
"n": 92,
"symbole": "MU",
"exactitude_base": 0.4673913043478261,
"exactitude_finetuned": 0.25,
"retour_base": -0.004780543208655841,
"retour_finetuned": -0.004780543208655841
},
"2019-11": {
"n": 26,
"symbole": "MU",
"exactitude_base": 0.4230769230769231,
"exactitude_finetuned": 0.2692307692307692,
"retour_base": 0.006251293375404976,
"retour_finetuned": 0.006251293375404976
},
"2019-12": {
"n": 24,
"symbole": "MU",
"exactitude_base": 0.4166666666666667,
"exactitude_finetuned": 0.2916666666666667,
"retour_base": NaN,
"retour_finetuned": NaN
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
{
"seed": 2,
"accuracy_base": 0.45149253731343286,
"accuracy_finetuned": 0.3694029850746269,
"delta": -0.08208955223880599,
"mcnemar": {
"b": 69,
"c": 47,
"n": 116,
"p_value": 0.050729556682883294
},
"pred_base": [
70,
105,
93
],
"pred_finetuned": [
20,
6,
242
],
"vrai": [
90,
75,
103
],
"par_mois": {
"2019-07": {
"n": 63,
"symbole": "MU",
"exactitude_base": 0.42857142857142855,
"exactitude_finetuned": 0.42857142857142855,
"retour_base": -0.05415104548060723,
"retour_finetuned": -0.05415104548060723
},
"2019-08": {
"n": 38,
"symbole": "MU",
"exactitude_base": 0.3684210526315789,
"exactitude_finetuned": 0.39473684210526316,
"retour_base": 0.013431673822974943,
"retour_finetuned": 0.013431673822974943
},
"2019-09": {
"n": 25,
"symbole": "MU",
"exactitude_base": 0.64,
"exactitude_finetuned": 0.4,
"retour_base": 0.0018912427231083662,
"retour_finetuned": -0.007564970892433465
},
"2019-10": {
"n": 92,
"symbole": "MU",
"exactitude_base": 0.4673913043478261,
"exactitude_finetuned": 0.30434782608695654,
"retour_base": -0.004780543208655841,
"retour_finetuned": -0.004780543208655841
},
"2019-11": {
"n": 26,
"symbole": "MU",
"exactitude_base": 0.4230769230769231,
"exactitude_finetuned": 0.34615384615384615,
"retour_base": 0.006251293375404976,
"retour_finetuned": -0.025005173501619904
},
"2019-12": {
"n": 24,
"symbole": "MU",
"exactitude_base": 0.4166666666666667,
"exactitude_finetuned": 0.4166666666666667,
"retour_base": NaN,
"retour_finetuned": NaN
}
}
}
Loading
Loading