# PROTOCOLE FORWARD — StrateForge (unique source de vérité)

Version 2.0 — 2026-10-06 (palier 0 de la tâche 38, cahier d'audit du
05/10/2026). Document normatif. Toute mesure publiée de StrateForge doit
pouvoir être reconstruite depuis ce protocole et le journal public, sans
fichier interne.

**Historique des versions.** La version 1.0 (2026-10-03) est archivée
intégralement à `PROTOCOLE_FORWARD_v1.md` et reste la référence des
observations des périodes qu'elle couvre — rien n'est effacé. La version
courante est 2.0 ; chaque observation conserve **son** protocole d'origine
via `protocol_id` en colonne du journal. Un document « courant » ne réécrit
jamais le passé.

## 0. Contrat de données commun (obligatoire en amont)

Tous les producteurs et consommateurs (modèles, mesures, PDF, CSV, site,
alertes) partagent le contrat versionné `sf-contrat/1.0`
(`cron/contrat_donnees.py`, cahier annexe 1) : une seule définition des
frais, une seule définition mathématique du label, un seul schéma
d'observation, un seul schéma de suivi de livraison. Les identifiants de
version ne sont pas des dates de génération.

## 1. Principe

StrateForge mesure un classement quotidien d'actifs crypto et publie la
performance de ce classement sous des règles figées. Mesure statistique,
jamais une recommandation. Le protocole a deux vertus : la preuve
(antériorité vérifiable) et la fraîcheur (le jour J complet est payant).

## 2. Les trois protocoles de mesure (jamais implicites)

| protocol_id | Statut | Ce qu'il mesure | Référence |
|---|---|---|---|
| `A` | historique | panier public top 10 long + top 3 short, mesure clôture à clôture — période 23/09 → 02/10/2026 | PROTOCOLE_FORWARD_v1.md |
| `B` | prospectif | pistes engagées : une ligne B n'existe QUE si le classement complet du jour a été engagé (hash vérifié) avant le début de la fenêtre mesurée ; gel vérifié contre le registre public | PROTOCOLE_FORWARD_v2.md |
| `6h` | contextuel | top 10 du classement_6h du jour ; jamais fusionné au daily, jamais affiché seul | PROTOCOLE_FORWARD_v2.md |

Le `protocol_id` figure en colonne de chaque ligne de chaque journal et dans
le manifeste de chaque bundle. Une archive couvrant plusieurs périodes
décrit ce périmètre au lieu d'exiger artificiellement une date unique.

## 3. Timeframe, cible et horizon — trois notions distinctes (fiche 18)

- **tf_data** : granularité des features du modèle (`1d` ou `6h`).
- **target_id / définition de la cible** : l'événement que le modèle prédit,
  versionné (fiche 19 et §6 ci-dessous). La vérification future de la
  calibration prospective portera exactement sur cette cible.
- **horizon de mesure** : la fenêtre du rendement journalisé par le forward
  test. Pour le daily : 24 h clôture J-1 → clôture J (UTC). Pour le 6h :
  **données 6h, rendement mesuré sur 24 h** (clôture de la barre 6h
  d'entrée → clôture du jour J+1) — le titre « 6h » désigne la granularité
  des données, jamais 6 h de rendement.

Le panier de rendements (horizon de mesure) et la vérification de
l'événement prédit (target_id) sont deux analyses distinctes : une
observation dont l'horizon diffère de la cible n'entre pas dans les
statistiques de calibration de cette cible.

## 4. Grille de lecture des dates

- Le scoring tourne chaque jour à 08h00 UTC. Au moment du run, la dernière
  bougie daily clôturée est celle du **jour J-1** (clôture à 00h00 UTC).
- « Classement PUBLIC J-1 » = le classement produit le jour J à 08h00 UTC,
  calculé sur les données clôturées jusqu'à J-1. Le fichier public sert la
  veille en intégralité et le jour J en accès réduit (top 10 long / top 3
  short), dès sa première publication.
- **Entrée** = clôture de la bougie daily du jour J-1 (dernière clôturée au
  moment du scoring).
- **Sortie** = clôture de la bougie daily du jour J, connue au scoring du
  jour J+1. Le rendement mesuré couvre donc ~24 h clôture-à-clôture.
- **data_cutoff_at** = 00h00 UTC fin J-1 (dernière donnée admissible) ;
  **data_available_at** = l'instant où la ligne devient publiable (sortie
  clôturée). Les deux sont distincts de `computed_at` (calcul) et de
  `committed_at` (écriture) — aucun ne prouve une publication.

