14522262e6
Reset del progetto su fondamenta verificate dopo la scoperta che l'intera libreria "validata OOS" era artefatto di feed contaminato (print fantasma del feed Cerbero TESTNET + storico Binance/USDT). - Storico ricostruito da Deribit MAINNET (ccxt pubblico, tokenless) e CERTIFICATO (certify_feed.py): BTC/ETH puliti su TUTTA la storia (mediana 2-6 bps vs Coinbase USD), integrita' OHLC + coerenza resample (maxΔ 0.00) + cross-venue OK. Alt esclusi (illiquidi/divergenti: LTC/DOGE 50-82% barre flat; XRP/BNB non certificabili). - Verdetto sul feed pulito: FADE / PAIRS / XS01 / TSM01 morti (ogni portafoglio Sharpe -2.3..-3.0, DD ~40%); solo SH01 e frammenti HONEST con segnale residuo, da ri-validare in isolamento. - Cleanup "restart pulito": strategie, stack live (src/live, src/portfolio, runner/executor, yml, docker), ~100 script ricerca/gate, waste/games/ portfolios, dati non certificati + cache e 60+ diari -> archiviati in Old/ (preservati, non cancellati). Diario consolidato in un unico documento. - Skeleton ricerca tenuto: Strategy ABC + indicatori + src/fractal + src/backtest/engine + load_data; tool dati certificati (rebuild_history, certify_feed, audit_feed, multi_source_check). - Universo dati ATTIVO: solo BTC/ETH (5m/15m/1h); guardrail fisico (load_data su alt -> FileNotFoundError). Esecuzione DISABILITATA, conto flat. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
52 lines
2.3 KiB
Python
52 lines
2.3 KiB
Python
"""Loss-guard Hurst: le fade saltano i segnali in regime persistente/trending (rolling-Hurst >=
|
|
soglia), dove si concentrano stop-loss e perdite. Validato 2026-06-02: filtrare hurst>=0.55
|
|
DIMEZZA il DD del PORT06 alzando lo Sharpe. Filtro CAUSALE (close<=i), default off (None)."""
|
|
import numpy as np
|
|
import pandas as pd
|
|
|
|
from src.strategies.fade_base import hurst_skip_mask
|
|
|
|
|
|
def _df(close):
|
|
n = len(close)
|
|
return pd.DataFrame({"timestamp": range(n), "open": close, "high": close,
|
|
"low": close, "close": close, "volume": [1.0] * n})
|
|
|
|
|
|
def test_mask_off_when_none():
|
|
df = _df(np.cumsum(np.random.default_rng(0).normal(size=400)) + 100)
|
|
m = hurst_skip_mask(df, None)
|
|
assert m.dtype == bool and not m.any() # None -> nessuno skip
|
|
|
|
|
|
def test_mask_flags_persistent_regime():
|
|
# serie fortemente TRENDING (persistente, Hurst alto) -> deve essere mascherata (skip) molto
|
|
trend = np.linspace(100, 300, 600)
|
|
df = _df(trend)
|
|
m = hurst_skip_mask(df, hurst_max=0.55, window=100)
|
|
# dopo il warmup, una rampa pulita e' persistente -> gran parte mascherata
|
|
assert m[150:].mean() > 0.5
|
|
|
|
|
|
def test_fade_strategy_filters_signals():
|
|
"""Una fade con hurst_max produce <= segnali del baseline, e tutti i superstiti sono in
|
|
regime non-persistente (la maschera e' False alla loro barra)."""
|
|
import importlib
|
|
rng = np.random.default_rng(1)
|
|
# serie mean-reverting (anti-persistente) con qualche estensione -> genera fade
|
|
n = 1200
|
|
c = 100 + np.cumsum(rng.normal(scale=0.5, size=n))
|
|
c = 100 + (c - c.mean()) * 0.3 # comprimi verso la media (mean-revert)
|
|
df = _df(c)
|
|
ts = pd.to_datetime(df["timestamp"], unit="s", utc=True)
|
|
m = importlib.import_module("scripts.strategies.MR01_bollinger_fade")
|
|
Strat = next(v for k, v in vars(m).items()
|
|
if isinstance(v, type) and getattr(v, "__module__", "") == m.__name__
|
|
and hasattr(v, "generate_signals"))
|
|
s = Strat()
|
|
base = s.generate_signals(df, ts, bb_window=50, k=2.0, sl_atr=2.0)
|
|
filt = s.generate_signals(df, ts, bb_window=50, k=2.0, sl_atr=2.0, hurst_max=0.55)
|
|
assert len(filt) <= len(base) # il filtro non aggiunge mai segnali
|
|
skip = hurst_skip_mask(df, 0.55, 100)
|
|
assert all(not skip[sig.idx] for sig in filt) # nessun superstite in regime persistente
|