"""SQ03 — Squeeze con filtri selezionabili.

Ogni filtro è opzionale e attivabile via parametro. Di default attiva solo
antifake + long_squeeze (i due filtri con miglior rapporto accuracy/trade).
Esegue tutte le combinazioni utili e classifica.

Filtri disponibili:
    - antifake:  scarta breakout con retrace >60% (guadagna ~+1% acc)
    - long_sq:   solo squeeze durata ≥10 barre (+1% acc, dimezza trade)
    - timing:    solo ore 4-16 UTC (+0.5% acc)
    - cross:     asset secondario in squeeze nelle ultime 10 barre (+0.5%)
    - vol:       volume al breakout >1.3× media squeeze (+1% acc)

IN:
    - OHLCV DataFrame (primario + secondario per cross-check)
    - Parametri: filters (lista), bb_window, sq_threshold

OUT:
    - BacktestResult per ogni preset di filtri

Risultati tipici (BTC 15m):
    antifake+long:    77.3% acc, 2179 trades
    antifake+vol:     79.7% acc, 1250 trades — SOLIDO
    ALL_FILTERS:      79.2% acc,  696 trades (restrittivo)
"""
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, BacktestResult, YearlyStats, TF_MINUTES
from src.strategies.indicators import keltner_ratio, detect_squeezes
from src.data.downloader import load_data


PRESETS = {
    "antifake":           ["antifake"],
    "long_sq":            ["long_sq"],
    "antifake+long":      ["antifake", "long_sq"],
    "antifake+vol":       ["antifake", "vol"],
    "antifake+timing":    ["antifake", "timing"],
    "long+timing":        ["long_sq", "timing"],
    "antifake+long+time": ["antifake", "long_sq", "timing"],
    "antifake+cross":     ["antifake", "cross"],
    "ALL_FILTERS":        ["antifake", "long_sq", "timing", "cross"],
}


class SqueezeFiltered(Strategy):
    name = "SQ03_filtered"
    description = "Squeeze + filtri selezionabili (antifake, long, timing, cross, vol)"
    default_assets = ["BTC", "ETH"]
    default_timeframes = ["15m", "1h"]

    def generate_signals(self, df: pd.DataFrame, ts: pd.DatetimeIndex,
                         **params) -> list[Signal]:
        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)
        filters = params.get("filters", ["antifake", "long_sq"])
        asset = params.get("asset", "BTC")
        tf = params.get("tf", "15m")

        kcr = keltner_ratio(c, h, l, bb_w)
        events = detect_squeezes(c, h, l, kcr, sq_thr)

        kcr2 = None
        ts2 = None
        if "cross" in filters:
            secondary = "ETH" if asset == "BTC" else "BTC"
            df2 = load_data(secondary, tf)
            kcr2 = keltner_ratio(df2["close"].values, df2["high"].values,
                                 df2["low"].values, bb_w)
            ts2 = df2["timestamp"].values

        signals = []
        for ev in events:
            i = ev["idx"]
            if i < 1 or i >= n:
                continue
            first_ret = (c[i] - c[i - 1]) / c[i - 1] if c[i - 1] > 0 else 0
            if abs(first_ret) < 0.001:
                continue

            skip = False

            if "antifake" in filters:
                br = h[i] - l[i]
                if br > 0:
                    if c[i] > c[i - 1] and (h[i] - c[i]) / br > 0.6:
                        skip = True
                    elif c[i] <= c[i - 1] and (c[i] - l[i]) / br > 0.6:
                        skip = True

            if not skip and "long_sq" in filters:
                if ev["dur"] < 10:
                    skip = True

            if not skip and "timing" in filters:
                hour = ts.iloc[i].hour
                if hour < 4 or hour > 16:
                    skip = True

            if not skip and "vol" in filters:
                avg_v = np.mean(v[ev["sq_start"]:i])
                if avg_v > 0 and v[i] <= avg_v * 1.3:
                    skip = True

            if not skip and "cross" in filters and kcr2 is not None and ts2 is not None:
                i2 = np.searchsorted(ts2, ts.values[i].astype("int64") // 10**6)
                i2 = min(i2, len(kcr2) - 1)
                cross_ok = any(
                    not np.isnan(kcr2[j]) and kcr2[j] < 0.85
                    for j in range(max(0, i2 - 10), i2 + 1)
                )
                if not cross_ok:
                    skip = True

            if skip:
                continue

            signals.append(Signal(
                idx=i,
                direction=1 if first_ret > 0 else -1,
                entry_price=c[i - 1],
                metadata={"dur": ev["dur"], "filters": filters},
            ))
        return signals

    def report_all_presets(self, assets=None, timeframes=None, hold=3):
        """Esegue tutti i preset di filtri × asset × tf."""
        assets = assets or self.default_assets
        timeframes = timeframes or self.default_timeframes
        all_results = []

        for preset_name, filter_list in PRESETS.items():
            for asset in assets:
                for tf in timeframes:
                    r = self.backtest(asset, tf, hold, filters=filter_list)
                    if r and r.trades >= 20:
                        r.strategy_name = f"SQ03 {preset_name}"
                        all_results.append(r)

        all_results.sort(key=lambda r: r.accuracy, reverse=True)

        print(f"\n{'=' * 120}")
        print(f"  SQ03 SQUEEZE FILTRATO — TUTTI I PRESET ({len(all_results)} config)")
        print(f"  Fee: {self.fee_rt*100:.1f}% RT | Leva: {self.leverage:.0f}x | Pos: {self.position_size*100:.0f}%")
        print(f"{'=' * 120}")
        print(f"  {'Nome':<30s} {'A/T':>7s} {'Trades':>6s} {'Acc':>6s} "
              f"{'PnL€':>10s} {'DD%':>6s} {'€/day':>7s} "
              f"{'Mkt%':>5s} {'Dur':>5s} {'Worst':>12s} {'Anni':>4s}")
        print(f"  {'─' * 110}")

        for r in all_results:
            r.print_summary()

        if all_results:
            print(f"\n  MIGLIORE: ", end="")
            best = all_results[0]
            best.print_yearly()

        return all_results


if __name__ == "__main__":
    strategy = SqueezeFiltered()
    strategy.report_all_presets()
