Files
trader-ml/docs/STRATEGY_GUIDE.md
2026-03-08 21:45:47 +00:00

32 KiB
Raw Permalink Blame History

📊 Guide des Stratégies - Trading AI Secure

📋 Table des Matières

  1. Vue d'ensemble
  2. Architecture Stratégies
  3. Scalping Strategy
  4. Intraday Strategy
  5. Swing Strategy
  6. Paramètres Adaptatifs
  7. Combinaison Multi-Stratégie
  8. Implémentation

🎯 Vue d'ensemble

Philosophie Multi-Stratégie

Le système utilise 3 stratégies complémentaires qui opèrent sur différents timeframes et profils de risque :

┌────────────────────────────────────────────────────────────┐
│              STRATÉGIES COMPLÉMENTAIRES                    │
├────────────────────────────────────────────────────────────┤
│                                                            │
│  SCALPING (Court Terme)                                   │
│  ├─ Timeframe: 1-5 minutes                                │
│  ├─ Objectif: Micro-mouvements                            │
│  ├─ Win Rate: 60-70%                                      │
│  └─ Risk/Trade: 0.5-1%                                    │
│                                                            │
│  INTRADAY (Moyen Terme)                                   │
│  ├─ Timeframe: 15-60 minutes                              │
│  ├─ Objectif: Tendances journalières                      │
│  ├─ Win Rate: 55-65%                                      │
│  └─ Risk/Trade: 1-2%                                      │
│                                                            │
│  SWING (Long Terme)                                       │
│  ├─ Timeframe: 4H-1D                                      │
│  ├─ Objectif: Mouvements multi-jours                      │
│  ├─ Win Rate: 50-60%                                      │
│  └─ Risk/Trade: 2-3%                                      │
│                                                            │
└────────────────────────────────────────────────────────────┘

Avantages Multi-Stratégie

  1. Diversification temporelle : Réduit corrélation
  2. Opportunités multiples : Capture différents mouvements
  3. Lissage performance : Compense pertes d'une stratégie
  4. Adaptabilité : Ajuste poids selon régime de marché

🏗️ Architecture Stratégies

Classe de Base Abstraite

# src/strategies/base_strategy.py

from abc import ABC, abstractmethod
from typing import Dict, List, Optional, Tuple
from dataclasses import dataclass
from datetime import datetime
import pandas as pd
import numpy as np

@dataclass
class Signal:
    """Signal de trading"""
    symbol: str
    direction: str  # 'LONG' or 'SHORT'
    entry_price: float
    stop_loss: float
    take_profit: float
    confidence: float  # 0.0 to 1.0
    timestamp: datetime
    strategy: str
    metadata: Dict

@dataclass
class StrategyConfig:
    """Configuration stratégie"""
    name: str
    timeframe: str
    risk_per_trade: float
    max_holding_time: int  # seconds
    max_trades_per_day: int
    min_profit_target: float
    max_slippage: float
    
    # Paramètres adaptatifs
    adaptive_params: Dict

