"""HY01 — Squeeze + Mean Reversion Ibrida.

Insight: durante lo squeeze (bassa volatilità), il prezzo mean-reverte
DENTRO il range compresso. Autocorrelazione negativa a 15m conferma.
Invece di aspettare il BREAKOUT, tradi la MEAN REVERSION dentro lo squeeze.

Completamente diverso da SQ01-SQ04 che aspettano il RILASCIO.

IN:
    - OHLCV DataFrame
    - Parametri: bb_window, sq_threshold, rsi_period, rsi_levels,
                 vol_filter, bb_touch (prezzo tocca banda Bollinger)

OUT:
    - Signal: long quando RSI oversold DURANTE squeeze, short quando overbought
    - BacktestResult

Logica:
    1. Verifica che siamo IN squeeze (BB dentro KC)
    2. Prezzo tocca banda inferiore BB → LONG (tornerà alla media)
    3. Prezzo tocca banda superiore BB → SHORT (tornerà alla media)
    4. Conferma RSI: deve essere estremo nella direzione
    5. Hold corto (2-3 barre) — target: ritorno alla media
    6. Stop: se prezzo rompe lo squeeze → chiudi subito
"""
from __future__ import annotations
import sys
sys.path.insert(0, ".")

import numpy as np
import pandas as pd
from src.strategies.base import Strategy, Signal
from src.strategies.indicators import keltner_ratio


def rsi(close, period=14):
    delta = np.diff(close)
    gain = np.where(delta > 0, delta, 0)
    loss = np.where(delta < 0, -delta, 0)
    result = np.full(len(close), 50.0)
    if len(gain) < period:
        return result
    ag = np.mean(gain[:period])
    al = np.mean(loss[:period])
    for i in range(period, len(delta)):
        ag = (ag * (period - 1) + gain[i]) / period
        al = (al * (period - 1) + loss[i]) / period
        result[i + 1] = 100 if al == 0 else 100 - 100 / (1 + ag / al)
    return result


def bollinger(close, window=14):
    n = len(close)
    upper = np.full(n, np.nan)
    lower = np.full(n, np.nan)
    mid = np.full(n, np.nan)
    for i in range(window, n):
        wc = close[i - window:i]
        m = np.mean(wc)
        s = np.std(wc)
        mid[i] = m
        upper[i] = m + 2 * s
        lower[i] = m - 2 * s
    return upper, mid, lower


class SqueezeMeanReversion(Strategy):
    name = "HY01_squeeze_mr"
    description = "Mean reversion DENTRO lo squeeze — fade estremi in range compresso"
    default_assets = ["BTC", "ETH"]
    default_timeframes = ["15m", "1h"]
    fee_rt = 0.002

    def generate_signals(self, df, ts, **params):
        c = df["close"].values
        h = df["high"].values
        l = df["low"].values
        v = df["volume"].values
        n = len(c)

        bb_w = params.get("bb_window", 14)
        sq_thr = params.get("sq_threshold", 0.8)
        rsi_period = params.get("rsi_period", 14)
        rsi_low = params.get("rsi_oversold", 30)
        rsi_high = params.get("rsi_overbought", 70)
        use_bb_touch = params.get("bb_touch", True)
        cooldown = params.get("cooldown", 3)

        kcr = keltner_ratio(c, h, l, bb_w)
        rsi_vals = rsi(c, rsi_period)
        bb_upper, bb_mid, bb_lower = bollinger(c, bb_w)

        signals = []
        last_idx = -cooldown

        for i in range(bb_w + 1, n):
            if i - last_idx < cooldown:
                continue
            if np.isnan(kcr[i]) or np.isnan(bb_lower[i]):
                continue

            # Must be IN squeeze
            if kcr[i] >= sq_thr:
                continue

            direction = 0

            if use_bb_touch:
                # Prezzo tocca/rompe BB lower → long (mean reversion up)
                if c[i] <= bb_lower[i] and rsi_vals[i] < rsi_low:
                    direction = 1
                # Prezzo tocca/rompe BB upper → short (mean reversion down)
                elif c[i] >= bb_upper[i] and rsi_vals[i] > rsi_high:
                    direction = -1
            else:
                # Solo RSI
                if rsi_vals[i] < rsi_low:
                    direction = 1
                elif rsi_vals[i] > rsi_high:
                    direction = -1

            if direction == 0:
                continue

            signals.append(Signal(
                idx=i, direction=direction, entry_price=c[i],
                metadata={
                    "rsi": float(rsi_vals[i]),
                    "kcr": float(kcr[i]),
                    "bb_pos": "lower" if direction == 1 else "upper",
                },
            ))
            last_idx = i

        return signals


if __name__ == "__main__":
    strategy = SqueezeMeanReversion()

    configs = [
        ("bb+rsi30/70", {"bb_touch": True, "rsi_oversold": 30, "rsi_overbought": 70}),
        ("bb+rsi25/75", {"bb_touch": True, "rsi_oversold": 25, "rsi_overbought": 75}),
        ("bb+rsi35/65", {"bb_touch": True, "rsi_oversold": 35, "rsi_overbought": 65}),
        ("rsi30/70 only", {"bb_touch": False, "rsi_oversold": 30, "rsi_overbought": 70}),
        ("rsi25/75 only", {"bb_touch": False, "rsi_oversold": 25, "rsi_overbought": 75}),
        ("sq<0.7 bb+rsi30", {"bb_touch": True, "sq_threshold": 0.7, "rsi_oversold": 30, "rsi_overbought": 70}),
        ("sq<0.9 bb+rsi30", {"bb_touch": True, "sq_threshold": 0.9, "rsi_oversold": 30, "rsi_overbought": 70}),
        ("sq<0.9 rsi35/65", {"bb_touch": False, "sq_threshold": 0.9, "rsi_oversold": 35, "rsi_overbought": 65}),
    ]

    all_results = []
    for label, params in configs:
        for asset in ["BTC", "ETH"]:
            for tf in ["15m", "1h"]:
                for hold in [2, 3, 4]:
                    r = strategy.backtest(asset, tf, hold=hold, **params)
                    if r and r.trades >= 30:
                        r.strategy_name = f"HY01 {label} h={hold}"
                        all_results.append(r)

    all_results.sort(key=lambda r: r.accuracy, reverse=True)
    print(f"\n{'=' * 130}")
    print(f"  HY01 SQUEEZE MEAN REVERSION — TOP 25")
    print(f"{'=' * 130}")
    for r in all_results[:25]:
        r.print_summary()
    if all_results:
        all_results[0].print_yearly()
