Files
PythagorasGoal/docs/diary/2026-07-26-skh-partial-entry.md
T
Adriano Dal Pastro c8d31bc944 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>
2026-07-25 22:12:42 +00:00

203 lines
11 KiB
Markdown
Raw 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-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".