#!/usr/bin/env python3
"""
PORTFOLIO STRATEGY - Diversification pour gagner tous les mois

Concept: Au lieu d'UNE stratégie, PORTFOLIO de micro-stratégies:
1. Mean Reversion (RSI)
2. Trend Following (MA)
3. Breakout
4. Range Trading

Chaque stratégie trade indépendamment.
La diversification assure qu'au moins l'une performe chaque mois.
"""

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

class PortfolioStrategy:
    """Portfolio de 4 stratégies qui tradent en parallèle"""

    def __init__(self, initial_capital=1000):
        # Capital divisé en 4 (250 par stratégie)
        self.capital_per_strategy = initial_capital / 4
        self.total_capital = initial_capital
        self.initial_capital = initial_capital

        # Positions par stratégie
        self.positions = {
            'mean_reversion': None,
            'trend_following': None,
            'breakout': None,
            'range_trading': None
        }

        self.trades = []

    # ===== STRATÉGIE 1: MEAN REVERSION =====
    def signal_mean_reversion(self, df, i):
        if i < 20:
            return None

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

        # RSI
        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:
            return None

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

        if rsi < 30:
            return {'side': 'LONG', 'strategy': 'mean_reversion', 'tp': 1.0, 'sl': 0.8}
        elif rsi > 70:
            return {'side': 'SHORT', 'strategy': 'mean_reversion', 'tp': 1.0, 'sl': 0.8}

        return None

    # ===== STRATÉGIE 2: TREND FOLLOWING =====
    def signal_trend_following(self, df, i):
        if i < 50:
            return None

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

        # EMAs
        ema10 = recent['close'].ewm(span=10).mean().iloc[-1]
        ema30 = recent['close'].ewm(span=30).mean().iloc[-1]
        prev_ema10 = recent['close'].ewm(span=10).mean().iloc[-2]
        prev_ema30 = recent['close'].ewm(span=30).mean().iloc[-2]

        # Crossover
        if prev_ema10 <= prev_ema30 and ema10 > ema30:
            return {'side': 'LONG', 'strategy': 'trend_following', 'tp': 2.0, 'sl': 1.0}
        elif prev_ema10 >= prev_ema30 and ema10 < ema30:
            return {'side': 'SHORT', 'strategy': 'trend_following', 'tp': 2.0, 'sl': 1.0}

        return None

    # ===== STRATÉGIE 3: BREAKOUT =====
    def signal_breakout(self, df, i):
        if i < 30:
            return None

        current = df.iloc[i]
        recent_20 = df.iloc[i-20:i]

        high_20 = recent_20['high'].max()
        low_20 = recent_20['low'].min()

        # Volume confirmation
        vol_ma = df.iloc[i-30:i]['volume'].mean()
        if current['volume'] < vol_ma * 1.3:
            return None

        if current['close'] > high_20:
            return {'side': 'LONG', 'strategy': 'breakout', 'tp': 1.5, 'sl': 0.7}
        elif current['close'] < low_20:
            return {'side': 'SHORT', 'strategy': 'breakout', 'tp': 1.5, 'sl': 0.7}

        return None

    # ===== STRATÉGIE 4: RANGE TRADING =====
    def signal_range_trading(self, df, i):
        if i < 50:
            return None

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

        # Support/Resistance
        high_50 = recent['high'].max()
        low_50 = recent['low'].min()
        mid_50 = (high_50 + low_50) / 2

        range_pct = ((high_50 - low_50) / mid_50) * 100

        # Range market (volatilité faible)
        if range_pct > 3:  # Trop volatile, skip
            return None

        # Acheter au support, vendre à la résistance
        if current['close'] < low_50 * 1.02:  # Proche support
            return {'side': 'LONG', 'strategy': 'range_trading', 'tp': 0.8, 'sl': 0.5}
        elif current['close'] > high_50 * 0.98:  # Proche résistance
            return {'side': 'SHORT', 'strategy': 'range_trading', 'tp': 0.8, 'sl': 0.5}

        return None

    def calculate_position_size(self, strategy_name):
        """Position size = capital de la stratégie"""
        return self.capital_per_strategy * 0.3  # 30% du capital de la stratégie

    def should_exit(self, df, i, position):
        """Check exit pour une position"""

        current = df.iloc[i]

        tp_pct = position['tp_pct']
        sl_pct = position['sl_pct']

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

            if current['close'] >= tp:
                return True, 'TP'
            if current['close'] <= sl:
                return True, 'SL'
        else:
            tp = position['entry'] * (1 - tp_pct/100)
            sl = position['entry'] * (1 + sl_pct/100)

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

        return False, None

    def close_position(self, strategy_name, exit_price, exit_type):
        """Ferme position et update capital"""

        position = self.positions[strategy_name]
        if not position:
            return

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

        pnl_pct *= 15  # Leverage fixe 15x
        pnl_pct -= 0.001

        pnl_usd = position['position_size'] * pnl_pct

        # Update capital de la stratégie
        self.capital_per_strategy += pnl_usd
        self.total_capital = self.capital_per_strategy * 4

        self.trades.append({
            'strategy': strategy_name,
            'pnl_usd': pnl_usd,
            'pnl_pct': pnl_pct * 100,
            'won': pnl_usd > 0,
            'exit_type': exit_type
        })

        self.positions[strategy_name] = None

    def backtest(self, df):
        """Backtest portfolio"""

        for i in range(50, len(df)):
            # Pour chaque stratégie
            for strat_name in ['mean_reversion', 'trend_following', 'breakout', 'range_trading']:
                # Gérer position existante
                if self.positions[strat_name]:
                    should_exit, exit_type = self.should_exit(df, i, self.positions[strat_name])
                    if should_exit:
                        self.close_position(strat_name, df.iloc[i]['close'], exit_type)
                    continue

                # Chercher nouveau signal
                signal = None
                if strat_name == 'mean_reversion':
                    signal = self.signal_mean_reversion(df, i)
                elif strat_name == 'trend_following':
                    signal = self.signal_trend_following(df, i)
                elif strat_name == 'breakout':
                    signal = self.signal_breakout(df, i)
                elif strat_name == 'range_trading':
                    signal = self.signal_range_trading(df, i)

                if signal:
                    self.positions[strat_name] = {
                        'entry': df.iloc[i]['close'],
                        'side': signal['side'],
                        'tp_pct': signal['tp'],
                        'sl_pct': signal['sl'],
                        'position_size': self.calculate_position_size(strat_name)
                    }

        # 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.total_capital - self.initial_capital) / self.initial_capital) * 100
        win_rate = len(wins) / len(self.trades)
        profit_factor = abs(sum([t['pnl_usd'] for t in wins]) / sum([t['pnl_usd'] for t in losses])) if losses else 999

        # Stats par stratégie
        strat_stats = {}
        for strat_name in ['mean_reversion', 'trend_following', 'breakout', 'range_trading']:
            strat_trades = [t for t in self.trades if t['strategy'] == strat_name]
            if strat_trades:
                strat_wins = [t for t in strat_trades if t['won']]
                strat_stats[strat_name] = {
                    'trades': len(strat_trades),
                    'win_rate': len(strat_wins) / len(strat_trades),
                    'roi': sum([t['pnl_usd'] for t in strat_trades]) / (self.initial_capital / 4) * 100
                }

        return {
            'roi': total_return,
            'capital': self.total_capital,
            'trades': len(self.trades),
            'win_rate': win_rate,
            'profit_factor': profit_factor,
            'strategy_stats': strat_stats
        }