## 5. Panier mesuré et frais canoniques (fiche 04)

- **Top 10 LONG + top 3 SHORT du classement PUBLIC J-1** (rangs 1-10 et 1-3,
  par score du jour — le rang est enregistré dans le journal au moment du
  scoring, personne ne peut le réécrire après coup).
- Portefeuille **équipondéré**, capital fictif de départ **10 000 $**.
- **Frais — contrat canonique unique** (`cst-ar-030-v1`) : taux
  **aller-retour de 0,30 %** (0,0030), proportionnel au notionnel d'entrée,
  déduit **une seule fois** : `rendement_net = rendement_brut − 0,0030`,
  long comme short. Le coût non négatif, l'identité brut − coût = net.
  Hypothèse maker ×1 documentée — approximation, pas le montant réel
  facturé par tous les exchanges.
  - *Période initiale — BRUTE* : classements du **23/09/2026 au
    02/10/2026** (`cst-aucun-v0`). Aucun frais déduit, ni à l'époque ni
    depuis. Ces lignes publiées brutes ne seront jamais réécrites : toute
    correction d'une donnée déjà publiée est un **erratum versionné**.
  - *Période courante — NETTE* : classements **à partir du 03/10/2026**.
  - *Correction fiche 04 (05/10/2026)* : la piste B doublait le coût
    (`2 × 0,30 %` = 0,60 %). Corrigée par le contrat canonique ; les lignes
    B déjà publiées avec `couts=0` ne sont pas rétroactivement modifiées
    (règle du cahier).
  - *6h (contextuel)* : frais déduits depuis sa création (29/09/2026).
  - **Comparabilité** : « Les résultats des deux périodes ne sont pas
    directement comparables : la période initiale est brute, la période
    courante est nette de frais. » Le graphique public présente les deux
    séries séparées avec la rupture datée ; elles ne sont jamais fusionnées
    en un seul cumulé.
- Long : rendement_brut = sortie/entrée − 1. Short : rendement_brut =
  1 − sortie/entrée.
- gagnant = 1 si rendement_net > 0, sinon 0.
- Performance journalière du panier = moyenne des rendements nets des 13
  lignes du jour. Cumul = produit des (1 + perf journalière) sur le capital
  fictif.
- Le 6h est un cadre **séparé, contextuel uniquement** : top 10 du
  classement_6h du jour, équipondéré, clôture → clôture +24 h, mêmes frais.
  Il n'est jamais additionné au daily et n'est jamais affiché seul.

## 6. Définition mathématique unique du label (fiche 19)

Source vérifiée : `claw_retrain.py` (build_panel, LABEL_K = 0.75).

> **target_id `tgt-rfwd-075atrpct` (v1)** — pour la barre t de clôture C_t :
> **y = 1[ (C_sortie / C_t − 1) > 0,75 × ATR%14_t ]**, comparaison stricte,
> prix de départ = clôture, unité = rendement proportionnel.
> **ATR%14_t = SMA(TR, 14)_t / C_t** — moyenne mobile simple du True Range
> sur 14 périodes divisée par la clôture (pas un ATR de Wilder).
> - Daily : C_sortie = clôture de la bougie daily suivante (24 h).
> - 6h : C_sortie = clôture de la 4e barre 6h suivante (24 h), seuil =
>   0,75 × moyenne d'ATR%14 sur les 4 barres courantes et précédentes.

