"""VO01 — Volume Spike Reversal.

Quando il volume esplode (>3× media) con un forte move direzionale,
il mercato è in eccesso → fade il move (mean reversion).

Diverso dallo squeeze: non cerca compressione, cerca ECCESSO.
Il volume spike indica panico/euforia → reversal probabile.

IN:
    - OHLCV DataFrame
    - Parametri: vol_mult (3), move_threshold (0.005), hold

OUT:
    - Signal: fade la direzione del volume spike
    - BacktestResult

Logica:
    1. Volume > vol_mult × media 20 periodi
    2. Move nella candela > move_threshold (0.5%)
    3. Direzione: opposta al move (mean reversion)
    4. Filtro: non entrare se già in trend forte (EMA slope)
"""
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


class VolumeSpikeReversal(Strategy):
    name = "VO01_vol_spike_reversal"
    description = "Volume spike reversal — fade eccessi di volume/prezzo"
    default_assets = ["BTC", "ETH"]
    default_timeframes = ["15m", "1h"]
    fee_rt = 0.002

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

        vol_mult = params.get("vol_mult", 3.0)
        move_thr = params.get("move_threshold", 0.005)
        use_trend_filter = params.get("trend_filter", False)
        cooldown = params.get("cooldown", 4)

        # Volume media rolling
        vol_ma = np.full(n, np.nan)
        for i in range(20, n):
            vol_ma[i] = np.mean(v[i - 20:i])

        # EMA per trend filter
        ema_20 = np.full(n, np.nan)
        k = 2 / 21
        ema_20[19] = np.mean(c[:20])
        for i in range(20, n):
            ema_20[i] = c[i] * k + ema_20[i - 1] * (1 - k)

        signals = []
        last_idx = -cooldown

        for i in range(21, n):
            if i - last_idx < cooldown:
                continue
            if np.isnan(vol_ma[i]):
                continue

            # Volume spike
            if v[i] < vol_ma[i] * vol_mult:
                continue

            # Price move
            move = (c[i] - o[i]) / o[i] if o[i] > 0 else 0
            if abs(move) < move_thr:
                continue

            # Fade: opposto al move
            direction = -1 if move > 0 else 1

            # Trend filter: non fare mean reversion contro trend forte
            if use_trend_filter and not np.isnan(ema_20[i]):
                ema_slope = (ema_20[i] - ema_20[max(0, i - 5)]) / ema_20[max(0, i - 5)]
                if direction == -1 and ema_slope > 0.005:
                    continue
                if direction == 1 and ema_slope < -0.005:
                    continue

            signals.append(Signal(
                idx=i, direction=direction, entry_price=c[i],
                metadata={"vol_ratio": float(v[i] / vol_ma[i]), "move_pct": round(move * 100, 3)},
            ))
            last_idx = i

        return signals


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

    configs = [
        ("v3x m0.5%", {"vol_mult": 3.0, "move_threshold": 0.005}),
        ("v3x m1%", {"vol_mult": 3.0, "move_threshold": 0.01}),
        ("v4x m0.5%", {"vol_mult": 4.0, "move_threshold": 0.005}),
        ("v4x m1%", {"vol_mult": 4.0, "move_threshold": 0.01}),
        ("v3x m0.5%+tf", {"vol_mult": 3.0, "move_threshold": 0.005, "trend_filter": True}),
        ("v3x m1%+tf", {"vol_mult": 3.0, "move_threshold": 0.01, "trend_filter": True}),
        ("v5x m1%", {"vol_mult": 5.0, "move_threshold": 0.01}),
        ("v5x m1%+tf", {"vol_mult": 5.0, "move_threshold": 0.01, "trend_filter": True}),
    ]

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

    all_results.sort(key=lambda r: r.accuracy, reverse=True)
    print(f"\n{'=' * 120}")
    print(f"  VO01 VOLUME SPIKE REVERSAL — TOP 20")
    print(f"{'=' * 120}")
    for r in all_results[:20]:
        r.print_summary()
    if all_results:
        all_results[0].print_yearly()
