#!/usr/bin/env python3
"""
COMBINED STRATEGY FULL - Meilleure stratégie avec filtres complets

Combine:
1. Multi-vote strategy (meilleur des tests)
2. Market regime filters (du hybrid_system)
3. Paramètres adaptatifs
4. Risk management
5. Trailing stops
"""

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

class CombinedStrategyFull:
    """Combined strategy avec tous les filtres"""

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

    def detect_market_regime(self, df, i):
        """Market regime filters"""

        if i < 100:
            return None

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

        # 1. TREND
        ma50 = recent['close'].tail(50).mean()
        ma20 = recent['close'].tail(20).mean()

        trend_strength = (ma20 - ma50) / ma50 if ma50 > 0 else 0

        # Skip strong downtrends
        if trend_strength < -0.05:
            return None

        if ma20 > ma50 * 1.02:
            trend = 'UPTREND'
        elif ma20 < ma50 * 0.98:
            trend = 'DOWNTREND'
        else:
            trend = 'RANGING'

        # 2. VOLATILITY
        returns = recent['close'].pct_change()
        volatility = returns.std()

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

        # 4. VOLUME
        vol_ma = recent['volume'].mean()
        current_vol = df.iloc[i]['volume']
        volume_ratio = current_vol / vol_ma if vol_ma > 0 else 1

        return {
            'trend': trend,
            'volatility': volatility,
            'atr': atr,
            'atr_pct': atr_pct,
            'volume_ratio': volume_ratio,
            'tradeable': True
        }

    def calculate_adaptive_params(self, regime):
        """Paramètres adaptatifs"""

        # LEVERAGE
        if regime['atr_pct'] > 2.0:
            base_leverage = 10
        elif regime['atr_pct'] > 1.0:
            base_leverage = 20
        elif regime['atr_pct'] > 0.5:
            base_leverage = 30
        else:
            base_leverage = 40

        if regime['volume_ratio'] > 2.0:
            leverage = int(base_leverage * 1.2)
        elif regime['volume_ratio'] < 0.5:
            leverage = int(base_leverage * 0.7)
        else:
            leverage = base_leverage

        leverage = min(leverage, 50)

        # TP/SL
        tp_pct = regime['atr_pct'] * 2.0
        sl_pct = regime['atr_pct'] * 1.0

        tp_pct = max(0.5, min(tp_pct, 5.0))
        sl_pct = max(0.3, min(sl_pct, 3.0))

        # POSITION SIZE
        if self.consecutive_losses >= 2:
            risk_pct = 0.01
        else:
            risk_pct = 0.02

        risk_amount = self.capital * risk_pct
        position_size = risk_amount / (sl_pct/100 * leverage)
        position_size = min(position_size, self.capital * 0.3)

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

    def detect_entry_signal(self, df, i, regime):
        """MULTI-VOTE SIGNAL - 3 stratégies votent"""

        if i < 50:
            return None

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

        votes = {'LONG': 0, 'SHORT': 0}
        reasons = []

        # ===== VOTE 1: VOLATILITY EXPANSION =====
        current_range = current['high'] - current['low']
        avg_range = (recent['high'] - recent['low']).mean()

        if current_range > avg_range * 1.5:
            # Direction
            if current['close'] > current['open']:
                votes['LONG'] += 1
                reasons.append('Vol Exp LONG')
            else:
                votes['SHORT'] += 1
                reasons.append('Vol Exp SHORT')

            # Volume bonus
            if regime['volume_ratio'] > 1.5:
                if current['close'] > current['open']:
                    votes['LONG'] += 0.5
                else:
                    votes['SHORT'] += 0.5

        # ===== VOTE 2: RSI MEAN REVERSION =====
        if i >= 20:
            recent_rsi = df.iloc[i-20:i]
            deltas = recent_rsi['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 rsi < 40:  # Oversold
                    votes['LONG'] += 1
                    reasons.append('RSI Oversold')
                elif rsi > 60:  # Overbought
                    votes['SHORT'] += 1
                    reasons.append('RSI Overbought')

        # ===== VOTE 3: TREND ALIGNMENT =====
        ma20 = recent['close'].tail(20).mean()

        if regime['trend'] == 'UPTREND':
            votes['LONG'] += 1
            reasons.append('Uptrend')
        elif regime['trend'] == 'DOWNTREND':
            votes['SHORT'] += 1
            reasons.append('Downtrend')

        # Momentum
        if current['close'] > ma20 * 1.01:
            votes['LONG'] += 0.5
        elif current['close'] < ma20 * 0.99:
            votes['SHORT'] += 0.5

        # ===== VOTE 4: BREAKOUT =====
        recent_20 = df.iloc[max(0, i-20):i]
        high_20 = recent_20['high'].max()
        low_20 = recent_20['low'].min()

        if current['close'] > high_20 and regime['volume_ratio'] > 1.3:
            votes['LONG'] += 1
            reasons.append('Breakout UP')
        elif current['close'] < low_20 and regime['volume_ratio'] > 1.3:
            votes['SHORT'] += 1
            reasons.append('Breakout DOWN')

        # ===== DÉCISION: Minimum 2 votes =====
        if votes['LONG'] >= 2:
            return {'side': 'LONG', 'votes': votes['LONG'], 'reasons': reasons}
        elif votes['SHORT'] >= 2:
            return {'side': 'SHORT', 'votes': votes['SHORT'], 'reasons': reasons}

        return None

    def should_exit(self, df, i, position, params):
        """Exit avec trailing stop"""

        current = df.iloc[i]

        if position['side'] == 'LONG':
            tp = position['entry'] * (1 + params['tp_pct']/100)
            sl = position['entry'] * (1 - params['sl_pct']/100)

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

            # Trailing
            profit = (current['close'] - position['entry']) / position['entry']
            if profit > (params['tp_pct']/100) * 0.5:
                trailing = position['entry'] + (current['close'] - position['entry']) * 0.7
                if current['close'] < trailing:
                    return True, 'TRAILING'

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

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

            profit = (position['entry'] - current['close']) / position['entry']
            if profit > (params['tp_pct']/100) * 0.5:
                trailing = position['entry'] - (position['entry'] - current['close']) * 0.7
                if current['close'] > trailing:
                    return True, 'TRAILING'

        return False, None

    def close_trade(self, exit_price, exit_type):
        """Close 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

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

        won = pnl_usd > 0

        if won:
            self.consecutive_losses = 0
        else:
            self.consecutive_losses += 1

        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,
            'votes': self.position.get('votes', 0)
        })

        self.position = None

    def backtest(self, df):
        """Backtest combined strategy"""

        for i in range(100, len(df)):
            # Market regime
            regime = self.detect_market_regime(df, i)
            if not regime or not regime.get('tradeable', False):
                continue

            # Params
            params = self.calculate_adaptive_params(regime)

            # Gérer position
            if self.position:
                should_exit, exit_type = self.should_exit(df, i, self.position, self.position['params'])
                if should_exit:
                    self.close_trade(df.iloc[i]['close'], exit_type)
                continue

            # Stop après 4 pertes
            if self.consecutive_losses >= 4:
                continue

            # Multi-vote signal
            signal = self.detect_entry_signal(df, i, regime)
            if signal:
                self.position = {
                    'entry': df.iloc[i]['close'],
                    'side': signal['side'],
                    'params': params,
                    'votes': signal['votes']
                }

        # 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
        profit_factor = 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': profit_factor,
            'max_dd': max_dd
        }


# ============ TESTS ============

print("=" * 80)
print("🚀 COMBINED STRATEGY FULL - Multi-Vote avec Filtres Complets")
print("=" * 80)
print("\nVote de 4 stratégies:")
print("  1. Volatility Expansion")
print("  2. RSI Mean Reversion")
print("  3. Trend Alignment")
print("  4. Breakout")
print("\n  → Minimum 2 votes requis pour trade")

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

print("\n📊 Test BTC 4h - Walk-Forward Validation:\n")
print("📥 Téléchargement...")
df_full = backtester.fetch_binance_data('BTCUSDT', '4h', 1000)

if df_full is None:
    exit(1)

# Split en 3
n = len(df_full)
periods = [
    ("P1 (Mai-Juil)", df_full.iloc[:n//3]),
    ("P2 (Juil-Sep)", df_full.iloc[n//3:2*n//3]),
    ("P3 (Sep-Nov)", df_full.iloc[2*n//3:])
]

results = []

for name, period_df in periods:
    print(f"🧪 {name}...", end=" ")

    system = CombinedStrategyFull(initial_capital=1000)
    result = system.backtest(period_df)

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

# Comparaison
print("\n" + "=" * 80)
print("📊 COMPARAISON vs BASELINE")
print("=" * 80)

if results:
    rois = [r['roi'] for r in results]
    wrs = [r['win_rate'] for r in results]
    pfs = [r['profit_factor'] for r in results]

    avg_roi_combined = np.mean(rois)
    std_roi_combined = np.std(rois)
    avg_wr_combined = np.mean(wrs)
    avg_pf_combined = np.mean(pfs)

    # Baseline
    baseline_roi = 17.14
    baseline_std = 4.08
    baseline_wr = 0.571
    baseline_pf = 2.26

    print(f"\n{'Métrique':<20} {'Baseline':>12} {'Combined':>12} {'Delta':>10}")
    print("-" * 60)
    print(f"{'ROI moyen':<20} {baseline_roi:>11.2f}% {avg_roi_combined:>11.2f}% {avg_roi_combined-baseline_roi:>9.2f}%")
    print(f"{'Écart-type':<20} {baseline_std:>11.2f}% {std_roi_combined:>11.2f}% {std_roi_combined-baseline_std:>9.2f}%")
    print(f"{'Win Rate':<20} {baseline_wr*100:>11.1f}% {avg_wr_combined*100:>11.1f}% {(avg_wr_combined-baseline_wr)*100:>9.1f}%")
    print(f"{'Profit Factor':<20} {baseline_pf:>12.2f} {avg_pf_combined:>12.2f} {avg_pf_combined-baseline_pf:>10.2f}")

    # Sharpe
    sharpe_baseline = baseline_roi / baseline_std
    sharpe_combined = avg_roi_combined / std_roi_combined if std_roi_combined > 0 else 0

    print(f"{'Sharpe Ratio':<20} {sharpe_baseline:>12.2f} {sharpe_combined:>12.2f} {sharpe_combined-sharpe_baseline:>10.2f}")

    # Verdict
    print(f"\n🎯 VERDICT:")
    if avg_roi_combined > baseline_roi * 1.1:
        print(f"   ✅ AMÉLIORATION SIGNIFICATIVE (+{((avg_roi_combined/baseline_roi - 1)*100):.1f}%)")
    elif avg_roi_combined > baseline_roi:
        print(f"   ✅ Amélioration ROI (+{avg_roi_combined-baseline_roi:.2f}%)")
    elif avg_roi_combined > baseline_roi * 0.95:
        print(f"   ⚠️  Performance similaire")
    else:
        print(f"   ❌ Performance inférieure")

    if sharpe_combined > sharpe_baseline * 1.1:
        print(f"   ✅ SHARPE AMÉLIORÉ (+{((sharpe_combined/sharpe_baseline - 1)*100):.1f}%)")

    # Sauvegarder
    with open('combined_strategy_full_results.json', 'w') as f:
        json.dump({
            'periods': results,
            'stats': {
                'avg_roi': avg_roi_combined,
                'std_roi': std_roi_combined,
                'avg_wr': avg_wr_combined,
                'avg_pf': avg_pf_combined,
                'sharpe': sharpe_combined
            },
            'baseline_comparison': {
                'roi_improvement': avg_roi_combined - baseline_roi,
                'sharpe_improvement': sharpe_combined - sharpe_baseline
            }
        }, f, indent=2, default=str)

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

print("=" * 80)
