research(wave-0822): BIN-FREQ — direzione reale (0/72 sul segno inverso), taglia = selezione; e due premesse del briefing refutate

This commit is contained in:
Adriano Dal Pastro
2026-08-22 21:22:43 +00:00
parent 74539db8b8
commit fd37add518
2 changed files with 117 additions and 23 deletions
+73 -1
View File
@@ -697,7 +697,7 @@ stessi hanno nominato senza poterli eseguire**. Stesso contratto di consegna, st
| 22 | MAKER | l'esecuzione passiva e' una fonte di ritorno, al netto del costo di non essere eseguiti? | **SCARTATO** — il segno dipende da `<` contro `<=` |
| 23 | BOCPD | un rilevatore di cambio di regime vero batte il miglior lookback COSTANTE? | **SCARTATO** — filone chiuso definitivamente |
| 24 | CRITICO | cosa NON e' stato misurato, e quale singola misura mancante vale di piu' | **3 correzioni all'ondata**, 1 regola mia ritirata |
| 25 | BIN-FREQ | il binario di frequenza su SKH01 e' un candidato o un sottoprodotto? | _in corso_ |
| 25 | BIN-FREQ | il binario di frequenza su SKH01 e' un candidato o un sottoprodotto? | **LEAD** — direzione reale, taglia = selezione |
| 26 | TP01-SINISTRO | quanto pesa TP01 se l'obiettivo include un crash che nel campione non c'e'? | **allarme SCARTATO** — il rischio e' XS01, non TP01 |
| 27 | SURFACE-RV | la superficie e' internamente incoerente, e l'incoerenza vale piu' del costo di attraversarla? | **SCARTATO** — l'incoerenza al mid **e'** la larghezza del mercato |
@@ -1157,3 +1157,75 @@ chiusure**, e i close giornalieri non lo vedrebbero.
⚠️ *Nota di attribuzione: il file risultava `M` in git perche' **io** (coordinatore) l'avevo incluso
in un commit mentre l'agente ci stava ancora lavorando — non un altro agente. Un `git add -A` durante
un'ondata cattura il lavoro in corso.*
---
## 25 — BIN-FREQ (il binario di frequenza su SKH01, giudicato come candidato)
`scripts/research/r0822b_bin_freq.py` (~400 s, **23 ancore piene**, due esecuzioni indipendenti con
output **numericamente identici**). **144 celle** dichiarate nel docstring **prima di guardare**, piu'
i **71 trial gia' spesi sullo stesso oggetto** (28 VOL-SIZE + 43 scettico) → **conto onesto = 215**:
*riaprire un parametro riapre la sua famiglia.*
**Repliche bit-exact** (`max|dif| = 0`) contro `sleeves._skyhook_returns()` **e** contro
`r0702_anchor_skh01.run_asset`; **titolo dello scettico riprodotto esattamente** chiamando la sua
funzione (`BIN L>=med` +0,080 in 20/23, inverso 0,159 in 0/23).
| | ShFULL | ShHOLD | maxDD | CAGR |
|---|---|---|---|---|
| BASE (libro live 75/25) | 1,626 | 1,037 | 10,4% | 18,6% |
| BIN, peso effettivo lasciato cadere | 1,690 | 1,031 | 10,3% | 17,8% |
| **BIN, controllo ISO-PESO** | **1,589** | 0,956 | 10,0% | 16,8% |
📌 **La statistica che decide non e' l'argmax, e' la MEDIANA DI FAMIGLIA.** Argmax
(`q0,50 w60 h0,10 s+`) ISO **+0,112 in 23/23**; **stima onesta di famiglia +0,007**, con **39/72
celle positive = 54% ≈ monetina**. **L'argmax vale 16× la mediana → punta, non plateau.**
**Controllo di segno: 0/72 celle positive** sulla meta' inversa → la *direzione* e' solidissima.
**Gate:** `marginal_vs_tp01` **ADDS**; selezione **in-sample-only** sceglie la stessa cella mentre
scegliendo **sull'hold-out** ne esce un'altra (0,017) → **il gate ha potenza**;
**`weights_tilt_null` FAIL** (pctl 36-38) **e per giunta inerte** (il binario *abbassa* il peso
effettivo 0,250→0,19, e nella forma di deploy naturale il vettore proposto e' identico a quello
corrente); causalita' verificata con **troncamento diretto** su 52 decisioni, 0 divergenti;
**trasferisce a SKH01_V1** (geometria d'uscita diversa): +0,087 in 20-21/23.
**Il deflated-Sharpe NON e' vacuo — refuta l'ipotesi a-priori che l'agente aveva registrato:**
**0,976** (N=144) / **0,977** (N=215, conto onesto) / 0,948 (N=500), baseline 0,828/0,834. E il
**motivo e' riusabile**: il DSR e' vacuo quando i trial si somigliano (var→0 ⇒ sr0→0 ⇒ DSR≈1 *per
chiunque*); una famiglia **binaria** contiene celle che scartano insiemi di trade molto diversi,
quindi **riacquista potenza**. *Non e' il binario a essere piu' solido: e' la famiglia a essere piu'
larga.* ✅ Terza misura convergente della stessa giornata sul difetto del DSR (con BOCPD e CRITICO).
⚠️ **Il null "con potenza" ha una mediana che NON e' zero, ed e' un fatto di misura:** scartare a caso
il 40% dei trade **danneggia**, perche' lo Sharpe annualizzato va con √(scommesse/anno) — misurato:
gamba 1,107 → **0,883** permutata contro **0,861** previsti da √0,60. Il null resta valido e **piu'
severo** (reale +0,112 vs massimo del null 0,031, p<0,01), **ma risponde a "batte una selezione
casuale?", non a "batte il non filtrare?"**.
🚨 **Due premesse del MIO briefing misurate e REFUTATE:**
(i) *"il binario dimezza gli ordini → migliora l'eseguibilita' e riduce le fee"* — gli ordini
**nettati** calano del **17%**, non del 50%; i sotto-min-order **non si muovono** perche' sono di
TP01; e l'**haircut a $635 e' 0,001 in entrambe le braccia**: non c'era niente da guadagnare.
(ii) *"converti il risparmio di fee in Sharpe con la curva del 26/07"* — **a fee ZERO l'effetto resta
l'87-110%** → non e' risparmio di costo, e' **informazione sul rendimento lordo**, e convertirlo
sarebbe stata un'attribuzione sbagliata.
**VERDETTO: LEAD** (non candidato, non scartato). La **direzione** e' reale e sopravvive a tutto; la
**taglia di una singola cella e' selezione**.
📌 **E la domanda che avevo posto — "+0,080 contro ±0,196 di banda d'ancora, merita?" — era il
confronto SBAGLIATO** (la banda del *livello* si cancella in un disegno appaiato), **ma la risposta
corretta e' PEGGIORE per il candidato**. Criterio dichiarato, 4 gambe: (1) mediana di famiglia ≥ meta'
della cella e >65% celle positive → **FAIL (6%, 54%)**; (2) `weights_tilt_null`**FAIL/vacuo**;
(3) marginale + null con potenza → PASS; (4) taglia ≥ €0,10/giorno per pagare un modo di fallire in
piu' → **FAIL: €0,0113/giorno** ($4,79/anno a $635).
Due prove ausiliarie che chiudono il caso: **due implementazioni difendibili della stessa regola**
(d'accordo sul 97,6% dei trade) danno **+0,066 contro +0,080** — *il 21% dell'effetto sta nel come si
riempie il warm-up*; e **versare €500/mese vale ~1456× l'effetto misurato**.
**Libro, pesi, cron, config INVARIATI.**
📌 **Riapertura pre-registrata: non una data, una SOGLIA DI CAPITALE** — cio' che fallisce e' il
rapporto fra un costo operativo **fisso** e un beneficio **proporzionale**, e nessun dato in piu' lo
cambia (con SE(Sharpe)≈1,4 su 6 mesi nessun forward misura 0,07). €0,10/giorno richiede **~$5.600**
alla stima della cella e **~$53.000** a quella onesta di famiglia → si rivaluta alla revisione dei
**$20k** gia' in calendario, **e solo se la cella e' giustificata da un MECCANISMO** invece che
dall'argmax di una griglia.
+44 -22
View File
@@ -56,8 +56,12 @@ Nessuna riscrittura: `extract / equity_daily / leg_daily / sizes_from_L / book /
sono IMPORTATE da r0822_vol_size, e il titolo dello scettico e' riprodotto chiamando la SUA
funzione. Replica bit-exact contro `sleeves._skyhook_returns()` prima di ogni delta (§0).
USO: nice -n 19 timeout 900 uv run python scripts/research/r0822b_bin_freq.py [--every K]
--every 8 -> 3 ancore (pilota). default: tutte e 23.
USO: nice -n 19 timeout 900 uv run python scripts/research/r0822b_bin_freq.py
[--every K] 1 ancora ogni K delle 23 (K=8 -> 3 ancore, pilota ~80 s)
[--ndraw N] estrazioni del null di permutazione (default 100)
Con i default gira in ~400 s e ~500 MB di RSS su questa VPS (2 core). Tutti i numeri
riportati qui vengono da questa invocazione, e due esecuzioni indipendenti hanno dato
output numericamente IDENTICI.
"""
from __future__ import annotations
@@ -192,9 +196,12 @@ def iso_pair(TP: pd.Series, leg: pd.Series, leg_base: pd.Series):
def main() -> None:
ap = argparse.ArgumentParser()
ap.add_argument("--every", type=int, default=1)
ap.add_argument("--ndraw", type=int, default=120)
ap.add_argument("--ndraw", type=int, default=100)
args = ap.parse_args()
offs = OFFSETS_FULL[:: max(1, args.every)]
if len(offs) < 3:
raise SystemExit(f"--every {args.every} lascia {len(offs)} ancore: "
"`A.anchor_luck_delta` ne vuole almeno 3 (usa --every <= 8).")
t0 = time.time()
print("=" * L)
@@ -772,11 +779,18 @@ def main() -> None:
print(f" (RSS {rss_mb():.0f} MB)")
f_mod = FEEDEC[(f"MODELLATA {keep_fee*1e4:.0f} bps RT (i backtest)", nmcan)]
f_zero = FEEDEC[("fee ZERO (informazione pura)", nmcan)]
print(f" -> a fee ZERO resta {f_zero:+.3f} di {f_mod:+.3f} = "
f"{100*f_zero/f_mod if f_mod else float('nan'):.0f}% dell'effetto: la parte restante")
print(" e' fee risparmiata. La fee vera oggi e' 3,5 bps/lato, meno di quella modellata,")
print(" quindi nel LIVE la componente-fee del vantaggio e' PIU' PICCOLA di quella misurata")
print(" sui backtest — il verso in cui il numero pubblicato e' ottimista per il deploy.")
quota_info = 100.0 * f_zero / f_mod if f_mod else float("nan")
print(f" -> a fee ZERO resta {f_zero:+.3f} di {f_mod:+.3f} = {quota_info:.0f}% dell'effetto.")
if quota_info >= 85.0:
print(" ❌ IPOTESI REFUTATA (era la mia, ed era anche quella del compito): il vantaggio")
print(" NON e' fee risparmiata. A fee zero l'effetto c'e' tutto, quindi scartare i")
print(" trade in alta vol e' INFORMAZIONE sul rendimento lordo, non un risparmio di")
print(" costo. Conseguenza pratica: NON si puo' replicare abbassando il turnover, e")
print(" NON va convertito con la curva fee del 26/07 (sarebbe doppio conteggio in una")
print(" direzione e attribuzione sbagliata nell'altra).")
else:
print(" Una parte e' fee risparmiata; la fee vera (3,5 bps/lato) e' piu' bassa di quella")
print(" modellata, quindi nel LIVE quella componente e' PIU' PICCOLA.")
# ---------------------------------------------------------- §10 trasferimento su V1
print("\n" + "-" * L)
@@ -863,21 +877,29 @@ def main() -> None:
f"= ${(max(cag_band)-min(cag_band))*CAPITAL:.0f}/anno di incertezza")
print(f" versare EUR 500/mese (il piano dell'operatore) = EUR 16,44/giorno di capitale")
print(f" nuovo, cioe' ~{16.44/max(1e-9, abs(dC*CAPITAL/365*0.86)):.0f}x l'effetto misurato qui.")
print(f" (v) IL CRITERIO APPLICATO, riga per riga:")
med_pos = float(np.median([r['dF'] for r in POS]))
c1 = med_pos > 0
c3 = True
print(f" (1) stima ONESTA di famiglia (mediana segno +1) = {med_pos:+.3f} "
f"contro argmax {argmax['dF']:+.3f} e cella pubblicata {RES[nmcan]['dF']:+.3f}"
f" -> {'PASS' if c1 else 'FAIL'}")
print(f" (v) IL CRITERIO APPLICATO, riga per riga. La forma quantitativa di (1) e'")
print(f" dichiarata qui: 'sopravvive alla stima di famiglia' = la MEDIANA della meta'")
print(f" d'ipotesi vale almeno la META' della cella selezionata (altrimenti non e' un")
print(f" plateau, e' una punta), E la quota di celle positive e' chiaramente sopra il")
print(f" 50% (altrimenti la famiglia e' una monetina). Soglie scelte per essere")
print(f" verificabili, non per far passare o bocciare un numero gia' visto.")
med_pos = float(np.median([r["dF"] for r in POS]))
fpos = sum(r["dF"] > 0 for r in POS) / len(POS)
c1 = (med_pos >= 0.5 * CHOSEN["dF"]) and (fpos >= 0.65)
eurday = dC * CAPITAL / 365 * 0.86
c4 = abs(eurday) >= 0.10
print(f" (1) mediana di famiglia (segno +1) {med_pos:+.3f} = "
f"{100*med_pos/CHOSEN['dF'] if CHOSEN['dF'] else float('nan'):.0f}% della cella scelta "
f"({CHOSEN['dF']:+.3f}); celle positive {fpos*100:.0f}% -> {'PASS' if c1 else 'FAIL'}")
print(f" (2) `weights_tilt_null`: gate_pass=False nella forma nuda, INERTE nella forma")
print(f" compensata -> il gate non lo sostiene in nessuna delle due -> FAIL/vacuo")
print(f" (3) marginale ADDS + null di permutazione con potenza (§6) -> vedi sopra")
print(f" (4) taglia contro il modo di fallire in piu': "
f"EUR {dC*CAPITAL/365*0.86:+.4f}/giorno a ${CAPITAL:.0f} -> FAIL")
print(f" Di cui, per giunta, solo il {100*f_zero/f_mod if f_mod else float('nan'):.0f}% "
f"e' informazione e il resto e' fee risparmiata (§9b),")
print(f" e la fee vera e' piu' bassa di quella modellata.")
print(f" compensata -> non lo sostiene in nessuna delle due -> FAIL/vacuo")
print(f" (3) marginale ADDS su entrambe le celle + null di permutazione battuto a")
print(f" p<0.01 con la penale meccanica dentro (§6) -> PASS")
print(f" (4) taglia contro un modo di fallire in piu': EUR {eurday:+.4f}/giorno a "
f"${CAPITAL:.0f} -> {'PASS' if c4 else 'FAIL'} (soglia dichiarata: EUR 0,10/giorno)")
print(f" VERDETTO: {'CANDIDATO' if (c1 and c4) else 'LEAD / NESSUN CAMBIO'} — servono")
print(f" tutte e quattro; (1) e (4) falliscono, (2) non e' utilizzabile. Cio' che")
print(f" REGGE e va conservato e' il SEGNO: 0/72 celle positive sul controllo inverso.")
print("\n" + "=" * L)
print(f" fatto in {time.time()-t0:.0f}s")