#!/usr/bin/env python3
"""
Ottimizzazione Parametri con Fee Realistiche
=============================================
Testa molte combinazioni per trovare la migliore configurazione.
"""

import pandas as pd
import numpy as np
from datetime import datetime
from pathlib import Path
import json
from itertools import product
import warnings
warnings.filterwarnings('ignore')

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

def load_data() -> pd.DataFrame:
    """Carica dati."""
    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 fast_backtest(prices: pd.DataFrame, dip_pct: float, tp_pct: float, sl_pct: float,
                   pos_size: float, capital: float = 5000.0) -> dict:
    """Backtest veloce con parametri dati."""

    position = 0
    entry_price = 0
    position_amount = 0

    trades = []
    start_capital = capital

    # Vectorized calculations
    close = prices['close'].values
    high = prices['high'].values
    low = prices['low'].values
    high_24h = prices['high_24h'].values

    start_idx = 1440

    for i in range(start_idx, len(prices)):
        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 - sl_pct)
            if candle_low <= stop_price:
                exec_price = stop_price * (1 - SPREAD_PCT - SLIPPAGE_BASE)
                gross_pnl = position_amount * (exec_price - entry_price)
                fee = position_amount * exec_price * KRAKEN_MAKER_FEE
                net_pnl = gross_pnl - fee - fee_entry
                capital += position_amount * entry_price + net_pnl
                trades.append({'pnl': net_pnl, 'type': 'sl'})
                position = 0
                continue

            # Check take profit
            target_price = entry_price * (1 + tp_pct)
            if candle_high >= target_price:
                exec_price = target_price * (1 - SPREAD_PCT)
                gross_pnl = position_amount * (exec_price - entry_price)
                fee = position_amount * exec_price * KRAKEN_MAKER_FEE
                net_pnl = gross_pnl - fee - fee_entry
                capital += position_amount * entry_price + net_pnl
                trades.append({'pnl': net_pnl, 'type': 'tp'})
                position = 0
                continue

        elif position == 0:
            if dip_from_high <= -dip_pct:
                trade_value = capital * pos_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

    # Chiudi posizione finale
    if position == 1:
        final_price = close[-1]
        gross_pnl = position_amount * (final_price - entry_price)
        fee = position_amount * final_price * KRAKEN_MAKER_FEE
        net_pnl = gross_pnl - fee - fee_entry
        capital += position_amount * entry_price + net_pnl
        trades.append({'pnl': net_pnl, 'type': 'end'})

    if not trades:
        return None

    total_pnl = sum(t['pnl'] for t in trades)
    wins = [t for t in trades if t['pnl'] > 0]
    losses = [t for t in trades if t['pnl'] <= 0]
    win_rate = len(wins) / len(trades) if trades else 0

    return {
        'dip': dip_pct,
        'tp': tp_pct,
        'sl': sl_pct,
        'pos_size': pos_size,
        'return_pct': (capital - start_capital) / start_capital * 100,
        'num_trades': len(trades),
        'win_rate': win_rate,
        'tp_count': sum(1 for t in trades if t['type'] == 'tp'),
        'sl_count': sum(1 for t in trades if t['type'] == 'sl'),
    }


