b4ec92734c
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>
146 lines
10 KiB
Markdown
146 lines
10 KiB
Markdown
# 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.16–2.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).
|