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>
51 lines
3.0 KiB
Python
51 lines
3.0 KiB
Python
import sys; sys.path.insert(0,".")
|
|
import numpy as np, pandas as pd
|
|
from scripts.analysis.regime_lab import load_features
|
|
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];reason='time'
|
|
for t in range(i+1,j+1):
|
|
if d==1:
|
|
if l[t]<=sl: exit_p=sl;j=t;reason='sl';break
|
|
if h[t]>=tp: exit_p=tp;j=t;reason='tp';break
|
|
else:
|
|
if h[t]>=sl: exit_p=sl;j=t;reason='sl';break
|
|
if l[t]<=tp: exit_p=tp;j=t;reason='tp';break
|
|
out.append((i,(exit_p-c[i])/c[i]*d*LEV-FEE*LEV,reason));last=j
|
|
return out
|
|
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)
|
|
for i,ret,reason in replay(df,s.generate_signals(df,ts,**par)):
|
|
rows.append(dict(ret=ret,reason=reason,dvol_pct=df['dvol_pct'].iloc[i],hurst=df['hurst'].iloc[i],
|
|
vratio=df['vratio'].iloc[i],higuchi=df['higuchi'].iloc[i]))
|
|
R=pd.DataFrame(rows).dropna(subset=['dvol_pct','hurst'])
|
|
L=R[R.ret<0] # solo i trade in perdita
|
|
print(f"trade {len(R)} | in perdita {len(L)} ({len(L)/len(R)*100:.0f}%) | somma perdite {L.ret.sum()*100:.0f}% | media perdita {L.ret.mean()*100:.2f}%")
|
|
print("\n=== somma PERDITE per regime (dove si concentra il danno) ===")
|
|
R['dvbin']=pd.cut(R.dvol_pct,[0,.33,.66,1],labels=['LOWvol','MID','HIGHvol'])
|
|
R['hbin']=pd.cut(R.hurst,[0,.45,.55,1],labels=['anti<.45','.45-.55','trend>.55'])
|
|
piv=R[R.ret<0].pivot_table(index='dvbin',columns='hbin',values='ret',aggfunc='sum',observed=True)*100
|
|
print((piv.round(0)).to_string())
|
|
print("\n (numeri = somma % delle perdite per cella; piu negativo = piu danno)")
|
|
print("\n=== quota di SL (stop) per regime ===")
|
|
slr=R.groupby(['dvbin','hbin'],observed=True).apply(lambda x:(x.reason=='sl').mean()*100, include_groups=False)
|
|
print(slr.round(0).to_string())
|
|
# worst tail
|
|
print(f"\n=== peggiori 1% trade: dove? ===")
|
|
W=R.nsmallest(max(10,len(R)//100),'ret')
|
|
print(f" worst {len(W)} trade: dvol_pct medio {W.dvol_pct.mean():.2f}, hurst medio {W.hurst.mean():.2f}, quota hurst>.55 {(W.hurst>.55).mean()*100:.0f}%, quota dvol<.33 {(W.dvol_pct<.33).mean()*100:.0f}%")
|