Toute modification de label crée une nouvelle expérience : nouveau
target_id, nouveau modèle, nouvelle validation — jamais de mélange avec les
résultats antérieurs. Le score publié est la probabilité calibrée de cet
événement exact, à distinguer de l'indice composite 0–100 des indicateurs
Pine (percentiles) et du rendement mesuré par le forward test.

## 7. Journal (schéma canonique v2, 23 colonnes)

`run_id, measured_at, available_at, date, horizon, symbole, sens, score,
rang, prix_entree, date_entree, prix_sortie, date_sortie, rendement_brut,
couts, rendement_net, gagnant, model_version, univers, reconstruction,
protocol_id, cost_model_id, target_id`

- `protocol_id` ∈ {`A`, `B`, `6h`} — jamais implicite (fiche 16).
- `cost_model_id` ∈ {`cst-aucun-v0`, `cst-ar-030-v1`} — régime applicable à
  la date, sans réécriture rétroactive (fiche 04).
- `target_id` = `tgt-rfwd-075atrpct` — cible exacte du modèle (fiches 18/19).
- `run_id` = `{date}_{horizon}_{sens}` — **idempotent** : re-scorer le même
  jour remplace les lignes du run, jamais doublonne.
- `measured_at` = horodatage du scoring ; `available_at` = horodatage auquel
  la ligne devient publiable (le lendemain, quand la sortie est connue).
- `model_version` = version des modèles ayant produit le score
  (ex. `retrain-2026-10-01`) ; c'est l'identifiant du modèle réellement
  chargé, pas la dernière date de folds.
- `univers` = `historique` / `elargi` (`historique` pour le 6h).
- `reconstruction` = `true` pour les lignes migrées dont la sortie n'a pas
  pu être reconstruite exactement (entrée = clôture exacte, sortie mesurée
  en spot ~24 h plus tard, sans frais) — période 23/09 → 02/10/2026.

`journal_b.csv` ajoute les mêmes trois colonnes à son schéma propre (la
piste B porte aussi `piste`, `engagement_hash`, `publie_le`,
`hash_verifie`). `journal_6h.csv` dérive le même schéma.

## 8. Publication — ce qui est public et quand

Règle d'or : **aucun artefact public ne contient les scores du jour J
au-delà du top 10 public (long) / top 3 (short)**.

- `scores.json` (public) : classement COMPLET de J-1 + top 10 long / top 3
  short du jour J + bloc stats. (En vigueur depuis la tâche 7.)
- `journal.csv` (public) : **uniquement les horizons clôturés** — les lignes
  du panier (top 10 long + top 3 short) dont la sortie est mesurée. Le détail
  d'une journée n'apparaît qu'au jour J+1, une fois clôturé.
- `journal_b.csv` (public) : pistes B clôturées, engagement vérifié.
- `journal_6h.csv` (public) : top 10 6h clôturés, contexte uniquement.
- **Engagement du jour J** : à chaque scoring, un nonce aléatoire est tiré et
  `sha256(classement complet du jour + nonce)` est publié dans le post de
  16h10 (social.txt / carte / balance.json). Le lendemain à 08h00 UTC, le
  fichier `reveles/{J}.json` révèle le nonce ET le classement complet : tout
  le monde peut vérifier que le classement diffusé hier n'a pas été réécrit.
- `reveles/index.json` : liste des journées révélées. La chaîne d'engagement
  démarre le 02/10/2026 : les journées antérieures n'ont pas de nonce
  (le classement complet J-1 reste néanmoins public via scores.json).
- **Registre permanent des engagements** : chaque engagement (date, hash,
  nonce, horodatage de publication, date de révélation) est conservé
  définitivement dans `engagements.json` (page publique `/engagements.html`,
  panneau sur la page du forward test). Rien ne peut être réécrit après
  coup. « L'engagement prouve l'antériorité du classement (son hash était
  public avant la clôture suivante) ; il ne prouve ni le rang futur, ni le
  rendement futur. »
