#!/usr/bin/env python3
"""
Backtest Finale - Configurazione Ottimale
==========================================
La configurazione che batte Buy & Hold con fee reali.
"""

import pandas as pd
import numpy as np
from datetime import datetime
from pathlib import Path
import json
from dataclasses import dataclass, field
from typing import List, Optional, Tuple
import warnings
warnings.filterwarnings('ignore')

# Fee Kraken realistiche
KRAKEN_MAKER_FEE = 0.0016
SPREAD_PCT = 0.0003
SLIPPAGE_BASE = 0.0001

# CONFIGURAZIONE VINCENTE
WINNING_CONFIG = {
    'dip_threshold': 0.08,   # Buy after 8% dip
    'profit_target': 0.15,   # 15% take profit
    'stop_loss': 0.10,       # 10% stop loss
    'position_size': 1.0,    # 100% capital
}

# Configurazione diversificata per 3 trader
TRADER_CONFIGS = {
    'Alpha': {
        'dip_threshold': 0.08,
        'profit_target': 0.15,
        'stop_loss': 0.10,
        'position_size': 1.0,
        'capital': 1667.0,
    },
    'Beta': {
        'dip_threshold': 0.10,
        'profit_target': 0.15,
        'stop_loss': 0.10,
        'position_size': 1.0,
        'capital': 1667.0,
    },
    'Gamma': {
        'dip_threshold': 0.10,
        'profit_target': 0.10,
        'stop_loss': 0.08,
        'position_size': 1.0,
        'capital': 1666.0,
    },
}


@dataclass
class Trade:
    entry_time: datetime
    entry_price: float
    amount: float
    fee_entry: float
    exit_time: Optional[datetime] = None
    exit_price: Optional[float] = None
    fee_exit: float = 0
    exit_reason: Optional[str] = None
    gross_pnl: float = 0
    net_pnl: float = 0
    pnl_pct: float = 0
    duration_hours: float = 0
    dip_at_entry: float = 0
    capital_before: float = 0
    capital_after: float = 0


def load_data() -> pd.DataFrame:
    DATA_DIR = Path('/var/www/html/pippo.cuttalo.com/data')
    dfs = []
    for f in sorted(DATA_DIR.glob('prices_BTC_EUR_*.csv')):
        df = pd.read_csv(f)
        if df['timestamp'].dtype == 'int64' or str(df['timestamp'].iloc[0]).isdigit():
            ts = pd.to_numeric(df['timestamp'])
            df['timestamp'] = pd.to_datetime(ts, unit='s' if ts.iloc[0] < 1e12 else 'ms')
        else:
            df['timestamp'] = pd.to_datetime(df['timestamp'])
        dfs.append(df)
    prices = pd.concat(dfs, ignore_index=True)
    prices = prices.sort_values('timestamp').drop_duplicates(subset='timestamp').reset_index(drop=True)
    prices['high_24h'] = prices['high'].rolling(window=1440, min_periods=1).max()
    return prices


