research(skh): misura dedicata sugli INGRESSI da barra 230m parziale
Chiude il follow-up dichiarato del 26/07 (misura T1). Il live valuta il segnale
a ogni giro orario del cron su una barra 230m mediamente completa a meta'; il
backtest solo a chiusura di bin. Domanda: di che segno e' il saldo.
Confronto di due path identici in tutto (livelli pct-asimmetrici, uscite
intra-barra, cap max_per_day, fee 0.10% RT) tranne quando si valuta l'ingresso.
ΔSharpe (live - backtest), 3 offset x 2 asset:
-0.01 / +0.04 / +0.44 / +0.32 / +0.59 / +0.98 -> 6/6 non negativi, mediana +0.38
Falsi ingressi ~5/anno/asset; cannibalizzano il cap 2-3 volte in 7 anni.
Meccanismo: Donchian breakout — aspettare la chiusura del bin fa pagare il
movimento gia' avvenuto (ingresso 0.25-0.26% peggiore), e su livelli percentuali
quello 0.26% vale il 6-13% della distanza dallo SL contro il 2.6-3.3% da quella
dal TP -> asimmetria a favore della sopravvivenza del trade.
Due attacchi superati:
- il divario NON e' concentrato: togliendo i 5 giorni migliori si ALLARGA
(ETH@460 35.6x vs 3.0x); e' il backtest il path concentrato;
- nessun look-ahead intra-bin: troncando i 5m alle sole barre gia' chiuse la
risposta e' identica in 289/289 osservazioni (test permanente). Serviva
perche' il self-check valida solo a CHIUSURA di bin.
Verdetto: non e' un difetto da correggere, il verso e' lasciarlo. Ma live e
backtest girano due strategie diverse e la differenza non e' neutra: sommato
alla misura sulle uscite, il path live di SKH01 e' stato modellato in modo
sistematicamente pessimistico su entrambi i lati.
Book, pesi, cron, config INVARIATI.
Due errori di metodo catturati in sessione e codificati come regole:
- un self-check su eventi rari si campiona sugli EVENTI (il primo dava
"80/80 OK" confrontando zeri con zeri, con la ricostruzione rotta);
- un conteggio su segnale grezzo non e' un conteggio di trade (sovrastima
~20x dei falsi ingressi ignorando cap e non-overlap del live).
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,202 @@
|
||||
# 2026-07-26 — SKH01: gli INGRESSI su barra 230m PARZIALE. Misura dedicata.
|
||||
|
||||
**Richiesta:** *"fai la misura sugli ingressi con barra parziale"*.
|
||||
**Follow-up dichiarato che chiude** (registrato in CLAUDE.md stamattina, misura T1):
|
||||
|
||||
> la stessa barra parziale tocca anche gli INGRESSI — `ent[n-1]` puo' venire da un breakout non
|
||||
> confermato a fine barra → se evapora, il book apre e richiude. Su 112 bin campionati:
|
||||
> disaccordo parziale-vs-completa in 1 (~1%), ma con sole 3 entry nel campione la taglia non e'
|
||||
> stimabile … serve una misura dedicata prima di decidere il verso.
|
||||
|
||||
**Script:** `scripts/research/r0726_skh_partial_entry.py` — **test:** `tests/test_skh_partial_entry.py` (8).
|
||||
**Book, pesi, cron: INVARIATI.** Nessuna decisione di allocazione cambia.
|
||||
|
||||
---
|
||||
|
||||
## 0. Il fatto meccanico da cui parte tutto
|
||||
|
||||
`resample_5m(..., origin="epoch")` **non scarta il bin in corso**. Il cron gira a ore piene; una
|
||||
barra SKH01 dura 230 minuti. Quindi a ogni giro il live valuta il segnale su una barra 230m che, in
|
||||
media, e' **completa a meta'**. Il backtest invece valuta solo alle chiusure dei bin.
|
||||
|
||||
Stamattina ho misurato il lato **uscite** di questo fatto (e ho scoperto che il live esce
|
||||
gia' intra-barra, quindi le uscite erano modellate troppo pessimisticamente). Restava il lato
|
||||
**ingressi**, che e' l'opposto: il live puo' entrare su un breakout che a fine barra **non c'e' piu'**.
|
||||
|
||||
La domanda non e' "succede?" (succede per costruzione). E' **di che segno e' il saldo**.
|
||||
|
||||
---
|
||||
|
||||
## 1. Come si misura senza misurare la propria aritmetica
|
||||
|
||||
Il rischio di questa misura non e' sbagliare un numero: e' costruire una ricostruzione rotta e non
|
||||
accorgersene. `signal_at(obs_ms, ...)` ricostruisce il segnale che il live *avrebbe visto* a un
|
||||
istante arbitrario: storia completa fino a li' + barra parziale, passata per i **veri**
|
||||
`htf_features` / `merge_htf_to_ltf` / `atr` su una finestra di 200 barre HTF.
|
||||
|
||||
**Due errori catturati in sessione, entrambi del tipo che passa i test se i test sono pigri:**
|
||||
|
||||
**(a) Self-check vacuo + bug di confine.** La prima versione stampava "BTC 80/80 OK". Era falso su
|
||||
due livelli insieme. `obs_ms // MS_LTF` a un'osservazione *alla chiusura* del bin cadeva nel bin
|
||||
**successivo**, ancora vuoto → aggregato vuoto → `signal_at` ritornava `None` → segnale 0. E il
|
||||
check campionava bin **a caso**: gli ingressi sono ~2% dei bin, quindi confrontava **zeri con zeri**
|
||||
e passava comunque. Le uniche 2 divergenze su ETH erano gli unici 2 bin con un segnale vero — cioe'
|
||||
il campione stava gia' dicendo che era rotto al 100%, non al 2.5%.
|
||||
|
||||
Fix: `((obs_ms - 1) // MS_LTF) * MS_LTF`, e `selfcheck` riscritta per testare **esplicitamente i bin
|
||||
CON ingresso**. Esito corretto: **120/120 su BTC, 120/120 su ETH**. I "falsi allarmi" sui bin flat
|
||||
(17 BTC / 22 ETH su 400) sono bin dove il segnale c'e' ma il backtest lo sopprime col cap
|
||||
`max_per_day=1` — atteso, non un difetto.
|
||||
|
||||
> **Regola:** un self-check su eventi rari va campionato **sugli eventi**, non sulla popolazione.
|
||||
> Un check che confronta zeri con zeri ha potenza zero e sembra un successo.
|
||||
|
||||
**(b) Sovrastima ~20× dei falsi ingressi.** La prima scansione contava **1361** falsi ingressi
|
||||
(204% in piu' dei trade veri, costo stimato −3.03%/anno di sleeve). Numero **sbagliato**: `signal_at`
|
||||
riporta il compositore **grezzo**, mentre il live applica anche il cap `max_per_day=1` e il
|
||||
non-overlap. La spia era nella sezione D della stessa stampa — **305 giorni** con un falso ingresso a
|
||||
fronte di 663 "eventi": con cap 1/giorno non possono essere eventi indipendenti. Riscritta come
|
||||
**macchina a stati sequenziale** fedele al live: i falsi ingressi veri sono **~5/anno/asset**.
|
||||
|
||||
> **Regola:** un conteggio di eventi su un segnale grezzo non e' un conteggio di **trade**. Prima di
|
||||
> monetizzare un evento, farlo passare per gli stessi filtri che ha il live.
|
||||
|
||||
---
|
||||
|
||||
## 2. Il risultato
|
||||
|
||||
Due path identici in tutto (stessi livelli SKH01_V2_DD pct-asimmetrici, stesse uscite intra-barra,
|
||||
stesso cap, stesse fee 0.10% RT) tranne **quando si valuta l'ingresso**:
|
||||
|
||||
- **LIVE** — segnale valutato a ogni confine orario interno al bin (= cio' che fa il cron oggi);
|
||||
- **BACKTEST** — segnale valutato solo alle chiusure dei bin (= cio' che il backtest assume).
|
||||
|
||||
| asset | offset | path | equity× | trade | falsi | Sharpe |
|
||||
|---|---:|---|---:|---:|---:|---:|
|
||||
| BTC | 0 | LIVE | 6.58 | 249 | 29 | **1.07** |
|
||||
| BTC | 0 | BACKTEST | 6.28 | 211 | 0 | 1.08 |
|
||||
| ETH | 0 | LIVE | 6.09 | 285 | 39 | **0.95** |
|
||||
| ETH | 0 | BACKTEST | 5.44 | 236 | 0 | 0.91 |
|
||||
| BTC | 230 | LIVE | 27.10 | 254 | 20 | **1.63** |
|
||||
| BTC | 230 | BACKTEST | 8.32 | 226 | 0 | 1.19 |
|
||||
| ETH | 230 | LIVE | 11.47 | 283 | 34 | **1.20** |
|
||||
| ETH | 230 | BACKTEST | 5.08 | 241 | 0 | 0.88 |
|
||||
| BTC | 460 | LIVE | 17.53 | 258 | 18 | **1.44** |
|
||||
| BTC | 460 | BACKTEST | 4.29 | 236 | 0 | 0.85 |
|
||||
| ETH | 460 | LIVE | 72.87 | 281 | 17 | **1.93** |
|
||||
| ETH | 460 | BACKTEST | 6.12 | 253 | 0 | 0.95 |
|
||||
|
||||
**ΔSharpe (live − backtest): −0.01, +0.04, +0.44, +0.32, +0.59, +0.98 → 6/6 non negativi,
|
||||
mediana +0.38.**
|
||||
|
||||
⚠️ **I multipli d'equity NON sono ritorni dello sleeve.** Questo simulatore compone per-trade senza
|
||||
vol-target, senza pesi di book, a nozionale unitario. Servono solo a confrontare due path che
|
||||
differiscono per una cosa sola. La lente onesta qui e' lo **Sharpe** (che non compone), e i multipli
|
||||
sono la sua integrazione su 7 anni.
|
||||
|
||||
**Split in-sample / hold-out (offset 0, dove ho le serie):**
|
||||
|
||||
| asset | path | FULL | IS | HOLD |
|
||||
|---|---|---:|---:|---:|
|
||||
| BTC | LIVE | 1.07 | 1.07 | **1.12** |
|
||||
| BTC | BACKTEST | 1.08 | 1.15 | 0.71 |
|
||||
| ETH | LIVE | 0.95 | 0.93 | 1.03 |
|
||||
| ETH | BACKTEST | 0.91 | 0.88 | 1.03 |
|
||||
|
||||
All'offset 0 il FULL e' un pareggio ma l'hold-out favorisce il live su BTC (**+0.41**) e pareggia su
|
||||
ETH. Coerente col fatto noto che **offset 0 e' l'ancora fortunata del backtest** (93-98° pctl sui 23
|
||||
offset a priori): e' esattamente l'ancora dove il path canonico ha meno da guadagnare.
|
||||
|
||||
---
|
||||
|
||||
## 2-bis. I due controlli che il risultato doveva superare
|
||||
|
||||
Un +0.98 di Sharpe e un 72.87× di equity sono il tipo di numero che va attaccato prima di crederci.
|
||||
|
||||
**(a) E' concentrato in pochi giorni?** No — e il controllo va nella direzione **opposta** al sospetto:
|
||||
|
||||
| offset 460 | ret/trade | win rate | top-5 gg = % del log-equity | equity senza i top-5 |
|
||||
|---|---:|---:|---:|---:|
|
||||
| ETH LIVE | +1.878% | 48.6% | **16.7%** | 35.64× |
|
||||
| ETH BACKTEST | +0.946% | 40.6% | **39.5%** | 2.99× |
|
||||
| BTC LIVE | +1.307% | 46.1% | **22.5%** | 9.20× |
|
||||
| BTC BACKTEST | +0.801% | 44.0% | **38.3%** | 2.46× |
|
||||
|
||||
Togliendo i 5 giorni migliori il divario si **allarga** (ETH 35.6× vs 3.0×). E' il **backtest** il
|
||||
path concentrato (~39% del log-equity in 5 giorni). La differenza vive nella **distribuzione**:
|
||||
ret/trade circa doppio e win rate +8pp (ETH) / +2pp (BTC).
|
||||
|
||||
**(b) C'e' look-ahead intra-bin?** E' il rischio serio, perche' il self-check valida la ricostruzione
|
||||
solo **a chiusura di bin** — un leak che esistesse solo intra-bin gli sarebbe **invisibile per
|
||||
costruzione**. Test decisivo: ricalcolare ogni osservazione intra-bin con la serie 5m **troncata**
|
||||
alle sole barre gia' chiuse a `obs_ms`. Se il futuro contasse, la risposta cambierebbe.
|
||||
|
||||
> **BTC 145/145 e ETH 144/144 osservazioni intra-bin: risposta identica dopo il troncamento**, e il
|
||||
> prezzo d'ingresso coincide sempre con l'ultimo close 5m gia' chiuso (241/241).
|
||||
|
||||
Cablato come test permanente (`test_nessun_lookahead_intra_bin`).
|
||||
|
||||
---
|
||||
|
||||
## 3. Il meccanismo (perche' vince, e perche' non e' una sorpresa)
|
||||
|
||||
Sezione B della scansione: quando il segnale c'e' **anche** a fine bin, il live entra in anticipo di
|
||||
**180 minuti mediani** a un prezzo **0.25-0.26% migliore** in media (0.11-0.12% mediano).
|
||||
|
||||
SKH01 e' un **Donchian breakout**. Aspettare fino a 230 minuti la chiusura del bin significa, sui
|
||||
casi che confermano, **pagare il pezzo di movimento gia' avvenuto**. Il costo di quell'attesa e'
|
||||
sistematico; il beneficio (evitare i breakout che evaporano) e' reale ma raro — **~5 eventi/anno/asset**
|
||||
dopo i filtri del live, perche' il cap `max_per_day=1` e il non-overlap assorbono quasi tutto.
|
||||
|
||||
E l'effetto si amplifica sui **livelli**, che sono percentuali sull'ingresso (long sl4%/tp10%, short
|
||||
sl2%/tp8%). Entrare 0.26% piu' avanti nella direzione del breakout sposta TP **e** SL dello stesso
|
||||
0.26% — ma il TP e' a 4-10% e lo SL a 2-4%: la stessa quantita' assoluta vale il **6-13% della
|
||||
distanza dallo SL** contro il 2.6-3.3% della distanza dal TP. Il vantaggio d'ingresso e' quindi
|
||||
**asimmetrico a favore della sopravvivenza del trade**: e' il canale che spiega il win rate piu' alto,
|
||||
non un giorno fortunato.
|
||||
|
||||
**La cannibalizzazione del cap non morde:** ingressi veri bloccati da un falso ingresso = **2 su BTC
|
||||
(0.6%) e 3 su ETH (0.9%)**.
|
||||
|
||||
---
|
||||
|
||||
## 4. Verdetto
|
||||
|
||||
**L'ingresso su barra parziale NON e' un difetto da correggere. Il verso del follow-up e': lasciarlo.**
|
||||
|
||||
Ma la lettura che conta e' la seconda:
|
||||
|
||||
> **Il live e il backtest stanno girando due strategie diverse, e la differenza non e' neutra.**
|
||||
|
||||
Tutti i numeri di SKH01 nel progetto — Sharpe standalone, il peso 25% del book Deribit, l'audit
|
||||
d'ancora del 02/07, la conferma di peso/cadenza del 24/07 — sono calcolati sul path **a chiusura di
|
||||
bin**, che non e' quello che gira. La misura dice che il path reale e' **≥** in 6/6 coppie
|
||||
asset×offset. Insieme alla misura di stamattina sulle uscite (il live esce gia' intra-barra, +0.081
|
||||
Sharpe FULL di book sottostimato), **il path live di SKH01 e' stato modellato in modo
|
||||
sistematicamente pessimistico su entrambi i lati**.
|
||||
|
||||
**Cosa NON concludo.** Che "l'ingresso intra-bin e' un miglioramento validato". Non ha passato
|
||||
nessuno dei gate del progetto: e' stato *misurato* sugli stessi 7 anni su cui SKH01 e' stato
|
||||
selezionato e affinato, non e' passato per `study_family_honest` ne' per un deflated-Sharpe, e la
|
||||
taglia varia molto fra ancore (da −0.01 a +0.98). Promuoverlo a meccanismo canonico sarebbe
|
||||
esattamente la selezione-sull'hold-out che il progetto ha gia' codificato come gate.
|
||||
|
||||
E non serve promuoverlo: **e' gia' cio' che il live fa**. L'azione richiesta e' l'opposta — smettere
|
||||
di trattare il numero del backtest come l'aspettativa del live, e non "riparare" il live per farlo
|
||||
somigliare a un backtest peggiore.
|
||||
|
||||
**Nessun cambio a book, pesi, cron, config.**
|
||||
|
||||
---
|
||||
|
||||
## 5. Regole nuove
|
||||
|
||||
1. **Un self-check su eventi rari si campiona sugli EVENTI.** Confrontare zeri con zeri ha potenza
|
||||
zero e passa. (Costo: un "80/80 OK" completamente falso.)
|
||||
2. **Un conteggio di eventi su segnale grezzo non e' un conteggio di trade.** I filtri del live
|
||||
(cap giornaliero, non-overlap) vanno applicati prima di monetizzare qualsiasi evento. (Costo:
|
||||
sovrastima 20× e un costo inventato di −3%/anno di sleeve.)
|
||||
3. **Quando live e backtest differiscono, misurare il DELTA con machinery identica** — un solo grado
|
||||
di liberta' cambiato — e quotarlo sulla **banda d'ancora**, non su un'ancora sola. All'ancora
|
||||
canonica questo effetto vale ~0; sulla banda vale +0.38 mediano. Su un'ancora sola avrei
|
||||
concluso "irrilevante".
|
||||
Reference in New Issue
Block a user