#!/usr/bin/env python3
"""
OPTIMISATION V10 TRAILING STOP - GRID SEARCH COMPLÈTE

Assouplit les paramètres V10 pour générer plus de trades
et teste différents trailing stop % pour chaque combo
"""
import pandas as pd
import numpy as np
import json
import logging
from datetime import datetime
from v10_range_strategy import V10RangeStrategy
from backtest_adaptive import AdaptiveBacktest
from backtest_v10_range import V10StrategyAdapter, Signal

logging.basicConfig(level=logging.ERROR)
logger = logging.getLogger(__name__)


class V10TrailingFullOptimizer:
    """Grid search complète V10 avec trailing stop"""

    def __init__(self, df: pd.DataFrame, initial_capital: float = 500.0):
        self.df = df
        self.initial_capital = initial_capital
        self.results = []

    def generate_grid_configs(self):
        """Génère grille de configs assouplies pour plus de trades"""

        grids = {
            # RSI: Assouplir pour plus de signaux
            'rsi_oversold': [25, 28, 30, 32, 35],
            'rsi_overbought': [65, 68, 70, 72, 75],

            # ATR: Varier pour accepter plus de volatilité
            'range_atr_threshold': [0.25, 0.30, 0.35, 0.40, 0.45],

            # Confidence: Assouplir pour plus de signaux
            'min_confidence': [65, 70, 75, 80],

            # SL: Garder serré
            'sl_atr_mult': [0.8, 1.0, 1.2],

            # Trailing stop %: Large gamme
            'trailing_pct': [0.10, 0.12, 0.15, 0.18, 0.20, 0.25, 0.30],

            # Leverage
            'leverage': [125, 150, 175, 200],
        }

        configs = []

        # Stratégie intelligente: combos qui génèrent plus de trades
        # 1. RSI assouppli + ATR plus tolérant
        for rsi_os in grids['rsi_oversold']:
            for rsi_ob in grids['rsi_overbought']:
                if rsi_ob - rsi_os < 30:  # Éviter overlap
                    continue

                for atr in grids['range_atr_threshold']:
                    for conf in grids['min_confidence']:
                        for sl in grids['sl_atr_mult']:
                            for trail in grids['trailing_pct']:
                                for lev in grids['leverage']:
                                    # Filtre: ne tester que configs prometteuses
                                    # ATR élevé + RSI assouppli = beaucoup de trades
                                    # On veut 20-50 trades, pas 100+

                                    # Règles intelligentes:
                                    # - Si ATR > 0.35, exiger conf >= 70
                                    # - Si RSI très large (gap > 40), exiger conf >= 75
                                    # - Si leverage > 175, exiger conf >= 75

                                    if atr > 0.35 and conf < 70:
                                        continue

                                    if (rsi_ob - rsi_os) > 40 and conf < 75:
                                        continue

                                    if lev > 175 and conf < 75:
                                        continue

                                    configs.append({
                                        'rsi_oversold': rsi_os,
                                        'rsi_overbought': rsi_ob,
                                        'range_atr_threshold': atr,
                                        'min_confidence': conf,
                                        'sl_atr_mult': sl,
                                        'trailing_pct': trail,
                                        'leverage': lev
                                    })

        # Limiter à 100 configs max (sinon trop long)
        if len(configs) > 100:
            # Échantillonner intelligemment
            # Prendre 1/N configs de manière uniforme
            step = len(configs) // 100
            configs = configs[::step][:100]

        print(f"✅ Grille générée: {len(configs)} configurations")
        return configs

    def test_config(self, params):
        """Teste une config avec trailing stop"""
        try:
            # Créer stratégie
            strategy = V10RangeStrategy(leverage=params['leverage'])

            # Appliquer params
            strategy.params['rsi_oversold'] = params['rsi_oversold']
            strategy.params['rsi_overbought'] = params['rsi_overbought']
            strategy.params['range_atr_threshold'] = params['range_atr_threshold']
            strategy.params['min_confidence'] = params['min_confidence']
            strategy.params['sl_atr_multiplier'] = params['sl_atr_mult']
            # TP pas utilisé en mode trailing, mais on le met quand même
            strategy.params['tp_atr_multiplier'] = 3.0

            # Adapter
            class CustomAdapter:
                def __init__(self, strat):
                    self.strategy = strat
                    self.leverage = strat.leverage
                    self.params = {
                        'ranging': {
                            'sl_atr_multiplier': strat.params['sl_atr_multiplier'],
                            'tp_atr_multiplier': strat.params['tp_atr_multiplier'],
                            'min_confidence': strat.params['min_confidence'],
                        }
                    }

                def analyze(self, symbol, df):
                    signal_dict = self.strategy.analyze(symbol, df)
                    if not signal_dict:
                        return None
                    return Signal(
                        symbol=signal_dict['symbol'],
                        side=signal_dict['side'],
                        entry_price=signal_dict['entry_price'],
                        stop_loss=signal_dict['stop_loss'],
                        take_profit=signal_dict['take_profit'],
                        leverage=signal_dict['leverage'],
                        risk_pct=signal_dict['risk_pct'],
                        score=signal_dict['confidence'],
                        confluences=signal_dict['confluences'],
                        strategy_used='v10_range_reversal',
                        confidence=signal_dict['confidence']
                    )

            adapter = CustomAdapter(strategy)

            # Backtest avec TRAILING STOP
            backtest = AdaptiveBacktest(
                adapter,
                initial_capital=self.initial_capital,
                use_trailing_stop=True,
                trailing_pct=params['trailing_pct']
            )

            results = backtest.run(self.df, 'BTCUSDT')

            if not results or results['total_trades'] == 0:
                return None

            return {
                'params': params.copy(),
                'results': {
                    'total_trades': results['total_trades'],
                    'win_rate': results['win_rate'],
                    'total_pnl_pct': results['total_pnl_pct'],
                    'final_capital': results['final_capital'],
                    'profit_factor': results['profit_factor'],
                    'avg_win': results['avg_win'],
                    'avg_loss': results['avg_loss']
                },
                'timestamp': datetime.now().isoformat()
            }

        except Exception as e:
            logger.error(f"Erreur test config: {e}")
            return None

    def optimize(self):
        """Lance l'optimisation"""

        print("="*80)
        print("🚀 OPTIMISATION V10 TRAILING STOP - GRID SEARCH COMPLÈTE")
        print("="*80)
        print(f"Capital: ${self.initial_capital}")
        print(f"Données: {len(self.df)} bougies")
        print(f"Période: {self.df['timestamp'].iloc[0]} → {self.df['timestamp'].iloc[-1]}")
        print(f"Mode: TRAILING STOP uniquement (exit dynamique)")
        print(f"{'='*80}\n")

        # Générer configs
        configs = self.generate_grid_configs()

        print(f"\n🎯 Objectif: Trouver le sweet spot entre:")
        print(f"   • Nombre de trades élevé (20-50)")
        print(f"   • Profit Factor élevé (>2.0)")
        print(f"   • PnL maximal\n")

        # Tester chaque config
        for i, params in enumerate(configs, 1):
            result = self.test_config(params)

            if result:
                self.results.append(result)

                pnl = result['results']['total_pnl_pct']
                pf = result['results']['profit_factor']
                trades = result['results']['total_trades']

                # Sauvegarder
                with open('v10_trailing_full_results.json', 'w') as f:
                    json.dump(self.results, f, indent=2)

                if pnl > 0 and pf > 1.0:
                    print(f"✅ {i}/{len(configs)} | "
                          f"PnL: {pnl:+.2f}% | PF: {pf:.2f} | "
                          f"Trades: {trades} | "
                          f"Trail:{int(params['trailing_pct']*100)}% "
                          f"RSI:{params['rsi_oversold']}/{params['rsi_overbought']} "
                          f"ATR:{params['range_atr_threshold']:.2f} "
                          f"Lev:{params['leverage']}x")
                else:
                    print(f"❌ {i}/{len(configs)} | "
                          f"PnL: {pnl:+.2f}% | PF: {pf:.2f} | Trades: {trades}")
            else:
                print(f"⚠️  {i}/{len(configs)} | Aucun trade généré")

        self.print_summary()

    def print_summary(self):
        """Affiche résumé détaillé"""
        if not self.results:
            print("\n❌ Aucun résultat")
            return

        print(f"\n\n{'='*80}")
        print(f"📊 RÉSUMÉ OPTIMISATION TRAILING STOP")
        print(f"{'='*80}\n")

        # Filtrer profitables
        profitable = [r for r in self.results if r['results']['total_pnl_pct'] > 0 and r['results']['profit_factor'] > 1.0]

        print(f"Tests effectués: {len(self.results)}")
        print(f"Profitables: {len(profitable)} ({len(profitable)/len(self.results)*100:.1f}%)\n")

        if not profitable:
            print("❌ Aucune config profitable!")
            return

        # Stats par nombre de trades
        trade_counts = [r['results']['total_trades'] for r in profitable]
        print(f"📊 Distribution des trades:")
        print(f"   Min: {min(trade_counts)}")
        print(f"   Max: {max(trade_counts)}")
        print(f"   Moyenne: {sum(trade_counts)/len(trade_counts):.1f}\n")

        # Trier par score composite: PF * PnL * min(trades/20, 1.0)
        def score(r):
            pf = r['results']['profit_factor']
            pnl = r['results']['total_pnl_pct']
            trades = r['results']['total_trades']
            # Favoriser configs avec 20+ trades, PF élevé, PnL positif
            trade_bonus = min(trades / 20.0, 1.5)  # Max 1.5x si 30+ trades
            return pf * (1 + pnl/100) * trade_bonus

        profitable.sort(key=score, reverse=True)

        print(f"🏆 TOP 20 CONFIGS (score: PF × PnL × trades):\n")
        print(f"{'#':<4} {'PnL':<10} {'PF':<7} {'Trades':<8} {'WR':<7} {'Params'}")
        print("-" * 100)

        for i, r in enumerate(profitable[:20], 1):
            res = r['results']
            p = r['params']

            param_str = (f"Trail:{int(p['trailing_pct']*100)}% "
                        f"RSI:{p['rsi_oversold']}/{p['rsi_overbought']} "
                        f"ATR:{p['range_atr_threshold']:.2f} "
                        f"Conf:{p['min_confidence']} "
                        f"SL:{p['sl_atr_mult']} "
                        f"Lev:{p['leverage']}x")

            print(f"{i:<4} {res['total_pnl_pct']:>+8.2f}% "
                  f"{res['profit_factor']:>5.2f}  "
                  f"{res['total_trades']:>5}   "
                  f"{res['win_rate']:>5.1f}%  "
                  f"{param_str}")

        # Analyse par trailing %
        print(f"\n📈 PERFORMANCE PAR TRAILING %:\n")
        trailing_stats = {}
        for r in profitable:
            trail = int(r['params']['trailing_pct'] * 100)
            if trail not in trailing_stats:
                trailing_stats[trail] = {'pnls': [], 'pfs': [], 'trades': []}

            trailing_stats[trail]['pnls'].append(r['results']['total_pnl_pct'])
            trailing_stats[trail]['pfs'].append(r['results']['profit_factor'])
            trailing_stats[trail]['trades'].append(r['results']['total_trades'])

        for trail in sorted(trailing_stats.keys()):
            stats = trailing_stats[trail]
            avg_pnl = sum(stats['pnls']) / len(stats['pnls'])
            avg_pf = sum(stats['pfs']) / len(stats['pfs'])
            avg_trades = sum(stats['trades']) / len(stats['trades'])

            print(f"   Trailing {trail:>2}%: "
                  f"PnL moyen: {avg_pnl:>+7.2f}% | "
                  f"PF moyen: {avg_pf:>5.2f} | "
                  f"Trades moyen: {avg_trades:>5.1f} | "
                  f"Configs: {len(stats['pnls'])}")

        # Meilleur résultat
        best = profitable[0]
        print(f"\n{'='*80}")
        print(f"🎯 MEILLEURE CONFIG:")
        print(f"{'='*80}")
        print(f"PnL: {best['results']['total_pnl_pct']:+.2f}%")
        print(f"Profit Factor: {best['results']['profit_factor']:.2f}")
        print(f"Trades: {best['results']['total_trades']}")
        print(f"Win Rate: {best['results']['win_rate']:.1f}%")
        print(f"\nParamètres:")
        print(f"   Trailing Stop: {int(best['params']['trailing_pct']*100)}%")
        print(f"   RSI: {best['params']['rsi_oversold']} / {best['params']['rsi_overbought']}")
        print(f"   ATR Threshold: {best['params']['range_atr_threshold']:.2f}")
        print(f"   Min Confidence: {best['params']['min_confidence']}")
        print(f"   SL Multiplier: {best['params']['sl_atr_mult']}")
        print(f"   Leverage: {best['params']['leverage']}x")
        print(f"{'='*80}")

        # Sauvegarder
        with open('v10_trailing_full_results.json', 'w') as f:
            json.dump(self.results, f, indent=2)

        # Sauvegarder meilleure config
        with open('v10_trailing_best_config.json', 'w') as f:
            json.dump(best, f, indent=2)

        print(f"\n💾 Résultats sauvegardés:")
        print(f"   • v10_trailing_full_results.json (tous)")
        print(f"   • v10_trailing_best_config.json (meilleur)")


if __name__ == '__main__':
    # Charger données
    df = pd.read_csv('historical_data/BTCUSDT_5m_9000candles.csv')
    df['timestamp'] = pd.to_datetime(df['timestamp'])

    print("📊 Chargement données...")
    print(f"   Période: {df['timestamp'].iloc[0]} → {df['timestamp'].iloc[-1]}")
    print(f"   Bougies: {len(df):,}\n")

    optimizer = V10TrailingFullOptimizer(df, initial_capital=500.0)
    optimizer.optimize()