def run_detailed_backtest(prices: pd.DataFrame, name: str, config: dict) -> Tuple[List[Trade], float, List[Tuple]]:
    """Backtest dettagliato con tracking completo."""

    capital = config['capital']
    initial_capital = capital
    position = 0
    entry_price = 0
    position_amount = 0
    fee_entry = 0
    entry_time = None
    dip_at_entry = 0

    trades = []
    equity_curve = []

    close = prices['close'].values
    high = prices['high'].values
    low = prices['low'].values
    high_24h = prices['high_24h'].values
    timestamps = prices['timestamp'].values

    start_idx = 1440

    for i in range(start_idx, len(prices)):
        ts = timestamps[i]
        current_price = close[i]
        candle_high = high[i]
        candle_low = low[i]
        max_24h = high_24h[i]

        dip_from_high = (current_price - max_24h) / max_24h if max_24h > 0 else 0

        if position == 1:
            # Check stop loss
            stop_price = entry_price * (1 - config['stop_loss'])
            if candle_low <= stop_price:
                exec_price = stop_price * (1 - SPREAD_PCT - SLIPPAGE_BASE)
                gross_pnl = position_amount * (exec_price - entry_price)
                fee_exit = position_amount * exec_price * KRAKEN_MAKER_FEE
                net_pnl = gross_pnl - fee_exit - fee_entry

                capital_before = capital
                capital += position_amount * entry_price + net_pnl

                trades.append(Trade(
                    entry_time=entry_time,
                    entry_price=entry_price,
                    amount=position_amount,
                    fee_entry=fee_entry,
                    exit_time=pd.Timestamp(ts),
                    exit_price=exec_price,
                    fee_exit=fee_exit,
                    exit_reason='stop_loss',
                    gross_pnl=gross_pnl,
                    net_pnl=net_pnl,
                    pnl_pct=net_pnl / (position_amount * entry_price),
                    duration_hours=(pd.Timestamp(ts) - entry_time).total_seconds() / 3600,
                    dip_at_entry=dip_at_entry,
                    capital_before=capital_before,
                    capital_after=capital,
                ))
                position = 0
                continue

            # Check take profit
            target_price = entry_price * (1 + config['profit_target'])
            if candle_high >= target_price:
                exec_price = target_price * (1 - SPREAD_PCT)
                gross_pnl = position_amount * (exec_price - entry_price)
                fee_exit = position_amount * exec_price * KRAKEN_MAKER_FEE
                net_pnl = gross_pnl - fee_exit - fee_entry

                capital_before = capital
                capital += position_amount * entry_price + net_pnl

                trades.append(Trade(
                    entry_time=entry_time,
                    entry_price=entry_price,
                    amount=position_amount,
                    fee_entry=fee_entry,
                    exit_time=pd.Timestamp(ts),
                    exit_price=exec_price,
                    fee_exit=fee_exit,
                    exit_reason='take_profit',
                    gross_pnl=gross_pnl,
                    net_pnl=net_pnl,
                    pnl_pct=net_pnl / (position_amount * entry_price),
                    duration_hours=(pd.Timestamp(ts) - entry_time).total_seconds() / 3600,
                    dip_at_entry=dip_at_entry,
                    capital_before=capital_before,
                    capital_after=capital,
                ))
                position = 0
                continue

        elif position == 0:
            if dip_from_high <= -config['dip_threshold']:
                trade_value = capital * config['position_size']
                if trade_value > 10:
                    exec_price = current_price * (1 + SPREAD_PCT + SLIPPAGE_BASE)
                    fee_entry = trade_value * KRAKEN_MAKER_FEE
                    position_amount = (trade_value - fee_entry) / exec_price
                    entry_price = exec_price
                    capital -= trade_value
                    position = 1
                    entry_time = pd.Timestamp(ts)
                    dip_at_entry = dip_from_high

        # Track equity ogni ora
        if i % 60 == 0:
            if position == 1:
                unrealized = position_amount * (current_price - entry_price)
                equity = capital + position_amount * entry_price + unrealized
            else:
                equity = capital
            equity_curve.append((pd.Timestamp(ts), equity))

    # Chiudi posizione finale
    if position == 1:
        final_price = close[-1]
        gross_pnl = position_amount * (final_price - entry_price)
        fee_exit = position_amount * final_price * KRAKEN_MAKER_FEE
        net_pnl = gross_pnl - fee_exit - fee_entry

        capital_before = capital
        capital += position_amount * entry_price + net_pnl

        trades.append(Trade(
            entry_time=entry_time,
            entry_price=entry_price,
            amount=position_amount,
            fee_entry=fee_entry,
            exit_time=pd.Timestamp(timestamps[-1]),
            exit_price=final_price,
            fee_exit=fee_exit,
            exit_reason='end_of_data',
            gross_pnl=gross_pnl,
            net_pnl=net_pnl,
            pnl_pct=net_pnl / (position_amount * entry_price),
            duration_hours=(pd.Timestamp(timestamps[-1]) - entry_time).total_seconds() / 3600,
            dip_at_entry=dip_at_entry,
            capital_before=capital_before,
            capital_after=capital,
        ))

    return trades, capital, equity_curve


