research(wave-0822): XS01-OOS — l'edge esiste FUORI dalla finestra di scoperta ed e' piu' grande li'; il gate del prop-alloc si sostituisce, non si aspetta

This commit is contained in:
Adriano Dal Pastro
2026-08-22 20:52:40 +00:00
parent 0f2e195674
commit 1ec928cf09
2 changed files with 116 additions and 10 deletions
+32 -9
View File
@@ -370,7 +370,7 @@ def test_calendario(q: pd.DataFrame, dte_min: float, ogni: int = 4) -> dict:
amp_pt = np.where(ok, iv_need - nxt_iv, np.nan)
room = amp_pt[ok] # <0 = margine, >0 = violazione
res = {"room_med_pt": float(np.nanmedian(room)), "room_p5_pt": float(np.nanpercentile(room, 5)),
res = {"room_med_pt": float(np.nanmedian(room)), "room_p5_pt": float(np.nanpercentile(room, 95)),
"i_n": int(ok.sum()), "i_viol": int(viol_i.sum()),
"i_quota": float(viol_i.sum() / max(1, ok.sum())),
"i_amp_med_pt": float(np.nanmedian(amp_pt[viol_i])) if viol_i.any() else np.nan,
@@ -473,7 +473,8 @@ def persistenza(bf: pd.DataFrame, viol_col: str) -> dict:
return {"n": int(m.sum()), "resta": float(nxt[m].mean()) if m.any() else np.nan}
def controfattuale_usdc(bf: pd.DataFrame, asset: str, rel_usdc: float) -> dict:
def controfattuale_usdc(bf: pd.DataFrame, asset: str, rel_usdc: float,
solo_gia_strette: bool = False) -> dict:
"""SE la famiglia USDC quotasse la STESSA superficie col SUO spread, sarebbe eseguibile?
Controfattuale DICHIARATO, non una misura: il collettore non raccoglie le USDC (difetto di
@@ -484,6 +485,14 @@ def controfattuale_usdc(bf: pd.DataFrame, asset: str, rel_usdc: float) -> dict:
"""
x = bf[(bf["asset"] == asset)].dropna(subset=["mid", "mid_p", "mid_n"]).copy()
x = x[np.isclose(x["lam"], 0.5, atol=0.02)]
if solo_gia_strette:
# il controfattuale ingenuo prende il MID di un mercato larghissimo e finge di poterlo
# quotare al 2,6%: e' proprio dove il mid e' meno informativo. Qui si tiene solo la parte
# di superficie GIA' quotata stretta quanto la famiglia USDC, dove il mid vale qualcosa.
rs = np.maximum.reduce([((x["ask"] - x["bid"]) / x["mid"]).to_numpy(),
((x["ask_p"] - x["bid_p"]) / x["mid_p"]).to_numpy(),
((x["ask_n"] - x["bid_n"]) / x["mid_n"]).to_numpy()])
x = x[rs <= rel_usdc]
lot = MIN_LOT_USDC[asset]
out = {}
for nome, rel in (("inverse osservato", None), (f"{asset}_USDC controfattuale", rel_usdc)):
@@ -499,8 +508,10 @@ def controfattuale_usdc(bf: pd.DataFrame, asset: str, rel_usdc: float) -> dict:
f = (fee_coin(b.to_numpy(), 2 * l) + fee_coin(a_p.to_numpy(), l) + fee_coin(a_n.to_numpy(), l))
netto = (cred - f) * x["spot_feed"].to_numpy()
out[nome] = {"n": int(len(x)), "pos": int(np.nansum(netto > 0)),
"max": float(np.nanmax(netto)), "p999": float(np.nanpercentile(netto, 99.9)),
"lotto_usd": float(np.nanmedian(x["spot_feed"]) * l)}
"pos1": int(np.nansum(netto > 1.0)), "somma": float(np.nansum(netto[netto > 0])),
"max": float(np.nanmax(netto)) if len(x) else np.nan,
"p999": float(np.nanpercentile(netto, 99.9)) if len(x) else np.nan,
"lotto_usd": float(np.nanmedian(x["spot_feed"]) * l) if len(x) else np.nan}
return out
@@ -694,7 +705,7 @@ def main() -> None:
print(f" valore mediano della violazione (i) al lotto minimo ${c['i_val_usd_med']:.2f} "
f"contro un costo di 2 gambe (spread + fee) di ${c['i_costo_usd_med']:.2f}")
print(f" margine di monotonia sull'intera superficie: la iv lunga sta {-c['room_med_pt']:.2f} pt-vol "
f"SOPRA il minimo che la non-arbitraggio richiede (p5 {-c['room_p5_pt']:.2f}) "
f"SOPRA il minimo che la non-arbitraggio richiede (5% piu' stretto: {-c['room_p5_pt']:.2f}) "
f"-> la superficie non e' nemmeno VICINA al bordo")
print("\n" + "=" * 100)
@@ -717,10 +728,22 @@ def main() -> None:
for a, rel in (("BTC", 0.082), ("ETH", 0.026)):
if a not in set(bf["asset"]):
continue
cf = controfattuale_usdc(bf, a, rel)
for nome, r in cf.items():
print(f" {a} {nome:<28}: lotto ${r['lotto_usd']:,.0f} · farfalle a credito netto>0 "
f"{r['pos']:,}/{r['n']:,} · migliore ${r['max']:.2f} · p99.9 ${r['p999']:.2f}")
for strette, et in ((False, "tutta la superficie"), (True, "solo quote gia' strette")):
cf = controfattuale_usdc(bf, a, rel, solo_gia_strette=strette)
for nome, r in cf.items():
if strette and "inverse" in nome:
continue
print(f" {a} {nome:<26} [{et:<24}]: lotto ${r['lotto_usd']:,.0f} · "
f"credito netto>0 {r['pos']:,}/{r['n']:,} · >$1 {r['pos1']:,} · "
f"migliore ${r['max']:.2f} · somma ${r['somma']:,.0f}")
# dove vive l'incoerenza: e' larga dove il mercato e' largo?
x = bf[(bf["asset"] == a)].dropna(subset=["iv_resid", "relspread"])
x = x[np.isfinite(x["relspread"])]
dec = pd.qcut(x["relspread"], 5, labels=False, duplicates="drop")
t = x.assign(d=dec).groupby("d").agg(spread=("relspread", "median"),
resid=("iv_resid", lambda v: v.abs().median()))
print(f" |residuo| per quintile di spread relativo {a}: " +
" · ".join(f"{r.spread:.1%}->{r.resid:.3f}pt" for r in t.itertuples()))
print("\n QUANTO MANCA per essere eseguibile (near-miss, triple equispaziate):")
for a in sorted(bf["asset"].unique()):