class BaseStrategy(ABC):
    """
    Classe de base pour toutes les stratégies
    
    Toutes stratégies doivent implémenter:
    - analyze(): Analyse marché et génère signaux
    - calculate_position_size(): Calcule taille position
    - update_parameters(): Met à jour paramètres adaptatifs
    """
    
    def __init__(self, config: StrategyConfig):
        self.config = config
        self.name = config.name
        
        # État
        self.active_positions: List[Dict] = []
        self.closed_trades: List[Dict] = []
        self.parameters = config.adaptive_params.copy()
        
        # Performance
        self.win_rate = 0.5
        self.avg_win = 0.0
        self.avg_loss = 0.0
        self.sharpe_ratio = 0.0
        
    @abstractmethod
    def analyze(self, market_data: pd.DataFrame) -> Optional[Signal]:
        """
        Analyse données marché et génère signal
        
        Args:
            market_data: DataFrame avec OHLCV + indicateurs
            
        Returns:
            Signal si opportunité détectée, None sinon
        """
        pass
    
    @abstractmethod
    def calculate_indicators(self, data: pd.DataFrame) -> pd.DataFrame:
        """
        Calcule indicateurs techniques nécessaires
        
        Args:
            data: DataFrame avec OHLCV
            
        Returns:
            DataFrame avec indicateurs ajoutés
        """
        pass
    
    def calculate_position_size(
        self,
        signal: Signal,
        portfolio_value: float,
        current_volatility: float
    ) -> float:
        """
        Calcule taille position optimale
        
        Utilise:
        - Kelly Criterion adaptatif
        - Volatility adjustment
        - Risk per trade limit
        """
        # Kelly de base
        kelly = (self.win_rate * (self.avg_win / abs(self.avg_loss)) - (1 - self.win_rate)) / (self.avg_win / abs(self.avg_loss))
        
        # Ajuster selon volatilité
        vol_adjustment = 0.02 / max(current_volatility, 0.01)  # Target 2% vol
        kelly *= vol_adjustment
        
        # Ajuster selon confiance du signal
        kelly *= signal.confidence
        
        # Appliquer limite risk per trade
        kelly = min(kelly, self.config.risk_per_trade)
        
        # Calculer taille position
        risk_amount = portfolio_value * kelly
        stop_distance = abs(signal.entry_price - signal.stop_loss)
        position_size = risk_amount / stop_distance
        
        return position_size
    
    def update_parameters(self, recent_performance: Dict):
        """
        Met à jour paramètres adaptatifs selon performance
        
        Args:
            recent_performance: Métriques des 30 derniers jours
        """
        # Mettre à jour statistiques
        self.win_rate = recent_performance.get('win_rate', self.win_rate)
        self.avg_win = recent_performance.get('avg_win', self.avg_win)
        self.avg_loss = recent_performance.get('avg_loss', self.avg_loss)
        self.sharpe_ratio = recent_performance.get('sharpe', self.sharpe_ratio)
        
        # Ajuster paramètres si sous-performance
        if self.sharpe_ratio < 1.0:
            self._reduce_aggressiveness()
        elif self.sharpe_ratio > 2.0:
            self._increase_aggressiveness()
    
    def _reduce_aggressiveness(self):
        """Réduit agressivité si sous-performance"""
        # Augmenter seuils de confiance
        if 'min_confidence' in self.parameters:
            self.parameters['min_confidence'] = min(
                self.parameters['min_confidence'] * 1.1,
                0.8
            )
        
        # Réduire nombre de trades
        self.config.max_trades_per_day = max(
            int(self.config.max_trades_per_day * 0.8),
            1
        )
    
    def _increase_aggressiveness(self):
        """Augmente agressivité si sur-performance"""
        # Réduire seuils de confiance
        if 'min_confidence' in self.parameters:
            self.parameters['min_confidence'] = max(
                self.parameters['min_confidence'] * 0.9,
                0.5
            )
        
        # Augmenter nombre de trades
        self.config.max_trades_per_day = min(
            int(self.config.max_trades_per_day * 1.2),
            100
        )
    
    def record_trade(self, trade: Dict):
        """Enregistre trade fermé"""
        self.closed_trades.append(trade)
        
        # Mettre à jour statistiques
        self._update_statistics()
    
    def _update_statistics(self):
        """Met à jour statistiques de performance"""
        if len(self.closed_trades) < 10:
            return
        
        recent_trades = self.closed_trades[-30:]  # 30 derniers trades
        
        wins = [t for t in recent_trades if t['pnl'] > 0]
        losses = [t for t in recent_trades if t['pnl'] < 0]
        
        self.win_rate = len(wins) / len(recent_trades)
        self.avg_win = np.mean([t['pnl'] for t in wins]) if wins else 0
        self.avg_loss = np.mean([t['pnl'] for t in losses]) if losses else 0
        
        # Calculer Sharpe
        returns = [t['pnl'] / t['risk'] for t in recent_trades]
        self.sharpe_ratio = np.mean(returns) / np.std(returns) if np.std(returns) > 0 else 0

