research(wave-0822): MAKER scartato (il segno dipende da < contro <=) + CRITICO — la cucitura dell'ondata, non i filoni
This commit is contained in:
@@ -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]),
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user