diff --git a/scripts/portfolios/_defs.py b/scripts/portfolios/_defs.py index ee51aa6..b8931b3 100644 --- a/scripts/portfolios/_defs.py +++ b/scripts/portfolios/_defs.py @@ -18,8 +18,14 @@ UNIVERSE8 = ["ADA", "BNB", "BTC", "DOGE", "ETH", "LTC", "SOL", "XRP"] # MR02/MR07 lo ignorano (**params). Vedi docs/diary/2026-06-01-tp-min-edge.md. MIN_TP_FRAC = 0.0015 +# Loss-guard Hurst (live): salta le fade in regime PERSISTENTE/trending (rolling-Hurst >= 0.55), +# dove si concentrano stop-loss e perdite (stop-rate 43% vs 21% anti-persistente). DIMEZZA il DD +# del PORT06 (FULL 4.10%->2.39%) alzando lo Sharpe. Calcolato dalle SOLE close (no feed esterno). +# Validato 2026-06-02, vedi docs/diary/2026-06-02-fade-lossguard.md. +HURST_MAX = 0.55 + FADE = [SleeveSpec(kind="single", name=c, sid=f"{c}_{a}", asset=a, cluster=f"{a}-rev", - params={"min_tp_frac": MIN_TP_FRAC}) + params={"min_tp_frac": MIN_TP_FRAC, "hurst_max": HURST_MAX}) for a in ("BTC", "ETH") for c in ("MR01", "MR02", "MR07")] HONEST = [ # DIP01: single-asset 1h -> StrategyWorker (Strategy DIP01_dip_buy). TR01/ROT02: multi-asset. diff --git a/src/strategies/fade_base.py b/src/strategies/fade_base.py index f503e6a..095ebf4 100644 --- a/src/strategies/fade_base.py +++ b/src/strategies/fade_base.py @@ -18,17 +18,21 @@ from src.data.downloader import load_data from src.fractal.indicators import rolling_hurst -def hurst_skip_mask(df: pd.DataFrame, hurst_max: float | None, window: int = 100) -> np.ndarray: +def hurst_skip_mask(df: pd.DataFrame, hurst_max: float | None, window: int = 100, + step: int = 6) -> np.ndarray: """Loss-guard Hurst: maschera bool (True = SALTA il segnale) per regime PERSISTENTE/trending, dove la rolling-Hurst >= hurst_max. Le fade concentrano stop-loss e perdite proprio li' (diagnosi: stop-rate 43% per hurst>0.55 vs 21% anti-persistente). Filtrare hurst>=0.55 DIMEZZA il DD del PORT06 (FULL 4.10%->2.39%) alzando lo Sharpe (validato 2026-06-02). CAUSALE: rolling_hurst usa solo i rendimenti fino a close[i]. hurst_max=None -> nessuno skip. - Calcolata dalle SOLE close -> nessun feed dati esterno necessario (a differenza di DVOL).""" + Calcolata dalle SOLE close -> nessun feed dati esterno necessario (a differenza di DVOL). + step=6: calcola l'Hurst ogni 6 barre (ffill) -> ~6x piu' veloce per il worker live su finestre + lunghe (440g/10560 barre), e coincide con la cache di validazione (frac_step=6). L'Hurst varia + lentamente -> differenza trascurabile vs step=1.""" n = len(df) if hurst_max is None: return np.zeros(n, dtype=bool) - h = rolling_hurst(df["close"].values.astype(float), window=window) + h = rolling_hurst(df["close"].values.astype(float), window=window, step=step) return h >= hurst_max