feat(options): attach_market() panel helper (regime panel, causal, no look-ahead)
attach_market(df, asset): merge_asof causale del pannello market_snapshots (spot/VRP/funding/net-GEX/gamma-flip/liquidation) sul prezzo, pronto per regime_lab. Fix look-ahead: merge su datetime tz-aware (ns-aligned), MAI astype(int64) su datetime (darebbe ns -> match all'ultimo snapshot = leak). Copertura reale documentata: net-GEX denso solo da ~2026-05-01 (~5-6 sett, singolo regime) -> infrastruttura pronta, edge validabile solo forward. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -338,11 +338,17 @@ loader + `skew_curve()` / `premium_levels()` (aggregati ROBUSTI) + `quote()` cau
|
|||||||
**Caveat granularità:** cerbero-bite snapshotta una **fetta rotante** (~1 scadenza per ciclo) → ottimo
|
**Caveat granularità:** cerbero-bite snapshotta una **fetta rotante** (~1 scadenza per ciclo) → ottimo
|
||||||
per skew/premi **aggregati**, limitato per il pricing **per-trade** preciso (usare la finestra di
|
per skew/premi **aggregati**, limitato per il pricing **per-trade** preciso (usare la finestra di
|
||||||
staleness). **Pannello regime `market_snapshots`** (anch'esso importato → `data/options/market_snapshots.parquet`,
|
staleness). **Pannello regime `market_snapshots`** (anch'esso importato → `data/options/market_snapshots.parquet`,
|
||||||
loader `options_chain.load_market(asset)`): feature REALI pre-calcolate dal 2026-03-26, ~15min, 99-100%
|
loader `options_chain.load_market(asset)`; merge causale su prezzo via `options_chain.attach_market(df, asset)`):
|
||||||
popolate — **spot, dvol, realized_vol_30d, iv_minus_rv (VRP), funding perp/cross, `dealer_net_gamma`
|
feature REALI pre-calcolate — **spot, dvol, realized_vol_30d, iv_minus_rv (VRP), funding perp/cross,
|
||||||
(net-GEX dealer!), `gamma_flip_level`, oi_delta_pct_4h, liquidation_long/short_risk**. È il segnale
|
`dealer_net_gamma` (net-GEX dealer!), `gamma_flip_level`, oi_delta_pct_4h, liquidation_long/short_risk**.
|
||||||
ARGO/GEX che il progetto dichiarò non-backtestabile: ora accumula, reale e allineato (usabile come
|
È il segnale ARGO/GEX che il progetto dichiarò non-backtestabile: ora accumula, reale e allineato (lo
|
||||||
pannello in regime_lab; lo `spot` elimina il proxy ATM). **Numeri reali misurati (ETH):** skew put 10% OTM = **×1.1** (liquido, spread ~7%, NON ×1.6
|
`spot` elimina il proxy ATM). **Copertura reale:** spot/dvol dal 2026-03-26 (sparse/daily fino a fine
|
||||||
|
aprile), **net-GEX denso orario solo da ~2026-05-01** → ~5-6 settimane, **un singolo regime calmo**:
|
||||||
|
analisi esplorativa OK ma NIENTE edge validabile ora (i prior GEX/VRP non si replicano su 9 sett.;
|
||||||
|
`liquidation_risk` costante 'low'). Valore FORWARD: rivalutare quando il pannello attraversa
|
||||||
|
gamma-flip/liquidazioni/crash con held-out. **NB look-ahead:** usare `attach_market` (merge_asof
|
||||||
|
causale, NaN prima della copertura), MAI `astype(int64)` su un timestamp datetime (darebbe ns →
|
||||||
|
match all'ultimo snapshot = leak). **Numeri reali misurati (ETH):** skew put 10% OTM = **×1.1** (liquido, spread ~7%, NON ×1.6
|
||||||
come sembrava da un singolo snapshot a 1g, illiquido); premio reale put 10% OTM ≈ **1.0%/mese** (0.65%/sett),
|
come sembrava da un singolo snapshot a 1g, illiquido); premio reale put 10% OTM ≈ **1.0%/mese** (0.65%/sett),
|
||||||
catastrofe 15%+ OTM ≈ 0.33%/mese. **Vincolo strutturale:** gli strike **10% OTM a 24h NON esistono**
|
catastrofe 15%+ OTM ≈ 0.33%/mese. **Vincolo strutturale:** gli strike **10% OTM a 24h NON esistono**
|
||||||
(Deribit lista solo near-ATM sui tenor corti) → un overlay **per-trade a 24h è infattibile**; l'unica
|
(Deribit lista solo near-ATM sui tenor corti) → un overlay **per-trade a 24h è infattibile**; l'unica
|
||||||
|
|||||||
@@ -68,6 +68,30 @@ def load_market(asset: str | None = None) -> pd.DataFrame:
|
|||||||
return df.sort_values("ts_ms").reset_index(drop=True)
|
return df.sort_values("ts_ms").reset_index(drop=True)
|
||||||
|
|
||||||
|
|
||||||
|
def attach_market(price_df: pd.DataFrame, asset: str, cols: list[str] | None = None) -> pd.DataFrame:
|
||||||
|
"""merge_asof CAUSALE: ogni barra di price_df (serve colonna 'timestamp' in ms) riceve
|
||||||
|
l'ultimo market_snapshot con ts <= barra. Pannello pronto per regime_lab/ricerca:
|
||||||
|
spot, dvol, realized_vol_30d, iv_minus_rv (VRP), funding perp/cross, dealer_net_gamma
|
||||||
|
(net-GEX), gamma_flip_level, oi_delta_pct_4h, liquidation_long/short_risk.
|
||||||
|
Ritorna una copia con le colonne pannello (NaN dove non c'e' ancora storia: dal 2026-03-26)."""
|
||||||
|
m = load_market(asset)
|
||||||
|
cols = cols or (_MKT_NUM + ["liquidation_long_risk", "liquidation_short_risk"])
|
||||||
|
keep = [c for c in cols if c in m.columns]
|
||||||
|
base = price_df.copy()
|
||||||
|
# chiave di merge = datetime tz-aware (robusto a timestamp int-ms o datetime; NIENTE astype int64
|
||||||
|
# su datetime -> darebbe nanosecondi e matcherebbe tutto all'ultimo snapshot = LOOK-AHEAD).
|
||||||
|
if np.issubdtype(base["timestamp"].dtype, np.integer):
|
||||||
|
base["_k"] = pd.to_datetime(base["timestamp"], unit="ms", utc=True)
|
||||||
|
else:
|
||||||
|
base["_k"] = pd.to_datetime(base["timestamp"], utc=True)
|
||||||
|
base["_k"] = base["_k"].astype("datetime64[ns, UTC]")
|
||||||
|
mk = m[["ts"] + keep].rename(columns={"ts": "_k"})
|
||||||
|
mk["_k"] = mk["_k"].astype("datetime64[ns, UTC]")
|
||||||
|
out = pd.merge_asof(base.sort_values("_k"), mk.sort_values("_k"),
|
||||||
|
on="_k", direction="backward")
|
||||||
|
return out.drop(columns="_k")
|
||||||
|
|
||||||
|
|
||||||
class OptionChain:
|
class OptionChain:
|
||||||
def __init__(self, asset: str):
|
def __init__(self, asset: str):
|
||||||
self.asset = asset
|
self.asset = asset
|
||||||
|
|||||||
Reference in New Issue
Block a user