Files
PythagorasGoal/docs/diary/2026-06-29-intraday-regime.md
T
Adriano Dal Pastro b4ec92734c research(intraday): de-bias del lead ERM (filone B) — falso positivo + gate selection-on-holdout
L'analisi di robustezza affonda lo "earns_slot=True" di ERM: era prodotto da
selezione-sull'hold-out + coda 2026 + multiple-testing non corretto.
  A) deflated-Sharpe FAIL: 0.00 (tutti 122 trial) / 0.16 (no-TOD) / 0.24 (solo-ERM) << 0.95
  B) selezione in-sample-only -> ALTRA cella (long-flat, corr->TP01 0.53) = NEUTRAL, no slot
  C) ensemble del plateau (no cherry-pick) -> ADDS ma robust_oos=False -> no slot
  D) uplift FULL solo +0.10, negativo 2021/2022; uplift HOLD +0.30 concentrato nel 2026
=> ERM SCARTATO come sleeve. Conferma ennesima del soffitto BTC/ETH-direzionale ~1.3.

Lezione CODIFICATA in altlib (LESSON 4, test in tests/test_harness_realism.py):
  - deflated_sharpe()       Bailey & Lopez de Prado, PASS >= 0.95
  - select_cell_insample()  scelta cella col solo Sharpe pre-HOLDOUT (no peeking)
  - study_family_honest()   gate combinato: earns_slot[cella in-sample] AND DSR>=0.95
Regola: una strategia direzionale grid-searched si giudica con study_family_honest,
non chiamando study_marginal sulla cella a max hold-out. Verificato end-to-end su ERM
(earns_slot_honest=False). Chiude il punto cieco gemello di CC01.

Diario aggiornato (verdetto downgrade), CLAUDE.md aggiornato. Test 119/119 verdi.
Nessun impatto live (branch separato).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-29 20:04:36 +00:00

10 KiB
Raw Blame History

2026-06-29 — Filone B: INTRADAY REGIME BTC/ETH — ERM (efficiency-ratio) è un LEAD reale, forward-monitor

Tesi. Cercare un meccanismo sub-daily sui dati certificati 1h→12h BTC/ETH che sia ortogonale sia a TP01 (TSMOM trend daily, long-flat) sia a SKH01 (regime BuzVola/BuzVolume + Donchian a 230m). SKH01 prova che il sub-daily PUÒ funzionare ed essere quasi-ortogonale: qui un MECCANISMO DIVERSO, basato sulla qualità del moto intraday (efficiency-ratio / vol-expansion / thrust) come regime che condiziona una posizione direzionale tenuta ~1 giorno. Script scripts/research/intraday_regime.py, test tests/test_intraday_regime.py. Branch research/equity-meanrev-ib (separato, nessun impatto live).

Killer ricorrente del progetto sotto le 12h = muro-fee 0.10% RT + overfitting. Ricetta SKH01: decisione sub-daily ma hold ~1 giorno → pochi trade → la fee non uccide. Ogni meccanismo qui è costruito a basso turnover e giudicato col fee-sweep alla sua frequenza reale.

Meccanismi provati (tutti come posizione CONTINUA decisa ≤ close[i], causali)

meccanismo
ERM Efficiency-Ratio regime momentum (Kaufman): ER = |moto netto su L barre| / |percorso|. Prendi la direzione del moto netto solo quando ER ≥ soglia (regime intraday "pulito"/trendy), altrimenti flat
VEM Vol-Expansion Momentum: direzione = segno del moto, attiva solo quando vol-corta > vol-lunga
VBR Volatility/thrust breakout (Larry-Williams ROLLING, no calendario): segui i moti > k·ATR
TOD Time-of-day seasonality — CONTROLLO calendario, incluso APPOSTA per day_boundary_robust

Selezione + fee-sweep a frequenza reale (vincitori per famiglia, min-asset)

ERM: 8h  L=2.0 thr=0.35 L/S   minFull +0.88  minHold +0.99  f@.20 +0.55  feeOK=True  turn/y 125
VEM: 12h Lmom=3 Ls=2 Ll=10    minFull +0.72  minHold -0.29  f@.20 +0.51  feeOK=True   (hold<0 → out)
VBR: 12h k=1.0 atr=14 L/S     minFull +0.33  minHold +1.20  f@.20 +0.25  feeOK=True
TOD: 1h                       minFull -3.99  minHold -4.49  feeOK=False              (controllo → FAIL atteso)

Solo ERM e VBR sopravvivono al gate fee+OOS. VEM ha hold-out negativo. TOD esplode (vedi sotto).

Il candidato: ERM 8h L=2.0 thr=0.35 (L/S) — earns_slot=True

