research(wave-0822): VOL-SIZE — il p-value del null di permutazione si cita col segno, non col decimale (3 ancore su 23)

This commit is contained in:
Adriano Dal Pastro
2026-08-22 17:24:10 +00:00
parent e8bbb92f8a
commit f975272ec8
4 changed files with 550 additions and 22 deletions
+29 -8
View File
@@ -318,6 +318,14 @@ def certify(asset: str, wide: pd.DataFrame, vwide: pd.DataFrame, meta: pd.DataFr
print(f" convergenza a scadenza |ln(F/indice)| ultima ora: "
f"mediana {np.median(conv):.1f} bps, max {np.max(conv):.1f} bps (n={len(conv)})")
# (c-bis) scarto PERP vs INDICE: la gamba FvP lo assume piccolo, quindi si misura
pj = perp.reindex(fund.set_index("ts").index).dropna()
if len(pj) > 1000:
ii = fund.set_index("ts")["index"].reindex(pj.index)
pb = (np.log(pj / ii) * 1e4).replace([np.inf, -np.inf], np.nan).dropna()
print(f" perp vs indice: mediana {pb.median():+.1f} bps, p95 |scarto| "
f"{pb.abs().quantile(0.95):.1f} bps (n={len(pb):,}) — la gamba FvP e' sul PERP")
# (d) CROSS-CHECK INDIPENDENTE: il forward implicito nella catena opzioni
xchk = cross_check_option_forward(asset, wide, ok)
return dict(flat=fb, vol=vb, conv=conv, xchk=xchk)
@@ -370,7 +378,8 @@ def cross_check_option_forward(asset: str, wide: pd.DataFrame, meta: pd.DataFram
# 2. CURVA — front/back trimestrali, basis annualizzato, slope forward
# ==========================================================================
def curve_frame(asset: str, wide: pd.DataFrame, meta: pd.DataFrame,
fund: pd.DataFrame, roll_dte: int) -> pd.DataFrame:
fund: pd.DataFrame, roll_dte: int,
perp: pd.Series | None = None) -> pd.DataFrame:
"""Per ogni ora: front = trimestrale piu' vicino con dte >= roll_dte, back = il successivo.
Ritorna prezzi, tau, basis annualizzati, slope forward e funding."""
ok = meta[meta.status == "ok"].sort_values("exp")
@@ -408,6 +417,15 @@ def curve_frame(asset: str, wide: pd.DataFrame, meta: pd.DataFrame,
out["tau_front_h"], out["tau_back_h"] = tf, tb
out["index"] = F["index"].values
out["f1h"] = F["f1h"].values
# gamba perp: il PERP CERTIFICATO, non l'indice (l'indice non si compra).
# fallback all'indice solo se il perp non copre l'ora, e lo si dichiara.
if perp is not None:
pp = perp.reindex(idx)
out["perp"] = pp.where(pp.notna(), out["index"]).values
out["perp_from_index"] = pp.isna().values
else:
out["perp"] = out["index"].values
out["perp_from_index"] = True
# basis annualizzato di ciascuna gamba vs INDICE
out["c_front"] = np.log(Ff / out["index"]) * HOURS_Y / tf
out["c_back"] = np.log(Fb / out["index"]) * HOURS_Y / tb
@@ -532,7 +550,7 @@ def run_strategy(cv: pd.DataFrame, ctx: dict, family: str, sig: str,
turn = np.abs(dP).sum(axis=1)
if family == "FvP":
perp_r = np.nan_to_num(cv["index"].pct_change().values, nan=0.0)
perp_r = np.nan_to_num(cv["perp"].pct_change().values, nan=0.0)
f1h = np.nan_to_num(cv["f1h"].values, nan=0.0)
wheld = held.sum(axis=1) # una sola gamba datata
# long datato => short perp: guadagna -w*perp_r e INCASSA w*funding
@@ -609,11 +627,14 @@ def main() -> None:
# ---------------- STEP 2: certificazione ----------------
print("\n[2] CERTIFICAZIONE (regola 4: un edge su un book fermo non e' un edge)")
certs = {}
certs, perps = {}, {}
for a in ASSETS:
perp = A.get(a, "1h").set_index(pd.DatetimeIndex(
pd.to_datetime(A.get(a, "1h")["datetime"], utc=True)))["close"]
certs[a] = certify(a, panels[a], vwides[a], metas[a], funds[a], perp)
pdf = A.get(a, "1h")
perps[a] = pd.Series(
pdf["close"].astype(float).values,
index=pd.DatetimeIndex(pd.to_datetime(pdf["datetime"], utc=True))).sort_index()
perps[a] = perps[a][~perps[a].index.duplicated()]
certs[a] = certify(a, panels[a], vwides[a], metas[a], funds[a], perps[a])
x = certs[a]["xchk"]
if x.get("status") == "ok" and len(x["table"]):
t = x["table"]
@@ -628,7 +649,7 @@ def main() -> None:
print("\n[3] Q1 — LA CURVA E' PREVEDIBILE?")
curves = {}
for a in ASSETS:
cv = curve_frame(a, panels[a], metas[a], funds[a], roll_dte=7)
cv = curve_frame(a, panels[a], metas[a], funds[a], roll_dte=7, perp=perps[a])
curves[a] = cv
describe_curve(a, cv)
@@ -665,7 +686,7 @@ def main() -> None:
f" le conto lo stesso, al rialzo)")
ctxs = {a: asset_ctx(panels[a]) for a in ASSETS}
cvcache = {(a, rl): curve_frame(a, panels[a], metas[a], funds[a], rl)
cvcache = {(a, rl): curve_frame(a, panels[a], metas[a], funds[a], rl, perp=perps[a])
for a in ASSETS for rl in ROLLS}
pidx = {(a, rl): pair_idx(cvcache[(a, rl)], ctxs[a]) for a in ASSETS for rl in ROLLS}