Scalping Strategy

Caractéristiques

  • Timeframe : 1-5 minutes
  • Holding Time : 5-30 minutes maximum
  • Risk per Trade : 0.5-1%
  • Win Rate Target : 60-70%
  • Profit Target : 0.3-0.5% par trade

Indicateurs Utilisés

# src/strategies/scalping/scalping_strategy.py

class ScalpingStrategy(BaseStrategy):
    """
    Stratégie de scalping basée sur:
    - Mean reversion (Bollinger Bands)
    - Momentum (RSI, MACD)
    - Volume profile
    - Order flow imbalance
    """
    
    def __init__(self, config: StrategyConfig):
        super().__init__(config)
        
        # Paramètres adaptatifs
        self.parameters = {
            'bb_period': 20,
            'bb_std': 2.0,
            'rsi_period': 14,
            'rsi_oversold': 30,
            'rsi_overbought': 70,
            'volume_threshold': 1.5,  # 1.5x volume moyen
            'min_confidence': 0.65,
        }
    
    def calculate_indicators(self, data: pd.DataFrame) -> pd.DataFrame:
        """Calcule indicateurs scalping"""
        df = data.copy()
        
        # Bollinger Bands
        bb_period = self.parameters['bb_period']
        bb_std = self.parameters['bb_std']
        
        df['bb_middle'] = df['close'].rolling(bb_period).mean()
        df['bb_std'] = df['close'].rolling(bb_period).std()
        df['bb_upper'] = df['bb_middle'] + (bb_std * df['bb_std'])
        df['bb_lower'] = df['bb_middle'] - (bb_std * df['bb_std'])
        df['bb_position'] = (df['close'] - df['bb_lower']) / (df['bb_upper'] - df['bb_lower'])
        
        # RSI
        rsi_period = self.parameters['rsi_period']
        delta = df['close'].diff()
        gain = (delta.where(delta > 0, 0)).rolling(rsi_period).mean()
        loss = (-delta.where(delta < 0, 0)).rolling(rsi_period).mean()
        rs = gain / loss
        df['rsi'] = 100 - (100 / (1 + rs))
        
        # MACD
        df['ema_12'] = df['close'].ewm(span=12).mean()
        df['ema_26'] = df['close'].ewm(span=26).mean()
        df['macd'] = df['ema_12'] - df['ema_26']
        df['macd_signal'] = df['macd'].ewm(span=9).mean()
        df['macd_hist'] = df['macd'] - df['macd_signal']
        
        # Volume
        df['volume_ma'] = df['volume'].rolling(20).mean()
        df['volume_ratio'] = df['volume'] / df['volume_ma']
        
        # ATR pour stop-loss
        df['tr'] = np.maximum(
            df['high'] - df['low'],
            np.maximum(
                abs(df['high'] - df['close'].shift(1)),
                abs(df['low'] - df['close'].shift(1))
            )
        )
        df['atr'] = df['tr'].rolling(14).mean()
        
        return df
    
    def analyze(self, market_data: pd.DataFrame) -> Optional[Signal]:
        """
        Génère signal de scalping
        
        Conditions LONG:
        - Prix proche BB lower (oversold)
        - RSI < 30 (oversold)
        - MACD histogram positif (momentum reversal)
        - Volume > 1.5x moyenne
        
        Conditions SHORT:
        - Prix proche BB upper (overbought)
        - RSI > 70 (overbought)
        - MACD histogram négatif
        - Volume > 1.5x moyenne
        """
        df = self.calculate_indicators(market_data)
        
        if len(df) < 50:
            return None
        
        current = df.iloc[-1]
        prev = df.iloc[-2]
        
        # Vérifier volume
        if current['volume_ratio'] < self.parameters['volume_threshold']:
            return None
        
        # Signal LONG
        if (current['bb_position'] < 0.2 and  # Proche BB lower
            current['rsi'] < self.parameters['rsi_oversold'] and
            current['macd_hist'] > 0 and prev['macd_hist'] <= 0):  # MACD cross
            
            confidence = self._calculate_confidence(df, 'LONG')
            
            if confidence >= self.parameters['min_confidence']:
                return Signal(
                    symbol=market_data.attrs.get('symbol', 'UNKNOWN'),
                    direction='LONG',
                    entry_price=current['close'],
                    stop_loss=current['close'] - (2 * current['atr']),
                    take_profit=current['close'] + (4 * current['atr']),  # R:R 2:1 (breakeven ~34%)
                    confidence=confidence,
                    timestamp=current.name,
                    strategy='scalping',
                    metadata={
                        'rsi': current['rsi'],
                        'bb_position': current['bb_position'],
                        'volume_ratio': current['volume_ratio']
                    }
                )
        
        # Signal SHORT
        elif (current['bb_position'] > 0.8 and  # Proche BB upper
              current['rsi'] > self.parameters['rsi_overbought'] and
              current['macd_hist'] < 0 and prev['macd_hist'] >= 0):
            
            confidence = self._calculate_confidence(df, 'SHORT')
            
            if confidence >= self.parameters['min_confidence']:
                return Signal(
                    symbol=market_data.attrs.get('symbol', 'UNKNOWN'),
                    direction='SHORT',
                    entry_price=current['close'],
                    stop_loss=current['close'] + (2 * current['atr']),
                    take_profit=current['close'] - (4 * current['atr']),  # R:R 2:1 (breakeven ~34%)
                    confidence=confidence,
                    timestamp=current.name,
                    strategy='scalping',
                    metadata={
                        'rsi': current['rsi'],
                        'bb_position': current['bb_position'],
                        'volume_ratio': current['volume_ratio']
                    }
                )
        
        return None
    
    def _calculate_confidence(self, df: pd.DataFrame, direction: str) -> float:
        """
        Calcule confiance du signal (0.0 à 1.0)
        
        Facteurs:
        - Force de l'oversold/overbought
        - Confirmation volume
        - Momentum MACD
        - Historique win rate
        """
        current = df.iloc[-1]
        
        confidence = 0.5  # Base
        
        if direction == 'LONG':
            # RSI oversold strength
            rsi_strength = (30 - current['rsi']) / 30
            confidence += 0.2 * max(0, rsi_strength)
            
            # BB position
            bb_strength = (0.2 - current['bb_position']) / 0.2
            confidence += 0.15 * max(0, bb_strength)
            
        else:  # SHORT
            # RSI overbought strength
            rsi_strength = (current['rsi'] - 70) / 30
            confidence += 0.2 * max(0, rsi_strength)
            
            # BB position
            bb_strength = (current['bb_position'] - 0.8) / 0.2
            confidence += 0.15 * max(0, bb_strength)
        
        # Volume confirmation
        volume_strength = min((current['volume_ratio'] - 1.5) / 1.5, 1.0)
        confidence += 0.15 * volume_strength
        
        # Historical win rate
        confidence += 0.1 * (self.win_rate - 0.5)
        
        return np.clip(confidence, 0.0, 1.0)

