"""r0822b_prop_recal — PROP-ALLOC RIFATTO con XS01 preso dalla STORIA LUNGA (2026-08-22, ondata 2). IL BUCO CHE CHIUDE ------------------ Il risultato di testa dell'ondata (RESULTS-0822 §1, PROP-ALLOC) e': su un conto FUNDED da $100k l'allocazione ottima sotto barriera di drawdown mette **XS01 al 50%** e porta J = P(pass) x P(vivo 12m) da 0,335 (libro live 75/25) a **0,738**, con **P(>=50 EUR/g) da 600 EUR in 36 mesi = 42%**. Quel filone ha dichiarato da solo il proprio limite: "tutto il vantaggio poggia sul drift di XS01 misurato sulla sua finestra di scoperta" e ha proposto un gate che era un'ATTESA: *non aprire un funded prima che XS01 abbia una finestra fuori dal 2024-2026*. Il filone XS01-OOS (RESULTS-0822 §21) ha trovato quella finestra lo stesso giorno — stessi 19 ticker su Binance spot USDT 1d, **2021-01 -> 2023-12, con LUNA e FTX dentro** — e ha misurato che l'edge di RENDIMENTO regge (mediana di fase +1,12 fuori contro +0,37 dentro, differenza appaiata +0,668 in 10/10 fasi). Ma ha anche misurato TRE correzioni nel verso opposto: (i) il maxDD standalone di XS01 RADDOPPIA: 10,8% -> 20,9-22,0%; (ii) il peso 0,50 non sopravvive: fuori campione a w=0,50 il maxDD di libro risale a 8,9% contro 6,0% a w=0,25 — cioe' restituisce la protezione di DD che e' l'UNICA ragione per mettere XS01 su un conto a barriera; (iii) il 32% dell'universo non e' testabile (ARB/OP/SUI/APT/SEI/TIA non quotati nel 2021-23) e su di esso vive gran parte del numero di scoperta (19 gambe 1,31 vs 13 gambe 0,54). Questo script rifa' il conto di PROP-ALLOC con la serie LUNGA al posto di quella di scoperta e risponde a UNA domanda: **J regge?** LA PREVISIONE, REGISTRATA PRIMA DI MISURARE ------------------------------------------- Mi aspetto che J SCENDA ma resti sopra il libro live, per tre ragioni che tirano in versi diversi: (a) il drift di XS01 fuori campione e' PIU' ALTO -> spinge J su; (b) il suo maxDD raddoppia e la barriera e' STATICA (una soglia assoluta non e' scale-invariante) -> spinge J giu', e su un conto a barriera questa e' la forza che conta; (c) la finestra lunga contiene il 2022, quindi TP01 (lo sleeve DIFENSIVO, sotto-pesato da PROP-ALLOC su un campione senza sinistro) ci arriva con un curriculum diverso -> mi aspetto che l'argmax RIALZI TP01 e ABBASSI XS01 rispetto al 25/25/50. Previsione numerica dichiarata prima: argmax con w_XS in [0,25 ; 0,38], J fra 0,50 e 0,70, P(>=50/g) fra il 10% e il 35%. Se J restasse >= 0,70 con w_XS = 0,50 la previsione e' sbagliata e lo dico; se J crollasse sotto il livello del libro live, il fronte funded va ripensato ADESSO. DISCIPLINA DI RIPRODUZIONE (non negoziabile) -------------------------------------------- Prima di pubblicare un numero nuovo la macchina deve riprodurre quello vecchio. §0 rigira PROP-ALLOC con la sua stessa macchineria (`r0822_prop_alloc` IMPORTATO, non riscritto), stessi semi, stessa griglia, stesso de-luck, e confronta con i valori PUBBLICATI, cablati qui sotto in `PUB`. Se la riproduzione fallisce, quello E' il risultato e viene detto. CONVENZIONI DICHIARATE ---------------------- * Lente **ACCOPPIATA obbligatoria**. Su una regola di DD a UN GIORNO la lente close-only e' esattamente CIECA (misurato oggi: 0,00 breach contro 0,40-1,21 veri, rapporto INF). La macchineria del wick e' quella di `r0725_prop_coupled` (crypto: minimo ESATTO sul path orario condiviso TP01+SKH01; XS01: 4 checkpoint O/estremo/estremo/C con ordinamento CONDIVISO e ordine AVVERSO dagli OHLC giornalieri). Per la finestra 2021-23 gli OHLC sono quelli **Binance** della cache di `r0822b_xs01_oos` — il costruttore di tuple e' lo STESSO codice e viene validato in §1 contro `pc.xsec_daily_tuples()` sui dati HL, dove deve dare max|diff| = 0. * **Universo dichiarato: U13** (i 13 dei 19 quotati prima del 2020-11) come serie PRIMARIA, perche' e' l'unica testabile fuori campione e perche' e' la versione **PIU' DEBOLE** di quella che sta nel libro (13 gambe invece di 19: XS01-OOS misura 0,54 contro 1,31 sulla stessa finestra e venue). Scegliere la piu' debole e' conservativo per la decisione. U19-EXP (universo che si allarga da 10 a 19 gambe) e' riportato come **altro capo della banda**, mai come numero di testa. * Meccanismo **CONGELATO** (XS_CFG: blend [30,90], H=10, k=5, gate p30, vol-target 20%, 5 bps/lato). Nessun parametro toccato, nessuna cella cercata: qui non si seleziona, si RI-MISURA. * de-luck sul drift **x0,89** (misurato 26/07), identico a PROP-ALLOC. * **Distorsioni portate avanti DICHIARATE, non rimosse in silenzio:** (1) la banda d'ancora NON e' girata sotto questa lente (fuori budget il 22/08 e fuori budget qui): 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 distorsione e' A FAVORE del risultato. Lo stress SKH x0,50 / x0,33 e' li' apposta. (2) l'intero risultato poggia sull'assunto — **MAI VERIFICATO CON UNA FIRM** — che una firm crypto listi i 19 (qui 13) alt Hyperliquid **con short abilitato**. Il critico dell'ondata lo chiama "la ripetizione esatta dell'errore GTAA01/PRIIPs". Non e' verificabile da qui (serve leggere un listino), quindi §6 misura quanto del risultato MUORE se e' falso. * Monte Carlo: 2500 percorsi per J (come PROP-ALLOC), 1500 per la scala di conti. **Un Monte Carlo ha una risoluzione e va detta**: SE su un livello di J ~ +-0,010, sulle differenze APPAIATE (indici di bootstrap condivisi) molto meno, ~+-0,007. Uso: `nice -n 19 timeout 900 uv run python scripts/research/r0822b_prop_recal.py` Rete: NESSUNA (tutto da disco: 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] sys.path.insert(0, str(ROOT)) sys.path.insert(0, str(ROOT / "scripts" / "research")) sys.path.insert(0, str(ROOT / "scripts" / "research" / "alt")) import r0725_prop_coupled as pc # noqa: E402 macchineria del wick accoppiato (25/07) import r0725_prop_ladder as pl # noqa: E402 regole delle firm + scala di conti import r0822_prop_alloc as PA # noqa: E402 IL filone da riprodurre (22/08) import r0822b_xs01_oos as X # noqa: E402 motore XS01 + cache Binance (22/08) from src.portfolio.sleeves import XS_UNIVERSE, _HL_DIR # noqa: E402 RAW = ROOT / "data" / "raw" FEE_SIDE = 0.001 / 2.0 HYRO = "HYRO" # ---------------------------------------------------------------- numeri PUBBLICATI da riprodurre # (RESULTS-0822 §1, tabella "1 — PROP-ALLOC"; log della corsa nello scratchpad) PUB = dict( argmaxJ=dict(w="25/25/50", lev=0.750, J=0.738, p_pass=0.822, p_alive=0.898, sharpe=1.69), argmaxS=dict(w="38/25/38", lev=0.750, J=0.721, p_pass=0.773, p_alive=0.933, sharpe=1.73), live=dict(w="75/25/0", lev=0.500, J=0.335, p_pass=0.386, p_alive=0.868, sharpe=1.09), dJ_vs_sharpe=0.0164, dJ_vs_live=0.3996, ladder_med=39.28, ladder_p50=0.417, ladder_zero=0.139, ) LEVS = (0.25, 0.375, 0.50, 0.625, 0.75, 1.00, 1.25, 1.50) STEP = 8 N_PATHS = PA.N_PATHS # 2500 EV_H = FU_H = 365 SEED_EV, SEED_FU = 8220, 8221 # identici a PROP-ALLOC W3 = PA.CRYPTO_PROP # ("TP01","SKH01","XS01") TRIALS = 0 # contatore della griglia dichiarata (al RIALZO) def bump(n: int) -> None: global TRIALS TRIALS += n def hr(t: str = "") -> None: print("\n" + "-" * 104) if t: print(f" {t}") print("-" * 104) # =============================================================== 1. XS01 ACCOPPIATO, QUALSIASI DATO def ohlc_panels(loader, syms: list[str]) -> tuple[pd.DataFrame, ...]: """(C, O, H, L) allineati. `loader(sym) -> DataFrame con timestamp/open/high/low/close`.""" o, h, lo, c = {}, {}, {}, {} for s in syms: d = loader(s) i = pd.to_datetime(d["timestamp"], unit="ms", utc=True) o[s] = pd.Series(d["open"].astype(float).values, index=i) h[s] = pd.Series(d["high"].astype(float).values, index=i) lo[s] = pd.Series(d["low"].astype(float).values, index=i) c[s] = pd.Series(d["close"].astype(float).values, index=i) C = pd.concat(c, axis=1).sort_index() return C, pd.concat(o, axis=1).reindex(C.index)[C.columns], \ pd.concat(h, axis=1).reindex(C.index)[C.columns], \ pd.concat(lo, axis=1).reindex(C.index)[C.columns] def xs_tuples(C: pd.DataFrame, O: pd.DataFrame, H: pd.DataFrame, L: pd.DataFrame, phase: int = 0, wick_mode: str = "shared", **kw) -> pd.DataFrame: """(R, m) giornalieri di XS01 su un pannello QUALSIASI, con la stessa convenzione di wick di `r0725_prop_coupled.xsec_daily_tuples`. Le posizioni vengono dal motore CONGELATO di `r0822b_xs01_oos.xsec_engine` (che regge i NaN = asset non ancora quotato). Validato in §1 contro `pc.xsec_daily_tuples()` sui dati HL: deve dare max|diff| = 0.""" det: dict = {} r = X.xsec_engine(C, phase=phase, detail=det, **kw) W, scale, turn = det["W"], det["scale"], det["turn"] px = C.values.astype(float) n, A = px.shape prev_c = np.vstack([np.full((1, A), np.nan), px[:-1]]) held = np.vstack([np.zeros((1, A)), W[:-1]]) # gambe TENUTE nel giorno i with np.errstate(invalid="ignore"): rO = O.values / prev_c - 1.0 rH = H.values / prev_c - 1.0 rL = L.values / prev_c - 1.0 rC = px / prev_c - 1.0 vO = np.nansum(held * rO, axis=1) vH = np.nansum(held * rH, axis=1) vL = np.nansum(held * rL, axis=1) vC = np.nansum(held * rC, axis=1) if wick_mode == "perleg": # bound severo: ogni gamba al suo peggio worst = np.where(held >= 0, rL, rH) m_gross = np.minimum(np.nansum(held * worst, axis=1), vC) else: # 'shared': ordine AVVERSO m_gross = np.minimum(np.minimum.reduce([vO, vL, vH, vC]), np.minimum.reduce([vO, vH, vL, vC])) m_net = (m_gross - turn * FEE_SIDE) * scale R_net = r.values out = pd.DataFrame({"R": R_net, "m": np.minimum(m_net, R_net)}, index=C.index) return out def bin_loader(sym: str) -> pd.DataFrame: """Legge dalla cache di r0822b (nessuna rete) e SCARTA la barra del giorno in corso: una barra giornaliera parziale in coda e' esattamente il difetto trovato oggi nei monitor forward.""" d = X.fetch_binance_daily(sym) today = pd.Timestamp.utcnow().normalize().value // 10**6 return d[d["timestamp"] < today].reset_index(drop=True) def hl_loader(sym: str) -> pd.DataFrame: return pd.read_parquet(_HL_DIR / f"hl_{sym.lower()}_1d.parquet") # =============================================================== 2. LIBRO A 3 GAMBE, PANNELLO LIBERO def rg(w: dict, idx: pd.DatetimeIndex, xs: pd.DataFrame, drop_xs: bool = False, dm: tuple = ()) -> tuple[np.ndarray, np.ndarray]: """(R, gap) del libro TP01/SKH01/XS01 su un pannello arbitrario e con una serie XS01 arbitraria. E' `PA.book_RG` con il pannello e la serie XS01 come argomenti invece che cablati: crypto col minimo ESATTO sul path orario condiviso (`PA._crypto`, che per uno stress taglia il drift sulle GAMBE ORARIE prima di comporre — il minimo giornaliero non e' separabile per gamba, quindi non si puo' tagliare a valle); il minimo di XS01 si SOMMA (worst simultaneo, convenzione severa del 25/07). gap = min_intraday - chiusura (<= 0). Validato bit-exact contro `PA.book_RG` in §1. `dm` = (("XS01", 0.5), ...) taglia il DRIFT di quello sleeve lasciando la forma della coda. ⚠ Il taglio crypto usa la media sull'INTERA storia oraria (2019+), non sulla finestra: e' la convenzione di PROP-ALLOC, tenuta identica perche' le due tabelle vanno confrontate.""" dmd = dict(dm) w_tp, w_sk = float(w.get("TP01", 0.0)), float(w.get("SKH01", 0.0)) w_xs = 0.0 if drop_xs else 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 = PA._crypto(w_tp, w_sk, dmd.get("TP01", 1.0), dmd.get("SKH01", 1.0)).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) rv = np.nan_to_num(f["R"].values) mv = np.nan_to_num(f["m"].values) mult = dmd.get("XS01", 1.0) if mult != 1.0: cutv = (1.0 - mult) * rv.mean() # media PRIMA del taglio (come PA) rv, mv = rv - cutv, mv - cutv R += w_xs * rv M += w_xs * mv return R, np.minimum(M, R) - R def obj_rg(R: np.ndarray, G: np.ndarray, lev: float, ev_idx, fu_idx, firm: str = HYRO, factor: float = PA.DELUCK) -> dict: """`PA.objective` senza la costruzione del libro (che qui e' su un pannello diverso).""" Rd = PA.deluck(R, factor) e = PA.eval_sim(Rd, G, ev_idx, lev, firm) f = PA.funded_sim(Rd, G, fu_idx, lev, firm, 100_000.0) ann = float(Rd.mean() * 365.0) vol = float(Rd.std() * np.sqrt(365.0)) return dict(J=e["p_pass"] * f["p_alive"], p_pass=e["p_pass"], p_alive=f["p_alive"], e_payout=f["e_payout"], sharpe=(ann / vol if vol > 0 else 0.0), vol=vol, drift=ann, jvec=(e["passed"] & f["alive"]).astype(float)) def maxdd(R: np.ndarray) -> float: eq = np.cumprod(1.0 + R) return float((eq / np.maximum.accumulate(eq) - 1.0).min()) def scan(grid: list[dict], idx, xs, ev_idx, fu_idx, drop_xs: bool = False, dm: tuple = ()) -> pd.DataFrame: rows = [] for w in grid: R, G = rg(w, idx, xs, drop_xs=drop_xs, dm=dm) dd = maxdd(PA.deluck(R)) for lev in LEVS: o = obj_rg(R, G, lev, ev_idx, fu_idx) rows.append(dict(w=PA.wkey(w), lev=lev, maxdd=dd, **{k: v for k, v in o.items() if k != "jvec"})) bump(len(grid) * len(LEVS)) return pd.DataFrame(rows) def best_row(df: pd.DataFrame, key: str = "J") -> pd.Series: return df.loc[df[key].idxmax()] def wdict(key: str) -> dict: return dict(zip(W3, [int(x) / 100 for x in key.split("/")])) # =============================================================== 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(" PROP-RECAL — J regge se XS01 viene dalla STORIA LUNGA invece che dalla sua finestra" " di scoperta?") print("=" * 104) print(" PREVISIONE REGISTRATA PRIMA: argmax con w_XS in [0.25, 0.38], J in [0.50, 0.70],") print(" P(>=50 EUR/g) in [10%, 35%]. Il verso atteso: drift XS piu' alto (spinge su) contro") print(" maxDD raddoppiato + barriera STATICA + 2022 dentro (spingono giu'; su un conto a") print(" barriera la seconda forza pesa di piu').") # ================================================== §0. RIPRODUZIONE hr("§0. RIPRODUZIONE — la macchina deve dare i numeri PUBBLICATI prima di darne di nuovi") idx_pub = PA._panel_index(PA.START_WIN) ev_pub = PA.boot_idx(len(idx_pub), EV_H, N_PATHS, seed=SEED_EV) fu_pub = PA.boot_idx(len(idx_pub), FU_H, N_PATHS, seed=SEED_FU) print(f" pannello 5-sleeve di PROP-ALLOC: {len(idx_pub)} giorni " f"{idx_pub[0].date()} -> {idx_pub[-1].date()} | de-luck x{PA.DELUCK} | " f"{N_PATHS} percorsi | semi {SEED_EV}/{SEED_FU}") grid = PA.simplex_grid(W3, STEP) t = time.time() Gpub = PA.grid_scan(grid, LEVS, HYRO, ev_pub, fu_pub) bump(len(grid) * len(LEVS)) print(f" griglia {len(grid)} pesi x {len(LEVS)} leve = {len(grid)*len(LEVS)} celle " f"({time.time()-t:.0f}s)") b_pub = best_row(Gpub) ws = Gpub.loc[Gpub["sharpe"].idxmax(), "w"] bs_pub = best_row(Gpub[Gpub["w"] == ws]) bl_pub = best_row(Gpub[Gpub["w"] == "75/25/0"]) print(f"\n {'':>18} {'pesi':>10} {'leva':>6} {'J':>7} {'PUB':>7} {'d':>7} | " f"{'P(pass)':>8} {'PUB':>7} | {'P(vivo)':>8} {'PUB':>7} | {'Sharpe':>7} {'PUB':>6}") ok = True for lab, row, p in (("argmax J", b_pub, PUB["argmaxJ"]), ("argmax Sharpe", bs_pub, PUB["argmaxS"]), ("libro LIVE 75/25", bl_pub, PUB["live"])): same_w = (row["w"] == p["w"]) and abs(row["lev"] - p["lev"]) < 1e-9 dj = row["J"] - p["J"] ok &= same_w and abs(dj) < 0.02 print(f" {lab:>18} {row['w']:>10} {row['lev']:>6.3f} {row['J']:>7.3f} {p['J']:>7.3f} " f"{dj:>+7.3f} | {row['p_pass']:>8.1%} {p['p_pass']:>7.1%} | " f"{row['p_alive']:>8.1%} {p['p_alive']:>7.1%} | {row['sharpe']:>7.2f} {p['sharpe']:>6.2f}" f"{'' if same_w else ' <-- CELLA DIVERSA'}") dA, seA = PA.paired_delta(wdict(b_pub["w"]), float(b_pub["lev"]), wdict(bs_pub["w"]), float(bs_pub["lev"]), HYRO, ev_pub, fu_pub) dB, seB = PA.paired_delta(wdict(b_pub["w"]), float(b_pub["lev"]), {"TP01": 0.75, "SKH01": 0.25}, float(bl_pub["lev"]), HYRO, ev_pub, fu_pub) bump(4) print(f"\n differenze APPAIATE argmaxJ - argmaxSharpe {dA:+.4f} +-{seA:.4f} " f"(PUB {PUB['dJ_vs_sharpe']:+.4f})") print(f" argmaxJ - LIVE {dB:+.4f} +-{seB:.4f} " f"(PUB {PUB['dJ_vs_live']:+.4f})") ok &= abs(dA - PUB["dJ_vs_sharpe"]) < 0.02 and abs(dB - PUB["dJ_vs_live"]) < 0.03 pc.DELUCK = PA.DELUCK r_lad = pc.simulate([tuple(wdict(b_pub["w"]).get(n, 0.0) for n in W3)], [0] * pl.MAX_CONCURRENT, n_paths=1500, lev=0.75, seed=20260822, lens="coupled", start=PA.START_WIN) print(f" scala di conti (I-bis) EUR/g med {r_lad['med']:.2f} (PUB {PUB['ladder_med']:.2f}) | " f"P(>=50/g) {r_lad['p_target']:.1%} (PUB {PUB['ladder_p50']:.1%}) | " f"P(zero) {r_lad['p_zero']:.1%} (PUB {PUB['ladder_zero']:.1%})") ok &= abs(r_lad["p_target"] - PUB["ladder_p50"]) < 0.03 verdetto = "OK — si procede" if ok else "FALLITA — ogni numero sotto va letto con questa riserva" print(f"\n >>> RIPRODUZIONE: {verdetto}") tick("§0 riproduzione") # ================================================== §1. LA SERIE LUNGA, ACCOPPIATA hr("§1. LA SERIE LUNGA DI XS01, CON IL WICK — e la validazione del costruttore di tuple") Chl, Ohl, Hhl, Lhl = ohlc_panels(hl_loader, [s for s in XS_UNIVERSE if (_HL_DIR / f"hl_{s.lower()}_1d.parquet").exists()]) Chl = Chl.dropna() Ohl, Hhl, Lhl = Ohl.reindex(Chl.index), Hhl.reindex(Chl.index), Lhl.reindex(Chl.index) mine = xs_tuples(Chl, Ohl, Hhl, Lhl, phase=0) ref = pc.xsec_daily_tuples("shared") j = mine.join(ref, lsuffix="_m", rsuffix="_r", how="inner").dropna() dR = float(np.abs(j["R_m"] - j["R_r"]).max()) dM = float(np.abs(j["m_m"] - j["m_r"]).max()) print(f" CONTROLLO: il mio costruttore di tuple, alimentato con gli OHLC HL, contro") print(f" `pc.xsec_daily_tuples()` (la macchineria del 25/07): n={len(j)} " f"max|dR| = {dR:.2e} max|dm| = {dM:.2e} -> " f"{'IDENTICO' if max(dR, dM) < 1e-12 else 'DIVERGE — STOP'}") if max(dR, dM) >= 1e-12: print(" !! il costruttore non replica: i numeri sulla storia lunga sarebbero di un'altra") print(" macchina. Mi fermo qui.") return for wtest in ({"TP01": 0.25, "SKH01": 0.25, "XS01": 0.50}, {"TP01": 0.75, "SKH01": 0.25}): Ra, Ga = rg(wtest, idx_pub, ref) Rb_, Gb_ = PA.book_RG(wtest) dd1 = float(np.abs(Ra - Rb_).max()); dd2 = float(np.abs(Ga - Gb_).max()) print(f" CONTROLLO: il mio costruttore di LIBRO contro `PA.book_RG` " f"({PA.wkey(wtest)}): max|dR| {dd1:.2e} max|dgap| {dd2:.2e} -> " f"{'IDENTICO' if max(dd1, dd2) < 1e-12 else 'DIVERGE'}") Cb, Ob, Hb, Lb = ohlc_panels(bin_loader, XS_UNIVERSE) p13 = Cb[X.U13].dropna() o13, h13, l13 = Ob.reindex(p13.index)[X.U13], Hb.reindex(p13.index)[X.U13], Lb.reindex(p13.index)[X.U13] xs13 = xs_tuples(p13, o13, h13, l13, phase=0) xsEXP = xs_tuples(Cb, Ob, Hb, Lb, phase=0) bump(2) first13 = xs13.index[np.flatnonzero(xs13["R"].values != 0.0)[0]] firstEX = xsEXP.index[np.flatnonzero(xsEXP["R"].values != 0.0)[0]] print(f"\n XS01 U13 (13 gambe fisse, Binance) prima barra ATTIVA {first13.date()} " f"-> {xs13.index[-1].date()}") print(f" XS01 U19-EXP (10 -> 19 gambe, Binance) prima barra ATTIVA {firstEX.date()} " f"-> {xsEXP.index[-1].date()}") print(" (barra del giorno in corso SCARTATA: una giornaliera parziale in coda e' il difetto") print(" trovato oggi nei monitor forward)") # finestre dichiarate cry_idx = pc.crypto_daily_tuples(1.0, 0.0).index base_idx = cry_idx.intersection(xs13.index) S_LONG = max(first13, pd.Timestamp("2021-01-01", tz="UTC")) S_DISC = pd.Timestamp(PA.START_WIN, tz="UTC") I_LONG = base_idx[base_idx >= S_LONG] I_OOS = I_LONG[I_LONG < S_DISC] I_DISC = base_idx[base_idx >= S_DISC] WINDOWS = (("W-LONG 2021+ (OOS + scoperta)", I_LONG), ("W-OOS 2021-2023 (puro OOS)", I_OOS), ("W-DISC 2024+ (scoperta)", I_DISC)) print(f"\n {'finestra':>32} {'giorni':>7} {'da':>12} {'a':>12}") for lab, ii in WINDOWS: print(f" {lab:>32} {len(ii):>7d} {str(ii[0].date()):>12} {str(ii[-1].date()):>12}") tick("§1 serie lunga") # ================================================== §2. PROFILO SOTTO BARRIERA hr("§2. IL PROFILO CHE CAMBIA — cio' che una barriera STATICA vede: drift, vol, coda, gap") print(" (de-luckato x0.89; `gap peggiore` = l'escursione intraday che la close-only NON vede)") print(f" {'sleeve / finestra':>34} {'Sharpe':>7} {'vol a':>7} {'drift a':>8} {'maxDD':>7} " f"{'g.peggiore':>11} {'gap p90':>9} {'gap peggiore':>13}") prof_rows = [ ("TP01 W-LONG", {"TP01": 1.0}, I_LONG, xs13), ("TP01 W-DISC", {"TP01": 1.0}, I_DISC, xs13), ("SKH01 W-LONG", {"SKH01": 1.0}, I_LONG, xs13), ("SKH01 W-DISC", {"SKH01": 1.0}, I_DISC, xs13), ("XS01 U13 Binance W-OOS", {"XS01": 1.0}, I_OOS, xs13), ("XS01 U13 Binance W-DISC", {"XS01": 1.0}, I_DISC, xs13), ("XS01 U19EXP Binance W-OOS", {"XS01": 1.0}, I_OOS, xsEXP), ("XS01 UFFICIALE HL W-DISC", {"XS01": 1.0}, I_DISC, ref), ] for lab, w, ii, xf in prof_rows: R, G = rg(w, ii, xf) Rd = PA.deluck(R) vol = Rd.std() * np.sqrt(365.0) print(f" {lab:>34} {Rd.mean()*365/vol if vol > 0 else 0:>7.2f} {vol:>6.1%} " f"{Rd.mean()*365:>8.1%} {maxdd(Rd)*100:>6.1f}% {Rd.min():>11.2%} " f"{-np.percentile(-G, 90)*100:>8.2f}pp {G.min()*100:>12.2f}pp") print("\n Le righe da leggere insieme sono XS01 W-OOS e XS01 UFFICIALE: il RENDIMENTO regge") print(" (anzi sale), ma il maxDD e la coda peggiorano — e la barriera guarda la coda.") tick("§2 profilo") # ================================================== §3. LA GRIGLIA RIFATTA hr("§3. LA GRIGLIA RIFATTA SULLA STORIA LUNGA — stesso simplesso, stesse leve, stessa lente") res = {} for lab, ii in WINDOWS: ev = PA.boot_idx(len(ii), EV_H, N_PATHS, seed=SEED_EV) fu = PA.boot_idx(len(ii), FU_H, N_PATHS, seed=SEED_FU) t = time.time() Gx = scan(grid, ii, xs13, ev, fu) res[lab] = dict(G=Gx, ev=ev, fu=fu, idx=ii) b, bl = best_row(Gx), best_row(Gx[Gx["w"] == "75/25/0"]) print(f"\n {lab} ({time.time()-t:.0f}s)") print(f" {'':>18} {'pesi':>10} {'leva':>6} {'J':>7} {'P(pass)':>8} {'P(vivo)':>8} " f"{'Sharpe':>7} {'maxDD':>7} {'E[pay/a]':>10}") for l2, r2 in (("argmax J", b), ("libro LIVE 75/25", bl)): print(f" {l2:>18} {r2['w']:>10} {r2['lev']:>6.3f} {r2['J']:>7.3f} " f"{r2['p_pass']:>8.1%} {r2['p_alive']:>8.1%} {r2['sharpe']:>7.2f} " f"{r2['maxdd']*100:>6.1f}% {r2['e_payout']:>9,.0f}$") d, se = None, None try: Rb, Gb = rg(wdict(b["w"]), ii, xs13) Rl, Gl = rg({"TP01": 0.75, "SKH01": 0.25}, ii, xs13) a1 = obj_rg(Rb, Gb, float(b["lev"]), ev, fu)["jvec"] b1 = obj_rg(Rl, Gl, float(bl["lev"]), ev, fu)["jvec"] dd_ = a1 - b1 d, se = float(dd_.mean()), float(dd_.std(ddof=1) / np.sqrt(len(dd_))) bump(2) except Exception as exc: # pragma: no cover print(f" delta appaiato non calcolato: {exc}") if d is not None: print(f" delta APPAIATO argmax - LIVE : {d:+.4f} +-{se:.4f}" f" (PROP-ALLOC pubblicava {PUB['dJ_vs_live']:+.4f} su W-DISC con XS01 HL)") tick("§3 griglie") # ================================================== §4. LA REGIONE, NON IL PUNTO hr("§4. IL PESO DI XS01 — c'e' una REGIONE robusta su ENTRAMBE le finestre?") print(" PROP-ALLOC ha gia' misurato che il PUNTO non conta (il massimo di 200 pesi casuali") print(" eguagliava l'argmax): la domanda giusta e' se esiste una regione comune. Per ogni") print(" livello di w_XS si riporta il MEGLIO su TP/SKH e su tutte le leve.") xs_levels = sorted({int(round(100 * (i / STEP))) for i in range(STEP + 1)}) print(f"\n {'w_XS':>6} | " + " | ".join(f"{lab.split()[0]:>26}" for lab, _ in WINDOWS)) print(f" {'':>6} | " + " | ".join(f"{'J':>7} {'maxDD':>7} {'pesi':>10}" for _ in WINDOWS)) prof_by_w = {lab: {} for lab, _ in WINDOWS} for wx in xs_levels: cells = [] for lab, _ in WINDOWS: Gx = res[lab]["G"] sub = Gx[Gx["w"].str.endswith(f"/{wx}")] if len(sub) == 0: cells.append(f"{'—':>7} {'—':>7} {'—':>10}") continue r2 = best_row(sub) prof_by_w[lab][wx] = float(r2["J"]) cells.append(f"{r2['J']:>7.3f} {r2['maxdd']*100:>6.1f}% {r2['w']:>10}") print(f" {wx:>5}% | " + " | ".join(cells)) print("\n Regione robusta = livelli di w_XS entro il 3% del meglio di finestra, su TUTTE e tre:") robust = [] for wx in xs_levels: okall = True for lab, _ in WINDOWS: d = prof_by_w[lab] if wx not in d or d[wx] < 0.97 * max(d.values()): okall = False if okall: robust.append(wx) print(f" {robust if robust else 'NESSUN livello comune'}" f" (entro il 5%: " f"{[wx for wx in xs_levels if all(wx in prof_by_w[l] and prof_by_w[l][wx] >= 0.95*max(prof_by_w[l].values()) for l, _ in WINDOWS)]})") tick("§4 regione") # ================================================== §5. LA BANDA ANCORATA AL MISURATO hr("§5. LA BANDA — con gli stress ANCORATI a cio' che e' misurato, non a cio' che era assunto") print(" PROP-ALLOC pubblicava 42% / 7,7% / 0,7% dove il caso severo era 'XS x0,00' = il drift") print(" di XS01 assunto NULLO perche' non misurabile fuori campione. Quel drift ORA E'") print(" MISURATO, e fuori campione e' PIU' ALTO: lo scenario cambia stato, da assunzione a") print(" misura. Cio' che resta assunto e' (a) la fortuna d'ancora di SKH01 (misurata altrove,") print(" non sotto questa lente) e (b) il LISTINO della firm (§6). Gli stress qui sotto sono") print(" quelli, non un pessimismo arbitrario.") st_rows = [] STRESS = ((), (("SKH01", 0.50),), (("SKH01", 0.33),)) SLAB = {(): "nessuno (drift pieno)", (("SKH01", 0.50),): "SKH x0.50 (ancora 26/07)", (("SKH01", 0.33),): "SKH x0.33 (ancora severa)"} for lab, ii in (WINDOWS[0], WINDOWS[1]): Gw, ev, fu = res[lab]["G"], res[lab]["ev"], res[lab]["fu"] top_keys = list(Gw.sort_values("J", ascending=False).drop_duplicates("w")["w"].head(10)) print(f"\n {lab} (argmax ri-cercato sulle {len(top_keys)} celle-peso migliori: e' un") print(f" risparmio di calcolo, ed e' CONSERVATIVO per la stabilita' dell'ottimo)") print(f" {'stress':>28} {'argmax pesi':>12} {'leva':>6} {'J':>7} {'J del LIVE':>11} " f"{'delta appaiato':>16}") for dmt in STRESS: best_j, best_k, best_l, best_v = -1.0, None, None, None for k in top_keys: R, G = rg(wdict(k), ii, xs13, dm=dmt) for lv in LEVS: o = obj_rg(R, G, lv, ev, fu) if o["J"] > best_j: best_j, best_k, best_l, best_v = o["J"], k, lv, o["jvec"] bump(len(top_keys) * len(LEVS)) RL, GL = rg({"TP01": 0.75, "SKH01": 0.25}, ii, xs13, dm=dmt) oL, lvL = None, None for lv in LEVS: o = obj_rg(RL, GL, lv, ev, fu) if oL is None or o["J"] > oL["J"]: oL, lvL = o, lv bump(len(LEVS)) d_ = best_v - oL["jvec"] # guardia contro l'errore del 22/08: un delta appaiato DEVE ereditare lo stress # della riga in cui compare, o confronta due configurazioni non stressate. assert abs(float(d_.mean()) - (best_j - oL["J"])) < 0.05, "delta appaiato incoerente" print(f" {SLAB[dmt]:>28} {best_k:>12} {best_l:>6.3f} {best_j:>7.3f} " f"{oL['J']:>11.3f} {d_.mean():>+11.4f} " f"+-{d_.std(ddof=1)/np.sqrt(len(d_)):.4f}") st_rows.append((lab, SLAB[dmt], best_k, best_l, best_j, oL["J"])) tick("§5 banda") # ================================================== §6. XS01 ASSENTE hr("§6. LO SCENARIO CHE UCCIDE TUTTO — la firm NON lista gli alt (assunto mai verificato)") print(" Non e' uno stress sul drift: e' la RIMOZIONE dello sleeve. Il libro torna a due gambe") print(" e il conto funded diventa il libro live a leva. E' l'unico scenario che non si misura") print(" con un backtest — si verifica leggendo un listino — quindi qui se ne misura il DANNO.") print(f"\n {'finestra':>28} {'con XS01: pesi':>15} {'lev':>5} {'J':>7} | " f"{'SENZA XS01: pesi':>17} {'lev':>5} {'J':>7} | {'J perso':>8} {'quota':>7}") absent = {} for lab, ii in WINDOWS: Gx, ev, fu = res[lab]["G"], res[lab]["ev"], res[lab]["fu"] b = best_row(Gx) sub2 = Gx[Gx["w"].str.endswith("/0")] b2 = best_row(sub2) absent[lab] = b2 lost = float(b["J"]) - float(b2["J"]) print(f" {lab:>28} {b['w']:>15} {b['lev']:>5.2f} {b['J']:>7.3f} | " f"{b2['w']:>17} {b2['lev']:>5.2f} {b2['J']:>7.3f} | {lost:>+8.3f} " f"{lost/max(b['J'],1e-9):>6.0%}") print("\n NB: 'SENZA XS01' e' gia' ri-ottimizzato su TP/SKH e su tutte le leve — e' il MEGLIO") print(" che il libro a due gambe puo' fare, non il 75/25 cablato.") tick("§6 assenza") # ================================================== §7. LA SCALA DI CONTI hr("§7. LA SCALA DI CONTI — P(>=50 EUR/g) da EUR 600 in 36 mesi, lente ACCOPPIATA") print(f" macchineria del 25/07 (`pc.simulate`), de-luck x{PA.DELUCK}, {pl.MAX_CONCURRENT} conti" f" max, morte-firm {pl.FIRM_DEATH_PER_YEAR:.0%}/anno, 1500 percorsi") print(" (le tuple del libro vengono INIETTATE nella cache di `pc.book_tuples`, cosi' la") print(" simulazione e' letteralmente la stessa, con un input diverso)") print(f"\n {'finestra':>28} {'allocazione':>26} {'leva':>6} {'EUR/g med':>10} " f"{'P(>=10/g)':>10} {'P(>=50/g)':>10} {'P(zero)':>8}") def ladder(ii, w: dict, lev: float, tag: str, wlab: str, drop_xs: bool = False) -> dict: R, G = rg(w, ii, xs13, drop_xs=drop_xs) cfg = (float(w.get("TP01", 0.0)), float(w.get("SKH01", 0.0)), 0.0 if drop_xs else float(w.get("XS01", 0.0))) key = (cfg[0], cfg[1], cfg[2], "shared") saved = pc._TUP_CACHE.pop(key, None) pc._TUP_CACHE[key] = (R, G, ii) r = pc.simulate([cfg], [0] * pl.MAX_CONCURRENT, n_paths=1500, lev=lev, seed=20260822, lens="coupled", start=str(ii[0].date())) pc._TUP_CACHE.pop(key, None) if saved is not None: pc._TUP_CACHE[key] = saved print(f" {tag:>28} {wlab:>26} {lev:>6.2f} {r['med']:>10.2f} {r['p_ge10']:>10.1%} " f"{r['p_target']:>10.1%} {r['p_zero']:>8.1%}") return r lad = {} for lab, ii in WINDOWS: Gx = res[lab]["G"] b = best_row(Gx) b2 = absent[lab] short = lab.split()[0] lad[(short, "argmax")] = ladder(ii, wdict(b["w"]), float(b["lev"]), short, f"argmax {b['w']}") lad[(short, "2550")] = ladder(ii, wdict("25/25/50"), 0.75, short, "25/25/50 (PROP-ALLOC)") lad[(short, "noxs")] = ladder(ii, wdict(b2["w"]), float(b2["lev"]), short, f"SENZA XS01 {b2['w']}", drop_xs=True) lad[(short, "live")] = ladder(ii, {"TP01": 0.75, "SKH01": 0.25}, 0.75, short, "LIVE 75/25 @0.75x") bump(12) tick("§7 scala di conti") # ================================================== §8. SINTESI + GATE hr("§8. SINTESI — la banda da citare, e il gate da RI-REGISTRARE") bL = best_row(res[WINDOWS[0][0]]["G"]) bO = best_row(res[WINDOWS[1][0]]["G"]) bD = best_row(res[WINDOWS[2][0]]["G"]) print(f" J e argmax per finestra (XS01 = U13 Binance in tutte e tre, quindi la SOLA cosa che") print(f" cambia fra le righe e' la FINESTRA):") print(f" W-DISC 2024+ argmax {bD['w']:>10} @{bD['lev']:.2f}x J {bD['J']:.3f}") print(f" W-OOS 2021-2023 argmax {bO['w']:>10} @{bO['lev']:.2f}x J {bO['J']:.3f}") print(f" W-LONG 2021+ argmax {bL['w']:>10} @{bL['lev']:.2f}x J {bL['J']:.3f}") print(f" (PROP-ALLOC pubblicato, XS01 HL 19 gambe, W-DISC: 25/25/50 @0.75x J 0.738)") print(f"\n P(>=50 EUR/g) in 36 mesi, banda onesta:") for short in ("W-LONG", "W-OOS", "W-DISC"): row = " | ".join(f"{k}: {lad[(short, k)]['p_target']:.1%}" for k in ("argmax", "2550", "noxs", "live") if (short, k) in lad) print(f" {short:>8} {row}") print(f"\n TRIAL/CELLE DICHIARATI (al rialzo): {TRIALS}") print(f"\n fatto in {time.time()-t0:.0f}s") print("=" * 104) if __name__ == "__main__": main()