"""Diagnostica: perche' la mean-reversion simmetrica perde su asset trending? Test: long-only vs short-only, e MR FILTRATA DAL TREND (buy-dip in uptrend, sell-rip in downtrend) per evitare di fadeare i trend forti. """ from __future__ import annotations import sys from pathlib import Path import numpy as np import pandas as pd PROJECT_ROOT = Path(__file__).resolve().parents[2] sys.path.insert(0, str(PROJECT_ROOT)) from scripts.analysis.honest_lab import ( # noqa: E402 atr, ema, get_df, simulate, oos_split, available_assets, FEE_RT, ) def zscore_entries(df, n=50, z_in=2.5, sl_atr=2.5, max_bars=24, trend_n=0, side="both"): """Z-score revert con filtro trend opzionale. trend_n>0: EMA di lungo periodo. Long solo se close>EMA (uptrend), short solo se close 0 else None start = max(n + 14, trend_n + 1 if trend_n else 0) ents = [] for i in range(start, len(c)): if np.isnan(z[i]) or np.isnan(a[i]): continue long_ok = (et is None or c[i] > et[i]) and side in ("both", "long") short_ok = (et is None or c[i] < et[i]) and side in ("both", "short") if z[i] <= -z_in and z[i - 1] > -z_in and long_ok: ents.append({"i": i, "d": 1, "tp": ma[i], "sl": c[i] - sl_atr * a[i], "max_bars": max_bars}) elif z[i] >= z_in and z[i - 1] < z_in and short_ok: ents.append({"i": i, "d": -1, "tp": ma[i], "sl": c[i] + sl_atr * a[i], "max_bars": max_bars}) return ents def row(label, df, ents): if len(ents) < 20: print(f" {label:<34s} {'<20 trd':>50s}") return None full = simulate(ents, df) _, oe = oos_split(ents, df) oos = simulate(oe, df) print(f" {label:<34s}{full.trades:>6d}{full.win:>7.1f}{full.ret:>+9.0f}" f"{oos.ret:>+9.0f}{full.dd:>6.0f}{f'{full.pos_years}/{full.n_years}':>8s}") return full, oos if __name__ == "__main__": assets = available_assets() print(f"HONEST DIAG — z-score revert, fee {FEE_RT*100:.2f}% RT, leva 3x | OOS 30%") for tf in ["1h"]: for a in assets: df = get_df(a, tf) print(f"\n === {a} {tf} === {'Trd':>5s}{'Win%':>7s}{'FULL%':>8s}{'OOS%':>8s}{'DD%':>6s}{'AnniP':>8s}") base = dict(n=50, z_in=2.5, sl_atr=2.5, max_bars=24) row("both, no filter", df, zscore_entries(df, **base, side="both")) row("long-only, no filter", df, zscore_entries(df, **base, side="long")) row("short-only, no filter", df, zscore_entries(df, **base, side="short")) row("both + trend200 filter", df, zscore_entries(df, **base, trend_n=200, side="both")) row("both + trend500 filter", df, zscore_entries(df, **base, trend_n=500, side="both")) row("long + trend200 filter", df, zscore_entries(df, **base, trend_n=200, side="long"))