import sys; sys.path.insert(0,".") import numpy as np, pandas as pd from scripts.analysis.regime_lab import load_features from scripts.analysis.explore_lab import atr import importlib FEE=0.001; LEV=3 def load_strat(mod): m=importlib.import_module(mod) return next(v() for k,v in vars(m).items() if isinstance(v,type) and hasattr(v,'generate_signals') and getattr(v,'__module__','')==m.__name__) STR={"MR01":("scripts.strategies.MR01_bollinger_fade",dict(bb_window=50,k=2.5,sl_atr=2.0,max_bars=24,trend_max=3.0)), "MR02":("scripts.strategies.MR02_donchian_fade",dict(n=20,sl_atr=2.0,max_bars=24,trend_max=3.0)), "MR07":("scripts.strategies.MR07_return_reversal",dict(n=50,k=3.5,tp_atr=2.0,sl_atr=1.5,max_bars=24,trend_max=3.0))} def replay(df, sigs): h=df['high'].values; l=df['low'].values; c=df['close'].values out=[]; last=-1 for s in sigs: i=s.idx if i<=last: continue d=s.direction; tp=s.metadata['tp']; sl=s.metadata['sl']; mb=s.metadata['max_bars'] j=min(i+mb,len(c)-1); exit_p=c[j] for t in range(i+1,j+1): if d==1: if l[t]<=sl: exit_p=sl;j=t;break if h[t]>=tp: exit_p=tp;j=t;break else: if h[t]>=sl: exit_p=sl;j=t;break if l[t]<=tp: exit_p=tp;j=t;break ret=(exit_p-c[i])/c[i]*d*LEV-FEE*LEV out.append((i,ret)); last=j return out # raccogli tutti i trade con il loro dvol_pct e hurst all'ingresso rows=[] for asset in ("BTC","ETH"): df=load_features(asset,"1h"); ts=pd.to_datetime(df['timestamp'],unit='ms',utc=True) for code,(mod,par) in STR.items(): s=load_strat(mod); sigs=s.generate_signals(df,ts,**par) for i,ret in replay(df,sigs): rows.append(dict(asset=asset,code=code,year=ts.iloc[i].year,ret=ret, dvol_pct=df['dvol_pct'].iloc[i], hurst=df['hurst'].iloc[i], dvol=df['dvol'].iloc[i])) R=pd.DataFrame(rows).dropna(subset=['dvol_pct']) print(f"trade totali (con DVOL, 2021+): {len(R)}") print("\n=== PnL medio per trade per TERZILE DVOL (bassa/media/alta vol) ===") R['dvbin']=pd.cut(R['dvol_pct'],[0,.33,.66,1.0],labels=['LOW-vol','MID','HIGH-vol']) g=R.groupby('dvbin',observed=True)['ret'] print(f" {'regime':<10}{'n':>6}{'ret_medio%':>12}{'win%':>8}{'somma%':>10}") for b in ['LOW-vol','MID','HIGH-vol']: x=R[R.dvbin==b]['ret'] print(f" {b:<10}{len(x):>6}{x.mean()*100:>12.3f}{(x>0).mean()*100:>8.1f}{x.sum()*100:>10.0f}") print("\n=== dentro LOW-vol: split per HURST (anti-persistente vs trending) ===") LV=R[R.dvbin=='LOW-vol'].copy() LV['hbin']=pd.cut(LV['hurst'],[0,.45,.55,1.0],labels=['hurst<.45 (anti-pers)','.45-.55','>.55 (trend)']) for b in ['hurst<.45 (anti-pers)','.45-.55','>.55 (trend)']: x=LV[LV.hbin==b]['ret'] if len(x): print(f" {b:<24}{len(x):>6} ret_medio {x.mean()*100:>+7.3f}% win {(x>0).mean()*100:>5.1f}% somma {x.sum()*100:>+6.0f}%") print("\n=== per anno: PnL fade in LOW-vol vs resto ===") for y in range(2021,2027): lo=R[(R.year==y)&(R.dvbin=='LOW-vol')]['ret']; hi=R[(R.year==y)&(R.dvbin!='LOW-vol')]['ret'] print(f" {y}: LOW-vol somma {lo.sum()*100:>+6.0f}% (n{len(lo)}) | MID/HIGH somma {hi.sum()*100:>+6.0f}% (n{len(hi)})")