research(wave-0822): MAKER scartato (il segno dipende da < contro <=) + CRITICO — la cucitura dell'ondata, non i filoni

This commit is contained in:
Adriano Dal Pastro
2026-08-22 20:51:47 +00:00
parent 103dd7bce5
commit 0f2e195674
4 changed files with 271 additions and 18 deletions
+8 -1
View File
@@ -486,9 +486,16 @@ def main() -> None:
print(f" e' la scelta CONSERVATIVA e resta quella dichiarata.")
print(f" dispersione dei trial: sd(Sharpe FULL) = {float(np.std(all_sr, ddof=1)):.3f} sulle")
print(f" {len(ALLC)} celle, {float(np.std(pos_sr, ddof=1)):.3f} sulle {len(POS)} di segno +1.")
print(f" -> il DSR qui NON e' vacuo (il baseline lo fallisce dove il candidato lo passa):")
print(f" -> il DSR qui NON e' vacuo (il baseline lo FALLISCE dove il candidato lo passa):")
print(f" la famiglia contiene celle davvero diverse fra loro, al contrario della famiglia")
print(f" VOL-SIZE dove erano tutte perturbazioni della stessa size continua.")
print(f" ❌ QUESTO REFUTA LA MIA IPOTESI (b), registrata nel docstring PRIMA di misurare")
print(f" ('il DSR sara' vacuo come su VOL-SIZE'). La ragione per cui mi sbagliavo e'")
print(f" strutturale e vale come regola: un DSR e' vacuo quando i trial si somigliano")
print(f" (var_tr -> 0 => sr0 -> 0 => DSR ~ 1 per chiunque). Una famiglia BINARIA contiene")
print(f" celle che scartano insiemi di trade MOLTO diversi — compreso il segno inverso —")
print(f" quindi la dispersione dei trial e' reale e il test riacquista potenza. Non e' il")
print(f" binario a essere piu' solido della formula: e' la FAMIGLIA a essere piu' larga.")
print("\n (c) MARGINAL vs TP01 — sulla gamba modificata e sulla gamba base")
for lab, s in (("BASE (size fissa)", base_leg[can]), (nmc, LEG[nmc][can]),
+57 -1
View File
@@ -369,7 +369,9 @@ def test_calendario(q: pd.DataFrame, dte_min: float, ogni: int = 4) -> dict:
iv_need = np.sqrt(np.maximum(x["w"].to_numpy(), 0) / np.maximum(nxt_T, 1e-9)) * 100.0
amp_pt = np.where(ok, iv_need - nxt_iv, np.nan)
res = {"i_n": int(ok.sum()), "i_viol": int(viol_i.sum()),
room = amp_pt[ok] # <0 = margine, >0 = violazione
res = {"room_med_pt": float(np.nanmedian(room)), "room_p5_pt": float(np.nanpercentile(room, 5)),
"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,
"i_amp_p95_pt": float(np.nanpercentile(amp_pt[viol_i], 95)) if viol_i.any() else np.nan}
@@ -471,6 +473,37 @@ 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:
"""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
produzione del filone 11), quindi si tiene il mid osservato sulle inverse e si stringe lo
spread al valore relativo misurato dagli altri filoni sulla famiglia USDC, con il lotto
minimo USDC e la stessa formula di fee. Assunzione dichiarata: stessa griglia di tick
(una griglia piu' grossa peggiorerebbe il risultato, quindi questo e' un LIMITE SUPERIORE).
"""
x = bf[(bf["asset"] == asset)].dropna(subset=["mid", "mid_p", "mid_n"]).copy()
x = x[np.isclose(x["lam"], 0.5, atol=0.02)]
lot = MIN_LOT_USDC[asset]
out = {}
for nome, rel in (("inverse osservato", None), (f"{asset}_USDC controfattuale", rel_usdc)):
if rel is None:
b, a_p, a_n = x["bid"], x["ask_p"], x["ask_n"]
l = MIN_LOT[asset]
else:
b = x["mid"] * (1 - rel / 2)
a_p = x["mid_p"] * (1 + rel / 2)
a_n = x["mid_n"] * (1 + rel / 2)
l = lot
cred = (b.to_numpy() * 2 * l - a_p.to_numpy() * l - a_n.to_numpy() * l)
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)}
return out
# --------------------------------------------------------------------------- controlli positivi
def _finto(rows) -> pd.DataFrame:
@@ -657,6 +690,12 @@ def main() -> None:
print(f" (ii) log-moneyness FISSO, con interpolazione (1 snapshot ogni {c['ii_ogni']}): "
f"{c['ii_viol']:,}/{c['ii_n']:,} = {c['ii_quota']:.3%} · ampiezza mediana {c['ii_amp_med_pt']:.3f} pt-vol")
print(f" -> contributo dell'INTERPOLATORE = {c['ii_quota'] - c['i_quota']:+.3%} di quota di violazione")
if "i_val_usd_med" in c:
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"-> la superficie non e' nemmeno VICINA al bordo")
print("\n" + "=" * 100)
print("5. TICK E PERSISTENZA — le due difese contro 'la mia incoerenza e' un artefatto'")
@@ -674,6 +713,23 @@ def main() -> None:
f"(mediana {np.median(nt):.0f} tick) · resta in violazione allo snapshot dopo "
f"{p.get('resta', float('nan')):.1%} (n={p['n']:,})")
print("\n CONTROFATTUALE (dichiarato, non misurato): la stessa superficie con lo spread USDC")
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}")
print("\n QUANTO MANCA per essere eseguibile (near-miss, triple equispaziate):")
for a in sorted(bf["asset"].unique()):
x = bf[(bf["asset"] == a) & np.isclose(bf["lam"], 0.5, atol=0.02)]
c = x["cost_exec"].to_numpy() * 2 * MIN_LOT[a] * x["spot_feed"].to_numpy() # >0 = costo
print(f" {a}: costo della farfalla eseguibile — migliore ${np.nanmin(c):.2f} · "
f"p0.1 ${np.nanpercentile(c, 0.1):.2f} · mediana ${np.nanmedian(c):.2f} "
f"(negativo = credito riskless)")
print("\n" + "=" * 100)
print("6. IL RESIDUO SI RICOMPONE? (l'ipotesi del filone, misurata invece che assunta)")
print("=" * 100)
+74 -14
View File
@@ -444,13 +444,19 @@ def main() -> None: # noqa: C901
wins_sk += d > c
print(f" {str(IDX[t].date()):>12} {win[t]:>9.1%} {a:>11.1%} {b:>11.1%} "
f"{c:>10.1%} {d:>11.1%} {('si' if b > a else 'no'):>9}")
pre = [t for t in w12 if IDX[t] < pd.Timestamp(RECENT_START, tz="UTC")]
wins_pre = sum(1 for t in pre
if float(np.expm1(lr_opt[t:t + EPI_LEN].sum()))
> float(np.expm1(lr_live[t:t + EPI_LEN].sum())))
print(f"\n OPT batte LIVE in **{wins}/12** finestre -> test dei segni "
f"p = {binom_two_sided(wins, 12):.4f}")
print(f" SKH01 puro batte TP01 puro in **{wins_sk}/12** -> p = "
f"{binom_two_sided(wins_sk, 12):.4f}")
print(" ⚠️ 5 di queste 12 finestre stanno nel 2024+, cioe' DENTRO la finestra su cui")
print(" SKH01 e' stato tarato e su cui PROP-ALLOC ha ottimizzato: la potenza sale ma")
print(" l'indipendenza scende. Il test va letto come **una diagnostica, non una prova**.")
print(f" ⚠️ {12-len(pre)} di queste 12 finestre stanno nel 2024+, cioe' DENTRO la finestra")
print(" su cui SKH01 e' stato tarato e su cui PROP-ALLOC ha ottimizzato: la potenza sale")
print(" ma l'indipendenza scende. **Sul solo sotto-campione pre-2024**, che e' quello")
print(f" indipendente: OPT batte LIVE in **{wins_pre}/{len(pre)}** -> p = "
f"{binom_two_sided(wins_pre, len(pre)):.4f}. E' questo il numero da citare.")
tick("§2 conto assicurazione")
@@ -583,11 +589,15 @@ def main() -> None: # noqa: C901
bw_c = max(s2, key=lambda w: s2[w][2])
print(f"\n argmax della colonna 'peggiore': leva-da-2024+ -> TP01 {bw_a:.3f} | "
f"leva comune 0.50x -> TP01 {bw_c:.3f}")
print(" Lettura: se la sopravvivenza al crash SALISSE col peso di TP01, l'assicurazione")
print(" servirebbe. **A leva comune non sale**: la curva e' piatta-decrescente. Il difensivo")
print(" non compra sopravvivenza su questa barriera, perche' cio' che la barriera punisce e'")
print(" la PERDITA nel crash, e nel crash TP01 perde (prende la prima gamba giu' e poi va")
print(" flat) mentre SKH01 si gira short e GUADAGNA.")
print(" ⚠️ LE DUE COLONNE DICONO COSE DIVERSE, E LA SECONDA E' A CAMPANA: a leva COMUNE la")
print(f" sopravvivenza al crash SALE col peso di TP01 fino a {bw_c:.3f} ("
f"{s2[0.0][2]:.1%} a TP01=0 -> {s2[bw_c][2]:.1%}) e poi ricade.")
print(" **Quindi TP01 protegge davvero: a pari leva, il libro con piu' difensivo sopravvive")
print(" di piu' al crash.** Il punto e' che quella protezione si compra anche abbassando la")
print(" leva — ed e' esattamente cio' che misura §5. Alla leva che ciascun libro sceglierebbe")
print(" (prima colonna) il vantaggio sparisce, perche' il libro senza TP01 la sceglie piu'")
print(" bassa. E' la firma del **null del de-levering**, qui in veste di 'protezione dal")
print(" crash' invece che di 'meno drawdown'.")
tick("§4 S2 replay")
@@ -637,9 +647,16 @@ def main() -> None: # noqa: C901
ret, _, _, _, _ = replay22(w, lo)
_, _, _, why, _ = replay22(w, hi)
print(f" {w:>7.3f} {lo:>23.2f}x {ret:>20.1%} {why:>25}")
print("\n ⚠️ La leva di rottura e' MONOTONA DECRESCENTE nel peso di TP01 solo se la")
print(" protezione fosse reale. Si legga la colonna: se il libro con piu' TP01 rompe PRIMA,")
print(" l'assicurazione non sta assicurando la cosa giusta.")
print("\n 📌 La colonna 'leva max viva' CRESCE col peso di TP01 (fino a TP01=0.875; a TP01=1")
print(" ricade perche' li' a mordere non e' piu' il giorno singolo ma la perdita CUMULATA).")
print(" **L'assicurazione e' reale e si vede: piu' TP01 = piu' leva sopportabile nel 2022.**")
print(" E accanto c'e' il prezzo: alla PROPRIA leva massima il libro senza TP01 porta a casa")
print(" il +23.3% e quello a TP01 pieno il -5.9%. TP01 compra capacita' di leva, e la")
print(" capacita' di leva non si converte in rendimento perche' cio' che TP01 toglie e'")
print(" proprio il rendimento. E' il null del de-levering scritto in una riga sola.")
print(" ⚠️ Cambia anche il VINCOLO che morde: sotto TP01 0.50 e' la regola a UN GIORNO,")
print(" sopra e' il max-loss cumulato. Due libri diversi muoiono di due morti diverse, e una")
print(" sola delle due lenti (l'accoppiata) vede la prima.")
tick("§4 replay 2022")
@@ -725,8 +742,10 @@ def main() -> None: # noqa: C901
print(f" {0.5*wxs30[t_xw]:.1%} al libro. La domanda non e' se XS01 sopravvive a cio' che ha")
print(" gia' visto, e' se il suo comportamento nel crash somiglia a cio' che ha gia' visto —")
print(" e su questo il campione non ha NIENTE da dire.")
W3 = [(0.75, 0.25, 0.00), (0.375, 0.625, 0.00), (0.25, 0.25, 0.50), (0.125, 0.125, 0.75),
(0.50, 0.25, 0.25)]
# ⚠ le ultime tre righe hanno XS01 FISSO al 50%: senza quel controllo si confonderebbe
# "piu' TP01" con "meno XS01", che e' un'altra domanda.
W3 = [(0.75, 0.25, 0.00), (0.375, 0.625, 0.00), (0.50, 0.25, 0.25),
(0.125, 0.375, 0.50), (0.25, 0.25, 0.50), (0.375, 0.125, 0.50)]
com = xs.index.intersection(IDX[i_rec:])
xs_al = xs.reindex(com)
print(f"\n {'TP/SKH/XS':>16} {'ipotesi XS':>12} {'leva':>6} {'J normale':>10} "
@@ -832,7 +851,9 @@ def main() -> None: # noqa: C901
n_act = int((np.abs(R) > 1e-12).sum())
n_los = int((R[np.abs(R) > 1e-12] < 0).sum())
f = A.implausible_sharpe(d, n_trades=n_act, n_losing_trades=n_los)
print(f" implausible_sharpe {lab:>11}: {f}")
print(f" implausible_sharpe {lab:>11}: implausible={f['implausible']} "
f"(Sharpe {f['sharpe']:.2f}, maxDD {f['maxdd']:.1%}, quota barre in perdita "
f"{f['loss_frac']:.1%}, Calmar {f['calmar']:.2f}) {f['reasons']}")
except Exception as exc: # pragma: no cover
print(f" implausible_sharpe: NON GIRATO ({exc})")
print(" marginal_vs_tp01 / study_family_honest / deflated_sharpe : **NON GIRATI, e non")
@@ -864,6 +885,45 @@ def main() -> None: # noqa: C901
am = max(tab, key=lambda w: tab[w][key][1]["J"])
print(f" {lab:>26} {am:>14.3f} {tab[am][key][1]['J']:>10.3f} "
f"{tab[0.75][key][1]['J']:>10.3f} {tab[0.375][key][1]['J']:>11.3f}")
print("\n ESISTE UN PESO DI COMPROMESSO? Il criterio dichiarato: (a) dentro il 5% del")
print(" massimo di J sulla finestra 2024+ (la 'regione robusta' del funded) E (b) dentro il")
print(" 5% del massimo di J sulla finestra 2019+, che il sinistro ce l'ha dentro.")
jr_max = max(tab[w]["rec"][1]["J"] for w in W_GRID)
jf_max = max(tab[w]["full"][1]["J"] for w in W_GRID)
js_max = max(tab[w]["sin"][1]["J"] for w in W_GRID)
print(f"\n {'w TP01':>7} {'J 2024+':>9} {'entro 5%':>9} {'J 2019+':>9} {'entro 5%':>9} "
f"{'J solo-sin':>11} {'entro 5%':>9} {'compromesso':>13}")
ok_both = []
for w in W_GRID:
a, b, c = (tab[w]["rec"][1]["J"], tab[w]["full"][1]["J"], tab[w]["sin"][1]["J"])
oa, ob, oc = a >= 0.95 * jr_max, b >= 0.95 * jf_max, c >= 0.95 * js_max
if oa and ob:
ok_both.append(w)
print(f" {w:>7.3f} {a:>9.3f} {('si' if oa else 'no'):>9} {b:>9.3f} "
f"{('si' if ob else 'no'):>9} {c:>11.3f} {('si' if oc else 'no'):>9} "
f"{('SI' if (oa and ob) else '-'):>13}")
print(f"\n -> pesi che soddisfano (a) E (b): {ok_both if ok_both else 'NESSUNO'}")
print(f" il libro live 75/25 e' {'dentro' if 0.75 in ok_both else 'FUORI'} da questa regione.")
print("\n LA RISPOSTA, IN QUATTRO RIGHE")
print(" 1) La premessa del caveat e' sbagliata nella FORMA: il 2024+ un crash ce l'ha (b&h")
print(" -60% di drawdown, -39.6% in 20 giorni). Manca la TAGLIA, ~2x sulla coda veloce.")
print(" 2) Il compromesso ESISTE e non e' un compromesso: lo stesso peso (TP01 ~0.375) e'")
print(" l'argmax sulla finestra con il 2022 dentro E su quella senza. Il caveat di")
print(" PROP-ALLOC e' onesto come cautela e **falsificato come previsione**.")
print(" 3) TP01 protegge davvero (a pari leva sopravvive di piu' al crash, e alza la leva")
print(" sopportabile nel 2022 da 0.93x a 2.20x) ma la stessa protezione si compra piu' a")
print(" buon mercato ABBASSANDO LA LEVA: a iso-sopravvivenza il payout e' massimo a")
print(" TP01 0.125-0.250 e cala monotonamente da li'. **7ª occorrenza del null del")
print(" de-levering**, in una veste nuova: 'protezione dal crash' invece di 'meno DD'.")
print(" 4) Il rischio vero dell'ottimo di PROP-ALLOC NON e' TP01 sotto-pesato: e' XS01 al")
print(" 50% con un comportamento nel crash che il campione non contiene. Un solo giorno")
print(" a -8% su XS01 dimezza la sopravvivenza; con TP01 al 50% ne serve uno a -12.3%.")
print(" **Se TP01 vale qualcosa su un conto funded, vale come zavorra contro l'ignoto di")
print(" XS01, non come assicurazione contro il crash di BTC/ETH — che SKH01 fa meglio.**")
print("\n ⚠️ COSA MI SMENTIREBBE: un crash in cui SKH01 NON si gira short in tempo (gap")
print(" notturno oltre lo stop, o venue fermo). Il campione ne ha quattro e in tutti e")
print(" quattro SKH01 ha girato. Basterebbe un episodio in cui SKH01 perde quanto il")
print(" sottostante per rovesciare §2 e §5 — e con n=4 quell'episodio non e' improbabile.")
print("\n" + "=" * 106)
print(f" fatto in {time.time()-t0:.0f}s | valutazioni: {N_EVAL}")
print("=" * 106)