- **Manifeste de bundle** (fiche 23) : chaque bundle publié décrit son
  périmètre (`schema_version`, `run_id`, `classification_date`, versions des
  composants, univers, calendrier, embargo, liste des artefacts avec empreinte
  sha256, contrôles réussis et leur version). Le manifeste public expurge
  secrets, nonce non révélé, données du jour J et identifiants clients.
- **Archives publiques `/archives/`** (sans compte) : publications initiales
  telles que publiées, versions corrigées, errata datés, règles par période,
  identifiants modèle/protocole. Preuve autonome — aucun dépôt privé requis
  pour vérifier.

## 9. Compteurs publics

- **jours observés** : journées dont le classement a été scoré et journalisé.
- **jours évalués** : journées dont le panier a une sortie clôturée mesurée.
- **dernier jour évalué** : date de la dernière journée clôturée.
Aucun de ces compteurs n'additionne le 6h au daily.

## 10. Historique et erratum

- 23-25/09/2026 : plusieurs runs du scoring le même jour ont doublonné des
  lignes du journal (détecté lors de l'audit du 02/10). Corrections : un run
  canonique par date (celui le plus complet, puis le plus récent), les autres
  versés intégralement dans `journal_archives.csv` (rien n'est détruit), les
  séries et compteurs recalculés sur le journal dédupliqué.
- Frais 0,30 % aller-retour : appliqués au protocole à partir du 03/10/2026.
  Les lignes antérieures (`reconstruction=true`) n'en portent pas : les
  séries protocolaires présentent donc une rupture explicite et documentée.
  (Une déduction rétroactive avait été exécutée le 03/10 sur les 1 386
  lignes 1d antérieures et a été annulée le 04/10 — opération consignée dans
  l'errata t30-e1 ; aucune mesure de prix réécrite.)
- **Errata t30-e1 et t30-e2 (04/10/2026)** — registre public daté
  `errata.json`, jamais effacé : présentation définitive des deux périodes
  (brute / nette) et phrase de non-comparabilité ; compteurs des posts du
  01/10→03/10 désynchronisés du JSON servi (journal exact en permanence),
  corrigé par recalcul systématique après mesure + validateur bloquant
  (tout écart post/JSON refuse la publication).
- **Errata palier 0 (06/10/2026)** — schéma des journaux étendu de 20 à 23
  colonnes (protocol_id, cost_model_id, target_id) par migration dérivée ;
  correction du double comptage des frais de la piste B (aucune ligne déjà
  publiée réécrite) ; protocole versionné 2.0 avec archive v1 intégrale.
- Les anciennes séries « moyenne de l'univers » (tous les actifs scorés) sont
  conservées à titre informatif sous ce nom explicite ; elles ne représentent
  pas le protocole.

## 11. Consommateurs

- Scoring / journal : `~/strateforge/claw_scoring.py`
- Mesure des sorties : `~/strateforge/cron/measure_rendements.py`
- Piste B : `~/strateforge/cron/mesure_b.py`
- Contrat commun : `~/strateforge/cron/contrat_donnees.py` (sf-contrat/1.0)
- Artefacts publics : `~/strateforge/cron/publish_public.py`
- Stats (site, PDF, marketing) : `ft_stats()` dans `claw_scoring.py`
- Classements archivés : `~/strateforge/cron/build_historique.py`
- Rapport PDF : `~/strateforge/rapport/generer_rapport.py`
- Scripts d'opération versionnés en interne (hors portée de la preuve
  publique : la preuve est le site et `/archives/`). Statistiques, posts et
  PDF sont générés depuis un **ensemble unique validé** (`validate_chaine`)
  : tout post citant des chiffres différents du JSON du jour bloque la
  publication.
