"""S2-12: Strategie SOLO su perpetual — dati 100% reali.
Niente opzioni, niente IV stimata. Solo prezzo OHLCV.
Mix di approcci diversi da quelli già testati su main.

1. Intraday range breakout con filtro volatilità
2. Daily open range breakout (prima ora di trading)
3. RSI divergence (prezzo fa nuovo min/max, RSI no)
4. Close-to-close momentum filtrato da volatilità regime
5. Multi-timeframe confirmation (15m signal + 1h trend)

Test per-anno, onesto, con fee reali perpetual (0.05% taker × 2 = 0.1% roundtrip).
"""
from __future__ import annotations
import sys
sys.path.insert(0, ".")

import numpy as np
import pandas as pd
from src.data.downloader import load_data

FEE_RT = 0.002  # 0.1% taker roundtrip
INITIAL = 1000
LEVERAGE = 3


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
        if al == 0:
            result[i + 1] = 100
        else:
            result[i + 1] = 100 - 100 / (1 + ag / al)
    return result


def ema(arr, period):
    r = np.full(len(arr), np.nan)
    k = 2 / (period + 1)
    r[period - 1] = np.mean(arr[:period])
    for i in range(period, len(arr)):
        r[i] = arr[i] * k + r[i - 1] * (1 - k)
    return r


