research(wave-0822): VRP-QUOTE-VERE scartato — e collect_chain raccoglie la famiglia di contratti che il conto non puo' marginare
This commit is contained in:
@@ -5,7 +5,11 @@ DOMANDA. Tre, dichiarate prima di misurare:
|
||||
Q2 il CARRY di vol (roll-down lungo la curva) e' INCASSABILE al bid-ask reale?
|
||||
Q3 vale come GATE DI RISCHIO sopra TP01/SKH01?
|
||||
|
||||
IPOTESI A PRIORI (registrate prima di guardare i numeri — servono a poterle smentire):
|
||||
IPOTESI A PRIORI (⚠️ da dove vengono: NON da una busta sigillata. Vengono dai precedenti gia'
|
||||
DOCUMENTATI del progetto — i 4 gate di de-risk risultati ridondanti col trend, il verdetto HEDGE
|
||||
su VRP-Z del 29/06, l'aritmetica fee/carry gia' fatta su VRP01 e gamma-scalping. Sono state
|
||||
scritte prima di far girare QUESTO script, ma dopo una fase esplorativa sui dati: si dichiara,
|
||||
perche' "l'avevo previsto" vale solo se si dice quanto costava prevederlo):
|
||||
Q1 contemporanea. Il front reagisce allo spot: un tuffo alza la vol a 7g piu' di quella a 30g.
|
||||
Se e' cosi', la pendenza e' un TERMOMETRO, non un segnale.
|
||||
Q2 no. Il roll-down fra 7g e 30g vale frazioni di punto-vol al giorno; il bid-ask ATM di
|
||||
@@ -77,6 +81,10 @@ CAPITALE = 635.0 # il conto vero
|
||||
|
||||
# Conteggio dei trial, AL RIALZO (sezione 6). Ogni voce e' una configurazione VALUTATA.
|
||||
TRIALS: list[tuple[str, int]] = []
|
||||
# I pochi numeri su cui poggia il verdetto, raccolti dalle sezioni che li misurano: il verdetto
|
||||
# NON li riscrive a mano (un numero ricopiato in una conclusione e' un numero che smette di
|
||||
# aggiornarsi quando i dati crescono).
|
||||
FACTS: dict = {}
|
||||
|
||||
|
||||
def hr(t: str = "") -> None:
|
||||
@@ -333,8 +341,11 @@ def sezione2(curve: pd.DataFrame) -> dict:
|
||||
idx = j.index
|
||||
r1 = lp.diff()
|
||||
sub(f"{a} — n ore = {len(j)} ({idx.min()} -> {idx.max()})")
|
||||
FACTS[f"back_sev_{a}"] = float((j.slope < -5).mean())
|
||||
FACTS[f"back_{a}"] = float((j.slope < 0).mean())
|
||||
print(f" pendenza iv_{BACK}-iv_{FRONT}: media {j.slope.mean():+.2f} pt-vol, "
|
||||
f"sd {j.slope.std():.2f}, backwardation nel {100*(j.slope<0).mean():.1f}% delle ore")
|
||||
f"sd {j.slope.std():.2f}, backwardation nel {100*(j.slope<0).mean():.1f}% delle ore, "
|
||||
f"SEVERA (< -5 pt-vol) nel {100*(j.slope<-5).mean():.1f}%")
|
||||
|
||||
print("\n (a) CROSS-CORRELAZIONE fra la VARIAZIONE oraria di pendenza e il ritorno orario.")
|
||||
print(" lag<0 = ritorno PRIMA della variazione (il prezzo guida) | lag>0 = DOPO (la")
|
||||
@@ -374,6 +385,7 @@ def sezione2(curve: pd.DataFrame) -> dict:
|
||||
print(f" corr(iv_{FRONT}, RV 24h passata) {float(ivf.corr(rvp)):+.3f} "
|
||||
f"corr(iv_{FRONT}, RV futura) {float(ivf.corr(rvf)):+.3f}")
|
||||
res[(a, "conf_past")], res[(a, "conf_fwd")] = c_pp, c_pf
|
||||
FACTS[f"conf_past_{a}"], FACTS[f"conf_fwd_{a}"] = c_pp, c_pf
|
||||
|
||||
print("\n (d) La correlazione col futuro SOPRAVVIVE se si controlla per il PASSATO?")
|
||||
print(" (regressione del ritorno futuro su pendenza + ritorno passato + RV passata;")
|
||||
@@ -412,6 +424,8 @@ def sezione2(curve: pd.DataFrame) -> dict:
|
||||
p = _block_pctl(x, y, c, L=max(3 * h, 7))
|
||||
c7 = float(np.corrcoef(x[:-7], y[:-7])[0, 1]) if len(x) > 20 else np.nan
|
||||
c7b = float(np.corrcoef(x[7:], y[7:])[0, 1]) if len(x) > 20 else np.nan
|
||||
if h == 5:
|
||||
FACTS[f"c5_{a}"], FACTS[f"c5_no7_{a}"] = c, c7
|
||||
print(f" h={h}g n={m.sum():3d} corr={c:+.3f} pctl vs null a blocchi={p:.3f}"
|
||||
f" | senza ultima settimana {c7:+.3f} | senza prima settimana {c7b:+.3f}")
|
||||
print(" ^ le due colonne di destra sono il test di RISOLUZIONE: se togliere 7")
|
||||
@@ -548,6 +562,7 @@ def sezione3(chain: pd.DataFrame, atm_exp: pd.DataFrame, curve: pd.DataFrame) ->
|
||||
f"un round-trip ATM 10-20g costa {sp:.3f} pt-vol")
|
||||
print(f" => servono {sp/rd:.1f} GIORNI di carry per pagare UN solo giro, "
|
||||
f"e nel frattempo si porta il vega.")
|
||||
FACTS[f"giorni_carry_{a}"] = sp / rd
|
||||
TRIALS.append(("Q2: 5 secchi di tenore x 2 asset (costo)", 10))
|
||||
|
||||
sub("(d) e a $635 la domanda non e' nemmeno questa: il LOTTO MINIMO")
|
||||
@@ -610,6 +625,8 @@ def sezione4(curve: pd.DataFrame) -> None:
|
||||
sub("(a) il fatto che decide da solo la sezione")
|
||||
W = pd.concat(pos, axis=1).loc[TS_START:]
|
||||
flat = (W.abs() < 0.05).mean()
|
||||
FACTS["tp01_flat"] = float(flat.mean())
|
||||
FACTS["n_giorni"] = int(len(W))
|
||||
print(f" Nella finestra {TS_START.date()} -> oggi ({len(W)} giorni), TP01 e' FLAT nel "
|
||||
f"{flat['BTC']:.1%} (BTC) / {flat['ETH']:.1%} (ETH) dei giorni.")
|
||||
print(f" Esposizione media: BTC {W['BTC'].mean():.3f}, ETH {W['ETH'].mean():.3f} "
|
||||
@@ -665,6 +682,8 @@ def sezione4(curve: pd.DataFrame) -> None:
|
||||
print(f" buy&hold sulla stessa finestra/ancore: mediana {bh['median']:+.2f} "
|
||||
f"[{bh['lo']:+.2f}, {bh['hi']:+.2f}] <-- il vero termine di paragone in un toro")
|
||||
dlt = A.anchor_luck_delta(cand, base, list(range(24)))
|
||||
FACTS[f"vs_bh_{a}"] = dlt["median_paired"]
|
||||
FACTS[f"vs_bh_n_{a}"] = (dlt["n_positive"], dlt["n_anchors"])
|
||||
print(f" mediana delle DIFFERENZE APPAIATE candidato-B&H: {dlt['median_paired']:+.2f} "
|
||||
f"(positiva in {dlt['n_positive']}/{dlt['n_anchors']} ancore)")
|
||||
TRIALS.append((f"Q3 {a}: 24 ancore x 2 varianti", 48))
|
||||
@@ -697,10 +716,12 @@ def sezione4(curve: pd.DataFrame) -> None:
|
||||
else:
|
||||
verdict = ("IL CANDIDATO NON SOPRAVVIVE (il de-levering fa meglio)"
|
||||
if best[1] >= sh_c else "candidato sopra il null di de-levering")
|
||||
FACTS[f"delev_{a}"] = bool(best[1] >= sh_c)
|
||||
print(f" {a}: candidato Sharpe {sh_c:+.2f} maxDD {dd_c:.2%} | buy&hold a leva "
|
||||
f"k={best[0]:.2f} -> stesso o minor DD ({best[2]:.2%}) con Sharpe {best[1]:+.2f}"
|
||||
f" => {verdict}")
|
||||
imp = A.implausible_sharpe(net)
|
||||
FACTS[f"implaus_{a}"] = bool(imp["implausible"])
|
||||
print(f" implausible_sharpe sul candidato di finestra: implausible="
|
||||
f"{imp['implausible']} {imp['reasons']}")
|
||||
TRIALS.append((f"Q3 {a}: 60 valori di k nel null de-levering", 60))
|
||||
@@ -760,6 +781,9 @@ def sezione5(curve: pd.DataFrame) -> None:
|
||||
print(f" era DVOL (2021-10 -> oggi, {len(era)} giorni): Sharpe {A._sh(era):+.3f} "
|
||||
f"maxDD {A._dd_ret(era):.2%}")
|
||||
rep = A.marginal_vs_tp01(cd)
|
||||
FACTS["proxy_marginal"] = rep.get("marginal_verdict")
|
||||
FACTS["proxy_sharpe"] = A._sh(era)
|
||||
FACTS["proxy_uplift"] = rep["blends"]["w25"]["uplift_full"]
|
||||
print(f" marginal_vs_tp01 -> {rep.get('marginal_verdict')} | corr a TP01 "
|
||||
f"{rep.get('corr_full')} | uplift w25 full {rep['blends']['w25']['uplift_full']:+.3f} "
|
||||
f"hold {rep['blends']['w25']['uplift_hold']}")
|
||||
@@ -807,6 +831,95 @@ def sezione6() -> None:
|
||||
print(" su cui questo filone ricade (VRP-Z: earns_slot=False).")
|
||||
|
||||
|
||||
|
||||
# ===========================================================================================
|
||||
# SEZIONE 7 — VERDETTO
|
||||
# ===========================================================================================
|
||||
def sezione7() -> None:
|
||||
F = FACTS
|
||||
hr("SEZIONE 7 — VERDETTO")
|
||||
print(f"""
|
||||
Q1 LA PENDENZA E' UN TERMOMETRO, NON UN SEGNALE — e la finestra non ha risoluzione.
|
||||
· La cross-correlazione oraria ha il picco a lag -1h/0h (il prezzo si muove PRIMA); da
|
||||
+1h in poi e' rumore (|corr| <= 0.05 su ogni lag fino a 24h).
|
||||
· corr(pendenza, RV 24h PASSATA) = {F.get('conf_past_BTC', float('nan')):+.2f} (BTC) / {F.get('conf_past_ETH', float('nan')):+.2f} (ETH)
|
||||
corr(pendenza, RV 24h FUTURA) = {F.get('conf_fwd_BTC', float('nan')):+.2f} (BTC) / {F.get('conf_fwd_ETH', float('nan')):+.2f} (ETH)
|
||||
La pendenza SA qualcosa della vol futura, ma sa DI PIU' di quella passata: e' un premio
|
||||
di vol con la gamba realizzata sostituita da una implicita, non una variabile nuova.
|
||||
· L'unica correlazione col ritorno futuro che sembra qualcosa (h=5g: {F.get('c5_BTC', float('nan')):+.2f} BTC /
|
||||
{F.get('c5_ETH', float('nan')):+.2f} ETH) NON SOPRAVVIVE alla rimozione dell'ULTIMA SETTIMANA:
|
||||
diventa {F.get('c5_no7_BTC', float('nan')):+.2f} / {F.get('c5_no7_ETH', float('nan')):+.2f}. Sette giorni tolti da un campione di 70 ne
|
||||
ribaltano il SEGNO — ed e' il test che conta piu' del percentile contro il null.
|
||||
E nella regressione controllata per il ritorno passato il t di Newey-West sta sotto 1.0
|
||||
a ogni orizzonte. Non e' "debole": e' sotto la risoluzione del campione.
|
||||
|
||||
Q2 IL CARRY C'E' MA NON PAGA IL BID-ASK, e non e' nemmeno incassato.
|
||||
· Roll-down da corto: ~0.07 (BTC) / ~0.13 (ETH) punti-vol al GIORNO.
|
||||
Round-trip ATM 10-20g: 1.26 (BTC) / 1.77 (ETH) punti-vol.
|
||||
=> {F.get('giorni_carry_BTC', float('nan')):.0f} giorni (BTC) / {F.get('giorni_carry_ETH', float('nan')):.0f} giorni (ETH) di carry per pagare UN giro.
|
||||
· E il roll NON viene incassato: beta(realizzato ~ implicito) e' NEGATIVO (~-1 a una
|
||||
settimana), cioe' la forma della curva torna indietro piu' di quanto la curva rotoli.
|
||||
La differenza realizzato-implicito e' dentro il rumore (|t| <= 0.9 a blocchi).
|
||||
· A $635 il vincolo binding e' prima di tutto il LOTTO MINIMO: una gamba ATM costa il 26%
|
||||
(BTC) / 10% (ETH) del conto in premio, la gamba corta vuole margine, e il carry lordo
|
||||
di una struttura ETH e' ~$0.19/giorno. Il bersaglio dichiarato e' 50 EUR/giorno.
|
||||
|
||||
Q3 GATE DI RISCHIO: RIDONDANTE COL TREND — 5a occorrenza, e NON MISURABILE su questa finestra.
|
||||
· TP01 e' FLAT nel {F.get('tp01_flat', float('nan')):.0%} dei giorni della finestra: non c'e' rischio da togliere.
|
||||
Un gate che spegne il 25% dei giorni trova TP01 gia' flat nel 79-90% di quei giorni, e
|
||||
corr(pendenza, esposizione TP01) e' NEGATIVA (-0.29 BTC / -0.52 ETH): la backwardation
|
||||
arriva quando il trend e' gia' uscito. Firma identica ai 4 precedenti.
|
||||
· Il candidato DIREZIONALE fa Sharpe ~3 su 24/24 ancore — e non vuol dire niente: il
|
||||
buy&hold sulla stessa finestra fa altrettanto. Mediana delle DIFFERENZE APPAIATE contro
|
||||
buy&hold: {F.get('vs_bh_BTC', float('nan')):+.2f} BTC (positiva in {F.get('vs_bh_n_BTC', (0, 0))[0]}/{F.get('vs_bh_n_BTC', (0, 0))[1]} ancore) e {F.get('vs_bh_ETH', float('nan')):+.2f} ETH ({F.get('vs_bh_n_ETH', (0, 0))[0]}/{F.get('vs_bh_n_ETH', (0, 0))[1]}).
|
||||
· Null del de-levering: ETH {'NON superato' if F.get('delev_ETH') else 'superato'}, BTC {'NON superato' if F.get('delev_BTC') else 'superato'}.
|
||||
· `implausible_sharpe` segnala ENTRAMBI i candidati di finestra (implaus BTC={F.get('implaus_BTC')},
|
||||
ETH={F.get('implaus_ETH')}): Calmar 26-52 su 74 giorni di toro = il modo di perdita non e' nel
|
||||
campione. Il gate qui funziona da controllo positivo del CAMPIONE, non della strategia.
|
||||
|
||||
TEST LUNGO (proxy DVOL-RV7, 5 anni): Sharpe {F.get('proxy_sharpe', float('nan')):+.2f}, marginale vs TP01 =
|
||||
{F.get('proxy_marginal')}, uplift w25 {F.get('proxy_uplift', float('nan')):+.3f} (NEGATIVO), robust_oos=False
|
||||
=> earns_slot = False. Il proxy e' sfocato (spiega il 21-48% della varianza della pendenza
|
||||
vera), quindi il suo negativo e' evidenza PIU' DEBOLE di un negativo diretto — ma va nella
|
||||
stessa direzione del verdetto HEDGE gia' dato a VRP-Z il 2026-06-29.
|
||||
|
||||
======================================================================================
|
||||
VERDETTO: SCARTATO come sleeve e come gate. LEAD residuo SOLO sulla term structure come
|
||||
PREVISORE DI VOL (non di ritorni), con gate pre-registrato qui sotto.
|
||||
======================================================================================
|
||||
|
||||
Perche' non "LEAD" sul lato direzionale: un LEAD e' una cosa che vale la pena SORVEGLIARE, e
|
||||
sorvegliare costa (la lezione DVOLSPREAD: 35 giorni di limbo). Qui il lato direzionale non ha
|
||||
bisogno di altri dati per essere giudicato: e' gia' misurato su 5 anni via proxy (earns_slot
|
||||
False) e la variabile e' quella di VRP-Z. Aprire un monitor sarebbe pagare due volte lo stesso
|
||||
filone. Il lato del CARRY e' chiuso dall'aritmetica, che non migliora col tempo: il bid-ask
|
||||
ATM di Deribit dovrebbe stringersi di 10-20x perche' il roll-down lo paghi.
|
||||
|
||||
GATE PRE-REGISTRATO (l'unico pezzo che il tempo puo' cambiare):
|
||||
· COSA: la pendenza come previsore della RV a 24h (misurata: corr {F.get('conf_fwd_BTC', float('nan')):+.2f} / {F.get('conf_fwd_ETH', float('nan')):+.2f}).
|
||||
Non serve a prendere posizione sul prezzo; servirebbe come DENOMINATORE del
|
||||
vol-target, cioe' esattamente cio' che `tp01_dvol_overlay` (26/06) provo' col DVOL
|
||||
a un tenore solo e trovo' essere puro de-levering. La curva a piu' tenori e' l'unica
|
||||
variante di quell'esperimento mai fatta.
|
||||
· QUANDO: 2027-02-01. A quella data la catena avra' ~8 mesi di curva (vs 2,4 oggi) e la
|
||||
finestra conterra' almeno un regime di vol diverso da questo toro.
|
||||
· SOGLIE (dichiarate ORA, non allora): si riapre SOLO se, sulla finestra estesa,
|
||||
(a) corr(pendenza, RV futura 24h) resta <= -0.30 su ENTRAMBI gli asset, E
|
||||
(b) la pendenza batte il DVOL a tenore singolo come previsore di RV
|
||||
(R2 incrementale > 0 in una regressione che contiene gia' il DVOL: se non
|
||||
aggiunge nulla al DVOL, la curva non serve e basta il tenore che abbiamo dal 2021), E
|
||||
(c) il null del de-levering e' superato sul vol-target risultante.
|
||||
Se (b) fallisce il filone si CHIUDE definitivamente: vorra' dire che la term
|
||||
structure non porta informazione oltre il livello, e il livello ce l'abbiamo gia'.
|
||||
· COSA MI SMENTIREBBE OGGI: una finestra che contenga un vero crash. Tutte e tre le risposte
|
||||
qui sono misurate su 74 giorni di TORO con TP01 flat il {F.get('tp01_flat', float('nan')):.0%} del tempo. La backwardation
|
||||
severa (pendenza < -5 pt-vol) copre il {F.get('back_sev_BTC', float('nan')):.1%} delle ore BTC e il {F.get('back_sev_ETH', float('nan')):.1%} delle ore
|
||||
ETH, e mai dentro un drawdown vero del libro: se in un crash
|
||||
la pendenza anticipasse i ritorni di ore invece di seguirli, Q1 cambierebbe risposta. Cio'
|
||||
che NON cambierebbe e' Q2 (aritmetica del bid-ask) ne' l'eseguibilita' a $635.
|
||||
""")
|
||||
|
||||
|
||||
def main() -> None:
|
||||
hr("r0822_term_structure — TERM STRUCTURE DELLA VOL IMPLICITA BTC/ETH")
|
||||
chain = load_quoted_chain()
|
||||
@@ -820,6 +933,7 @@ def main() -> None:
|
||||
sezione4(curve)
|
||||
sezione5(curve)
|
||||
sezione6()
|
||||
sezione7()
|
||||
hr("FINE")
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user