"""S2-03: Volatility Selling — Straddle/Strangle corto simulato.
La IV crypto è cronicamente sopra la realized vol → vendere premium è profittevole.
Simulazione: vendi straddle ATM → profitto = max(0, premium - |move|).
Premium stimato da IV storica. Ingresso giornaliero.
"""
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: np.ndarray, window: int = 24) -> np.ndarray:
    """Annualized realized volatility rolling."""
    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)):
        rv = np.std(log_ret[i - window : i]) * np.sqrt(24 * 365)
        result[i + 1] = rv
    return result


def implied_vol_proxy(close: np.ndarray, window: int = 48) -> np.ndarray:
    """IV proxy: realized vol * premium factor.
    Storicamente IV crypto ≈ 1.2-1.5x realized vol (variance risk premium).
    """
    rv = realized_vol(close, window)
    # Premium factor varia: alto in panic, basso in calma
    result = np.full(len(close), 0.5)
    for i in range(window, len(close)):
        short_rv = realized_vol(close[max(0, i-12):i+1], min(12, i))[-1] if i >= 12 else rv[i]
        if rv[i] > 0:
            regime = short_rv / rv[i]
            premium = 1.15 + 0.3 * max(0, regime - 1)  # più alto in regime volatile
        else:
            premium = 1.2
        result[i] = rv[i] * premium
    return result


def bs_straddle_price(spot: float, iv: float, dte_hours: float) -> float:
    """Black-Scholes straddle price (call + put ATM)."""
    if dte_hours <= 0 or iv <= 0:
        return 0
    t = dte_hours / (24 * 365)
    d1 = (0.5 * iv * iv * t) / (iv * np.sqrt(t))
    call = spot * (2 * norm.cdf(d1) - 1)
    return call * 2  # straddle = 2 * ATM call (approx for ATM)


def run_vol_selling(asset):
    print(f"\n{'#'*60}")
    print(f"  {asset} — VOLATILITY SELLING (SHORT STRADDLE)")
    print(f"{'#'*60}")

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

    rv = realized_vol(close, 24)
    iv_proxy = implied_vol_proxy(close)

    configs = [
        # (dte_hours, iv_floor, iv_rv_ratio_min, position_pct, name)
        (24, 0.3, 1.15, 0.1, "daily_24h"),
        (12, 0.3, 1.15, 0.08, "half_day_12h"),
        (48, 0.3, 1.10, 0.12, "2day_48h"),
        (24, 0.4, 1.20, 0.1, "daily_highIV"),
        (8, 0.25, 1.10, 0.06, "ultra_short_8h"),
        (24, 0.3, 1.30, 0.15, "daily_bigPremium"),
    ]

    for dte, iv_floor, ratio_min, pos_pct, name in configs:
        capital = float(INITIAL)
        correct = 0
        total = 0
        daily_trades = {}

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

            hour = timestamps[i].dt.hour if hasattr(timestamps[i], 'dt') else timestamps.iloc[i].hour
            if hour != 8:  # entrata alle 08 UTC ogni giorno
                continue

            current_iv = iv_proxy[i]
            current_rv = rv[i]

            if current_iv < iv_floor:
                continue
            if current_rv > 0 and current_iv / current_rv < ratio_min:
                continue

            spot = close[i]
            premium = bs_straddle_price(spot, current_iv, dte)
            premium_pct = premium / spot

            # Actual move during holding period
            exit_idx = min(i + dte, n - 1)
            actual_move = abs(close[exit_idx] - spot)
            actual_move_pct = actual_move / spot

            # P&L: premium received - actual move (capped at max loss)
            max_loss = spot * 0.05  # cap loss at 5% of spot
            pnl = premium - min(actual_move, max_loss + premium)

            pnl_on_capital = pnl / spot * pos_pct
            fee_cost = FEE * 4 * pos_pct  # 4 legs: sell call, sell put, buy back
            net_pnl = pnl_on_capital - fee_cost

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

            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 >= 60 and ann >= 30 else ""
        print(f"  {name:20s}: trades={total:4d} acc={acc:.1f}% ret={ret:+.1f}% ann={ann:+.1f}% €/day={dpnl:.2f} active={days_active} {tag}")


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