892 lines
51 KiB
Python
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()
|