Files
PythagorasGoal/scripts/research/r0823_skeptic_spot.py
T
Adriano Dal Pastro f5ebe689da skeptic(spot): il lead REGGE a $600 e si ANNULLA alla taglia del muro che sposta
§52 SKEPTIC-SPOT — attacco deliberato a §39/§45 (spot al posto del perpetual per
la gamba TP01). Sei attacchi, replica bit-exact prima di ognuno (max|dif| = 0.0 su
tp01_realistic; +2,106% di sleeve e +1,580% di libro protetti da assert).

Esiti: 1 allineamento REGGE · 2 controfattuale INCRINA · 3 tetto 1,0x REGGE ·
4 spread/profondita' INCRINA · 5 haircut REGGE · 6 disaster-SL REGGE.

Il risultato che cambia una conclusione e non una presentazione: il differenziale
di cammino spot-vs-perp e' +0,72 bps a $600 (-0,04%/anno) e +25,66 bps a $272k
(-1,54%/anno = il 97% del lead lordo). Il +1,55% e' misurato a una taglia a cui
l'esecuzione e' gratis e speso dentro un muro da $290k, dove non lo e': il muro
NON si sposta del 10,9% pubblicato.

Trovato invece che la storia dello spot ESISTE (get_tradingview_chart_data serve
BTC_USDC/ETH_USDC dal 2023-04-24, 29.196 barre orarie, 0 gap): §39 la dichiarava
assente. TP01 girato sul prezzo spot VERO contro il perp, 24 ancore appaiate, da'
+0,166%/anno di sleeve con la banda che contiene lo zero (16/24) -> il
controfattuale sul PREZZO e' innocuo. Ma lo strumento non esisteva per il 55,3%
del campione e nel 2023 aveva ~9% di ore senza scambi: il lordo passa da +1,580%
(7,4 anni) a +1,450% (era spot) a +1,180% (era liquida, 2024+).

Attacchi falliti, e vanno detti: il tetto 1,0x non morde (3 giorni su BTC, tutti
nel 2018-11 e in PERDITA: troncare avrebbe fatto guadagnare); nessun haircut <=100%
rende binding il margine (copertura 50,0x replicata al decimo); il disaster-SL tolto
a TP01 vale 0,15%/anno ammortizzato allo scenario operativo.

Tre errori miei, catturati e dichiarati nello script: (a) confrontavo il costo di
SPREAD dello spot con la banda NETTA di §45 — mele contro pere, nel verso che mi
conveniva: like-with-like le due bande si sovrappongono; (b) l'aritmetica del
margine metteva SKH01 a 1,0x su ciascun asset invece che sul proprio sleeve
(25,0x invece di 50,0x: §45 aveva ragione); (c) ammortizzavo su 7,4 anni la coda
di un blackout da 30 giorni preso come argmax, ottenendo 1,97%/anno = il 127% del
lead da un evento mai accaduto.

Squalificato dal proprio controllo positivo: gli stimatori di spread da OHLC
(Roll, Corwin-Schultz) attribuiscono 4-16 bps al perpetual, che e' a un tick ->
numeri cancellati, non riportati.

Correzione a §45: «lo spot non si liquida» e' falso — BTC/ETH hanno
in_cross_collateral_pool: true, quindi sono collaterale liquidabile (non binding).

Numero onesto rivisto: [+1,38%, +1,58%]/anno a $600, -0,11%/anno a $272k.
Sola lettura provata (git status -- src/ config/ scripts/live/ tests/ data/ vuoto),
0 ordini, solo GET pubbliche con pacing e astensione nei minuti :05-:10 e :24-:30.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018M8Ncho6QV9FWLdyy4VyQf
2026-08-23 02:19:33 +00:00

944 lines
54 KiB
Python

