research(fade-lossguard): diagnostici perdite fade per regime + workflow anti-perdite
Diagnosi (3022 trade fade 2021+): perdite/stop concentrati in regime PERSISTENTE (hurst>0.55, SL-rate 43% vs 21% anti-persistente), NON in bassa vol (low-vol e' net positivo). Ricerca web conferma: filtro Hurst<0.45 / ADX<20 / vol-expansion ratio>1.5 (prevenne 72% perdite maggiori). Workflow 11 agenti testa i meccanismi sulle fade reali. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,66 @@
|
||||
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)})")
|
||||
Reference in New Issue
Block a user