# 📊 Guide des Stratégies - Trading AI Secure ## 📋 Table des Matières 1. [Vue d'ensemble](#vue-densemble) 2. [Architecture Stratégies](#architecture-stratégies) 3. [Scalping Strategy](#scalping-strategy) 4. [Intraday Strategy](#intraday-strategy) 5. [Swing Strategy](#swing-strategy) 6. [Paramètres Adaptatifs](#paramètres-adaptatifs) 7. [Combinaison Multi-Stratégie](#combinaison-multi-stratégie) 8. [Implémentation](#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 ```python # 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 ```python # 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 ```python # 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 ```python # 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](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 ```bash # 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 ```python 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`.