abs=PASS  marginal=ADDS  EARNS_SLOT=True
standalone: cand FULL 0.919 / HOLD 1.105   (TP01 ref FULL 1.297 / HOLD 0.303)
corr->TP01 full 0.146  hold 0.057   beta 0.197   resid Sharpe 0.738   alpha/yr 0.121
robust_oos=True (clean-year +0.103, drop-best-month +0.283)
multi-cut persistence: 2020 .166 / 2021 .167 / 2022 .298 / 2023 .337 / 2024 .418 / 2025 .497 / 2026 2.861  persistent=True
has_insample_edge=True  (standalone PRE-holdout Sharpe 0.874, > null-pctl 0.317)
is_hedge=False  (yearly corr(TP01-Sh, uplift) -0.762; paga sia TP01-up 0.139 sia TP01-down 0.384)
blend w25: FULL 1.481 (+0.184)  HOLD 0.80 (+0.497)  DD 11.0%
corr con SKH01: full 0.278  hold 0.291
day_boundary=INVARIANT (spread 0.0)   haircut $600 ≈ 0.00 su BTC/ETH

È il primo sub-daily da SKH01 a passare TUTTI i gate del marginal scorer indurito: edge in-sample reale (non diversification-math), persistenza multi-cut, robust_oos, NON hedge, scorrelato a TP01 (0.15).

ERM aggiunge OLTRE SKH01? (gate #5 — il book esistente ne ha già uno sub-daily)

blend FULL Sh FULL DD HOLD Sh HOLD DD
TP01 +1.30 14.3% +0.30 7.5%
TP01+SKH 75/25 +1.78 9.4% +1.17 6.2%
TP01+SKH+ERM 60/25/15 +1.88 8.9% +1.46 5.3%
TP01+SKH+ERM 55/20/25 +1.84 8.8% +1.48 5.1%

Sì: corr con SKH01 solo 0.28 → ERM aggiunge oltre SKH (FULL +0.10, HOLD +0.29 a peso 15%, DD ancora giù). Non è SKH01 travestito.

Il controllo TOD (calendario) — fa esattamente ciò che doveva

TOD (direzione per ora-del-giorno, media espandente causale) è incluso come trappola: è il tipo di effetto che uccise open_drive (artefatto di etichettatura UTC). Esito: FAIL (FULL 3.99, 31.811 trade, fee-killed), marginal=DILUTES. day_boundary_robust=INVARIANT → l'effetto è robustamente negativo a ogni offset (non un artefatto di confine giorno: è proprio che la time-of-day-direzionale non ha edge e sanguina fee). Il controllo conferma che l'harness non si fa ingannare e che il segno dei segnali di prezzo (ERM/VBR) è reale, non rumore di calendario.

Causalità / eseguibilità

  • Leak-free: causality_ok=True, max_tail_diff 0.0 su tutti i candidati (ER, rank espandente, medie su prefisso = identiche → nessun future-peeking). Test dedicato in test_intraday_regime.py.
  • day_boundary_robust=INVARIANT (spread 0.0) per ERM/VBR/TOD: segnali di prezzo, non di calendario.
  • Eseguibile a $600: eval_weights_smallcap haircut ≈ 0.00 su BTC ed ETH. MA ERM a 8h fa ~3.158 trade BTC / ~2.823 ETH su tutta la storia (turnover 125/anno): haircut nullo nel modello, ma l'esecuzione reale sub-daily sul book è operativamente più pesante di un segnale 1d (slippage/spread intraday non interamente catturati dalla fee proporzionale).

Caveat (perché LEAD, non sleeve)

  1. Plateau hold-out a UNA SOLA RIGA. Il FULL è robusto su tutta la griglia L∈[2.0,3.0] (+0.6..+1.0), ma l'hold-out è positivo SOLO a L=2.0 (a L=2.5/3.0 crolla a 0.5..0.8). Il plateau sul full è ampio, quello che conta — l'hold-out — è single-row. Da rinforzare prima di credere alla stazionarietà.
  2. Standalone FULL 0.92 < soffitto ~1.3. Coerente col soffitto direzionale BTC/ETH: il valore di ERM è marginale/diversificante (corr 0.15 a TP01, 0.28 a SKH01), non assoluto. Non rompe il soffitto.
  3. Multiple-testing non deflazionato. 102 celle testate (60 ERM + 16 VEM + 24 VBR + 2 TOD) senza deflated-Sharpe (a differenza del filone C). Il multi-cut 2026 = +2.861 è una manciata di giorni che gonfia. Storia sub-daily certificata utile ~quanto SKH01 → finestra non lunghissima.
  4. Esecuzione 8h = complessità operativa reale (vedi sopra), oltre il modello a haircut nullo.

Analisi di robustezza / de-bias (intraday_regime_analysis.py) — il lead NON regge

I caveat #1 (plateau hold-out single-row) e #3 (multiple-testing) erano i sospetti giusti. Tre test di de-bias li trasformano da sospetto in bocciatura dello slot:

test esito
A) Deflated-Sharpe (Bailey & Lopez de Prado) su 122 trial cercati FAIL. DSR 0.000 (tutti) / 0.163 (escludendo i trap TOD) / 0.241 (solo-ERM) — tutti ≪ 0.95. Lo Sharpe winner (0.92) è sotto lo Sharpe-max-atteso-null (1.162.51): il search ha trovato celle a 1.6 full / 1.7 in-sample, il winner 0.92 non è eccezionale.
B) Selezione IN-SAMPLE-only (scelgo la cella ERM col solo Sharpe < 2025) earns_slot=False. La cella migliore pre-hold-out è un'ALTRA (8h L=2.0 thr=0.4 long-flat), con corr→TP01 0.53 (è trend-beta travestito) → marginal=NEUTRAL. Il winner max-hold non si seleziona senza guardare l'hold-out → il suo earns_slot=True era selezione-sull'hold-out.
C) Ensemble del plateau (media 20 celle L×thr, niente cherry-pick) earns_slot=False. marginal=ADDS, in-sample Sh 1.01, corr→TP01 0.18 — ma robust_oos=False (clean-year + jackknife): l'uplift hold-out è trascinato dal 2026 (+2.09 multicut), manciata di giorni.

