Initial commit — Trading AI Secure project complet
Architecture Docker (8 services), FastAPI, TimescaleDB, Redis, Streamlit. Stratégies : scalping, intraday, swing. MLEngine + RegimeDetector (HMM). BacktestEngine + WalkForwardAnalyzer + Optuna optimizer. Routes API complètes dont /optimize async. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
849
docs/STRATEGY_GUIDE.md
Normal file
849
docs/STRATEGY_GUIDE.md
Normal file
@@ -0,0 +1,849 @@
|
||||
# 📊 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'] + (3 * current['atr']), # R:R 1.5
|
||||
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'] - (3 * current['atr']),
|
||||
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)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
**Suite dans le prochain fichier...**
|
||||
Reference in New Issue
Block a user