def print_detailed_results(name: str, config: dict, trades: List[Trade],
                          final_capital: float, equity_curve: List, buy_hold: float):
    """Stampa risultati dettagliati."""

    initial = config['capital']
    return_pct = (final_capital - initial) / initial * 100

    print(f"\n{'='*70}")
    print(f"TRADER: {name}")
    print(f"{'='*70}")
    print(f"Config: Dip {config['dip_threshold']*100:.0f}% | TP {config['profit_target']*100:.0f}% | SL {config['stop_loss']*100:.0f}% | Size {config['position_size']*100:.0f}%")

    print(f"\n--- CAPITALE ---")
    print(f"Iniziale:     €{initial:>10,.2f}")
    print(f"Finale:       €{final_capital:>10,.2f}")
    print(f"P&L:          €{final_capital - initial:>+10,.2f} ({return_pct:+.2f}%)")

    if trades:
        total_fees = sum(t.fee_entry + t.fee_exit for t in trades)
        total_gross = sum(t.gross_pnl for t in trades)
        wins = [t for t in trades if t.net_pnl > 0]
        losses = [t for t in trades if t.net_pnl <= 0]
        tp_trades = [t for t in trades if t.exit_reason == 'take_profit']
        sl_trades = [t for t in trades if t.exit_reason == 'stop_loss']

        print(f"Fee Totali:   €{total_fees:>10,.2f}")
        print(f"P&L Lordo:    €{total_gross:>10,.2f}")

        print(f"\n--- TRADE ---")
        print(f"Totali:       {len(trades):>10}")
        print(f"Take Profit:  {len(tp_trades):>10} ({len(tp_trades)/len(trades)*100:.1f}%)")
        print(f"Stop Loss:    {len(sl_trades):>10} ({len(sl_trades)/len(trades)*100:.1f}%)")
        print(f"Win Rate:     {len(wins)/len(trades)*100:>9.1f}%")

        avg_win = np.mean([t.net_pnl for t in wins]) if wins else 0
        avg_loss = np.mean([t.net_pnl for t in losses]) if losses else 0
        avg_duration = np.mean([t.duration_hours for t in trades])

        print(f"\n--- METRICHE ---")
        print(f"Avg Win:      €{avg_win:>+10,.2f}")
        print(f"Avg Loss:     €{avg_loss:>+10,.2f}")
        print(f"Avg Duration: {avg_duration:>10.1f} ore")

        if equity_curve:
            eq = [e[1] for e in equity_curve]
            peak = np.maximum.accumulate(eq)
            dd = (np.array(eq) - peak) / peak
            max_dd = abs(min(dd)) if len(dd) > 0 else 0
            print(f"Max Drawdown: {max_dd*100:>9.1f}%")

        print(f"\n--- VS BUY & HOLD ---")
        print(f"B&H Return:   {buy_hold:>+9.2f}%")
        print(f"Strategy:     {return_pct:>+9.2f}%")
        print(f"Alpha:        {return_pct - buy_hold:>+9.2f}%")

        # Dettaglio trade
        print(f"\n--- TUTTI I TRADE ---")
        print(f"{'#':>3} {'Data Entry':<12} {'Dip':>6} {'Entry':>10} {'Exit':>10} {'Reason':>12} {'P&L':>12} {'Capital':>12}")
        print("-"*85)

        for i, t in enumerate(trades, 1):
            print(f"{i:>3} {t.entry_time.strftime('%Y-%m-%d'):<12} {t.dip_at_entry*100:>+5.1f}% "
                  f"€{t.entry_price:>9,.0f} €{t.exit_price:>9,.0f} {t.exit_reason:>12} "
                  f"€{t.net_pnl:>+11,.2f} €{t.capital_after:>11,.2f}")

    return {
        'name': name,
        'config': config,
        'initial': initial,
        'final': final_capital,
        'return_pct': return_pct,
        'trades': len(trades),
        'alpha': return_pct - buy_hold,
    }


