Files
PythagorasGoal/docs/diary/2026-06-29-intraday-regime.md
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

146 lines
10 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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-row~~**confermato fatale**: l'edge hold-out a L=2.0 è cell-selection.
2. ~~Multiple-testing non deflazionato~~**deflazionato: 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).