📈 Intraday Strategy

Caractéristiques

  • Timeframe : 15-60 minutes
  • Holding Time : 2-8 heures
  • Risk per Trade : 1-2%
  • Win Rate Target : 55-65%
  • Profit Target : 1-2% par trade

Implémentation

# src/strategies/intraday/intraday_strategy.py

class IntradayStrategy(BaseStrategy):
    """
    Stratégie intraday basée sur:
    - Trend following (EMA crossovers)
    - Support/Resistance
    - Volume analysis
    - Market regime
    """
    
    def __init__(self, config: StrategyConfig):
        super().__init__(config)
        
        self.parameters = {
            'ema_fast': 9,
            'ema_slow': 21,
            'ema_trend': 50,
            'atr_multiplier': 2.5,
            'volume_confirmation': 1.2,
            'min_confidence': 0.60,
        }
    
    def calculate_indicators(self, data: pd.DataFrame) -> pd.DataFrame:
        """Calcule indicateurs intraday"""
        df = data.copy()
        
        # EMAs
        df['ema_fast'] = df['close'].ewm(span=self.parameters['ema_fast']).mean()
        df['ema_slow'] = df['close'].ewm(span=self.parameters['ema_slow']).mean()
        df['ema_trend'] = df['close'].ewm(span=self.parameters['ema_trend']).mean()
        
        # Trend direction
        df['trend'] = np.where(df['ema_fast'] > df['ema_slow'], 1, -1)
        
        # ATR
        df['tr'] = np.maximum(
            df['high'] - df['low'],
            np.maximum(
                abs(df['high'] - df['close'].shift(1)),
                abs(df['low'] - df['close'].shift(1))
            )
        )
        df['atr'] = df['tr'].rolling(14).mean()
        
        # Support/Resistance (pivot points)
        df['pivot'] = (df['high'] + df['low'] + df['close']) / 3
        df['r1'] = 2 * df['pivot'] - df['low']
        df['s1'] = 2 * df['pivot'] - df['high']
        
        # Volume
        df['volume_ma'] = df['volume'].rolling(20).mean()
        df['volume_ratio'] = df['volume'] / df['volume_ma']
        
        # ADX (trend strength)
        df['adx'] = self._calculate_adx(df)
        
        return df
    
    def analyze(self, market_data: pd.DataFrame) -> Optional[Signal]:
        """
        Génère signal intraday
        
        Conditions LONG:
        - EMA fast cross above EMA slow
        - Prix au-dessus EMA trend (uptrend)
        - ADX > 25 (strong trend)
        - Volume confirmation
        
        Conditions SHORT:
        - EMA fast cross below EMA slow
        - Prix en-dessous EMA trend (downtrend)
        - ADX > 25
        - Volume confirmation
        """
        df = self.calculate_indicators(market_data)
        
        if len(df) < 100:
            return None
        
        current = df.iloc[-1]
        prev = df.iloc[-2]
        
        # Vérifier trend strength
        if current['adx'] < 25:
            return None
        
        # Signal LONG (bullish crossover)
        if (current['ema_fast'] > current['ema_slow'] and
            prev['ema_fast'] <= prev['ema_slow'] and
            current['close'] > current['ema_trend'] and
            current['volume_ratio'] > self.parameters['volume_confirmation']):
            
            confidence = self._calculate_confidence(df, 'LONG')
            
            if confidence >= self.parameters['min_confidence']:
                atr_mult = self.parameters['atr_multiplier']
                
                return Signal(
                    symbol=market_data.attrs.get('symbol', 'UNKNOWN'),
                    direction='LONG',
                    entry_price=current['close'],
                    stop_loss=current['close'] - (atr_mult * current['atr']),
                    take_profit=current['close'] + (atr_mult * 2 * current['atr']),
                    confidence=confidence,
                    timestamp=current.name,
                    strategy='intraday',
                    metadata={
                        'adx': current['adx'],
                        'trend': 'UP',
                        'volume_ratio': current['volume_ratio']
                    }
                )
        
        # Signal SHORT (bearish crossover)
        elif (current['ema_fast'] < current['ema_slow'] and
              prev['ema_fast'] >= prev['ema_slow'] and
              current['close'] < current['ema_trend'] and
              current['volume_ratio'] > self.parameters['volume_confirmation']):
            
            confidence = self._calculate_confidence(df, 'SHORT')
            
            if confidence >= self.parameters['min_confidence']:
                atr_mult = self.parameters['atr_multiplier']
                
                return Signal(
                    symbol=market_data.attrs.get('symbol', 'UNKNOWN'),
                    direction='SHORT',
                    entry_price=current['close'],
                    stop_loss=current['close'] + (atr_mult * current['atr']),
                    take_profit=current['close'] - (atr_mult * 2 * current['atr']),
                    confidence=confidence,
                    timestamp=current.name,
                    strategy='intraday',
                    metadata={
                        'adx': current['adx'],
                        'trend': 'DOWN',
                        'volume_ratio': current['volume_ratio']
                    }
                )
        
        return None
    
    def _calculate_adx(self, df: pd.DataFrame, period=14) -> pd.Series:
        """Calcule Average Directional Index"""
        # Simplified ADX calculation
        high_diff = df['high'].diff()
        low_diff = -df['low'].diff()
        
        pos_dm = np.where((high_diff > low_diff) & (high_diff > 0), high_diff, 0)
        neg_dm = np.where((low_diff > high_diff) & (low_diff > 0), low_diff, 0)
        
        tr = df['tr']
        
        pos_di = 100 * pd.Series(pos_dm).rolling(period).mean() / tr.rolling(period).mean()
        neg_di = 100 * pd.Series(neg_dm).rolling(period).mean() / tr.rolling(period).mean()
        
        dx = 100 * abs(pos_di - neg_di) / (pos_di + neg_di)
        adx = dx.rolling(period).mean()
        
        return adx
    
    def _calculate_confidence(self, df: pd.DataFrame, direction: str) -> float:
        """Calcule confiance signal intraday"""
        current = df.iloc[-1]
        
        confidence = 0.5
        
        # ADX strength
        adx_strength = min((current['adx'] - 25) / 25, 1.0)
        confidence += 0.2 * adx_strength
        
        # Volume confirmation
        volume_strength = min((current['volume_ratio'] - 1.2) / 1.0, 1.0)
        confidence += 0.15 * volume_strength
        
        # Trend alignment
        if direction == 'LONG':
            trend_alignment = (current['close'] - current['ema_trend']) / current['ema_trend']
        else:
            trend_alignment = (current['ema_trend'] - current['close']) / current['ema_trend']
        
        confidence += 0.15 * min(trend_alignment * 10, 1.0)
        
        # Historical performance
        confidence += 0.1 * (self.win_rate - 0.5)
        
        return np.clip(confidence, 0.0, 1.0)