# ============ TEST MENSUEL ============

print("=" * 80)
print("🎯 PORTFOLIO STRATEGY - 4 stratégies en parallèle")
print("=" * 80)
print("\nStratégies (250$ chacune):")
print("  1. Mean Reversion (RSI)")
print("  2. Trend Following (EMA crossover)")
print("  3. Breakout (Range breakout)")
print("  4. Range Trading (Support/Resistance)")

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

print("\n📥 Téléchargement BTC 1h...")
df_full = backtester.fetch_binance_data('BTCUSDT', '1h', 1000)

if df_full is None:
    exit(1)

# Timestamp
if 'timestamp' not in df_full.columns:
    from datetime import datetime
    df_full['timestamp'] = pd.date_range(end=datetime.now(), periods=len(df_full), freq='1H')

df_full['year_month'] = pd.to_datetime(df_full['timestamp']).dt.to_period('M')
months = df_full['year_month'].unique()

print(f"\n📊 Test mensuel ({len(months)} mois):\n")

monthly_results = []

for month in months:
    month_data = df_full[df_full['year_month'] == month].copy()

    if len(month_data) < 50:
        continue

    month_str = str(month)
    print(f"🧪 {month_str}...", end=" ")

    system = PortfolioStrategy(initial_capital=1000)
    result = system.backtest(month_data)

    if result:
        result['month'] = month_str
        monthly_results.append(result)

        status = "✅" if result['roi'] > 0 else "❌"
        print(f"{status} ROI: {result['roi']:+6.2f}% | WR: {result['win_rate']*100:5.1f}% | Trades: {result['trades']:3d} | PF: {result['profit_factor']:.2f}")
    else:
        print(f"⚠️  Aucun trade")

# Stats
print("\n" + "=" * 80)
print("📊 RÉSULTATS PORTFOLIO")
print("=" * 80)

if monthly_results:
    profitable = [r for r in monthly_results if r['roi'] > 0]
    consistance = len(profitable) / len(monthly_results)

    print(f"\n🎯 CONSISTANCE:")
    print(f"   Mois profitables:      {len(profitable)}/{len(monthly_results)} ({consistance*100:.1f}%)")

    if consistance >= 0.9:
        print(f"   ✅ OBJECTIF ATTEINT!")
    elif consistance >= 0.75:
        print(f"   ✅ TRÈS BON")
    else:
        print(f"   ⚠️  À améliorer")

    rois = [r['roi'] for r in monthly_results]
    print(f"\n💰 PERFORMANCE:")
    print(f"   ROI moyen mensuel:     {np.mean(rois):+.2f}%")

    # Sauvegarder
    with open('portfolio_strategy_results.json', 'w') as f:
        json.dump({'months': monthly_results, 'consistance': consistance}, f, indent=2, default=str)

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

print("=" * 80)
