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:
798
docs/AI_FRAMEWORK.md
Normal file
798
docs/AI_FRAMEWORK.md
Normal file
@@ -0,0 +1,798 @@
|
||||
# 🤖 Framework IA Adaptative - Trading AI Secure
|
||||
|
||||
## 📋 Table des Matières
|
||||
1. [Vue d'ensemble](#vue-densemble)
|
||||
2. [Philosophie de l'IA Auto-Optimisante](#philosophie-de-lia-auto-optimisante)
|
||||
3. [Architecture ML](#architecture-ml)
|
||||
4. [Optimisation Continue des Paramètres](#optimisation-continue-des-paramètres)
|
||||
5. [Regime Detection](#regime-detection)
|
||||
6. [Position Sizing Adaptatif](#position-sizing-adaptatif)
|
||||
7. [Validation et Anti-Overfitting](#validation-et-anti-overfitting)
|
||||
8. [Implémentation Technique](#implémentation-technique)
|
||||
|
||||
---
|
||||
|
||||
## 🎯 Vue d'ensemble
|
||||
|
||||
Le framework IA de Trading AI Secure est conçu pour être **auto-adaptatif** et en **constante remise en question**. Contrairement aux systèmes traditionnels avec paramètres fixes, notre IA ajuste continuellement ses décisions en fonction :
|
||||
|
||||
- 📊 **Conditions de marché** (volatilité, tendance, liquidité)
|
||||
- 📈 **Performance récente** (win rate, Sharpe ratio, drawdown)
|
||||
- 🔗 **Corrélations inter-stratégies** (diversification)
|
||||
- ⚠️ **Métriques de risque** (VaR, CVaR, max drawdown)
|
||||
- 🌍 **Événements macro-économiques** (taux, inflation, sentiment)
|
||||
|
||||
---
|
||||
|
||||
## 🧠 Philosophie de l'IA Auto-Optimisante
|
||||
|
||||
### Principe Fondamental : "Doute Permanent"
|
||||
|
||||
Notre IA opère selon le principe du **doute méthodique** :
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────┐
|
||||
│ CYCLE D'AUTO-AMÉLIORATION CONTINUE (24h) │
|
||||
├─────────────────────────────────────────────────────────┤
|
||||
│ │
|
||||
│ 1. COLLECTE → Données marché + performance │
|
||||
│ 2. ANALYSE → Détection dégradation/amélioration │
|
||||
│ 3. HYPOTHÈSE → Nouveaux paramètres candidats │
|
||||
│ 4. TEST → Backtesting + Monte Carlo │
|
||||
│ 5. VALIDATION → A/B testing paper trading │
|
||||
│ 6. DÉPLOIEMENT → Adoption progressive si validé │
|
||||
│ 7. MONITORING → Surveillance performance │
|
||||
│ │
|
||||
│ ↻ RETOUR À L'ÉTAPE 1 │
|
||||
└─────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
### Questions Permanentes de l'IA
|
||||
|
||||
L'IA se pose continuellement ces questions :
|
||||
|
||||
1. **Mes paramètres actuels sont-ils toujours optimaux ?**
|
||||
- Comparaison performance vs. variantes
|
||||
- Détection de drift statistique
|
||||
|
||||
2. **Le régime de marché a-t-il changé ?**
|
||||
- Bull → Bear → Sideways
|
||||
- Haute volatilité → Basse volatilité
|
||||
- Trending → Mean-reverting
|
||||
|
||||
3. **Mes prédictions sont-elles calibrées ?**
|
||||
- Probabilités prédites vs. réalisées
|
||||
- Brier score, log-loss
|
||||
|
||||
4. **Mon sizing est-il adapté au risque actuel ?**
|
||||
- Kelly Criterion dynamique
|
||||
- Ajustement selon drawdown
|
||||
|
||||
5. **Existe-t-il de meilleures combinaisons de features ?**
|
||||
- Feature importance évolutive
|
||||
- Sélection automatique
|
||||
|
||||
---
|
||||
|
||||
## 🏗️ Architecture ML
|
||||
|
||||
### Stack Technologique
|
||||
|
||||
```python
|
||||
# Core ML
|
||||
scikit-learn==1.4.0 # Modèles de base
|
||||
xgboost==2.0.3 # Gradient boosting
|
||||
lightgbm==4.1.0 # Alternative rapide
|
||||
catboost==1.2.2 # Categorical features
|
||||
|
||||
# Optimisation
|
||||
optuna==3.5.0 # Bayesian optimization
|
||||
hyperopt==0.2.7 # Alternative optimization
|
||||
ray[tune]==2.9.0 # Distributed tuning
|
||||
|
||||
# Time Series
|
||||
statsmodels==0.14.1 # ARIMA, GARCH
|
||||
arch==6.2.0 # Volatility models
|
||||
prophet==1.1.5 # Forecasting
|
||||
|
||||
# Deep Learning (optionnel)
|
||||
tensorflow==2.15.0 # Neural networks
|
||||
pytorch==2.1.2 # Alternative DL
|
||||
keras-tuner==1.4.6 # Hyperparameter tuning
|
||||
|
||||
# Reinforcement Learning
|
||||
stable-baselines3==2.2.1 # RL algorithms
|
||||
gym==0.26.2 # RL environment
|
||||
```
|
||||
|
||||
### Pipeline ML Multi-Niveaux
|
||||
|
||||
```
|
||||
┌──────────────────────────────────────────────────────────────┐
|
||||
│ ENSEMBLE ADAPTATIF │
|
||||
├──────────────────────────────────────────────────────────────┤
|
||||
│ │
|
||||
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
|
||||
│ │ Modèle 1 │ │ Modèle 2 │ │ Modèle 3 │ │
|
||||
│ │ XGBoost │ │ LightGBM │ │ CatBoost │ │
|
||||
│ │ (Trending) │ │(Mean-Rev.) │ │ (Volatility)│ │
|
||||
│ └──────┬──────┘ └──────┬──────┘ └──────┬──────┘ │
|
||||
│ │ │ │ │
|
||||
│ └────────────────┼────────────────┘ │
|
||||
│ │ │
|
||||
│ ┌──────▼──────┐ │
|
||||
│ │ META- │ │
|
||||
│ │ LEARNER │ │
|
||||
│ │ (Stacking) │ │
|
||||
│ └──────┬──────┘ │
|
||||
│ │ │
|
||||
│ ┌──────▼──────┐ │
|
||||
│ │ REGIME │ │
|
||||
│ │ DETECTOR │ │
|
||||
│ │ (Weights) │ │
|
||||
│ └──────┬──────┘ │
|
||||
│ │ │
|
||||
│ ┌──────▼──────┐ │
|
||||
│ │ FINAL │ │
|
||||
│ │ DECISION │ │
|
||||
│ └─────────────┘ │
|
||||
└──────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## ⚙️ Optimisation Continue des Paramètres
|
||||
|
||||
### 1. Optimisation Bayésienne (Optuna)
|
||||
|
||||
**Fréquence** : Quotidienne (après clôture marché)
|
||||
|
||||
**Paramètres Optimisés** :
|
||||
|
||||
```python
|
||||
# Exemple configuration Optuna
|
||||
def objective(trial):
|
||||
params = {
|
||||
# Modèle
|
||||
'n_estimators': trial.suggest_int('n_estimators', 100, 1000),
|
||||
'max_depth': trial.suggest_int('max_depth', 3, 12),
|
||||
'learning_rate': trial.suggest_float('learning_rate', 0.001, 0.3, log=True),
|
||||
|
||||
# Features
|
||||
'lookback_period': trial.suggest_int('lookback_period', 10, 100),
|
||||
'volatility_window': trial.suggest_int('volatility_window', 5, 50),
|
||||
|
||||
# Trading
|
||||
'stop_loss_atr_mult': trial.suggest_float('stop_loss_atr_mult', 1.0, 5.0),
|
||||
'take_profit_ratio': trial.suggest_float('take_profit_ratio', 1.5, 5.0),
|
||||
'min_probability': trial.suggest_float('min_probability', 0.5, 0.8),
|
||||
|
||||
# Risk
|
||||
'kelly_fraction': trial.suggest_float('kelly_fraction', 0.1, 0.5),
|
||||
'max_position_size': trial.suggest_float('max_position_size', 0.01, 0.1),
|
||||
}
|
||||
|
||||
# Backtesting avec paramètres
|
||||
sharpe = backtest_strategy(params)
|
||||
return sharpe
|
||||
```
|
||||
|
||||
**Contraintes de Sécurité** :
|
||||
|
||||
```python
|
||||
# Limites strictes pour éviter paramètres dangereux
|
||||
PARAMETER_CONSTRAINTS = {
|
||||
'max_position_size': {'min': 0.01, 'max': 0.10}, # 1-10% max
|
||||
'stop_loss_atr_mult': {'min': 1.0, 'max': 5.0}, # Stop raisonnable
|
||||
'kelly_fraction': {'min': 0.1, 'max': 0.5}, # Kelly conservateur
|
||||
'min_probability': {'min': 0.5, 'max': 0.9}, # Seuil décision
|
||||
}
|
||||
```
|
||||
|
||||
### 2. A/B Testing Automatique
|
||||
|
||||
**Principe** : Tester simultanément plusieurs variantes de stratégies
|
||||
|
||||
```python
|
||||
class ABTestingEngine:
|
||||
"""
|
||||
Teste 2-3 variantes de paramètres en parallèle
|
||||
sur paper trading pendant 7 jours
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
self.variants = {
|
||||
'control': current_params, # Paramètres actuels
|
||||
'variant_a': optimized_params_1, # Optuna suggestion 1
|
||||
'variant_b': optimized_params_2, # Optuna suggestion 2
|
||||
}
|
||||
|
||||
def allocate_capital(self):
|
||||
"""Allocation capital par variante"""
|
||||
return {
|
||||
'control': 0.50, # 50% capital actuel
|
||||
'variant_a': 0.25, # 25% variante A
|
||||
'variant_b': 0.25, # 25% variante B
|
||||
}
|
||||
|
||||
def evaluate_winner(self, results: Dict):
|
||||
"""
|
||||
Critères de sélection :
|
||||
- Sharpe Ratio > control + 10%
|
||||
- Max Drawdown < control
|
||||
- Win Rate > control
|
||||
- Profit Factor > control
|
||||
"""
|
||||
winner = max(results, key=lambda x: results[x]['sharpe'])
|
||||
|
||||
if self.is_significantly_better(winner, 'control'):
|
||||
return winner
|
||||
return 'control' # Conserver actuel si pas mieux
|
||||
```
|
||||
|
||||
### 3. Reinforcement Learning pour Position Sizing
|
||||
|
||||
**Approche** : Agent RL apprend le sizing optimal
|
||||
|
||||
```python
|
||||
import gym
|
||||
from stable_baselines3 import PPO
|
||||
|
||||
class TradingEnvironment(gym.Env):
|
||||
"""
|
||||
Environment RL pour position sizing
|
||||
|
||||
State: [portfolio_value, current_drawdown, volatility,
|
||||
win_rate_recent, correlation_portfolio, regime]
|
||||
|
||||
Action: position_size (0.0 à max_position_size)
|
||||
|
||||
Reward: Sharpe ratio - penalty(drawdown) - penalty(correlation)
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
self.action_space = gym.spaces.Box(
|
||||
low=0.0, high=0.1, shape=(1,)
|
||||
)
|
||||
self.observation_space = gym.spaces.Box(
|
||||
low=-np.inf, high=np.inf, shape=(6,)
|
||||
)
|
||||
|
||||
def step(self, action):
|
||||
position_size = action[0]
|
||||
|
||||
# Simuler trade avec ce sizing
|
||||
pnl = self.simulate_trade(position_size)
|
||||
|
||||
# Calculer reward
|
||||
reward = self.calculate_reward(pnl, position_size)
|
||||
|
||||
return next_state, reward, done, info
|
||||
|
||||
def calculate_reward(self, pnl, position_size):
|
||||
"""
|
||||
Reward = PnL ajusté du risque
|
||||
Pénalités :
|
||||
- Drawdown excessif
|
||||
- Position trop grande
|
||||
- Corrélation élevée
|
||||
"""
|
||||
reward = pnl
|
||||
|
||||
if self.current_drawdown > 0.05:
|
||||
reward -= 10 * self.current_drawdown
|
||||
|
||||
if position_size > 0.05:
|
||||
reward -= 5 * (position_size - 0.05)
|
||||
|
||||
return reward
|
||||
|
||||
# Entraînement
|
||||
env = TradingEnvironment()
|
||||
model = PPO("MlpPolicy", env, verbose=1)
|
||||
model.learn(total_timesteps=100000)
|
||||
```
|
||||
|
||||
### 4. Parameter Drift Detection
|
||||
|
||||
**Objectif** : Détecter quand paramètres deviennent obsolètes
|
||||
|
||||
```python
|
||||
from scipy import stats
|
||||
|
||||
class ParameterDriftDetector:
|
||||
"""
|
||||
Détecte changements statistiques dans performance
|
||||
"""
|
||||
|
||||
def __init__(self, window=30):
|
||||
self.window = window
|
||||
self.historical_sharpe = []
|
||||
|
||||
def detect_drift(self, current_sharpe: float) -> bool:
|
||||
"""
|
||||
Test statistique : performance actuelle vs. historique
|
||||
"""
|
||||
self.historical_sharpe.append(current_sharpe)
|
||||
|
||||
if len(self.historical_sharpe) < self.window:
|
||||
return False
|
||||
|
||||
# Test t de Student
|
||||
recent = self.historical_sharpe[-7:] # 7 derniers jours
|
||||
baseline = self.historical_sharpe[-self.window:-7]
|
||||
|
||||
t_stat, p_value = stats.ttest_ind(recent, baseline)
|
||||
|
||||
# Drift détecté si p < 0.05 et performance dégradée
|
||||
if p_value < 0.05 and np.mean(recent) < np.mean(baseline):
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
def trigger_reoptimization(self):
|
||||
"""Lance optimisation Optuna si drift détecté"""
|
||||
logger.warning("Parameter drift detected! Triggering reoptimization...")
|
||||
run_optuna_optimization()
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🎭 Regime Detection
|
||||
|
||||
### Détection de Régimes de Marché
|
||||
|
||||
**Objectif** : Adapter stratégies selon régime (Bull/Bear/Sideways)
|
||||
|
||||
```python
|
||||
from hmmlearn import hmm
|
||||
import numpy as np
|
||||
|
||||
class MarketRegimeDetector:
|
||||
"""
|
||||
Hidden Markov Model pour détecter régimes
|
||||
|
||||
États :
|
||||
- 0: Bull Market (trending up, low volatility)
|
||||
- 1: Bear Market (trending down, high volatility)
|
||||
- 2: Sideways (no trend, medium volatility)
|
||||
"""
|
||||
|
||||
def __init__(self, n_regimes=3):
|
||||
self.model = hmm.GaussianHMM(
|
||||
n_components=n_regimes,
|
||||
covariance_type="full",
|
||||
n_iter=1000
|
||||
)
|
||||
|
||||
def fit(self, returns, volatility):
|
||||
"""
|
||||
Entraîne HMM sur données historiques
|
||||
"""
|
||||
features = np.column_stack([returns, volatility])
|
||||
self.model.fit(features)
|
||||
|
||||
def predict_regime(self, recent_returns, recent_volatility):
|
||||
"""
|
||||
Prédit régime actuel
|
||||
"""
|
||||
features = np.column_stack([recent_returns, recent_volatility])
|
||||
regime = self.model.predict(features)[-1]
|
||||
|
||||
regime_names = {0: 'BULL', 1: 'BEAR', 2: 'SIDEWAYS'}
|
||||
return regime_names[regime]
|
||||
|
||||
def get_regime_probabilities(self, recent_data):
|
||||
"""
|
||||
Probabilités de chaque régime
|
||||
"""
|
||||
return self.model.predict_proba(recent_data)[-1]
|
||||
```
|
||||
|
||||
### Adaptation Stratégies par Régime
|
||||
|
||||
```python
|
||||
REGIME_STRATEGY_WEIGHTS = {
|
||||
'BULL': {
|
||||
'scalping': 0.2,
|
||||
'intraday': 0.5, # Favoriser intraday en bull
|
||||
'swing': 0.3,
|
||||
},
|
||||
'BEAR': {
|
||||
'scalping': 0.4, # Favoriser scalping en bear
|
||||
'intraday': 0.3,
|
||||
'swing': 0.1, # Réduire swing en bear
|
||||
'short_bias': 0.2, # Activer short bias
|
||||
},
|
||||
'SIDEWAYS': {
|
||||
'scalping': 0.5, # Favoriser scalping en sideways
|
||||
'intraday': 0.3,
|
||||
'swing': 0.2,
|
||||
}
|
||||
}
|
||||
|
||||
def adjust_strategy_allocation(regime: str) -> Dict[str, float]:
|
||||
"""
|
||||
Ajuste allocation capital par stratégie selon régime
|
||||
"""
|
||||
return REGIME_STRATEGY_WEIGHTS[regime]
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📏 Position Sizing Adaptatif
|
||||
|
||||
### Kelly Criterion Dynamique
|
||||
|
||||
```python
|
||||
class AdaptiveKellyCriterion:
|
||||
"""
|
||||
Kelly Criterion avec ajustements dynamiques
|
||||
|
||||
Kelly% = (p * b - q) / b
|
||||
où :
|
||||
- p = probabilité de gain (prédite par ML)
|
||||
- q = probabilité de perte (1 - p)
|
||||
- b = ratio gain/perte moyen
|
||||
"""
|
||||
|
||||
def __init__(self, kelly_fraction=0.25):
|
||||
self.kelly_fraction = kelly_fraction # Fraction conservatrice
|
||||
|
||||
def calculate_position_size(
|
||||
self,
|
||||
win_probability: float,
|
||||
avg_win: float,
|
||||
avg_loss: float,
|
||||
current_drawdown: float,
|
||||
portfolio_volatility: float
|
||||
) -> float:
|
||||
"""
|
||||
Calcule taille position optimale
|
||||
"""
|
||||
# Kelly de base
|
||||
b = avg_win / abs(avg_loss)
|
||||
kelly = (win_probability * b - (1 - win_probability)) / b
|
||||
|
||||
# Ajustements dynamiques
|
||||
kelly = self._adjust_for_drawdown(kelly, current_drawdown)
|
||||
kelly = self._adjust_for_volatility(kelly, portfolio_volatility)
|
||||
kelly = self._adjust_for_confidence(kelly, win_probability)
|
||||
|
||||
# Appliquer fraction conservatrice
|
||||
position_size = kelly * self.kelly_fraction
|
||||
|
||||
# Limites strictes
|
||||
return np.clip(position_size, 0.01, 0.10)
|
||||
|
||||
def _adjust_for_drawdown(self, kelly: float, drawdown: float) -> float:
|
||||
"""
|
||||
Réduire sizing si drawdown élevé
|
||||
"""
|
||||
if drawdown > 0.05: # > 5% drawdown
|
||||
reduction = 1 - (drawdown / 0.10) # Réduction linéaire
|
||||
kelly *= max(reduction, 0.5) # Min 50% du Kelly
|
||||
return kelly
|
||||
|
||||
def _adjust_for_volatility(self, kelly: float, volatility: float) -> float:
|
||||
"""
|
||||
Réduire sizing si volatilité élevée
|
||||
"""
|
||||
if volatility > 0.02: # > 2% volatilité quotidienne
|
||||
kelly *= (0.02 / volatility)
|
||||
return kelly
|
||||
|
||||
def _adjust_for_confidence(self, kelly: float, probability: float) -> float:
|
||||
"""
|
||||
Réduire sizing si faible confiance
|
||||
"""
|
||||
if probability < 0.6:
|
||||
kelly *= (probability / 0.6)
|
||||
return kelly
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## ✅ Validation et Anti-Overfitting
|
||||
|
||||
### Walk-Forward Analysis
|
||||
|
||||
```python
|
||||
class WalkForwardValidator:
|
||||
"""
|
||||
Validation temporelle rigoureuse
|
||||
|
||||
Principe :
|
||||
1. Entraîner sur fenêtre N
|
||||
2. Tester sur fenêtre N+1
|
||||
3. Glisser fenêtre
|
||||
4. Répéter
|
||||
"""
|
||||
|
||||
def __init__(self, train_window=252, test_window=63):
|
||||
self.train_window = train_window # 1 an
|
||||
self.test_window = test_window # 3 mois
|
||||
|
||||
def validate(self, data, strategy):
|
||||
"""
|
||||
Effectue walk-forward analysis
|
||||
"""
|
||||
results = []
|
||||
|
||||
for i in range(0, len(data) - self.train_window - self.test_window, self.test_window):
|
||||
# Fenêtre entraînement
|
||||
train_data = data[i:i+self.train_window]
|
||||
|
||||
# Fenêtre test
|
||||
test_data = data[i+self.train_window:i+self.train_window+self.test_window]
|
||||
|
||||
# Entraîner
|
||||
strategy.fit(train_data)
|
||||
|
||||
# Tester
|
||||
performance = strategy.backtest(test_data)
|
||||
results.append(performance)
|
||||
|
||||
return self._aggregate_results(results)
|
||||
|
||||
def _aggregate_results(self, results):
|
||||
"""
|
||||
Agrège résultats walk-forward
|
||||
"""
|
||||
return {
|
||||
'mean_sharpe': np.mean([r['sharpe'] for r in results]),
|
||||
'std_sharpe': np.std([r['sharpe'] for r in results]),
|
||||
'mean_drawdown': np.mean([r['max_drawdown'] for r in results]),
|
||||
'worst_period': min(results, key=lambda x: x['sharpe']),
|
||||
}
|
||||
```
|
||||
|
||||
### Monte Carlo Simulation
|
||||
|
||||
```python
|
||||
class MonteCarloValidator:
|
||||
"""
|
||||
Simulation Monte Carlo pour robustesse
|
||||
"""
|
||||
|
||||
def __init__(self, n_simulations=10000):
|
||||
self.n_simulations = n_simulations
|
||||
|
||||
def simulate(self, strategy, historical_trades):
|
||||
"""
|
||||
Simule N scénarios en réordonnant trades
|
||||
"""
|
||||
results = []
|
||||
|
||||
for _ in range(self.n_simulations):
|
||||
# Réordonner trades aléatoirement
|
||||
shuffled_trades = np.random.permutation(historical_trades)
|
||||
|
||||
# Calculer métriques
|
||||
sharpe = self._calculate_sharpe(shuffled_trades)
|
||||
max_dd = self._calculate_max_drawdown(shuffled_trades)
|
||||
|
||||
results.append({'sharpe': sharpe, 'max_dd': max_dd})
|
||||
|
||||
return self._analyze_distribution(results)
|
||||
|
||||
def _analyze_distribution(self, results):
|
||||
"""
|
||||
Analyse distribution résultats
|
||||
"""
|
||||
sharpes = [r['sharpe'] for r in results]
|
||||
drawdowns = [r['max_dd'] for r in results]
|
||||
|
||||
return {
|
||||
'sharpe_mean': np.mean(sharpes),
|
||||
'sharpe_5th_percentile': np.percentile(sharpes, 5),
|
||||
'sharpe_95th_percentile': np.percentile(sharpes, 95),
|
||||
'max_dd_mean': np.mean(drawdowns),
|
||||
'max_dd_95th_percentile': np.percentile(drawdowns, 95),
|
||||
'probability_positive_sharpe': np.mean(np.array(sharpes) > 0),
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 💻 Implémentation Technique
|
||||
|
||||
### Architecture Complète
|
||||
|
||||
```python
|
||||
# src/ml/adaptive_ai_engine.py
|
||||
|
||||
from typing import Dict, List, Optional
|
||||
import optuna
|
||||
import numpy as np
|
||||
from dataclasses import dataclass
|
||||
|
||||
@dataclass
|
||||
class AIConfig:
|
||||
"""Configuration IA adaptative"""
|
||||
optimization_frequency: str = 'daily' # daily, weekly
|
||||
ab_test_duration_days: int = 7
|
||||
parameter_drift_window: int = 30
|
||||
kelly_fraction: float = 0.25
|
||||
min_sharpe_improvement: float = 0.1 # 10% amélioration minimum
|
||||
|
||||
class AdaptiveAIEngine:
|
||||
"""
|
||||
Moteur IA adaptatif central
|
||||
|
||||
Responsabilités :
|
||||
- Optimisation continue paramètres
|
||||
- Détection régimes
|
||||
- Position sizing adaptatif
|
||||
- Validation anti-overfitting
|
||||
"""
|
||||
|
||||
def __init__(self, config: AIConfig):
|
||||
self.config = config
|
||||
|
||||
# Composants
|
||||
self.optimizer = OptunaOptimizer()
|
||||
self.regime_detector = MarketRegimeDetector()
|
||||
self.kelly_calculator = AdaptiveKellyCriterion(config.kelly_fraction)
|
||||
self.drift_detector = ParameterDriftDetector()
|
||||
self.ab_tester = ABTestingEngine()
|
||||
|
||||
# État
|
||||
self.current_params = {}
|
||||
self.current_regime = 'SIDEWAYS'
|
||||
self.performance_history = []
|
||||
|
||||
async def daily_optimization_cycle(self):
|
||||
"""
|
||||
Cycle d'optimisation quotidien
|
||||
"""
|
||||
logger.info("Starting daily optimization cycle...")
|
||||
|
||||
# 1. Détecter drift
|
||||
if self.drift_detector.detect_drift(self.get_recent_sharpe()):
|
||||
logger.warning("Parameter drift detected!")
|
||||
|
||||
# 2. Optimiser nouveaux paramètres
|
||||
new_params = await self.optimizer.optimize()
|
||||
|
||||
# 3. Valider avec walk-forward
|
||||
if self._validate_params(new_params):
|
||||
# 4. Lancer A/B test
|
||||
self.ab_tester.add_variant('optimized', new_params)
|
||||
|
||||
# 5. Détecter régime
|
||||
self.current_regime = self.regime_detector.predict_regime(
|
||||
self.get_recent_returns(),
|
||||
self.get_recent_volatility()
|
||||
)
|
||||
|
||||
# 6. Ajuster allocations
|
||||
self._adjust_strategy_weights(self.current_regime)
|
||||
|
||||
logger.info(f"Optimization cycle complete. Regime: {self.current_regime}")
|
||||
|
||||
def calculate_position_size(
|
||||
self,
|
||||
signal_probability: float,
|
||||
current_price: float,
|
||||
portfolio_value: float
|
||||
) -> float:
|
||||
"""
|
||||
Calcule taille position optimale
|
||||
"""
|
||||
# Métriques actuelles
|
||||
current_dd = self.get_current_drawdown()
|
||||
portfolio_vol = self.get_portfolio_volatility()
|
||||
|
||||
# Kelly adaptatif
|
||||
kelly_size = self.kelly_calculator.calculate_position_size(
|
||||
win_probability=signal_probability,
|
||||
avg_win=self.get_avg_win(),
|
||||
avg_loss=self.get_avg_loss(),
|
||||
current_drawdown=current_dd,
|
||||
portfolio_volatility=portfolio_vol
|
||||
)
|
||||
|
||||
# Convertir en nombre d'unités
|
||||
position_value = portfolio_value * kelly_size
|
||||
units = position_value / current_price
|
||||
|
||||
return units
|
||||
|
||||
def _validate_params(self, params: Dict) -> bool:
|
||||
"""
|
||||
Valide nouveaux paramètres
|
||||
"""
|
||||
validator = WalkForwardValidator()
|
||||
results = validator.validate(self.get_historical_data(), params)
|
||||
|
||||
# Critères validation
|
||||
if results['mean_sharpe'] < 1.5:
|
||||
return False
|
||||
if results['mean_drawdown'] > 0.10:
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
def get_model_explanation(self, prediction: float) -> Dict:
|
||||
"""
|
||||
Explique décision du modèle (SHAP values)
|
||||
"""
|
||||
import shap
|
||||
|
||||
explainer = shap.TreeExplainer(self.model)
|
||||
shap_values = explainer.shap_values(self.current_features)
|
||||
|
||||
return {
|
||||
'prediction': prediction,
|
||||
'feature_importance': dict(zip(
|
||||
self.feature_names,
|
||||
shap_values[0]
|
||||
)),
|
||||
'base_value': explainer.expected_value
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📊 Métriques de Monitoring IA
|
||||
|
||||
### Dashboard Métriques
|
||||
|
||||
```python
|
||||
AI_METRICS = {
|
||||
'optimization': {
|
||||
'last_optimization_date': datetime,
|
||||
'optimization_frequency': str,
|
||||
'parameters_changed': int,
|
||||
'improvement_sharpe': float,
|
||||
},
|
||||
'regime_detection': {
|
||||
'current_regime': str,
|
||||
'regime_probability': float,
|
||||
'regime_changes_last_30d': int,
|
||||
},
|
||||
'parameter_drift': {
|
||||
'drift_detected': bool,
|
||||
'drift_magnitude': float,
|
||||
'days_since_last_drift': int,
|
||||
},
|
||||
'ab_testing': {
|
||||
'active_tests': int,
|
||||
'winning_variant': str,
|
||||
'improvement_vs_control': float,
|
||||
},
|
||||
'model_performance': {
|
||||
'sharpe_ratio_7d': float,
|
||||
'sharpe_ratio_30d': float,
|
||||
'calibration_score': float, # Brier score
|
||||
'feature_stability': float,
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🚀 Prochaines Étapes
|
||||
|
||||
### Phase 1 : Implémentation de Base
|
||||
- [ ] Optimisation Optuna basique
|
||||
- [ ] Regime detection HMM
|
||||
- [ ] Kelly Criterion adaptatif
|
||||
- [ ] Walk-forward validation
|
||||
|
||||
### Phase 2 : Fonctionnalités Avancées
|
||||
- [ ] A/B testing automatique
|
||||
- [ ] Reinforcement Learning sizing
|
||||
- [ ] Parameter drift detection
|
||||
- [ ] SHAP explainability
|
||||
|
||||
### Phase 3 : Production
|
||||
- [ ] Monitoring temps réel
|
||||
- [ ] Alertes dégradation
|
||||
- [ ] Auto-retraining pipeline
|
||||
- [ ] Audit trail complet
|
||||
|
||||
---
|
||||
|
||||
**Note** : Ce framework garantit que l'IA reste **adaptative** et **auto-critique**, ajustant continuellement ses décisions pour maximiser la performance ajustée du risque.
|
||||
575
docs/ARCHITECTURE.md
Normal file
575
docs/ARCHITECTURE.md
Normal file
@@ -0,0 +1,575 @@
|
||||
# 🏗️ Architecture Détaillée - Trading AI Secure
|
||||
|
||||
## 📋 Table des Matières
|
||||
1. [Vue d'ensemble](#vue-densemble)
|
||||
2. [Architecture Globale](#architecture-globale)
|
||||
3. [Modules Core](#modules-core)
|
||||
4. [Flux de Données](#flux-de-données)
|
||||
5. [Patterns et Principes](#patterns-et-principes)
|
||||
6. [Sécurité](#sécurité)
|
||||
7. [Scalabilité](#scalabilité)
|
||||
|
||||
---
|
||||
|
||||
## 🎯 Vue d'ensemble
|
||||
|
||||
### Principes Architecturaux
|
||||
|
||||
1. **Separation of Concerns** : Chaque module a une responsabilité unique
|
||||
2. **Dependency Injection** : Facilite tests et modularité
|
||||
3. **Event-Driven** : Communication asynchrone entre composants
|
||||
4. **Fail-Safe** : Dégradation gracieuse en cas d'erreur
|
||||
5. **Observable** : Monitoring et logging à tous les niveaux
|
||||
|
||||
### Stack Technologique
|
||||
|
||||
```yaml
|
||||
Backend:
|
||||
Language: Python 3.11+
|
||||
Framework: FastAPI
|
||||
Async: asyncio + threading
|
||||
|
||||
Data:
|
||||
Storage: PostgreSQL (positions, trades)
|
||||
Cache: Redis (market data, signals)
|
||||
Time-Series: InfluxDB (métriques)
|
||||
|
||||
ML/AI:
|
||||
Core: scikit-learn, XGBoost, LightGBM
|
||||
Optimization: Optuna
|
||||
Deep Learning: TensorFlow/PyTorch (optionnel)
|
||||
|
||||
Monitoring:
|
||||
Metrics: Prometheus
|
||||
Visualization: Grafana
|
||||
Logging: ELK Stack (Elasticsearch, Logstash, Kibana)
|
||||
|
||||
UI:
|
||||
Dashboard: Streamlit
|
||||
API Docs: Swagger/OpenAPI
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🏛️ Architecture Globale
|
||||
|
||||
### Diagramme de Haut Niveau
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────────┐
|
||||
│ TRADING AI SECURE │
|
||||
├─────────────────────────────────────────────────────────────────┤
|
||||
│ │
|
||||
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │
|
||||
│ │ UI LAYER │ │ API LAYER │ │ MONITORING │ │
|
||||
│ │ │ │ │ │ │ │
|
||||
│ │ Streamlit │ │ FastAPI │ │ Prometheus │ │
|
||||
│ │ Dashboard │ │ REST API │ │ Grafana │ │
|
||||
│ └──────┬───────┘ └──────┬───────┘ └──────┬───────┘ │
|
||||
│ │ │ │ │
|
||||
│ └─────────────────┼─────────────────┘ │
|
||||
│ │ │
|
||||
│ ┌────────────────────────▼────────────────────────┐ │
|
||||
│ │ ORCHESTRATION LAYER │ │
|
||||
│ │ │ │
|
||||
│ │ ┌──────────────┐ ┌──────────────┐ │ │
|
||||
│ │ │ Strategy │ │ Risk │ │ │
|
||||
│ │ │ Engine │ │ Manager │ │ │
|
||||
│ │ │ (Singleton) │ │ (Singleton) │ │ │
|
||||
│ │ └──────┬───────┘ └──────┬───────┘ │ │
|
||||
│ │ │ │ │ │
|
||||
│ │ └────────┬────────┘ │ │
|
||||
│ │ │ │ │
|
||||
│ └──────────────────┼──────────────────────────────┘ │
|
||||
│ │ │
|
||||
│ ┌──────────────────▼──────────────────────────────┐ │
|
||||
│ │ CORE SERVICES │ │
|
||||
│ │ │ │
|
||||
│ │ ┌──────────┐ ┌──────────┐ ┌──────────┐ │ │
|
||||
│ │ │ Data │ │ ML │ │ Order │ │ │
|
||||
│ │ │ Service │ │ Engine │ │ Manager │ │ │
|
||||
│ │ └────┬─────┘ └────┬─────┘ └────┬─────┘ │ │
|
||||
│ │ │ │ │ │ │
|
||||
│ └───────┼─────────────┼─────────────┼─────────────┘ │
|
||||
│ │ │ │ │
|
||||
│ ┌───────▼─────────────▼─────────────▼─────────────┐ │
|
||||
│ │ DATA & INTEGRATION LAYER │ │
|
||||
│ │ │ │
|
||||
│ │ ┌──────────┐ ┌──────────┐ ┌──────────┐ │ │
|
||||
│ │ │ Market │ │ IG │ │ Cache │ │ │
|
||||
│ │ │ Data │ │ API │ │ (Redis) │ │ │
|
||||
│ │ │ Sources │ │Connector │ │ │ │ │
|
||||
│ │ └──────────┘ └──────────┘ └──────────┘ │ │
|
||||
│ │ │ │
|
||||
│ └──────────────────────────────────────────────────┘ │
|
||||
│ │
|
||||
│ ┌──────────────────────────────────────────────────┐ │
|
||||
│ │ PERSISTENCE LAYER │ │
|
||||
│ │ │ │
|
||||
│ │ ┌──────────┐ ┌──────────┐ ┌──────────┐ │ │
|
||||
│ │ │PostgreSQL│ │ InfluxDB │ │ File │ │ │
|
||||
│ │ │(Trades, │ │(Metrics, │ │ System │ │ │
|
||||
│ │ │Positions)│ │TimeSeries│ │ (Logs) │ │ │
|
||||
│ │ └──────────┘ └──────────┘ └──────────┘ │ │
|
||||
│ └──────────────────────────────────────────────────┘ │
|
||||
│ │
|
||||
└─────────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🔧 Modules Core
|
||||
|
||||
### 1. Strategy Engine
|
||||
|
||||
**Responsabilité** : Orchestration des stratégies de trading
|
||||
|
||||
```python
|
||||
# src/core/strategy_engine.py
|
||||
|
||||
class StrategyEngine:
|
||||
"""
|
||||
Moteur central de gestion des stratégies
|
||||
|
||||
Responsabilités:
|
||||
- Charger et initialiser stratégies
|
||||
- Distribuer données marché
|
||||
- Collecter signaux
|
||||
- Coordonner exécution
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
self.strategies: Dict[str, BaseStrategy] = {}
|
||||
self.active_signals: List[Signal] = []
|
||||
self.risk_manager = RiskManager()
|
||||
self.order_manager = OrderManager()
|
||||
|
||||
async def run(self):
|
||||
"""Boucle principale"""
|
||||
while True:
|
||||
# 1. Récupérer données marché
|
||||
market_data = await self.fetch_market_data()
|
||||
|
||||
# 2. Analyser avec chaque stratégie
|
||||
signals = await self.analyze_strategies(market_data)
|
||||
|
||||
# 3. Filtrer avec risk manager
|
||||
valid_signals = self.risk_manager.filter_signals(signals)
|
||||
|
||||
# 4. Exécuter signaux valides
|
||||
await self.execute_signals(valid_signals)
|
||||
|
||||
# 5. Mettre à jour positions
|
||||
await self.update_positions()
|
||||
|
||||
# 6. Sleep jusqu'à prochaine itération
|
||||
await asyncio.sleep(self.interval)
|
||||
```
|
||||
|
||||
### 2. Risk Manager (Singleton)
|
||||
|
||||
**Responsabilité** : Gestion centralisée du risque
|
||||
|
||||
```python
|
||||
# src/core/risk_manager.py
|
||||
|
||||
class RiskManager:
|
||||
"""
|
||||
Singleton Risk Manager
|
||||
|
||||
Garantit:
|
||||
- Une seule instance
|
||||
- État global cohérent
|
||||
- Thread-safe
|
||||
"""
|
||||
|
||||
_instance = None
|
||||
_lock = threading.Lock()
|
||||
|
||||
def __new__(cls):
|
||||
if cls._instance is None:
|
||||
with cls._lock:
|
||||
if cls._instance is None:
|
||||
cls._instance = super().__new__(cls)
|
||||
return cls._instance
|
||||
|
||||
def __init__(self):
|
||||
if not hasattr(self, 'initialized'):
|
||||
self.initialized = True
|
||||
self.positions = {}
|
||||
self.portfolio_value = 0.0
|
||||
self.peak_value = 0.0
|
||||
# ... autres attributs
|
||||
```
|
||||
|
||||
### 3. Data Service
|
||||
|
||||
**Responsabilité** : Abstraction des sources de données
|
||||
|
||||
```python
|
||||
# src/data/data_service.py
|
||||
|
||||
class DataService:
|
||||
"""
|
||||
Service unifié d'accès aux données
|
||||
|
||||
Features:
|
||||
- Multi-source avec failover
|
||||
- Cache intelligent
|
||||
- Validation données
|
||||
- Rate limiting
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
self.sources = self._initialize_sources()
|
||||
self.cache = RedisCache()
|
||||
self.validator = DataValidator()
|
||||
|
||||
async def get_market_data(
|
||||
self,
|
||||
symbol: str,
|
||||
timeframe: str,
|
||||
start: datetime,
|
||||
end: datetime
|
||||
) -> pd.DataFrame:
|
||||
"""
|
||||
Récupère données marché avec failover
|
||||
"""
|
||||
# 1. Check cache
|
||||
cached = await self.cache.get(symbol, timeframe, start, end)
|
||||
if cached:
|
||||
return cached
|
||||
|
||||
# 2. Essayer sources par priorité
|
||||
for source in self.sources:
|
||||
try:
|
||||
data = await source.fetch(symbol, timeframe, start, end)
|
||||
|
||||
# 3. Valider
|
||||
if self.validator.validate(data):
|
||||
# 4. Cache
|
||||
await self.cache.set(symbol, timeframe, data)
|
||||
return data
|
||||
|
||||
except Exception as e:
|
||||
logger.warning(f"Source {source.name} failed: {e}")
|
||||
continue
|
||||
|
||||
raise DataUnavailableError("All sources failed")
|
||||
```
|
||||
|
||||
### 4. ML Engine
|
||||
|
||||
**Responsabilité** : Intelligence artificielle adaptative
|
||||
|
||||
```python
|
||||
# src/ml/ml_engine.py
|
||||
|
||||
class MLEngine:
|
||||
"""
|
||||
Moteur ML adaptatif
|
||||
|
||||
Features:
|
||||
- Ensemble de modèles
|
||||
- Auto-retraining
|
||||
- Parameter optimization
|
||||
- Regime detection
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
self.models = self._initialize_models()
|
||||
self.optimizer = OptunaOptimizer()
|
||||
self.regime_detector = RegimeDetector()
|
||||
|
||||
async def predict(self, features: pd.DataFrame) -> Dict:
|
||||
"""
|
||||
Prédiction avec ensemble
|
||||
"""
|
||||
# 1. Détecter régime
|
||||
regime = self.regime_detector.detect(features)
|
||||
|
||||
# 2. Sélectionner modèles selon régime
|
||||
active_models = self._select_models(regime)
|
||||
|
||||
# 3. Prédictions individuelles
|
||||
predictions = []
|
||||
for model in active_models:
|
||||
pred = model.predict(features)
|
||||
predictions.append(pred)
|
||||
|
||||
# 4. Agrégation (stacking)
|
||||
final_prediction = self._aggregate(predictions)
|
||||
|
||||
return {
|
||||
'prediction': final_prediction,
|
||||
'confidence': self._calculate_confidence(predictions),
|
||||
'regime': regime
|
||||
}
|
||||
|
||||
async def optimize_daily(self):
|
||||
"""
|
||||
Optimisation quotidienne des paramètres
|
||||
"""
|
||||
# 1. Récupérer performance récente
|
||||
recent_performance = self._get_recent_performance()
|
||||
|
||||
# 2. Détecter drift
|
||||
if self._detect_drift(recent_performance):
|
||||
# 3. Lancer optimisation Optuna
|
||||
new_params = await self.optimizer.optimize()
|
||||
|
||||
# 4. Valider avec backtesting
|
||||
if self._validate_params(new_params):
|
||||
# 5. Appliquer nouveaux paramètres
|
||||
self._update_parameters(new_params)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🔄 Flux de Données
|
||||
|
||||
### Flux Principal (Trading Loop)
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────┐
|
||||
│ TRADING LOOP (60s) │
|
||||
├─────────────────────────────────────────────────────────────┤
|
||||
│ │
|
||||
│ 1. FETCH MARKET DATA │
|
||||
│ ├─ Check cache │
|
||||
│ ├─ Fetch from sources (failover) │
|
||||
│ └─ Validate & store │
|
||||
│ │
|
||||
│ 2. ANALYZE STRATEGIES │
|
||||
│ ├─ Calculate indicators │
|
||||
│ ├─ ML predictions │
|
||||
│ ├─ Generate signals │
|
||||
│ └─ Calculate confidence │
|
||||
│ │
|
||||
│ 3. RISK VALIDATION │
|
||||
│ ├─ Check portfolio risk │
|
||||
│ ├─ Validate position size │
|
||||
│ ├─ Check correlation │
|
||||
│ ├─ Verify margin │
|
||||
│ └─ Circuit breakers │
|
||||
│ │
|
||||
│ 4. ORDER EXECUTION │
|
||||
│ ├─ Place orders (IG API) │
|
||||
│ ├─ Confirm execution │
|
||||
│ └─ Update positions │
|
||||
│ │
|
||||
│ 5. MONITORING │
|
||||
│ ├─ Update metrics │
|
||||
│ ├─ Check alerts │
|
||||
│ └─ Log events │
|
||||
│ │
|
||||
│ 6. SLEEP (until next iteration) │
|
||||
│ │
|
||||
└─────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
### Flux Optimisation (Daily)
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────┐
|
||||
│ OPTIMIZATION LOOP (Daily 00:00) │
|
||||
├─────────────────────────────────────────────────────────────┤
|
||||
│ │
|
||||
│ 1. COLLECT PERFORMANCE DATA │
|
||||
│ ├─ Last 30 days trades │
|
||||
│ ├─ Calculate metrics │
|
||||
│ └─ Detect drift │
|
||||
│ │
|
||||
│ 2. PARAMETER OPTIMIZATION (if drift detected) │
|
||||
│ ├─ Define search space │
|
||||
│ ├─ Run Optuna (Bayesian) │
|
||||
│ ├─ Backtest candidates │
|
||||
│ └─ Select best parameters │
|
||||
│ │
|
||||
│ 3. VALIDATION │
|
||||
│ ├─ Walk-forward analysis │
|
||||
│ ├─ Monte Carlo simulation │
|
||||
│ └─ Out-of-sample test │
|
||||
│ │
|
||||
│ 4. A/B TESTING │
|
||||
│ ├─ Deploy variant in paper trading │
|
||||
│ ├─ Monitor 7 days │
|
||||
│ └─ Compare vs control │
|
||||
│ │
|
||||
│ 5. DEPLOYMENT (if validated) │
|
||||
│ ├─ Update strategy parameters │
|
||||
│ ├─ Retrain ML models │
|
||||
│ └─ Notify operators │
|
||||
│ │
|
||||
└─────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🎨 Patterns et Principes
|
||||
|
||||
### Design Patterns Utilisés
|
||||
|
||||
#### 1. Singleton (Risk Manager)
|
||||
|
||||
```python
|
||||
class RiskManager:
|
||||
_instance = None
|
||||
_lock = threading.Lock()
|
||||
|
||||
def __new__(cls):
|
||||
if cls._instance is None:
|
||||
with cls._lock:
|
||||
if cls._instance is None:
|
||||
cls._instance = super().__new__(cls)
|
||||
return cls._instance
|
||||
```
|
||||
|
||||
**Pourquoi** : Garantir une seule instance pour état global cohérent
|
||||
|
||||
#### 2. Strategy Pattern (Stratégies de Trading)
|
||||
|
||||
```python
|
||||
class BaseStrategy(ABC):
|
||||
@abstractmethod
|
||||
def analyze(self, data):
|
||||
pass
|
||||
|
||||
class ScalpingStrategy(BaseStrategy):
|
||||
def analyze(self, data):
|
||||
# Implémentation scalping
|
||||
pass
|
||||
|
||||
class IntradayStrategy(BaseStrategy):
|
||||
def analyze(self, data):
|
||||
# Implémentation intraday
|
||||
pass
|
||||
```
|
||||
|
||||
**Pourquoi** : Facilite ajout de nouvelles stratégies
|
||||
|
||||
#### 3. Observer Pattern (Events)
|
||||
|
||||
```python
|
||||
class EventBus:
|
||||
def __init__(self):
|
||||
self.subscribers = {}
|
||||
|
||||
def subscribe(self, event_type, callback):
|
||||
if event_type not in self.subscribers:
|
||||
self.subscribers[event_type] = []
|
||||
self.subscribers[event_type].append(callback)
|
||||
|
||||
def publish(self, event_type, data):
|
||||
for callback in self.subscribers.get(event_type, []):
|
||||
callback(data)
|
||||
|
||||
# Usage
|
||||
event_bus.subscribe('trade_executed', log_trade)
|
||||
event_bus.subscribe('trade_executed', update_metrics)
|
||||
event_bus.publish('trade_executed', trade_data)
|
||||
```
|
||||
|
||||
**Pourquoi** : Découplage entre composants
|
||||
|
||||
#### 4. Factory Pattern (Création Stratégies)
|
||||
|
||||
```python
|
||||
class StrategyFactory:
|
||||
@staticmethod
|
||||
def create(strategy_type: str, config: Dict) -> BaseStrategy:
|
||||
if strategy_type == 'scalping':
|
||||
return ScalpingStrategy(config)
|
||||
elif strategy_type == 'intraday':
|
||||
return IntradayStrategy(config)
|
||||
elif strategy_type == 'swing':
|
||||
return SwingStrategy(config)
|
||||
else:
|
||||
raise ValueError(f"Unknown strategy: {strategy_type}")
|
||||
```
|
||||
|
||||
**Pourquoi** : Centralise logique de création
|
||||
|
||||
### Principes SOLID
|
||||
|
||||
- **S**ingle Responsibility : Chaque classe une responsabilité
|
||||
- **O**pen/Closed : Ouvert extension, fermé modification
|
||||
- **L**iskov Substitution : Sous-classes substituables
|
||||
- **I**nterface Segregation : Interfaces spécifiques
|
||||
- **D**ependency Inversion : Dépendre d'abstractions
|
||||
|
||||
---
|
||||
|
||||
## 🔒 Sécurité
|
||||
|
||||
### Niveaux de Sécurité
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────┐
|
||||
│ SECURITY LAYERS │
|
||||
├─────────────────────────────────────────────────────────────┤
|
||||
│ │
|
||||
│ LAYER 1: Authentication & Authorization │
|
||||
│ ├─ API Key management │
|
||||
│ ├─ OAuth 2.0 (IG Markets) │
|
||||
│ └─ Role-based access control │
|
||||
│ │
|
||||
│ LAYER 2: Data Encryption │
|
||||
│ ├─ Credentials encrypted at rest │
|
||||
│ ├─ TLS/SSL for API calls │
|
||||
│ └─ Database encryption │
|
||||
│ │
|
||||
│ LAYER 3: Input Validation │
|
||||
│ ├─ Pydantic models │
|
||||
│ ├─ SQL injection prevention │
|
||||
│ └─ XSS protection │
|
||||
│ │
|
||||
│ LAYER 4: Rate Limiting │
|
||||
│ ├─ API rate limiting │
|
||||
│ ├─ Brute force protection │
|
||||
│ └─ DDoS mitigation │
|
||||
│ │
|
||||
│ LAYER 5: Audit & Monitoring │
|
||||
│ ├─ All actions logged │
|
||||
│ ├─ Anomaly detection │
|
||||
│ └─ Security alerts │
|
||||
│ │
|
||||
└─────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📈 Scalabilité
|
||||
|
||||
### Stratégies de Scaling
|
||||
|
||||
#### Horizontal Scaling
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────┐
|
||||
│ HORIZONTAL SCALING STRATEGY │
|
||||
├─────────────────────────────────────────────────────────────┤
|
||||
│ │
|
||||
│ Load Balancer │
|
||||
│ │ │
|
||||
│ ├─── Instance 1 (Scalping) │
|
||||
│ ├─── Instance 2 (Intraday) │
|
||||
│ └─── Instance 3 (Swing) │
|
||||
│ │
|
||||
│ Shared: │
|
||||
│ ├─ Redis (Cache) │
|
||||
│ ├─ PostgreSQL (Positions) │
|
||||
│ └─ Message Queue (RabbitMQ) │
|
||||
│ │
|
||||
└─────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
#### Vertical Scaling
|
||||
|
||||
- Augmenter RAM pour cache plus large
|
||||
- Plus de CPU cores pour ML parallèle
|
||||
- SSD NVMe pour I/O rapide
|
||||
|
||||
---
|
||||
|
||||
**Architecture complète et évolutive !**
|
||||
585
docs/BACKTESTING_GUIDE.md
Normal file
585
docs/BACKTESTING_GUIDE.md
Normal file
@@ -0,0 +1,585 @@
|
||||
# 🧪 Guide de Backtesting - Trading AI Secure
|
||||
|
||||
## 📋 Table des Matières
|
||||
1. [Philosophie Anti-Overfitting](#philosophie-anti-overfitting)
|
||||
2. [Walk-Forward Analysis](#walk-forward-analysis)
|
||||
3. [Out-of-Sample Testing](#out-of-sample-testing)
|
||||
4. [Monte Carlo Simulation](#monte-carlo-simulation)
|
||||
5. [Paper Trading](#paper-trading)
|
||||
6. [Métriques de Validation](#métriques-de-validation)
|
||||
7. [Implémentation](#implémentation)
|
||||
|
||||
---
|
||||
|
||||
## 🎯 Philosophie Anti-Overfitting
|
||||
|
||||
### Principe Fondamental
|
||||
|
||||
**"Une stratégie qui performe parfaitement en backtest est probablement sur-optimisée"**
|
||||
|
||||
### Les 7 Règles d'Or du Backtesting
|
||||
|
||||
1. ✅ **Toujours réserver 30% des données pour out-of-sample**
|
||||
2. ✅ **Utiliser walk-forward analysis (pas de look-ahead bias)**
|
||||
3. ✅ **Valider avec Monte Carlo (10,000+ simulations)**
|
||||
4. ✅ **Paper trading obligatoire 30 jours minimum**
|
||||
5. ✅ **Inclure coûts réalistes (slippage, commissions)**
|
||||
6. ✅ **Tester sur multiple marchés/périodes**
|
||||
7. ✅ **Documenter tous les paramètres testés (éviter cherry-picking)**
|
||||
|
||||
---
|
||||
|
||||
## 🔄 Walk-Forward Analysis
|
||||
|
||||
### Concept
|
||||
|
||||
La walk-forward analysis simule le trading réel en :
|
||||
1. Entraînant sur une fenêtre passée
|
||||
2. Testant sur la fenêtre suivante
|
||||
3. Glissant les fenêtres progressivement
|
||||
|
||||
```
|
||||
┌────────────────────────────────────────────────────────────┐
|
||||
│ WALK-FORWARD ANALYSIS │
|
||||
├────────────────────────────────────────────────────────────┤
|
||||
│ │
|
||||
│ Période 1: │
|
||||
│ [====TRAIN====][TEST] │
|
||||
│ │
|
||||
│ Période 2: │
|
||||
│ [====TRAIN====][TEST] │
|
||||
│ │
|
||||
│ Période 3: │
|
||||
│ [====TRAIN====][TEST] │
|
||||
│ │
|
||||
│ Période 4: │
|
||||
│ [====TRAIN====][TEST] │
|
||||
│ │
|
||||
└────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
### Implémentation
|
||||
|
||||
```python
|
||||
# src/backtesting/walk_forward.py
|
||||
|
||||
from typing import Dict, List, Tuple
|
||||
import pandas as pd
|
||||
import numpy as np
|
||||
from datetime import datetime, timedelta
|
||||
|
||||
class WalkForwardAnalyzer:
|
||||
"""
|
||||
Walk-Forward Analysis Engine
|
||||
|
||||
Paramètres:
|
||||
- train_window: Taille fenêtre entraînement (ex: 252 jours = 1 an)
|
||||
- test_window: Taille fenêtre test (ex: 63 jours = 3 mois)
|
||||
- step_size: Pas de glissement (ex: 21 jours = 1 mois)
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
train_window: int = 252,
|
||||
test_window: int = 63,
|
||||
step_size: int = 21
|
||||
):
|
||||
self.train_window = train_window
|
||||
self.test_window = test_window
|
||||
self.step_size = step_size
|
||||
|
||||
self.results = []
|
||||
|
||||
def run(
|
||||
self,
|
||||
data: pd.DataFrame,
|
||||
strategy_class,
|
||||
optimization_func
|
||||
) -> Dict:
|
||||
"""
|
||||
Exécute walk-forward analysis
|
||||
|
||||
Args:
|
||||
data: Données historiques complètes
|
||||
strategy_class: Classe de stratégie à tester
|
||||
optimization_func: Fonction d'optimisation des paramètres
|
||||
|
||||
Returns:
|
||||
Résultats agrégés de tous les walks
|
||||
"""
|
||||
total_length = len(data)
|
||||
current_pos = 0
|
||||
|
||||
while current_pos + self.train_window + self.test_window <= total_length:
|
||||
# Fenêtre entraînement
|
||||
train_start = current_pos
|
||||
train_end = current_pos + self.train_window
|
||||
train_data = data.iloc[train_start:train_end]
|
||||
|
||||
# Fenêtre test
|
||||
test_start = train_end
|
||||
test_end = train_end + self.test_window
|
||||
test_data = data.iloc[test_start:test_end]
|
||||
|
||||
# Optimiser paramètres sur train
|
||||
optimal_params = optimization_func(train_data, strategy_class)
|
||||
|
||||
# Tester sur test
|
||||
strategy = strategy_class(optimal_params)
|
||||
test_results = self._backtest_strategy(strategy, test_data)
|
||||
|
||||
# Enregistrer résultats
|
||||
self.results.append({
|
||||
'train_period': (train_data.index[0], train_data.index[-1]),
|
||||
'test_period': (test_data.index[0], test_data.index[-1]),
|
||||
'optimal_params': optimal_params,
|
||||
'test_sharpe': test_results['sharpe'],
|
||||
'test_returns': test_results['total_return'],
|
||||
'test_max_dd': test_results['max_drawdown'],
|
||||
'test_win_rate': test_results['win_rate'],
|
||||
'num_trades': test_results['num_trades']
|
||||
})
|
||||
|
||||
# Glisser fenêtre
|
||||
current_pos += self.step_size
|
||||
|
||||
return self._aggregate_results()
|
||||
|
||||
def _backtest_strategy(
|
||||
self,
|
||||
strategy,
|
||||
data: pd.DataFrame
|
||||
) -> Dict:
|
||||
"""
|
||||
Backtest stratégie sur données
|
||||
"""
|
||||
trades = []
|
||||
equity_curve = [10000] # Capital initial
|
||||
|
||||
for i in range(len(data)):
|
||||
current_data = data.iloc[:i+1]
|
||||
|
||||
# Générer signal
|
||||
signal = strategy.analyze(current_data)
|
||||
|
||||
if signal:
|
||||
# Simuler trade
|
||||
trade_result = self._simulate_trade(
|
||||
signal,
|
||||
data.iloc[i:min(i+100, len(data))] # Données futures pour exit
|
||||
)
|
||||
trades.append(trade_result)
|
||||
equity_curve.append(equity_curve[-1] + trade_result['pnl'])
|
||||
|
||||
# Calculer métriques
|
||||
returns = pd.Series(equity_curve).pct_change().dropna()
|
||||
|
||||
return {
|
||||
'total_return': (equity_curve[-1] - equity_curve[0]) / equity_curve[0],
|
||||
'sharpe': self._calculate_sharpe(returns),
|
||||
'max_drawdown': self._calculate_max_drawdown(equity_curve),
|
||||
'win_rate': len([t for t in trades if t['pnl'] > 0]) / len(trades) if trades else 0,
|
||||
'num_trades': len(trades),
|
||||
'equity_curve': equity_curve
|
||||
}
|
||||
|
||||
def _simulate_trade(
|
||||
self,
|
||||
signal: 'Signal',
|
||||
future_data: pd.DataFrame
|
||||
) -> Dict:
|
||||
"""
|
||||
Simule exécution d'un trade
|
||||
"""
|
||||
entry_price = signal.entry_price
|
||||
stop_loss = signal.stop_loss
|
||||
take_profit = signal.take_profit
|
||||
|
||||
# Ajouter slippage réaliste
|
||||
slippage = 0.001 # 0.1%
|
||||
entry_price *= (1 + slippage if signal.direction == 'LONG' else 1 - slippage)
|
||||
|
||||
# Simuler holding jusqu'à exit
|
||||
for i, row in future_data.iterrows():
|
||||
# Check stop-loss
|
||||
if signal.direction == 'LONG':
|
||||
if row['low'] <= stop_loss:
|
||||
exit_price = stop_loss * (1 - slippage)
|
||||
pnl = (exit_price - entry_price) / entry_price
|
||||
return {'pnl': pnl, 'exit_reason': 'stop_loss', 'holding_bars': i}
|
||||
|
||||
# Check take-profit
|
||||
if row['high'] >= take_profit:
|
||||
exit_price = take_profit * (1 - slippage)
|
||||
pnl = (exit_price - entry_price) / entry_price
|
||||
return {'pnl': pnl, 'exit_reason': 'take_profit', 'holding_bars': i}
|
||||
|
||||
else: # SHORT
|
||||
if row['high'] >= stop_loss:
|
||||
exit_price = stop_loss * (1 + slippage)
|
||||
pnl = (entry_price - exit_price) / entry_price
|
||||
return {'pnl': pnl, 'exit_reason': 'stop_loss', 'holding_bars': i}
|
||||
|
||||
if row['low'] <= take_profit:
|
||||
exit_price = take_profit * (1 + slippage)
|
||||
pnl = (entry_price - exit_price) / entry_price
|
||||
return {'pnl': pnl, 'exit_reason': 'take_profit', 'holding_bars': i}
|
||||
|
||||
# Timeout (max holding time)
|
||||
exit_price = future_data.iloc[-1]['close']
|
||||
if signal.direction == 'LONG':
|
||||
pnl = (exit_price - entry_price) / entry_price
|
||||
else:
|
||||
pnl = (entry_price - exit_price) / entry_price
|
||||
|
||||
return {'pnl': pnl, 'exit_reason': 'timeout', 'holding_bars': len(future_data)}
|
||||
|
||||
def _aggregate_results(self) -> Dict:
|
||||
"""
|
||||
Agrège résultats de tous les walks
|
||||
"""
|
||||
if not self.results:
|
||||
return {}
|
||||
|
||||
sharpes = [r['test_sharpe'] for r in self.results]
|
||||
returns = [r['test_returns'] for r in self.results]
|
||||
drawdowns = [r['test_max_dd'] for r in self.results]
|
||||
win_rates = [r['test_win_rate'] for r in self.results]
|
||||
|
||||
return {
|
||||
'num_walks': len(self.results),
|
||||
'avg_sharpe': np.mean(sharpes),
|
||||
'std_sharpe': np.std(sharpes),
|
||||
'min_sharpe': np.min(sharpes),
|
||||
'max_sharpe': np.max(sharpes),
|
||||
'avg_return': np.mean(returns),
|
||||
'avg_max_dd': np.mean(drawdowns),
|
||||
'worst_max_dd': np.max(drawdowns),
|
||||
'avg_win_rate': np.mean(win_rates),
|
||||
'consistency': len([s for s in sharpes if s > 1.0]) / len(sharpes),
|
||||
'all_walks': self.results
|
||||
}
|
||||
|
||||
def _calculate_sharpe(self, returns: pd.Series, risk_free=0.02) -> float:
|
||||
"""Calcule Sharpe Ratio"""
|
||||
excess_returns = returns - risk_free / 252
|
||||
return np.mean(excess_returns) / np.std(excess_returns) * np.sqrt(252)
|
||||
|
||||
def _calculate_max_drawdown(self, equity_curve: List[float]) -> float:
|
||||
"""Calcule Maximum Drawdown"""
|
||||
peak = np.maximum.accumulate(equity_curve)
|
||||
drawdown = (np.array(equity_curve) - peak) / peak
|
||||
return np.min(drawdown)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📊 Out-of-Sample Testing
|
||||
|
||||
### Principe
|
||||
|
||||
**Jamais optimiser sur 100% des données !**
|
||||
|
||||
```
|
||||
┌────────────────────────────────────────────────────────────┐
|
||||
│ SPLIT IN-SAMPLE / OUT-OF-SAMPLE │
|
||||
├────────────────────────────────────────────────────────────┤
|
||||
│ │
|
||||
│ [========== IN-SAMPLE 70% ==========][OUT-SAMPLE 30%] │
|
||||
│ │
|
||||
│ Optimisation paramètres Validation finale │
|
||||
│ Walk-forward analysis Performance réelle │
|
||||
│ Tuning hyperparamètres JAMAIS touché │
|
||||
│ │
|
||||
└────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
### Implémentation
|
||||
|
||||
```python
|
||||
class OutOfSampleValidator:
|
||||
"""
|
||||
Validation out-of-sample stricte
|
||||
"""
|
||||
|
||||
def __init__(self, oos_ratio=0.30):
|
||||
self.oos_ratio = oos_ratio
|
||||
|
||||
def split_data(
|
||||
self,
|
||||
data: pd.DataFrame
|
||||
) -> Tuple[pd.DataFrame, pd.DataFrame]:
|
||||
"""
|
||||
Split données en in-sample / out-of-sample
|
||||
"""
|
||||
split_point = int(len(data) * (1 - self.oos_ratio))
|
||||
|
||||
in_sample = data.iloc[:split_point]
|
||||
out_of_sample = data.iloc[split_point:]
|
||||
|
||||
return in_sample, out_of_sample
|
||||
|
||||
def validate(
|
||||
self,
|
||||
strategy,
|
||||
in_sample_data: pd.DataFrame,
|
||||
out_of_sample_data: pd.DataFrame
|
||||
) -> Dict:
|
||||
"""
|
||||
Valide stratégie sur out-of-sample
|
||||
"""
|
||||
# Performance in-sample
|
||||
is_results = self._backtest(strategy, in_sample_data)
|
||||
|
||||
# Performance out-of-sample (CRITIQUE)
|
||||
oos_results = self._backtest(strategy, out_of_sample_data)
|
||||
|
||||
# Comparer performances
|
||||
degradation = self._calculate_degradation(is_results, oos_results)
|
||||
|
||||
return {
|
||||
'in_sample': is_results,
|
||||
'out_of_sample': oos_results,
|
||||
'degradation': degradation,
|
||||
'is_valid': self._is_valid_strategy(degradation)
|
||||
}
|
||||
|
||||
def _calculate_degradation(
|
||||
self,
|
||||
is_results: Dict,
|
||||
oos_results: Dict
|
||||
) -> Dict:
|
||||
"""
|
||||
Calcule dégradation performance IS → OOS
|
||||
"""
|
||||
return {
|
||||
'sharpe_degradation': (is_results['sharpe'] - oos_results['sharpe']) / is_results['sharpe'],
|
||||
'return_degradation': (is_results['total_return'] - oos_results['total_return']) / is_results['total_return'],
|
||||
'winrate_degradation': (is_results['win_rate'] - oos_results['win_rate']) / is_results['win_rate'],
|
||||
}
|
||||
|
||||
def _is_valid_strategy(self, degradation: Dict) -> bool:
|
||||
"""
|
||||
Critères de validation
|
||||
|
||||
Stratégie valide si:
|
||||
- Sharpe OOS > 1.0
|
||||
- Dégradation Sharpe < 30%
|
||||
- Dégradation Return < 40%
|
||||
"""
|
||||
if degradation['sharpe_degradation'] > 0.30:
|
||||
return False
|
||||
if degradation['return_degradation'] > 0.40:
|
||||
return False
|
||||
|
||||
return True
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🎲 Monte Carlo Simulation
|
||||
|
||||
### Objectif
|
||||
|
||||
Tester robustesse en simulant milliers de scénarios aléatoires
|
||||
|
||||
```python
|
||||
class MonteCarloSimulator:
|
||||
"""
|
||||
Simulation Monte Carlo pour validation robustesse
|
||||
"""
|
||||
|
||||
def __init__(self, n_simulations=10000):
|
||||
self.n_simulations = n_simulations
|
||||
|
||||
def simulate(
|
||||
self,
|
||||
historical_trades: List[Dict]
|
||||
) -> Dict:
|
||||
"""
|
||||
Simule N scénarios en réordonnant trades
|
||||
|
||||
Principe:
|
||||
- Même trades, ordre différent
|
||||
- Teste sensibilité à la séquence
|
||||
- Identifie lucky streaks vs. edge réel
|
||||
"""
|
||||
results = []
|
||||
|
||||
for _ in range(self.n_simulations):
|
||||
# Réordonner trades aléatoirement
|
||||
shuffled_trades = np.random.permutation(historical_trades)
|
||||
|
||||
# Calculer equity curve
|
||||
equity = self._calculate_equity_curve(shuffled_trades)
|
||||
|
||||
# Métriques
|
||||
sharpe = self._calculate_sharpe(equity)
|
||||
max_dd = self._calculate_max_drawdown(equity)
|
||||
final_return = (equity[-1] - equity[0]) / equity[0]
|
||||
|
||||
results.append({
|
||||
'sharpe': sharpe,
|
||||
'max_dd': max_dd,
|
||||
'return': final_return
|
||||
})
|
||||
|
||||
return self._analyze_distribution(results)
|
||||
|
||||
def _analyze_distribution(self, results: List[Dict]) -> Dict:
|
||||
"""
|
||||
Analyse distribution résultats Monte Carlo
|
||||
"""
|
||||
sharpes = [r['sharpe'] for r in results]
|
||||
returns = [r['return'] for r in results]
|
||||
drawdowns = [r['max_dd'] for r in results]
|
||||
|
||||
return {
|
||||
# Sharpe
|
||||
'sharpe_mean': np.mean(sharpes),
|
||||
'sharpe_median': np.median(sharpes),
|
||||
'sharpe_5th_percentile': np.percentile(sharpes, 5),
|
||||
'sharpe_95th_percentile': np.percentile(sharpes, 95),
|
||||
'prob_sharpe_positive': np.mean(np.array(sharpes) > 0),
|
||||
'prob_sharpe_above_1': np.mean(np.array(sharpes) > 1.0),
|
||||
|
||||
# Returns
|
||||
'return_mean': np.mean(returns),
|
||||
'return_5th_percentile': np.percentile(returns, 5),
|
||||
'return_95th_percentile': np.percentile(returns, 95),
|
||||
'prob_positive_return': np.mean(np.array(returns) > 0),
|
||||
|
||||
# Drawdown
|
||||
'max_dd_mean': np.mean(drawdowns),
|
||||
'max_dd_95th_percentile': np.percentile(drawdowns, 95),
|
||||
'prob_dd_below_10pct': np.mean(np.array(drawdowns) > -0.10),
|
||||
}
|
||||
|
||||
def _calculate_equity_curve(self, trades: List[Dict]) -> List[float]:
|
||||
"""Calcule equity curve à partir de trades"""
|
||||
equity = [10000] # Capital initial
|
||||
|
||||
for trade in trades:
|
||||
pnl = trade['pnl'] * equity[-1]
|
||||
equity.append(equity[-1] + pnl)
|
||||
|
||||
return equity
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📝 Paper Trading
|
||||
|
||||
### Protocole Strict
|
||||
|
||||
```python
|
||||
class PaperTradingEngine:
|
||||
"""
|
||||
Paper trading avec conditions réelles
|
||||
|
||||
Règles:
|
||||
- Minimum 30 jours de trading
|
||||
- Données temps réel (pas historiques)
|
||||
- Slippage et commissions réalistes
|
||||
- Latence simulée
|
||||
- Validation quotidienne
|
||||
"""
|
||||
|
||||
def __init__(self, min_days=30):
|
||||
self.min_days = min_days
|
||||
self.start_date = None
|
||||
self.trades = []
|
||||
self.daily_metrics = []
|
||||
|
||||
def start(self):
|
||||
"""Démarre paper trading"""
|
||||
self.start_date = datetime.now()
|
||||
logger.info(f"Paper trading started. Minimum duration: {self.min_days} days")
|
||||
|
||||
def can_go_live(self) -> Tuple[bool, str]:
|
||||
"""
|
||||
Vérifie si stratégie peut passer en live
|
||||
|
||||
Critères:
|
||||
- Minimum 30 jours
|
||||
- Sharpe > 1.5
|
||||
- Max DD < 10%
|
||||
- Win rate > 55%
|
||||
- Minimum 50 trades
|
||||
"""
|
||||
if not self.start_date:
|
||||
return False, "Paper trading not started"
|
||||
|
||||
days_elapsed = (datetime.now() - self.start_date).days
|
||||
|
||||
if days_elapsed < self.min_days:
|
||||
return False, f"Only {days_elapsed}/{self.min_days} days completed"
|
||||
|
||||
# Calculer métriques
|
||||
metrics = self._calculate_metrics()
|
||||
|
||||
# Vérifier critères
|
||||
if metrics['sharpe'] < 1.5:
|
||||
return False, f"Sharpe {metrics['sharpe']:.2f} below 1.5"
|
||||
|
||||
if metrics['max_dd'] > 0.10:
|
||||
return False, f"Max DD {metrics['max_dd']:.2%} above 10%"
|
||||
|
||||
if metrics['win_rate'] < 0.55:
|
||||
return False, f"Win rate {metrics['win_rate']:.2%} below 55%"
|
||||
|
||||
if len(self.trades) < 50:
|
||||
return False, f"Only {len(self.trades)} trades (minimum 50)"
|
||||
|
||||
return True, "All criteria met. Ready for live trading."
|
||||
|
||||
def _calculate_metrics(self) -> Dict:
|
||||
"""Calcule métriques paper trading"""
|
||||
# TODO: Implémenter calculs
|
||||
pass
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📊 Métriques de Validation
|
||||
|
||||
### Seuils Minimaux
|
||||
|
||||
```yaml
|
||||
validation_criteria:
|
||||
# Performance
|
||||
sharpe_ratio:
|
||||
in_sample: 1.8
|
||||
out_of_sample: 1.5
|
||||
paper_trading: 1.5
|
||||
|
||||
# Risk
|
||||
max_drawdown:
|
||||
in_sample: 0.08
|
||||
out_of_sample: 0.10
|
||||
paper_trading: 0.10
|
||||
|
||||
# Consistency
|
||||
win_rate:
|
||||
minimum: 0.55
|
||||
target: 0.60
|
||||
|
||||
profit_factor:
|
||||
minimum: 1.3
|
||||
target: 1.5
|
||||
|
||||
# Robustness
|
||||
monte_carlo:
|
||||
prob_positive_sharpe: 0.95
|
||||
sharpe_5th_percentile: 0.8
|
||||
|
||||
# Sample size
|
||||
minimum_trades:
|
||||
in_sample: 100
|
||||
out_of_sample: 30
|
||||
paper_trading: 50
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
**Documentation complète du backtesting terminée !**
|
||||
615
docs/CONTRIBUTING.md
Normal file
615
docs/CONTRIBUTING.md
Normal file
@@ -0,0 +1,615 @@
|
||||
# 🤝 Guide de Contribution - Trading AI Secure
|
||||
|
||||
## 📋 Table des Matières
|
||||
1. [Code of Conduct](#code-of-conduct)
|
||||
2. [Comment Contribuer](#comment-contribuer)
|
||||
3. [Standards de Code](#standards-de-code)
|
||||
4. [Workflow Git](#workflow-git)
|
||||
5. [Tests](#tests)
|
||||
6. [Documentation](#documentation)
|
||||
7. [Review Process](#review-process)
|
||||
|
||||
---
|
||||
|
||||
## 📜 Code of Conduct
|
||||
|
||||
### Nos Valeurs
|
||||
|
||||
- **Respect** : Traiter tous les contributeurs avec respect
|
||||
- **Collaboration** : Travailler ensemble vers un objectif commun
|
||||
- **Excellence** : Viser la qualité dans tout ce que nous faisons
|
||||
- **Transparence** : Communication ouverte et honnête
|
||||
- **Sécurité** : Priorité absolue dans le trading
|
||||
|
||||
### Comportements Attendus
|
||||
|
||||
✅ Être accueillant et inclusif
|
||||
✅ Respecter les opinions différentes
|
||||
✅ Accepter les critiques constructives
|
||||
✅ Se concentrer sur ce qui est meilleur pour la communauté
|
||||
✅ Faire preuve d'empathie
|
||||
|
||||
### Comportements Inacceptables
|
||||
|
||||
❌ Langage ou images inappropriés
|
||||
❌ Attaques personnelles ou politiques
|
||||
❌ Harcèlement public ou privé
|
||||
❌ Publication d'informations privées sans permission
|
||||
❌ Conduite non professionnelle
|
||||
|
||||
---
|
||||
|
||||
## 🚀 Comment Contribuer
|
||||
|
||||
### Types de Contributions
|
||||
|
||||
#### 1. Rapporter des Bugs
|
||||
|
||||
**Template Issue Bug** :
|
||||
|
||||
```markdown
|
||||
**Description du Bug**
|
||||
Description claire et concise du bug.
|
||||
|
||||
**Étapes pour Reproduire**
|
||||
1. Aller à '...'
|
||||
2. Cliquer sur '...'
|
||||
3. Voir erreur
|
||||
|
||||
**Comportement Attendu**
|
||||
Ce qui devrait se passer.
|
||||
|
||||
**Comportement Actuel**
|
||||
Ce qui se passe réellement.
|
||||
|
||||
**Screenshots**
|
||||
Si applicable.
|
||||
|
||||
**Environnement**
|
||||
- OS: [e.g. Windows 11]
|
||||
- Python: [e.g. 3.11.5]
|
||||
- Version: [e.g. 0.1.0]
|
||||
|
||||
**Logs**
|
||||
```
|
||||
Coller logs pertinents
|
||||
```
|
||||
```
|
||||
|
||||
#### 2. Proposer des Features
|
||||
|
||||
**Template Issue Feature** :
|
||||
|
||||
```markdown
|
||||
**Problème à Résoudre**
|
||||
Description du problème que cette feature résout.
|
||||
|
||||
**Solution Proposée**
|
||||
Description de la solution.
|
||||
|
||||
**Alternatives Considérées**
|
||||
Autres approches envisagées.
|
||||
|
||||
**Contexte Additionnel**
|
||||
Informations supplémentaires.
|
||||
```
|
||||
|
||||
#### 3. Améliorer la Documentation
|
||||
|
||||
- Corriger typos
|
||||
- Clarifier explications
|
||||
- Ajouter exemples
|
||||
- Traduire documentation
|
||||
|
||||
#### 4. Développer du Code
|
||||
|
||||
- Nouvelles stratégies
|
||||
- Améliorations ML
|
||||
- Optimisations performance
|
||||
- Nouveaux connecteurs de données
|
||||
|
||||
---
|
||||
|
||||
## 💻 Standards de Code
|
||||
|
||||
### Style Python (PEP 8)
|
||||
|
||||
```python
|
||||
# ✅ BON
|
||||
def calculate_position_size(
|
||||
portfolio_value: float,
|
||||
risk_per_trade: float,
|
||||
stop_distance: float
|
||||
) -> float:
|
||||
"""
|
||||
Calcule taille position optimale.
|
||||
|
||||
Args:
|
||||
portfolio_value: Valeur totale du portfolio
|
||||
risk_per_trade: Risque par trade (0.0 à 1.0)
|
||||
stop_distance: Distance au stop-loss
|
||||
|
||||
Returns:
|
||||
Taille de position en unités
|
||||
|
||||
Raises:
|
||||
ValueError: Si paramètres invalides
|
||||
"""
|
||||
if portfolio_value <= 0:
|
||||
raise ValueError("Portfolio value must be positive")
|
||||
|
||||
risk_amount = portfolio_value * risk_per_trade
|
||||
position_size = risk_amount / stop_distance
|
||||
|
||||
return position_size
|
||||
|
||||
|
||||
# ❌ MAUVAIS
|
||||
def calc_pos(pv,rpt,sd):
|
||||
return pv*rpt/sd
|
||||
```
|
||||
|
||||
### Type Hints (Obligatoires)
|
||||
|
||||
```python
|
||||
# ✅ BON
|
||||
from typing import Dict, List, Optional
|
||||
from datetime import datetime
|
||||
|
||||
def analyze_market(
|
||||
data: pd.DataFrame,
|
||||
timeframe: str,
|
||||
indicators: List[str]
|
||||
) -> Optional[Dict[str, float]]:
|
||||
pass
|
||||
|
||||
|
||||
# ❌ MAUVAIS
|
||||
def analyze_market(data, timeframe, indicators):
|
||||
pass
|
||||
```
|
||||
|
||||
### Docstrings (Google Style)
|
||||
|
||||
```python
|
||||
def backtest_strategy(
|
||||
strategy: BaseStrategy,
|
||||
data: pd.DataFrame,
|
||||
initial_capital: float = 10000.0
|
||||
) -> Dict[str, float]:
|
||||
"""
|
||||
Backtest une stratégie sur données historiques.
|
||||
|
||||
Cette fonction exécute un backtest complet incluant:
|
||||
- Simulation des trades
|
||||
- Calcul des métriques
|
||||
- Gestion du risque
|
||||
|
||||
Args:
|
||||
strategy: Instance de stratégie à tester
|
||||
data: DataFrame avec données OHLCV
|
||||
initial_capital: Capital initial en USD
|
||||
|
||||
Returns:
|
||||
Dictionnaire avec métriques:
|
||||
- 'total_return': Return total (%)
|
||||
- 'sharpe_ratio': Sharpe ratio
|
||||
- 'max_drawdown': Drawdown maximum (%)
|
||||
- 'win_rate': Taux de réussite (%)
|
||||
|
||||
Raises:
|
||||
ValueError: Si données insuffisantes
|
||||
|
||||
Example:
|
||||
>>> strategy = ScalpingStrategy(config)
|
||||
>>> data = load_historical_data('EURUSD', '1h')
|
||||
>>> results = backtest_strategy(strategy, data)
|
||||
>>> print(f"Sharpe: {results['sharpe_ratio']:.2f}")
|
||||
Sharpe: 1.85
|
||||
"""
|
||||
pass
|
||||
```
|
||||
|
||||
### Naming Conventions
|
||||
|
||||
```python
|
||||
# Classes: PascalCase
|
||||
class RiskManager:
|
||||
pass
|
||||
|
||||
# Functions/Methods: snake_case
|
||||
def calculate_sharpe_ratio():
|
||||
pass
|
||||
|
||||
# Constants: UPPER_SNAKE_CASE
|
||||
MAX_POSITION_SIZE = 0.05
|
||||
DEFAULT_RISK_PER_TRADE = 0.02
|
||||
|
||||
# Private: _leading_underscore
|
||||
def _internal_helper():
|
||||
pass
|
||||
|
||||
# Variables: snake_case
|
||||
portfolio_value = 10000.0
|
||||
current_drawdown = 0.05
|
||||
```
|
||||
|
||||
### Imports Organization
|
||||
|
||||
```python
|
||||
# 1. Standard library
|
||||
import os
|
||||
import sys
|
||||
from datetime import datetime, timedelta
|
||||
from typing import Dict, List, Optional
|
||||
|
||||
# 2. Third-party
|
||||
import numpy as np
|
||||
import pandas as pd
|
||||
from fastapi import FastAPI, HTTPException
|
||||
|
||||
# 3. Local
|
||||
from src.core.risk_manager import RiskManager
|
||||
from src.strategies.base_strategy import BaseStrategy
|
||||
from src.utils.logger import get_logger
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🌿 Workflow Git
|
||||
|
||||
### Branches
|
||||
|
||||
```
|
||||
main
|
||||
├── develop
|
||||
│ ├── feature/add-new-strategy
|
||||
│ ├── feature/improve-ml-engine
|
||||
│ ├── bugfix/fix-risk-calculation
|
||||
│ └── hotfix/critical-security-patch
|
||||
```
|
||||
|
||||
**Conventions de nommage** :
|
||||
|
||||
- `feature/description` : Nouvelles fonctionnalités
|
||||
- `bugfix/description` : Corrections de bugs
|
||||
- `hotfix/description` : Corrections urgentes
|
||||
- `docs/description` : Documentation
|
||||
- `refactor/description` : Refactoring
|
||||
- `test/description` : Ajout de tests
|
||||
|
||||
### Workflow Contribution
|
||||
|
||||
#### 1. Fork et Clone
|
||||
|
||||
```bash
|
||||
# Fork sur GitHub
|
||||
# Puis cloner votre fork
|
||||
git clone https://github.com/VOTRE-USERNAME/trading-ai-secure.git
|
||||
cd trading-ai-secure
|
||||
|
||||
# Ajouter upstream
|
||||
git remote add upstream https://github.com/ORIGINAL-OWNER/trading-ai-secure.git
|
||||
```
|
||||
|
||||
#### 2. Créer Branche
|
||||
|
||||
```bash
|
||||
# Mettre à jour develop
|
||||
git checkout develop
|
||||
git pull upstream develop
|
||||
|
||||
# Créer branche feature
|
||||
git checkout -b feature/ma-nouvelle-feature
|
||||
```
|
||||
|
||||
#### 3. Développer
|
||||
|
||||
```bash
|
||||
# Faire vos modifications
|
||||
# ...
|
||||
|
||||
# Commiter régulièrement
|
||||
git add .
|
||||
git commit -m "feat: add new scalping indicator"
|
||||
|
||||
# Suivre conventions de commit (voir ci-dessous)
|
||||
```
|
||||
|
||||
#### 4. Tester
|
||||
|
||||
```bash
|
||||
# Lancer tests
|
||||
pytest tests/
|
||||
|
||||
# Vérifier couverture
|
||||
pytest --cov=src tests/
|
||||
|
||||
# Linter
|
||||
pylint src/
|
||||
black src/
|
||||
isort src/
|
||||
```
|
||||
|
||||
#### 5. Push et Pull Request
|
||||
|
||||
```bash
|
||||
# Push vers votre fork
|
||||
git push origin feature/ma-nouvelle-feature
|
||||
|
||||
# Créer Pull Request sur GitHub
|
||||
# Remplir template PR
|
||||
```
|
||||
|
||||
### Conventions de Commit
|
||||
|
||||
Format : `<type>(<scope>): <description>`
|
||||
|
||||
**Types** :
|
||||
|
||||
- `feat`: Nouvelle fonctionnalité
|
||||
- `fix`: Correction de bug
|
||||
- `docs`: Documentation
|
||||
- `style`: Formatage (pas de changement de code)
|
||||
- `refactor`: Refactoring
|
||||
- `test`: Ajout de tests
|
||||
- `chore`: Maintenance
|
||||
|
||||
**Exemples** :
|
||||
|
||||
```bash
|
||||
feat(strategies): add VWAP indicator to intraday strategy
|
||||
fix(risk): correct position sizing calculation
|
||||
docs(readme): update installation instructions
|
||||
test(backtesting): add Monte Carlo simulation tests
|
||||
refactor(ml): optimize feature engineering pipeline
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🧪 Tests
|
||||
|
||||
### Structure Tests
|
||||
|
||||
```
|
||||
tests/
|
||||
├── unit/
|
||||
│ ├── test_risk_manager.py
|
||||
│ ├── test_strategies.py
|
||||
│ └── test_ml_engine.py
|
||||
├── integration/
|
||||
│ ├── test_data_sources.py
|
||||
│ └── test_ig_api.py
|
||||
├── e2e/
|
||||
│ └── test_full_trading_loop.py
|
||||
└── fixtures/
|
||||
└── sample_data.py
|
||||
```
|
||||
|
||||
### Écrire Tests
|
||||
|
||||
```python
|
||||
# tests/unit/test_risk_manager.py
|
||||
|
||||
import pytest
|
||||
from src.core.risk_manager import RiskManager
|
||||
|
||||
class TestRiskManager:
|
||||
"""Tests pour RiskManager"""
|
||||
|
||||
@pytest.fixture
|
||||
def risk_manager(self):
|
||||
"""Fixture RiskManager"""
|
||||
return RiskManager()
|
||||
|
||||
def test_singleton_pattern(self):
|
||||
"""Vérifie pattern singleton"""
|
||||
rm1 = RiskManager()
|
||||
rm2 = RiskManager()
|
||||
assert rm1 is rm2
|
||||
|
||||
def test_validate_trade_success(self, risk_manager):
|
||||
"""Test validation trade valide"""
|
||||
is_valid, error = risk_manager.validate_trade(
|
||||
symbol='EURUSD',
|
||||
quantity=1000,
|
||||
price=1.1000,
|
||||
stop_loss=1.0950,
|
||||
take_profit=1.1100,
|
||||
strategy='intraday'
|
||||
)
|
||||
|
||||
assert is_valid is True
|
||||
assert error is None
|
||||
|
||||
def test_validate_trade_no_stop_loss(self, risk_manager):
|
||||
"""Test rejet si pas de stop-loss"""
|
||||
is_valid, error = risk_manager.validate_trade(
|
||||
symbol='EURUSD',
|
||||
quantity=1000,
|
||||
price=1.1000,
|
||||
stop_loss=None, # Pas de stop-loss
|
||||
take_profit=1.1100,
|
||||
strategy='intraday'
|
||||
)
|
||||
|
||||
assert is_valid is False
|
||||
assert "stop-loss" in error.lower()
|
||||
|
||||
@pytest.mark.parametrize("position_size,expected", [
|
||||
(0.01, True), # 1% OK
|
||||
(0.05, True), # 5% OK
|
||||
(0.10, False), # 10% trop grand
|
||||
])
|
||||
def test_position_size_limits(self, risk_manager, position_size, expected):
|
||||
"""Test limites taille position"""
|
||||
# ... test paramétré
|
||||
```
|
||||
|
||||
### Coverage Minimum
|
||||
|
||||
- **Global** : 85%
|
||||
- **Core modules** : 90%
|
||||
- **Stratégies** : 85%
|
||||
- **ML** : 80%
|
||||
- **UI** : 70%
|
||||
|
||||
---
|
||||
|
||||
## 📚 Documentation
|
||||
|
||||
### Documentation Code
|
||||
|
||||
Chaque module doit avoir :
|
||||
|
||||
1. **Module docstring** : Description du module
|
||||
2. **Class docstrings** : Description de la classe
|
||||
3. **Method docstrings** : Description des méthodes
|
||||
4. **Type hints** : Sur tous les paramètres et retours
|
||||
|
||||
### Documentation Utilisateur
|
||||
|
||||
Mettre à jour si changements :
|
||||
|
||||
- `README.md` : Vue d'ensemble
|
||||
- `docs/GETTING_STARTED.md` : Guide démarrage
|
||||
- `docs/ARCHITECTURE.md` : Architecture
|
||||
- `docs/API.md` : Documentation API
|
||||
|
||||
### Exemples
|
||||
|
||||
Ajouter exemples d'utilisation :
|
||||
|
||||
```python
|
||||
# examples/strategies/custom_strategy_example.py
|
||||
|
||||
"""
|
||||
Exemple de création d'une stratégie custom.
|
||||
|
||||
Cet exemple montre comment:
|
||||
- Hériter de BaseStrategy
|
||||
- Implémenter les méthodes requises
|
||||
- Configurer les paramètres
|
||||
- Backtester la stratégie
|
||||
"""
|
||||
|
||||
from src.strategies.base_strategy import BaseStrategy
|
||||
|
||||
class MyCustomStrategy(BaseStrategy):
|
||||
"""Ma stratégie personnalisée"""
|
||||
|
||||
def analyze(self, data):
|
||||
# Implémentation
|
||||
pass
|
||||
|
||||
# Usage
|
||||
if __name__ == "__main__":
|
||||
strategy = MyCustomStrategy(config)
|
||||
results = backtest_strategy(strategy, data)
|
||||
print(results)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 👀 Review Process
|
||||
|
||||
### Checklist PR
|
||||
|
||||
Avant de soumettre PR, vérifier :
|
||||
|
||||
- [ ] Code suit standards (PEP 8, type hints, docstrings)
|
||||
- [ ] Tests ajoutés et passent (coverage > 85%)
|
||||
- [ ] Documentation mise à jour
|
||||
- [ ] Pas de secrets/credentials dans le code
|
||||
- [ ] Commits suivent conventions
|
||||
- [ ] Branch à jour avec develop
|
||||
- [ ] Pas de conflits
|
||||
|
||||
### Template Pull Request
|
||||
|
||||
```markdown
|
||||
## Description
|
||||
Description claire des changements.
|
||||
|
||||
## Type de Changement
|
||||
- [ ] Bug fix
|
||||
- [ ] Nouvelle feature
|
||||
- [ ] Breaking change
|
||||
- [ ] Documentation
|
||||
|
||||
## Tests
|
||||
- [ ] Tests unitaires ajoutés
|
||||
- [ ] Tests d'intégration ajoutés
|
||||
- [ ] Tous les tests passent
|
||||
- [ ] Coverage > 85%
|
||||
|
||||
## Checklist
|
||||
- [ ] Code suit standards
|
||||
- [ ] Documentation mise à jour
|
||||
- [ ] Pas de secrets dans le code
|
||||
- [ ] Commits conventionnels
|
||||
|
||||
## Screenshots (si applicable)
|
||||
|
||||
## Notes Additionnelles
|
||||
```
|
||||
|
||||
### Process de Review
|
||||
|
||||
1. **Automated Checks** : CI/CD vérifie tests, linting
|
||||
2. **Code Review** : Au moins 1 reviewer approuve
|
||||
3. **Testing** : Reviewer teste localement
|
||||
4. **Merge** : Squash and merge vers develop
|
||||
|
||||
---
|
||||
|
||||
## 🎯 Priorités Contributions
|
||||
|
||||
### High Priority
|
||||
|
||||
- 🔴 Corrections de bugs critiques
|
||||
- 🔴 Améliorations sécurité
|
||||
- 🔴 Optimisations performance
|
||||
|
||||
### Medium Priority
|
||||
|
||||
- 🟡 Nouvelles stratégies
|
||||
- 🟡 Améliorations ML
|
||||
- 🟡 Documentation
|
||||
|
||||
### Low Priority
|
||||
|
||||
- 🟢 Refactoring
|
||||
- 🟢 Optimisations mineures
|
||||
- 🟢 Traductions
|
||||
|
||||
---
|
||||
|
||||
## 💬 Communication
|
||||
|
||||
### Channels
|
||||
|
||||
- **GitHub Issues** : Bugs, features
|
||||
- **GitHub Discussions** : Questions, idées
|
||||
- **Discord** : Chat temps réel
|
||||
- **Email** : contact@trading-ai-secure.com
|
||||
|
||||
### Réponse
|
||||
|
||||
- Issues : < 48h
|
||||
- PRs : < 72h
|
||||
- Questions : < 24h
|
||||
|
||||
---
|
||||
|
||||
## 🏆 Reconnaissance
|
||||
|
||||
Les contributeurs sont reconnus dans :
|
||||
|
||||
- `CONTRIBUTORS.md`
|
||||
- Release notes
|
||||
- Documentation
|
||||
|
||||
---
|
||||
|
||||
**Merci de contribuer à Trading AI Secure ! 🚀**
|
||||
540
docs/GETTING_STARTED.md
Normal file
540
docs/GETTING_STARTED.md
Normal file
@@ -0,0 +1,540 @@
|
||||
# 🚀 Guide de Démarrage - Trading AI Secure
|
||||
|
||||
## 📋 Table des Matières
|
||||
1. [Prérequis](#prérequis)
|
||||
2. [Installation](#installation)
|
||||
3. [Configuration](#configuration)
|
||||
4. [Premier Lancement](#premier-lancement)
|
||||
5. [Workflow Développement](#workflow-développement)
|
||||
6. [Tests](#tests)
|
||||
7. [Troubleshooting](#troubleshooting)
|
||||
|
||||
---
|
||||
|
||||
## 💻 Prérequis
|
||||
|
||||
### Système
|
||||
|
||||
- **OS** : Windows 10/11, Linux (Ubuntu 20.04+), macOS 11+
|
||||
- **Python** : 3.11 ou supérieur
|
||||
- **RAM** : 8 GB minimum (16 GB recommandé)
|
||||
- **Disque** : 10 GB espace libre
|
||||
- **Internet** : Connexion stable pour données temps réel
|
||||
|
||||
### Logiciels
|
||||
|
||||
```bash
|
||||
# Python 3.11+
|
||||
python --version # Doit afficher 3.11.x ou supérieur
|
||||
|
||||
# pip (gestionnaire de paquets)
|
||||
pip --version
|
||||
|
||||
# Git
|
||||
git --version
|
||||
|
||||
# (Optionnel) Docker
|
||||
docker --version
|
||||
```
|
||||
|
||||
### Connaissances Recommandées
|
||||
|
||||
- ✅ Python intermédiaire
|
||||
- ✅ Bases de trading (ordres, stop-loss, etc.)
|
||||
- ✅ Notions de machine learning (optionnel)
|
||||
- ✅ Git basique
|
||||
|
||||
---
|
||||
|
||||
## 📥 Installation
|
||||
|
||||
### Étape 1 : Cloner le Repository
|
||||
|
||||
```bash
|
||||
# Cloner le projet
|
||||
git clone https://github.com/votre-username/trading-ai-secure.git
|
||||
cd trading-ai-secure
|
||||
|
||||
# Vérifier structure
|
||||
ls -la
|
||||
```
|
||||
|
||||
### Étape 2 : Créer Environnement Virtuel
|
||||
|
||||
```bash
|
||||
# Windows
|
||||
python -m venv venv
|
||||
venv\Scripts\activate
|
||||
|
||||
# Linux/macOS
|
||||
python3 -m venv venv
|
||||
source venv/bin/activate
|
||||
|
||||
# Vérifier activation (prompt doit afficher (venv))
|
||||
```
|
||||
|
||||
### Étape 3 : Installer Dépendances
|
||||
|
||||
```bash
|
||||
# Mettre à jour pip
|
||||
pip install --upgrade pip
|
||||
|
||||
# Installer dépendances
|
||||
pip install -r requirements.txt
|
||||
|
||||
# Vérifier installation
|
||||
pip list
|
||||
```
|
||||
|
||||
### Étape 4 : Installer Dépendances Optionnelles
|
||||
|
||||
```bash
|
||||
# Pour développement
|
||||
pip install -r requirements-dev.txt
|
||||
|
||||
# Pour backtesting avancé
|
||||
pip install -r requirements-backtest.txt
|
||||
|
||||
# Pour production
|
||||
pip install -r requirements-prod.txt
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## ⚙️ Configuration
|
||||
|
||||
### Étape 1 : Copier Fichiers de Configuration
|
||||
|
||||
```bash
|
||||
# Copier templates de configuration
|
||||
cp config/risk_limits.example.yaml config/risk_limits.yaml
|
||||
cp config/strategy_params.example.yaml config/strategy_params.yaml
|
||||
cp config/data_sources.example.yaml config/data_sources.yaml
|
||||
|
||||
# NE PAS copier ig_config (contient credentials sensibles)
|
||||
# Créer manuellement si nécessaire
|
||||
```
|
||||
|
||||
### Étape 2 : Configurer Sources de Données
|
||||
|
||||
```yaml
|
||||
# config/data_sources.yaml
|
||||
|
||||
data_sources:
|
||||
# Yahoo Finance (gratuit, illimité)
|
||||
yahoo_finance:
|
||||
enabled: true
|
||||
priority: 1
|
||||
|
||||
# Alpha Vantage (gratuit, 500 calls/jour)
|
||||
alpha_vantage:
|
||||
enabled: true
|
||||
api_key: "YOUR_API_KEY_HERE" # Obtenir sur https://www.alphavantage.co/support/#api-key
|
||||
priority: 2
|
||||
rate_limit: 500 # calls per day
|
||||
|
||||
# Twelve Data (gratuit, 800 calls/jour)
|
||||
twelve_data:
|
||||
enabled: false
|
||||
api_key: "YOUR_API_KEY_HERE" # Obtenir sur https://twelvedata.com/
|
||||
priority: 3
|
||||
rate_limit: 800
|
||||
```
|
||||
|
||||
### Étape 3 : Obtenir Clés API Gratuites
|
||||
|
||||
#### Alpha Vantage (Recommandé)
|
||||
|
||||
1. Aller sur https://www.alphavantage.co/support/#api-key
|
||||
2. Entrer email
|
||||
3. Copier clé API
|
||||
4. Coller dans `config/data_sources.yaml`
|
||||
|
||||
#### Twelve Data (Optionnel)
|
||||
|
||||
1. Créer compte sur https://twelvedata.com/
|
||||
2. Aller dans Dashboard → API
|
||||
3. Copier clé
|
||||
4. Coller dans config
|
||||
|
||||
### Étape 4 : Configurer Risk Limits
|
||||
|
||||
```yaml
|
||||
# config/risk_limits.yaml
|
||||
|
||||
global_limits:
|
||||
max_portfolio_risk: 0.02 # 2% du capital max
|
||||
max_position_size: 0.05 # 5% par position max
|
||||
max_correlation: 0.7 # Corrélation max entre positions
|
||||
max_drawdown: 0.10 # 10% drawdown max
|
||||
daily_loss_limit: 0.03 # 3% perte journalière max
|
||||
|
||||
strategy_limits:
|
||||
scalping:
|
||||
max_trades_per_day: 50
|
||||
risk_per_trade: 0.005 # 0.5% par trade
|
||||
max_holding_time: 1800 # 30 minutes
|
||||
|
||||
intraday:
|
||||
max_trades_per_day: 10
|
||||
risk_per_trade: 0.015 # 1.5% par trade
|
||||
max_holding_time: 86400 # 1 jour
|
||||
|
||||
swing:
|
||||
max_trades_per_week: 5
|
||||
risk_per_trade: 0.025 # 2.5% par trade
|
||||
max_holding_time: 432000 # 5 jours
|
||||
```
|
||||
|
||||
### Étape 5 : Variables d'Environnement
|
||||
|
||||
```bash
|
||||
# Créer fichier .env
|
||||
touch .env
|
||||
|
||||
# Ajouter variables (NE PAS COMMITER)
|
||||
echo "ENVIRONMENT=development" >> .env
|
||||
echo "LOG_LEVEL=INFO" >> .env
|
||||
echo "INITIAL_CAPITAL=10000" >> .env
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🎬 Premier Lancement
|
||||
|
||||
### Mode 1 : Backtesting (Recommandé pour débuter)
|
||||
|
||||
```bash
|
||||
# Lancer backtesting sur données historiques
|
||||
python src/main.py --mode backtest --strategy intraday --symbol EURUSD --period 1y
|
||||
|
||||
# Avec paramètres personnalisés
|
||||
python src/main.py \
|
||||
--mode backtest \
|
||||
--strategy all \
|
||||
--symbol EURUSD,GBPUSD,USDJPY \
|
||||
--period 2y \
|
||||
--initial-capital 10000
|
||||
```
|
||||
|
||||
**Sortie attendue** :
|
||||
```
|
||||
[INFO] Loading historical data for EURUSD...
|
||||
[INFO] Backtesting intraday strategy...
|
||||
[INFO] Walk-forward analysis: 12 periods
|
||||
[INFO] Results:
|
||||
- Total Return: 15.3%
|
||||
- Sharpe Ratio: 1.82
|
||||
- Max Drawdown: 7.2%
|
||||
- Win Rate: 58.3%
|
||||
- Total Trades: 127
|
||||
```
|
||||
|
||||
### Mode 2 : Paper Trading
|
||||
|
||||
```bash
|
||||
# Lancer paper trading (simulation temps réel)
|
||||
python src/main.py --mode paper --strategy intraday
|
||||
|
||||
# Avec dashboard
|
||||
python src/main.py --mode paper --strategy all --dashboard
|
||||
```
|
||||
|
||||
### Mode 3 : Dashboard Uniquement
|
||||
|
||||
```bash
|
||||
# Lancer dashboard Streamlit
|
||||
streamlit run src/ui/dashboard.py
|
||||
|
||||
# Ouvrir navigateur sur http://localhost:8501
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🔄 Workflow Développement
|
||||
|
||||
### Workflow Quotidien
|
||||
|
||||
```bash
|
||||
# 1. Activer environnement
|
||||
source venv/bin/activate # Linux/macOS
|
||||
venv\Scripts\activate # Windows
|
||||
|
||||
# 2. Mettre à jour code
|
||||
git pull origin main
|
||||
|
||||
# 3. Installer nouvelles dépendances si nécessaire
|
||||
pip install -r requirements.txt
|
||||
|
||||
# 4. Lancer tests
|
||||
pytest tests/
|
||||
|
||||
# 5. Développer nouvelle feature
|
||||
# ... éditer code ...
|
||||
|
||||
# 6. Tester localement
|
||||
python src/main.py --mode backtest --strategy your_strategy
|
||||
|
||||
# 7. Commit et push
|
||||
git add .
|
||||
git commit -m "feat: add new strategy"
|
||||
git push origin your-branch
|
||||
```
|
||||
|
||||
### Structure Développement
|
||||
|
||||
```
|
||||
Développement d'une nouvelle stratégie:
|
||||
|
||||
1. Créer fichier stratégie
|
||||
src/strategies/your_strategy/your_strategy.py
|
||||
|
||||
2. Hériter de BaseStrategy
|
||||
class YourStrategy(BaseStrategy):
|
||||
...
|
||||
|
||||
3. Implémenter méthodes requises
|
||||
- calculate_indicators()
|
||||
- analyze()
|
||||
- _calculate_confidence()
|
||||
|
||||
4. Créer tests
|
||||
tests/test_your_strategy.py
|
||||
|
||||
5. Backtester
|
||||
python src/main.py --mode backtest --strategy your_strategy
|
||||
|
||||
6. Valider métriques
|
||||
- Sharpe > 1.5
|
||||
- Max DD < 10%
|
||||
- Win Rate > 55%
|
||||
|
||||
7. Paper trading 30 jours
|
||||
python src/main.py --mode paper --strategy your_strategy
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🧪 Tests
|
||||
|
||||
### Lancer Tests Unitaires
|
||||
|
||||
```bash
|
||||
# Tous les tests
|
||||
pytest
|
||||
|
||||
# Tests spécifiques
|
||||
pytest tests/test_risk_manager.py
|
||||
pytest tests/test_strategies.py
|
||||
|
||||
# Avec couverture
|
||||
pytest --cov=src tests/
|
||||
|
||||
# Avec rapport HTML
|
||||
pytest --cov=src --cov-report=html tests/
|
||||
# Ouvrir htmlcov/index.html
|
||||
```
|
||||
|
||||
### Tests d'Intégration
|
||||
|
||||
```bash
|
||||
# Tests intégration données
|
||||
pytest tests/integration/test_data_sources.py
|
||||
|
||||
# Tests intégration IG (nécessite credentials)
|
||||
pytest tests/integration/test_ig_api.py --ig-demo
|
||||
```
|
||||
|
||||
### Tests de Performance
|
||||
|
||||
```bash
|
||||
# Benchmark stratégies
|
||||
python tests/benchmark/benchmark_strategies.py
|
||||
|
||||
# Profiling
|
||||
python -m cProfile -o profile.stats src/main.py --mode backtest
|
||||
python -m pstats profile.stats
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🐛 Troubleshooting
|
||||
|
||||
### Problème : Installation Dépendances Échoue
|
||||
|
||||
```bash
|
||||
# Erreur: "Could not find a version that satisfies the requirement..."
|
||||
|
||||
# Solution 1: Mettre à jour pip
|
||||
pip install --upgrade pip setuptools wheel
|
||||
|
||||
# Solution 2: Installer individuellement
|
||||
pip install numpy pandas scikit-learn
|
||||
|
||||
# Solution 3: Utiliser conda (si disponible)
|
||||
conda install -c conda-forge numpy pandas scikit-learn
|
||||
```
|
||||
|
||||
### Problème : Erreur Import Module
|
||||
|
||||
```bash
|
||||
# Erreur: "ModuleNotFoundError: No module named 'src'"
|
||||
|
||||
# Solution: Ajouter projet au PYTHONPATH
|
||||
export PYTHONPATH="${PYTHONPATH}:$(pwd)" # Linux/macOS
|
||||
set PYTHONPATH=%PYTHONPATH%;%CD% # Windows
|
||||
|
||||
# Ou installer en mode développement
|
||||
pip install -e .
|
||||
```
|
||||
|
||||
### Problème : API Rate Limit Dépassé
|
||||
|
||||
```bash
|
||||
# Erreur: "API rate limit exceeded"
|
||||
|
||||
# Solution 1: Utiliser cache
|
||||
# Éditer config/data_sources.yaml
|
||||
cache:
|
||||
enabled: true
|
||||
ttl: 3600 # 1 heure
|
||||
|
||||
# Solution 2: Alterner sources
|
||||
# Activer multiple sources dans config
|
||||
|
||||
# Solution 3: Réduire fréquence requêtes
|
||||
# Augmenter timeframe ou réduire nombre de symboles
|
||||
```
|
||||
|
||||
### Problème : Backtesting Trop Lent
|
||||
|
||||
```bash
|
||||
# Solution 1: Réduire période
|
||||
python src/main.py --mode backtest --period 6m # 6 mois au lieu de 2 ans
|
||||
|
||||
# Solution 2: Utiliser données cached
|
||||
# Activer cache dans config
|
||||
|
||||
# Solution 3: Paralléliser
|
||||
python src/main.py --mode backtest --parallel --workers 4
|
||||
```
|
||||
|
||||
### Problème : Dashboard Ne Se Lance Pas
|
||||
|
||||
```bash
|
||||
# Erreur: "streamlit: command not found"
|
||||
|
||||
# Solution: Réinstaller streamlit
|
||||
pip install --upgrade streamlit
|
||||
|
||||
# Vérifier installation
|
||||
streamlit --version
|
||||
|
||||
# Lancer avec chemin complet
|
||||
python -m streamlit run src/ui/dashboard.py
|
||||
```
|
||||
|
||||
### Problème : Credentials IG Invalides
|
||||
|
||||
```bash
|
||||
# Erreur: "Authentication failed"
|
||||
|
||||
# Vérifications:
|
||||
# 1. API key correcte
|
||||
# 2. Username/password corrects
|
||||
# 3. Compte démo activé
|
||||
# 4. Pas de caractères spéciaux dans password
|
||||
|
||||
# Tester connexion
|
||||
python tests/test_ig_connection.py
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📚 Ressources Supplémentaires
|
||||
|
||||
### Documentation
|
||||
|
||||
- [Architecture Détaillée](ARCHITECTURE.md)
|
||||
- [Framework IA](AI_FRAMEWORK.md)
|
||||
- [Risk Management](RISK_FRAMEWORK.md)
|
||||
- [Guide Stratégies](STRATEGY_GUIDE.md)
|
||||
- [Backtesting](BACKTESTING_GUIDE.md)
|
||||
- [Intégration IG](IG_INTEGRATION.md)
|
||||
|
||||
### Tutoriels
|
||||
|
||||
```bash
|
||||
# Tutoriel 1: Créer première stratégie
|
||||
python tutorials/01_create_strategy.py
|
||||
|
||||
# Tutoriel 2: Backtesting avancé
|
||||
python tutorials/02_advanced_backtesting.py
|
||||
|
||||
# Tutoriel 3: Optimisation paramètres
|
||||
python tutorials/03_parameter_optimization.py
|
||||
```
|
||||
|
||||
### Exemples
|
||||
|
||||
```bash
|
||||
# Exemples de stratégies
|
||||
ls examples/strategies/
|
||||
|
||||
# Exemples de backtests
|
||||
ls examples/backtests/
|
||||
|
||||
# Exemples de configurations
|
||||
ls examples/configs/
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🎯 Prochaines Étapes
|
||||
|
||||
### Semaine 1 : Familiarisation
|
||||
|
||||
- [ ] Installer et configurer environnement
|
||||
- [ ] Lancer premier backtest
|
||||
- [ ] Explorer dashboard
|
||||
- [ ] Lire documentation
|
||||
|
||||
### Semaine 2 : Expérimentation
|
||||
|
||||
- [ ] Tester différentes stratégies
|
||||
- [ ] Ajuster paramètres risk
|
||||
- [ ] Analyser résultats backtests
|
||||
- [ ] Comprendre métriques
|
||||
|
||||
### Semaine 3 : Développement
|
||||
|
||||
- [ ] Créer première stratégie custom
|
||||
- [ ] Implémenter tests
|
||||
- [ ] Backtester sur multiple périodes
|
||||
- [ ] Optimiser paramètres
|
||||
|
||||
### Semaine 4 : Validation
|
||||
|
||||
- [ ] Walk-forward analysis
|
||||
- [ ] Monte Carlo simulation
|
||||
- [ ] Paper trading
|
||||
- [ ] Documenter résultats
|
||||
|
||||
---
|
||||
|
||||
## 💬 Support
|
||||
|
||||
### Obtenir de l'Aide
|
||||
|
||||
1. **Documentation** : Lire docs/ en premier
|
||||
2. **Issues GitHub** : Créer issue si bug
|
||||
3. **Discussions** : Forum communauté
|
||||
4. **Discord** : Chat temps réel
|
||||
|
||||
### Contribuer
|
||||
|
||||
Voir [CONTRIBUTING.md](CONTRIBUTING.md) pour guidelines.
|
||||
|
||||
---
|
||||
|
||||
**Bon trading ! 🚀**
|
||||
719
docs/IG_INTEGRATION.md
Normal file
719
docs/IG_INTEGRATION.md
Normal file
@@ -0,0 +1,719 @@
|
||||
# 🔌 Guide d'Intégration IG Markets - Trading AI Secure
|
||||
|
||||
## 📋 Table des Matières
|
||||
1. [Vue d'ensemble IG Markets](#vue-densemble-ig-markets)
|
||||
2. [Configuration Compte](#configuration-compte)
|
||||
3. [API REST](#api-rest)
|
||||
4. [Streaming Lightstreamer](#streaming-lightstreamer)
|
||||
5. [Gestion des Ordres](#gestion-des-ordres)
|
||||
6. [Risk Management IG](#risk-management-ig)
|
||||
7. [Migration Progressive](#migration-progressive)
|
||||
8. [Implémentation](#implémentation)
|
||||
|
||||
---
|
||||
|
||||
## 🎯 Vue d'ensemble IG Markets
|
||||
|
||||
### Pourquoi IG Markets ?
|
||||
|
||||
- ✅ **API complète** : REST + Streaming temps réel
|
||||
- ✅ **Compte démo gratuit** : Test sans risque
|
||||
- ✅ **Large gamme d'instruments** : Forex, Indices, Actions, Crypto
|
||||
- ✅ **CFD et DMA** : Flexibilité trading
|
||||
- ✅ **Guaranteed stops** : Protection slippage
|
||||
- ✅ **Documentation** : API bien documentée
|
||||
|
||||
### Architecture IG
|
||||
|
||||
```
|
||||
┌──────────────────────────────────────────────────────────┐
|
||||
│ IG MARKETS API │
|
||||
├──────────────────────────────────────────────────────────┤
|
||||
│ │
|
||||
│ REST API STREAMING API │
|
||||
│ ├─ Authentication ├─ Lightstreamer │
|
||||
│ ├─ Account Info ├─ Prix temps réel │
|
||||
│ ├─ Market Data ├─ Positions updates │
|
||||
│ ├─ Orders (CRUD) └─ Account updates │
|
||||
│ ├─ Positions │
|
||||
│ └─ Historical Data │
|
||||
│ │
|
||||
└──────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🔐 Configuration Compte
|
||||
|
||||
### Étape 1 : Créer Compte Démo
|
||||
|
||||
1. **S'inscrire** : https://www.ig.com/uk/demo-account
|
||||
2. **Activer compte démo** : Capital virtuel £10,000
|
||||
3. **Accéder API** : Dashboard → API → Generate API Key
|
||||
|
||||
### Étape 2 : Obtenir Credentials
|
||||
|
||||
```yaml
|
||||
# config/ig_config.yaml (EXEMPLE - NE PAS COMMITER)
|
||||
ig_credentials:
|
||||
# Démo
|
||||
demo:
|
||||
api_key: "YOUR_DEMO_API_KEY"
|
||||
username: "YOUR_DEMO_USERNAME"
|
||||
password: "YOUR_DEMO_PASSWORD"
|
||||
account_id: "YOUR_DEMO_ACCOUNT_ID"
|
||||
api_url: "https://demo-api.ig.com/gateway/deal"
|
||||
|
||||
# Live (À ACTIVER APRÈS VALIDATION)
|
||||
live:
|
||||
api_key: "YOUR_LIVE_API_KEY"
|
||||
username: "YOUR_LIVE_USERNAME"
|
||||
password: "YOUR_LIVE_PASSWORD"
|
||||
account_id: "YOUR_LIVE_ACCOUNT_ID"
|
||||
api_url: "https://api.ig.com/gateway/deal"
|
||||
|
||||
# Lightstreamer
|
||||
lightstreamer:
|
||||
demo_url: "https://demo-apd.marketdatasys.com"
|
||||
live_url: "https://apd.marketdatasys.com"
|
||||
```
|
||||
|
||||
### Étape 3 : Sécuriser Credentials
|
||||
|
||||
```python
|
||||
# src/core/config_manager.py
|
||||
|
||||
import os
|
||||
from cryptography.fernet import Fernet
|
||||
import yaml
|
||||
|
||||
class SecureConfigManager:
|
||||
"""
|
||||
Gestion sécurisée des credentials IG
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
# Générer clé encryption (à stocker en variable d'environnement)
|
||||
self.key = os.getenv('ENCRYPTION_KEY') or Fernet.generate_key()
|
||||
self.cipher = Fernet(self.key)
|
||||
|
||||
def load_ig_credentials(self, environment='demo'):
|
||||
"""
|
||||
Charge credentials IG de manière sécurisée
|
||||
"""
|
||||
with open('config/ig_config.yaml', 'r') as f:
|
||||
config = yaml.safe_load(f)
|
||||
|
||||
credentials = config['ig_credentials'][environment]
|
||||
|
||||
# Décrypter password si encrypté
|
||||
if credentials['password'].startswith('encrypted:'):
|
||||
encrypted_password = credentials['password'].replace('encrypted:', '')
|
||||
credentials['password'] = self.cipher.decrypt(
|
||||
encrypted_password.encode()
|
||||
).decode()
|
||||
|
||||
return credentials
|
||||
|
||||
def encrypt_password(self, password: str) -> str:
|
||||
"""Encrypte password"""
|
||||
return 'encrypted:' + self.cipher.encrypt(password.encode()).decode()
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🌐 API REST
|
||||
|
||||
### Authentification
|
||||
|
||||
```python
|
||||
# src/data/ig_connector.py
|
||||
|
||||
import requests
|
||||
from typing import Dict, Optional
|
||||
import logging
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
class IGRestAPI:
|
||||
"""
|
||||
Connecteur IG REST API
|
||||
"""
|
||||
|
||||
def __init__(self, credentials: Dict):
|
||||
self.api_key = credentials['api_key']
|
||||
self.username = credentials['username']
|
||||
self.password = credentials['password']
|
||||
self.account_id = credentials['account_id']
|
||||
self.base_url = credentials['api_url']
|
||||
|
||||
self.session = requests.Session()
|
||||
self.access_token = None
|
||||
self.cst_token = None
|
||||
self.security_token = None
|
||||
|
||||
def authenticate(self) -> bool:
|
||||
"""
|
||||
Authentification IG API
|
||||
|
||||
Returns:
|
||||
True si succès, False sinon
|
||||
"""
|
||||
url = f"{self.base_url}/session"
|
||||
|
||||
headers = {
|
||||
'Content-Type': 'application/json',
|
||||
'Accept': 'application/json',
|
||||
'X-IG-API-KEY': self.api_key,
|
||||
'Version': '2'
|
||||
}
|
||||
|
||||
payload = {
|
||||
'identifier': self.username,
|
||||
'password': self.password
|
||||
}
|
||||
|
||||
try:
|
||||
response = self.session.post(url, json=payload, headers=headers)
|
||||
response.raise_for_status()
|
||||
|
||||
# Extraire tokens
|
||||
self.cst_token = response.headers.get('CST')
|
||||
self.security_token = response.headers.get('X-SECURITY-TOKEN')
|
||||
|
||||
# Sélectionner compte
|
||||
self._select_account(self.account_id)
|
||||
|
||||
logger.info("IG API authentication successful")
|
||||
return True
|
||||
|
||||
except requests.exceptions.RequestException as e:
|
||||
logger.error(f"IG API authentication failed: {e}")
|
||||
return False
|
||||
|
||||
def _select_account(self, account_id: str):
|
||||
"""Sélectionne compte de trading"""
|
||||
url = f"{self.base_url}/session"
|
||||
|
||||
headers = self._get_headers()
|
||||
headers['Version'] = '1'
|
||||
headers['_method'] = 'PUT'
|
||||
|
||||
payload = {
|
||||
'accountId': account_id,
|
||||
'defaultAccount': True
|
||||
}
|
||||
|
||||
response = self.session.put(url, json=payload, headers=headers)
|
||||
response.raise_for_status()
|
||||
|
||||
def _get_headers(self) -> Dict:
|
||||
"""Headers pour requêtes authentifiées"""
|
||||
return {
|
||||
'Content-Type': 'application/json',
|
||||
'Accept': 'application/json',
|
||||
'X-IG-API-KEY': self.api_key,
|
||||
'CST': self.cst_token,
|
||||
'X-SECURITY-TOKEN': self.security_token
|
||||
}
|
||||
|
||||
def get_account_info(self) -> Dict:
|
||||
"""Récupère informations compte"""
|
||||
url = f"{self.base_url}/accounts"
|
||||
|
||||
response = self.session.get(url, headers=self._get_headers())
|
||||
response.raise_for_status()
|
||||
|
||||
return response.json()
|
||||
|
||||
def get_market_details(self, epic: str) -> Dict:
|
||||
"""
|
||||
Récupère détails d'un marché
|
||||
|
||||
Args:
|
||||
epic: Identifiant marché IG (ex: 'CS.D.EURUSD.MINI.IP')
|
||||
"""
|
||||
url = f"{self.base_url}/markets/{epic}"
|
||||
|
||||
headers = self._get_headers()
|
||||
headers['Version'] = '3'
|
||||
|
||||
response = self.session.get(url, headers=headers)
|
||||
response.raise_for_status()
|
||||
|
||||
return response.json()
|
||||
|
||||
def get_positions(self) -> Dict:
|
||||
"""Récupère positions ouvertes"""
|
||||
url = f"{self.base_url}/positions"
|
||||
|
||||
headers = self._get_headers()
|
||||
headers['Version'] = '2'
|
||||
|
||||
response = self.session.get(url, headers=headers)
|
||||
response.raise_for_status()
|
||||
|
||||
return response.json()
|
||||
|
||||
def place_order(
|
||||
self,
|
||||
epic: str,
|
||||
direction: str,
|
||||
size: float,
|
||||
stop_loss: Optional[float] = None,
|
||||
take_profit: Optional[float] = None,
|
||||
guaranteed_stop: bool = True
|
||||
) -> Dict:
|
||||
"""
|
||||
Place ordre de trading
|
||||
|
||||
Args:
|
||||
epic: Identifiant marché
|
||||
direction: 'BUY' ou 'SELL'
|
||||
size: Taille position
|
||||
stop_loss: Niveau stop-loss
|
||||
take_profit: Niveau take-profit
|
||||
guaranteed_stop: Utiliser guaranteed stop (recommandé)
|
||||
"""
|
||||
url = f"{self.base_url}/positions/otc"
|
||||
|
||||
headers = self._get_headers()
|
||||
headers['Version'] = '2'
|
||||
|
||||
payload = {
|
||||
'epic': epic,
|
||||
'direction': direction,
|
||||
'size': size,
|
||||
'orderType': 'MARKET',
|
||||
'timeInForce': 'FILL_OR_KILL',
|
||||
'guaranteedStop': guaranteed_stop,
|
||||
'currencyCode': 'GBP'
|
||||
}
|
||||
|
||||
# Ajouter stop-loss
|
||||
if stop_loss:
|
||||
payload['stopLevel'] = stop_loss
|
||||
payload['stopDistance'] = None # Calculé automatiquement
|
||||
|
||||
# Ajouter take-profit
|
||||
if take_profit:
|
||||
payload['limitLevel'] = take_profit
|
||||
payload['limitDistance'] = None
|
||||
|
||||
try:
|
||||
response = self.session.post(url, json=payload, headers=headers)
|
||||
response.raise_for_status()
|
||||
|
||||
result = response.json()
|
||||
deal_reference = result.get('dealReference')
|
||||
|
||||
# Confirmer ordre
|
||||
return self._confirm_order(deal_reference)
|
||||
|
||||
except requests.exceptions.RequestException as e:
|
||||
logger.error(f"Order placement failed: {e}")
|
||||
raise
|
||||
|
||||
def _confirm_order(self, deal_reference: str) -> Dict:
|
||||
"""Confirme exécution ordre"""
|
||||
url = f"{self.base_url}/confirms/{deal_reference}"
|
||||
|
||||
headers = self._get_headers()
|
||||
headers['Version'] = '1'
|
||||
|
||||
response = self.session.get(url, headers=headers)
|
||||
response.raise_for_status()
|
||||
|
||||
return response.json()
|
||||
|
||||
def close_position(self, deal_id: str) -> Dict:
|
||||
"""Ferme position"""
|
||||
# Récupérer détails position
|
||||
position = self._get_position_details(deal_id)
|
||||
|
||||
url = f"{self.base_url}/positions/otc"
|
||||
|
||||
headers = self._get_headers()
|
||||
headers['Version'] = '1'
|
||||
headers['_method'] = 'DELETE'
|
||||
|
||||
payload = {
|
||||
'dealId': deal_id,
|
||||
'direction': 'SELL' if position['direction'] == 'BUY' else 'BUY',
|
||||
'size': position['size'],
|
||||
'orderType': 'MARKET'
|
||||
}
|
||||
|
||||
response = self.session.delete(url, json=payload, headers=headers)
|
||||
response.raise_for_status()
|
||||
|
||||
return response.json()
|
||||
|
||||
def get_historical_prices(
|
||||
self,
|
||||
epic: str,
|
||||
resolution: str = 'MINUTE',
|
||||
num_points: int = 100
|
||||
) -> Dict:
|
||||
"""
|
||||
Récupère prix historiques
|
||||
|
||||
Args:
|
||||
epic: Identifiant marché
|
||||
resolution: 'SECOND', 'MINUTE', 'MINUTE_5', 'HOUR', 'DAY'
|
||||
num_points: Nombre de points (max 1000)
|
||||
"""
|
||||
url = f"{self.base_url}/prices/{epic}"
|
||||
|
||||
headers = self._get_headers()
|
||||
headers['Version'] = '3'
|
||||
|
||||
params = {
|
||||
'resolution': resolution,
|
||||
'max': min(num_points, 1000)
|
||||
}
|
||||
|
||||
response = self.session.get(url, headers=headers, params=params)
|
||||
response.raise_for_status()
|
||||
|
||||
return response.json()
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📡 Streaming Lightstreamer
|
||||
|
||||
### Configuration
|
||||
|
||||
```python
|
||||
# src/data/ig_streaming.py
|
||||
|
||||
from lightstreamer.client import LightstreamerClient, Subscription
|
||||
import logging
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
class IGStreamingAPI:
|
||||
"""
|
||||
Connecteur IG Streaming (Lightstreamer)
|
||||
|
||||
Permet de recevoir:
|
||||
- Prix en temps réel
|
||||
- Updates positions
|
||||
- Updates compte
|
||||
"""
|
||||
|
||||
def __init__(self, credentials: Dict, cst_token: str, security_token: str):
|
||||
self.lightstreamer_url = credentials.get('lightstreamer_url')
|
||||
self.cst_token = cst_token
|
||||
self.security_token = security_token
|
||||
self.account_id = credentials['account_id']
|
||||
|
||||
self.client = None
|
||||
self.subscriptions = {}
|
||||
|
||||
def connect(self):
|
||||
"""Connexion Lightstreamer"""
|
||||
self.client = LightstreamerClient(self.lightstreamer_url, "DEFAULT")
|
||||
|
||||
# Configuration connexion
|
||||
self.client.connectionDetails.setUser(self.account_id)
|
||||
self.client.connectionDetails.setPassword(f"CST-{self.cst_token}|XST-{self.security_token}")
|
||||
|
||||
# Listeners
|
||||
self.client.addListener(ConnectionListener())
|
||||
|
||||
# Connecter
|
||||
self.client.connect()
|
||||
|
||||
logger.info("Lightstreamer connection established")
|
||||
|
||||
def subscribe_market(self, epic: str, callback):
|
||||
"""
|
||||
Subscribe à prix temps réel d'un marché
|
||||
|
||||
Args:
|
||||
epic: Identifiant marché
|
||||
callback: Fonction appelée à chaque update
|
||||
"""
|
||||
# Items à subscribe
|
||||
items = [f"MARKET:{epic}"]
|
||||
|
||||
# Fields à recevoir
|
||||
fields = [
|
||||
"BID", "OFFER", "HIGH", "LOW",
|
||||
"MID_OPEN", "CHANGE", "CHANGE_PCT",
|
||||
"UPDATE_TIME", "MARKET_STATE"
|
||||
]
|
||||
|
||||
# Créer subscription
|
||||
subscription = Subscription(
|
||||
mode="MERGE",
|
||||
items=items,
|
||||
fields=fields
|
||||
)
|
||||
|
||||
# Ajouter listener
|
||||
subscription.addListener(MarketDataListener(callback))
|
||||
|
||||
# Subscribe
|
||||
self.client.subscribe(subscription)
|
||||
self.subscriptions[epic] = subscription
|
||||
|
||||
logger.info(f"Subscribed to market: {epic}")
|
||||
|
||||
def subscribe_account(self, callback):
|
||||
"""Subscribe à updates compte"""
|
||||
items = [f"ACCOUNT:{self.account_id}"]
|
||||
|
||||
fields = [
|
||||
"AVAILABLE_CASH", "PNL", "DEPOSIT",
|
||||
"USED_MARGIN", "AVAILABLE_TO_DEAL", "EQUITY"
|
||||
]
|
||||
|
||||
subscription = Subscription(
|
||||
mode="MERGE",
|
||||
items=items,
|
||||
fields=fields
|
||||
)
|
||||
|
||||
subscription.addListener(AccountListener(callback))
|
||||
|
||||
self.client.subscribe(subscription)
|
||||
self.subscriptions['account'] = subscription
|
||||
|
||||
def unsubscribe(self, key: str):
|
||||
"""Unsubscribe d'un stream"""
|
||||
if key in self.subscriptions:
|
||||
self.client.unsubscribe(self.subscriptions[key])
|
||||
del self.subscriptions[key]
|
||||
|
||||
def disconnect(self):
|
||||
"""Déconnexion Lightstreamer"""
|
||||
if self.client:
|
||||
self.client.disconnect()
|
||||
logger.info("Lightstreamer disconnected")
|
||||
|
||||
class MarketDataListener:
|
||||
"""Listener pour données marché"""
|
||||
|
||||
def __init__(self, callback):
|
||||
self.callback = callback
|
||||
|
||||
def onItemUpdate(self, update):
|
||||
"""Appelé à chaque update prix"""
|
||||
data = {
|
||||
'bid': update.getValue('BID'),
|
||||
'offer': update.getValue('OFFER'),
|
||||
'high': update.getValue('HIGH'),
|
||||
'low': update.getValue('LOW'),
|
||||
'change': update.getValue('CHANGE'),
|
||||
'change_pct': update.getValue('CHANGE_PCT'),
|
||||
'update_time': update.getValue('UPDATE_TIME'),
|
||||
'market_state': update.getValue('MARKET_STATE')
|
||||
}
|
||||
|
||||
self.callback(data)
|
||||
|
||||
class AccountListener:
|
||||
"""Listener pour updates compte"""
|
||||
|
||||
def __init__(self, callback):
|
||||
self.callback = callback
|
||||
|
||||
def onItemUpdate(self, update):
|
||||
"""Appelé à chaque update compte"""
|
||||
data = {
|
||||
'available_cash': update.getValue('AVAILABLE_CASH'),
|
||||
'pnl': update.getValue('PNL'),
|
||||
'deposit': update.getValue('DEPOSIT'),
|
||||
'used_margin': update.getValue('USED_MARGIN'),
|
||||
'equity': update.getValue('EQUITY')
|
||||
}
|
||||
|
||||
self.callback(data)
|
||||
|
||||
class ConnectionListener:
|
||||
"""Listener pour état connexion"""
|
||||
|
||||
def onStatusChange(self, status):
|
||||
logger.info(f"Lightstreamer status: {status}")
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📊 Gestion des Ordres
|
||||
|
||||
### Wrapper Unifié
|
||||
|
||||
```python
|
||||
# src/core/order_manager.py
|
||||
|
||||
class IGOrderManager:
|
||||
"""
|
||||
Gestionnaire d'ordres IG avec validation
|
||||
"""
|
||||
|
||||
def __init__(self, ig_api: IGRestAPI, risk_manager: RiskManager):
|
||||
self.ig_api = ig_api
|
||||
self.risk_manager = risk_manager
|
||||
|
||||
def execute_signal(self, signal: Signal) -> Optional[str]:
|
||||
"""
|
||||
Exécute signal de trading
|
||||
|
||||
Returns:
|
||||
Deal ID si succès, None sinon
|
||||
"""
|
||||
# 1. Valider avec risk manager
|
||||
is_valid, error = self.risk_manager.validate_trade(
|
||||
symbol=signal.symbol,
|
||||
quantity=signal.quantity,
|
||||
price=signal.entry_price,
|
||||
stop_loss=signal.stop_loss,
|
||||
take_profit=signal.take_profit,
|
||||
strategy=signal.strategy
|
||||
)
|
||||
|
||||
if not is_valid:
|
||||
logger.warning(f"Trade rejected: {error}")
|
||||
return None
|
||||
|
||||
# 2. Convertir symbol en EPIC IG
|
||||
epic = self._symbol_to_epic(signal.symbol)
|
||||
|
||||
# 3. Placer ordre
|
||||
try:
|
||||
result = self.ig_api.place_order(
|
||||
epic=epic,
|
||||
direction='BUY' if signal.direction == 'LONG' else 'SELL',
|
||||
size=signal.quantity,
|
||||
stop_loss=signal.stop_loss,
|
||||
take_profit=signal.take_profit,
|
||||
guaranteed_stop=True # Toujours utiliser guaranteed stop
|
||||
)
|
||||
|
||||
deal_id = result.get('dealId')
|
||||
|
||||
# 4. Enregistrer position dans risk manager
|
||||
if deal_id:
|
||||
self.risk_manager.add_position(Position(
|
||||
symbol=signal.symbol,
|
||||
quantity=signal.quantity,
|
||||
entry_price=signal.entry_price,
|
||||
current_price=signal.entry_price,
|
||||
stop_loss=signal.stop_loss,
|
||||
take_profit=signal.take_profit,
|
||||
strategy=signal.strategy,
|
||||
entry_time=datetime.now(),
|
||||
unrealized_pnl=0.0,
|
||||
risk_amount=abs(signal.entry_price - signal.stop_loss) * signal.quantity
|
||||
))
|
||||
|
||||
return deal_id
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Order execution failed: {e}")
|
||||
return None
|
||||
|
||||
def _symbol_to_epic(self, symbol: str) -> str:
|
||||
"""
|
||||
Convertit symbol standard en EPIC IG
|
||||
|
||||
Exemples:
|
||||
- EURUSD → CS.D.EURUSD.MINI.IP
|
||||
- GBPUSD → CS.D.GBPUSD.MINI.IP
|
||||
- US500 → IX.D.SPTRD.IFE.IP
|
||||
"""
|
||||
# Mapping symbol → EPIC
|
||||
EPIC_MAP = {
|
||||
'EURUSD': 'CS.D.EURUSD.MINI.IP',
|
||||
'GBPUSD': 'CS.D.GBPUSD.MINI.IP',
|
||||
'USDJPY': 'CS.D.USDJPY.MINI.IP',
|
||||
'US500': 'IX.D.SPTRD.IFE.IP',
|
||||
'US30': 'IX.D.DOW.IFE.IP',
|
||||
'GER40': 'IX.D.DAX.IFE.IP',
|
||||
}
|
||||
|
||||
return EPIC_MAP.get(symbol, symbol)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🛡️ Risk Management IG
|
||||
|
||||
### Spécificités IG
|
||||
|
||||
```python
|
||||
class IGRiskCalculator:
|
||||
"""
|
||||
Calculs risk spécifiques IG
|
||||
"""
|
||||
|
||||
@staticmethod
|
||||
def calculate_margin_required(
|
||||
epic: str,
|
||||
size: float,
|
||||
market_details: Dict
|
||||
) -> float:
|
||||
"""
|
||||
Calcule margin requis pour position
|
||||
|
||||
IG utilise margin factor variable selon instrument
|
||||
"""
|
||||
margin_factor = market_details['dealingRules']['minDealSize']['value']
|
||||
current_price = market_details['snapshot']['offer']
|
||||
|
||||
margin_required = size * current_price * margin_factor
|
||||
|
||||
return margin_required
|
||||
|
||||
@staticmethod
|
||||
def calculate_guaranteed_stop_premium(
|
||||
stop_distance: float,
|
||||
market_details: Dict
|
||||
) -> float:
|
||||
"""
|
||||
Calcule coût du guaranteed stop
|
||||
|
||||
IG facture premium pour guaranteed stops
|
||||
"""
|
||||
premium_factor = market_details['dealingRules']['marketOrderPreference']
|
||||
|
||||
# Premium généralement 0.3-0.5% du stop distance
|
||||
premium = stop_distance * 0.003 # 0.3%
|
||||
|
||||
return premium
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🚀 Migration Progressive
|
||||
|
||||
### Plan de Migration
|
||||
|
||||
```
|
||||
┌──────────────────────────────────────────────────────────┐
|
||||
│ MIGRATION VERS IG MARKETS │
|
||||
├──────────────────────────────────────────────────────────┤
|
||||
│ │
|
||||
│ PHASE 1: Développement (Semaines 1-8) │
|
||||
│ └─ Sources gratuites (Yahoo, Alpha Vantage) │
|
||||
│ │
|
||||
│ PHASE 2: Tests IG Démo (Semaines 9-10) │
|
||||
│ ├─ Connexion API IG démo │
|
||||
│ ├─ Paper trading 30 jours │
|
||||
│ └─ Validation performance │
|
||||
│ │
|
||||
│ PHASE 3: Live Progressif (Semaine 11+) │
|
||||
│ ├─ Capital initial minimal (£500) │
|
||||
│ ├─ 1 stratégie uniquement │
|
||||
│ ├─ Monitoring 24/7 │
|
||||
│ └─ Scale progressif si succès │
|
||||
│ │
|
||||
└──────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
**Documentation IG Markets complète !**
|
||||
429
docs/PROJECT_STATUS.md
Normal file
429
docs/PROJECT_STATUS.md
Normal file
@@ -0,0 +1,429 @@
|
||||
# 📈 État d'Avancement du Projet - Trading AI Secure
|
||||
|
||||
**Dernière mise à jour** : 2024-01-15
|
||||
**Version** : 0.1.0-alpha
|
||||
**Statut Global** : 🟡 En Développement Actif
|
||||
|
||||
---
|
||||
|
||||
## 📊 Vue d'Ensemble Globale
|
||||
|
||||
| Phase | Statut | Progression | Début | Fin Prévue | Fin Réelle |
|
||||
|-------|--------|-------------|-------|------------|------------|
|
||||
| Phase 1: Architecture | 🟡 En cours | 15% | 2024-01-15 | 2024-01-29 | - |
|
||||
| Phase 2: IA Adaptative | ⚪ Planifié | 0% | 2024-01-30 | 2024-02-12 | - |
|
||||
| Phase 3: Stratégies | ⚪ Planifié | 0% | 2024-02-13 | 2024-02-26 | - |
|
||||
| Phase 4: Interface | ⚪ Planifié | 0% | 2024-02-27 | 2024-03-11 | - |
|
||||
| Phase 5: Production | ⚪ Planifié | 0% | 2024-03-12 | 2024-03-25 | - |
|
||||
|
||||
**Progression Totale** : 3% ████░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
|
||||
|
||||
---
|
||||
|
||||
## 🎯 Phase 1 : Architecture Multi-Stratégie (Semaines 1-2)
|
||||
|
||||
**Statut** : 🟡 En cours
|
||||
**Progression** : 15%
|
||||
**Responsable** : Équipe Core
|
||||
**Priorité** : 🔴 CRITIQUE
|
||||
|
||||
### 1.1 Stack Technologique Sécurisé
|
||||
|
||||
#### Backend Core
|
||||
| Composant | Statut | Progression | Notes |
|
||||
|-----------|--------|-------------|-------|
|
||||
| Python 3.11+ setup | ✅ Terminé | 100% | Environnement configuré |
|
||||
| FastAPI structure | 🟡 En cours | 30% | Routes de base créées |
|
||||
| Pydantic models | 🟡 En cours | 20% | Modèles risk en cours |
|
||||
| asyncio/threading | ⚪ À faire | 0% | Planifié semaine 2 |
|
||||
| Singleton RiskManager | ⚪ À faire | 0% | Dépend de FastAPI |
|
||||
|
||||
**Bloqueurs** : Aucun
|
||||
**Risques** : Complexité threading pour multi-stratégie
|
||||
|
||||
#### Risk Management Intégré
|
||||
| Bibliothèque | Installation | Intégration | Tests | Notes |
|
||||
|--------------|--------------|-------------|-------|-------|
|
||||
| quantlib-python | ⚪ À faire | ⚪ À faire | ⚪ À faire | Calculs financiers |
|
||||
| riskfolio-lib | ⚪ À faire | ⚪ À faire | ⚪ À faire | Optimisation portfolio |
|
||||
| pypfopt | ⚪ À faire | ⚪ À faire | ⚪ À faire | Modern Portfolio Theory |
|
||||
|
||||
**Bloqueurs** : Aucun
|
||||
**Risques** : Compatibilité versions entre bibliothèques
|
||||
|
||||
### 1.2 Sources de Données Gratuites
|
||||
|
||||
#### Connecteurs Implémentés
|
||||
| Source | Statut | Priorité | Limite API | Notes |
|
||||
|--------|--------|----------|------------|-------|
|
||||
| Yahoo Finance (yfinance) | ⚪ À faire | 🔴 Haute | Illimité | EOD + intraday limité |
|
||||
| Alpha Vantage | ⚪ À faire | 🟡 Moyenne | 500/jour | Données temps réel |
|
||||
| Twelve Data | ⚪ À faire | 🟡 Moyenne | 800/jour | Alternative robuste |
|
||||
| Polygon.io | ⚪ À faire | 🟢 Basse | 5/min | Données US premium |
|
||||
| FRED API | ⚪ À faire | 🟢 Basse | Illimité | Données macro |
|
||||
|
||||
#### Crypto (Tests)
|
||||
| Source | Statut | Priorité | Limite API | Notes |
|
||||
|--------|--------|----------|------------|-------|
|
||||
| Binance Public API | ⚪ À faire | 🟡 Moyenne | Illimité | Pour tests initiaux |
|
||||
| CoinGecko API | ⚪ À faire | 🟢 Basse | 50/min | Backup crypto |
|
||||
|
||||
**Bloqueurs** : Besoin clés API (gratuites)
|
||||
**Risques** : Rate limiting, fiabilité données gratuites
|
||||
|
||||
### 1.3 Intégration IG Markets Préparée
|
||||
|
||||
#### Composants IG
|
||||
| Composant | Statut | Progression | Notes |
|
||||
|-----------|--------|-------------|-------|
|
||||
| ig-trading-api | ⚪ À faire | 0% | Library Python |
|
||||
| lightstreamer-client | ⚪ À faire | 0% | Streaming temps réel |
|
||||
| requests-oauthlib | ⚪ À faire | 0% | OAuth authentication |
|
||||
| Architecture REST | ⚪ À faire | 0% | Ordres, positions |
|
||||
| Streaming setup | ⚪ À faire | 0% | Prix temps réel |
|
||||
|
||||
**Bloqueurs** : Compte IG démo requis (gratuit)
|
||||
**Risques** : Complexité Lightstreamer, documentation limitée
|
||||
|
||||
---
|
||||
|
||||
## 🤖 Phase 2 : IA Adaptative avec Risk Management (Semaines 3-4)
|
||||
|
||||
**Statut** : ⚪ Planifié
|
||||
**Progression** : 0%
|
||||
**Responsable** : Équipe ML
|
||||
**Priorité** : 🔴 CRITIQUE
|
||||
|
||||
### 2.1 IA Risk-Aware
|
||||
|
||||
#### Core ML avec Risk Integration
|
||||
| Composant | Statut | Priorité | Notes |
|
||||
|-----------|--------|----------|-------|
|
||||
| scikit-learn pipeline | ⚪ Planifié | 🔴 Haute | Pipeline custom risk-aware |
|
||||
| Kelly Criterion auto | ⚪ Planifié | 🔴 Haute | Position sizing optimal |
|
||||
| Value at Risk (VaR) | ⚪ Planifié | 🔴 Haute | Calcul risque portfolio |
|
||||
| Max Drawdown limiter | ⚪ Planifié | 🔴 Haute | Circuit breaker |
|
||||
|
||||
#### Features Risk-Adjusted
|
||||
| Feature | Statut | Priorité | Notes |
|
||||
|---------|--------|----------|-------|
|
||||
| Volatility forecasting (GARCH) | ⚪ Planifié | 🟡 Moyenne | Prédiction volatilité |
|
||||
| Correlation matrix dynamique | ⚪ Planifié | 🔴 Haute | Diversification |
|
||||
| Regime detection | ⚪ Planifié | 🔴 Haute | Bull/Bear/Sideways |
|
||||
| Position sizing adaptatif | ⚪ Planifié | 🔴 Haute | Ajustement dynamique |
|
||||
|
||||
#### IA Auto-Optimisante (Nouvelle Exigence)
|
||||
| Composant | Statut | Priorité | Notes |
|
||||
|-----------|--------|----------|-------|
|
||||
| Optimisation bayésienne (Optuna) | ⚪ Planifié | 🔴 Haute | Tuning hyperparamètres |
|
||||
| A/B testing automatique | ⚪ Planifié | 🟡 Moyenne | Test variantes stratégies |
|
||||
| Reinforcement Learning | ⚪ Planifié | 🟡 Moyenne | Apprentissage continu |
|
||||
| Parameter drift detection | ⚪ Planifié | 🔴 Haute | Détection obsolescence |
|
||||
| Auto-retraining pipeline | ⚪ Planifié | 🔴 Haute | Réentraînement automatique |
|
||||
|
||||
**Bloqueurs** : Dépend Phase 1 (données)
|
||||
**Risques** : Overfitting, complexité optimisation
|
||||
|
||||
### 2.2 Module de Sécurité Trading
|
||||
|
||||
#### Safety Layer
|
||||
| Composant | Statut | Priorité | Notes |
|
||||
|-----------|--------|----------|-------|
|
||||
| RiskManager class | ⚪ Planifié | 🔴 Haute | Singleton pattern |
|
||||
| Pre-trade validation | ⚪ Planifié | 🔴 Haute | Checks avant ordre |
|
||||
| Circuit breakers | ⚪ Planifié | 🔴 Haute | Arrêt automatique |
|
||||
| Margin verification | ⚪ Planifié | 🔴 Haute | Temps réel |
|
||||
|
||||
**Bloqueurs** : Aucun
|
||||
**Risques** : Faux positifs arrêts intempestifs
|
||||
|
||||
---
|
||||
|
||||
## 📊 Phase 3 : Système Multi-Stratégie (Semaines 5-6)
|
||||
|
||||
**Statut** : ⚪ Planifié
|
||||
**Progression** : 0%
|
||||
**Responsable** : Équipe Stratégies
|
||||
**Priorité** : 🔴 CRITIQUE
|
||||
|
||||
### 3.1 Framework Stratégies Modulaire
|
||||
|
||||
#### Stratégies à Implémenter
|
||||
| Stratégie | Statut | Priorité | Complexité | Notes |
|
||||
|-----------|--------|----------|------------|-------|
|
||||
| ScalpingStrategy | ⚪ Planifié | 🟡 Moyenne | Haute | 1-5 min, risque 0.5-1% |
|
||||
| IntradayStrategy | ⚪ Planifié | 🔴 Haute | Moyenne | 15-60 min, risque 1-2% |
|
||||
| SwingStrategy | ⚪ Planifié | 🟡 Moyenne | Basse | 4H-1D, risque 2-3% |
|
||||
| BaseStrategy (abstract) | ⚪ Planifié | 🔴 Haute | Moyenne | Classe mère |
|
||||
|
||||
#### Paramètres Adaptatifs par Stratégie
|
||||
| Paramètre | Auto-Ajustement | Fréquence | Notes |
|
||||
|-----------|-----------------|-----------|-------|
|
||||
| Timeframe | ✅ Oui | Quotidien | Selon volatilité |
|
||||
| Risk per trade | ✅ Oui | Quotidien | Selon performance |
|
||||
| Stop-loss distance | ✅ Oui | Par trade | Selon ATR |
|
||||
| Take-profit ratio | ✅ Oui | Quotidien | Selon win rate |
|
||||
| Max positions | ✅ Oui | Hebdomadaire | Selon corrélation |
|
||||
|
||||
**Bloqueurs** : Dépend Phase 2 (ML models)
|
||||
**Risques** : Suroptimisation, instabilité paramètres
|
||||
|
||||
### 3.2 Backtesting Avancé Anti-Overfitting
|
||||
|
||||
#### Framework de Validation
|
||||
| Méthode | Statut | Priorité | Notes |
|
||||
|---------|--------|----------|-------|
|
||||
| Walk-forward analysis | ⚪ Planifié | 🔴 Haute | Validation temporelle |
|
||||
| Out-of-sample testing | ⚪ Planifié | 🔴 Haute | 30% données réservées |
|
||||
| Monte Carlo simulation | ⚪ Planifié | 🟡 Moyenne | 10,000+ scénarios |
|
||||
| Bootstrap validation | ⚪ Planifié | 🟢 Basse | Validation robustesse |
|
||||
| Paper trading engine | ⚪ Planifié | 🔴 Haute | 30 jours minimum |
|
||||
|
||||
#### Métriques Critiques
|
||||
| Métrique | Seuil Minimum | Statut Implémentation | Notes |
|
||||
|----------|---------------|----------------------|-------|
|
||||
| Sharpe Ratio | > 1.5 | ⚪ À faire | Risk-adjusted return |
|
||||
| Max Drawdown | < 10% | ⚪ À faire | Perte maximale |
|
||||
| Win Rate | > 55% | ⚪ À faire | % trades gagnants |
|
||||
| Profit Factor | > 1.3 | ⚪ À faire | Gains/Pertes |
|
||||
| Calmar Ratio | > 0.5 | ⚪ À faire | Return/Drawdown |
|
||||
|
||||
**Bloqueurs** : Données historiques suffisantes
|
||||
**Risques** : Biais survivorship, look-ahead bias
|
||||
|
||||
---
|
||||
|
||||
## 🖥️ Phase 4 : Interface Sécurisée (Semaines 7-8)
|
||||
|
||||
**Statut** : ⚪ Planifié
|
||||
**Progression** : 0%
|
||||
**Responsable** : Équipe UI/UX
|
||||
**Priorité** : 🟡 MOYENNE
|
||||
|
||||
### 4.1 Dashboard Risk-Centric
|
||||
|
||||
#### Composants Interface
|
||||
| Composant | Statut | Priorité | Notes |
|
||||
|-----------|--------|----------|-------|
|
||||
| Streamlit setup | ⚪ Planifié | 🔴 Haute | Framework UI |
|
||||
| Real-time P&L monitoring | ⚪ Planifié | 🔴 Haute | WebSocket updates |
|
||||
| Risk metrics dashboard | ⚪ Planifié | 🔴 Haute | VaR, drawdown, etc. |
|
||||
| Portfolio heat map | ⚪ Planifié | 🟡 Moyenne | Visualisation corrélations |
|
||||
| Drawdown alerts | ⚪ Planifié | 🔴 Haute | Alertes visuelles |
|
||||
| Performance attribution | ⚪ Planifié | 🟢 Basse | Par stratégie |
|
||||
|
||||
#### Safety Controls UI
|
||||
| Contrôle | Statut | Priorité | Notes |
|
||||
|----------|--------|----------|-------|
|
||||
| Emergency stop button | ⚪ Planifié | 🔴 Haute | Arrêt immédiat |
|
||||
| Position size calculator | ⚪ Planifié | 🟡 Moyenne | Aide décision |
|
||||
| Risk budget monitor | ⚪ Planifié | 🔴 Haute | Temps réel |
|
||||
| Correlation matrix live | ⚪ Planifié | 🟡 Moyenne | Heatmap dynamique |
|
||||
|
||||
**Bloqueurs** : Dépend Phase 3 (stratégies)
|
||||
**Risques** : Performance temps réel, latence
|
||||
|
||||
### 4.2 Système d'Alertes Intelligent
|
||||
|
||||
#### Canaux de Notification
|
||||
| Canal | Statut | Priorité | Use Case | Notes |
|
||||
|-------|--------|----------|----------|-------|
|
||||
| Telegram bot | ⚪ Planifié | 🔴 Haute | Alertes urgentes | Temps réel |
|
||||
| Email | ⚪ Planifié | 🟡 Moyenne | Rapports quotidiens | Asynchrone |
|
||||
| SMS | ⚪ Planifié | 🟢 Basse | Urgences critiques | Coût élevé |
|
||||
| In-app notifications | ⚪ Planifié | 🟡 Moyenne | Dashboard | Temps réel |
|
||||
|
||||
#### Types d'Alertes
|
||||
| Type | Statut | Priorité | Notes |
|
||||
|------|--------|----------|-------|
|
||||
| Risk threshold breaches | ⚪ Planifié | 🔴 Haute | Dépassement limites |
|
||||
| Unusual market conditions | ⚪ Planifié | 🟡 Moyenne | Volatilité extrême |
|
||||
| Strategy underperformance | ⚪ Planifié | 🟡 Moyenne | Sharpe < seuil |
|
||||
| Technical conflicts | ⚪ Planifié | 🟢 Basse | Indicateurs contradictoires |
|
||||
| News sentiment changes | ⚪ Planifié | 🟢 Basse | Analyse sentiment |
|
||||
|
||||
**Bloqueurs** : API Telegram, SMTP config
|
||||
**Risques** : Spam alertes, faux positifs
|
||||
|
||||
---
|
||||
|
||||
## 🚀 Phase 5 : Intégration IG et Production (Semaines 9-10)
|
||||
|
||||
**Statut** : ⚪ Planifié
|
||||
**Progression** : 0%
|
||||
**Responsable** : Équipe DevOps
|
||||
**Priorité** : 🔴 CRITIQUE
|
||||
|
||||
### 5.1 Migration vers IG Markets
|
||||
|
||||
#### Étapes d'Intégration
|
||||
| Étape | Statut | Priorité | Notes |
|
||||
|-------|--------|----------|-------|
|
||||
| Compte démo IG | ⚪ Planifié | 🔴 Haute | Gratuit, requis |
|
||||
| API key generation | ⚪ Planifié | 🔴 Haute | Credentials sécurisés |
|
||||
| Streaming setup (Lightstreamer) | ⚪ Planifié | 🔴 Haute | Prix temps réel |
|
||||
| Paper trading validation | ⚪ Planifié | 🔴 Haute | 30 jours minimum |
|
||||
| Live trading activation | ⚪ Planifié | 🔴 Haute | Après validation |
|
||||
|
||||
#### Features IG Spécifiques
|
||||
| Feature | Statut | Priorité | Notes |
|
||||
|---------|--------|----------|-------|
|
||||
| CFD margin calculator | ⚪ Planifié | 🔴 Haute | Calcul margin requis |
|
||||
| DMA vs CFD selection | ⚪ Planifié | 🟡 Moyenne | Choix instrument |
|
||||
| Guaranteed stops | ⚪ Planifié | 🔴 Haute | Protection slippage |
|
||||
| Weekend risk assessment | ⚪ Planifié | 🟡 Moyenne | Gap risk |
|
||||
|
||||
**Bloqueurs** : Validation paper trading 30 jours
|
||||
**Risques** : API changes, downtime IG
|
||||
|
||||
### 5.2 Production Safety
|
||||
|
||||
#### Deployment Checks
|
||||
| Check | Statut | Priorité | Notes |
|
||||
|-------|--------|----------|-------|
|
||||
| Position limits verification | ⚪ Planifié | 🔴 Haute | Hardcoded limits |
|
||||
| API rate limiting respect | ⚪ Planifié | 🔴 Haute | Éviter bans |
|
||||
| Redundant data sources | ⚪ Planifié | 🟡 Moyenne | Failover automatique |
|
||||
| Automatic failover | ⚪ Planifié | 🔴 Haute | Haute disponibilité |
|
||||
| Daily reconciliation | ⚪ Planifié | 🔴 Haute | Vérification positions |
|
||||
|
||||
#### Monitoring Stack
|
||||
| Outil | Statut | Priorité | Notes |
|
||||
|-------|--------|----------|-------|
|
||||
| Prometheus | ⚪ Planifié | 🟡 Moyenne | Métriques système |
|
||||
| Grafana | ⚪ Planifié | 🟡 Moyenne | Dashboards |
|
||||
| Custom trading metrics | ⚪ Planifié | 🔴 Haute | P&L, Sharpe, etc. |
|
||||
| Health check endpoints | ⚪ Planifié | 🔴 Haute | /health, /ready |
|
||||
| Error rate monitoring | ⚪ Planifié | 🔴 Haute | Alertes erreurs |
|
||||
| Latency tracking | ⚪ Planifié | 🟡 Moyenne | Performance API |
|
||||
|
||||
**Bloqueurs** : Infrastructure cloud (à définir)
|
||||
**Risques** : Coûts infrastructure, complexité DevOps
|
||||
|
||||
---
|
||||
|
||||
## 🎯 Objectifs Hebdomadaires
|
||||
|
||||
### Semaine 1 (15-21 Jan 2024) - EN COURS
|
||||
- [x] Création structure projet
|
||||
- [x] Documentation complète
|
||||
- [ ] Setup environnement Python 3.11+
|
||||
- [ ] Installation dépendances de base
|
||||
- [ ] Premiers modèles Pydantic
|
||||
- [ ] Tests unitaires structure
|
||||
|
||||
### Semaine 2 (22-28 Jan 2024)
|
||||
- [ ] RiskManager singleton
|
||||
- [ ] Connecteur Yahoo Finance
|
||||
- [ ] Connecteur Alpha Vantage
|
||||
- [ ] Data validation layer
|
||||
- [ ] Tests intégration données
|
||||
|
||||
### Semaine 3 (29 Jan - 4 Fév 2024)
|
||||
- [ ] Modèles ML de base (scikit-learn)
|
||||
- [ ] Regime detection (HMM)
|
||||
- [ ] Kelly Criterion implementation
|
||||
- [ ] VaR calculator
|
||||
|
||||
### Semaine 4 (5-11 Fév 2024)
|
||||
- [ ] Optimisation bayésienne (Optuna)
|
||||
- [ ] Auto-retraining pipeline
|
||||
- [ ] Parameter drift detection
|
||||
- [ ] Tests ML complets
|
||||
|
||||
---
|
||||
|
||||
## 📊 Métriques de Développement
|
||||
|
||||
### Couverture de Code
|
||||
| Module | Couverture | Objectif | Statut |
|
||||
|--------|------------|----------|--------|
|
||||
| core/ | 0% | 90% | ⚪ À démarrer |
|
||||
| strategies/ | 0% | 85% | ⚪ À démarrer |
|
||||
| ml/ | 0% | 80% | ⚪ À démarrer |
|
||||
| data/ | 0% | 90% | ⚪ À démarrer |
|
||||
| backtesting/ | 0% | 95% | ⚪ À démarrer |
|
||||
| ui/ | 0% | 70% | ⚪ À démarrer |
|
||||
|
||||
**Objectif Global** : 85% de couverture
|
||||
|
||||
### Qualité Code
|
||||
| Métrique | Valeur Actuelle | Objectif | Statut |
|
||||
|----------|-----------------|----------|--------|
|
||||
| Pylint Score | N/A | > 9.0/10 | ⚪ À mesurer |
|
||||
| Complexité Cyclomatique | N/A | < 10 | ⚪ À mesurer |
|
||||
| Duplications | N/A | < 3% | ⚪ À mesurer |
|
||||
| Documentation | 100% | 100% | ✅ OK |
|
||||
|
||||
---
|
||||
|
||||
## 🚧 Bloqueurs et Risques Globaux
|
||||
|
||||
### Bloqueurs Actuels
|
||||
| Bloqueur | Impact | Priorité | Solution Proposée | ETA |
|
||||
|----------|--------|----------|-------------------|-----|
|
||||
| Aucun actuellement | - | - | - | - |
|
||||
|
||||
### Risques Identifiés
|
||||
| Risque | Probabilité | Impact | Mitigation | Responsable |
|
||||
|--------|-------------|--------|------------|-------------|
|
||||
| Overfitting IA | 🟡 Moyenne | 🔴 Haute | Walk-forward, out-of-sample | Équipe ML |
|
||||
| Rate limiting APIs gratuites | 🟡 Moyenne | 🟡 Moyenne | Multiple sources, cache | Équipe Data |
|
||||
| Complexité Lightstreamer | 🟡 Moyenne | 🟡 Moyenne | POC early, documentation | Équipe IG |
|
||||
| Instabilité paramètres auto-optimisés | 🔴 Haute | 🔴 Haute | Contraintes, validation Monte Carlo | Équipe ML |
|
||||
| Faux positifs circuit breakers | 🟡 Moyenne | 🟡 Moyenne | Tuning seuils, historique | Équipe Risk |
|
||||
|
||||
---
|
||||
|
||||
## 📅 Prochaines Étapes Immédiates
|
||||
|
||||
### Cette Semaine (15-21 Jan)
|
||||
1. ✅ Finaliser documentation
|
||||
2. ⏳ Setup environnement développement
|
||||
3. ⏳ Créer structure fichiers src/
|
||||
4. ⏳ Premiers tests unitaires
|
||||
5. ⏳ Configuration CI/CD basique
|
||||
|
||||
### Semaine Prochaine (22-28 Jan)
|
||||
1. Implémenter RiskManager core
|
||||
2. Connecteur Yahoo Finance fonctionnel
|
||||
3. Validation données temps réel
|
||||
4. Tests intégration
|
||||
5. Première démo interne
|
||||
|
||||
---
|
||||
|
||||
## 📝 Notes de Version
|
||||
|
||||
### v0.1.0-alpha (2024-01-15)
|
||||
- ✅ Structure projet créée
|
||||
- ✅ Documentation complète
|
||||
- ✅ Roadmap détaillée
|
||||
- ⏳ Développement Phase 1 démarré
|
||||
|
||||
---
|
||||
|
||||
## 📞 Contacts Équipe
|
||||
|
||||
| Rôle | Responsable | Contact |
|
||||
|------|-------------|---------|
|
||||
| Chef de Projet | TBD | - |
|
||||
| Lead Backend | TBD | - |
|
||||
| Lead ML/IA | TBD | - |
|
||||
| Lead DevOps | TBD | - |
|
||||
| QA Lead | TBD | - |
|
||||
|
||||
---
|
||||
|
||||
**Légende Statuts** :
|
||||
- ✅ Terminé
|
||||
- 🟡 En cours
|
||||
- ⏳ En attente
|
||||
- ⚪ Planifié
|
||||
- 🔴 Bloqué
|
||||
- ❌ Annulé
|
||||
|
||||
**Légende Priorités** :
|
||||
- 🔴 Haute (Critique)
|
||||
- 🟡 Moyenne (Important)
|
||||
- 🟢 Basse (Nice to have)
|
||||
842
docs/RISK_FRAMEWORK.md
Normal file
842
docs/RISK_FRAMEWORK.md
Normal file
@@ -0,0 +1,842 @@
|
||||
# ⚠️ Framework de Risk Management - Trading AI Secure
|
||||
|
||||
## 📋 Table des Matières
|
||||
1. [Philosophie du Risk Management](#philosophie-du-risk-management)
|
||||
2. [Architecture Multi-Niveaux](#architecture-multi-niveaux)
|
||||
3. [Limites et Contraintes](#limites-et-contraintes)
|
||||
4. [Risk Manager Core](#risk-manager-core)
|
||||
5. [Validation Pré-Trade](#validation-pré-trade)
|
||||
6. [Circuit Breakers](#circuit-breakers)
|
||||
7. [Métriques de Risque](#métriques-de-risque)
|
||||
8. [Implémentation Technique](#implémentation-technique)
|
||||
|
||||
---
|
||||
|
||||
## 🎯 Philosophie du Risk Management
|
||||
|
||||
### Principe Fondamental : "Survivre d'abord, Profiter ensuite"
|
||||
|
||||
Le risk management est **intégré à tous les niveaux** du système, pas une couche ajoutée après coup.
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────┐
|
||||
│ HIÉRARCHIE DES PRIORITÉS │
|
||||
├─────────────────────────────────────────────────────────┤
|
||||
│ │
|
||||
│ 1. 🛡️ PROTECTION DU CAPITAL (Priorité absolue) │
|
||||
│ 2. 📉 LIMITATION DES PERTES (Drawdown < 10%) │
|
||||
│ 3. 🎯 CONSISTANCE (Sharpe > volatilité) │
|
||||
│ 4. 💰 PROFITABILITÉ (Objectif final) │
|
||||
│ │
|
||||
└─────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
### Règles d'Or
|
||||
|
||||
1. **Jamais de trade sans stop-loss** ❌
|
||||
2. **Jamais plus de 2% du capital en risque total** ❌
|
||||
3. **Jamais plus de 10% de drawdown** ❌
|
||||
4. **Toujours vérifier la corrélation** ✅
|
||||
5. **Toujours valider la liquidité** ✅
|
||||
|
||||
---
|
||||
|
||||
## 🏗️ Architecture Multi-Niveaux
|
||||
|
||||
### 5 Niveaux de Protection
|
||||
|
||||
```
|
||||
┌──────────────────────────────────────────────────────────┐
|
||||
│ NIVEAU 5: GLOBAL PORTFOLIO RISK │
|
||||
│ ├─ Max total risk: 2% du capital │
|
||||
│ ├─ Max drawdown: 10% │
|
||||
│ └─ Daily loss limit: 3% │
|
||||
├──────────────────────────────────────────────────────────┤
|
||||
│ NIVEAU 4: STRATEGY ALLOCATION │
|
||||
│ ├─ Max per strategy: 33% du capital │
|
||||
│ ├─ Correlation limit: 0.7 entre stratégies │
|
||||
│ └─ Min diversification: 3 stratégies actives │
|
||||
├──────────────────────────────────────────────────────────┤
|
||||
│ NIVEAU 3: POSITION SIZING │
|
||||
│ ├─ Kelly Criterion adaptatif │
|
||||
│ ├─ Max position: 5% du capital │
|
||||
│ └─ Volatility-adjusted sizing │
|
||||
├──────────────────────────────────────────────────────────┤
|
||||
│ NIVEAU 2: TRADE VALIDATION │
|
||||
│ ├─ Stop-loss obligatoire │
|
||||
│ ├─ Risk/Reward > 1.5 │
|
||||
│ ├─ Liquidity check │
|
||||
│ └─ Margin verification │
|
||||
├──────────────────────────────────────────────────────────┤
|
||||
│ NIVEAU 1: CIRCUIT BREAKERS │
|
||||
│ ├─ Emergency stop │
|
||||
│ ├─ Volatility spike detection │
|
||||
│ ├─ Flash crash protection │
|
||||
│ └─ API failure handling │
|
||||
└──────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📊 Limites et Contraintes
|
||||
|
||||
### Limites Globales (Portfolio)
|
||||
|
||||
```yaml
|
||||
global_limits:
|
||||
# Capital
|
||||
max_portfolio_risk: 0.02 # 2% du capital total en risque
|
||||
max_position_size: 0.05 # 5% max par position
|
||||
max_total_exposure: 1.0 # 100% du capital (pas de levier)
|
||||
|
||||
# Drawdown
|
||||
max_drawdown: 0.10 # 10% drawdown maximum
|
||||
max_daily_loss: 0.03 # 3% perte journalière max
|
||||
max_weekly_loss: 0.07 # 7% perte hebdomadaire max
|
||||
|
||||
# Corrélation
|
||||
max_correlation: 0.7 # Corrélation max entre positions
|
||||
min_diversification: 3 # Minimum 3 positions non-corrélées
|
||||
|
||||
# Liquidité
|
||||
min_daily_volume: 1000000 # 1M$ volume quotidien minimum
|
||||
max_position_vs_volume: 0.01 # Max 1% du volume quotidien
|
||||
|
||||
# Concentration
|
||||
max_sector_exposure: 0.30 # 30% max par secteur
|
||||
max_asset_class_exposure: 0.50 # 50% max par classe d'actif
|
||||
```
|
||||
|
||||
### Limites par Stratégie
|
||||
|
||||
```yaml
|
||||
strategy_limits:
|
||||
scalping:
|
||||
max_trades_per_day: 50
|
||||
risk_per_trade: 0.005 # 0.5% par trade
|
||||
max_holding_time: 1800 # 30 minutes
|
||||
max_slippage: 0.001 # 0.1% slippage max
|
||||
min_profit_target: 0.003 # 0.3% profit minimum
|
||||
|
||||
intraday:
|
||||
max_trades_per_day: 10
|
||||
risk_per_trade: 0.015 # 1.5% par trade
|
||||
max_holding_time: 86400 # 1 jour
|
||||
max_slippage: 0.002 # 0.2% slippage max
|
||||
min_profit_target: 0.01 # 1% profit minimum
|
||||
|
||||
swing:
|
||||
max_trades_per_week: 5
|
||||
risk_per_trade: 0.025 # 2.5% par trade
|
||||
max_holding_time: 432000 # 5 jours
|
||||
max_slippage: 0.003 # 0.3% slippage max
|
||||
min_profit_target: 0.03 # 3% profit minimum
|
||||
```
|
||||
|
||||
### Limites Dynamiques (Ajustées selon conditions)
|
||||
|
||||
```python
|
||||
class DynamicLimits:
|
||||
"""
|
||||
Limites ajustées selon conditions de marché
|
||||
"""
|
||||
|
||||
def adjust_limits(self, market_conditions: Dict) -> Dict:
|
||||
"""
|
||||
Ajuste limites selon volatilité, drawdown, etc.
|
||||
"""
|
||||
base_limits = self.get_base_limits()
|
||||
|
||||
# Réduire limites si haute volatilité
|
||||
if market_conditions['volatility'] > 0.03: # > 3% vol quotidienne
|
||||
base_limits['max_position_size'] *= 0.5
|
||||
base_limits['max_portfolio_risk'] *= 0.7
|
||||
|
||||
# Réduire limites si drawdown élevé
|
||||
if market_conditions['current_drawdown'] > 0.05: # > 5% DD
|
||||
reduction_factor = 1 - (market_conditions['current_drawdown'] / 0.10)
|
||||
base_limits['max_position_size'] *= reduction_factor
|
||||
|
||||
# Réduire limites si losing streak
|
||||
if market_conditions['consecutive_losses'] > 3:
|
||||
base_limits['max_trades_per_day'] //= 2
|
||||
base_limits['risk_per_trade'] *= 0.5
|
||||
|
||||
return base_limits
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🛡️ Risk Manager Core
|
||||
|
||||
### Singleton Pattern
|
||||
|
||||
```python
|
||||
# src/core/risk_manager.py
|
||||
|
||||
from typing import Dict, List, Optional
|
||||
from dataclasses import dataclass
|
||||
from datetime import datetime, timedelta
|
||||
import threading
|
||||
import numpy as np
|
||||
|
||||
@dataclass
|
||||
class Position:
|
||||
"""Représente une position ouverte"""
|
||||
symbol: str
|
||||
quantity: float
|
||||
entry_price: float
|
||||
current_price: float
|
||||
stop_loss: float
|
||||
take_profit: float
|
||||
strategy: str
|
||||
entry_time: datetime
|
||||
unrealized_pnl: float
|
||||
risk_amount: float
|
||||
|
||||
@dataclass
|
||||
class RiskMetrics:
|
||||
"""Métriques de risque en temps réel"""
|
||||
total_risk: float
|
||||
current_drawdown: float
|
||||
daily_pnl: float
|
||||
weekly_pnl: float
|
||||
portfolio_var: float
|
||||
portfolio_cvar: float
|
||||
correlation_matrix: np.ndarray
|
||||
largest_position: float
|
||||
num_positions: int
|
||||
|
||||
class RiskManager:
|
||||
"""
|
||||
Risk Manager Central (Singleton)
|
||||
|
||||
Responsabilités:
|
||||
- Validation pré-trade
|
||||
- Monitoring positions
|
||||
- Circuit breakers
|
||||
- Calcul métriques risque
|
||||
"""
|
||||
|
||||
_instance = None
|
||||
_lock = threading.Lock()
|
||||
|
||||
def __new__(cls):
|
||||
if cls._instance is None:
|
||||
with cls._lock:
|
||||
if cls._instance is None:
|
||||
cls._instance = super().__new__(cls)
|
||||
return cls._instance
|
||||
|
||||
def __init__(self):
|
||||
if not hasattr(self, 'initialized'):
|
||||
self.initialized = True
|
||||
|
||||
# Configuration
|
||||
self.config = self._load_config()
|
||||
|
||||
# État
|
||||
self.positions: Dict[str, Position] = {}
|
||||
self.daily_trades: List[Dict] = []
|
||||
self.portfolio_value: float = 100000.0 # Initial capital
|
||||
self.peak_value: float = 100000.0
|
||||
|
||||
# Historique
|
||||
self.pnl_history: List[float] = []
|
||||
self.drawdown_history: List[float] = []
|
||||
|
||||
# Circuit breakers
|
||||
self.trading_halted: bool = False
|
||||
self.halt_reason: Optional[str] = None
|
||||
|
||||
def validate_trade(
|
||||
self,
|
||||
symbol: str,
|
||||
quantity: float,
|
||||
price: float,
|
||||
stop_loss: float,
|
||||
take_profit: float,
|
||||
strategy: str
|
||||
) -> tuple[bool, Optional[str]]:
|
||||
"""
|
||||
Valide un trade avant exécution
|
||||
|
||||
Returns:
|
||||
(is_valid, error_message)
|
||||
"""
|
||||
# 1. Vérifier si trading halted
|
||||
if self.trading_halted:
|
||||
return False, f"Trading halted: {self.halt_reason}"
|
||||
|
||||
# 2. Vérifier stop-loss obligatoire
|
||||
if stop_loss is None or stop_loss == 0:
|
||||
return False, "Stop-loss is mandatory"
|
||||
|
||||
# 3. Calculer risque du trade
|
||||
risk_amount = abs(price - stop_loss) * quantity
|
||||
risk_pct = risk_amount / self.portfolio_value
|
||||
|
||||
# 4. Vérifier limites par trade
|
||||
max_risk_per_trade = self.config['strategy_limits'][strategy]['risk_per_trade']
|
||||
if risk_pct > max_risk_per_trade:
|
||||
return False, f"Risk per trade ({risk_pct:.2%}) exceeds limit ({max_risk_per_trade:.2%})"
|
||||
|
||||
# 5. Vérifier risque total portfolio
|
||||
total_risk = self._calculate_total_risk() + risk_amount
|
||||
max_portfolio_risk = self.config['global_limits']['max_portfolio_risk'] * self.portfolio_value
|
||||
|
||||
if total_risk > max_portfolio_risk:
|
||||
return False, f"Total portfolio risk ({total_risk:.2f}) exceeds limit ({max_portfolio_risk:.2f})"
|
||||
|
||||
# 6. Vérifier taille position
|
||||
position_value = price * quantity
|
||||
position_pct = position_value / self.portfolio_value
|
||||
max_position_size = self.config['global_limits']['max_position_size']
|
||||
|
||||
if position_pct > max_position_size:
|
||||
return False, f"Position size ({position_pct:.2%}) exceeds limit ({max_position_size:.2%})"
|
||||
|
||||
# 7. Vérifier corrélation
|
||||
if not self._check_correlation(symbol, strategy):
|
||||
return False, "Correlation with existing positions too high"
|
||||
|
||||
# 8. Vérifier nombre de trades quotidiens
|
||||
strategy_trades_today = len([t for t in self.daily_trades if t['strategy'] == strategy])
|
||||
max_trades = self.config['strategy_limits'][strategy]['max_trades_per_day']
|
||||
|
||||
if strategy_trades_today >= max_trades:
|
||||
return False, f"Max daily trades for {strategy} reached ({max_trades})"
|
||||
|
||||
# 9. Vérifier Risk/Reward ratio
|
||||
risk = abs(price - stop_loss)
|
||||
reward = abs(take_profit - price)
|
||||
rr_ratio = reward / risk if risk > 0 else 0
|
||||
|
||||
if rr_ratio < 1.5:
|
||||
return False, f"Risk/Reward ratio ({rr_ratio:.2f}) below minimum (1.5)"
|
||||
|
||||
# 10. Vérifier drawdown actuel
|
||||
current_dd = self._calculate_current_drawdown()
|
||||
max_dd = self.config['global_limits']['max_drawdown']
|
||||
|
||||
if current_dd >= max_dd:
|
||||
return False, f"Max drawdown reached ({current_dd:.2%})"
|
||||
|
||||
# Toutes validations passées
|
||||
return True, None
|
||||
|
||||
def add_position(self, position: Position):
|
||||
"""Ajoute une position au portfolio"""
|
||||
self.positions[position.symbol] = position
|
||||
|
||||
# Enregistrer trade
|
||||
self.daily_trades.append({
|
||||
'symbol': position.symbol,
|
||||
'strategy': position.strategy,
|
||||
'time': position.entry_time,
|
||||
'risk': position.risk_amount
|
||||
})
|
||||
|
||||
def update_position(self, symbol: str, current_price: float):
|
||||
"""Met à jour prix d'une position"""
|
||||
if symbol in self.positions:
|
||||
position = self.positions[symbol]
|
||||
position.current_price = current_price
|
||||
position.unrealized_pnl = (current_price - position.entry_price) * position.quantity
|
||||
|
||||
# Vérifier stop-loss / take-profit
|
||||
self._check_exit_conditions(position)
|
||||
|
||||
def close_position(self, symbol: str, exit_price: float) -> float:
|
||||
"""Ferme une position et retourne P&L"""
|
||||
if symbol not in self.positions:
|
||||
return 0.0
|
||||
|
||||
position = self.positions[symbol]
|
||||
pnl = (exit_price - position.entry_price) * position.quantity
|
||||
|
||||
# Mettre à jour portfolio
|
||||
self.portfolio_value += pnl
|
||||
self.pnl_history.append(pnl)
|
||||
|
||||
# Mettre à jour peak
|
||||
if self.portfolio_value > self.peak_value:
|
||||
self.peak_value = self.portfolio_value
|
||||
|
||||
# Supprimer position
|
||||
del self.positions[symbol]
|
||||
|
||||
return pnl
|
||||
|
||||
def get_risk_metrics(self) -> RiskMetrics:
|
||||
"""Calcule métriques de risque en temps réel"""
|
||||
return RiskMetrics(
|
||||
total_risk=self._calculate_total_risk(),
|
||||
current_drawdown=self._calculate_current_drawdown(),
|
||||
daily_pnl=self._calculate_daily_pnl(),
|
||||
weekly_pnl=self._calculate_weekly_pnl(),
|
||||
portfolio_var=self._calculate_var(),
|
||||
portfolio_cvar=self._calculate_cvar(),
|
||||
correlation_matrix=self._calculate_correlation_matrix(),
|
||||
largest_position=self._get_largest_position(),
|
||||
num_positions=len(self.positions)
|
||||
)
|
||||
|
||||
def _calculate_total_risk(self) -> float:
|
||||
"""Calcule risque total du portfolio"""
|
||||
return sum(pos.risk_amount for pos in self.positions.values())
|
||||
|
||||
def _calculate_current_drawdown(self) -> float:
|
||||
"""Calcule drawdown actuel"""
|
||||
if self.peak_value == 0:
|
||||
return 0.0
|
||||
return (self.peak_value - self.portfolio_value) / self.peak_value
|
||||
|
||||
def _calculate_daily_pnl(self) -> float:
|
||||
"""Calcule P&L du jour"""
|
||||
today = datetime.now().date()
|
||||
daily_pnl = sum(
|
||||
pnl for pnl, time in zip(self.pnl_history, self.daily_trades)
|
||||
if time['time'].date() == today
|
||||
)
|
||||
|
||||
# Ajouter unrealized P&L
|
||||
unrealized = sum(pos.unrealized_pnl for pos in self.positions.values())
|
||||
|
||||
return daily_pnl + unrealized
|
||||
|
||||
def _calculate_var(self, confidence=0.95) -> float:
|
||||
"""
|
||||
Calcule Value at Risk (VaR)
|
||||
|
||||
VaR = perte maximale avec X% de confiance
|
||||
"""
|
||||
if len(self.pnl_history) < 30:
|
||||
return 0.0
|
||||
|
||||
returns = np.array(self.pnl_history[-30:]) / self.portfolio_value
|
||||
var = np.percentile(returns, (1 - confidence) * 100)
|
||||
|
||||
return abs(var * self.portfolio_value)
|
||||
|
||||
def _calculate_cvar(self, confidence=0.95) -> float:
|
||||
"""
|
||||
Calcule Conditional Value at Risk (CVaR)
|
||||
|
||||
CVaR = perte moyenne au-delà du VaR
|
||||
"""
|
||||
if len(self.pnl_history) < 30:
|
||||
return 0.0
|
||||
|
||||
returns = np.array(self.pnl_history[-30:]) / self.portfolio_value
|
||||
var_threshold = np.percentile(returns, (1 - confidence) * 100)
|
||||
|
||||
# Moyenne des pertes au-delà du VaR
|
||||
tail_losses = returns[returns <= var_threshold]
|
||||
cvar = np.mean(tail_losses) if len(tail_losses) > 0 else 0
|
||||
|
||||
return abs(cvar * self.portfolio_value)
|
||||
|
||||
def _check_correlation(self, symbol: str, strategy: str) -> bool:
|
||||
"""
|
||||
Vérifie corrélation avec positions existantes
|
||||
"""
|
||||
if len(self.positions) == 0:
|
||||
return True
|
||||
|
||||
# Simplification: vérifier si même stratégie
|
||||
# En production: calculer corrélation réelle des returns
|
||||
same_strategy_positions = [
|
||||
pos for pos in self.positions.values()
|
||||
if pos.strategy == strategy
|
||||
]
|
||||
|
||||
max_correlation = self.config['global_limits']['max_correlation']
|
||||
|
||||
# Si trop de positions de même stratégie, corrélation trop haute
|
||||
if len(same_strategy_positions) >= 3:
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
def _check_exit_conditions(self, position: Position):
|
||||
"""Vérifie conditions de sortie (stop-loss / take-profit)"""
|
||||
# Stop-loss hit
|
||||
if position.current_price <= position.stop_loss:
|
||||
self.close_position(position.symbol, position.stop_loss)
|
||||
logger.warning(f"Stop-loss hit for {position.symbol}")
|
||||
|
||||
# Take-profit hit
|
||||
elif position.current_price >= position.take_profit:
|
||||
self.close_position(position.symbol, position.take_profit)
|
||||
logger.info(f"Take-profit hit for {position.symbol}")
|
||||
|
||||
def check_circuit_breakers(self):
|
||||
"""
|
||||
Vérifie conditions d'arrêt automatique
|
||||
"""
|
||||
# 1. Drawdown excessif
|
||||
current_dd = self._calculate_current_drawdown()
|
||||
if current_dd >= self.config['global_limits']['max_drawdown']:
|
||||
self.halt_trading(f"Max drawdown reached: {current_dd:.2%}")
|
||||
return
|
||||
|
||||
# 2. Perte journalière excessive
|
||||
daily_pnl_pct = self._calculate_daily_pnl() / self.portfolio_value
|
||||
if daily_pnl_pct <= -self.config['global_limits']['max_daily_loss']:
|
||||
self.halt_trading(f"Max daily loss reached: {daily_pnl_pct:.2%}")
|
||||
return
|
||||
|
||||
# 3. Volatilité extrême
|
||||
if self._detect_volatility_spike():
|
||||
self.halt_trading("Extreme volatility detected")
|
||||
return
|
||||
|
||||
def halt_trading(self, reason: str):
|
||||
"""Arrête le trading"""
|
||||
self.trading_halted = True
|
||||
self.halt_reason = reason
|
||||
|
||||
logger.critical(f"TRADING HALTED: {reason}")
|
||||
|
||||
# Fermer toutes positions (optionnel)
|
||||
# self._close_all_positions()
|
||||
|
||||
# Envoyer alertes
|
||||
self._send_emergency_alert(reason)
|
||||
|
||||
def resume_trading(self):
|
||||
"""Reprend le trading (manuel uniquement)"""
|
||||
self.trading_halted = False
|
||||
self.halt_reason = None
|
||||
logger.info("Trading resumed")
|
||||
|
||||
def _detect_volatility_spike(self) -> bool:
|
||||
"""Détecte spike de volatilité anormal"""
|
||||
if len(self.pnl_history) < 20:
|
||||
return False
|
||||
|
||||
recent_vol = np.std(self.pnl_history[-5:])
|
||||
baseline_vol = np.std(self.pnl_history[-20:-5])
|
||||
|
||||
# Spike si volatilité > 3x baseline
|
||||
return recent_vol > 3 * baseline_vol
|
||||
|
||||
def _send_emergency_alert(self, reason: str):
|
||||
"""Envoie alerte d'urgence"""
|
||||
# TODO: Implémenter notifications (Telegram, SMS, Email)
|
||||
pass
|
||||
|
||||
def _load_config(self) -> Dict:
|
||||
"""Charge configuration depuis YAML"""
|
||||
import yaml
|
||||
with open('config/risk_limits.yaml', 'r') as f:
|
||||
return yaml.safe_load(f)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## ✅ Validation Pré-Trade
|
||||
|
||||
### Checklist Complète
|
||||
|
||||
```python
|
||||
class PreTradeValidator:
|
||||
"""
|
||||
Validation exhaustive avant chaque trade
|
||||
"""
|
||||
|
||||
def __init__(self, risk_manager: RiskManager):
|
||||
self.risk_manager = risk_manager
|
||||
|
||||
def validate(self, trade_request: Dict) -> tuple[bool, List[str]]:
|
||||
"""
|
||||
Exécute toutes validations
|
||||
|
||||
Returns:
|
||||
(is_valid, list_of_errors)
|
||||
"""
|
||||
errors = []
|
||||
|
||||
# 1. Validation basique
|
||||
errors.extend(self._validate_basic(trade_request))
|
||||
|
||||
# 2. Validation risque
|
||||
errors.extend(self._validate_risk(trade_request))
|
||||
|
||||
# 3. Validation liquidité
|
||||
errors.extend(self._validate_liquidity(trade_request))
|
||||
|
||||
# 4. Validation margin
|
||||
errors.extend(self._validate_margin(trade_request))
|
||||
|
||||
# 5. Validation technique
|
||||
errors.extend(self._validate_technical(trade_request))
|
||||
|
||||
return len(errors) == 0, errors
|
||||
|
||||
def _validate_basic(self, trade: Dict) -> List[str]:
|
||||
"""Validations basiques"""
|
||||
errors = []
|
||||
|
||||
# Stop-loss obligatoire
|
||||
if 'stop_loss' not in trade or trade['stop_loss'] is None:
|
||||
errors.append("Stop-loss is mandatory")
|
||||
|
||||
# Take-profit obligatoire
|
||||
if 'take_profit' not in trade or trade['take_profit'] is None:
|
||||
errors.append("Take-profit is mandatory")
|
||||
|
||||
# Quantité positive
|
||||
if trade.get('quantity', 0) <= 0:
|
||||
errors.append("Quantity must be positive")
|
||||
|
||||
# Prix valide
|
||||
if trade.get('price', 0) <= 0:
|
||||
errors.append("Price must be positive")
|
||||
|
||||
return errors
|
||||
|
||||
def _validate_risk(self, trade: Dict) -> List[str]:
|
||||
"""Validations risque"""
|
||||
errors = []
|
||||
|
||||
# Risk/Reward ratio
|
||||
risk = abs(trade['price'] - trade['stop_loss'])
|
||||
reward = abs(trade['take_profit'] - trade['price'])
|
||||
|
||||
if risk > 0:
|
||||
rr_ratio = reward / risk
|
||||
if rr_ratio < 1.5:
|
||||
errors.append(f"Risk/Reward ratio {rr_ratio:.2f} below minimum 1.5")
|
||||
|
||||
# Taille position
|
||||
position_value = trade['price'] * trade['quantity']
|
||||
position_pct = position_value / self.risk_manager.portfolio_value
|
||||
|
||||
if position_pct > 0.05: # 5% max
|
||||
errors.append(f"Position size {position_pct:.2%} exceeds 5%")
|
||||
|
||||
return errors
|
||||
|
||||
def _validate_liquidity(self, trade: Dict) -> List[str]:
|
||||
"""Validations liquidité"""
|
||||
errors = []
|
||||
|
||||
# TODO: Vérifier volume quotidien
|
||||
# TODO: Vérifier spread bid/ask
|
||||
# TODO: Vérifier market depth
|
||||
|
||||
return errors
|
||||
|
||||
def _validate_margin(self, trade: Dict) -> List[str]:
|
||||
"""Validations margin"""
|
||||
errors = []
|
||||
|
||||
# TODO: Vérifier margin disponible
|
||||
# TODO: Calculer margin requis
|
||||
# TODO: Vérifier margin call risk
|
||||
|
||||
return errors
|
||||
|
||||
def _validate_technical(self, trade: Dict) -> List[str]:
|
||||
"""Validations techniques"""
|
||||
errors = []
|
||||
|
||||
# Vérifier que stop-loss est du bon côté
|
||||
if trade['quantity'] > 0: # Long
|
||||
if trade['stop_loss'] >= trade['price']:
|
||||
errors.append("Stop-loss must be below entry price for long")
|
||||
if trade['take_profit'] <= trade['price']:
|
||||
errors.append("Take-profit must be above entry price for long")
|
||||
else: # Short
|
||||
if trade['stop_loss'] <= trade['price']:
|
||||
errors.append("Stop-loss must be above entry price for short")
|
||||
if trade['take_profit'] >= trade['price']:
|
||||
errors.append("Take-profit must be below entry price for short")
|
||||
|
||||
return errors
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🚨 Circuit Breakers
|
||||
|
||||
### Types de Circuit Breakers
|
||||
|
||||
```python
|
||||
class CircuitBreaker:
|
||||
"""
|
||||
Système d'arrêt automatique multi-niveaux
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
self.breakers = {
|
||||
'drawdown': DrawdownBreaker(),
|
||||
'daily_loss': DailyLossBreaker(),
|
||||
'volatility': VolatilityBreaker(),
|
||||
'flash_crash': FlashCrashBreaker(),
|
||||
'api_failure': APIFailureBreaker(),
|
||||
'correlation': CorrelationBreaker(),
|
||||
}
|
||||
|
||||
def check_all(self, market_data: Dict, portfolio_state: Dict) -> Optional[str]:
|
||||
"""
|
||||
Vérifie tous circuit breakers
|
||||
|
||||
Returns:
|
||||
Reason for halt, or None if all OK
|
||||
"""
|
||||
for name, breaker in self.breakers.items():
|
||||
if breaker.should_halt(market_data, portfolio_state):
|
||||
return f"{name}: {breaker.get_reason()}"
|
||||
|
||||
return None
|
||||
|
||||
class DrawdownBreaker:
|
||||
"""Arrêt si drawdown excessif"""
|
||||
|
||||
def __init__(self, max_drawdown=0.10):
|
||||
self.max_drawdown = max_drawdown
|
||||
self.reason = ""
|
||||
|
||||
def should_halt(self, market_data: Dict, portfolio: Dict) -> bool:
|
||||
current_dd = portfolio['current_drawdown']
|
||||
|
||||
if current_dd >= self.max_drawdown:
|
||||
self.reason = f"Drawdown {current_dd:.2%} >= {self.max_drawdown:.2%}"
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
def get_reason(self) -> str:
|
||||
return self.reason
|
||||
|
||||
class VolatilityBreaker:
|
||||
"""Arrêt si volatilité extrême"""
|
||||
|
||||
def __init__(self, spike_threshold=3.0):
|
||||
self.spike_threshold = spike_threshold
|
||||
self.reason = ""
|
||||
|
||||
def should_halt(self, market_data: Dict, portfolio: Dict) -> bool:
|
||||
current_vol = market_data.get('current_volatility', 0)
|
||||
baseline_vol = market_data.get('baseline_volatility', 0)
|
||||
|
||||
if baseline_vol > 0 and current_vol > self.spike_threshold * baseline_vol:
|
||||
self.reason = f"Volatility spike: {current_vol/baseline_vol:.1f}x baseline"
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
def get_reason(self) -> str:
|
||||
return self.reason
|
||||
|
||||
class FlashCrashBreaker:
|
||||
"""Arrêt si mouvement de prix extrême"""
|
||||
|
||||
def __init__(self, max_move_pct=0.05):
|
||||
self.max_move_pct = max_move_pct
|
||||
self.reason = ""
|
||||
|
||||
def should_halt(self, market_data: Dict, portfolio: Dict) -> bool:
|
||||
price_change = market_data.get('price_change_1min', 0)
|
||||
|
||||
if abs(price_change) > self.max_move_pct:
|
||||
self.reason = f"Flash crash detected: {price_change:.2%} move in 1 minute"
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
def get_reason(self) -> str:
|
||||
return self.reason
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📊 Métriques de Risque
|
||||
|
||||
### Calculs Avancés
|
||||
|
||||
```python
|
||||
class RiskMetricsCalculator:
|
||||
"""
|
||||
Calcule métriques de risque avancées
|
||||
"""
|
||||
|
||||
@staticmethod
|
||||
def calculate_var(returns: np.ndarray, confidence=0.95) -> float:
|
||||
"""Value at Risk"""
|
||||
return np.percentile(returns, (1 - confidence) * 100)
|
||||
|
||||
@staticmethod
|
||||
def calculate_cvar(returns: np.ndarray, confidence=0.95) -> float:
|
||||
"""Conditional Value at Risk (Expected Shortfall)"""
|
||||
var = RiskMetricsCalculator.calculate_var(returns, confidence)
|
||||
return returns[returns <= var].mean()
|
||||
|
||||
@staticmethod
|
||||
def calculate_sharpe_ratio(returns: np.ndarray, risk_free_rate=0.02) -> float:
|
||||
"""Sharpe Ratio"""
|
||||
excess_returns = returns - risk_free_rate / 252 # Daily
|
||||
return np.mean(excess_returns) / np.std(excess_returns) * np.sqrt(252)
|
||||
|
||||
@staticmethod
|
||||
def calculate_sortino_ratio(returns: np.ndarray, risk_free_rate=0.02) -> float:
|
||||
"""Sortino Ratio (downside deviation)"""
|
||||
excess_returns = returns - risk_free_rate / 252
|
||||
downside_returns = returns[returns < 0]
|
||||
downside_std = np.std(downside_returns)
|
||||
|
||||
return np.mean(excess_returns) / downside_std * np.sqrt(252)
|
||||
|
||||
@staticmethod
|
||||
def calculate_max_drawdown(equity_curve: np.ndarray) -> float:
|
||||
"""Maximum Drawdown"""
|
||||
peak = np.maximum.accumulate(equity_curve)
|
||||
drawdown = (equity_curve - peak) / peak
|
||||
return np.min(drawdown)
|
||||
|
||||
@staticmethod
|
||||
def calculate_calmar_ratio(returns: np.ndarray, equity_curve: np.ndarray) -> float:
|
||||
"""Calmar Ratio (Return / Max Drawdown)"""
|
||||
annual_return = np.mean(returns) * 252
|
||||
max_dd = abs(RiskMetricsCalculator.calculate_max_drawdown(equity_curve))
|
||||
|
||||
return annual_return / max_dd if max_dd > 0 else 0
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🔔 Système d'Alertes
|
||||
|
||||
### Configuration Alertes
|
||||
|
||||
```yaml
|
||||
# config/alerts.yaml
|
||||
alerts:
|
||||
risk_threshold_breach:
|
||||
channels: ['telegram', 'email']
|
||||
priority: high
|
||||
conditions:
|
||||
- total_risk > max_portfolio_risk
|
||||
- current_drawdown > 0.08 # 80% du max
|
||||
- daily_loss > 0.025 # 83% du max
|
||||
|
||||
position_alerts:
|
||||
channels: ['telegram']
|
||||
priority: medium
|
||||
conditions:
|
||||
- position_size > 0.04 # 80% du max
|
||||
- correlation > 0.6 # 85% du max
|
||||
|
||||
circuit_breaker:
|
||||
channels: ['telegram', 'sms', 'email']
|
||||
priority: critical
|
||||
conditions:
|
||||
- trading_halted == true
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
**Suite dans le prochain fichier...**
|
||||
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