research(wave-0822): lo scettico RITIRA il difetto di contabilita' (non esiste, bit-exact) e chiude oggi il gate del 22/12 — il meccanismo e' un filtro di frequenza

This commit is contained in:
Adriano Dal Pastro
2026-08-22 17:43:33 +00:00
parent b6c0deb866
commit 1750b5a93b
5 changed files with 240 additions and 25 deletions
+88 -3
View File
@@ -356,9 +356,16 @@ def liquidita(rows: list[dict], tab: dict) -> None:
s = stat[f]
print(f"{f:<10} {s['oi']:>11} {rA[f]:>8} {s['puts']:>10} {s['two']:>10} "
f"{s['pct']:>9.0%} {rB[f]:>8}")
print("\n 📌 LA CLASSIFICA SI ROVESCIA. SOL_USDC e' 1° per open interest e ULTIMO per quote")
print(" presenti; BTC_USDC, dichiarato 'praticamente morto' (5 strumenti con OI>=100), ha")
print(" quasi tutti i put quotati a due lati. L'open interest misura posizioni ACCUMULATE,")
# la correlazione di rango si CALCOLA, non si racconta: sulle sole famiglie lineari,
# cioe' quelle su cui la domanda del filone e' stata posta.
a = np.array([rA[f] for f in LINEAR], float)
c = np.array([rB[f] for f in LINEAR], float)
rho = float(np.corrcoef(a, c)[0, 1])
print(f"\n 📌 LA CLASSIFICA SI ROVESCIA. Correlazione di rango fra i due criteri sulle sei")
print(f" famiglie lineari: {rho:+.2f}. SOL_USDC e' 1ª per open interest fra le lineari e")
print(" ULTIMA di tutte per quote presenti; BTC_USDC, dichiarato 'praticamente morto' (5")
print(" strumenti con OI>=100), ha quasi tutti i put quotati a due lati. L'open interest")
print(" misura posizioni ACCUMULATE,")
print(" non la possibilita' di negoziare ADESSO — e sull'universo lineare le due cose non")
print(" hanno quasi nulla a che vedere.")
print(" ⚠️ Ricaduta sul codice di produzione: `collect_chain.py` prefiltra a OI>=100. Sulle")
@@ -366,6 +373,28 @@ def liquidita(rows: list[dict], tab: dict) -> None:
print(" suo nome lo lascia credere. 4ª occorrenza di 'una riga presente non e' un dato")
print(" presente' — qui in versione: 'una posizione aperta non e' un prezzo negoziabile'.")
print("\n" + "-" * 104)
print("2b. E ALLORA DOV'E' LA LIQUIDITA' CHE SI VEDE NEL VOLUME? Nel mercato a BLOCCHI,")
print(" che ha un minimo d'ordine suo — e non e' raggiungibile con $635.")
print("-" * 104)
print(f"{'famiglia':<10} {'con volume 24h':>15} {'volume 24h $':>14} {'blocco min':>11} "
f"{'blocco min $':>13} {'x il conto':>11}")
for f in FAMS:
sub = [r for r in rows if r["fam"] == f]
if not sub:
continue
nv = sum(1 for r in sub if r["vol"] > 0)
vusd = sum(r["vol_usd"] for r in sub)
bm = sub[0]["blockmin"]
bm_usd = bm * tab[f]["S"] * (1.0 if not is_inv(f) else 1.0)
print(f"{f:<10} {nv:>15} {vusd:>14,.0f} {bm:>11g} {bm_usd:>13,.0f} "
f"{bm_usd/CAPITAL:>10.0f}x")
print("\n 📌 SOL_USDC ha molti piu' strumenti con volume nelle ultime 24h che quotati a due")
print(" lati: quelle opzioni si negoziano, ma via blocchi/RFQ. Il minimo di blocco e' un")
print(" fatto del venue, non un'opinione, e a $635 e' fuori portata di due ordini di")
print(" grandezza. **Non si conclude che il mercato alt non esista: si conclude che la parte")
print(" a cui questo conto ha accesso — il book continuo — e' quella sottile.**")
# ---------------------------------------------------------------- §3 la struttura
@@ -461,9 +490,65 @@ def struttura(rows: list[dict]) -> dict:
f"{len(gi):>12} {len(ge):>11}")
print(" Se la colonna `eseg.` si allontana dal target -0.28/-0.10, la struttura che si")
print(" comprerebbe NON e' quella richiesta: la griglia degli strike quotati la deforma.")
print("\n --- CONTROLLO POSITIVO: questo snapshot e' di SABATO. E' atipico? ---")
print(" Non posso aspettare una settimana, ma posso puntare lo stesso strumento su una")
print(" grandezza che il progetto ha GIA' misurato su 19 settimane di giorni feriali:")
print(" attraversare lo spread sul canonico BTC/ETH inverse costa ~10% del credito (30/07).")
for f in INVERSE:
if f in res:
print(f" {f:<4} inverse: costo di attraversamento oggi = "
f"{(1-res[f]['fv'])*100:5.1f}% del credito (atteso ~10%)")
print(" Se coincide, il sabato non sta degradando i book che il progetto conosce, e la")
print(" lettura sulle famiglie nuove non e' un artefatto del giorno. **E' un controllo")
print(" debole** (una grandezza sola, un istante), non una prova di tipicita'.")
robustezza_dte(rows)
return res
def robustezza_dte(rows: list[dict]) -> None:
"""L'UNICO grado di liberta' di questo file, esposto invece che nascosto.
VRP01 e' settimanale (tenor 7g, finestra 4-10). Qui la finestra e' stata allargata a 4-45
per dare piu' scadenze alle famiglie alt, che ne hanno poche. Allargare una finestra e'
una scelta, e una scelta va misurata: se la conclusione dipendesse da essa, sarebbe la
conclusione a essere stata scelta.
"""
global DTE_HI
print("\n --- ROBUSTEZZA: la finestra DTE e' il mio unico grado di liberta' ---")
print(f" {'famiglia':<10} {'finestra':>9} {'scad':>5} {'f_venue':>8} {'spr% corta':>11} "
f"{'cred eseg$':>11} {'maxloss$':>9}")
orig = DTE_HI
try:
for hi in (10.0, 45.0):
DTE_HI = hi
for f in FAMS:
gg = scegli(rows, f, eseguibile=True)
fv, ss, ce, ml = [], [], [], []
for g in gg:
s, lo = g["short"], g["long"]
cm = usd(s["mark"], s) - usd(lo["mark"], lo)
c = usd(s["bid"], s) - usd(lo["ask"], lo)
if cm <= 0:
continue
fv.append(c / cm); ss.append(rel_spread(s)); ce.append(c)
ml.append(loss_usd(s["K"], lo["K"], s) - c)
w = f"{DTE_LO:.0f}-{hi:.0f}g"
if not fv:
print(f" {f:<10} {w:>9} {len(gg):>5} {'n/d':>8}")
continue
print(f" {f:<10} {w:>9} {len(fv):>5} {np.median(fv):>8.3f} "
f"{np.nanmedian(ss)*100:>10.1f}% {np.median(ce):>11.2f} "
f"{np.median(ml):>9.2f}")
print()
finally:
DTE_HI = orig
print(" 📌 Da leggere sull'ORDINE, non sulla terza cifra: le quote si muovono fra un giro e")
print(" l'altro (su tre snapshot a pochi minuti ETH_USDC ha dato 0.947-0.983, le inverse")
print(" 0.847-0.942, le alt 0.79-0.87, SOL sempre negativo). Cio' che NON si muove e' la")
print(" graduatoria, e alla finestra VERA di VRP01 (4-10g) e' piu' netta, non meno.")
def profondita(rows: list[dict], fams: list[str], b: Budget) -> None:
print("\n" + "=" * 104)
print("3b. PROFONDITA' IN CIMA AL BOOK (public/get_order_book depth=5) sulle gambe scelte")
+47 -8
View File
@@ -129,8 +129,10 @@ def read_equity_csv(p: Path, col: int = 1) -> tuple[list[pd.Timestamp], np.ndarr
# della giornata e' finita nella serie": in radice di tempo il rapporto di VARIANZA fra le due
# serie e' la frazione di giornata misurata (ipotesi: random walk sull'intervallo troncato).
# --------------------------------------------------------------------------------------------
def compare(rec: np.ndarray, rep: np.ndarray) -> dict:
def compare(rec: np.ndarray, rep: np.ndarray, mask: np.ndarray | None = None) -> dict:
ok = np.isfinite(rec) & np.isfinite(rep)
if mask is not None:
ok &= mask
a, b = rec[ok], rep[ok]
n = len(a)
if n < 3:
@@ -145,18 +147,53 @@ def compare(rec: np.ndarray, rep: np.ndarray) -> dict:
sh_rep=float(b.mean() / b.std() * ANN) if b.std() > 0 else float("nan"))
def show(tag: str, c: dict, note: str = "") -> None:
def show(T: pd.DatetimeIndex, rec: np.ndarray, rep: np.ndarray, note: str = "") -> dict:
"""Stampa il confronto e ritorna la statistica sulla finestra PULITA (senza l'incidente)."""
c = compare(rec, rep)
fuori = ~((T >= FREEZE[0]) & (T <= FREEZE[1]))
cf = compare(rec, rep, mask=fuori)
print(f" barre appaiate {c['n']} identiche entro 1e-6: {c['ident']}/{c['n']} "
f"corr {c['corr']:+.4f}")
print(f" vol annualizzata REGISTRATA {c['sd_rec']:6.2f}% RICALCOLATA {c['sd_rep']:6.2f}%"
f" rapporto di varianza {c['vr']:.4f}")
print(f" -> il monitor misura ~{c['minuti']:,.0f} minuti di mercato al giorno (su 1440){note}")
if cf["n"] != c["n"]:
print(f" ESCLUSO l'incidente del feed 09-15/07 ({c['n'] - cf['n']} barre recuperate a "
f"posteriori, quindi CHIUSE):")
print(f" barre {cf['n']} identiche {cf['ident']}/{cf['n']} corr {cf['corr']:+.4f}"
f" vol reg. {cf['sd_rec']:.2f}% vs ric. {cf['sd_rep']:.2f}%"
f" -> ~{cf['minuti']:,.0f} min/giorno")
print(f" Sharpe della finestra: REGISTRATO {c['sh_rec']:+.2f} RICALCOLATO {c['sh_rep']:+.2f}"
f" [SE ~{np.sqrt(365.0 / max(c['n'], 1)):.2f}: NON e' una lettura di gate]")
return dict(all=c, clean=cf)
RES: dict[str, dict] = {} # riepilogo per la tabella finale
# --------------------------------------------------------------------------------------------
# L'INCIDENTE DEL FEED (2026-07-09 -> 2026-07-15). Non e' un'assunzione: e' documentato nel
# sorgente di produzione (`rebuild_history.py`, commento "incidente 2026-07-15: feed fermo 7g" —
# `shutil.copy2` del backup falliva con EPERM e ABORTIVA il rebuild) ed e' verificabile nel log
# del cron. Conta qui per una ragione precisa: durante il congelamento i monitor NON trovavano
# barre nuove, e al ripristino hanno recuperato in un colpo solo barre gia' CHIUSE — cioe' le
# uniche barre corrette delle loro serie. Vanno escluse da ogni stima del difetto, o si finisce
# per attribuire al difetto un'attenuazione che viene da un guasto diverso.
FREEZE = (pd.Timestamp("2026-07-09", tz="UTC"), pd.Timestamp("2026-07-16", tz="UTC"))
CRONLOG = ROOT / "logs" / "cron_daily.log"
def freeze_evidence() -> dict[str, int]:
"""Giorni in cui `rebuild_history` e' ABORTITO nel cron (letto dal log, non assunto)."""
out: dict[str, int] = {}
day = None
lines = CRONLOG.read_text(errors="replace").splitlines() if CRONLOG.exists() else []
for i, ln in enumerate(lines):
if ln.startswith("===== 2026-") and "cron_daily" in ln:
day = ln.split()[1][:10]
if day and "rebuild_history.py" in ln and "Traceback" in "".join(lines[max(0, i - 3):i]):
out[day] = out.get(day, 0) + 1
return out
# ============================================================================================
# A — PROVENIENZA: quanto e' completa l'ultima riga di ogni sorgente
@@ -245,12 +282,14 @@ def b_xsr():
reals={n: px._book(c, len(st["syms"])) for c, n in px.REAL_BOOKS})
px.advance(st0)
rep = {int(r["ts"]): float(r["net_modeled"]) for r in sink.of("returns.jsonl")}
pairs = [(float(r["net_modeled"]), rep[int(r["ts"])]) for r in rec if int(r["ts"]) in rep]
c = compare(np.array([p[0] for p in pairs]), np.array([p[1] for p in pairs]))
print(f"\n pannello: `build_panel` -> `load_hl` -> parquet HL 1d (ultima riga = giorno in corso)")
show("xsr", c)
RES["paper_xsr"] = dict(parziale="SI", cmp=c, gate="XSR01 2026-10-23",
src="hl_*_1d.parquet", cad_h=24.0)
pairs = [(int(r["ts"]), float(r["net_modeled"]), rep[int(r["ts"])])
for r in rec if int(r["ts"]) in rep]
T = pd.DatetimeIndex(pd.to_datetime([p[0] for p in pairs], unit="ms", utc=True))
print("\n pannello: `build_panel` -> `load_hl` -> parquet HL 1d (ultima riga = giorno in corso)")
print(f" inception {T[0].date()} = DOPO l'incidente del feed: la finestra e' gia' pulita.")
out = show(T, np.array([p[1] for p in pairs]), np.array([p[2] for p in pairs]))
RES["paper_xsr"] = dict(parziale="SI", cmp=out["clean"], gate="XSR01 2026-10-23",
src="hl_*_1d.parquet", cad_h=24.0, ric="SI (bit-exact)")
def b_statarb():
+58 -8
View File
@@ -186,10 +186,41 @@ def _d(s: pd.Series) -> pd.Series:
_CRY_CACHE: dict = {}
def _crypto(w_tp: float, w_skh: float) -> pd.DataFrame:
key = (round(w_tp, 6), round(w_skh, 6))
def _crypto_stressed(w_tp: float, w_skh: float, m_tp: float, m_skh: float) -> pd.DataFrame:
"""Come `pc.crypto_daily_tuples` (minimo ESATTO sul path orario condiviso) ma con un taglio
del DRIFT applicato alla singola gamba PRIMA di comporre il path. Serve perche' il minimo
giornaliero non e' separabile per gamba: non si puo' tagliare il drift a valle."""
(J,) = pc._hourly_legs()
legs = {}
for nm, (rc, wc, mult) in {"tp": ("tp_r", "tp_w", m_tp), "sk": ("skh_r", "skh_w", m_skh)}.items():
r = J[rc].values.copy()
w = J[wc].values.copy()
if mult != 1.0:
cut = (1.0 - mult) * r.mean()
r = r - cut
w = w - cut
legs[nm] = (r, w)
r_h = w_tp * legs["tp"][0] + w_skh * legs["sk"][0]
w_h = w_tp * legs["tp"][1] + w_skh * legs["sk"][1]
idx = J.index
days = idx.floor("D")
rows = []
for day, pos in pd.Series(np.arange(len(idx)), index=days).groupby(level=0):
sl = pos.values
r = r_h[sl]
wk = w_h[sl]
cum = np.cumprod(1.0 + r)
cum_prev = np.concatenate(([1.0], cum[:-1]))
m = min(cum.min(), (cum_prev * (1.0 + wk)).min()) - 1.0
rows.append((day, cum[-1] - 1.0, min(m, cum[-1] - 1.0)))
return pd.DataFrame(rows, columns=["day", "R", "m"]).set_index("day")
def _crypto(w_tp: float, w_skh: float, m_tp: float = 1.0, m_skh: float = 1.0) -> pd.DataFrame:
key = (round(w_tp, 6), round(w_skh, 6), round(m_tp, 4), round(m_skh, 4))
if key not in _CRY_CACHE:
_CRY_CACHE[key] = pc.crypto_daily_tuples(w_tp, w_skh)
_CRY_CACHE[key] = (pc.crypto_daily_tuples(w_tp, w_skh) if (m_tp == 1.0 and m_skh == 1.0)
else _crypto_stressed(w_tp, w_skh, m_tp, m_skh))
return _CRY_CACHE[key]
@@ -221,11 +252,8 @@ def book_RG(w: dict, start: str = START_WIN,
R = np.zeros(len(idx))
M = np.zeros(len(idx))
if w_tp > 0 or w_skh > 0:
c = _crypto(w_tp, w_skh).reindex(idx)
cr = c["R"].values
if w_tp > 0 and dm.get("TP01", 1.0) != 1.0 or w_skh > 0 and dm.get("SKH01", 1.0) != 1.0:
raise NotImplementedError("stress sul drift crypto: il minimo e' path-esatto, non separabile")
R += cr
c = _crypto(w_tp, w_skh, dm.get("TP01", 1.0), dm.get("SKH01", 1.0)).reindex(idx)
R += c["R"].values
M += c["m"].values
for nm, fr in (("XS01", pc.xsec_daily_tuples()), ("VRP01", vrp_daily_tuples()),
("GTAA01", gtaa_daily_tuples())):
@@ -702,6 +730,28 @@ def main() -> None:
print(" scoperta; se resta a drift 0, il vantaggio e' pura DECORRELAZIONE e non dipende da")
print(" quell'ipotesi. Il numero, non la narrazione, dice quale delle due.")
hr("E-quater. LA DISTORSIONE CHE MI RIGUARDA — l'ottimo si appoggia a SKH01, e SKH01 e' lo")
print(" sleeve con la FORTUNA D'ANCORA piu' grande da restituire. Il LOO del 26/07: l'ancora")
print(" canonica gli regala 2/3 del contributo FULL (+0.43 -> +0.14) e il 70% dell'hold-out;")
print(" l'audit 02/07 lo trova al 93-98° pctl di 23 offset, 'spike, non plateau'. Questa lente")
print(" gira sull'ancora CANONICA e la banda non e' ri-misurabile qui -> il mio ottimo e'")
print(" distorto A FAVORE di SKH01 e va detto PRIMA di leggerlo. Taglio del drift di SKH01:")
print(f"\n {'drift SKH01':>13} {'config':>16} {'leva':>6} {'J':>7} {'J del LIVE':>11} "
f"{'delta appaiato':>16}")
for mult in (1.00, 0.66, 0.50, 0.33):
dmt = (("SKH01", mult),)
rows = [(w, lv, objective(w, lv, "HYRO", ev_idx, fu_idx, drift_mult=dmt))
for w in top_w for lv in LEVS]
w_, lv_, o_ = max(rows, key=lambda x: x[2]["J"])
lvL, oL = max(((lv, objective({"TP01": 0.75, "SKH01": 0.25}, lv, "HYRO", ev_idx, fu_idx,
drift_mult=dmt)) for lv in LEVS), key=lambda x: x[1]["J"])
dd_, sd_ = paired_delta(w_, lv_, {"TP01": 0.75, "SKH01": 0.25}, lvL, "HYRO",
ev_idx, fu_idx)
print(f" {mult:>13.2f} {wkey(w_):>16} {lv_:>6.3f} {o_['J']:>7.3f} {oL['J']:>11.3f} "
f"{dd_:>+11.3f} +-{sd_:.3f}")
print(" NB: il taglio si applica anche al LIVE 75/25 (che ha SKH01 al 25%), quindi il delta e'")
print(" un confronto onesto: entrambe le colonne perdono, la domanda e' quale perde di piu'.")
hr("E-ter. IL BUCO CHE LA FINESTRA NON PUO' CHIUDERE — 2024-2026 non contiene un CRASH")
print(" L'ottimizzazione toglie peso a TP01 (Sharpe 0.79 su questa finestra). Ma TP01 e' lo")
print(" sleeve DIFENSIVO: il LOO del 26/07 misura il suo contributo hold-out NEGATIVO nel 99.1%")
+4 -3
View File
@@ -252,9 +252,10 @@ def main() -> None:
FUN_V1["VTL tv20 w90 (LEAD)"] = size_vtl(FROZEN)
FUN_V1["BIN L>=1 (0/1)"] = size_bin(FROZEN, "ge1")
NTRIAL = len(FUN_V2) + len(FUN_V1) + 2 # +2 baseline
print(f"\n GRIGLIA DICHIARATA: {len(FUN_V2)} celle su V2-DD + {len(FUN_V1)} su V1 "
f"+ 2 baseline = {NTRIAL} valutazioni x {len(offs)} ancore. Nessuna cella scelta")
NTRIAL = len(FUN_V2) + len(FUN_V1) + 2 + 4 # +2 baseline, +4 controlli 'L combinata' (§1c)
print(f"\n GRIGLIA DICHIARATA (contata al RIALZO): {len(FUN_V2)} celle su V2-DD + "
f"{len(FUN_V1)} su V1 + 4 controlli 'L combinata' (§1c) + 2 baseline = {NTRIAL} "
f"valutazioni x {len(offs)} ancore. Nessuna cella scelta")
print(" dopo aver guardato: IV e' la famiglia PRE-REGISTRATA, VTL/w e' l'asse dichiarato")
print(" fragile dall'agente, BIN/DROP sono controlli miei dichiarati sopra.")