"""r0822c_skh_anchor_prop — LA GAMBA (b) DEL `GATE PROP-01`, GIRATA DAVVERO (2026-08-22, ondata 3). COSA CHIUDE ----------- `r0822b_prop_recal` (RESULTS-0822 §29) ha registrato un gate a tre gambe e ne ha DATATA una al **2026-10-31**: (b) BANDA D'ANCORA DI SKH01 SOTTO LA LENTE PROP — MISURA. Data: 2026-10-31. Costo: rifare `pc._hourly_legs` ai 23 offset della griglia 230m/690m e rigirare §3. Metrica: MEDIANA DELLE DIFFERENZE APPAIATE J(peso nella regione w_XS 25-38%) - J(libro LIVE 75/25) sui 23 offset, finestra W-LONG. PASS se > +0.05. Surrogato di oggi (taglio del drift SKH x0.50 / x0.33): +0.124 e +0.113. Mi aspetto un PASS e lo dichiaro prima. E' l'unica distorsione che il progetto ha dichiarato DUE VOLTE (PROP-ALLOC §1 e PROP-RECAL §29) e mai rimossa: *l'ottimo gira sull'ancora canonica e si appoggia a SKH01, che il progetto misura come lo sleeve con la fortuna d'ancora piu' grande da restituire*. ⚠️ **LA DATA ERA UN BUDGET, E IL BUDGET ERA ANCORATO ALLA COMPUTAZIONE SBAGLIATA.** Il costo temuto ("~9 min per asset x offset") e' quello di `r0726_skh_sigcache`, che ricostruisce il segnale intra-bin a OGNI osservazione oraria dentro ogni bin 230m (~82.000 righe per cella). Questa misura non ne ha bisogno: serve `pc._hourly_legs` all'offset, cioe' resample + entries + MTM per-trade, **misurato 0,6 s per offset** (entrambi gli asset). I 23 offset costano ~15 s. La gamba (b) non aspettava un dato ne' una macchina: aspettava che qualcuno misurasse il costo. IL PROBLEMA VERO: LE ANCORE SONO DUE ------------------------------------ SKH01 ha 23 offset di griglia (230m/690m, uno ogni 30m su [0,690)); XS01 ha 10 fasi del ciclo H=10. PROP-RECAL ha girato la banda di FASE (a offset SKH canonico); PROP-ALLOC nessuna delle due. La lezione registrata di `r0726_loo_deluck` e' che **la somma delle fortune marginali NON e' la mediana congiunta** (sbagliava di +0,82 di Sharpe hold-out). **SCELTA DICHIARATA: griglia congiunta PIENA 23 x 10 = 230 configurazioni**, non un campionamento e non una somma di marginali. E' possibile perche' il costo e' stato MISURATO prima di scegliere (~0,06 s per coppia config-leva): con 23 offset x 10 fasi x 8 leve la spesa e' ~110 s. **Perche' questa scelta NON e' quella che mi conviene:** la scelta comoda sarebbe fissare la fase di XS01 a 0, che PROP-RECAL misura fortunata di +0,019 di J; il collasso per MEDIANA sulle fasi la restituisce. Entrambe sono riportate, cosi' si vede da che parte taglia. CONVENZIONI (tutte ereditate, nessuna nuova) -------------------------------------------- * Statistica = **MEDIANA DELLE DIFFERENZE APPAIATE**, mai differenza delle mediane (26/07: con quella correzione un verdetto si ribalto'). Ogni offset e' valutato con ENTRAMBE le configurazioni (candidato e LIVE) alla STESSA ancora e sugli STESSI indici di bootstrap. * Lente **ACCOPPIATA** ovunque (su una regola di DD a UN GIORNO la close-only e' cieca). * Pesi FISSI, mai ri-ottimizzati per offset: ri-cercare l'argmax a ogni offset sarebbe un max-of-23, cioe' esattamente il difetto sotto audit. La leva si', ma su ENTRAMBI i lati. * de-luck sul drift x0,89 tenuto **per comparabilita' con PROP-RECAL**. ⚠️ Su questa dimensione e' un DOPPIO CONTEGGIO conservativo: lo 0,89 fu misurato (26/07) proprio come rapporto mediana-ancore / ancora-canonica del drift. §6 rigira la misura a fattore 1,00. * Universo XS01 = **U13 Binance** (la versione piu' DEBOLE, conservativa), finestra **W-LONG 2021+**, meccanismo congelato. Identici a PROP-RECAL. COSA QUESTA MISURA NON TOGLIE (dichiarato, non nascosto) -------------------------------------------------------- L'ancora giornaliera di **TP01** (24 ore possibili) resta canonica 00:00 UTC, che il progetto misura come la migliore delle 24 per l'hold-out di TP01. E' una TERZA dimensione, fuori dal perimetro della gamba (b), e la sua fortuna resta dentro questi numeri. ESITO (2026-08-22, corsa completa 391 s, 5.127 celle dichiarate) ---------------------------------------------------------------- * RIPRODUZIONE all'ancora canonica **7/7 esatta** (J LIVE 0,281 · fase 0 0,472 · mediana 0,453 · banda [0,431 0,488] · delta +0,175 · 10/10 fasi) e **3 controlli bit-exact a 0,0** (gambe orarie a off=0 contro `pc._hourly_legs()` su 4 colonne; costruttore di libro contro `PR.rg` a due pesi). * **GAMBA (b): PASS.** Mediana delle differenze appaiate sui 23 offset = **+0,1162** contro la soglia pre-registrata +0,05; banda p10-p90 **[+0,087, +0,152]**; **23/23 offset positivi**, **230/230 celle** dello spazio congiunto positive. La regione tiene tutta: 38/38/25 +0,116 · 38/25/38 +0,120 · 25/38/38 +0,109. * **La fortuna d'ancora c'e' ed e' quella attesa, ma non e' dove si temeva.** L'offset canonico 0 sta al **91° pctl** dei 23 su tutte e tre le grandezze e regala **+0,059 sul delta**, +0,112 sul livello J del candidato, +0,050 su quello del LIVE. Il delta e' MENO ancorato dei livelli perche' il LIVE contiene SKH01 al 25% e la fortuna si cancella in parte nella differenza appaiata. * 📌 **IL RISULTATO INTERPRETATIVO: il vantaggio NON e' un tilt su SKH01.** A `38/25/38` SKH01 sta al **medesimo peso del LIVE (25%)**, quindi la sua ancora si cancella quasi esattamente — e li' il delta e' il **piu' alto dei tre (+0,120)** con la banda **piu' stretta** (ampiezza 0,032 contro 0,065 di W_A e 0,057 di W_C, che portano SKH a 38%). **L'ampiezza della banda scala col DIFFERENZIALE di peso su SKH01**, esattamente come previsto prima di misurare. Cio' che paga e' XS01, non un sovrappeso dello sleeve fragile. * ⚠️ **Il numero OPERATIVO invece si dimezza, e per una ragione diversa dal gate.** P(>=50 EUR/g) da EUR 600 in 36 mesi: mediana della banda d'ancora **4,4% [p10-p90 2,5%-8,2%]** contro il **7,8%** pubblicato; P(zero) **35,3%** contro 25,5%. All'ancora canonica la macchina ridice **7,7%** (= replica di PROP-RECAL): il crollo e' la fortuna d'ancora, non un disaccordo. **Il gate e' su una DIFFERENZA (dove l'ancora si cancella in parte), il numero operativo e' un LIVELLO (dove non si cancella affatto) — passano insieme e si muovono in modo diverso.** * ⚠️ **Il surrogato del 22/08 e' finito vicino al vero per la CANCELLAZIONE DI DUE ASIMMETRIE.** Ricalcolato a peso FISSO il taglio del drift da' **+0,076 (x0,50)** e **+0,045 (x0,33)**, cioe' SOTTOSTIMA il vero (+0,116); ma PROP-RECAL lo calcolava ri-cercando l'argmax sui 10 pesi migliori mentre il LIVE resta inchiodato a 75/25 — sotto stress il candidato puo' scappare da SKH01 e il LIVE no -> il surrogato si gonfia a +0,124/+0,113. **Due errori in versi opposti che quasi si annullano: il numero era giusto, il metodo no.** * Sensibilita' dichiarata: a de-luck **x1,00** (senza il doppio conteggio dell'ancora) il delta sale a **+0,148**, 23/23 positivi -> tenere lo 0,89 e' conservativo su questa dimensione. * Banda del maxDD del candidato sui 23 offset: **-8,5% ... -13,0%**, mediana **-10,8%** (il pubblicato a peso 25% era -9,6%): su un conto a barriera la coda peggiora quanto il livello. * **RESTA FUORI, dichiarato:** l'ancora giornaliera di **TP01** (24 ore) e' una TERZA dimensione, fuori dal perimetro della gamba (b), e la sua fortuna e' ancora dentro questi numeri. Uso: `nice -n 19 timeout 900 uv run python scripts/research/r0822c_skh_anchor_prop.py` Rete: NESSUNA (parquet certificati + cache Binance di r0822b_xs01_oos). """ from __future__ import annotations import sys import time from pathlib import Path import numpy as np import pandas as pd ROOT = Path(__file__).resolve().parents[2] for _p in (ROOT, ROOT / "scripts" / "research", ROOT / "scripts" / "research" / "alt"): sys.path.insert(0, str(_p)) import r0702_anchor_skh01 as AN # noqa: E402 resample con fase spostata (02/07) import r0724_goal50_intraday_mc as mc # noqa: E402 recon orario per-sleeve (24/07) import r0725_prop_coupled as pc # noqa: E402 lente accoppiata (25/07) import r0725_prop_ladder as pl # noqa: E402 regole firm + scala di conti import r0822_prop_alloc as PA # noqa: E402 J, bootstrap, simulatore (22/08) import r0822b_prop_recal as PR # noqa: E402 IL file di cui giro la gamba (b) import r0822b_xs01_oos as X # noqa: E402 motore XS01 + cache Binance from src.data.downloader import load_data # noqa: E402 from src.strategies.skyhook import HTF_MIN, LTF_MIN # noqa: E402 ASSETS = ("BTC", "ETH") OFFSETS = tuple(range(0, 690, 30)) # 23 offset a priori, identici a r0702 (0 = canonico) PHASES = tuple(range(10)) # 10 fasi del ciclo H=10 di XS01 PHASES_SUB = (0, 3, 7) # sottocampione a priori per la scala di conti LEVS = PR.LEVS N_PATHS = PA.N_PATHS EV_H = FU_H = 365 SEED_EV, SEED_FU = 8220, 8221 HYRO = "HYRO" GATE_B = 0.05 # soglia PRE-REGISTRATA da PROP-RECAL # pesi FISSI che coprono la regione robusta w_XS in [25%, 38%] (griglia simplesso a passo 1/8) W_LIVE = {"TP01": 0.75, "SKH01": 0.25} W_A = {"TP01": 0.38, "SKH01": 0.38, "XS01": 0.25} # centro dichiarato da PROP-RECAL (headline) W_B = {"TP01": 0.38, "SKH01": 0.25, "XS01": 0.38} # w_XS all'estremo alto, TP-pesante W_C = {"TP01": 0.25, "SKH01": 0.38, "XS01": 0.38} # w_XS all'estremo alto, SKH-pesante # numeri PUBBLICATI da riprodurre prima di produrne di nuovi (RESULTS-0822 §29) PUB = dict(j_med=0.453, j_ph0=0.472, j_p10=0.431, j_p90=0.488, j_live=0.281, dmed=0.175, npos=10, surr_50=0.124, surr_33=0.113, p50g=0.078) TRIALS = 0 def bump(n: int = 1) -> None: global TRIALS TRIALS += n def hr(t: str = "") -> None: print("\n" + "-" * 104) if t: print(f" {t}") print("-" * 104) # ==================================================================== 1. GAMBE ORARIE ALL'OFFSET _LEGS: dict[int, pd.DataFrame] = {} def hourly_legs_off(off: int) -> pd.DataFrame: """`pc._hourly_legs()` con la griglia 230m/690m di SKH01 spostata di `off` minuti. NON riscrive la macchineria: rimpiazza temporaneamente `mc.build_frames` con il resample a fase spostata di `r0702_anchor_skh01` e richiama `mc.skh_hourly_contrib` — cosi' la logica trade-per-trade (entry a close del bin, exit AL LIVELLO, SL prioritario, fee nell'ora d'uscita) resta letteralmente quella del 24/07. La fedelta' della sostituzione e' provata dal controllo bit-exact a off=0 (§1). La gamba TP01 NON viene toccata: l'ancora di TP01 e' un'ALTRA dimensione (24 ore giornaliere) e la gamba (b) del gate riguarda i 23 offset di SKH01. """ if off in _LEGS: return _LEGS[off] tp_r = tp_w = sk_r = sk_w = None orig = mc.build_frames def patched(df5: pd.DataFrame, _off: int = off): ltf = AN.resample_off(df5, LTF_MIN, _off) htf = AN.resample_off(df5, HTF_MIN, _off) # confini HTF sottoinsieme dei LTF (690 = 3 x 230): la stessa guardia di r0702. assert np.isin(htf["timestamp"].values[1:], ltf["timestamp"].values).all(), \ f"confini HTF non sottoinsieme dei LTF (off={_off})" return ltf, htf mc.build_frames = patched try: for a in ASSETS: df = load_data(a, "1h") idx = pd.DatetimeIndex(pd.to_datetime(df["datetime"], utc=True)) close = df["close"].values.astype(float) low = df["low"].values.astype(float) high = df["high"].values.astype(float) prev = np.concatenate(([np.nan], close[:-1])) r_close = close / prev - 1.0 tpf = mc.tp01_daily_frame(a) day_of = idx.floor("D") w_tp = np.nan_to_num(tpf["w"].reindex(day_of).values) fee_tp = np.nan_to_num(tpf["fee"].reindex(day_of).values) first = np.concatenate(([True], day_of[1:] != day_of[:-1])) r_wick = np.where(w_tp >= 0, low / prev - 1.0, high / prev - 1.0) t_r = pd.Series(w_tp * r_close - np.where(first, fee_tp, 0.0), index=idx) t_w = pd.Series(w_tp * r_wick - np.where(first, fee_tp, 0.0), index=idx) sa, sb = mc.skh_hourly_contrib(a, idx, close, low, high) s_r = pd.Series(sa / mc.W_SKH, index=idx) s_w = pd.Series(sb / mc.W_SKH, index=idx) tp_r = t_r if tp_r is None else tp_r.add(t_r, fill_value=np.nan) tp_w = t_w if tp_w is None else tp_w.add(t_w, fill_value=np.nan) sk_r = s_r if sk_r is None else sk_r.add(s_r, fill_value=np.nan) sk_w = s_w if sk_w is None else sk_w.add(s_w, fill_value=np.nan) finally: mc.build_frames = orig J = pd.concat({"tp_r": tp_r, "tp_w": tp_w, "skh_r": sk_r, "skh_w": sk_w}, axis=1).dropna() _LEGS[off] = J return J _CRY: dict = {} def crypto_off(off: int, w_tp: float, w_skh: float) -> pd.DataFrame: """`pc.crypto_daily_tuples` con le gambe orarie all'offset `off` (minimo ESATTO sul path orario condiviso: si compone l'ora per ora e si guarda il minimo, wick incluso).""" key = (off, round(w_tp, 6), round(w_skh, 6)) if key in _CRY: return _CRY[key] J = hourly_legs_off(off) r_h = (w_tp * J["tp_r"] + w_skh * J["skh_r"]).values w_h = (w_tp * J["tp_w"] + w_skh * J["skh_w"]).values idx = J.index days = idx.floor("D") rows = [] for day, pos in pd.Series(np.arange(len(idx)), index=days).groupby(level=0): sl = pos.values r = r_h[sl] wk = w_h[sl] cum = np.cumprod(1.0 + r) cum_prev = np.concatenate(([1.0], cum[:-1])) m = min(cum.min(), (cum_prev * (1.0 + wk)).min()) - 1.0 rows.append((day, cum[-1] - 1.0, min(m, cum[-1] - 1.0))) out = pd.DataFrame(rows, columns=["day", "R", "m"]).set_index("day") _CRY[key] = out return out def rg_off(w: dict, idx: pd.DatetimeIndex, xs: pd.DataFrame, off: int) -> tuple: """`PR.rg` con il sub-book crypto all'offset `off`. Validato bit-exact a off=0 in §1.""" w_tp, w_sk = float(w.get("TP01", 0.0)), float(w.get("SKH01", 0.0)) w_xs = float(w.get("XS01", 0.0)) R = np.zeros(len(idx)) M = np.zeros(len(idx)) if w_tp > 0 or w_sk > 0: c = crypto_off(off, w_tp, w_sk).reindex(idx) R += np.nan_to_num(c["R"].values) M += np.nan_to_num(c["m"].values) if w_xs > 0: f = xs.reindex(idx) R += w_xs * np.nan_to_num(f["R"].values) M += w_xs * np.nan_to_num(f["m"].values) return R, np.minimum(M, R) - R def best_over_levers(R, G, ev, fu, factor: float = PA.DELUCK) -> tuple[float, float, np.ndarray]: """(J, leva, vettore per-percorso) alla leva che massimizza J. La ricerca sulla leva e' applicata SIMMETRICAMENTE al candidato e al LIVE (convenzione di PROP-RECAL).""" jb, lb, vb = -1.0, None, None for lv in LEVS: o = PR.obj_rg(R, G, lv, ev, fu, factor=factor) if o["J"] > jb: jb, lb, vb = o["J"], lv, o["jvec"] bump(len(LEVS)) return jb, lb, vb # ==================================================================== main def main() -> None: try: sys.stdout.reconfigure(line_buffering=True) except Exception: # pragma: no cover pass t0 = time.time() def tick(lab: str) -> None: print(f" [{time.time()-t0:6.0f}s] {lab}") print("=" * 104) print(" SKH-ANCHOR-PROP — la gamba (b) del `GATE PROP-01`, girata invece che aspettata") print("=" * 104) print(" DOMANDA: la mediana delle differenze appaiate J(regione w_XS 25-38%) - J(LIVE 75/25)") print(f" sui 23 offset della griglia 230m/690m di SKH01, W-LONG, supera +{GATE_B:.2f}?") print(" ASPETTATIVA REGISTRATA PRIMA DI MISURARE (mia, non ereditata): mi aspetto un PASS ma") print(" con una taglia SENSIBILMENTE minore del surrogato (+0.124/+0.113), per un motivo") print(" strutturale: il LIVE 75/25 contiene SKH01 al 25% e il candidato al 38%, quindi la") print(" fortuna d'ancora si cancella in PARTE nella differenza appaiata — il residuo pesa") print(" ~13 punti di peso, non 38. Se il delta uscisse >= al surrogato, il surrogato non era") print(" conservativo e lo dico.") # ============================================ §0. RIPRODUZIONE hr("§0. RIPRODUZIONE — la macchina deve dare i numeri di PROP-RECAL prima di darne di nuovi") Cb, Ob, Hb, Lb = PR.ohlc_panels(PR.bin_loader, PR.XS_UNIVERSE) p13 = Cb[X.U13].dropna() o13 = Ob.reindex(p13.index)[X.U13] h13 = Hb.reindex(p13.index)[X.U13] l13 = Lb.reindex(p13.index)[X.U13] XSP = {ph: PR.xs_tuples(p13, o13, h13, l13, phase=ph) for ph in PHASES} bump(len(PHASES)) cry_idx = pc.crypto_daily_tuples(1.0, 0.0).index base_idx = cry_idx.intersection(XSP[0].index) first13 = XSP[0].index[np.flatnonzero(XSP[0]["R"].values != 0.0)[0]] I_LONG = base_idx[base_idx >= max(first13, pd.Timestamp("2021-01-01", tz="UTC"))] ev = PA.boot_idx(len(I_LONG), EV_H, N_PATHS, seed=SEED_EV) fu = PA.boot_idx(len(I_LONG), FU_H, N_PATHS, seed=SEED_FU) print(f" W-LONG: {len(I_LONG)} giorni {I_LONG[0].date()} -> {I_LONG[-1].date()} | " f"de-luck x{PA.DELUCK} | {N_PATHS} percorsi | semi {SEED_EV}/{SEED_FU} | firm {HYRO}") RL0, GL0 = PR.rg(W_LIVE, I_LONG, XSP[0]) jL0, lvL0, vL0 = best_over_levers(RL0, GL0, ev, fu) js0, ds0 = [], [] for ph in PHASES: Rp, Gp = PR.rg(W_A, I_LONG, XSP[ph]) jb, _, vb = best_over_levers(Rp, Gp, ev, fu) js0.append(jb) ds0.append(float((vb - vL0).mean())) print(f" {'grandezza':>42} {'mio':>9} {'PROP-RECAL':>11} esito") checks = [("J libro LIVE 75/25 (ancora canonica)", jL0, PUB["j_live"]), ("J candidato 38/38/25 fase 0", js0[0], PUB["j_ph0"]), ("J candidato MEDIANA su 10 fasi", float(np.median(js0)), PUB["j_med"]), ("banda di fase p10", float(np.percentile(js0, 10)), PUB["j_p10"]), ("banda di fase p90", float(np.percentile(js0, 90)), PUB["j_p90"]), ("delta appaiato MEDIANO (10 fasi)", float(np.median(ds0)), PUB["dmed"])] ok = True for lab, mine, pub in checks: good = abs(mine - pub) <= 0.005 ok &= good print(f" {lab:>42} {mine:>9.3f} {pub:>11.3f} {'OK' if good else '<-- DIVERGE'}") npos0 = sum(1 for d in ds0 if d > 0) print(f" {'fasi con delta > 0':>42} {npos0:>9d} {PUB['npos']:>11d} " f"{'OK' if npos0 == PUB['npos'] else '<-- DIVERGE'}") ok &= npos0 == PUB["npos"] print(f"\n >>> RIPRODUZIONE ALL'ANCORA CANONICA: " f"{'OK — si procede' if ok else 'FALLITA — questo E il risultato'}") if not ok: print(" La macchina non riproduce PROP-RECAL: qualunque numero sugli offset sarebbe di") print(" un'altra macchina. Mi fermo qui, come impone la disciplina di riproduzione.") return tick("§0 riproduzione") # ============================================ §1. LA MACCHINERIA ALL'OFFSET hr("§1. LA MACCHINERIA ALL'OFFSET — e il controllo che prova che la sostituzione e' fedele") t = time.time() J0 = hourly_legs_off(0) (Jref,) = pc._hourly_legs() same_idx = J0.index.equals(Jref.index) dmax = max(float(np.abs(J0[c].values - Jref[c].values).max()) for c in J0.columns) print(f" gambe orarie a off=0 contro `pc._hourly_legs()`: n={len(J0)} indice identico=" f"{same_idx} max|dif| su 4 colonne = {dmax:.2e} -> " f"{'BIT-EXACT' if (same_idx and dmax == 0.0) else 'DIVERGE — STOP'}") if not (same_idx and dmax == 0.0): print(" La sostituzione di `build_frames` non riproduce il canonico: mi fermo.") return for wt in (W_A, W_LIVE): Ra, Ga = rg_off(wt, I_LONG, XSP[0], 0) Rb, Gb = PR.rg(wt, I_LONG, XSP[0]) d1 = float(np.abs(Ra - Rb).max()) d2 = float(np.abs(Ga - Gb).max()) print(f" costruttore di libro a off=0 contro `PR.rg` ({PA.wkey(wt)}): " f"max|dR| {d1:.2e} max|dgap| {d2:.2e} -> " f"{'BIT-EXACT' if max(d1, d2) == 0.0 else 'DIVERGE'}") if max(d1, d2) != 0.0: return print(f" costo misurato: {time.time()-t:.1f}s per la prima cella (import+cache inclusi)") t = time.time() for off in OFFSETS: hourly_legs_off(off) assert hourly_legs_off(off).index.equals(J0.index), \ f"indice orario diverso a off={off}: il bootstrap non sarebbe appaiato" print(f" 23 offset costruiti in {time.time()-t:.0f}s — l'indice giornaliero e' lo STESSO a") print(" ogni offset, quindi gli indici di bootstrap sono condivisi e il confronto e' appaiato.") print(" ⚠️ La data 2026-10-31 era un budget di calcolo ancorato al costo di `skh_sigcache`") print(" (segnale intra-bin, ~9 min/cella), che questa misura non usa.") n_tr = {} for off in (0, 330, 660): J = hourly_legs_off(off) n_tr[off] = int((J["skh_r"].values != 0.0).sum()) print(f" ore con contributo SKH01 non nullo: " + " · ".join(f"off{o}: {v}" for o, v in n_tr.items()) + " (griglie diverse, non una traslazione)") tick("§1 macchineria") # ============================================ §2. LA GRIGLIA CONGIUNTA hr("§2. LA GRIGLIA CONGIUNTA 23 OFFSET x 10 FASI — la gamba (b), headline W_A = 38/38/25") print(" Per ogni offset: J del LIVE (che contiene SKH01 al 25% e quindi si muove anch'esso)") print(" e J del candidato a ciascuna delle 10 fasi di XS01, alla leva che massimizza J.") print(" Il delta e' appaiato sui MEDESIMI indici di bootstrap; il collasso sulle fasi e' la") print(" MEDIANA (la stima onesta di PROP-RECAL), non la fase 0 (fortunata di +0.019).") D = np.zeros((len(OFFSETS), len(PHASES))) JC = np.zeros((len(OFFSETS), len(PHASES))) JL = np.zeros(len(OFFSETS)) LVL = np.zeros(len(OFFSETS)) DDC = np.zeros((len(OFFSETS), len(PHASES))) DDL = np.zeros(len(OFFSETS)) t = time.time() print(f"\n {'off':>4} {'J LIVE':>7} {'levL':>5} {'J cand fase0':>12} {'MEDIANA':>8} " f"{'p10-p90':>15} {'delta MEDIANO':>13} {'fasi>0':>7} {'maxDD cand':>10}") for i, off in enumerate(OFFSETS): RL, GL = rg_off(W_LIVE, I_LONG, XSP[0], off) jL, lvL, vL = best_over_levers(RL, GL, ev, fu) JL[i], LVL[i] = jL, lvL DDL[i] = PR.maxdd(PA.deluck(RL)) for k, ph in enumerate(PHASES): Rc, Gc = rg_off(W_A, I_LONG, XSP[ph], off) jc, _, vc = best_over_levers(Rc, Gc, ev, fu) JC[i, k] = jc DDC[i, k] = PR.maxdd(PA.deluck(Rc)) D[i, k] = float((vc - vL).mean()) print(f" {off:>4} {jL:>7.3f} {lvL:>5.2f} {JC[i,0]:>12.3f} {np.median(JC[i]):>8.3f} " f"[{np.percentile(JC[i],10):>6.3f},{np.percentile(JC[i],90):>6.3f}] " f"{np.median(D[i]):>+13.3f} {int((D[i] > 0).sum()):>5}/10 " f"{np.median(DDC[i])*100:>9.1f}%") print(f" ({time.time()-t:.0f}s per 23 x (1 LIVE + 10 fasi) x 8 leve)") d_per_off = np.median(D, axis=1) # collasso onesto sulle fasi d_gate = float(np.median(d_per_off)) # <-- IL NUMERO DELLA GAMBA (b) d_ph0 = float(np.median(D[:, 0])) # la scelta comoda, per confronto print(f"\n {'':>52}{'mediana':>9}{'p10':>9}{'p90':>9}{'>0':>8}") print(f" {'delta appaiato per OFFSET (fasi collassate a mediana)':>52}" f"{d_gate:>+9.3f}{np.percentile(d_per_off,10):>+9.3f}" f"{np.percentile(d_per_off,90):>+9.3f}{int((d_per_off>0).sum()):>5}/23") print(f" {'delta appaiato, spazio CONGIUNTO 230 celle':>52}" f"{np.median(D):>+9.3f}{np.percentile(D,10):>+9.3f}" f"{np.percentile(D,90):>+9.3f}{int((D>0).sum()):>4}/230") print(f" {'delta a fase 0 fissa (la scelta COMODA)':>52}" f"{d_ph0:>+9.3f}{np.percentile(D[:,0],10):>+9.3f}" f"{np.percentile(D[:,0],90):>+9.3f}{int((D[:,0]>0).sum()):>5}/23") print(f" {'ancora canonica (off 0, fase mediana) = PROP-RECAL':>52}" f"{np.median(D[0]):>+9.3f}{'':>9}{'':>9}{int((D[0]>0).sum()):>5}/10") print(f"\n Fortuna d'ancora di SKH01 SOTTO LA LENTE PROP (canonico - mediana):") print(f" sul DELTA : {np.median(D[0]) - d_gate:+.3f} " f"(l'offset 0 sta al {(d_per_off < d_per_off[0]).mean()*100:.0f}° pctl dei 23)") print(f" sul LIVELLO J del candidato : {np.median(JC[0]) - np.median(np.median(JC,axis=1)):+.3f} " f"(off 0 al {(np.median(JC,axis=1) < np.median(JC[0])).mean()*100:.0f}° pctl)") print(f" sul LIVELLO J del LIVE : {JL[0] - np.median(JL):+.3f} " f"(off 0 al {(JL < JL[0]).mean()*100:.0f}° pctl)") print(" ⚠️ La fortuna si cancella in PARTE perche' il LIVE contiene SKH01 al 25%: il delta") print(" e' meno ancorato dei livelli. E' la ragione per cui la statistica giusta e' il") print(" DELTA APPAIATO e non il livello.") tick("§2 griglia congiunta") # ============================================ §3. LA REGIONE, NON IL PUNTO hr("§3. LA REGIONE w_XS 25-38% — il gate parla di una REGIONE, non di un punto") print(" Tre pesi FISSI dichiarati che coprono la regione (nessuna ri-ottimizzazione per") print(" offset: sarebbe un max-of-23, cioe' il difetto sotto audit). W_A gira sulle 10 fasi") print(f" piene; i due pesi di sensibilita' sul sottocampione a priori {PHASES_SUB} (budget).") print(f"\n {'peso':>12} {'w_XS':>5} {'J mediano':>10} {'delta MEDIANO':>13} " f"{'p10':>8} {'p90':>8} {'offset>0':>9} {'celle>0':>9} {'gate':>6}") reg = {} for lab, wt, phs in (("38/38/25", W_A, PHASES), ("38/25/38", W_B, PHASES_SUB), ("25/38/38", W_C, PHASES_SUB)): if wt is W_A: Dw = D Jw = JC else: Dw = np.zeros((len(OFFSETS), len(phs))) Jw = np.zeros((len(OFFSETS), len(phs))) for i, off in enumerate(OFFSETS): RL, GL = rg_off(W_LIVE, I_LONG, XSP[0], off) _, _, vL = best_over_levers(RL, GL, ev, fu) for k, ph in enumerate(phs): Rc, Gc = rg_off(wt, I_LONG, XSP[ph], off) jc, _, vc = best_over_levers(Rc, Gc, ev, fu) Jw[i, k] = jc Dw[i, k] = float((vc - vL).mean()) dpo = np.median(Dw, axis=1) dm = float(np.median(dpo)) reg[lab] = dm print(f" {lab:>12} {int(round(100*wt['XS01'])):>4}% {np.median(Jw):>10.3f} " f"{dm:>+13.3f} {np.percentile(dpo,10):>+8.3f} {np.percentile(dpo,90):>+8.3f} " f"{int((dpo>0).sum()):>7}/23 {int((Dw>0).sum()):>6}/{Dw.size} " f"{'PASS' if dm > GATE_B else 'FAIL':>6}") worst = min(reg.values()) print(f"\n Peggiore della regione: {min(reg, key=reg.get)} con {worst:+.3f}; " f"mediana dei tre {np.median(list(reg.values())):+.3f}") tick("§3 regione") # ============================================ §4. SURROGATO vs MISURA hr("§4. IL SURROGATO CONTRO LA MISURA — quanto valeva il taglio del drift") print(" PROP-RECAL surrogava la banda d'ancora con un taglio del DRIFT di SKH01 (x0.50 e") print(" x0.33) e leggeva +0.124 / +0.113. Qui lo stesso surrogato e' ricalcolato al peso") print(" FISSO W_A e con lo stesso collasso di fase, cosi' il confronto e' appaiato.") print(f"\n {'lente':>44} {'delta MEDIANO':>13}") for lab, dmt in (("misura vera: 23 offset x 10 fasi", None), ("surrogato SKH x0.50 (ancora canonica)", (("SKH01", 0.50),)), ("surrogato SKH x0.33 (ancora canonica)", (("SKH01", 0.33),))): if dmt is None: print(f" {lab:>44} {d_gate:>+13.3f}") continue RLs, GLs = PR.rg(W_LIVE, I_LONG, XSP[0], dm=dmt) _, _, vLs = best_over_levers(RLs, GLs, ev, fu) dsx = [] for ph in PHASES: Rs, Gs = PR.rg(W_A, I_LONG, XSP[ph], dm=dmt) _, _, vs = best_over_levers(Rs, Gs, ev, fu) dsx.append(float((vs - vLs).mean())) print(f" {lab:>44} {np.median(dsx):>+13.3f}") print("\n Un surrogato che taglia il DRIFT non e' la stessa cosa di una griglia diversa:") print(" cambiare l'ancora cambia QUALI trade esistono, non solo quanto rendono.") tick("§4 surrogato") # ============================================ §5. IL NUMERO OPERATIVO hr("§5. IL NUMERO OPERATIVO — P(>=50 EUR/g) da EUR 600 in 36 mesi, sulla banda d'ancora") print(f" Sottocampione DICHIARATO: 23 offset x {len(PHASES_SUB)} fasi {PHASES_SUB} (uniformi a") print(" priori sul ciclo H=10) = 69 simulazioni da 1500 percorsi. La scala di conti costa") print(" ~1.7 s a simulazione: la griglia piena 23x10 costerebbe ~7 min in piu' e la mediana") print(" di 69 estrazioni basta per una banda al punto percentuale.") pc.DELUCK = PA.DELUCK W3 = PA.CRYPTO_PROP pt, pz, med_eur = [], [], [] t = time.time() if time.time() - t0 < 620: for i, off in enumerate(OFFSETS): for ph in PHASES_SUB: Rc, Gc = rg_off(W_A, I_LONG, XSP[ph], off) jc, lvc, _ = best_over_levers(Rc, Gc, ev, fu) cfg = tuple(W_A.get(nm, 0.0) for nm in W3) key = (cfg[0], cfg[1], cfg[2], "shared") saved = pc._TUP_CACHE.pop(key, None) pc._TUP_CACHE[key] = (Rc, Gc, I_LONG) r = pc.simulate([cfg], [0] * pl.MAX_CONCURRENT, n_paths=1500, lev=lvc, seed=20260822, lens="coupled", start=str(I_LONG[0].date())) pc._TUP_CACHE.pop(key, None) if saved is not None: pc._TUP_CACHE[key] = saved pt.append(r["p_target"]); pz.append(r["p_zero"]); med_eur.append(r["med"]) bump(len(OFFSETS) * len(PHASES_SUB)) print(f"\n P(>=50 EUR/g) su {len(pt)} celle (offset x fase): mediana " f"{np.median(pt):.1%} banda p10-p90 [{np.percentile(pt,10):.1%}, " f"{np.percentile(pt,90):.1%}] min {min(pt):.1%} max {max(pt):.1%}") print(f" P(zero) mediana {np.median(pz):.1%} | EUR/g mediana {np.median(med_eur):.2f}") print(f" All'ancora CANONICA (off 0, fasi {PHASES_SUB}): " f"{np.median(pt[:len(PHASES_SUB)]):.1%}") print(f" PROP-RECAL pubblicava {PUB['p50g']:.1%} [7.2-9.1%] (ancora canonica, 10 fasi)") print(f" ({time.time()-t:.0f}s)") else: print("\n NON GIRATO: budget di tempo esaurito prima di §5. Si dichiara, non si stima.") pt = [] tick("§5 numero operativo") # ============================================ §6. SENSIBILITA' AL DE-LUCK hr("§6. SENSIBILITA' — il de-luck x0.89 su questa dimensione e' un doppio conteggio") print(" Lo 0.89 fu MISURATO (26/07) come rapporto mediana-ancore / ancora-canonica del") print(" drift: applicarlo mentre si gira la banda d'ancora conta due volte la stessa") print(" correzione. E' tenuto per comparabilita' con PROP-RECAL; qui la stessa misura a") print(" fattore 1.00 (nessun de-luck), sulle stesse 23 x 3 celle.") d_nd = [] for i, off in enumerate(OFFSETS): RL, GL = rg_off(W_LIVE, I_LONG, XSP[0], off) _, _, vL = best_over_levers(RL, GL, ev, fu, factor=1.0) row = [] for ph in PHASES_SUB: Rc, Gc = rg_off(W_A, I_LONG, XSP[ph], off) _, _, vc = best_over_levers(Rc, Gc, ev, fu, factor=1.0) row.append(float((vc - vL).mean())) d_nd.append(float(np.median(row))) print(f"\n delta appaiato mediano de-luck x0.89 (headline) : {d_gate:+.3f}") print(f" de-luck x1.00 (no double) : {np.median(d_nd):+.3f}" f" ({int(sum(1 for d in d_nd if d > 0))}/23 > 0)") print(" Il verso di questa differenza dice se la scelta conservativa lo era davvero.") tick("§6 sensibilita'") # ============================================ §7. VERDETTO hr("§7. VERDETTO SULLA GAMBA (b)") passed = d_gate > GATE_B print(f" Metrica pre-registrata: mediana delle differenze appaiate J(w_XS 25-38%) - J(LIVE)") print(f" sui 23 offset, W-LONG. Soglia: > +{GATE_B:.2f}") print(f"\n MISURA (headline W_A 38/38/25, fasi collassate a mediana): {d_gate:+.4f}") print(f" banda p10-p90 sui 23 offset: [{np.percentile(d_per_off,10):+.3f}, " f"{np.percentile(d_per_off,90):+.3f}] offset positivi: " f"{int((d_per_off>0).sum())}/23 celle positive: {int((D>0).sum())}/230") print(f" regione: peggiore {worst:+.3f} ({min(reg, key=reg.get)}), " f"mediana dei tre pesi {np.median(list(reg.values())):+.3f}") print(f"\n >>> GAMBA (b): {'PASS' if passed else 'FAIL'} " f"({d_gate:+.4f} {'>' if passed else '<='} +{GATE_B:.2f})") if passed: print(" L'aspettativa registrata da PROP-RECAL (un PASS) e' confermata.") else: print(" L'aspettativa registrata da PROP-RECAL (un PASS) e' SMENTITA: il vantaggio") print(" era in parte calendario.") if pt: print(f"\n COSA RESTA del risultato funded, ri-ancorato: P(>=50 EUR/g) mediana " f"{np.median(pt):.1%} [banda d'ancora {np.percentile(pt,10):.1%}-" f"{np.percentile(pt,90):.1%}] contro il {PUB['p50g']:.1%} pubblicato.") print(f"\n RESTA FUORI (dichiarato): l'ancora giornaliera di TP01 (24 ore) — terza dimensione,") print(" non nel perimetro della gamba (b), e la sua fortuna e' ancora dentro questi numeri.") print(f"\n TRIAL/CELLE DICHIARATI (al rialzo): {TRIALS}") print(f" fatto in {time.time()-t0:.0f}s") print("=" * 104) if __name__ == "__main__": main()