def main():
    print("="*70)
    print("BACKTEST FINALE - CONFIGURAZIONE OTTIMALE")
    print("="*70)
    print(f"\nFee Maker: {KRAKEN_MAKER_FEE*100:.2f}%")
    print(f"Spread: {SPREAD_PCT*100:.3f}%")

    prices = load_data()
    print(f"\nDati: {len(prices):,} candele")
    print(f"Periodo: {prices['timestamp'].min().date()} -> {prices['timestamp'].max().date()}")

    start_price = prices.iloc[1440]['close']
    end_price = prices.iloc[-1]['close']
    buy_hold = (end_price - start_price) / start_price * 100
    print(f"\nBuy & Hold: €{start_price:,.0f} -> €{end_price:,.0f} = {buy_hold:+.2f}%")

    results = []

    for name, config in TRADER_CONFIGS.items():
        trades, final_capital, equity_curve = run_detailed_backtest(prices.copy(), name, config)
        result = print_detailed_results(name, config, trades, final_capital, equity_curve, buy_hold)
        results.append(result)

    # Riepilogo
    print(f"\n{'='*70}")
    print("RIEPILOGO PORTFOLIO")
    print("="*70)

    total_initial = sum(r['initial'] for r in results)
    total_final = sum(r['final'] for r in results)
    total_return = (total_final - total_initial) / total_initial * 100

    print(f"\n{'Trader':<10} {'Config':^25} {'Return':>10} {'Trades':>8} {'Alpha':>10}")
    print("-"*70)
    for r in results:
        cfg = r['config']
        cfg_str = f"D{cfg['dip_threshold']*100:.0f}/TP{cfg['profit_target']*100:.0f}/SL{cfg['stop_loss']*100:.0f}"
        print(f"{r['name']:<10} {cfg_str:^25} {r['return_pct']:>+9.2f}% {r['trades']:>8} {r['alpha']:>+9.2f}%")

    print("-"*70)
    print(f"{'TOTALE':<10} {'':<25} {total_return:>+9.2f}% {sum(r['trades'] for r in results):>8} {total_return - buy_hold:>+9.2f}%")
    print(f"{'Buy&Hold':<10} {'':<25} {buy_hold:>+9.2f}%")

    print(f"\n--- CAPITALE FINALE ---")
    print(f"Iniziale: €{total_initial:,.2f}")
    print(f"Finale:   €{total_final:,.2f}")
    print(f"Profitto: €{total_final - total_initial:,.2f}")

    # Confronto visivo
    print(f"\n{'='*70}")
    print("CONCLUSIONE")
    print("="*70)

    if total_return > buy_hold:
        print(f"\n>>> STRATEGIA BATTE BUY & HOLD! <<<")
        print(f"    Alpha: {total_return - buy_hold:+.2f}%")
    else:
        print(f"\n>>> Buy & Hold vince in questo periodo")
        print(f"    Sottoperformance: {total_return - buy_hold:.2f}%")

    # Salva
    output = {
        'timestamp': datetime.now().isoformat(),
        'buy_hold': buy_hold,
        'portfolio_return': total_return,
        'portfolio_alpha': total_return - buy_hold,
        'traders': results,
    }

    output_path = Path('/var/www/html/pippo.cuttalo.com/optimization_results/final_backtest.json')
    with open(output_path, 'w') as f:
        json.dump(output, f, indent=2, default=str)

    print(f"\nSalvato: {output_path}")


if __name__ == '__main__':
    main()