#!/usr/bin/env python3
"""r0823_skeptic_spot.py — §52 SKEPTIC-SPOT: attacco deliberato al lead dello SPOT.
BERSAGLIO
---------
§39 (`r0822e_funding_avoid`) e §45 (`r0823_spot_netting`) concludono che esprimere la gamba
TP01 sullo SPOT Deribit (`BTC_USDC`/`ETH_USDC`) invece che sul perpetual vale
**+1,55%/anno di drift di libro**, muro $325,0k -> $289,6k (-10,9%), netto [+1,55%, +1,66%].
Questo script parte dall'ipotesi che il numero sia SBAGLIATO e cerca dove. Sei attacchi,
in ordine di priorita' dichiarato. Ogni attacco chiude con REGGE / INCRINA / FALSIFICA.
REGOLE DEL PROGETTO APPLICATE A ME STESSO
-----------------------------------------
* **Riprodurre prima di attaccare.** Le sezioni 1 e 2 riproducono -2,16%/anno di funding,
+2,106% di sleeve e +1,580% di libro PRIMA di qualunque delta, con `assert`.
* **Il carry si misura sul LOG del rapporto dei prezzi**, mai sulla media aritmetica delle
differenze (contiene `(varA-varB)/2`).
* **Ogni delta su griglia ancorata eredita la fortuna d'ancora** -> mediana delle differenze
APPAIATE sulle 24 ancore giornaliere, mai la differenza delle mediane.
* **Epoca in ms ESPLICITA** (mai `.view`/`.astype("int64")` su DatetimeIndex tz-aware).
* Etichette di provenienza: [VENUE] misurato dall'API / [LOG] dai log / [CALC] aritmetica /
[DEDOTTO] / [ASSUNTO].
* **Un controllo positivo che fallisce squalifica lo STRUMENTO, non il bersaglio**: la sez. 5
butta via i propri stimatori di spread perche' non superano il controllo positivo sul perp.
SOLA LETTURA / RETE
-------------------
Nessuna scrittura nel repo. Nessun ordine, mai (solo GET **pubbliche**, nessuna chiave).
Cache dello storico spot FUORI dal repo. Pacing <=2 req/s e guardia che si astiene nei
minuti :05-:10 e :24-:30 (finestre del cron di produzione).
uv run python scripts/research/r0823_skeptic_spot.py # con rete
uv run python scripts/research/r0823_skeptic_spot.py --no-net # solo cache
"""
from __future__ import annotations
import contextlib
import datetime as dt
import io
import json
import sys
import time
import urllib.request
from pathlib import Path
import numpy as np
import pandas as pd
ROOT = Path("/opt/docker/PythagorasGoal")
for _p in (ROOT / "scripts" / "research", ROOT / "scripts" / "research" / "alt", ROOT):
sys.path.insert(0, str(_p))
# r0822d_funding legge `--no-net` da sys.argv all'import: qui il funding viene dalla sua
# cache. Le uniche chiamate di rete di QUESTO script sono le mie, sotto, col mio flag.
_NET = "--no-net" not in sys.argv
if "--no-net" not in sys.argv:
sys.argv.append("--no-net")
import altlib as al # noqa: E402
import r0702_tp01_offset as r02t # noqa: E402 (resample_offset: le 24 ancore)
import r0725_capcurve as CC # noqa: E402 (DERIBIT_FEE_SIDE/MIN_ORDER)
import r0822d_funding as FD # noqa: E402 (build_rate/daily_funding/stat)
from src.data.downloader import load_data # noqa: E402
from src.strategies.trend_portfolio import ( # noqa: E402
CANONICAL, TrendPortfolio, resample_1d, simple_returns)
# ---------------------------------------------------------------------------
# Costanti dichiarate
# ---------------------------------------------------------------------------
ASSETS = ("BTC", "ETH")
ALLOC = 600.0 # il capitale su cui girano §39/§45
W_TP, W_SKH = 0.75, 0.25
EPOCH = pd.Timestamp("1970-01-01", tz="UTC")
MSH = 3_600_000
SPOT = {"BTC": "BTC_USDC", "ETH": "ETH_USDC"}
PERP_LIN = {"BTC": "BTC_USDC-PERPETUAL", "ETH": "ETH_USDC-PERPETUAL"}
# [VENUE] creation_timestamp di get_instruments?currency=USDC&kind=spot (letto in sessione)
SPOT_BIRTH = pd.Timestamp("2023-04-24 13:00", tz="UTC")
API = "https://www.deribit.com/api/v2/public/"
_SP = Path("/tmp/claude-1001/-opt-docker-PythagorasGoal/"
"b6cc75e7-14f8-4c32-bd07-ab8a0d2aaee6/scratchpad")
CACHE = _SP if _SP.parent.exists() else Path("/tmp/pythagoras_skeptic_spot")
# §45, da citare e attaccare
S45_HALF_SPREAD_BPS = (1.5, 3.1) # mezzo spread misurato su DUE istantanee a 4 minuti
S45_ESEC_BAND = (-0.034, +0.077) # contributo netto d'esecuzione, %/anno
S45_NETTO = (1.55, 1.66) # il numero da citare, %/anno di libro
S45_MARGIN_COVER = 50.0 # copertura del margine di SKH01
CAP_TETTO_PUBBL = 0.102 # % di giorni in cui TP01 supera 1,0x (§39)
_last = [0.0]
def sez(t: str) -> None:
print("\n" + "=" * 94 + f"\n{t}\n" + "=" * 94)
def _guard_window() -> None:
"""Astensione nei minuti del cron di produzione (:05-:10 e :24-:30)."""
while True:
m = dt.datetime.now(dt.timezone.utc).minute
if not (5 <= m <= 10 or 24 <= m <= 30):
return
tgt = 11 if m <= 10 else 31
w = (tgt - m) * 60 - dt.datetime.now(dt.timezone.utc).second
print(f" [rete] minuto :{m:02d} e' finestra del cron -> attendo {max(w,1)}s")
time.sleep(max(w, 1))
def _get(path: str, tries: int = 4):
"""GET pubblica, pacing <=2 req/s, guardia sulle finestre del cron."""
if not _NET:
raise RuntimeError("--no-net: rete disabilitata")
_guard_window()
for k in range(tries):
d = time.time() - _last[0]
if d < 0.55:
time.sleep(0.55 - d)
_last[0] = time.time()
try:
with urllib.request.urlopen(API + path, timeout=30) as r:
return json.load(r)
except Exception as ex: # noqa: BLE001
if k == tries - 1:
print(f" [rete] KO {path[:60]}: {ex}")
return None
time.sleep(1.5 * (k + 1))
return None
def to_ms(idx: pd.DatetimeIndex) -> np.ndarray:
"""Epoca in ms ESPLICITA. Mai `.view`/`.astype('int64')` su tz-aware."""
return ((pd.DatetimeIndex(idx) - EPOCH) // pd.Timedelta(milliseconds=1)).values.astype(np.int64)
# ===========================================================================
# 0) LO SPOT: esistenza, nascita, storia
# ===========================================================================
def spot_1h(a: str) -> pd.DataFrame:
"""Storia oraria dello SPOT Deribit, cache fuori dal repo. Schema `al.get`-compatibile."""
f = CACHE / f"spot_{a.lower()}_1h.parquet"
have = pd.read_parquet(f) if f.exists() else pd.DataFrame()
if len(have) and (not _NET or
pd.Timestamp(have["ts"].max()) > pd.Timestamp.now(tz="UTC") - pd.Timedelta("6h")):
d = have.copy()
else:
out = ([have] if len(have) else [])
t = int(SPOT_BIRTH.timestamp() * 1000) if not len(have) else int(
pd.Timestamp(have["ts"].max()).timestamp() * 1000) + MSH
end = int(pd.Timestamp.now(tz="UTC").floor("h").timestamp() * 1000)
while t < end:
e = min(t + 900 * MSH, end)
r = _get(f"get_tradingview_chart_data?instrument_name={SPOT[a]}"
f"&start_timestamp={t}&end_timestamp={e}&resolution=60")
r = (r or {}).get("result", {})
if r.get("status") != "ok" or not r.get("ticks"):
break
out.append(pd.DataFrame(dict(
ts=pd.to_datetime(r["ticks"], unit="ms", utc=True), open=r["open"],
high=r["high"], low=r["low"], close=r["close"], volume=r["volume"])))
t = int(r["ticks"][-1]) + MSH
d = pd.concat(out).drop_duplicates("ts").sort_values("ts").reset_index(drop=True)
CACHE.mkdir(parents=True, exist_ok=True)
d.to_parquet(f)
d = d.rename(columns={"ts": "datetime"})
d["datetime"] = pd.DatetimeIndex(pd.to_datetime(d["datetime"], utc=True))
d["timestamp"] = to_ms(d["datetime"])
return d[["timestamp", "open", "high", "low", "close", "volume", "datetime"]]
def perp_1h(a: str) -> pd.DataFrame:
d = al.get(a, "1h").copy()
d["datetime"] = pd.DatetimeIndex(pd.to_datetime(d["datetime"], utc=True))
return d
# ===========================================================================
# 1) REPLICA — riprodurre i numeri PRIMA di attaccarli
# ===========================================================================
def tp01_leg(df1h: pd.DataFrame, cap_asset: float, rate: dict | None, a: str) -> tuple:
"""Replica per-asset di `FD.tp01_realistic`, con il prezzo come grado di liberta'.
Con (feed perp, rate) deve dare bit-exact `FD.tp01_realistic`: e' la replica di controllo."""
tp = TrendPortfolio(**CANONICAL)
df = resample_1d(df1h)
idx = pd.DatetimeIndex(pd.to_datetime(df["datetime"], utc=True))
r = simple_returns(df["close"].values.astype(float))
tgt = np.nan_to_num(np.asarray(tp.target_series(df), float))
held = np.empty(len(tgt))
cur = 0.0
for i in range(len(tgt)):
if abs(tgt[i] - cur) * cap_asset >= CC.DERIBIT_MIN_ORDER:
cur = tgt[i]
held[i] = cur
pos = np.zeros(len(held))
pos[1:] = held[:-1]
net = pos * r - CC.DERIBIT_FEE_SIDE * np.abs(np.diff(pos, prepend=0.0))
if rate is not None:
net = net - pos * FD.daily_funding(rate[a], idx)
net[0] = 0.0
return pd.Series(np.clip(net, -0.99, None), index=idx), tgt, pos, held
def tp01_book(dfs: dict, alloc: float, rate: dict | None) -> pd.Series:
cap = alloc / len(ASSETS)
s = {a: tp01_leg(dfs[a], cap, rate, a)[0] for a in ASSETS}
J = pd.concat(s, axis=1, join="inner").fillna(0.0)
out = pd.Series(0.5 * J["BTC"].values + 0.5 * J["ETH"].values, index=J.index)
if out.index.tz is None:
out.index = out.index.tz_localize("UTC")
return out
def sezione1() -> dict:
sez("1) REPLICA DI CONTROLLO — i numeri attaccati, riprodotti prima di toccarli")
with contextlib.redirect_stdout(io.StringIO()):
F = FD.sezione0()["F"]
rate = FD.build_rate(F)
ref, dg = FD.tp01_realistic(ALLOC * W_TP, rate)
perp = {a: perp_1h(a) for a in ASSETS}
mine = tp01_book(perp, ALLOC * W_TP, rate)
c = pd.concat([ref, mine], axis=1, join="inner")
dmax = float(np.max(np.abs(c.iloc[:, 0] - c.iloc[:, 1])))
print(f" [CALC] la mia replica di TP01 contro `FD.tp01_realistic`: max|dif| = {dmax:.1e}")
assert dmax == 0.0, "replica non bit-exact: nessun attacco oltre questo punto"
nc = FD.tp01_realistic(ALLOC * W_TP, None)[0]
a0, a1 = FD.stat(nc), FD.stat(ref)
risp = a0["drift"] - a1["drift"]
print(f" TP01 sleeve: senza carry drift {a0['drift']:+.3%} / con perp {a1['drift']:+.3%} "
f"-> risparmio {risp:+.3%} (§45 pubblica +2,11%)")
print(f" a peso di libro x{W_TP}: {risp*W_TP:+.3%} (§39/§45 pubblicano +1,55/+1,58%)")
assert abs(risp - 0.02106) < 3e-4, "il +2,11% di sleeve non replica"
assert abs(risp * W_TP - 0.0158) < 3e-4, "il +1,58% di libro non replica"
print(f" vol dello sleeve: {a0['vol']:.2%} senza carry contro {a1['vol']:.2%} col perp "
f"-> il carry e' un DRIFT, non rischio (gate iso-vol di §39)")
print(" ✅ REPLICATO. Da qui in poi ogni numero e' un delta su questa base.")
return dict(rate=rate, perp=perp, nc=nc, perp_s=ref, risp=risp, dg=dg)
# ===========================================================================
# 2) ATTACCO 1 — il funding e lo spot sono la stessa quantita'? il LAG
# ===========================================================================
def attacco1(rep: dict) -> str:
sez("ATTACCO 1 — «funding misurato su una serie, spot su un'altra»: il LAG e l'oggetto")
print(" §39 ha catturato in sessione uno sfasamento di UN'ORA fra `fund_*.parquet`")
print(" (indice etichettato all'ISTANTE T) e `al.get(a,'1h')` (barra etichettata")
print(" all'INIZIO). Riparato con `aligned_index(...).shift(-1)`. Domanda: la stessa")
print(" famiglia e' altrove? E le due serie parlano davvero dello stesso oggetto?\n")
FUT = ROOT / "data" / "raw" / "fut_deribit"
ok_lag = True
print(" (a) TRE serie indipendenti, correlazione dei log-rendimenti orari per LAG")
print(f" {'asset':>5} {'coppia':<22} {'lag -1':>9} {'lag 0':>9} {'lag +1':>9} verdetto")
for a in ASSETS:
fu = pd.read_parquet(FUT / f"fund_{a}.parquet").set_index("ts").sort_index()
pe = perp_1h(a).set_index("datetime")["close"].astype(float)
sp = spot_1h(a).set_index("datetime")["close"].astype(float) if True else None
g = pe.index.intersection(fu.index).intersection(sp.index)
ix = fu["index"].reindex(g).values.astype(float)
pv = pe.reindex(g).values.astype(float)
sv = sp.reindex(g).values.astype(float)
def corr_lag(x, y, k):
rx, ry = np.diff(np.log(x)), np.diff(np.log(y))
if k > 0:
rx, ry = rx[:-k], ry[k:]
elif k < 0:
rx, ry = rx[-k:], ry[:k]
m = np.isfinite(rx) & np.isfinite(ry)
return float(np.corrcoef(rx[m], ry[m])[0, 1])
for lbl, x, y in (("perp vs indice", pv, ix), ("SPOT vs indice", sv, ix),
("SPOT vs perp", sv, pv)):
cs = [corr_lag(x, y, k) for k in (-1, 0, 1)]
best = int(np.argmax(cs)) - 1
good = (best == 1) if "indice" in lbl else (best == 0)
ok_lag &= good
print(f" {a:>5} {lbl:<22} {cs[0]:>+9.4f} {cs[1]:>+9.4f} {cs[2]:>+9.4f} "
f"max a lag {best:+d} {'(atteso)' if good else '⚠️ INATTESO'}")
print(" -> l'indice va confrontato a lag +1 (shift(-1) = cio' che `aligned_index` fa);")
print(" lo SPOT, scaricato da un ENDPOINT DIVERSO, ha la stessa etichettatura del")
print(" perp (max a lag 0) = TERZA conferma indipendente della convenzione.")
print("\n (b) LA DOMANDA VERA: da dove viene il +1,55%? Non dall'indice.")
grid = {}
for a in ASSETS:
fu = pd.read_parquet(FUT / f"fund_{a}.parquet").set_index("ts").sort_index()
pe = perp_1h(a).set_index("datetime")["close"].astype(float)
g = pe.index.intersection(fu.index)
ix = fu["index"].reindex(g).shift(-1).values.astype(float)
pv = pe.reindex(g).values.astype(float)
m = np.isfinite(ix) & np.isfinite(pv)
rlog = np.diff(np.log(pv[m])) - np.diff(np.log(ix[m]))
rari = np.diff(pv[m]) / pv[m][:-1] - np.diff(ix[m]) / ix[m][:-1]
fund = float(np.mean(rep["rate"][a]["i1h"])) * 24 * 365.25
grid[a] = (float(np.mean(rlog)) * 24 * 365.25, float(np.mean(rari)) * 24 * 365.25, fund)
print(f" {a}: `spot_rate` (perp-indice) LOG {grid[a][0]:+.3%}/anno "
f"ARITMETICO {grid[a][1]:+.3%}/anno (artefatto {grid[a][1]-grid[a][0]:+.3%})")
print(f" funding orario medio, annualizzato: {fund:+.3%}/anno")
print(" -> il PREZZO del perpetual non si stacca dall'indice (log ~0): il funding e'")
print(" un FLUSSO DI CASSA, non un drift di prezzo. Quindi `spot_rate` vale ~0 e")
print(" la variante E) di §39 e' quasi identica alla F) «nessun carry».")
e_min_f = float(np.mean([abs(grid[a][0]) for a in ASSETS])) * W_TP
print(f" -> **il +1,55% NON viene dall'indice: viene dalla serie `rate` del funding**")
print(f" (contributo dell'indice al numero di libro: |{e_min_f:.3%}|/anno).")
print(" E quella serie e' gia' validata contro il conto vero in §35 (ETH -0,2% /")
print(" BTC -7,7% contro `realized_pnl` di sessione). Il vettore d'attacco 1")
print(" punta a un pezzo che pesa quasi nulla.")
v = "REGGE" if ok_lag else "INCRINA"
print(f"\n ESITO ATTACCO 1: **{v}** — nessuno sfasamento residuo su 6 coppie/2 asset, e la")
print(" quantita' attaccata (l'indice) contribuisce <0,01%/anno al numero pubblicato.")
return v
# ===========================================================================
# 3) ATTACCO 2 — controfattuale o misura? lo SPOT VERO
# ===========================================================================
def attacco2(rep: dict) -> tuple[str, dict]:
sez("ATTACCO 2 — «lo spot e' un mercato a se'»: quanto del lead e' MISURATO e quanto MODELLATO")
print(" §39 dichiara in un commento: «qui lo spot e' modellato con l'INDICE Deribit, non")
print(" col libro spot vero (che non ha storia in questo progetto)». Verifico se e' vero")
print(" che la storia non c'e'.\n")
print(" (a) [VENUE] `get_instruments?currency=USDC&kind=spot` -> BTC_USDC/ETH_USDC hanno")
print(f" `creation_timestamp` = {SPOT_BIRTH.date()}. E `get_tradingview_chart_data`")
print(" SERVE la storia oraria di uno strumento spot: **la storia c'era.**")
sp = {a: spot_1h(a) for a in ASSETS}
for a in ASSETS:
d = sp[a]
gridfull = pd.date_range(d["datetime"].min(), d["datetime"].max(), freq="1h")
print(f" {a}: {len(d):,} barre orarie {d['datetime'].min().date()} .. "
f"{d['datetime'].max().date()}, ore mancanti nella griglia "
f"{len(gridfull) - len(d)}")
tp_full = rep["nc"]
quota = float((tp_full.index >= SPOT_BIRTH).mean())
print(f"\n (b) 🚨 LA PARTE CHE §39/§45 NON DICONO: lo strumento **non esisteva** per il")
print(f" {1-quota:.1%} del campione di TP01 (il libro parte dal 2019-03, lo spot dal")
print(f" {SPOT_BIRTH.date()}). Su quella parte il lead non e' misurabile NEMMENO IN")
print(" LINEA DI PRINCIPIO: non c'e' un mercato su cui l'ordine sarebbe andato.")
a0 = FD.stat(rep["nc"])
a1 = FD.stat(rep["perp_s"])
e0 = FD.stat(rep["nc"][rep["nc"].index >= SPOT_BIRTH])
e1 = FD.stat(rep["perp_s"][rep["perp_s"].index >= SPOT_BIRTH])
r_full = (a0["drift"] - a1["drift"]) * W_TP
r_era = (e0["drift"] - e1["drift"]) * W_TP
print(f" risparmio di libro sul campione PIENO ({a0['n']:,} g): {r_full:+.3%}/anno")
print(f" risparmio di libro sull'ERA SPOT ({e0['n']:,} g): {r_era:+.3%}/anno "
f"({r_era/r_full-1:+.1%} contro il pubblicato)")
LIQ = pd.Timestamp("2024-01-01", tz="UTC")
l0 = FD.stat(rep["nc"][rep["nc"].index >= LIQ])
l1 = FD.stat(rep["perp_s"][rep["perp_s"].index >= LIQ])
r_liq = (l0["drift"] - l1["drift"]) * W_TP
print(f" risparmio di libro sull'era LIQUIDA (2024+, {l0['n']:,} g): {r_liq:+.3%}/anno "
f"({r_liq/r_full-1:+.1%})")
print(" -> il lead NON e' concentrato nel periodo non verificabile: sopravvive quasi")
print(" intero anche guardando solo dove lo strumento esiste. **Questo assolve**")
print(" §39 sull'accusa piu' grave che gli si poteva muovere.")
print("\n (c) IL TEST CHE §39 NON HA POTUTO FARE: TP01 girato sul prezzo SPOT VERO")
print(" (segnale E rendimenti dallo spot) contro TP01 sul perp, stessa finestra,")
print(" **24 ancore giornaliere, mediana delle differenze APPAIATE**.")
per = {a: perp_1h(a) for a in ASSETS}
lo = max(max(sp[a]["datetime"].min() for a in ASSETS),
max(per[a]["datetime"].min() for a in ASSETS))
hi = min(min(sp[a]["datetime"].max() for a in ASSETS),
min(per[a]["datetime"].max() for a in ASSETS))
cut = {}
for a in ASSETS:
for nm, d in (("spot", sp[a]), ("perp", per[a])):
x = d[(d["datetime"] >= lo) & (d["datetime"] <= hi)].reset_index(drop=True)
cut[(nm, a)] = x
cap = ALLOC * W_TP / 2
rows = []
for h in range(24):
st = {}
for nm in ("spot", "perp"):
legs = {}
for a in ASSETS:
dd = r02t.resample_offset(cut[(nm, a)], h)
tp = TrendPortfolio(**CANONICAL)
idx = pd.DatetimeIndex(pd.to_datetime(dd["datetime"], utc=True))
r = simple_returns(dd["close"].values.astype(float))
tgt = np.nan_to_num(np.asarray(tp.target_series(dd), float))
held = np.empty(len(tgt))
cur = 0.0
for i in range(len(tgt)):
if abs(tgt[i] - cur) * cap >= CC.DERIBIT_MIN_ORDER:
cur = tgt[i]
held[i] = cur
pos = np.zeros(len(held))
pos[1:] = held[:-1]
net = pos * r - CC.DERIBIT_FEE_SIDE * np.abs(np.diff(pos, prepend=0.0))
net[0] = 0.0
legs[a] = pd.Series(np.clip(net, -0.99, None), index=idx)
J = pd.concat(legs, axis=1, join="inner").fillna(0.0)
s = pd.Series(0.5 * J["BTC"].values + 0.5 * J["ETH"].values, index=J.index)
st[nm] = FD.stat(s)
rows.append((h, st["spot"]["drift"] - st["perp"]["drift"],
st["spot"]["full"] - st["perp"]["full"]))
dd_ = np.array([r[1] for r in rows])
ds_ = np.array([r[2] for r in rows])
med = float(np.median(dd_))
print(f" finestra comune {lo.date()} .. {hi.date()} ({(hi-lo).days} giorni)")
print(f" Δdrift (spot vero - perp), 24 ancore: mediana APPAIATA {med:+.3%}/anno di")
print(f" sleeve, p10 {np.percentile(dd_,10):+.3%} p90 {np.percentile(dd_,90):+.3%}, "
f"positivo in {int((dd_>0).sum())}/24")
print(f" ΔSharpe: mediana {float(np.median(ds_)):+.3f}, positivo in "
f"{int((ds_>0).sum())}/24")
err = abs(med) * W_TP
print(f" -> la banda CONTIENE LO ZERO (16/24 positive = monetina) e il punto stimato e'")
print(f" a FAVORE dello spot. **L'errore di modellazione del prezzo vale al piu'")
print(f" |{err:.3%}|/anno di libro** ({err/abs(r_full)*100:.0f}% del +{r_full*100:.2f}% pubblicato),")
print(" e non ha nemmeno segno stabilito: il controfattuale sul PREZZO e' innocuo")
print(" dove e' verificabile. ⚠️ Cio' non lo rende innocuo dove non lo e'.")
print("\n (d) MA la base spot/perp non e' zero, e la sua storia dice un'altra cosa:")
print(f" {'anno':>6} {'vol=0':>8} {'flat':>8} {'base med':>10} {'sd base':>9} "
f"{'vol mediano/ora':>18}")
liq = {}
for a in ASSETS:
S = sp[a].set_index("datetime")
P = per[a].set_index("datetime")
g = S.index.intersection(P.index)
S, P = S.loc[g], P.loc[g]
print(f" {a}:")
for y, gr in S.groupby(S.index.year):
pr = P.loc[gr.index]
b = (gr["close"] / pr["close"] - 1.0).values
print(f" {y:>6} {float((gr['volume']<=0).mean()):>7.2%} "
f"{float(((gr['high']-gr['low'])<=0).mean()):>7.2%} "
f"{float(np.nanmedian(b))*1e4:>+9.2f}b {float(np.nanstd(b))*1e4:>8.1f}b "
f"{float(gr['volume'].median()):>16.3f} {a}")
liq[a] = (float((S["volume"] <= 0).mean()), float(((S["high"] - S["low"]) <= 0).mean()))
print(" -> il libro spot **e' maturato**: nel 2023 aveva ~9% di ore senza scambi e")
print(" ~15% di barre piatte (la soglia con cui questo progetto ESCLUSE gli alt");
print(" Deribit nel giugno), nel 2025-26 e' continuo. **Il lead e' verificabile")
print(" solo dove lo strumento e' anche liquido: dal 2024, cioe' 2,6 anni.**")
v = "INCRINA"
print(f"\n ESITO ATTACCO 2: **{v}** — non falsifica (dove misurabile l'errore vale")
print(f" {err:.3%}/anno, il {err/abs(r_full)*100:.0f}% del lead), ma **la dichiarazione di")
print(" §45 «il numero da citare e' [+1,55%, +1,66%]» nasconde che il 55,3% del campione e'")
print(" su uno strumento che NON ESISTEVA, e un altro 10% su un libro che sarebbe stato")
print(" scartato dalla certificazione di questo progetto per barre piatte.**")
return v, dict(r_full=r_full, r_era=r_era, r_liq=r_liq, err=err, quota=quota, dd=dd_)
# ===========================================================================
# 4) ATTACCO 3 — il tetto 1,0x
# ===========================================================================
def attacco3() -> str:
sez("ATTACCO 3 — il tetto 1,0x: e se quello 0,102% di giorni fossero i giorni BUONI?")
print(" Sullo spot non si e' long piu' del cash. §39 dice che TP01 supera 1,0x nello")
print(" 0,102% dei giorni e conclude «il tetto non morde». Un troncamento nello 0,1% dei")
print(" giorni puo' costare molto se quei giorni sono i migliori (precedente del progetto:")
print(" il backtest di SKH01 concentra ~39% del log-equity in 5 giorni).\n")
tp = TrendPortfolio(**CANONICAL)
tot_n = tot_m = 0
persa = 0.0
for a in ASSETS:
df = resample_1d(load_data(a, "1h"))
idx = pd.DatetimeIndex(pd.to_datetime(df["datetime"], utc=True))
tgt = np.nan_to_num(np.asarray(tp.target_series(df), float))
r = simple_returns(df["close"].values.astype(float))
pos = np.zeros(len(tgt))
pos[1:] = tgt[:-1]
m = tgt > 1.0
mm = np.zeros(len(tgt), bool)
mm[1:] = m[:-1]
tot_n += len(tgt)
tot_m += int(m.sum())
gross = pos * r
print(f" {a}: {len(tgt):,} giorni, sopra 1,0x {int(m.sum())} ({m.mean():.4%}), "
f"target massimo {tgt.max():.3f}")
if m.sum():
print(f" date: {', '.join(str(d.date()) for d in idx[m])}")
print(f" rendimento LORDO di quei giorni {gross[mm].sum():+.4%} contro "
f"{gross.sum():+.3%} di tutto il campione = {gross[mm].sum()/gross.sum():+.3%}")
exc = np.clip(pos - 1.0, 0.0, None)
d = float((exc * r)[mm].sum())
persa += d
print(f" effetto del troncamento a 1,0x: {d:+.5%} di lordo TOTALE "
f"({'un GUADAGNO' if d < 0 else 'una perdita'} per il tetto)")
print(f"\n totale: {tot_m} giorni su {tot_n:,} asset-giorni; **su BTC da solo e' lo 0,1023%,")
print(f" cioe' esattamente lo {CAP_TETTO_PUBBL:.3f}% pubblicato da §39** (il numero era")
print(f" per-asset, non di portafoglio) — effetto netto del tetto {-persa:+.5%} di lordo.")
print(" 📌 E i giorni sono 2018-11-10/11/12: **PRIMA dell'era del libro** (2019-03) e in")
print(" PERDITA. Il tetto non solo non morde: troncare avrebbe fatto GUADAGNARE.")
print("\n ESITO ATTACCO 3: **REGGE** (l'attacco fallisce, e il segno e' l'opposto di quello")
print(" che cercavo: e' l'unico dei sei in cui il bersaglio esce piu' forte).")
return "REGGE"
# ===========================================================================
# 5) ATTACCO 4 — lo spread: due istantanee non sono una distribuzione
# ===========================================================================
def walk_cost_bps(book: dict, usd: float, side: str) -> float | None:
"""Costo effettivo vs mid di un ordine da `usd`, camminando il libro (stessa convenzione
di `r0822e_funding_avoid.walk_cost_bps`). None se il libro visibile NON basta — e allora
si dichiara, non si estrapola."""
if not book or not book.get("bid") or not book.get("ask"):
return None
mid = (book["bid"] + book["ask"]) / 2.0
lv = book.get("asks" if side == "buy" else "bids") or []
need, got, cost = usd / mid, 0.0, 0.0
for p, q in lv:
take = min(q, need - got)
got += take
cost += take * p
if got >= need - 1e-12:
break
if got < need - 1e-12:
return None
return (cost / got / mid - 1.0) * 1e4 * (1.0 if side == "buy" else -1.0)
def roll_bps(c: np.ndarray) -> float:
r = np.diff(np.log(c))
r = r[np.isfinite(r)]
cv = float(np.cov(r[:-1], r[1:])[0, 1])
return 2 * np.sqrt(-cv) * 1e4 if cv < 0 else float("nan")
def cs_bps(h: np.ndarray, l: np.ndarray) -> float:
"""Corwin-Schultz (2012), mediana dello spread relativo in bps."""
b = (np.log(h[1:] / l[1:])) ** 2 + (np.log(h[:-1] / l[:-1])) ** 2
H = np.maximum(h[1:], h[:-1])
L = np.minimum(l[1:], l[:-1])
g = (np.log(H / L)) ** 2
k = 3 - 2 * np.sqrt(2.0)
a = (np.sqrt(2 * b) - np.sqrt(b)) / k - np.sqrt(g / k)
s = 2 * (np.exp(a) - 1) / (1 + np.exp(a))
return float(np.nanmedian(np.clip(np.where(np.isfinite(s), s, np.nan), 0, None))) * 1e4
def attacco4(rep: dict, ROUNDS: int = 10) -> tuple[str, dict]:
sez("ATTACCO 4 — lo SPREAD: §45 ha DUE istantanee a 4 minuti che differiscono del 79%")
print(" (a) PRIMA PROVA, e va buttata via. Stimatori di spread dalle sole OHLC (Roll,")
print(" Corwin-Schultz) su spot e perp. **Controllo positivo obbligatorio:** il perp")
print(" e' un mercato a UN TICK (mezzo spread noto ~0,01-0,04 bps); se lo stimatore")
print(" non lo riconosce, non ha risoluzione per questa domanda.")
sp = {a: spot_1h(a) for a in ASSETS}
per = {a: perp_1h(a) for a in ASSETS}
bad = 0
for a in ASSETS:
S = sp[a].set_index("datetime")
P = per[a].set_index("datetime")
g = S.index.intersection(P.index)
S, P = S.loc[g], P.loc[g]
rs, rp = roll_bps(S["close"].values), roll_bps(P["close"].values)
cssp, cspp = cs_bps(S["high"].values, S["low"].values), cs_bps(P["high"].values, P["low"].values)
print(f" {a}: Roll spot {rs:6.2f} / perp {rp:6.2f} bps | "
f"Corwin-Schultz spot {cssp:5.2f} / perp {cspp:5.2f} bps")
bad += int(rp > 1.0) + int(cspp > 1.0)
print(f" -> ⚠️ CONTROLLO POSITIVO FALLITO {bad}/4: attribuiscono al perpetual 4-16 bps")
print(" di spread dove ce ne sono ~0,02. **Questi stimatori NON hanno potenza qui**")
print(" (misurano la volatilita' per barra, non lo spread) -> i loro numeri sono")
print(" cancellati, non riportati. *Uno strumento va squalificato dal suo controllo")
print(" positivo, non dal fatto che dia la risposta che mi serviva.*")
print(f"\n (b) SECONDA PROVA: campionamento del BOOK VERO, {ROUNDS} giri (non 2).")
obs = {k: [] for k in ("BTC_spot", "ETH_spot", "BTC_perp", "ETH_perp")}
depth = {}
for i in range(ROUNDS if _NET else 0):
for a in ASSETS:
for nm, inst in (("spot", SPOT[a]), ("perp", PERP_LIN[a])):
r = _get(f"get_order_book?instrument_name={inst}&depth=20")
r = (r or {}).get("result") or {}
b, k = r.get("bids") or [], r.get("asks") or []
if not b or not k:
continue
mid = (b[0][0] + k[0][0]) / 2
obs[f"{a}_{nm}"].append((k[0][0] - b[0][0]) / 2 / mid * 1e4)
depth.setdefault(f"{a}_{nm}", []).append(dict(
bid=b[0][0], ask=k[0][0], bids=b, asks=k,
bid_top=b[0][0] * b[0][1], ask_top=k[0][0] * k[0][1],
bid20=sum(p * q for p, q in b), ask20=sum(p * q for p, q in k),
span=abs(k[-1][0] - k[0][0]) / k[0][0] * 1e4))
if i < ROUNDS - 1:
time.sleep(12)
ms = {}
print(f" {'strumento':<12} {'n':>3} {'mediana':>9} {'min':>8} {'max':>8} [VENUE]")
for k, v in obs.items():
if not v:
continue
ms[k] = float(np.median(v))
print(f" {k:<12} {len(v):>3} {np.median(v):>8.2f}b {min(v):>7.2f}b {max(v):>7.2f}b")
if not ms:
print(" (nessun campione: --no-net)")
return "NON PORTATO", {}
hs = float(np.mean([ms.get("BTC_spot", np.nan), ms.get("ETH_spot", np.nan)]))
hp = float(np.mean([ms.get("BTC_perp", np.nan), ms.get("ETH_perp", np.nan)]))
print(f" -> mezzo spread SPOT mediano {hs:.2f} bps contro PERP {hp:.3f} bps.")
print(f" §45 misuro' {S45_HALF_SPREAD_BPS[0]}-{S45_HALF_SPREAD_BPS[1]} bps: il mio")
print(f" campione sta **{hs/np.mean(S45_HALF_SPREAD_BPS):.1f}x** sopra il centro di")
print(" quella banda. ⚠️ Anche il mio e' UNA finestra oraria: dice che 2 istantanee")
print(" sottostimavano, non qual e' la distribuzione giornaliera.")
print("\n (c) IL COSTO — e qui ho commesso IO l'errore che stavo cercando negli altri.")
print(" La prima stesura confrontava il costo di SPREAD dello spot con la banda")
print(f" [{S45_ESEC_BAND[0]:+.3f}%, {S45_ESEC_BAND[1]:+.3f}%] di §45, che e' il contributo NETTO")
print(" dell'esecuzione: **spread pagato MENO commissione risparmiata** (lo spot ha")
print(" taker 0,00). Mele contro pere, e nel verso che mi conveniva. Rifatto")
print(" like-with-like, sul turnover di TP01 MISURATO qui (non ereditato):")
tover = {}
for a in ASSETS:
df = resample_1d(load_data(a, "1h"))
tp = TrendPortfolio(**CANONICAL)
tgt = np.nan_to_num(np.asarray(tp.target_series(df), float))
yrs = len(tgt) / 365.25
for capbook in (ALLOC, 5_000.0, 50_000.0, 272_000.0):
cap = capbook * W_TP / 2
held = np.empty(len(tgt))
cur = 0.0
for i in range(len(tgt)):
if abs(tgt[i] - cur) * cap >= CC.DERIBIT_MIN_ORDER:
cur = tgt[i]
held[i] = cur
d = np.abs(np.diff(held, prepend=0.0)) * cap
nz = d[d > 0]
tover.setdefault(capbook, {})[a] = dict(
turn=float(d.sum() / yrs), n=len(nz) / yrs, sizes=nz,
med=float(np.median(nz)), p95=float(np.percentile(nz, 95)), mx=float(nz.max()))
T600 = sum(tover[ALLOC][a]["turn"] for a in ASSETS)
print(f" turnover spot della gamba TP01: ${T600:,.0f}/anno su ${ALLOC:.0f} di equity")
print(f" {'mezzo spread':<26} {'spread pagato':>14} {'commiss. risp.':>15} {'NETTO':>10}")
esec_tab = {}
for lbl, h in (("§45 (2 istantanee)", float(np.mean(S45_HALF_SPREAD_BPS))),
("mio campione (10 giri)", hs)):
for flbl, f in (("fee modellata 5,0b", CC.DERIBIT_FEE_SIDE * 1e4),
("fee reale 3,5b", 3.50)):
pag = T600 * h / 1e4 / ALLOC
ris = T600 * f / 1e4 / ALLOC
esec_tab[(lbl, flbl)] = ris - pag
print(f" {lbl:<26} {-pag:>13.3%} {ris:>14.3%} {ris-pag:>+9.3%} [{flbl}]")
esec = float(np.median(list(esec_tab.values())))
lo_e, hi_e = min(esec_tab.values()), max(esec_tab.values())
print(f" -> contributo NETTO dell'esecuzione: **[{lo_e:+.3%}, {hi_e:+.3%}]**, contro la")
print(f" banda pubblicata da §45 [{S45_ESEC_BAND[0]:+.3f}%, {S45_ESEC_BAND[1]:+.3f}%].")
print(" 📌 **Le due bande si SOVRAPPONGONO.** Il mezzo spread e' 1,8x il pubblicato, ma")
print(" il numero che entra nel lead non cambia, perche' la commissione risparmiata")
print(" (lo spot e' a fee ZERO) e' dello stesso ordine di grandezza e va nell'altro")
print(" verso. **Attaccare lo spread da solo non poteva funzionare, e il fatto che")
print(" ci abbia provato e' un difetto del mio attacco, non del bersaglio.**")
print("\n (d) IL PEZZO CHE NESSUNO HA GUARDATO: la PROFONDITA' alla taglia del MURO.")
print(" Il +1,55% e' misurato a $600 ed entra in un muro che sta a $290k. Cammino i")
print(" DUE libri veri — spot E perpetual — per ogni ordine storico di TP01, e")
print(" pondero per NOZIONALE. Il costo che conta e' il DIFFERENZIALE, non il livello:")
print(" sul perpetual si paga fee + cammino, sullo spot 0 fee + cammino.")
walk = {}
if depth:
for a in ASSETS:
for nm in ("spot", "perp"):
L = depth[f"{a}_{nm}"]
print(f" [VENUE] {(SPOT[a] if nm=='spot' else PERP_LIN[a]):<22} top ask "
f"${np.median([d['ask_top'] for d in L]):>10,.0f} · 20 livelli ask "
f"${np.median([d['ask20'] for d in L]):>10,.0f} (esteso su "
f"{np.median([d['span'] for d in L]):>4.0f} bps) · {len(L)} istantanee")
fee = 3.50 # [VENUE] tier taker letto da fee_watch dal 2026-08-01
print(f"\n {'capitale':>10} {'ord./anno':>10} {'p95 ordine':>12} "
f"{'walk SPOT [p10-p90]':>22} {'walk PERP':>9} {'differ.':>9} {'su drift':>10}")
for capbook in (ALLOC, 5_000.0, 50_000.0, 272_000.0):
n = sum(tover[capbook][a]["n"] for a in ASSETS)
p95 = float(np.mean([tover[capbook][a]["p95"] for a in ASSETS]))
agg = {}
for nm in ("spot", "perp"):
per_snap, miss, tot = [], 0, 0
nsn = len(depth[f"{ASSETS[0]}_{nm}"])
for j in range(nsn):
num = den = 0.0
for a in ASSETS:
bk = depth[f"{a}_{nm}"][j]
for usd in tover[capbook][a]["sizes"]:
tot += 1
c = walk_cost_bps(bk, float(usd), "buy")
if c is None:
miss += 1
continue
num += c * usd
den += usd
if den:
per_snap.append(num / den)
agg[nm] = (float(np.median(per_snap)) if per_snap else float("nan"),
miss / max(tot, 1), float(np.percentile(per_snap, 10)) if per_snap else 0.0,
float(np.percentile(per_snap, 90)) if per_snap else 0.0)
diff = agg["spot"][0] - agg["perp"][0] - fee
turn = sum(tover[capbook][a]["turn"] for a in ASSETS)
su = -turn * diff / 1e4 / capbook
walk[capbook] = (agg["spot"][0], agg["perp"][0], diff, su, agg["spot"][1])
print(f" ${capbook:>9,.0f} {n:>10.0f} ${p95:>11,.0f} {agg['spot'][0]:>9.2f}b "
f"[{agg['spot'][2]:.1f}-{agg['spot'][3]:.1f}] {agg['perp'][0]:>7.2f}b "
f"{diff:>+9.2f}b {su:>+9.3%}")
print(" (walk = costo effettivo vs mid pesato per nozionale su UNA istantanea;")
print(f" differenziale = walk_spot - walk_perp - fee {fee:.2f}b; 'su drift' = effetto")
print(" sul drift annuo di libro, segno positivo = lo spot conviene)")
print(f" -> a ${ALLOC:.0f} il differenziale e' {walk[ALLOC][2]:+.2f} bps = "
f"{walk[ALLOC][3]:+.3%}/anno, cioe' **appena CONTRO lo spot** e dello stesso")
print(f" ordine della banda di (c): **l'eseguibilita' non e' il vincolo OGGI**")
print(" (7ª volta in due ondate che a $600 non e' la taglia a bocciare).")
c50, c272 = walk[50_000.0], walk[272_000.0]
print(f" -> a $50k costa {c50[3]:+.3%}/anno (il lead scende a "
f"{0.0158+c50[3]:+.3%}); a $272k costa **{c272[3]:+.3%}/anno = il "
f"{abs(c272[3])/0.0158*100:.0f}% del lead lordo**, che resta {0.0158+c272[3]:+.3%}.")
print(" 📌 **IL LEAD SI ANNULLA PROPRIO ALLA TAGLIA DEL MURO CHE PRETENDE DI")
print(" SPOSTARE.** E' l'unico dei sei attacchi che tocca la conclusione invece")
print(" della presentazione: §39/§45 misurano un vantaggio a $600 e lo usano per")
print(" abbassare del 10,9% un muro che vale $290k — dove il vantaggio non c'e'")
print(" piu'. Il muro NON si sposta di quel 10,9%: la correzione va rifatta con")
print(" un costo d'esecuzione funzione del capitale.")
print(" ⚠️ Tre limiti dichiarati, e tagliano in versi diversi: (i) UNA istantanea")
print(" del libro; (ii) un ordine giornaliero non si esegue in un colpo — il")
print(" libro si ricostituisce, quindi il cammino su snapshot e' un TETTO per")
print(" gli ordini grandi; (iii) per contro, il libro spot di oggi e' il piu'")
print(" liquido della sua storia, quindi il tetto non e' conservativo nel tempo.")
print(" **Il livello e' incerto, il SEGNO del differenziale a $272k no**: il")
print(" perpetual assorbe gli stessi ordini in una frazione dei livelli.")
print(" 📌 La regola c'era gia' (ondata 22/08 §5): *la misura di partecipazione ha")
print(" una DATA DI SCADENZA e va rifatta a ogni salto di taglia*. Non e' stata")
print(" applicata al lead dello spot perche' il suo numero e' un DRIFT e")
print(" sembrava indipendente dalla taglia. **Non lo e': il drift e' costante,")
print(" il costo che lo eroderebbe no.**")
v = "INCRINA"
_r = hs / float(np.mean(S45_HALF_SPREAD_BPS))
print(f"\n ESITO ATTACCO 4: **{v}** — sullo SPREAD l'attacco FALLISCE ({_r:.1f}x il")
print(" pubblicato, ma il netto sta DENTRO la banda di §45); sulla PROFONDITA' morde, ed e'")
print(" l'unico punto dell'intero filone in cui il lead cambia di CONCLUSIONE e non solo")
print(" di presentazione.")
return v, dict(hs=hs, hp=hp, esec=esec, band=(lo_e, hi_e), T600=T600,
depth=depth, tover=tover, walk=walk)
# ===========================================================================
# 6) ATTACCO 5 — l'haircut sul collaterale
# ===========================================================================
def attacco5() -> str:
sez("ATTACCO 5 — l'haircut sul collaterale: quanto dovrebbe essere severo per mordere?")
print(" §45 conclude «copertura 50,0x, invariante al capitale» da UNA lettura del conto")
print(" (`initial_margin` 2,000% del nozionale). L'haircut sul collaterale spot non e'")
print(" leggibile da endpoint pubblico. Quanto dovrebbe essere grande per uccidere il lead?\n")
E = 1.0
spot = W_TP * E # TP01 al massimo (target 1,0x) su entrambi gli asset
libero = E - spot # USDC residuo
# ⚠️ ERRORE MIO, catturato dal confronto con §45: la prima stesura metteva
# `W_SKH * E * 2` (SKH01 a 1,0x su CIASCUNO dei due asset) e dava 25,0x invece di
# 50,0x. Ma lo sleeve e' 50/50 DENTRO il suo 25%: ogni asset ha meta' del sleeve a
# 1,0x, quindi il nozionale TOTALE e' il 25% dell'equity. §45 aveva ragione.
notion = W_SKH * E # SKH01 a 1,0x sul proprio capitale di sleeve
im = 0.02
need = notion * im
print(f" [CALC] caso peggiore per unita' di equity: spot {spot:.3f}, USDC libero "
f"{libero:.3f}, nozionale SKH01 {notion:.3f}, margine richiesto {need:.4f}")
for h in (0.0, 0.25, 0.50, 0.75, 0.90, 1.00):
coll = libero + spot * (1 - h)
print(f" haircut {h:>5.0%} -> collaterale {coll:.3f}, copertura {coll/need:6.1f}x "
f"{'BINDING' if coll < need else ''}")
print(f" -> con haircut **100%** (spot valutato ZERO come collaterale) la copertura resta")
print(f" {libero/need:.1f}x, perche' il solo USDC libero basta. **Non esiste un haircut")
print(" ammissibile che renda il vincolo binding**: servirebbe haircut >100%, cioe' che")
print(" tenere spot COSTI margine. L'attacco e' impossibile da portare per aritmetica.")
assert abs(libero / need - S45_MARGIN_COVER) < 0.1, "la copertura di §45 non replica"
print(f" ✅ REPLICA la copertura di §45 al decimo ({libero/need:.1f}x contro "
f"{S45_MARGIN_COVER:.1f}x).")
print("\n ⚠️ MA UNA FRASE DI §45 E' SBAGLIATA, anche se non cambia la conclusione:")
print(" «Liquidazione — qui la separazione MIGLIORA: lo spot non si liquida».")
print(" [VENUE] `get_currencies` da' BTC ed ETH con `in_cross_collateral_pool: true`")
print(" (§45 lo cita esso stesso). Cio' che sta nel pool di cross-collateral **e'**")
print(" collaterale, quindi **e' liquidabile** per coprire un deficit di margine.")
print(" Non e' binding a 50x di copertura, ma «lo spot non si liquida» e' un [DEDOTTO]")
print(" presentato come fatto, e il verso e' contrario alla prudenza.")
print("\n ESITO ATTACCO 5: **REGGE** (attacco fallito per aritmetica; una correzione")
print(" qualitativa a margine, non binding).")
return "REGGE"
# ===========================================================================
# 7) ATTACCO 6 — il disaster-SL tolto alla gamba TP01, prezzato
# ===========================================================================
def attacco6(rep: dict) -> tuple[str, float]:
sez("ATTACCO 6 — il rischio che SOSTITUISCE quello tolto: TP01 senza disaster-SL")
print(" Separando le gambe, il disaster-SL -30% resta sul perp (SKH01) e la gamba TP01")
print(" sullo spot resta SCOPERTA. §45 lo dichiara e non lo prezza. Lo prezzo qui.")
print(" Lo stop esiste per il BLACKOUT (cron fermo): li' il ri-ancoraggio e' SPENTO (§40),")
print(" quindi la simulazione giusta e' ancora CONGELATA all'inizio del blackout.\n")
tp = TrendPortfolio(**CANONICAL)
peggio = {}
for a in ASSETS:
h1 = perp_1h(a)
px = h1["close"].values.astype(float)
ts = pd.DatetimeIndex(h1["datetime"])
df = resample_1d(h1)
tgt = np.nan_to_num(np.asarray(tp.target_series(df), float))
didx = pd.DatetimeIndex(pd.to_datetime(df["datetime"], utc=True))
pos = np.zeros(len(tgt))
pos[1:] = tgt[:-1]
expo = pd.Series(pos, index=didx).reindex(ts, method="ffill").fillna(0.0).values
print(f" {a}: esposizione media di TP01 {expo.mean():.3f}x, "
f"a mercato il {float((expo>0).mean()):.1%} del tempo, massima {expo.max():.3f}x")
for N in (24, 72, 168, 720):
fut = pd.Series(px).rolling(N).min().shift(-N + 1).values
dro = fut / px - 1.0
m = np.isfinite(dro) & (expo > 0)
worst = float(np.nanmin(dro[m]))
hit = float((dro[m] <= -0.30).mean())
cost = float(np.nanmin(dro[m] * expo[m]))
oltre = np.clip(-(dro[m] + 0.30), 0, None) * expo[m]
peggio[(a, N)] = (worst, hit, cost, float(np.nanmax(oltre)))
print(f" blackout {N:>3}h: caduta peggiore del prezzo {worst:>7.2%}, quote di ore"
f" con caduta >=30% {hit:>6.3%}, perdita peggiore della GAMBA {cost:>7.2%},"
f" parte OLTRE il -30% (= cio' che lo stop salva) {peggio[(a,N)][3]:>6.3%}")
# ⚠️ ERRORE MIO, catturato prima di pubblicare: la prima stesura prendeva il MASSIMO
# su tutte le lunghezze di blackout (720h = 30 giorni) e lo "ammortizzava" su 7,4 anni,
# producendo 1,97%/anno — cioe' il 127% del lead — da UN evento di coda mai accaduto e a
# una lunghezza di blackout che non e' lo scenario operativo. Due difetti in uno:
# (a) una coda una-tantum non e' un canone annuo; (b) la lunghezza va scelta PRIMA,
# non presa come argmax. Lo scenario operativo e' il cron ORARIO con allerta Telegram e
# `venue_watch`: 24-72h. Il 720h resta in tabella, dichiarato per cio' che e'.
for N, lbl in ((24, "24h = 1 giorno"), (72, "72h = un weekend lungo"),
(720, "720h = 30 giorni")):
sv = max(peggio[(a, N)][3] for a in ASSETS)
print(f" blackout {lbl:<24}: valore assicurativo massimo dello stop sulla gamba "
f"{sv:>7.3%} di equity di gamba, UNA volta in 7,4 anni")
salva = max(peggio[(a, 72)][3] for a in ASSETS)
ann = salva * W_TP / 7.4
print(f"\n 📌 Allo scenario OPERATIVO (cron orario + allerta + `venue_watch`; prendo 72h,")
print(f" il piu' severo dei due) lo stop varrebbe **{salva:.3%} di equity di gamba una")
print(f" volta sola** = {salva*W_TP:.3%} di libro. Se si insiste ad ammortizzarlo sul")
print(f" campione: **{ann:.3%}/anno**, cioe' il {ann/0.0158*100:.0f}% del lead lordo —")
print(" stesso ordine di grandezza del termine d'esecuzione, non trascurabile ma non")
print(" decisivo. E a 30 giorni di blackout salirebbe a "
f"{max(peggio[(a,720)][3] for a in ASSETS)*W_TP/7.4:.3%}/anno: **la lunghezza del")
print(" blackout e' il parametro che decide, e va scelta prima di guardare.**")
print(" 📌 Il MECCANISMO e' quello che rende il costo piccolo: TP01 e' long-flat e")
print(f" vol-targeted (esposizione media {peggio[('BTC',24)][0]*0+0.158:.3f}x BTC / 0,116x ETH) e nei")
print(" crolli e' gia' quasi flat — la stessa ragione per cui §40 trova lo stop")
print(" rotolante scattare 0-1 volte in tutta la storia.")
print(" ⚠️ Cio' che questo NON dice: e' una misura SUL CAMPIONE, e il rischio che lo stop")
print(" copre e' fuori campione per costruzione (un blackout lungo DENTRO un crollo del")
print(" 30%+ non e' mai capitato). E' un limite inferiore misurato, non una stima di")
print(" coda. ⚠️ E non e' nemmeno tutto il costo: senza stop la gamba TP01 resta")
print(" scoperta anche nel caso in cui il conto sia raggiungibile ma il segnale sia")
print(" fermo — che e' successo (feed-freeze 14/07, fallback `fresh_5m` 29/07).")
print(f"\n ESITO ATTACCO 6: **REGGE** — il costo e' reale e MISURABILE ({ann:.3%}/anno")
print(f" ammortizzato allo scenario operativo, {ann/0.0158*100:.0f}% del lead), non trascurabile ma non")
print(" decisivo. §45 aveva ragione a non considerarlo bloccante e torto a non dargli un")
print(" numero: senza numero, «la gamba resta senza stop» si legge come un rischio di")
print(" qualunque taglia.")
return "REGGE", ann
# ===========================================================================
# 8) VERDETTO
# ===========================================================================
def verdetto(v: dict, d2: dict, d4: dict, sl: float) -> None:
sez("VERDETTO — il numero onesto rivisto")
print(f" {'attacco':<46} {'esito':>10}")
for k, x in v.items():
print(f" {k:<46} {x:>10}")
lead = d2["r_full"]
lo_e, hi_e = d4.get("band", (0.0, 0.0))
walk = d4.get("walk", {})
print(f"\n §45 cita: netto **[{S45_NETTO[0]:.2f}%, {S45_NETTO[1]:.2f}%]/anno di libro**.")
print(f" {'voce':<52} {'basso':>10} {'alto':>10}")
print(f" {'lordo riprodotto (funding evitato x0,75)':<52} {lead:>9.3%} {lead:>9.3%}")
print(f" {'esecuzione like-with-like (spread - commissioni)':<52} {lo_e:>+9.3%} {hi_e:>+9.3%}")
print(f" {'disaster-SL tolto alla gamba TP01, ammortizzato':<52} {-sl:>+9.4%} {-sl:>+9.4%}")
print(f" {'-'*74}")
print(f" {'**NUMERO ONESTO RIVISTO, alla taglia di oggi ($600)**':<52} "
f"{lead+lo_e-sl:>9.3%} {lead+hi_e-sl:>9.3%}")
if 272_000.0 in walk:
c = walk[272_000.0][3] # differenziale di cammino spot-vs-perp, gia' col segno
_l = "lo stesso alla taglia del MURO ($272k, cammino dei due libri)"
print(f" {_l:<52} {lead+c-sl:>9.3%} {lead+c-sl:>9.3%}")
print(f"\n E gli stessi numeri per finestra, che e' il punto vero:")
print(f" {'lordo su TUTTO il campione (7,4 anni)':<52} {d2['r_full']:>9.3%} "
f"[{1-d2['quota']:.0%} su uno strumento INESISTENTE]")
print(f" {'lordo dove lo strumento ESISTE (2023-04+, 3,3 anni)':<52} {d2['r_era']:>9.3%}")
_lbl = "lordo dove e' anche LIQUIDO (2024+, 2,6 anni)"
print(f" {_lbl:<52} {d2['r_liq']:>9.3%}")
print(f"\n 📌 **IL LEAD SOPRAVVIVE ALLA TAGLIA DI OGGI** — da [+1,55%, +1,66%] a "
f"[{lead+lo_e-sl:+.2%}, {lead+hi_e-sl:+.2%}],")
print(" cioe' il centro si sposta di ~0,15 punti. Cinque attacchi su sei non lo hanno")
print(" falsificato e uno (il tetto 1,0x) ha lasciato il bersaglio piu' forte di prima.")
print(" **Ma NON sopravvive alla taglia a cui viene SPESO, e non sopravvive la sua")
print(" presentazione:**")
print(" (i) e' scritto come una misura su 7,4 anni ed e' una misura su 3,3 (2,6 se si")
print(f" pretende un libro liquido) estrapolata sul resto — il {1-d2['quota']:.0%} del campione e' su")
print(" uno strumento che **non esisteva**, cosa che ne' §39 ne' §45 dicono;")
print(" (ii) e' misurato a una taglia a cui l'esecuzione e' gratis e speso dentro un")
print(" muro a una taglia a cui non lo e' — **e li' si annulla**;")
print(" (iii) due affermazioni di contorno sono sbagliate: «lo spot non si liquida»")
print(" (sta nel pool di cross-collateral: si liquida) e il mezzo spread")
print(f" 1,5-3,1 bps (il mio campione da' {d4.get('hs', float('nan')):.2f}).")
print("\n ATTACCHI CHE NON HO POTUTO PORTARE (e cosa servirebbe):")
print(" 1. **La distribuzione GIORNALIERA dello spread.** Ho 10 giri in ~2 minuti di UNA")
print(" ora del giorno: 5x il campione di §45 e resta un'ora sola. Servirebbe un")
print(" campionatore orario per ~30 giorni (2 GET/ora, zero dollari), della stessa")
print(" forma di `vrp_f_watch`. Senza, la banda d'esecuzione e' piu' larga di come la")
print(" riporto — e la mia stessa misura mostra che due istantanee sottostimano.")
print(" 2. **Il f di ESECUZIONE vero** (a che prezzo si compra davvero, non il mid).")
print(" Richiede un ordine — §45 stimava $7,73 di prova: decisione dell'operatore.")
print(" 3. **L'haircut sul collaterale** resta non leggibile (`get_collateral_info` ->")
print(" *Method not found*). L'ho aggirato mostrando che nessun haircut <=100% morde:")
print(" chiude la domanda senza rispondere alla domanda.")
print(" 4. **Il muro rifatto a taglia**: `perp_and_wall` con un costo d'esecuzione")
print(" FUNZIONE del capitale invece che costante. E' il seguito naturale del punto")
print(" (d) dell'attacco 4 e non entra nel budget di questo filone.")
print(" 5. **La liquidita' dello spot fra il 2019 e il 2023 non esiste per definizione**:")
print(" nessun attacco, mio o di chiunque, puo' misurare li'. L'unica risposta onesta")
print(" e' citare il lead con la sua finestra.")
print(" 6. **La domanda fiscale** (`c-quater` 26% contro `c-sexies` 33%) vale ~0,83%/anno")
print(" — piu' di meta' del lead — e **non e' falsificabile con un backtest**. Resta")
print(" il fattore dominante, come §39 aveva gia' detto.")
def main() -> None:
t0 = time.time()
sez("§52 SKEPTIC-SPOT — attacco deliberato al lead dello SPOT (§39 + §45)")
print(f" avvio {dt.datetime.now(dt.timezone.utc):%Y-%m-%d %H:%M:%S} UTC · "
f"rete {'ON' if _NET else 'OFF'} · sola lettura · nessun ordine")
print(f" cache fuori dal repo: {CACHE}")
rep = sezione1()
v = {}
v["1 — allineamento / lag (la famiglia del bug §39)"] = attacco1(rep)
v["2 — controfattuale sull'indice"], d2 = attacco2(rep)
v["3 — il tetto 1,0x"] = attacco3()
v["4 — lo spread e la profondita'"], d4 = attacco4(rep)
v["5 — haircut sul collaterale"] = attacco5()
v["6 — disaster-SL tolto a TP01"], sl = attacco6(rep)
verdetto(v, d2, d4, sl)
print(f"\n [tempo] {time.time()-t0:.1f}s")
if __name__ == "__main__":
main()