#!/usr/bin/env python3
"""
SYSTÈME FINAL - Version ultra-robuste

Leçons apprises:
1. 4h >> 1h (moins de bruit)
2. Volatility expansion marche
3. Besoin de meilleure sélection de trades (qualité > quantité)
4. Risk management crucial

Améliorations:
- Multi-confirmation (3 signaux minimum)
- Gestion des séries de pertes
- Position sizing adaptatif selon performance
- Stop trading en période défavorable
"""

import pandas as pd
import numpy as np
from martingale_backtester import MartingaleBacktester
import json

class FinalSystem:
    """Système final ultra-robuste"""

    def __init__(self, initial_capital=1000):
        self.capital = initial_capital
        self.initial_capital = initial_capital
        self.position = None
        self.trades = []
        self.consecutive_losses = 0
        self.performance_multiplier = 1.0

    def multi_confirmation_signal(self, df, i):
        """Signal avec MULTI-CONFIRMATION (évite faux signaux)"""

        if i < 100:
            return None

        current = df.iloc[i]
        recent = df.iloc[i-100:i]

        confirmations = 0
        side = None

        # CONFIRMATION 1: Volatility Expansion (obligatoire)
        high_low = recent['high'] - recent['low']
        atr = high_low.mean()
        current_range = current['high'] - current['low']

        if current_range <= atr * 1.5:
            return None  # Pas de vol expansion = pas de trade

        # Direction de la bougie
        is_bullish = current['close'] > current['open']
        base_side = 'LONG' if is_bullish else 'SHORT'

        # CONFIRMATION 2: Trend (50 MA)
        ma50 = recent['close'].tail(50).mean()

        if is_bullish and current['close'] > ma50:
            confirmations += 1
            side = 'LONG'
        elif not is_bullish and current['close'] < ma50:
            confirmations += 1
            side = 'SHORT'

        # CONFIRMATION 3: Volume
        vol_ma = recent['volume'].tail(20).mean()
        if current['volume'] > vol_ma * 1.5:
            confirmations += 1

        # CONFIRMATION 4: Momentum (prix > MA20)
        ma20 = recent['close'].tail(20).mean()

        if is_bullish and current['close'] > ma20 * 1.01:
            confirmations += 1
        elif not is_bullish and current['close'] < ma20 * 0.99:
            confirmations += 1

        # CONFIRMATION 5: RSI zone favorable
        # Calcul RSI simple
        deltas = recent['close'].diff()
        gains = deltas.clip(lower=0).rolling(14).mean()
        losses = -deltas.clip(upper=0).rolling(14).mean()

        if losses.iloc[-1] != 0:
            rs = gains.iloc[-1] / losses.iloc[-1]
            rsi = 100 - (100 / (1 + rs))

            if is_bullish and 30 < rsi < 70:  # Pas overbought
                confirmations += 1
            elif not is_bullish and 30 < rsi < 70:  # Pas oversold
                confirmations += 1

        # Besoin d'au moins 3 confirmations
        if confirmations < 3:
            return None

        return {
            'side': side or base_side,
            'confirmations': confirmations,
            'confidence': confirmations / 5.0  # Score sur 5
        }

    def calculate_dynamic_params(self, df, i):
        """Paramètres dynamiques basés sur marché"""

        recent = df.iloc[i-50:i]

        # ATR pour TP/SL
        high_low = recent['high'] - recent['low']
        atr = high_low.mean()
        atr_pct = (atr / recent['close'].mean()) * 100

        # Volatilité récente
        returns = recent['close'].pct_change()
        volatility = returns.std()

        # LEVERAGE: Inverse de la volatilité + ajusté selon performance
        if volatility > 0.03:  # Très volatile
            base_lev = 15
        elif volatility > 0.02:
            base_lev = 25
        else:
            base_lev = 35

        # Réduire leverage après pertes
        if self.consecutive_losses >= 3:
            base_lev = int(base_lev * 0.5)
        elif self.consecutive_losses >= 2:
            base_lev = int(base_lev * 0.7)

        leverage = min(base_lev, 50)

        # TP/SL basés sur ATR
        tp_pct = atr_pct * 2.5  # 2.5x ATR pour TP
        sl_pct = atr_pct * 1.2  # 1.2x ATR pour SL

        # Limites
        tp_pct = max(1.0, min(tp_pct, 6.0))
        sl_pct = max(0.5, min(sl_pct, 3.0))

        # POSITION SIZE: Risque 1-3% selon performance
        if self.consecutive_losses >= 2:
            risk_pct = 0.01  # 1% après pertes
        else:
            risk_pct = 0.02  # 2% normal

        # Multiplier si on est en série gagnante
        risk_pct *= self.performance_multiplier

        risk_amount = self.capital * risk_pct
        position_size = risk_amount / (sl_pct/100 * leverage)
        position_size = min(position_size, self.capital * 0.25)  # Max 25%

        return {
            'leverage': leverage,
            'tp_pct': tp_pct,
            'sl_pct': sl_pct,
            'position_size': position_size
        }

    def should_trade(self):
        """Détermine si on doit trader (évite périodes défavorables)"""

        # Stop trading après 4 pertes consécutives
        if self.consecutive_losses >= 4:
            return False

        # Stop si drawdown > 25%
        if self.capital < self.initial_capital * 0.75:
            return False

        return True

    def manage_exit(self, df, i):
        """Gestion de sortie améliorée"""

        if not self.position:
            return False, None

        current = df.iloc[i]
        params = self.position['params']

        # TP/SL standards
        if self.position['side'] == 'LONG':
            tp = self.position['entry'] * (1 + params['tp_pct']/100)
            sl = self.position['entry'] * (1 - params['sl_pct']/100)

            if current['close'] >= tp:
                return True, 'TP'
            if current['close'] <= sl:
                return True, 'SL'

            # Trailing stop si > 60% du TP
            profit_pct = (current['close'] - self.position['entry']) / self.position['entry']
            if profit_pct > (params['tp_pct']/100) * 0.6:
                trailing = self.position['entry'] + (current['close'] - self.position['entry']) * 0.75
                if current['close'] < trailing:
                    return True, 'TRAILING'

        else:  # SHORT
            tp = self.position['entry'] * (1 - params['tp_pct']/100)
            sl = self.position['entry'] * (1 + params['sl_pct']/100)

            if current['close'] <= tp:
                return True, 'TP'
            if current['close'] >= sl:
                return True, 'SL'

            profit_pct = (self.position['entry'] - current['close']) / self.position['entry']
            if profit_pct > (params['tp_pct']/100) * 0.6:
                trailing = self.position['entry'] - (self.position['entry'] - current['close']) * 0.75
                if current['close'] > trailing:
                    return True, 'TRAILING'

        return False, None

    def close_trade(self, exit_price, exit_type):
        """Ferme et enregistre le trade"""

        if not self.position:
            return

        params = self.position['params']

        # PnL
        if self.position['side'] == 'LONG':
            pnl_pct = (exit_price - self.position['entry']) / self.position['entry']
        else:
            pnl_pct = (self.position['entry'] - exit_price) / self.position['entry']

        pnl_pct *= params['leverage']
        pnl_pct -= 0.001  # Frais

        pnl_usd = params['position_size'] * pnl_pct
        self.capital += pnl_usd

        won = pnl_usd > 0

        # Update streaks
        if won:
            self.consecutive_losses = 0
            # Augmenter multiplier après 2 wins
            if len([t for t in self.trades[-2:] if t.get('won', False)]) == 2:
                self.performance_multiplier = min(self.performance_multiplier * 1.2, 1.5)
        else:
            self.consecutive_losses += 1
            self.performance_multiplier = 1.0  # Reset multiplier

        self.trades.append({
            'entry': self.position['entry'],
            'exit': exit_price,
            'side': self.position['side'],
            'pnl_usd': pnl_usd,
            'pnl_pct': pnl_pct * 100,
            'won': won,
            'exit_type': exit_type,
            'confirmations': self.position['confirmations']
        })

        self.position = None

    def backtest(self, df):
        """Backtest le système"""

        for i in range(100, len(df)):
            # Sortir position si existe
            if self.position:
                should_exit, exit_type = self.manage_exit(df, i)
                if should_exit:
                    self.close_trade(df.iloc[i]['close'], exit_type)
                continue

            # Check si on trade
            if not self.should_trade():
                continue

            # Signal multi-confirmation
            signal = self.multi_confirmation_signal(df, i)
            if not signal:
                continue

            # Params dynamiques
            params = self.calculate_dynamic_params(df, i)

            # Ouvrir position
            self.position = {
                'entry': df.iloc[i]['close'],
                'side': signal['side'],
                'params': params,
                'confirmations': signal['confirmations']
            }

        # Stats
        if not self.trades:
            return None

        wins = [t for t in self.trades if t['won']]
        losses = [t for t in self.trades if not t['won']]

        total_return = ((self.capital - self.initial_capital) / self.initial_capital) * 100
        win_rate = len(wins) / len(self.trades)
        avg_win = np.mean([t['pnl_pct'] for t in wins]) if wins else 0
        avg_loss = np.mean([t['pnl_pct'] for t in losses]) if losses else 0
        pf = abs(sum([t['pnl_usd'] for t in wins]) / sum([t['pnl_usd'] for t in losses])) if losses else 999

        # Max DD
        peak = self.initial_capital
        max_dd = 0
        cap = self.initial_capital
        for t in self.trades:
            cap += t['pnl_usd']
            if cap > peak:
                peak = cap
            dd = ((peak - cap) / peak) * 100
            if dd > max_dd:
                max_dd = dd

        return {
            'roi': total_return,
            'capital': self.capital,
            'trades': len(self.trades),
            'win_rate': win_rate,
            'avg_win': avg_win,
            'avg_loss': avg_loss,
            'profit_factor': pf,
            'max_dd': max_dd
        }