🌊 Swing Strategy

Caractéristiques

  • Timeframe : 4H-1D
  • Holding Time : 2-5 jours
  • Risk per Trade : 2-3%
  • Win Rate Target : 50-60%
  • Profit Target : 3-5% par trade

Implémentation

# src/strategies/swing/swing_strategy.py

class SwingStrategy(BaseStrategy):
    """
    Stratégie swing basée sur:
    - Multi-timeframe analysis
    - Chart patterns
    - Fibonacci retracements
    - Macro trends
    """
    
    def __init__(self, config: StrategyConfig):
        super().__init__(config)
        
        self.parameters = {
            'sma_short': 20,
            'sma_long': 50,
            'rsi_period': 14,
            'macd_fast': 12,
            'macd_slow': 26,
            'macd_signal': 9,
            'min_confidence': 0.55,
        }
    
    def calculate_indicators(self, data: pd.DataFrame) -> pd.DataFrame:
        """Calcule indicateurs swing"""
        df = data.copy()
        
        # SMAs
        df['sma_short'] = df['close'].rolling(self.parameters['sma_short']).mean()
        df['sma_long'] = df['close'].rolling(self.parameters['sma_long']).mean()
        
        # RSI
        delta = df['close'].diff()
        gain = (delta.where(delta > 0, 0)).rolling(14).mean()
        loss = (-delta.where(delta < 0, 0)).rolling(14).mean()
        rs = gain / loss
        df['rsi'] = 100 - (100 / (1 + rs))
        
        # MACD
        df['ema_fast'] = df['close'].ewm(span=self.parameters['macd_fast']).mean()
        df['ema_slow'] = df['close'].ewm(span=self.parameters['macd_slow']).mean()
        df['macd'] = df['ema_fast'] - df['ema_slow']
        df['macd_signal'] = df['macd'].ewm(span=self.parameters['macd_signal']).mean()
        df['macd_hist'] = df['macd'] - df['macd_signal']
        
        # ATR
        df['tr'] = np.maximum(
            df['high'] - df['low'],
            np.maximum(
                abs(df['high'] - df['close'].shift(1)),
                abs(df['low'] - df['close'].shift(1))
            )
        )
        df['atr'] = df['tr'].rolling(14).mean()
        
        # Fibonacci levels (simplified)
        df['fib_high'] = df['high'].rolling(50).max()
        df['fib_low'] = df['low'].rolling(50).min()
        df['fib_range'] = df['fib_high'] - df['fib_low']
        df['fib_382'] = df['fib_high'] - 0.382 * df['fib_range']
        df['fib_618'] = df['fib_high'] - 0.618 * df['fib_range']
        
        return df
    
    def analyze(self, market_data: pd.DataFrame) -> Optional[Signal]:
        """
        Génère signal swing
        
        Conditions LONG:
        - SMA short > SMA long (uptrend)
        - RSI 40-60 (not overbought)
        - MACD bullish
        - Prix near Fibonacci support
        
        Conditions SHORT:
        - SMA short < SMA long (downtrend)
        - RSI 40-60 (not oversold)
        - MACD bearish
        - Prix near Fibonacci resistance
        """
        df = self.calculate_indicators(market_data)
        
        if len(df) < 100:
            return None
        
        current = df.iloc[-1]
        prev = df.iloc[-2]
        
        # Signal LONG
        if (current['sma_short'] > current['sma_long'] and
            40 < current['rsi'] < 60 and
            current['macd'] > current['macd_signal'] and
            abs(current['close'] - current['fib_618']) / current['close'] < 0.01):
            
            confidence = self._calculate_confidence(df, 'LONG')
            
            if confidence >= self.parameters['min_confidence']:
                return Signal(
                    symbol=market_data.attrs.get('symbol', 'UNKNOWN'),
                    direction='LONG',
                    entry_price=current['close'],
                    stop_loss=current['fib_low'],
                    take_profit=current['fib_high'],
                    confidence=confidence,
                    timestamp=current.name,
                    strategy='swing',
                    metadata={
                        'rsi': current['rsi'],
                        'macd_hist': current['macd_hist'],
                        'fib_level': 'support_618'
                    }
                )
        
        # Signal SHORT
        elif (current['sma_short'] < current['sma_long'] and
              40 < current['rsi'] < 60 and
              current['macd'] < current['macd_signal'] and
              abs(current['close'] - current['fib_382']) / current['close'] < 0.01):
            
            confidence = self._calculate_confidence(df, 'SHORT')
            
            if confidence >= self.parameters['min_confidence']:
                return Signal(
                    symbol=market_data.attrs.get('symbol', 'UNKNOWN'),
                    direction='SHORT',
                    entry_price=current['close'],
                    stop_loss=current['fib_high'],
                    take_profit=current['fib_low'],
                    confidence=confidence,
                    timestamp=current.name,
                    strategy='swing',
                    metadata={
                        'rsi': current['rsi'],
                        'macd_hist': current['macd_hist'],
                        'fib_level': 'resistance_382'
                    }
                )
        
        return None
    
    def _calculate_confidence(self, df: pd.DataFrame, direction: str) -> float:
        """Calcule confiance signal swing"""
        current = df.iloc[-1]
        
        confidence = 0.5
        
        # Trend strength
        sma_distance = abs(current['sma_short'] - current['sma_long']) / current['sma_long']
        confidence += 0.2 * min(sma_distance * 20, 1.0)
        
        # MACD strength
        macd_strength = abs(current['macd_hist']) / current['close']
        confidence += 0.15 * min(macd_strength * 100, 1.0)
        
        # RSI neutral zone (good for swing)
        rsi_score = 1 - abs(current['rsi'] - 50) / 50
        confidence += 0.15 * rsi_score
        
        # Historical performance
        confidence += 0.1 * (self.win_rate - 0.5)
        
        return np.clip(confidence, 0.0, 1.0)

