CVN-N001-EI-S05 — Economic metrics + θ-curves (Block 4) — Plan dossier¶
STATUT : r2.7 (raffiné, non encore soumis à
plan_review— 2ᵉ review opérateur 2026-06-02 foldée). r0 = squelette S04 r0 ; r1 = stats S04 r3 ; r2 = aggrégation/symétrie/UQ ; r2.2 = niveau d'inférence group-level + pseudo-code ; r2.3 = désambiguïsation « REFUTED » + micro-average + rate_buy=0 + priorité inconclusif + B=2000 + softening SOTA + output schema.Changelog r2.2→r2.3 : (1) désambiguïsation « C REFUTED » (Epic = tradabilité réfutée ; verdict S05 = statut C réfuté — sens opposés, note §0+§1) ; (2) glose
C_REFUTED_TRADEABLE; (3) agrégation groupe = micro-average pondéré opportunités (≠ macro) ; (4)rate_buy(θ)=0→ exclu deM(−∞, pas NaN) ; all-zero →reason=no_trade_points; (5) priorité inconclusif global TOOLING>COST_SENSITIVE>UNDERPOWERED ; (6)B_canonical=2000figé ; (7) Reality-Check/SPA reformulé en inspiration, pas équivalence ; (+) output schema pré-enregistré.r2.4 — committee
plan_reviewc6a789faPASSED (strong, 0 blocker, 5×9/9 — Meeting #240). 6 recommandations non-bloquantes foldées : (#1) sub-block défini (k=5 contigus non-chevauchants, longueur lue du parquet) ; (#2) anti look-aheadATR/price(trailing, test unitaire) ; (#3)n_buy_at_star(nb absolu de BUY) au schema ; (#4) hypothèses slippage 7 bps documentées + slippage dynamique = Story suivi ; (#5) réalisme intrabar triple-barrier (hérité, SL-first, sans biais inter-familles) §5.7 ; (#6) routing : XGB tradable → validation calibration prod-grade avant ship §5.3.r2.5 (note légère, post-archi-review — pas de re-open committee) : agrégation groupe = micro décisionnelle (inchangé) + macro = sensibilité conditionnelle à la matérialité de taille (
max/min n_scored_rows > 1.2) →reason=size_domination_sensitivesi désaccord micro/macro. Check surn_scored_rows(pasn_candles). Même statut qu'une sensibilité (coût/excl-OPUSDC), pas une re-décision.r2.7 — fix logique
_decide_family(post-test-strategy review) : la branche D5 keyait surM_obs>0(point-estimate) → misclassification (straddle non-significatif +M_obs>0+ rate<min était classéNOT_TRADEABLEau lieu deUNDERPOWERED). Corrigé : décision keyée 100% surci_low/ci_high(significance),M_obsn'entre jamais (reporting-only). Arbre complet + sans recouvrement sur l'IC seul. Combinaison cross-famille : « 1 REFUTED suffit » (n_refuted≥1 & n_not==0 → REFUTED) confirmé intentionnel (cohérent H1/H2 : réfuter H₀ pour ≥1 famille = tradable quelque part).Changelog r2→r2.2 : (A)
p(θ) = precision_buy(θ)observée explicité (EV empirique par seuil) ; (B) tie-break θ* déterministe (plus petit θ) ; (C) pseudo-codefamily_verdictcomplet incl.M≤0 mais IC∋0 → INCONCLUSIVE_UNDERPOWERED; (D)M>0 & rate<rate_min → NOT_TRADEABLE+ reason code obligatoire ; (E)INCONCLUSIVE_COST_SENSITIVEintégré à l'aggrégation ; (F) niveau d'inférence = group-family bootstrap (n=25), per-asset = reporting only (Option 3) — supprime la fausse matrice cell-level ; (G) micro-smoke bootstrap ajouté ; + reason codes loggués (cost/calibration/slippage), guardcost_bps<0.Structure narrative (amont des sections techniques §0-§7) : Ch.1 Problématisation (non-technique) · Ch.2 User stories (10) · Ch.3 Hypotheses (8, EN) · Ch.4 State of the art (18 réfs, EN) · Ch.5 Consolidation & traçabilité (matrice end-to-end + decision routing verdict→action).
Story : CVN-N001-EI-S05 (wp#228, GH #1060) · Epic : CVN-N001-EI (#1055) — Block 4 · généralise le statut C.
PTE : ATR0.5_1.5_H4 (tp=1.5, sl=0.5) — [[project_pte_policy]]. Cohorte : defi_top5, fold_id=3, warm pin S07. Diagnostic : s43 (libre).
Chapitre 1 — Problématisation (le problème en clair)¶
La question, en une phrase¶
Nos modèles d'intelligence artificielle qui décident d'acheter ou non une crypto rapportent-ils réellement de l'argent une fois les frais payés — ou seulement sur le papier ?
Pourquoi on se pose la question maintenant¶
Depuis plusieurs mois, on a construit des modèles censés détecter les bons moments d'achat. Sur les tableaux de bord, certains chiffres « ont l'air bons ». Mais un chiffre flatteur sur un écran et un gain réel sur un compte en banque sont deux choses différentes. Tout un programme d'enquête (l'Epic « diagnostic ») cherche méthodiquement à savoir si le signal détecté par ces modèles est exploitable pour de vrai, ou s'il s'agit d'un mirage statistique.
Une première réponse est déjà tombée pour un des trois modèles (celui appelé CatBoost) : même en réglant au mieux son niveau de prudence, une fois les frais de transaction déduits, il perd de l'argent à chaque réglage possible. Conclusion provisoire : ce modèle-là n'est pas tradable.
Le problème : on a supposé que les deux autres modèles (LightGBM et XGBoost) se comportaient pareil, sans le prouver. C'est exactement le genre de raccourci qu'on s'interdit. Cette Story comble ce trou : prouver — ou réfuter — le constat sur les trois modèles, proprement.
Les deux pièges qui font qu'un modèle « semble » gagner alors qu'il perd¶
Mesurer « est-ce que ça marche ? » est plus subtil qu'il n'y paraît. Deux illusions classiques :
Piège n°1 — la métrique de laboratoire. Jusqu'ici on jugeait les modèles sur un score abstrait (le « f1_buy »). Ce score peut être correct alors que la stratégie perd de l'argent : il ne tient pas compte des frais réels (commissions de l'exchange, écart de prix à l'exécution, coût de portage). Un modèle peut avoir un beau score de laboratoire et un compte qui se vide. On remplace donc la métrique de labo par la seule qui compte : le gain net par trade, frais déduits.
Piège n°2 — trader presque jamais. Un modèle peut afficher un « gain positif » en ne prenant qu'une poignée de paris ultra-prudents, une fois tous les quinze jours. Statistiquement, ça peut sembler rentable ; opérationnellement, ce n'est pas une stratégie déployable — c'est trop rare pour vivre avec, et trop fragile. On exige donc qu'un « gain » ne compte que s'il s'accompagne d'un rythme de trading crédible (au moins un certain nombre d'opérations).
Ce que cette Story mesure vraiment¶
Pour chacun des trois modèles, on balaie tous les niveaux de prudence possibles (du plus audacieux au plus prudent) et, à chaque niveau, on calcule : combien d'argent on gagne ou perd par opération, après frais réels, et à quelle fréquence on trade. On en tire une conclusion claire par modèle : « existe-t-il au moins un réglage qui rapporte de l'argent net, tout en tradant assez souvent ? »
Les frais utilisés ne sont pas inventés : ce sont les vraies valeurs validées par l'opérateur (commissions, glissement de prix, portage), figées et tracées pour qu'on puisse rejouer l'analyse à l'identique plus tard.
L'honnêteté du verdict¶
Trois résultats sont possibles, et on s'engage à les distinguer franchement : - « Pas exploitable » — aucun réglage ne rapporte d'argent net à un rythme crédible (le constat CatBoost se généralise). - « Exploitable » — au moins un modèle a un réglage qui gagne réellement, frais déduits → c'est une piste à creuser pour un déploiement. - « On ne sait pas encore » — les données ne permettent pas de trancher avec assez de certitude.
Ce dernier verdict est assumé : mieux vaut dire « on ne sait pas » que fabriquer une fausse certitude. De même, si la réponse dépend trop de l'hypothèse de frais (elle basculerait si les frais étaient un peu plus élevés ou plus bas), on le signale explicitement plutôt que de la présenter comme acquise.
Pourquoi ça compte pour la suite¶
Cette réponse alimente une décision de niveau supérieur sur tout le programme : faut-il continuer à essayer de trader ce signal tel quel, ou faut-il revenir en amont retravailler les données et les cibles des modèles ? Se tromper ici coûte cher dans les deux sens : déployer un modèle qui perd de l'argent en croyant qu'il gagne, ou abandonner un signal réellement exploitable parce qu'on l'a mal mesuré. L'enjeu de cette Story est donc de fournir le verdict fiable sur lequel cette décision pourra s'appuyer sans regret.
Chapitre 2 — User stories mises en œuvre¶
Ce que la Story délivre, exprimé du point de vue de ceux qui s'en servent. Chaque US renvoie à la section du plan qui la réalise.
| # | User story | Réalisée par |
|---|---|---|
| US-1 | En tant que décideur Epic, je veux savoir pour chacun des 3 modèles (LightGBM, XGBoost, CatBoost) s'il existe un réglage rentable après frais, afin de trancher : continuer à trader le signal ou revenir retravailler les données/cibles. | §1 verdict par famille + combinaison ; §0 généralisation statut C |
| US-2 | En tant que quant, je veux que la métrique de décision soit le gain net par trade après frais réels (et non un score de laboratoire comme f1_buy), afin que la décision reflète l'argent réel, pas le papier. | §1 E(θ) net ; §2 (métrique trading-truth) |
| US-3 | En tant qu'opérateur, je veux qu'un « gain » ne compte que s'il s'accompagne d'un rythme de trading crédible (≥ 10 % d'opportunités saisies), afin de ne pas confondre une stratégie déployable avec une poignée de paris ultra-rares. | §1 rate_min + reason=non_degenerate_rate_failed |
| US-4 | En tant qu'opérateur, je veux que les frais utilisés soient mes vraies valeurs, figées et tracées au moment du run, afin de pouvoir rejouer l'analyse à l'identique dans 6 mois. | §3 valeurs validées + event=s43_cost_snapshot |
| US-5 | En tant que relecteur, je veux un verdict « on ne sait pas encore » honnête quand les données sont insuffisantes, afin de ne jamais recevoir une fausse certitude. | §1 INCONCLUSIVE_UNDERPOWERED ; §5.5 max-bias |
| US-6 | En tant que décideur, je veux savoir si le verdict basculerait pour un petit changement de frais, afin de mesurer la fragilité de la conclusion avant d'agir. | §5.1 sensibilité ±50 % → INCONCLUSIVE_COST_SENSITIVE |
| US-7 | En tant que quant, je veux voir si un seul actif (ex. OPUSDC) tire le résultat du groupe, afin de distinguer un constat uniforme d'un effet hétérogène. | §1 per-asset heterogeneity report (per_asset_divergence) |
| US-8 | En tant qu'ingénieur plateforme, je veux que la courbe de point de fonctionnement soit émise pour les 3 modèles (XGBoost inclus, aujourd'hui manquant), afin de disposer de cette télémétrie au-delà de S05, en production. | §2 #3 (θ-swept XGB câblé au harness) ; §4 xgboost_dag.py |
| US-9 | En tant qu'auditeur, je veux que chaque hypothèse de l'analyse soit tracée par un code-raison (frais sensibles, calibration XGB de substitution, source du slippage), afin de ne jamais lire « pas rentable » alors que la vraie nuance était ailleurs. | §6 reason codes ; §5.3 xgb_calibration_source |
| US-10 | En tant qu'opérateur, je veux que toute panne d'exécution se traduise par un verdict structuré (jamais un crash dans l'interface), afin de garder un diagnostic lisible même en cas d'incident. | §6 ops (INCONCLUSIVE_TOOLING, ADR-25) |
Chapter 3 — Hypotheses & how they are tested (English)¶
This chapter enumerates every hypothesis the Story positions, the exact statement being tested, and the method that decides it. The primary hypothesis (H1) is the decision; H2–H7 are the supporting hypotheses that make H1's verdict trustworthy; H8 is an explicit working assumption (surfaced, not proven).
H1 — Primary decision hypothesis (per model family), stated as the NULL tested¶
H₀(C) — For a given model family (LightGBM / θ-swept XGBoost / CatBoost), there is no decision threshold θ ∈ [0.05, 0.95] at which the net-of-cost per-trade expectancy is positive, at a non-degenerate trade rate.
How tested. For each family, build the operating-point curve E(θ) = precision_buy(θ)·tp − (1−precision_buy(θ))·sl − cost over the full θ grid, take the envelope statistic M = maxθ E(θ). Estimate the sampling distribution of M by a group-level clustered bootstrap over (crypto, sub-block) units (n = 5 cryptos × 5 sub-blocks = 25, plus seeds where the family is stochastic). Decide on the 95 % CI of M, with Bonferroni correction across the 3 families (α = 0.0167). The decision rule is the family_verdict pseudo-code (§1): ci_low > 0 and rate ok → refute H₀ (tradable); ci_high ≤ 0 → fail to refute (not tradable); CI straddling 0 → inconclusive.
Rejecting H₀ for a family = that family IS tradable somewhere. Failing to reject across all families = status C generalised.
H2 — Generalisation of status C across families¶
The "not tradable after costs" result already established for CatBoost (Epic #1055) generalises to LightGBM and θ-swept XGBoost.
How tested. Run H1 on all three families, then combine cross-family (§1 combination table): all three fail to reject → C_GENERALISED_NOT_TRADEABLE; any family refutes → C_REFUTED_TRADEABLE or C_PER_FAMILY. This is the Story's headline output.
H3 — Net economics is the correct truth metric (not the lab metric)¶
A model can have a healthy
f1_buywhile losing money after real costs; therefore net expectancy after costs, notf1_buy, is the valid trading-truth metric.
How tested. The verdict is driven entirely by E(θ) net of operator-validated costs (26 bps round-trip), never by f1_buy. f1_buy is reported on the curve for continuity but is non-decisional. The test is constructive: the pipeline emits and decides on net EV.
H4 — Non-degeneracy of any positive result¶
A positive expectancy is only economically real if it comes with a deployable trade frequency (
rate_buy(θ*) ≥ rate_min = 10 %); a "win" achieved by trading 2–5 % of the time is an artefact, not a strategy.
How tested. The family_verdict rule conjoins M > 0 with rate_buy(θ*) ≥ rate_min (θ* = observed argmax, smallest-θ tie-break). A positive-EV-but-low-rate case is classified C_GENERALISED_NOT_TRADEABLE carrying reason = non_degenerate_rate_failed — distinguishing "no edge" from "edge only at unusable coverage".
H5 — Cost-robustness of the verdict¶
The verdict does not depend fragilely on the cost assumption — it would not flip for a modest change in fees/slippage/funding.
How tested. Re-evaluate the verdict across a ±50 % cost band (13–39 bps round-trip, §5.1). If the verdict flips (REFUTED ↔ NOT_TRADEABLE) inside that band, the family verdict is downgraded to INCONCLUSIVE_COST_SENSITIVE (reason = cost_sensitive) rather than asserting a brittle conclusion.
H6 — Per-family model stochasticity (structural; mostly resolved by code)¶
Each family's seed-to-seed variance differs: LGB is deterministic, CatBoost is stochastic by default, XGBoost is conditional on its sampling hyper-parameters.
How tested. Structural verification of the code paths (whether bagging_freq is ever set for LGB → it is not, so subsample is a no-op → deterministic; CatBoost's default bootstrap_type=Bayesian → stochastic; XGBoost depends on the live PG subsample/colsample_bytree). Consequence on UQ: the primary variance source is the (crypto, sub-block) bootstrap, independent of model stochasticity, so the design is valid either way; multi-seed is added only for families confirmed stochastic. Asymmetric effective sample size is reported per family.
H7 — Cross-asset homogeneity of the group verdict¶
The group-level verdict is uniform across defi_top5 and not driven by a single asset.
How tested. Per-asset point net-EV curves are computed for all 5 cryptos. per_asset_heterogeneity = fraction of cryptos whose pointwise sign of M diverges from the group verdict; any clear divergence raises a per_asset_divergence = [SYM,…] caveat on the (still group-level) decision. This is a reporting test — it nuances, it does not overturn, the statistical verdict.
H8 — Working assumption (surfaced, NOT proven): calibration comparability¶
Calibrating XGBoost with the LightGBM-like method yields a probability comparable enough for cross-family comparison, in the absence of a deployed production XGBoost to mirror.
Status. This is an assumption, not a tested hypothesis. It is surfaced explicitly via reason = xgb_calibration_source = lgb_like_fallback in every XGB output, so a reviewer weighs the XGB verdict knowing its calibration is a proxy. If it later matters, a follow-up Story can test calibration-method sensitivity.
Chapter 4 — State of the art (theory & best practices)¶
This chapter grounds the Story's method in the established literature on (i) operating-point selection under class imbalance and cost, (ii) economic evaluation of trading signals, (iii) transaction-cost modelling, (iv) data-snooping / multiple-testing and the maximum-statistic bias — the central methodological risk here — (v) resampling-based uncertainty for dependent financial data, (vi) probability calibration, and (vii) pre-registration and honest inconclusive verdicts. Each subsection maps to the hypotheses of Chapter 3.
4.1 Operating-point selection under imbalance and cost (→ H3, H4)¶
Choosing a decision threshold is not a modelling detail but the economic decision itself. Classical cost-sensitive learning shows the optimal threshold is a function of the cost/benefit ratio, not a fixed 0.5 (Elkan, 2001). Under heavy class imbalance — our case, a BUY base rate near 16 % — ROC analysis is misleading and precision-recall curves are the informative object (Saito & Rehmsmeier, 2015); the achievable operating points form the convex hull of the PR/ROC space (Provost & Fawcett, 2001). This justifies S05's full θ-sweep (rather than a single production threshold) and its use of precision_buy(θ) — the observed BUY-class precision at the threshold — as the driver of realised expectancy (H3), together with a trade-rate floor to exclude degenerate corners of the operating envelope (H4).
4.2 Economic evaluation of trading signals (→ H1, H2, H3)¶
The discipline's standard reference for ML-driven trading evaluation, López de Prado (2018, Advances in Financial Machine Learning), is explicit that a model's statistical score and its net P&L are different objects, and that labels and evaluation should be expressed in the trade's own economics (the triple-barrier / meta-labelling framework). Net per-trade expectancy E = p·payoff_win − (1−p)·payoff_loss − cost is the canonical "trading-truth" quantity. S05 adopts exactly this: replacing f1_buy with net expectancy after costs as the primary metric, computed on the project's ATR0.5_1.5_H4 triple-barrier geometry.
4.3 Transaction-cost and execution modelling (→ H5)¶
Backtests that ignore costs systematically overstate edge. Realistic evaluation requires fees, slippage and market impact; the Almgren & Chriss (2000) framework formalises impact as a function of traded size, and the practitioner consensus is that cost assumptions must be explicit and stress-tested. S05 encodes costs in basis points (fees + slippage + funding), converts them to the ATR units of the barriers, and — following the sensitivity-analysis best practice — reports the verdict across a ±50 % cost band, downgrading to cost-sensitive when the conclusion is not robust (H5).
4.4 Data-snooping, multiple testing, and the maximum-statistic bias (→ H1, H2)¶
This is the central methodological risk and the reason for the envelope design. Searching over many thresholds and selecting the best one is a textbook data-snooping / selection-bias problem: the maximum of many noisy statistics is upward-biased ("winner's curse"), so the naïve "best θ looks profitable" is almost guaranteed under the null. The seminal corrections are White's Reality Check (2000) and Hansen's Superior Predictive Ability test (2005), which bootstrap the distribution of the best rule's performance under the null. S05 mirrors their key design principle — bootstrap the selected / envelope statistic M = maxθ E(θ) (recomputing the max inside each resample) rather than testing each θ independently. S05 applies this principle via a clustered bootstrap of the envelope; it is not a formal Reality Check with explicit null-recentering — the inspiration is the selection-bias-aware design, not a strict equivalence. In finance specifically, Bailey & López de Prado (2014) Deflated Sharpe Ratio and Harvey, Liu & Zhu (2016) quantify how multiple trials inflate apparent performance and prescribe a statistical haircut; Romano & Wolf (2005) formalise stepwise control of the family-wise error rate. S05 mirrors this: one envelope statistic per family and Bonferroni control across the three families (α = 0.0167), avoiding the ≈95 % false-positive rate a per-θ test would incur (H1), and combining family verdicts into the status-C generalisation (H2).
4.5 Resampling-based uncertainty for dependent data (→ H1, H6)¶
Financial time series are serially dependent, so the i.i.d. bootstrap (Efron & Tibshirani, 1993) is invalid for serial structure. The standard remedies are the moving-block bootstrap (Künsch, 1989) and the stationary bootstrap (Politis & Romano, 1994), which resample contiguous blocks to preserve short-range dependence; when the natural sampling units are groups (here, the five cryptos), a clustered bootstrap resamples whole units. S05's UQ design resamples (crypto, sub-block) clusters — combining the cluster idea with contiguous sub-blocks — so the interval is valid whether or not the model is stochastic (H6); multi-seed variance is added only where a family is genuinely stochastic. The deliberate choice of a clustered design over a pure moving-block reflects that here the inter-asset units, not the intra-series serial structure, carry the decision-relevant variance.
4.6 Probability calibration (→ H8 and the p_buy definition)¶
Decisions must use calibrated probabilities or the operating-point economics are wrong. The standard post-hoc methods are Platt scaling (Platt, 1999) and isotonic regression (Zadrozny & Elkan, 2002); Niculescu-Mizil & Caruana (2005) compare them across model families and note that boosted trees in particular are typically mis-calibrated and benefit from isotonic correction. This both motivates S05's requirement that p_buy be the deployed calibrated probability, and frames the XGBoost calibration as an explicit comparability assumption (H8) when no production-calibrated XGBoost exists to mirror.
4.7 Pre-registration and honest inconclusive verdicts (→ H1, H5, H7)¶
The reproducibility literature shows that decision rules fixed after seeing results inflate false discoveries; pre-registration of the hypothesis and the decision rule is the accepted antidote (Nosek et al., 2018), and in quantitative finance the backtest-overfitting work (Bailey, Borwein, López de Prado & Zhu, 2014) makes the same point for trading research. S05 follows this by pre-committing the family_verdict rule, the tie-break, the trade-rate floor, and the inconclusive classes before the run, and by treating INCONCLUSIVE_UNDERPOWERED / INCONCLUSIVE_COST_SENSITIVE as first-class honest outcomes rather than failures — directly supporting the "we'd rather say we don't know" stance of Chapter 1.
References¶
- Almgren, R., & Chriss, N. (2000). Optimal Execution of Portfolio Transactions. Journal of Risk, 3(2).
- Bailey, D. H., Borwein, J., López de Prado, M., & Zhu, Q. J. (2014). Pseudo-Mathematics and Financial Charlatanism: The Effects of Backtest Overfitting. Notices of the AMS, 61(5).
- Bailey, D. H., & López de Prado, M. (2014). The Deflated Sharpe Ratio. Journal of Portfolio Management, 40(5).
- Efron, B., & Tibshirani, R. (1993). An Introduction to the Bootstrap. Chapman & Hall.
- Elkan, C. (2001). The Foundations of Cost-Sensitive Learning. IJCAI.
- Hansen, P. R. (2005). A Test for Superior Predictive Ability. Journal of Business & Economic Statistics, 23(4).
- Harvey, C. R., Liu, Y., & Zhu, H. (2016). … and the Cross-Section of Expected Returns. Review of Financial Studies, 29(1).
- Künsch, H. R. (1989). The Jackknife and the Bootstrap for General Stationary Observations. Annals of Statistics, 17(3).
- López de Prado, M. (2018). Advances in Financial Machine Learning. Wiley.
- Niculescu-Mizil, A., & Caruana, R. (2005). Predicting Good Probabilities with Supervised Learning. ICML.
- Nosek, B. A., et al. (2018). The Preregistration Revolution. PNAS, 115(11).
- Platt, J. (1999). Probabilistic Outputs for Support Vector Machines. In Advances in Large Margin Classifiers.
- Politis, D. N., & Romano, J. P. (1994). The Stationary Bootstrap. JASA, 89(428).
- Provost, F., & Fawcett, T. (2001). Robust Classification for Imprecise Environments. Machine Learning, 42.
- Romano, J. P., & Wolf, M. (2005). Stepwise Multiple Testing as Formalized Data Snooping. Econometrica, 73(4).
- Saito, T., & Rehmsmeier, M. (2015). The Precision-Recall Plot Is More Informative than the ROC Plot … PLoS ONE, 10(3).
- White, H. (2000). A Reality Check for Data Snooping. Econometrica, 68(5).
- Zadrozny, B., & Elkan, C. (2002). Transforming Classifier Scores into Accurate Multiclass Probability Estimates. KDD.
Chapter 5 — Consolidation & traceability (solution ↔ chapters 1-4)¶
This chapter closes the loop: it verifies that the technical solution (§0-§7) answers every point raised in the framing chapters, and fills the one gap an audit surfaced — the decision routing (Chapter 1 promises the verdict feeds a decision; the technical sections did not previously state which verdict triggers which next action).
5.A Traceability matrix — every thread, end to end¶
Each row is one promise of the Story, traced from the plain-language problem (Ch.1) → the hypothesis that formalises it (Ch.3) → the user story that owns it (Ch.2) → the section that builds it (§) → the literature that grounds it (Ch.4). No row dangles.
| Problem point (Ch.1) | Hypothesis | User story | Built in | Grounded by |
|---|---|---|---|---|
| Net money after fees, not a lab score | H3 | US-2 | §1 E(θ) net, §2 |
§4.1-4.2 |
| A "win" must trade often enough | H4 | US-3 | §1 rate_min + reason |
§4.1 |
| Generalise the CatBoost result to all 3 models | H1+H2 | US-1 | §1 family_verdict + cross-family combination |
§4.2, §4.4 |
| Don't be fooled by picking the best of many θ | H1 | US-1 | §1 envelope M + Bonferroni |
§4.4 |
| Say "don't know" honestly | H1 | US-5 | §1 INCONCLUSIVE_UNDERPOWERED, §5.5 |
§4.7 |
| Flag if the answer flips on a small cost change | H5 | US-6 | §5.1 cost band → INCONCLUSIVE_COST_SENSITIVE |
§4.3 |
| Use real, frozen, traced costs | H1 (input) | US-4 | §3 cost snapshot | §4.3 |
| Show if one asset drives the result | H7 | US-7 | §1 per-asset report | §4.5 |
| θ-curve telemetry for all 3 models | enabler | US-8 | §2#3, §4 xgboost_dag.py |
§4.1 |
| Decide on calibrated (prod-like) probabilities | H8 | US-9 | §3 calibration, §5.3 | §4.6 |
| Trace every assumption by a reason code | cross-cutting | US-9 | §6 reason codes | §4.7 |
| Never crash the operator UI | ADR-25 | US-10 | §6 ops INCONCLUSIVE_TOOLING |
— |
| Valid uncertainty even if the model is deterministic | H6 | US-5 (power) | §3 sub-block bootstrap | §4.5 |
5.B Decision routing — what each verdict triggers (closes the Ch.1 "why it matters" gap)¶
The Story's output is not an end in itself; it feeds the Epic-level decision of Chapter 1. Pre-committed routing (recorded in the closure note alongside the verdict) :
| Verdict (global) | Plain meaning (Ch.1) | Next action (pre-committed) |
|---|---|---|
C_GENERALISED_NOT_TRADEABLE |
"Pas exploitable" — confirmed on all 3 models | Status C generalised → route to S06 (features / target redesign) ; do not pursue deploying this signal as-is (ADR-2). |
C_REFUTED_TRADEABLE |
"Exploitable" — a model has real net edge | Deployment-investigation follow-up Story for that family (a lead to confirm, not an automatic ship — ADR-2). |
C_PER_FAMILY |
Mixed | Route per family: the refuting family → deployment-investigation ; the not-tradable ones → S06 input. |
INCONCLUSIVE_UNDERPOWERED |
"On ne sait pas encore" | No Epic decision ; options = more data / wider cohort / accept the open question. No routing to S06 nor to deployment. |
INCONCLUSIVE_COST_SENSITIVE |
Answer hinges on the cost assumption | No robust decision ; refine the cost inputs (operator) and re-decide before routing. |
INCONCLUSIVE_TOOLING |
Machinery failed | Re-run / fix tooling ; not a scientific verdict, no routing. |
5.C Coherence statement¶
- User stories : 10/10 realised (matrix 5.A) — each has a building section.
- Hypotheses : 8/8 have a stated test (Ch.3) and a concrete output in §1-§6 ; H8 is honestly carried as a surfaced assumption, not a claim.
- State-of-the-art practices : the 7 areas of Ch.4 are each applied (envelope/Reality-Check → §1 ; clustered bootstrap → §3 ; calibration → §3/§5.3 ; cost stress-test → §5.1 ; pre-registration → §1 pre-committed rules).
- Decision routing : every terminal verdict now maps to a pre-committed next action (5.B) — the Ch.1 "why it matters" promise is honoured.
- Residual openness (by design, not gap) : one non-blocking PG read (XGB stochasticity, Q6) ; H8 calibration assumption ; both surfaced, neither blocks the verdict.
0. Why this Story exists (provenance)¶
Statut C = la question « le signal est-il tradable ? ». Sur CatBoost (#1055), la réponse est NON : CatBoost tradability REFUTED on captured evidence — net-EV < 0 à tout θ. LGB/XGB inférés non-tradables mais θ-curves pending. Block 4 = tester si ce « non-tradable » se généralise à LGB + XGBoost θ-swept (+ CatBoost re-vérifié), en espérance nette après coûts, avec UQ et verdict décisionnel remplaçant f1_buy.
⚠️ Désambiguïsation « REFUTED » : dans l'Epic, « C REFUTED » = la tradabilité est réfutée (= non tradable). Dans le verdict S05,
C_REFUTED_TRADEABLE= le statut C « non-tradable » est réfuté (= oui tradable). Sens opposés selon l'objet — voir la note de lecture en §1.
S32 (wp#222) a livré le node theta_curve PUR (src/training/harness/nodes/theta_curve.py), émis LGB+CB mais gross_pre_cost cost_atr=0, θ∈[0.05,0.40], tradeable=no partout, EV>0 à rate_buy≈2-5%.
1. Objective & pre-committed decisions¶
Hypothèse C — NULL testée¶
H₀(C) : Pour CHAQUE famille (LGB, XGB θ-swept, CatBoost), la statistique d'enveloppe
M = max_{θ∈[0.05,0.95]} E(θ)(net après coûts), estimée au niveau groupe surdefi_top5, n'est PAS significativement > 0 (IC95 bootstrap clustered sur la distribution deM, Bonferroni inter-familles), à un taux de trade non-dégénérérate_buy(θ*) ≥ rate_min.
Définitions empiriques (amendement A — figées)¶
p(θ), rate_buy(θ) sont les quantités observées au seuil (EV trading-truth, pas la proba prédite moyenne) — c'est déjà ce que theta_curve.py calcule (p = precision[1]) :
precision_buy(θ) = mean( y_true == 1 | p_buy ≥ θ ) # BUY-class precision observée
rate_buy(θ) = mean( p_buy ≥ θ )
E(θ) = precision_buy(θ)·tp_atr − (1 − precision_buy(θ))·sl_atr − cost_atr
p_buy = proba calibrée que la prod déploierait (cf. Q5). ATR0.5_1.5_H4 → E = 2p − 0.5 − cost.
rate_min + tie-break θ* (amendement B)¶
rate_min = 0.10. θ* = argmax_θ E(θ) sur les données originales (pas bootstrap). Tie-break : en cas d'égalité de E(θ) à ε=1e-12, choisir le plus petit θ parmi les maxima (maximise la couverture / rate_buy, choix le moins cherry-pické, compatible anti-overtrade).
NIVEAU D'INFÉRENCE — group-level (amendement F, Option 3)¶
Décision scientifique = par famille, AU NIVEAU GROUPE : bootstrap clustered sur (crypto, sub-block), n_cluster = 5 cryptos × 5 sub-blocks = 25 (+ ×K seeds si famille stochastique, cf. §3). Chaque resample recompute la courbe net-EV agrégée sur le groupe puis prend M = max_θ E(θ). → IC95 sur la distribution de M, Bonferroni inter-familles (α=0.0167).
Per-asset (par crypto) = REPORTING ONLY : courbes net-EV ponctuelles + flag d'hétérogénéité — PAS de verdict statistique par cellule (n=5 sub-blocks/crypto trop faible pour un IC cell-level valide). (Supprime la fausse matrice cell-level de r2 qui mélangeait les niveaux.)
Agrégation de la courbe groupe — décisionnelle + sensibilité (r2.5) :
- DÉCISIONNELLE = micro-average pondéré par opportunités (committee-PASSED, primaire) : à chaque resample, concaténer les opportunités de tous les clusters (crypto, sub-block) tirés, puis precision_buy(θ), rate_buy(θ), E(θ), M=maxθ E(θ). Répond à la question de déploiement : « le portefeuille defi_top5 déployé gagne-t-il de l'argent net ? » (trading proportionnel-aux-opportunités).
- Check de matérialité (avant full run) : logger n_scored_rows par crypto sur fold_id=3 (event=s43_opportunity_balance) — lignes réellement scorées éligibles au θ-sweep, PAS n_candles brut (le pipeline peut filtrer / labels manquants). Si max/min ≤ 1.2 → distinction micro/macro immatérielle, aucune sensibilité macro requise.
- Sensibilité MACRO (non-décisionnelle, ssi max/min > 1.2) : M_macro = maxθ mean_c E_c(θ) (poids 1/crypto, répond à « l'asset typique gagne-t-il ? »). Si micro et macro s'accordent sur le family_verdict → verdict micro tient (note de robustesse). S'ils divergent → verdict micro reste enregistré + reason=size_domination_sensitive + escalade aux notes de revue Epic.
- Pas de swap micro→macro : même modèle que la sensibilité coût ±50% (§5.1) — un garde-fou, pas une re-décision. Courbes per-asset rapportées séparément (reporting).
θ sans trade (amendement r2.3-4, pré-enregistré) : si rate_buy(θ)=0, precision_buy(θ) est indéfinie → E(θ) est exclu du max M (traité comme −∞, jamais de NaN propagé). Si tous les θ ont zéro trade → family_verdict = INCONCLUSIVE_UNDERPOWERED avec reason=no_trade_points.
family_verdict — pseudo-code complet (amendements C, D, E)¶
def family_verdict(ci_low, ci_high, M_obs, rate_at_star, *, rate_min=0.10,
tooling_error=False, cost_sensitive=False):
if tooling_error: return "INCONCLUSIVE_TOOLING"
if cost_sensitive: return "INCONCLUSIVE_COST_SENSITIVE" # (E)
# Décision keyée sur la SIGNIFIANCE (position de l'IC), JAMAIS sur M_obs (point-estimate, reporting-only).
if ci_low > 0 and rate_at_star >= rate_min: return "C_REFUTED_TRADEABLE" # (D3) IC>0 significatif
if ci_low > 0 and rate_at_star < rate_min: # (D5, r2.7 fix : ci_low pas M_obs)
return "C_GENERALISED_NOT_TRADEABLE", "reason=non_degenerate_rate_failed" # significatif mais rate dégénéré
if ci_high <= 0: return "C_GENERALISED_NOT_TRADEABLE" # (D4) significatif non-positif
# else : ci_low ≤ 0 < ci_high (straddle, non-significatif) — TOUT M_obs → sous-puissance, PAS not-tradeable
return "INCONCLUSIVE_UNDERPOWERED" # (D6)
INCONCLUSIVE_COST_SENSITIVE (amendement E) : levé si le verdict bascule (REFUTED↔NOT) dans la band coût ±50% (§5.1). Sous-type de INCONCLUSIVE_* pour l'aggrégation (compte comme n_inc), reporté séparément via reason=cost_sensitive.
- Tout C_GENERALISED_NOT_TRADEABLE issu de la branche rate porte reason=non_degenerate_rate_failed (≠ « pas d'EV positive »).
- Note de lecture des noms (amendement r2.3-2) : C_REFUTED_TRADEABLE signifie « le statut C "non-tradable" est réfuté, car ≥1 famille EST tradable » (« REFUTED » porte sur le statut C, pas sur la tradabilité). C_GENERALISED_NOT_TRADEABLE = « le "non-tradable" de CatBoost se généralise ». Voir la désambiguïsation §0.
Combinaison CROSS-FAMILLE → verdict C global (ex-table cell, re-leveled group)¶
n_refuted/n_not/n_inc = nb des 3 family_verdict (group-level) dans chaque classe :
n_refuted ≥ 1 et n_not == 0 → C_REFUTED_TRADEABLE
n_refuted ≥ 1 et n_not ≥ 1 → C_PER_FAMILY
n_not == 3 → C_GENERALISED_NOT_TRADEABLE
n_not ≥ 2 et n_refuted == 0 → C_GENERALISED_NOT_TRADEABLE (majorité claire)
sinon (n_inc dominant) → INCONCLUSIVE_* selon la priorité ci-dessous
INCONCLUSIVE_TOOLING > INCONCLUSIVE_COST_SENSITIVE > INCONCLUSIVE_UNDERPOWERED. Ex. 1 NOT_TRADEABLE + 2 COST_SENSITIVE + 0 refuted → global = INCONCLUSIVE_COST_SENSITIVE (le vrai signal est la fragilité aux coûts), pas UNDERPOWERED.
Per-asset heterogeneity report (remplace C_SYSTEMATIC_*/C_PER_ASSET cell-counted)¶
Le « systématique vs per-asset » devient descriptif (le verdict est déjà group-level) : pour le verdict C global, rapporter per_asset_heterogeneity = fraction de cryptos dont le signe du M ponctuel diverge du verdict groupe. Si ≥ 1 crypto diverge nettement → caveat per_asset_divergence=[SYM,…] sur le verdict (n'annule pas la décision group-level, la nuance). (Plus de comptage cell ≥80% — il n'y a pas de verdict cell statistique sous Option 3.)
Cohorte & folds + couverture (r2.6)¶
defi_top5 (5 cryptos = clusters bootstrap, pas cellules-décision), fold_id=3, warm pin S07.
Couverture cohorte (échec partiel de cellules, pré-enregistré) : le group bootstrap pool les prédictions des cryptos disponibles. Si une cellule-pod échoue (OOM/eviction) et ne produit pas ses prédictions : floor = ≥ 4/5 cryptos. À 4/5 → verdict émis sur la cohorte disponible + reason=partial_cohort coverage=4/5. À < 4/5 → INCONCLUSIVE_TOOLING reason=cohort_coverage_below_floor (pas de verdict sur une cohorte trop dégradée). La synthèse (trigger_rule="all_done") applique cette règle ; l'opérateur relance la/les cellule(s) manquante(s) seule(s) (warm).
Output schema (par famille — pré-enregistré pour impl + tests)¶
family # lightgbm | xgboost | catboost
M_obs # max_θ E(θ) sur données originales (net, après coûts)
theta_star # argmax θ (tie-break = plus petit θ)
rate_buy_at_star # rate_buy(θ*)
n_buy_at_star # NB ABSOLU de trades BUY contribuant à M à θ* (committee rec #3 — contexte de l'UNDERPOWERED)
ci_low, ci_high # IC95 percentile de M (bootstrap B=2000, group-level)
cost_snapshot_id # ancre de réplication (ts + clés/valeurs PG)
family_verdict # C_REFUTED_TRADEABLE | C_GENERALISED_NOT_TRADEABLE | INCONCLUSIVE_{UNDERPOWERED,COST_SENSITIVE,TOOLING}
reason_codes # [non_degenerate_rate_failed | cost_sensitive | no_trade_points | size_domination_sensitive | …]
per_asset_divergence # [SYM,…] cryptos dont le signe du M ponctuel diverge du groupe
n_scored_rows_by_crypto # {SYM: n} lignes scorées éligibles θ-sweep (check matérialité micro/macro, max/min vs 1.2)
xgb_calibration_source # lgb_like_fallback (XGB uniquement)
n_eff # taille effective (25 | 25×K) — asymétrie famille rapportée
c_verdict (combinaison cross-famille) + decision_routing (Chapter 5.B).
2. Scope — ce que S05 AJOUTE à theta_curve¶
| # | Item | État (post-S32) | Delta S05 |
|---|---|---|---|
| 1 | Plage θ | [0.05,0.40] step 0.01 | [0.05,0.95] step 0.05 |
| 2 | Coût | cost_atr=0 (gross) |
net : cost_bps (PG) × ATR/price (fold) via cost_atr_from_bps |
| 3 | XGBoost | wired LGB+CB | θ-swept XGB (harness) + parité calibration |
| 4 | UQ | point-estimate | group-family clustered bootstrap (crypto, sub-block) sur M |
| 5 | Verdict | aucun | _decide_s43 : family_verdict (group) → combinaison cross-famille + per-asset report |
| 6 | Métrique | f1_buy | net_expectancy = trading-truth |
Hors-scope : PTE/modèle/split (S03/S04/S06) ; promotion prod (ADR-2).
3. Technical approach¶
Deux couches (Airflow + Hamilton), Hamilton-native (s41/s42).¶
Réutilisation node existant¶
theta_curve(thetas=grid(0.05,0.95,0.05), cost_atr=resolved) + breakeven_precision() + cost_atr_from_bps() (PURS, inchangés). p=precision_buy déjà correct.
Modèle de coût (ADR-59/ADR-90 fail-loud)¶
cost_atr = legs·(cost_bps/1e4)/(ATR/price). ATR/price mesuré sur le fold ; cost_bps_per_leg = FEES + SLIPPAGE + FUNDING_H4 depuis PG. Fail-loud event=s43_io_cost_resolve_failed severity=error.
Anti look-ahead ATR/price (committee rec #2, pré-enregistré) : ATR est un indicateur trailing (calculé sur des bougies strictement passées à chaque point) ; ATR/price est pris par crypto sur les mêmes lignes du fold capturé que p_buy/E(θ), jamais une statistique full-window ni future. → aucune fuite temporelle. Couvert par un test unitaire asserttant que le ratio à l'index t n'utilise que des données ≤ t.
Valeurs en Console ftf_config (saisies 2026-06-02) : CVN_COST_FEES_BPS=5, CVN_COST_SLIPPAGE_BPS=7, CVN_COST_FUNDING_BPS_H4=1 → cost_bps_per_leg = 13 → round-trip (legs=2) = 26 bps.
⚠️ À confirmer comme valeurs RÉELLES avant le full run : ces 3 chiffres sont des ordres de grandeur défendables (palier taker institutionnel typique), pas garantis être les valeurs mesurées de l'opérateur. Confirmer le palier taker courant, le slippage à la taille de position sur fold-3, le funding amorti mesuré — sinon verdict sur coûts de convenance (anti-pattern S03).
FUNDING_BPS_H4= résultat amorti pré-calculé (funding_8h × 0.5 × hold_eff/4h, §3.3), pas l'input brut.
Trois points actés (spec) :
1. legs = 2 confirmé (cost_atr_from_bps(..., *, legs=2), src/training/harness/nodes/theta_curve.py:102).
2. Slippage : défaut clé globale CVN_COST_SLIPPAGE_BPS ; option per-crypto CVN_COST_SLIPPAGE_BPS_<SYM>. Résolution loggée par crypto : event=s43_cost_snapshot crypto=<SYM> slippage_source=global|per_crypto value_bps=<v>. Hypothèses derrière les 7 bps (committee rec #4, documentées) : ordre taker market, taille modeste vs top-of-book (pas d'impact non-linéaire), conditions de marché moyennes. Slippage statique = simplification ; un modèle de slippage dynamique (size-dependent, Almgren-Chriss §4.3) est une Story de suivi explicitement hors-scope S05.
3. Funding amorti : la clé PG CVN_COST_FUNDING_BPS_H4 stocke le résultat pré-calculé funding_8h × 0.5 × (hold_eff/4h) (un nombre additionné direct à fees+slippage), pas le funding 8h brut ni hold_eff (le code n'applique pas la formule — l'opérateur la calcule hors-ligne et stocke le résultat ; funding_8h/hold_eff documentés dans les notes du run pour la réplication). Peut être ≤ 0 (carry positif sur les longs defi) → guard : cost_bps_per_leg < 0 est économiquement valide, loggué event=s43_cost_negative, pas config invalide. ⚠️ Signe load-bearing : un funding mis à +1 alors qu'il est réellement négatif biaise le verdict vers NOT_TRADEABLE à tort — hors-band de la sensibilité ±50%, donc à mesurer (signe inclus), pas à supposer.
Cost snapshot : lecture PG au démarrage du dry-run, loggué event=s43_cost_snapshot key=… value=… ts=…, utilisé pour toutes les évaluations du run (ancre de réplication).
Prédictions par famille — cache-hit vs cold-train + calibration¶
(y_true, p_buy) calibré par famille sur le fold pinné : cache-hit si (crypto, fold_id=3, model_type, hp_id) existe ; sinon cold-train. Calibration parité : xgboost_dag.py produit predict_proba brut (vérifié). Faute de prod XGB déployée, S05 calibre XGB par la méthode de la famille la plus proche (LGB) — comparability assumption marquée dans l'output : reason xgb_calibration_source=lgb_like_fallback. Spy from_training_cache call_count==0 sur pin HIT (ADR-0094 Inv 7).
UQ — group-family clustered bootstrap (stochasticité per-famille tranchée)¶
Vérif structurelle (3 conventions LGB/XGB/CB) :
| Famille | Stochasticité | n_eff |
|---|---|---|
| LGB | DÉTERMINISTE (bagging_freq jamais passé → subsample no-op — confirmé sans PG) |
sub-block = 25 |
| XGB | stochastique ssi PG subsample/colsample < 1.0 (1 lecture opérateur, non-bloquante) | 25, ou 25×K |
| CB | stochastique (défaut Bayesian, bagging_temperature=1 — confirmé sans PG) | 25×K |
- Variance PRIMAIRE = clustered bootstrap sur
(crypto, sub-block), n_cluster=25 — indépendante de la stochasticité, comparable cross-famille. Mode par défaut des 3. - Sub-block — définition précise (committee rec #1, pré-enregistré) : la fenêtre du fold capturé (
fold_id=3) est partitionnée en k=5 sub-blocks temporels CONTIGUS, NON-chevauchants, de longueur égale (bougies H4). Longueur =n_candles_fold // 5, le reste affecté au dernier block. Pas de chevauchement (évite la corrélation inter-blocks qui sous-estimerait l'IC).n_candles_foldest lu du parquet capturé au run-time et loggué (event=s43_subblock_partition n_candles=<N> block_len=<L>), pas codé en dur. - Multi-seed PER-FAMILLE additif : LGB aucun ; CB K=5 ; XGB K=5 ssi <1.0. n_eff asymétrique acté + rapporté par famille.
- Bootstrap de
M(group-level) : chaque resample tire des clusters(crypto, sub-block[, seed]), recompute la courbe net-EV agrégée groupe (micro-average, §1), prend le max.B_canonical = 2000resamples, seed figé (CI95 reproductible + queues stables vu n=25). IC95 surM(percentile). Bit-identical (rng-enfants indépendants — S30 ; clustered PUR, pas moving-block). - Coût compute : LGB 25 fits ; CB+XGB(si stoch.) 25×5. Theta_curve par fit cheap. ~3-5 min/fit warm, parallélisable 5 cell-pods.
Warm-pin reuse from S07¶
s43_io._pin_load/_pin_store ; use_pin=true ; phase_a_should_run(skip_phase_a, use_pin).
4. Files to create / modify¶
À créer (Hamilton-native, /diagnostic-scaffold) : src/commun/finetune/diagnostic/s43_economic_tradability.py (_decide_s43 : family_verdict pseudo-code §1 + combinaison cross-famille + per-asset report) ; hamilton/s43_nodes.py (net_ev_envelope, group_M_bootstrap, subblock_partition[, seed_fits], per_asset_report) ; hamilton/s43_io.py (pin + cost_bps PG + snapshot per-crypto + ATR/price fold) ; dags/dag_diagnostic__s43.py (mirror s42, smoke + micro-smoke §6, ADR-92, quotas) ; tests/unit/test_s43_*.py (verdict pseudo-code incl. M≤0/IC∋0, rate-fail reason, cost_sensitive, tie-break θ*, group bootstrap déterminisme bit-identical, fail-loud coût, cost_bps<0 guard, ADR-0094 Inv 7).
À modifier : xgboost_dag.py (theta_curve + θ-sweep + calibration LGB-like + reason code) ; theta_curve.py (grid-helper optionnel).
5. Risks & threats to validity¶
- 5.1 Coût mal calibré → EV faux. Source PG unique (ADR-59). Sensibilité ±50% rapportée : round-trip 26 bps ⇒ band 13–39 bps. Si le verdict bascule (REFUTED↔NOT) dans la band →
family_verdict = INCONCLUSIVE_COST_SENSITIVE(intégré §1, compten_inc,reason=cost_sensitive). - 5.2 Over-trade trap.
rate_buy(θ*) ≥ 10%conjoint àM>0; sinon NOT_TRADEABLEreason=non_degenerate_rate_failed. - 5.3 Calibration XGB. Pas de prod XGB → LGB-like,
reason xgb_calibration_source=lgb_like_fallback(comparability assumption). Routing pré-engagé (committee rec #6) : si XGB ressortC_REFUTED_TRADEABLE(edge net), le follow-up déploiement (Chapter 5.B) DOIT inclure la validation/raffinement de la calibration prod-grade XGB avant tout ship — le LGB-like n'est qu'un proxy de comparaison (H8), pas une calibration de production. - 5.4 Pin invalidation / feature-name drift (hérité S03/S04).
- 5.5 Sous-puissance + max-bias.
M = max_θ E(θ)sur 19 points à n=25 a un biais positif (concentration sur le max) :C_GENERALISED_NOT_TRADEABLEstrict exigeM_obsnettement < 0 ; pour un C vrai marginal,INCONCLUSIVE_UNDERPOWEREDest l'outcome probable (propriété connue, pas déception). - 5.6 Cost time-decay. Verdict = coûts ACTUELS (snapshot loggué ts), réplicable via le ts.
- 5.7 Réalisme intrabar du triple-barrier (committee rec #5). Le
y_trueque S05 consomme provient du labeling triple-barrier canonique du projet (ATR0.5_1.5_H4) — S05 hérite sa règle intrabar, ne la redéfinit pas. Hypothèse héritée documentée : en cas de SL/TP touchés dans la même bougie, la convention projet (tie-break conservateur — SL-first) s'applique ; le prix de déclenchement est celui de la barrière. Toute imprécision intrabar affecteprecision_buy(θ)de façon identique pour les 3 familles (mêmey_true), donc n'introduit pas de biais inter-familles dans le verdict comparatif. Un raffinement du labeling intrabar est hors-scope (toucherait S03/S06).
6. Success criteria¶
- 3 familles : courbe net-EV(θ) [0.05,0.95] + IC group-level clustered, coût net réel.
_decide_s43:family_verdict(group) → combinaison cross-famille + per-asset report, tous dans l'ensemble pré-enregistré (incl.INCONCLUSIVE_COST_SENSITIVE).net_expectancydocumenté métrique trading-truth primaire ; reason codes émis (non_degenerate_rate_failed,cost_sensitive,xgb_calibration_source,slippage_source).- Verdict généralisation statut C enregistré + decision routing appliqué (Chapter 5.B : NOT_TRADEABLE → S06 features/target ; REFUTED → follow-up déploiement ; INCONCLUSIVE_* → pas de routing). Le verdict alimente la décision Epic, il n'est pas une fin en soi (cohérence Chapitre 1).
Ops (hérité S04 r3 §5.7)¶
- Quotas pods explicites (re-train → +RAM vs s42).
- Smoke pré-merge (2 niveaux) :
- aggrégation :
2 cryptos × 3 familles × 1 seed = 6 runs— exercefamily_verdict× combinaison cross-famille 3-familles. - micro-smoke bootstrap (amendement G) :
1 crypto × 1 famille × 5 sub-blocks × B=20 resamples— exerce la mécanique UQ (recomputeM/resample, clustering sub-block, bit-identical), pas la puissance. - Full post-merge = 3 familles × 5 cryptos × (1|5 seeds) × group bootstrap B_canonical = 2000 (seed figé).
- Pod-failure →
INCONCLUSIVE_TOOLING(jamais raise UI, ADR-25 ;trigger_rule="all_done").
Out-of-scope : promotion prod (ADR-2) ; PTE/feature/split ; re-tune θ prod.
7. Decisions tranchées¶
- Q1
s43: libre. - Q2 Coût PG : clés créées en Console (2026-06-02)
CVN_COST_FEES_BPS=5,CVN_COST_SLIPPAGE_BPS=7,CVN_COST_FUNDING_BPS_H4=1. ⚠️ Valeurs à confirmer réelles (cf. §3) avant le full run — saisies = ordres de grandeur, pas garantis mesurés. - Q3 XGB θ-sweep : harness ;
p_buycalibré LGB-like (reason code). - Q4
rate_min= 10% : §1. - Q5 Calibration : mirror prod ; faute de prod XGB → LGB-like (comparability assumption loggée).
- Q6 Stochasticité : LGB déterministe + CB stochastique tranchés par le code ; reste 1 lecture PG XGB
SUBSAMPLE/COLSAMPLE_BYTREE(non-bloquante, sub-block plancher).
Appendix — Evidence anchors¶
- S32 Loki (2026-06-01) :
theta_curveLGBgross_pre_cost cost_atr=0,tradeable=no, EV>0 à rate_buy 2-5%. - Epic #1055 : C REFUTED CatBoost ; LGB/XGB pending = le gap S05.
- Code :
theta_curve.py(S32, PUR —theta_curvecalculep=precision_buy,breakeven_precision,cost_atr_from_bps(legs=2),best_operating_point), wired CB+LGB ; XGB non-wired, brut. - Scaffold :
s42_*(S04 r3). - Vérifs r1/r2 (2026-06-01/02) :
s43libre ;CVN_COST_*créés ; XGB calibration absente ; LGBbagging_freqjamais passé (déterministe) ; CB défaut Bayesian (stochastique).