Dove vive l'(eventuale) edge (per-anno, blend 3-way 60/25/15 vs 2-way 75/25): uplift FULL solo +0.10, negativo nel 2021 (0.23) e 2022 (0.15), positivo altrove; l'uplift HOLD +0.30 è concentrato nel 2026 (+0.46). corr(ERM,SKH) 0.28 full (fino a 0.42 in alcuni anni) → parziale sovrapposizione con SKH, non ortogonalità piena.

Lettura. Il segnale efficiency-ratio non è rumore puro (l'ensemble ha in-sample Sh ~1.0, positivo nella maggior parte degli anni), ma come slot fallisce ogni de-bias: il earns_slot=True della scoperta era prodotto da (1) selezione della cella sull'hold-out + (2) coda 2026 + (3) multiple-testing non corretto. È lo stesso falso-positivo che l'alt-sweep 100-agent imparò a uccidere — qui ucciso dai gate.

Caveat originari (ora risolti dall'analisi sopra)

  1. Plateau hold-out single-rowconfermato fatale: l'edge hold-out a L=2.0 è cell-selection.
  2. Multiple-testing non deflazionatodeflazionato: DSR FAIL anche senza i trap.
  3. Esecuzione 8h: irrilevante ormai (lo slot non c'è).

Verdetto onesto: NON è uno slot. Falso positivo da selezione-hold-out + coda 2026. SCARTATO come sleeve.

Lo "earns_slot=True" della scoperta non sopravvive alla de-selezione: deflated-Sharpe FAIL (anche escludendo i controlli), selezione in-sample-only → NEUTRAL su un'altra cella (trend-beta corr 0.53), ensemble del plateau → robust_oos FAIL. Conferma ennesima del soffitto direzionale BTC/ETH ~1.3: un segnale a 2 asset non lo supera; la via resta il cross-sectional (XS01). Resta al più una curiosità in forward-monitor (l'efficiency-ratio ha un debole edge in-sample reale), ma da non armare come alpha. Gli altri tre meccanismi: VBR=NOISE, VEM hold-out<0, TOD=controllo FAIL come atteso.

Lezione harness (CODIFICATA). Lo script di scoperta selezionava il vincitore per min_hold massimo sulla griglia = selezione-sull'hold-out, il punto cieco che ha generato il falso PASS. Il marginal scorer da solo non basta se la cella è scelta guardando l'hold-out: serve (a) scegliere la cella in-sample-only (o walk-forward) prima di valutare il marginal, e (b) deflazionare per il numero di celle/famiglie testate. Stesso buco segnalato per CC01 ("Sharpe implausibile") e per l'alt-sweep (hold-out-fitting): qui in forma "selection-on-holdout". Ora è codice in altlib (LESSON 4): tre funzioni nuove — deflated_sharpe() (Bailey & Lopez de Prado), select_cell_insample() (scelta cella col solo Sharpe pre-HOLDOUT), e il gate combinato study_family_honest() (earns_slot_honest = earns_slot[cella in-sample] AND deflated-Sharpe≥0.95). Verificato: su ERM il gate ritorna earns_slot_honest=False (cella in-sample = trend-beta NEUTRAL, DSR 0.74). Analisi completa in scripts/research/intraday_regime_analysis.py; test in tests/test_harness_realism.py. Regola nuova: una strategia direzionale grid-searched si giudica con study_family_honest, non chiamando study_marginal sulla cella a max hold-out.

Nessun impatto sul book live (branch separato, config canonica invariata).