ML-Driven Strategy (Phase 4b — 2026-03-08)

Concept

Contrairement aux stratégies règle-based (scalping/intraday/swing), la MLDrivenStrategy remplace les conditions codées en dur par un modèle XGBoost/LightGBM entraîné sur des données historiques.

Le modèle apprend quelles combinaisons d'indicateurs classiques sont réellement prédictives, reproduisant l'intuition d'un trader expérimenté.

Fichiers

Fichier Rôle
src/strategies/ml_driven/ml_strategy.py Stratégie (interface BaseStrategy)
src/ml/ml_strategy_model.py Entraînement + prédiction
src/ml/features/technical_features.py ~50 features TA
src/ml/features/label_generator.py Labels LONG/SHORT/NEUTRAL

Documentation complète : docs/ML_STRATEGY_GUIDE.md

Features utilisées

  • RSI : valeur, zones survente/surachat, divergences haussières/baissières
  • MACD : crossovers, histogramme, pente du momentum
  • Bollinger Bands : position relative, squeeze, cassures et rebonds
  • Supports/Résistances : distance aux niveaux pivots locaux (50 barres)
  • Points Pivots : classiques (R1/R2/S1/S2) + Fibonacci (38.2%, 61.8%, 100%)
  • ATR : volatilité normalisée, ratio vs moyenne
  • Chandeliers : marteau, étoile filante, engulfing haussier/baissier, doji
  • EMAs : alignement 8/21/50/200, distance % du prix, pente
  • Volume : ratio vs moyenne, pics, OBV
  • Sessions : Londres (8h-16h), NY (13h-21h), chevauchement (13h-16h)

