#!/usr/bin/env python3
"""
GRID SEARCH CIBLÉ autour de V10 STRICT
Variations légères pour améliorer sans casser la stratégie
"""
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 V10GridOptimizer:
    """Grid search autour des params V10 STRICT"""
    
    def __init__(self, df: pd.DataFrame, initial_capital: float = 500.0):
        self.df = df
        self.initial_capital = initial_capital
        self.results = []
        
        # BASELINE STRICT qui marche
        self.baseline = {
            'rsi_oversold': 30,
            'rsi_overbought': 70,
            'min_confidence': 75,
            'range_atr_threshold': 0.30,
            'sl_atr_mult': 1.2,
            'tp_atr_mult': 3.0,
            'leverage': 150,
        }
    
    def generate_grid_params(self):
        """Génère grille de paramètres autour du baseline"""
        # Variations légères autour des valeurs qui marchent
        grids = {
            'rsi_oversold': [28, 30, 32, 35],
            'rsi_overbought': [65, 68, 70, 72],
            'min_confidence': [70, 75, 80],
            'range_atr_threshold': [0.25, 0.28, 0.30, 0.32, 0.35],
            'sl_atr_mult': [1.0, 1.2, 1.5],
            'tp_atr_mult': [2.5, 3.0, 3.5, 4.0],
            'leverage': [125, 150, 175, 200],
        }
        
        # Combinaisons intelligentes (pas toutes - trop de combos)
        # Stratégie: varier 1-2 params à la fois
        configs = []
        
        # 1. Baseline
        configs.append(self.baseline.copy())
        
        # 2. Varier RSI seul
        for rsi_os in grids['rsi_oversold']:
            for rsi_ob in grids['rsi_overbought']:
                if rsi_ob - rsi_os >= 30:  # Min 30 points écart
                    c = self.baseline.copy()
                    c['rsi_oversold'] = rsi_os
                    c['rsi_overbought'] = rsi_ob
                    configs.append(c)
        
        # 3. Varier ATR threshold seul
        for atr in grids['range_atr_threshold']:
            c = self.baseline.copy()
            c['range_atr_threshold'] = atr
            configs.append(c)
        
        # 4. Varier SL/TP ratio
        for sl in grids['sl_atr_mult']:
            for tp in grids['tp_atr_mult']:
                if tp >= sl * 2.0:  # TP doit être au moins 2x SL
                    c = self.baseline.copy()
                    c['sl_atr_mult'] = sl
                    c['tp_atr_mult'] = tp
                    configs.append(c)
        
        # 5. Varier leverage seul
        for lev in grids['leverage']:
            c = self.baseline.copy()
            c['leverage'] = lev
            configs.append(c)
        
        # 6. Varier confidence seul
        for conf in grids['min_confidence']:
            c = self.baseline.copy()
            c['min_confidence'] = conf
            configs.append(c)
        
        # Dédupliquer
        seen = set()
        unique_configs = []
        for c in configs:
            key = tuple(sorted(c.items()))
            if key not in seen:
                seen.add(key)
                unique_configs.append(c)
        
        return unique_configs
    
    def test_params(self, params):
        """Teste une config"""
        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']
            strategy.params['tp_atr_multiplier'] = params['tp_atr_mult']
            
            # 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'],
                        confidence=signal_dict['confidence'],
                        strategy_used=signal_dict['strategy_used'],
                        confluences=signal_dict['confluences'],
                        leverage=self.leverage,
                        timeframe='5m',
                        timestamp=pd.Timestamp.now()
                    )
            
            adapter = CustomAdapter(strategy)
            
            # Backtest
            backtest = AdaptiveBacktest(adapter, initial_capital=self.initial_capital)
            results = backtest.run(self.df, "BTCUSD_DEGEN")
            
            if not results or results['total_trades'] == 0:
                return None
            
            return {
                'params': params,
                '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(),
                'is_baseline': params == self.baseline
            }
            
        except Exception as e:
            logger.error(f"Error: {e}")
            return None
    
    def optimize(self):
        """Lance grid search"""
        configs = self.generate_grid_params()
        
        print(f"\n{'='*80}")
        print(f"🔬 GRID SEARCH V10 AUTOUR STRICT")
        print(f"{'='*80}")
        print(f"Configs à tester: {len(configs)}")
        print(f"Capital: ${self.initial_capital}")
        print(f"Données: {len(self.df)} bougies")
        print(f"{'='*80}\n")
        
        for i, params in enumerate(configs, 1):
            result = self.test_params(params)
            
            if result:
                self.results.append(result)
                
                # Sauvegarder
                with open('v10_grid_results.json', 'w') as f:
                    json.dump(self.results, f, indent=2)
                
                pnl = result['results']['total_pnl_pct']
                pf = result['results']['profit_factor']
                trades = result['results']['total_trades']
                
                baseline_mark = "⭐" if result['is_baseline'] else "  "
                
                if pnl > 0 and pf > 1.0:
                    print(f"{baseline_mark} ✅ {i}/{len(configs)} | "
                          f"PnL: {pnl:+.2f}% | PF: {pf:.2f} | "
                          f"Trades: {trades} | "
                          f"RSI:{params['rsi_oversold']}/{params['rsi_overbought']} "
                          f"ATR:{params['range_atr_threshold']:.2f} "
                          f"Lev:{params['leverage']}x")
                else:
                    print(f"{baseline_mark} ❌ {i}/{len(configs)} | "
                          f"PnL: {pnl:+.2f}% | PF: {pf:.2f}")
            else:
                print(f"   ⚠️  {i}/{len(configs)} | Aucun trade")
        
        self.print_summary()
    
    def print_summary(self):
        """Résumé"""
        if not self.results:
            print("\n❌ Aucun résultat")
            return
        
        print(f"\n\n{'='*80}")
        print(f"📊 RÉSUMÉ GRID SEARCH V10")
        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: {len(self.results)}")
        print(f"Profitables: {len(profitable)} ({len(profitable)/len(self.results)*100:.1f}%)\n")
        
        if not profitable:
            print("❌ Aucune config profitable!")
            return
        
        # Trouver baseline
        baseline = [r for r in profitable if r.get('is_baseline')]
        if baseline:
            b = baseline[0]
            print(f"⭐ BASELINE (V10 STRICT):")
            print(f"   PnL: {b['results']['total_pnl_pct']:+.2f}%")
            print(f"   PF: {b['results']['profit_factor']:.2f}")
            print(f"   Trades: {b['results']['total_trades']}\n")
        
        # Trier par score composite: PF * (1 + PnL/100) * min(trades/5, 1)
        def score(r):
            pf = r['results']['profit_factor']
            pnl = r['results']['total_pnl_pct']
            trades = r['results']['total_trades']
            # Favoriser PF élevé, PnL positif, et min 5 trades
            return pf * (1 + pnl/100) * min(trades/5.0, 1.0)
        
        profitable.sort(key=score, reverse=True)
        
        print(f"🏆 TOP 10 CONFIGS (score: PF * PnL * trades):\n")
        print(f"{'#':<4} {'PnL':<10} {'PF':<8} {'Trades':<8} {'Params'}")
        print("-" * 80)
        
        for i, r in enumerate(profitable[:10], 1):
            res = r['results']
            p = r['params']
            
            param_str = (f"RSI:{p['rsi_oversold']}/{p['rsi_overbought']} "
                        f"ATR:{p['range_atr_threshold']:.2f} "
                        f"Conf:{p['min_confidence']} "
                        f"SL/TP:{p['sl_atr_mult']}/{p['tp_atr_mult']} "
                        f"Lev:{p['leverage']}x")
            
            baseline_mark = "⭐" if r.get('is_baseline') else "  "
            
            print(f"{baseline_mark}{i:<2} {res['total_pnl_pct']:>+7.2f}%  "
                  f"{res['profit_factor']:>5.2f}   "
                  f"{res['total_trades']:>5}    "
                  f"{param_str}")
        
        print("\n" + "="*80 + "\n")


if __name__ == '__main__':
    # Charger 9000 bougies
    df = pd.read_csv('historical_data/BTCUSDT_5m_9000candles.csv')
    df['timestamp'] = pd.to_datetime(df['timestamp'])
    
    optimizer = V10GridOptimizer(df, initial_capital=500.0)
    optimizer.optimize()
