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

934 lines
32 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 📊 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`.