From f0122d97a1cde727fe25c8afdb73818d17cbd207 Mon Sep 17 00:00:00 2001 From: Adriano Dal Pastro Date: Sat, 22 Aug 2026 17:24:23 +0000 Subject: [PATCH] =?UTF-8?q?research(wave-0822):=20VOL-SIZE=20=E2=80=94=20i?= =?UTF-8?q?l=20null=20di=20permutazione=20si=20cita=20col=20segno,=20non?= =?UTF-8?q?=20col=20decimale=20(gira=20su=203=20ancore=20su=2023)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/research/RESULTS-0822.md | 8 +++++-- scripts/research/r0822_basis_calendar.py | 29 ++++++++++++++++++++++-- 2 files changed, 33 insertions(+), 4 deletions(-) diff --git a/docs/research/RESULTS-0822.md b/docs/research/RESULTS-0822.md index fc3385a..1ecf1b1 100644 --- a/docs/research/RESULTS-0822.md +++ b/docs/research/RESULTS-0822.md @@ -266,8 +266,12 @@ zeri nei due canali, il guadagno FULL viene dalla **FREQUENZA** (il solo filtro fortuna d'ancora del libro vale **+0,196 FULL / +0,448 HOLD**. - Cella congelata del LEAD (`VTL tv20 w90`): size fissata **all'ingresso** = `clip(0,20 / rv90(t−1), 0, 3)`. ISO dShFULL **+0,070 (23/23 ancore)**, dShHOLD +0,101 (19/23), - **8/8 anni positivi**, null di permutazione p~0,027 (e la mediana del null e' **−0,04**: - riassegnare a caso la stessa distribuzione di size **danneggia**). + **8/8 anni positivi**, **null di permutazione superato in ENTRAMBE le terne di ancore provate, + p <~ 0,03** (mediana del null −0,03/−0,05: riassegnare a caso la stessa distribuzione di size + **danneggia**). ⚠️ **Autocorrezione dell'agente, da rispettare:** quel null gira su **3 ancore + delle 23** per costo, e la TAGLIA dell'effetto varia di **3x** fra due terne diverse della + stessa banda (+0,033 vs +0,111 sulla stessa cella) -> **i p-value non si citano a tre decimali**: + il numero da mettere a verbale e' il **segno**. Il resto del report e' su 23 ancore piene. - ⚠️ `DSR = 1,000` dichiarato **VACUO, non PASS**: lo passa anche il baseline — su una famiglia di perturbazioni della stessa strategia il deflated-Sharpe non ha potenza. Onesta' rara. - ⚠️ Punto piu' fragile, dichiarato: l'asse della finestra e' un **picco, non un plateau** diff --git a/scripts/research/r0822_basis_calendar.py b/scripts/research/r0822_basis_calendar.py index a3138b2..a3199b1 100644 --- a/scripts/research/r0822_basis_calendar.py +++ b/scripts/research/r0822_basis_calendar.py @@ -514,7 +514,7 @@ def signal_w(cv: pd.DataFrame, family: str, sig: str, win_h: int, thr: float) -> def run_strategy(cv: pd.DataFrame, ctx: dict, family: str, sig: str, win_h: int, thr: float, slip_bps: float, - fee_side: float = FEE_SIDE, fi=None, bi=None) -> pd.Series: + fee_side: float = FEE_SIDE, fi=None, bi=None, lag: int = 1) -> pd.Series: """Ritorno ORARIO netto per $1 di nozionale LORDO PER GAMBA. Le posizioni vivono in spazio CONTRATTO: quando la coppia cambia (roll) la variazione @@ -541,7 +541,7 @@ def run_strategy(cv: pd.DataFrame, ctx: dict, family: str, sig: str, P[rows[m], fi[m]] = w[m] held = np.zeros_like(P) - held[1:] = P[:-1] # deciso a t, tenuto in t->t+1 + held[lag:] = P[:-lag] if lag else P # deciso a t, tenuto in t->t+lag gross = (held * ctx["R"]).sum(axis=1) dP = np.zeros_like(P) @@ -794,6 +794,31 @@ def main() -> None: except Exception: # noqa: BLE001 pass + # ---------------- STEP 6b: RITARDO D'ESECUZIONE (il test decisivo qui) ---------------- + print("\n[6b] RITARDO D'ESECUZIONE — il test decisivo di questo filone") + print(" Un datato lontano ha prezzi STANTII: una mean-reversion su una serie stantia") + print(" mostra profitti FINTI (il prezzo 'reverte' solo perche' si aggiorna in ritardo).") + print(" Un edge di STRUTTURA A TERMINE e' lento e sopravvive a un'ora in piu' di ritardo;") + print(" un artefatto di microstruttura muore. (Stessa lente del decadimento di XSR01.)") + lag_tab = [] + for lg in [1, 2, 3, 6, 12, 24]: + hs = [] + for a in ASSETS: + f_i, b_i = pidx[(a, int(b.roll))] + hs.append(run_strategy(cvcache[(a, int(b.roll))], ctxs[a], b.family, b.sig, + int(b.win), float(b.thr), slip_est, fi=f_i, bi=b_i, lag=lg)) + sl_ = stats(to_daily(pd.concat(hs, axis=1).fillna(0.0).mean(axis=1))) + lag_tab.append((lg, sl_["sharpe"], sl_["cagr"])) + print(f" ritardo {lg:2d}h -> Sharpe {sl_['sharpe']:+6.2f} CAGR {sl_['cagr']*100:+6.2f}%") + s1 = lag_tab[0][1]; s2 = lag_tab[1][1] + if s1 > 0: + keep = s2 / s1 + print(f" con UNA sola ora in piu' resta il {keep*100:.0f}% dello Sharpe " + f"({s1:+.2f} -> {s2:+.2f})") + print(" -> " + ("compatibile con un edge lento di struttura a termine" + if keep > 0.6 else + "CROLLO: firma di microstruttura/prezzo stantio, NON di term structure")) + # ---------------- STEP 7: quanto spread uccide l'edge ---------------- print("\n[7] A CHE SPREAD MUORE L'EDGE (la stima di slippage e' il rischio #1)") for sl in [0.0, 1.0, 2.5, 5.0, 7.5, 10.0, 20.0]: