Files
PythagorasGoal/scripts/research/r0822b_prop_recal.py
T

892 lines
51 KiB
Python

"""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.
ESITO (2026-08-22, corsa completa 309 s, 5.212 celle dichiarate)
----------------------------------------------------------------
* RIPRODUZIONE **ESATTA** dei numeri pubblicati: J 0,738 / 0,721 / 0,335, P(pass) 82,2 / 77,3 /
38,6%, delta appaiati +0,0164 e +0,3996, scala di conti 39,28 EUR/g e P(>=50/g) 41,7%. Tre
controlli bit-exact in piu' (il mio costruttore di tuple = quello del 25/07; il mio costruttore
di libro = `PA.book_RG`, anche sotto stress): **max|diff| = 0,0** su tutti.
* **J NON regge alla taglia pubblicata, e il colpevole NON e' la finestra.** Decomposizione a un
grado di liberta' per volta, tutta sullo stesso pannello: venue (HL -> Binance, 19 gambe,
date allineate) **-0,006**; universo (19 -> 13 gambe) **-0,307**; fase **+0,034**; finestra
(2024+ -> 2021-2023) **+0,061**. Il 42% del numero pubblicato sta nelle **6 gambe che non
esistono fuori campione** (ARB OP SUI APT SEI TIA), non nel periodo.
* Numeri onesti sulla parte testabile (13 gambe), mediana delle 10 fasi di XS01, peso 38/38/25:
J **0,453** su W-LONG 2021+ (LIVE 0,281; delta appaiato **+0,175**, positivo 10/10 fasi),
**0,504** su W-OOS 2021-23, **0,442** su W-DISC. Cioe' ~0,45-0,50 invece di 0,738.
* **La previsione registrata prima e' confermata sui pesi e sbagliata (ottimista) sul livello:**
regione robusta **w_XS = 25-38%** su TUTTE e tre le finestre (prevista [0,25 ; 0,38]) — quindi
il **50% dell'argmax di PROP-ALLOC non sopravvive**; J 0,45-0,52 contro [0,50 ; 0,70] previsto;
P(>=50/g) 7,8% contro [10% ; 35%] previsto.
* **Il numero operativo: P(>=50 EUR/g) da EUR 600 in 36 mesi = 7,8% [banda di fase 7,2-9,1%],
P(zero) 25,5%** — contro il 41,7% pubblicato, e contro **6,6%** se la firm non lista gli alt.
Il libro live sulla stessa lente fa 4,3%.
* Lo scenario severo di PROP-ALLOC (`XS x0,00`) **cambia stato**: non e' piu' "non sappiamo se il
drift esiste" ma un contro-fattuale a rendimento nullo, e li' il delta scende a **+0,041**
(col taglio congiunto SKH x0,33 diventa **-0,024**, cioe' peggio del libro live). Tutto il
vantaggio vive sul drift di XS01 — che ora e' misurato, per 13 gambe su 19.
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.now("UTC").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")
# ================================================== §3-bis. DECOMPOSIZIONE
hr("§3-bis. DA DOVE VIENE IL SALTO 0.738 -> ~0.47 — un grado di liberta' alla volta")
print(" Fra il numero pubblicato e quello di §3 cambiano TRE cose insieme (venue, universo,")
print(" finestra). Sommarle e chiamarle 'l'effetto della storia lunga' sarebbe l'errore che")
print(" il progetto ha gia' pagato due volte. Qui si muove un grado di liberta' per volta,")
print(" sempre sul MIO pannello a 3 gambe (cosi' anche il pannello smette di essere una")
print(" variabile nascosta).")
ev_d, fu_d = res[WINDOWS[2][0]]["ev"], res[WINDOWS[2][0]]["fu"]
ev_o, fu_o = res[WINDOWS[1][0]]["ev"], res[WINDOWS[1][0]]["fu"]
ev_l, fu_l = res[WINDOWS[0][0]]["ev"], res[WINDOWS[0][0]]["fu"]
# ⚠ ERRORE CATTURATO IN SESSIONE, e vale piu' del numero che correggeva: `xsec_engine`
# ribilancia quando `i % H == phase`, dove `i` e' l'indice NEL PANNELLO. Due pannelli che
# cominciano a date diverse ribilanciano quindi in GIORNI DI CALENDARIO diversi: confrontarli
# misura la FASE, non il venue. Alla prima stesura il passo "venue" dava -0.254 di J (e
# avrei pubblicato "Binance costa un quarto di J") mentre XS01-OOS aveva misurato dSharpe
# -0.025 sullo stesso confronto fatto bene. Qui il pannello Binance e' RIALLINEATO alle
# stesse date di quello HL, cosi' la parita' dell'indice — e con essa la fase — coincide.
hl_idx = ref.index
C19 = Cb[list(XS_UNIVERSE)].reindex(hl_idx).dropna()
xs19_al = xs_tuples(C19, Ob.reindex(C19.index)[C19.columns],
Hb.reindex(C19.index)[C19.columns],
Lb.reindex(C19.index)[C19.columns], phase=0)
C13 = Cb[X.U13].reindex(C19.index).dropna()
xs13_al = xs_tuples(C13, Ob.reindex(C13.index)[X.U13], Hb.reindex(C13.index)[X.U13],
Lb.reindex(C13.index)[X.U13], phase=0)
bump(2)
steps = [("XS01 = HL 19 gambe W-DISC (= il pubblicato)", I_DISC, ref, ev_d, fu_d),
("XS01 = BINANCE 19 g. W-DISC allineato (VENUE)", I_DISC, xs19_al, ev_d, fu_d),
("XS01 = BINANCE 13 g. W-DISC allineato (UNIVERSO)", I_DISC, xs13_al, ev_d, fu_d),
("XS01 = BINANCE 13 g. W-DISC pannello lungo (FASE)", I_DISC, xs13, ev_d, fu_d),
("XS01 = BINANCE 13 g. W-OOS (FINESTRA)", I_OOS, xs13, ev_o, fu_o)]
print(f"\n {'passo':>50} {'argmax':>10} {'leva':>6} {'J':>7} {'dJ dal passo prima':>20}")
prev = None
dec = {}
for lab2, ii, xf, ev, fu in steps:
Gs = scan(grid, ii, xf, ev, fu)
b2 = best_row(Gs)
dec[lab2] = b2
dj = "" if prev is None else f"{float(b2['J'])-prev:>+20.3f}"
print(f" {lab2:>50} {b2['w']:>10} {b2['lev']:>6.3f} {b2['J']:>7.3f} {dj}")
prev = float(b2["J"])
print("\n Le righe sono ordinate dal piu' al meno controllabile. Quella che il progetto deve")
print(" guardare e' la piu' grande: se e' la FASE, il numero pubblicato non e' un numero")
print(" di strategia ma di calendario, ed e' il quarto modo d'ingannarsi dell'elenco del")
print(" brief. La finestra fuori campione, da sola, non peggiora nulla — conferma su J cio'")
print(" che XS01-OOS ha misurato sul rendimento.")
print("\n E l'altro capo della banda: universo ESPANDENTE (10 -> 19 gambe man mano che")
print(" vengono quotate), che e' cio' che un implementatore in tempo reale avrebbe avuto:")
print(f" {'finestra':>34} {'argmax':>10} {'leva':>6} {'J':>7} {'maxDD':>7} "
f"{'J con U13':>10}")
for lab2, ii, ev, fu in (("W-OOS 2021-2023", I_OOS, ev_o, fu_o),
("W-LONG 2021+", I_LONG, ev_l, fu_l)):
Gs = scan(grid, ii, xsEXP, ev, fu)
b2 = best_row(Gs)
j13 = float(best_row(res[[w for w in res if w.startswith(lab2.split()[0])][0]]["G"])["J"])
print(f" {lab2:>34} {b2['w']:>10} {b2['lev']:>6.3f} {b2['J']:>7.3f} "
f"{b2['maxdd']*100:>6.1f}% {j13:>10.3f}")
hr("§3-ter. CONTROLLO DI LENTE — quanto sarebbe cieca la close-only su QUESTI numeri")
print(" Su una regola di DD a UN GIORNO la close-only non scatta mai. Non e' un'opinione:")
print(" si vede rigirando le stesse celle con gap = 0.")
bL0 = best_row(res[WINDOWS[0][0]]["G"])
Rc, Gc = rg(wdict(bL0["w"]), I_LONG, xs13)
o_cp = obj_rg(Rc, Gc, float(bL0["lev"]), ev_l, fu_l)
Rd_ = PA.deluck(Rc)
e_cl = PA.eval_sim(Rd_, Gc, ev_l, float(bL0["lev"]), HYRO, lens="close")
f_cl = PA.funded_sim(Rd_, Gc, fu_l, float(bL0["lev"]), HYRO, 100_000.0, lens="close")
bump(2)
print(f" W-LONG argmax {bL0['w']} @{bL0['lev']:.2f}x :"
f" ACCOPPIATA J {o_cp['J']:.3f} (P(pass) {o_cp['p_pass']:.1%}, P(vivo) {o_cp['p_alive']:.1%})")
print(f" {'':>44} CLOSE-ONLY J {e_cl['p_pass']*f_cl['p_alive']:.3f} "
f"(P(pass) {e_cl['p_pass']:.1%}, P(vivo) {f_cl['p_alive']:.1%})")
print(f" -> la close-only regala {e_cl['p_pass']*f_cl['p_alive']-o_cp['J']:+.3f} di J. Ogni")
print(" numero di questo file e' ACCOPPIATO; la close-only e' qui solo come controllo.")
tick("§3-bis/ter decomposizione")
# ================================================== §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")
# ================================================== §4-bis. BANDA DI FASE
hr("§4-bis. LA BANDA DI FASE DI XS01 (H=10) SOTTO LA LENTE PROP — mai girata prima")
print(" XS01 ribilancia ogni 10 giorni: le fasi possibili sono 10 e il progetto misura che la")
print(" fase canonica e' fortunata (audit 02/07: DD al 15° pctl, FULL all'85°). PROP-ALLOC")
print(" gira sulla SOLA fase 0. Qui la si gira tutta, al peso della REGIONE ROBUSTA di §4.")
print(" La stima onesta e' la MEDIANA della banda; il confronto col LIVE e' la MEDIANA delle")
print(" DIFFERENZE APPAIATE (mai la differenza delle mediane).")
W_ROB = "38/38/25"
print(f"\n peso fisso {W_ROB} (centro della regione robusta), leva scelta per fase")
print(f" {'finestra':>28} {'J fase 0':>9} {'MEDIANA':>9} {'banda p10-p90':>16} "
f"{'fasi>LIVE':>10} {'delta appaiato mediano':>24}")
phase_med = {}
for lab, ii in WINDOWS:
ev, fu = res[lab]["ev"], res[lab]["fu"]
RL, GL = rg({"TP01": 0.75, "SKH01": 0.25}, ii, xs13)
jL, lvL = -1.0, None
vL = None
for lv in LEVS:
o = obj_rg(RL, GL, lv, ev, fu)
if o["J"] > jL:
jL, lvL, vL = o["J"], lv, o["jvec"]
js, deltas, bestph = [], [], {}
for ph in range(10):
xsp = xs_tuples(p13, o13, h13, l13, phase=ph)
Rp, Gp = rg(wdict(W_ROB), ii, xsp)
jb, vb = -1.0, None
for lv in LEVS:
o = obj_rg(Rp, Gp, lv, ev, fu)
if o["J"] > jb:
jb, vb = o["J"], o["jvec"]
js.append(jb)
deltas.append(float((vb - vL).mean()))
bestph[ph] = jb
bump(10 * len(LEVS) + len(LEVS))
med = float(np.median(js))
phase_med[lab] = dict(median=med, band=(float(np.percentile(js, 10)),
float(np.percentile(js, 90))),
ph0=js[0], jlive=jL, dmed=float(np.median(deltas)),
npos=int(sum(1 for d in deltas if d > 0)),
argmax_phase=int(np.argmax(js)))
print(f" {lab:>28} {js[0]:>9.3f} {med:>9.3f} "
f"[{np.percentile(js, 10):>6.3f},{np.percentile(js, 90):>6.3f}] "
f"{sum(1 for j2 in js if j2 > jL):>7}/10 "
f"{np.median(deltas):>+18.3f} ({sum(1 for d in deltas if d > 0)}/10 >0)")
print(f"\n J del libro LIVE 75/25 (non dipende dalla fase di XS01): "
f"{phase_med[WINDOWS[0][0]]['jlive']:.3f} W-LONG, "
f"{phase_med[WINDOWS[1][0]]['jlive']:.3f} W-OOS, "
f"{phase_med[WINDOWS[2][0]]['jlive']:.3f} W-DISC")
print(" ⚠ Questa e' la banda della fase di XS01 SOLTANTO. La banda d'ancora di SKH01 (23")
print(" offset, quella che il progetto misura come la piu' grande da restituire) resta")
print(" NON GIRATA sotto questa lente — costo del recon orario. §5 la surroga con un")
print(" taglio del drift, che e' una stima, non una misura.")
tick("§4-bis banda di fase")
# ================================================== §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),),
(("XS01", 0.00),), (("SKH01", 0.33), ("XS01", 0.00)))
SLAB = {(): "nessuno (drift pieno)",
(("SKH01", 0.50),): "SKH x0.50 (ancora 26/07)",
(("SKH01", 0.33),): "SKH x0.33 (ancora severa)",
(("XS01", 0.00),): "XS x0.00 [ORA SUPERATO]",
(("SKH01", 0.33), ("XS01", 0.00)): "SKH x0.33 + XS x0.00 [vecchio severo]"}
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"]))
print(" Le ultime due righe sono gli scenari di PROP-ALLOC. `XS x0.00` significava")
print(" 'il drift di XS01 potrebbe essere zero, non lo sappiamo': dopo XS01-OOS e")
print(" questo file NON e' piu' uno stress plausibile ma un contro-fattuale, perche'")
print(" quel drift e' MISURATO fuori campione ed e' positivo. Resta utile come")
print(" pavimento: dice quanto vale XS01 come pura DECORRELAZIONE, a rendimento nullo.")
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")
hr("§7-bis. LA BANDA DI FASE DEL NUMERO OPERATIVO — P(>=50/g) alle 10 fasi di XS01")
print(" Il numero che l'operatore legge non puo' stare su una fase sola. Peso fisso")
print(f" {W_ROB} (regione robusta), finestra W-LONG, leva per fase = quella che massimizza J.")
ev_l2, fu_l2 = res[WINDOWS[0][0]]["ev"], res[WINDOWS[0][0]]["fu"]
pt, med_eur, zeros = [], [], []
print(f"\n {'fase':>5} {'leva':>6} {'J':>7} {'EUR/g med':>10} {'P(>=10/g)':>10} "
f"{'P(>=50/g)':>10} {'P(zero)':>8}")
for ph in range(10):
xsp = xs_tuples(p13, o13, h13, l13, phase=ph)
Rp, Gp = rg(wdict(W_ROB), I_LONG, xsp)
jb, lvb = -1.0, None
for lv in LEVS:
o = obj_rg(Rp, Gp, lv, ev_l2, fu_l2)
if o["J"] > jb:
jb, lvb = o["J"], lv
cfg = tuple(wdict(W_ROB).get(n, 0.0) for n in W3)
key = (cfg[0], cfg[1], cfg[2], "shared")
saved = pc._TUP_CACHE.pop(key, None)
pc._TUP_CACHE[key] = (Rp, Gp, I_LONG)
r = pc.simulate([cfg], [0] * pl.MAX_CONCURRENT, n_paths=1500, lev=lvb,
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"]); med_eur.append(r["med"]); zeros.append(r["p_zero"])
print(f" {ph:>5} {lvb:>6.3f} {jb:>7.3f} {r['med']:>10.2f} {r['p_ge10']:>10.1%} "
f"{r['p_target']:>10.1%} {r['p_zero']:>8.1%}")
bump(10 * len(LEVS))
print(f"\n MEDIANA su 10 fasi: EUR/g {np.median(med_eur):.2f} | "
f"P(>=50/g) {np.median(pt):.1%} [p10 {np.percentile(pt,10):.1%}, "
f"p90 {np.percentile(pt,90):.1%}] | P(zero) {np.median(zeros):.1%}")
print(f" Contro il PUBBLICATO di PROP-ALLOC: P(>=50/g) 41.7%, P(zero) 13.9% (fase 0, XS01 a")
print(f" 19 gambe, finestra di scoperta).")
ladder_band = (float(np.median(pt)), float(np.percentile(pt, 10)), float(np.percentile(pt, 90)),
float(np.median(zeros)))
tick("§7-bis banda di fase della scala")
# ================================================== §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 Gli stessi J, ma alla MEDIANA delle 10 fasi di XS01 (peso fisso 38/38/25):")
for lab, _ in WINDOWS:
pm = phase_med[lab]
print(f" {lab:>28} fase 0 {pm['ph0']:.3f} -> MEDIANA {pm['median']:.3f} "
f"[{pm['band'][0]:.3f},{pm['band'][1]:.3f}] LIVE {pm['jlive']:.3f} "
f"delta mediano {pm['dmed']:+.3f} ({pm['npos']}/10 >0)")
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 P(>=50 EUR/g) — IL NUMERO DA CITARE (W-LONG, peso {W_ROB}, mediana di 10 fasi):")
print(f" {ladder_band[0]:.1%} [banda di fase {ladder_band[1]:.1%} - {ladder_band[2]:.1%}]"
f" P(zero) {ladder_band[3]:.1%}")
print(f" contro il 41.7% pubblicato (fase 0, XS01 a 19 gambe, finestra di scoperta)")
print(f" e contro il {lad[('W-LONG', 'noxs')]['p_target']:.1%} se la firm non lista gli alt.")
hr("§8-bis. IL GATE DA RI-REGISTRARE — `GATE PROP-01`")
print(""" SOSTITUISCE il gate del 22/08 ("non aprire un funded prima che XS01 abbia una
finestra fuori dal 2024-2026"), che e' CHIUSO: la finestra esiste, e' stata misurata, e
NON e' lei il problema (+0.061 di J, cioe' migliora). Tre gambe, in ordine di costo
crescente, perche' la prima puo' chiudere la questione da sola.
(a) LISTINO — VERIFICA, non misura. Costo EUR 0. BLOCCANTE, prima di qualunque spesa,
nessuna data perche' precede tutto.
Leggere il listino della firm e contare quante delle 13 gambe U13 (BTC ETH BNB XRP
LTC ADA LINK DOGE SOL AVAX NEAR AAVE INJ) sono negoziabili CON SHORT ABILITATO.
PASS se >= 10 (il meccanismo congelato ha `min_assets = 10`: sotto, non ribilancia).
FAIL -> il canale non si chiude, ma si valuta sulla riga SENZA XS01: J 0.38-0.40
invece di 0.45-0.52, P(>=50/g) ~6.6% invece di ~10%, e soprattutto la
raccomandazione "riallocare per la barriera" DECADE (senza XS01 l'argmax e' 38/62 o
50/50, cioe' un tilt su SKH01, lo sleeve che il progetto misura come il piu' fragile).
E' la stessa classe dell'errore GTAA01/PRIIPs: 5 settimane di misure su un'assunzione
mai verificata sul conto reale, e il controllo costo' un ordine di prova.
(b) BANDA D'ANCORA DI SKH01 SOTTO LA LENTE PROP — MISURA. Data: 2026-10-31.
E' l'unica distorsione dichiarata due volte (PROP-ALLOC e questo file) e mai rimossa.
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; banda di FASE di XS01, che ho potuto girare: +0.175, positiva in 10/10.
Mi aspetto un PASS e lo dichiaro prima. FAIL <= +0.05 -> il vantaggio era calendario.
(c) CAPITALE DEL BIGLIETTO — condizione, non data. Un'eval HYRO $100k costa $579 su un
conto di $635. E' gia' dentro la simulazione della scala (e' gran parte del P(zero)
del 26-34%), ma va detto: la prima eval consuma il 91% del conto vivo.
COSA FA FALLIRE L'INTERO RISULTATO
1. il listino non ha 10 gambe shortabili -> (a);
2. la banda d'ancora di SKH01 azzera il delta -> (b);
3. si continua a citare 0.738. E' un numero a 19 GAMBE, e le 6 che lo separano dalle
13 testabili valgono -0.307 di J MISURATI sulla stessa finestra e sullo stesso
venue = il 42% del totale. Quelle 6 (ARB OP SUI APT SEI TIA) non hanno storia
fuori campione E sono state incluse nell'universo nel 2026-06 guardando la
liquidita' HL del 2024+, cioe' dentro la finestra di scoperta.""")
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()