# ============ TESTS FINAUX ============

print("=" * 80)
print("🚀 SYSTÈME FINAL - Multi-Symbol 4h (Timeframe Optimal)")
print("=" * 80)

backtester = MartingaleBacktester(
    initial_capital=1000.0,
    trading_fee_pct=0.0005,
    timeout_minutes=60
)

symbols = ['BTCUSDT', 'ETHUSDT', 'SOLUSDT', 'BNBUSDT', 'ADAUSDT', 'AVAXUSDT']
results = []

print("\n📊 Tests sur 4h (timeframe optimal):\n")

for symbol in symbols:
    print(f"🧪 {symbol}...", end=" ")

    df = backtester.fetch_binance_data(symbol, '4h', 500)

    if df is None:
        print("❌")
        continue

    system = FinalSystem(initial_capital=1000)
    result = system.backtest(df)

    if result:
        result['symbol'] = symbol
        results.append(result)
        status = "✅" if result['roi'] > 10 else "⚠️" if result['roi'] > 0 else "❌"
        print(f"{status} ROI: {result['roi']:+.2f}% | WR: {result['win_rate']*100:.1f}% | Trades: {result['trades']}")
    else:
        print("❌ Aucun trade")

# Résultats
print("\n\n" + "=" * 80)
print("🏆 RÉSULTATS FINAUX")
print("=" * 80)

