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:
@@ -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")
|
||||
|
||||
@@ -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():
|
||||
|
||||
@@ -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%")
|
||||
|
||||
@@ -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.")
|
||||
|
||||
|
||||
Reference in New Issue
Block a user