"""S2-07: Variance Risk Premium harvesting — versione raffinata.
Ottimizzazione del vol selling con:
1. IV/RV ratio dinamico per entry timing
2. Tail risk cutoff (chiudi se move > N sigma)
3. Position sizing proporzionale al premium
4. Combinazione con directional bias (da gap fade)
5. Multi-asset portfolio (ETH + BTC)
"""
from __future__ import annotations
import sys
sys.path.insert(0, ".")

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

FEE = 0.001
INITIAL = 1000


def realized_vol(close, window=24):
    log_ret = np.diff(np.log(np.where(close == 0, 1e-10, close)))
    result = np.full(len(close), 0.5)
    for i in range(window, len(log_ret)):
        result[i + 1] = np.std(log_ret[i - window : i]) * np.sqrt(24 * 365)
    return result


def run_vrp(asset):
    print(f"\n{'#'*60}")
    print(f"  {asset} 1h — VARIANCE RISK PREMIUM REFINED")
    print(f"{'#'*60}")

    df = load_data(asset, "1h")
    close = df["close"].values
    high = df["high"].values
    low = df["low"].values
    n = len(close)
    split = int(n * 0.7)
    timestamps = pd.to_datetime(df["timestamp"], unit="ms", utc=True)

    rv_24 = realized_vol(close, 24)
    rv_48 = realized_vol(close, 48)
    rv_168 = realized_vol(close, 168)

    configs = [
        # (dte_h, iv_mult, cutoff_sigma, pos_base, entry_hour, dynamic_sizing, name)
        (24, 1.20, 2.5, 0.10, 8, False, "24h_base"),
        (24, 1.25, 2.5, 0.12, 8, False, "24h_highPrem"),
        (24, 1.20, 2.0, 0.10, 8, False, "24h_tightCut"),
        (24, 1.20, 3.0, 0.12, 8, False, "24h_wideCut"),
        (48, 1.20, 2.5, 0.12, 8, False, "48h_base"),
        (48, 1.25, 2.5, 0.15, 8, False, "48h_highPrem"),
        (48, 1.30, 2.5, 0.15, 8, False, "48h_vhighPrem"),
        (48, 1.20, 3.0, 0.15, 8, False, "48h_wideCut"),
        (24, 1.20, 2.5, 0.10, 8, True, "24h_dynSize"),
        (48, 1.20, 2.5, 0.12, 8, True, "48h_dynSize"),
        (24, 1.20, 2.5, 0.10, 0, False, "24h_midnight"),
        (24, 1.20, 2.5, 0.10, 16, False, "24h_afternoon"),
        (36, 1.22, 2.5, 0.12, 8, False, "36h_medium"),
        (24, 1.15, 2.5, 0.08, 8, False, "24h_lowPrem_safe"),
        (48, 1.20, 2.0, 0.10, 8, True, "48h_tight_dyn"),
    ]

    for dte, iv_mult, cutoff, pos_base, entry_h, dyn_size, name in configs:
        capital = float(INITIAL)
        correct = 0
        total = 0
        daily_trades = {}
        peak_capital = capital
        max_dd = 0

        for i in range(max(split, 170), n - dte):
            day = timestamps.iloc[i].strftime("%Y-%m-%d")
            if daily_trades.get(day, 0) >= 1:
                continue

            if timestamps.iloc[i].hour != entry_h:
                continue

            rv_s = rv_24[i]
            rv_l = rv_168[i]
            if rv_s <= 0.05 or rv_l <= 0.05:
                continue

            iv_est = rv_l * iv_mult
            vrp = iv_est - rv_s

            if vrp <= 0:
                continue

            spot = close[i]
            t = dte / (24 * 365)
            daily_std = rv_s / np.sqrt(365)

            # Premium = IV * sqrt(t) * spot * factor
            premium = iv_est * np.sqrt(t) * spot * 0.4
            premium_pct = premium / spot

            # Expected move based on IV
            expected_move = iv_est * np.sqrt(t) * spot

            # Cutoff: close if actual move > cutoff * expected_move
            max_allowed_move = expected_move * cutoff

            # Dynamic sizing: more when VRP is high
            if dyn_size:
                vrp_ratio = vrp / rv_s
                pos_pct = min(pos_base * (1 + vrp_ratio), pos_base * 2)
            else:
                pos_pct = pos_base

            # Check actual path
            exit_idx = min(i + dte, n - 1)
            actual_move = abs(close[exit_idx] - spot)

            # Early exit: check if intra-period move exceeds cutoff
            breached = False
            for j in range(i + 1, exit_idx + 1):
                intra_move = abs(close[j] - spot)
                if intra_move > max_allowed_move:
                    breached = True
                    exit_idx = j
                    actual_move = intra_move
                    break

            if breached:
                loss = min(actual_move / spot, 0.05) * pos_pct
                pnl = -loss
            else:
                profit = premium_pct * pos_pct
                partial_loss = max(0, actual_move / spot - premium_pct) * pos_pct * 0.5
                pnl = profit - partial_loss

            fee_cost = FEE * 2 * pos_pct
            net = pnl - fee_cost

            capital += capital * net
            capital = max(capital, 0)

            if capital > peak_capital:
                peak_capital = capital
            dd = (peak_capital - capital) / peak_capital if peak_capital > 0 else 0
            max_dd = max(max_dd, dd)

            total += 1
            if pnl > 0:
                correct += 1
            daily_trades[day] = daily_trades.get(day, 0) + 1

        if total < 20:
            continue

        acc = correct / total * 100
        ret = (capital - INITIAL) / INITIAL * 100
        test_days = (n - split) / 24
        test_years = test_days / 365.25
        ann = ((capital / INITIAL) ** (1 / test_years) - 1) * 100 if test_years > 0 and capital > 0 else -100
        dpnl = (capital - INITIAL) / test_days if test_days > 0 else 0
        days_active = len(daily_trades)

        tag = "✅✅" if acc >= 70 and ann >= 50 else "✅" if acc >= 65 and ann >= 30 else ""
        print(f"  {name:22s}: trades={total:4d} acc={acc:.1f}% ret={ret:+.1f}% ann={ann:+.1f}% dd={max_dd*100:.1f}% €/day={dpnl:.2f} active={days_active} {tag}")

    return daily_trades