if not results:
    print("\n❌ Aucun résultat!")
    exit(1)

results.sort(key=lambda x: x['roi'], reverse=True)

print(f"\n{'Symbol':<12} {'ROI%':>8} {'WR%':>6} {'PF':>6} {'Trades':>7} {'DD%':>7} {'Avg+':>7} {'Avg-':>7}")
print("-" * 80)

for res in results:
    status = "✅" if res['roi'] > 10 else "⚠️" if res['roi'] > 0 else "❌"
    print(f"{res['symbol']:<12} {res['roi']:>7.2f}% {res['win_rate']*100:>5.1f}% "
          f"{res['profit_factor']:>6.2f} {res['trades']:>7} {res['max_dd']:>6.2f}% "
          f"{res['avg_win']:>6.2f}% {res['avg_loss']:>6.2f}% {status}")

# Stats
profitable = [r for r in results if r['roi'] > 0]
avg_roi = np.mean([r['roi'] for r in results])
median_roi = np.median([r['roi'] for r in results])

print(f"\n📈 STATISTIQUES:")
print(f"   Tests:               {len(results)}")
print(f"   Profitables:         {len(profitable)} ({len(profitable)/len(results)*100:.1f}%)")
print(f"   ROI moyen:           {avg_roi:+.2f}%")
print(f"   ROI médian:          {median_roi:+.2f}%")

if profitable:
    avg_profit = np.mean([r['roi'] for r in profitable])
    avg_wr = np.mean([r['win_rate'] for r in profitable])
    print(f"   ROI moyen (profit):  +{avg_profit:.2f}%")
    print(f"   WR moyen (profit):   {avg_wr*100:.1f}%")

# Meilleur
best = results[0]
print(f"\n🥇 MEILLEUR:")
print(f"   Symbol:  {best['symbol']}")
print(f"   ROI:     {best['roi']:+.2f}%")
print(f"   WR:      {best['win_rate']*100:.1f}%")
print(f"   PF:      {best['profit_factor']:.2f}")
print(f"   Trades:  {best['trades']}")

# Robustesse
std_roi = np.std([r['roi'] for r in results])
print(f"\n🎯 ROBUSTESSE:")
print(f"   Écart-type: {std_roi:.2f}%")

if std_roi < 15 and len(profitable)/len(results) > 0.65:
    print(f"   ✅ SYSTÈME ROBUSTE!")
elif len(profitable)/len(results) > 0.5:
    print(f"   ⚠️  Système modérément robuste")
else:
    print(f"   ❌ Système instable")

# Sauvegarder
with open('final_system_results.json', 'w') as f:
    json.dump(results, f, indent=2, default=str)

print(f"\n💾 Résultats: final_system_results.json")

# Verdict
print(f"\n{'=' * 80}")
if avg_roi > 10 and len(profitable)/len(results) > 0.7:
    print("🎉 SYSTÈME FINAL EXCELLENT - PRÊT POUR LE LIVE!")
    print(f"   Espérance: +{avg_roi:.2f}% par période")
    print(f"   Robustesse: {len(profitable)/len(results)*100:.0f}% profitable")
elif avg_roi > 5 and len(profitable)/len(results) > 0.6:
    print("✅ SYSTÈME FINAL BON - Peut être utilisé avec prudence")
elif avg_roi > 0:
    print("⚠️  Système légèrement profitable - À améliorer")
else:
    print("❌ Système non profitable")
print(f"{'=' * 80}")