def main():
    print("="*60)
    print("OTTIMIZZAZIONE PARAMETRI CON FEE REALI")
    print("="*60)

    prices = load_data()
    print(f"Dati: {len(prices):,} candele")

    # Buy & Hold
    start_price = prices.iloc[1440]['close']
    end_price = prices.iloc[-1]['close']
    buy_hold = (end_price - start_price) / start_price * 100
    print(f"Buy & Hold: {buy_hold:+.2f}%")

    # Parametri da testare
    dip_thresholds = [0.03, 0.04, 0.05, 0.06, 0.07, 0.08, 0.10, 0.12, 0.15]
    profit_targets = [0.03, 0.04, 0.05, 0.06, 0.07, 0.08, 0.10, 0.12, 0.15, 0.20]
    stop_losses = [0.02, 0.03, 0.04, 0.05, 0.06, 0.08, 0.10]
    position_sizes = [0.3, 0.5, 0.7, 1.0]

    total_combos = len(dip_thresholds) * len(profit_targets) * len(stop_losses) * len(position_sizes)
    print(f"\nTestando {total_combos:,} combinazioni...")

    results = []
    tested = 0

    for dip, tp, sl, pos in product(dip_thresholds, profit_targets, stop_losses, position_sizes):
        # Skip configurazioni illogiche
        if tp <= sl:  # TP deve essere > SL
            continue

        result = fast_backtest(prices, dip, tp, sl, pos)

        if result and result['num_trades'] >= 10:  # Minimo 10 trade
            results.append(result)

        tested += 1
        if tested % 500 == 0:
            print(f"  Testato {tested}/{total_combos}...")

    # Ordina per return
    results.sort(key=lambda x: x['return_pct'], reverse=True)

    print(f"\n{'='*60}")
    print("TOP 20 CONFIGURAZIONI")
    print("="*60)

    print(f"\n{'Dip':>6} {'TP':>6} {'SL':>6} {'Size':>6} {'Return':>10} {'Trades':>8} {'Win%':>8} {'TP#':>6} {'SL#':>6}")
    print("-"*70)

    for r in results[:20]:
        print(f"{r['dip']*100:>5.0f}% {r['tp']*100:>5.0f}% {r['sl']*100:>5.0f}% "
              f"{r['pos_size']*100:>5.0f}% {r['return_pct']:>+9.2f}% {r['num_trades']:>8} "
              f"{r['win_rate']*100:>7.1f}% {r['tp_count']:>6} {r['sl_count']:>6}")

    print("-"*70)
    print(f"Buy & Hold: {buy_hold:+.2f}%")

    # Analisi
    if results:
        best = results[0]

        print(f"\n{'='*60}")
        print("MIGLIORE CONFIGURAZIONE")
        print("="*60)
        print(f"Dip threshold: {best['dip']*100:.0f}%")
        print(f"Take profit: {best['tp']*100:.0f}%")
        print(f"Stop loss: {best['sl']*100:.0f}%")
        print(f"Position size: {best['pos_size']*100:.0f}%")
        print(f"\nReturn: {best['return_pct']:+.2f}%")
        print(f"Trades: {best['num_trades']}")
        print(f"Win rate: {best['win_rate']*100:.1f}%")
        print(f"Alpha vs B&H: {best['return_pct'] - buy_hold:+.2f}%")

        # Trova configurazioni che battono B&H
        beating_bh = [r for r in results if r['return_pct'] > buy_hold]

        if beating_bh:
            print(f"\n{'='*60}")
            print(f"CONFIGURAZIONI CHE BATTONO BUY & HOLD ({len(beating_bh)})")
            print("="*60)
            for r in beating_bh[:10]:
                print(f"  Dip {r['dip']*100:.0f}% | TP {r['tp']*100:.0f}% | SL {r['sl']*100:.0f}% | "
                      f"Size {r['pos_size']*100:.0f}% -> {r['return_pct']:+.2f}%")
        else:
            print(f"\n>>> NESSUNA configurazione batte Buy & Hold in questo bull market!")
            print(">>> Il Dip Buyer funziona meglio in mercati laterali/ribassisti.")

    # Analisi per tipo di mercato
    print(f"\n{'='*60}")
    print("ANALISI PER PERIODO")
    print("="*60)

    # Split 2024 vs 2025
    prices_2024 = prices[prices['timestamp'] < '2025-01-01'].copy()
    prices_2025 = prices[prices['timestamp'] >= '2025-01-01'].copy()

    if len(prices_2024) > 2000:
        prices_2024['high_24h'] = prices_2024['high'].rolling(window=1440, min_periods=1).max()
        result_2024 = fast_backtest(prices_2024, 0.07, 0.05, 0.03, 0.5)

        bh_2024_start = prices_2024.iloc[1440]['close']
        bh_2024_end = prices_2024.iloc[-1]['close']
        bh_2024 = (bh_2024_end - bh_2024_start) / bh_2024_start * 100

        print(f"\n2024:")
        print(f"  Buy & Hold: {bh_2024:+.2f}%")
        if result_2024:
            print(f"  Dip Buyer (7%/5%/3%): {result_2024['return_pct']:+.2f}%")
            print(f"  Trades: {result_2024['num_trades']}, Win: {result_2024['win_rate']*100:.1f}%")

    if len(prices_2025) > 2000:
        prices_2025['high_24h'] = prices_2025['high'].rolling(window=1440, min_periods=1).max()
        result_2025 = fast_backtest(prices_2025, 0.07, 0.05, 0.03, 0.5)

        bh_2025_start = prices_2025.iloc[1440]['close']
        bh_2025_end = prices_2025.iloc[-1]['close']
        bh_2025 = (bh_2025_end - bh_2025_start) / bh_2025_start * 100

        print(f"\n2025:")
        print(f"  Buy & Hold: {bh_2025:+.2f}%")
        if result_2025:
            print(f"  Dip Buyer (7%/5%/3%): {result_2025['return_pct']:+.2f}%")
            print(f"  Trades: {result_2025['num_trades']}, Win: {result_2025['win_rate']*100:.1f}%")

    # Salva risultati
    output = {
        'timestamp': datetime.now().isoformat(),
        'buy_hold': buy_hold,
        'best_config': results[0] if results else None,
        'top_20': results[:20],
        'beating_buy_hold': len([r for r in results if r['return_pct'] > buy_hold]),
    }

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

    print(f"\nRisultati salvati in: {output_path}")


if __name__ == '__main__':
    main()