# Run both assets
results = {}
for asset in ["ETH", "BTC"]:
    results[asset] = run_vrp(asset)

# Multi-asset portfolio simulation
print(f"\n{'#'*60}")
print(f"  MULTI-ASSET PORTFOLIO: ETH + BTC")
print(f"{'#'*60}")

df_eth = load_data("ETH", "1h")
df_btc = load_data("BTC", "1h")
close_eth = df_eth["close"].values
close_btc = df_btc["close"].values
n = min(len(close_eth), len(close_btc))
split = int(n * 0.7)
ts = pd.to_datetime(df_eth["timestamp"].values[:n], unit="ms", utc=True)

rv_eth = realized_vol(close_eth[:n], 168)
rv_btc = realized_vol(close_btc[:n], 168)

capital = float(INITIAL)
total = 0
correct = 0
peak = capital
max_dd = 0
daily_trades = {}

for i in range(max(split, 170), n - 48):
    day = ts[i].strftime("%Y-%m-%d")
    if daily_trades.get(day, 0) >= 1:
        continue
    if ts[i].hour != 8:
        continue

    for asset_close, rv_arr, name in [(close_eth[:n], rv_eth, "ETH"), (close_btc[:n], rv_btc, "BTC")]:
        rv = rv_arr[i]
        if rv <= 0.05:
            continue
        iv = rv * 1.22
        spot = asset_close[i]
        t = 48 / (24 * 365)
        premium_pct = iv * np.sqrt(t) * 0.4
        expected_move = iv * np.sqrt(t) * spot
        max_move = expected_move * 2.5

        exit_idx = min(i + 48, n - 1)
        actual_move = abs(asset_close[exit_idx] - spot)

        breached = False
        for j in range(i + 1, exit_idx + 1):
            if abs(asset_close[j] - spot) > max_move:
                breached = True
                actual_move = abs(asset_close[j] - spot)
                break

        pos_pct = 0.07  # 7% per asset = 14% total
        if breached:
            pnl = -min(actual_move / spot, 0.05) * pos_pct
        else:
            profit = premium_pct * pos_pct
            partial = max(0, actual_move / spot - premium_pct) * pos_pct * 0.5
            pnl = profit - partial

        capital += capital * (pnl - FEE * 2 * pos_pct)
        capital = max(capital, 0)
        total += 1
        if pnl > 0:
            correct += 1

    if capital > peak:
        peak = capital
    dd = (peak - capital) / peak if peak > 0 else 0
    max_dd = max(max_dd, dd)
    daily_trades[day] = daily_trades.get(day, 0) + 1

if total > 0:
    acc = correct / total * 100
    ret = (capital - INITIAL) / INITIAL * 100
    test_days = (n - split) / 24
    test_years = test_days / 365.25
    ann = ((capital / INITIAL) ** (1 / test_years) - 1) * 100 if test_years > 0 and capital > 0 else -100
    dpnl = (capital - INITIAL) / test_days if test_days > 0 else 0
    print(f"\n  ETH+BTC 48h portfolio: trades={total:4d} acc={acc:.1f}% ret={ret:+.1f}% ann={ann:+.1f}% dd={max_dd*100:.1f}% €/day={dpnl:.2f} active={len(daily_trades)}")
