"""MR07 — Return Reversal (fade del movimento di barra estremo).

Meccanismo distinto da MR01/MR02/MR03: non guarda i LIVELLI di prezzo (bande,
canali) ma la VOLATILITA' dei rendimenti. Quando una singola barra si muove di
piu' di k deviazioni standard rolling dei rendimenti, e' un'over-reaction che
tende a rientrare: si fada nella direzione opposta. Coerente con la lezione
mean-reversion.

Logica:
    1. ret[i] = rendimento dell'ultima barra; sigma = std rolling(n) dei rendimenti
    2. z = ret[i]/sigma. Se z <= -k (crollo) -> LONG; se z >= +k (spike) -> SHORT.
       Ingresso a close[i] (eseguibile dal vivo, nessun look-ahead).
    3. EXIT: take-profit a tp_atr*ATR a favore, stop-loss a sl_atr*ATR contro,
       time-limit max_bars.

Validazione (netto, fee 0.10% RT reale Deribit, leva 3x, OOS = ultimo 30%):
    config robusta k=3.5 tp=2ATR sl=1.5ATR n=50:
    BTC 1h: +447% FULL / +105% OOS, DD 25%
    ETH 1h: +335% FULL / +195% OOS, DD 46%
    L'intero blocco tp_atr=2.0 (k in {2.5,3.0,3.5} x sl in {1.5,2.0,2.5}) e'
    positivo full+OOS su entrambi gli asset 1h.
    Ricerca completa: scripts/analysis/strategy_research_v2.py.
"""
from __future__ import annotations
import sys
sys.path.insert(0, ".")

import numpy as np
import pandas as pd

from src.strategies.base import Signal
from src.strategies.fade_base import FadeStrategy, atr, trend_distance, hurst_skip_mask


class ReturnReversal(FadeStrategy):
    name = "MR07_return_reversal"
    description = "Mean-reversion: fada il movimento di barra estremo (z dei rendimenti)"
    default_assets = ["BTC", "ETH"]
    default_timeframes = ["1h"]

    def generate_signals(self, df: pd.DataFrame, ts: pd.DatetimeIndex,
                         **params) -> list[Signal]:
        n = params.get("n", 50)
        k = params.get("k", 3.5)
        tp_atr = params.get("tp_atr", 2.0)
        sl_atr = params.get("sl_atr", 1.5)
        max_bars = params.get("max_bars", 24)
        trend_max = params.get("trend_max")          # None = filtro disattivo
        ema_long = params.get("ema_long", 200)
        # Edge minimo: salta i fade il cui TP (ATR-scaled) è entro il costo RT. 0 = off.
        min_tp_frac = params.get("min_tp_frac", 0.0)
        # Loss-guard Hurst: salta in regime persistente/trending (hurst >= soglia). None = off.
        hurst_max = params.get("hurst_max")

        c = df["close"].values
        ret = np.zeros_like(c)
        ret[1:] = np.diff(c) / c[:-1]
        sig = pd.Series(ret).rolling(n).std().values
        a = atr(df, 14)
        td = trend_distance(df, ema_long) if trend_max is not None else None
        skip = hurst_skip_mask(df, hurst_max, params.get("hurst_win", 100))

        signals: list[Signal] = []
        for i in range(n + 14, len(c)):
            if np.isnan(sig[i]) or sig[i] == 0 or np.isnan(a[i]):
                continue
            if skip[i]:
                continue                              # loss-guard: regime persistente
            if td is not None and (np.isnan(td[i]) or td[i] > trend_max):
                continue
            z = ret[i] / sig[i]
            if z <= -k:                                # crollo di barra -> fade long
                d, tp, sl = 1, c[i] + tp_atr * a[i], c[i] - sl_atr * a[i]
            elif z >= k:                               # spike di barra -> fade short
                d, tp, sl = -1, c[i] - tp_atr * a[i], c[i] + sl_atr * a[i]
            else:
                continue
            if min_tp_frac > 0 and abs(tp - c[i]) / c[i] <= min_tp_frac:
                continue                              # TP entro le fee -> non eseguibile in utile
            signals.append(Signal(
                idx=i, direction=d, entry_price=c[i],
                metadata={"tp": float(tp), "sl": float(sl), "max_bars": max_bars},
            ))
        return signals


if __name__ == "__main__":
    strat = ReturnReversal()
    print("=" * 110)
    print(f"  MR07 RETURN REVERSAL — netto fee {strat.fee_rt*100:.2f}% RT, leva {strat.leverage:.0f}x")
    print("=" * 110)
    for asset in ["BTC", "ETH"]:
        r = strat.backtest(asset, "1h", n=50, k=3.5, tp_atr=2.0, sl_atr=1.5, max_bars=24)
        if r:
            r.strategy_name = f"MR07 {asset} 1h k3.5"
            r.print_summary()
            r.print_yearly()