Labels (supervision)

Pour chaque barre i, simulation forward sur N barres :

  • LONG (1) : HIGH atteint entry + tp_atr × ATR avant que LOW descende sous entry - sl_atr × ATR
  • SHORT (-1) : l'inverse
  • NEUTRAL (0) : ni TP ni SL dans l'horizon

Usage via API

# Entraîner
curl -X POST http://localhost:8100/trading/train \
  -H "Content-Type: application/json" \
  -d '{"symbol":"EURUSD","timeframe":"1h","period":"2y","model_type":"xgboost","tp_atr_mult":2.0,"sl_atr_mult":1.0}'

# Suivre l'entraînement
curl http://localhost:8100/trading/train/{job_id}

# Lister les modèles disponibles
curl http://localhost:8100/trading/ml-models

# Feature importance
curl http://localhost:8100/trading/ml-models/EURUSD/1h/importance

Paramètres de configuration

config = {
    'name':           'ml_driven',
    'symbol':         'EURUSD',
    'timeframe':      '1h',
    'risk_per_trade': 0.01,
    'model_type':     'xgboost',      # xgboost | lightgbm | random_forest
    'min_confidence': 0.55,           # Seuil de confiance minimum
    'tp_atr_mult':    2.0,            # TP = entry ± 2×ATR → R:R = 2:1
    'sl_atr_mult':    1.0,            # SL = entry ∓ 1×ATR
    'auto_load':      True,           # Charge le modèle existant au démarrage
}

Validation

Walk-forward cross-validation (3 folds temporels) — protège contre l'overfitting.

Métriques cibles :

  • wf_accuracy > 0.55
  • wf_precision > 0.50 sur signaux directionnels
  • Distribution LONG/SHORT équilibrée

Modèles sauvegardés

models/ml_strategy/
├── EURUSD_1h_xgboost.joblib
└── EURUSD_1h_xgboost_meta.json

Chargement automatique au démarrage si auto_load=True.