def run_all_perpetual(asset):
    print(f"\n{'#'*70}")
    print(f"  {asset} — STRATEGIE PERPETUAL (dati reali)")
    print(f"  Fee: {FEE_RT*100}% roundtrip, Leva: {LEVERAGE}x")
    print(f"{'#'*70}")

    df_1h = load_data(asset, "1h")
    df_15m = load_data(asset, "15m")
    c1h = df_1h["close"].values
    h1h = df_1h["high"].values
    l1h = df_1h["low"].values
    v1h = df_1h["volume"].values
    n1h = len(c1h)
    ts1h = pd.to_datetime(df_1h["timestamp"], unit="ms", utc=True)

    rsi_14 = rsi(c1h, 14)
    ema_20 = ema(c1h, 20)
    ema_50 = ema(c1h, 50)

    results = {}

    # ======================================================
    # STRAT 1: Daily Open Range Breakout
    # Prima ora (08-09 UTC) definisce il range. Breakout = entrata.
    # ======================================================
    for hold, stop_m in [(6, 1.0), (12, 1.5), (4, 0.8)]:
        name = f"ORB_h{hold}_s{stop_m}"
        capital = float(INITIAL)
        yearly = {}

        for i in range(50, n1h - hold):
            if ts1h.iloc[i].hour != 9:  # fine della prima ora
                continue
            day = ts1h.iloc[i].strftime("%Y-%m-%d")
            if day in yearly and len(yearly[day]) >= 1:
                continue

            range_high = h1h[i - 1]
            range_low = l1h[i - 1]
            range_size = range_high - range_low
            if range_size <= 0:
                continue

            # ATR per stop
            atr_14 = np.mean(h1h[max(0,i-14):i] - l1h[max(0,i-14):i])
            if atr_14 <= 0:
                continue

            # Breakout detection: la candela attuale rompe il range
            if c1h[i] > range_high:
                direction = "long"
            elif c1h[i] < range_low:
                direction = "short"
            else:
                continue

            entry = c1h[i]
            stop_dist = atr_14 * stop_m
            exit_price = c1h[min(i + hold, n1h - 1)]

            for j in range(i + 1, min(i + hold + 1, n1h)):
                if direction == "long":
                    if l1h[j] <= entry - stop_dist:
                        exit_price = entry - stop_dist
                        break
                    if h1h[j] >= entry + stop_dist * 2:
                        exit_price = entry + stop_dist * 2
                        break
                else:
                    if h1h[j] >= entry + stop_dist:
                        exit_price = entry + stop_dist
                        break
                    if l1h[j] <= entry - stop_dist * 2:
                        exit_price = entry - stop_dist * 2
                        break
                exit_price = c1h[j]

            trade_ret = ((exit_price - entry) / entry if direction == "long" else (entry - exit_price) / entry)
            net = trade_ret * LEVERAGE - FEE_RT * LEVERAGE
            capital += capital * 0.15 * net
            capital = max(capital, 10)

            year = ts1h.iloc[i].year
            if year not in yearly:
                yearly[year] = []
            yearly[year].append(net > 0)
            if day not in yearly:
                yearly[day] = []

        if sum(len(v) for v in yearly.values() if isinstance(v, list) and all(isinstance(x, bool) for x in v)) > 30:
            all_wins = [w for v in yearly.values() if isinstance(v, list) for w in v if isinstance(w, bool)]
            acc = sum(all_wins) / len(all_wins) * 100
            ret = (capital - INITIAL) / INITIAL * 100
            results[name] = {"acc": acc, "ret": ret, "trades": len(all_wins), "capital": capital}

    # ======================================================
    # STRAT 2: RSI Divergence
    # Prezzo fa nuovo low, RSI no = bullish divergence → long
    # ======================================================
    for lookback, rsi_thr_low, rsi_thr_high, hold in [(20, 30, 70, 6), (14, 25, 75, 8), (10, 35, 65, 4)]:
        name = f"RSIdiv_lb{lookback}_h{hold}"
        capital = float(INITIAL)
        trades_list = []

        for i in range(max(50, lookback + 1), n1h - hold):
            day = ts1h.iloc[i].strftime("%Y-%m-%d")

            # Bullish divergence: price new low, RSI higher low
            price_new_low = c1h[i] < np.min(c1h[i - lookback : i])
            rsi_higher = rsi_14[i] > np.min(rsi_14[i - lookback : i]) and rsi_14[i] < rsi_thr_low

            # Bearish divergence: price new high, RSI lower high
            price_new_high = c1h[i] > np.max(c1h[i - lookback : i])
            rsi_lower = rsi_14[i] < np.max(rsi_14[i - lookback : i]) and rsi_14[i] > rsi_thr_high

            direction = None
            if price_new_low and rsi_higher:
                direction = "long"
            elif price_new_high and rsi_lower:
                direction = "short"

            if direction is None:
                continue

            entry = c1h[i]
            exit_price = c1h[min(i + hold, n1h - 1)]
            trade_ret = ((exit_price - entry) / entry if direction == "long" else (entry - exit_price) / entry)
            net = trade_ret * LEVERAGE - FEE_RT * LEVERAGE
            capital += capital * 0.12 * net
            capital = max(capital, 10)
            trades_list.append({"year": ts1h.iloc[i].year, "win": trade_ret > 0})

        if len(trades_list) > 30:
            acc = sum(1 for t in trades_list if t["win"]) / len(trades_list) * 100
            ret = (capital - INITIAL) / INITIAL * 100
            results[name] = {"acc": acc, "ret": ret, "trades": len(trades_list), "capital": capital}

    # ======================================================
    # STRAT 3: Momentum regime — trend following solo in low-vol regime
    # ======================================================
    for fast, slow, vol_w, vol_thr, hold in [
        (8, 21, 48, 0.8, 12),
        (5, 13, 24, 0.8, 6),
        (13, 34, 72, 0.7, 24),
        (8, 21, 48, 0.9, 8),
    ]:
        name = f"MomReg_f{fast}s{slow}_h{hold}"
        ema_f = ema(c1h, fast)
        ema_s = ema(c1h, slow)

        rv_short = np.full(n1h, np.nan)
        rv_long = np.full(n1h, np.nan)
        lr = np.diff(np.log(np.where(c1h == 0, 1e-10, c1h)))
        for idx in range(vol_w, len(lr)):
            rv_short[idx + 1] = np.std(lr[idx - min(12, vol_w) : idx])
            rv_long[idx + 1] = np.std(lr[idx - vol_w : idx])

        capital = float(INITIAL)
        trades_list = []
        daily_done = set()

        for i in range(max(60, slow + 1), n1h - hold):
            if np.isnan(ema_f[i]) or np.isnan(ema_s[i]) or np.isnan(rv_short[i]) or np.isnan(rv_long[i]):
                continue
            if rv_long[i] <= 0:
                continue

            day = ts1h.iloc[i].strftime("%Y-%m-%d")
            if day in daily_done:
                continue

            # Only trade in low-vol regime
            vol_ratio = rv_short[i] / rv_long[i]
            if vol_ratio > vol_thr:
                continue

            # EMA crossover signal
            cross_up = ema_f[i] > ema_s[i] and ema_f[i - 1] <= ema_s[i - 1]
            cross_down = ema_f[i] < ema_s[i] and ema_f[i - 1] >= ema_s[i - 1]

            if not (cross_up or cross_down):
                continue

            direction = "long" if cross_up else "short"
            entry = c1h[i]
            exit_price = c1h[min(i + hold, n1h - 1)]
            trade_ret = ((exit_price - entry) / entry if direction == "long" else (entry - exit_price) / entry)
            net = trade_ret * LEVERAGE - FEE_RT * LEVERAGE
            capital += capital * 0.15 * net
            capital = max(capital, 10)
            trades_list.append({"year": ts1h.iloc[i].year, "win": trade_ret > 0})
            daily_done.add(day)

        if len(trades_list) > 30:
            acc = sum(1 for t in trades_list if t["win"]) / len(trades_list) * 100
            ret = (capital - INITIAL) / INITIAL * 100
            results[name] = {"acc": acc, "ret": ret, "trades": len(trades_list), "capital": capital}

    # ======================================================
    # STRAT 4: Multi-TF confirmation (15m entry, 1h trend)
    # ======================================================
    c15 = df_15m["close"].values
    h15 = df_15m["high"].values
    l15 = df_15m["low"].values
    ts15 = df_15m["timestamp"].values
    n15 = len(c15)

    ema_1h_50 = ema(c1h, 50)
    rsi_15m = rsi(c15, 14)

    capital = float(INITIAL)
    trades_list = []
    daily_done = set()

    for i in range(100, n15 - 12):
        ts_dt = pd.Timestamp(ts15[i], unit="ms", tz="UTC")
        day = ts_dt.strftime("%Y-%m-%d")
        if day in daily_done:
            continue

        # 15m signal: RSI extreme
        if rsi_15m[i] > 35 and rsi_15m[i] < 65:
            continue

        # Find matching 1h candle
        h_idx = np.searchsorted(ts1h.values.astype("int64"), ts15[i]) - 1
        if h_idx < 50 or h_idx >= n1h or np.isnan(ema_1h_50[h_idx]):
            continue

        # 1h trend confirmation
        trend_up = c1h[h_idx] > ema_1h_50[h_idx]
        trend_down = c1h[h_idx] < ema_1h_50[h_idx]

        direction = None
        if rsi_15m[i] < 30 and trend_up:
            direction = "long"  # oversold in uptrend
        elif rsi_15m[i] > 70 and trend_down:
            direction = "short"  # overbought in downtrend

        if direction is None:
            continue

        entry = c15[i]
        hold_bars = 12  # 12 × 15m = 3h
        exit_price = c15[min(i + hold_bars, n15 - 1)]
        trade_ret = ((exit_price - entry) / entry if direction == "long" else (entry - exit_price) / entry)
        net = trade_ret * LEVERAGE - FEE_RT * LEVERAGE
        capital += capital * 0.12 * net
        capital = max(capital, 10)
        trades_list.append({"year": ts_dt.year, "win": trade_ret > 0})
        daily_done.add(day)

    if len(trades_list) > 30:
        acc = sum(1 for t in trades_list if t["win"]) / len(trades_list) * 100
        ret = (capital - INITIAL) / INITIAL * 100
        results["MultiTF_15m1h"] = {"acc": acc, "ret": ret, "trades": len(trades_list), "capital": capital}

    # === PRINT RESULTS ===
    print(f"\n  {'Strategy':<25s} {'Trades':>7s} {'Acc':>6s} {'Return':>10s} {'Capital':>10s}")
    print(f"  {'-'*60}")
    for name, r in sorted(results.items(), key=lambda x: x[1]["acc"], reverse=True):
        tag = "✅" if r["acc"] >= 60 and r["ret"] > 30 else ""
        print(f"  {name:<25s} {r['trades']:>7d} {r['acc']:>5.1f}% {r['ret']:>+9.1f}% €{r['capital']:>9,.0f} {tag}")


for asset in ["ETH", "BTC"]:
    run_all_perpetual(asset)
