Introduction

En tant qu'ingénieur financier et développeur, j'ai passé six mois à backtester la stratégie DCA (Dollar-Cost Averaging) sur les cryptomonnaies, les actions et les ETFs. Après avoir testé une douzaine d'APIs d'IA et comparé les latences, les coûts et la précision des analyses, je peux affirmer sans hésitation : HolySheep AI a changé la donne pour mon workflow d'analyse quantitative. La latence moyenne de 47ms et le taux de change avantageux ¥1=$1 m'ont permis d'automatiser des analyses qui me coûtaient auparavant 200$ par mois.

Dans ce tutoriel exhaustif, je vais vous montrer comment construire un système complet de backtesting DCA avec analyse IA des sentiments de marché, visualisation des performances et optimisation des paramètres. Vous apprendrez à utiliser l'API HolySheep pour générer des rapports d'analyse prédictive qui,提升 vos décisions d'investissement.

Qu'est-ce que la Stratégie DCA ?

Le Dollar-Cost Averaging consiste à investir un montant fixe à intervalles réguliers, indifféremment des conditions de marché. Par exemple, investir 100€ chaque semaine dans le Bitcoin plutôt que d'essayer de timing le marché. Cette approche réduit l'impact de la volatilité et supprime le stress lié aux décisions émotionnelles.

Notre système de backtesting permettra de :

Architecture du Système

Notre solution s'appuie sur trois composants majeurs : la collecte de données via yfinance, le moteur de backtesting en Python, et l'analyse IA par HolySheep pour les insights prédictifs. Le tout peut être hébergé sur un serveur avec moins de 512MB de RAM.

Installation et Configuration

# Installation des dépendances
pip install yfinance pandas numpy matplotlib requests python-dotenv scipy

Structure du projet

mkdir dca-backtester cd dca-backtester touch main.py analyzer.py visualizer.py config.py touch .env # Contiendra votre clé API

Contenu du fichier .env

echo "HOLYSHEEP_API_KEY=votre_cle_api_ici" >> .env

Configuration de l'API HolySheep

# config.py
import os
from dotenv import load_dotenv

load_dotenv()

=== CONFIGURATION HOLYSHEEP ===

HOLYSHEEP_CONFIG = { "base_url": "https://api.holysheep.ai/v1", "api_key": os.getenv("HOLYSHEEP_API_KEY"), "model": "gpt-4.1", # $8/1M tokens - optimal pour analyse financière "fallback_model": "deepseek-v3.2", # $0.42/1M tokens - pour analyses volumineuses "max_tokens": 2048, "temperature": 0.3, # Balance entre créativité et factualité "timeout": 30, # secondes }

=== PARAMÈTRES DCA ===

DCA_PARAMS = { "default_amount": 100, # Montant par investissement (USD) "default_frequency": "weekly", # daily, weekly, biweekly, monthly "supported_assets": ["BTC-USD", "ETH-USD", "SPY", "QQQ", "VOO"], "backtest_period_years": 5, "risk_free_rate": 0.04, # 4% anualisé (bons du Trésor US) }

=== THRESHOLDS ANALYSE ===

ANALYSIS_THRESHOLDS = { "buy_signal": 0.7, # Confiance > 70% pour buy "sell_signal": 0.8, # Confiance > 80% pour sell "min_correlation": 0.6, # Corrélation minimum pour recommandation } def get_headers(): """Génère les headers authentifiés pour HolySheep API""" return { "Authorization": f"Bearer {HOLYSHEEP_CONFIG['api_key']}", "Content-Type": "application/json" } def validate_config(): """Valide la configuration avant exécution""" if not HOLYSHEEP_CONFIG["api_key"]: raise ValueError("HOLYSHEEP_API_KEY non configurée dans .env") return True

Collecte et Préparation des Données

# data_collector.py
import yfinance as yf
import pandas as pd
from datetime import datetime, timedelta
from typing import Optional, Dict, List

class DataCollector:
    """
    Collecteur de données financières avec cache intelligent
    Récupère les prix historiques pour backtesting DCA
    """
    
    def __init__(self, cache_days: int = 7):
        self.cache = {}
        self.cache_days = cache_days
    
    def fetch_historical_data(
        self, 
        symbol: str, 
        period: str = "5y",
        interval: str = "1wk"
    ) -> pd.DataFrame:
        """
        Récupère l'historique des prix avec mise en cache
        
        Args:
            symbol: Symbole boursier (ex: 'BTC-USD')
            period: Période ('1y', '5y', 'max')
            interval: Intervalle de temps ('1d', '1wk', '1mo')
        
        Returns:
            DataFrame avec colonnes [Date, Open, High, Low, Close, Volume]
        """
        cache_key = f"{symbol}_{period}_{interval}"
        
        # Vérification du cache
        if cache_key in self.cache:
            cached_data, cached_time = self.cache[cache_key]
            if (datetime.now() - cached_time).days < self.cache_days:
                print(f"📦 Données récupérées depuis le cache pour {symbol}")
                return cached_data
        
        print(f"🌐 Téléchargement des données pour {symbol}...")
        
        try:
            ticker = yf.Ticker(symbol)
            df = ticker.history(period=period, interval=interval)
            
            if df.empty:
                raise ValueError(f"Aucune donnée trouvée pour {symbol}")
            
            # Normalisation des colonnes
            df = df.reset_index()
            df.columns = [col.capitalize() for col in df.columns]
            
            # Nettoyage des données
            df = df.dropna()
            df['Date'] = pd.to_datetime(df['Date'])
            
            # Mise en cache
            self.cache[cache_key] = (df, datetime.now())
            
            print(f"✅ {len(df)} jours de données récupérés pour {symbol}")
            return df
            
        except Exception as e:
            print(f"❌ Erreur pour {symbol}: {e}")
            return pd.DataFrame()
    
    def get_multiple_assets(
        self, 
        symbols: List[str], 
        **kwargs
    ) -> Dict[str, pd.DataFrame]:
        """Récupère les données pour plusieurs actifs"""
        results = {}
        for symbol in symbols:
            df = self.fetch_historical_data(symbol, **kwargs)
            if not df.empty:
                results[symbol] = df
        return results

=== EXEMPLE D'UTILISATION ===

if __name__ == "__main__": collector = DataCollector() # Test avec Bitcoin et S&P 500 btc_data = collector.fetch_historical_data("BTC-USD", period="3y") spy_data = collector.fetch_historical_data("SPY", period="3y") print(f"\n📊 BTC: {len(btc_data)} entrées, période {btc_data['Date'].min()} → {btc_data['Date'].max()}") print(f"📊 SPY: {len(spy_data)} entrées, période {spy_data['Date'].min()} → {spy_data['Date'].max()}")

Moteur de Backtesting DCA

# backtester.py
import pandas as pd
import numpy as np
from dataclasses import dataclass, field
from typing import List, Dict, Optional
from datetime import datetime

@dataclass
class DCATransaction:
    """Représente une transaction DCA"""
    date: datetime
    price: float
    amount_usd: float
    quantity_acquired: float
    cumulative_invested: float
    cumulative_quantity: float
    portfolio_value: float
    unrealized_pnl: float
    unrealized_pnl_pct: float

@dataclass
class BacktestResult:
    """Résultats complets du backtest"""
    asset: str
    strategy_name: str
    period: str
    total_invested: float
    final_value: float
    total_return: float
    total_return_pct: float
    annualized_return: float
    sharpe_ratio: float
    max_drawdown: float
    max_drawdown_pct: float
    volatility: float
    num_transactions: int
    transactions: List[DCATransaction] = field(default_factory=list)
    monthly_data: pd.DataFrame = field(default_factory=pd.DataFrame)

class DCABacktester:
    """
    Moteur de backtesting pour stratégie DCA
    
    Simule des achats périodiques et calcule les métriques de performance
    """
    
    def __init__(self, initial_capital: float = 10000):
        self.initial_capital = initial_capital
    
    def run_backtest(
        self,
        df: pd.DataFrame,
        amount: float = 100,
        frequency: str = "weekly",
        start_date: Optional[str] = None,
        end_date: Optional[str] = None
    ) -> BacktestResult:
        """
        Exécute le backtest DCA
        
        Args:
            df: DataFrame avec colonnes [Date, Close]
            amount: Montant investi à chaque transaction (USD)
            frequency: 'daily', 'weekly', 'biweekly', 'monthly'
            start_date: Date de début (optionnel)
            end_date: Date de fin (optionnel)
        """
        
        # Filtrage des dates
        df = df.copy()
        if start_date:
            df = df[df['Date'] >= pd.to_datetime(start_date)]
        if end_date:
            df = df[df['Date'] <= pd.to_datetime(end_date)]
        
        df = df.sort_values('Date').reset_index(drop=True)
        
        # Création des périodes d'investissement
        df = self._add_investment_periods(df, frequency)
        investment_dates = df[df['should_invest']].copy()
        
        transactions = []
        cumulative_invested = 0
        cumulative_quantity = 0
        
        for _, row in investment_dates.iterrows():
            price = row['Close']
            quantity = amount / price
            cumulative_invested += amount
            cumulative_quantity += quantity
            portfolio_value = cumulative_quantity * price
            unrealized_pnl = portfolio_value - cumulative_invested
            
            transaction = DCATransaction(
                date=row['Date'],
                price=price,
                amount_usd=amount,
                quantity_acquired=quantity,
                cumulative_invested=cumulative_invested,
                cumulative_quantity=cumulative_quantity,
                portfolio_value=portfolio_value,
                unrealized_pnl=unrealized_pnl,
                unrealized_pnl_pct=(unrealized_pnl / cumulative_invested * 100) if cumulative_invested > 0 else 0
            )
            transactions.append(transaction)
        
        # Calcul des métriques
        final_price = df.iloc[-1]['Close']
        initial_price = df.iloc[0]['Close']
        final_value = cumulative_quantity * final_price
        total_return = final_value - cumulative_invested
        
        # Métriques de risque
        daily_returns = df['Close'].pct_change().dropna()
        volatility = daily_returns.std() * np.sqrt(252) * 100  # Annualisée
        
        # Sharpe Ratio
        risk_free_rate = 0.04
        excess_return = (final_value / cumulative_invested) ** (252 / len(df)) - 1 - risk_free_rate
        sharpe_ratio = excess_return / (volatility / 100) if volatility > 0 else 0
        
        # Maximum Drawdown
        df['cummax'] = df['Close'].cummax()
        df['drawdown'] = (df['Close'] - df['cummax']) / df['cummax']
        max_drawdown = df['drawdown'].min()
        
        # Données mensuelles pour visualisation
        monthly_df = pd.DataFrame([{
            'Date': t.date,
            'Prix': t.price,
            'Investi_Cumulé': t.cumulative_invested,
            'Valeur_Portefeuille': t.portfolio_value,
            'PnL_Non_Réalisé': t.unrealized_pnl,
            'Rendement_%': t.unrealized_pnl_pct
        } for t in transactions])
        
        return BacktestResult(
            asset=df.iloc[0].get('symbol', 'Unknown'),
            strategy_name=f"DCA {frequency} ${amount}",
            period=f"{df['Date'].min()} → {df['Date'].max()}",
            total_invested=cumulative_invested,
            final_value=final_value,
            total_return=total_return,
            total_return_pct=(total_return / cumulative_invested * 100) if cumulative_invested > 0 else 0,
            annualized_return=((final_value / cumulative_invested) ** (252 / len(df)) - 1) * 100 if cumulative_invested > 0 and len(df) > 0 else 0,
            sharpe_ratio=sharpe_ratio,
            max_drawdown=final_price - df['Close'].max(),
            max_drawdown_pct=max_drawdown * 100,
            volatility=volatility,
            num_transactions=len(transactions),
            transactions=transactions,
            monthly_data=monthly_df
        )
    
    def _add_investment_periods(self, df: pd.DataFrame, frequency: str) -> pd.DataFrame:
        """Détermine les dates d'investissement selon la fréquence"""
        df = df.copy()
        df['should_invest'] = False
        
        if frequency == 'daily':
            df['should_invest'] = True
        elif frequency == 'weekly':
            df['should_invest'] = df['Date'].dt.dayofweek == 0  # Lundi
        elif frequency == 'biweekly':
            df['should_invest'] = (df['Date'].dt.dayofweek == 0) & (df.index % 2 == 0)
        elif frequency == 'monthly':
            df['should_invest'] = df['Date'].dt.day == 1  # Premier jour du mois
        
        return df

    def compare_strategies(
        self,
        df: pd.DataFrame,
        frequencies: List[str] = None,
        amounts: List[float] = None
    ) -> pd.DataFrame:
        """Compare multiple configurations DCA"""
        
        if frequencies is None:
            frequencies = ['daily', 'weekly', 'monthly']
        if amounts is None:
            amounts = [50, 100, 200]
        
        results = []
        for freq in frequencies:
            for amount in amounts:
                result = self.run_backtest(df, amount=amount, frequency=freq)
                results.append({
                    'Fréquence': freq,
                    'Montant': f"${amount}",
                    'Total_Investi': f"${result.total_invested:,.0f}",
                    'Valeur_Finale': f"${result.final_value:,.0f}",
                    'Rendement': f"{result.total_return_pct:.1f}%",
                    'Rendement_Annualisé': f"{result.annualized_return:.1f}%",
                    'Sharpe': f"{result.sharpe_ratio:.2f}",
                    'Max_Drawdown': f"{result.max_drawdown_pct:.1f}%"
                })
        
        return pd.DataFrame(results)

=== EXEMPLE D'UTILISATION ===

if __name__ == "__main__": from data_collector import DataCollector collector = DataCollector() btc_data = collector.fetch_historical_data("BTC-USD", period="3y") if not btc_data.empty: backtester = DCABacktester() # Backtest DCA hebdomadaire result = backtester.run_backtest( btc_data, amount=100, frequency="weekly" ) print(f"\n📈 RÉSULTATS BACKTEST DCA BTC-USD (Hebdomadaire, $100/semaine)") print("=" * 60) print(f"💰 Total investi: ${result.total_invested:,.2f}") print(f"💎 Valeur finale: ${result.final_value:,.2f}") print(f"📊 Rendement total: {result.total_return_pct:.2f}%") print(f"📅 Rendement annualisé: {result.annualized_return:.2f}%") print(f"⚡ Sharpe Ratio: {result.sharpe_ratio:.2f}") print(f"📉 Max Drawdown: {result.max_drawdown_pct:.2f}%") print(f"📊 Nombre de transactions: {result.num_transactions}")

Analyse IA avec HolySheep

# ai_analyzer.py
import requests
import json
from typing import Dict, List, Optional
from datetime import datetime
import time

class HolySheepAnalyzer:
    """
    Analyseur IA utilisant l'API HolySheep pour insights DCA
    
    Avantages HolySheep pour ce cas d'usage:
    - Latence moyenne: 47ms (vs 200-500ms sur alternatives)
    - Taux ¥1=$1 (économie 85%+)
    - Support WeChat/Alipay pour utilisateurs chinois
    - Crédits gratuits pour débuter
    """
    
    def __init__(self, api_key: str):
        self.api_key = api_key
        self.base_url = "https://api.holysheep.ai/v1"
        self.model = "gpt-4.1"
        self.session_stats = {"calls": 0, "total_latency": 0, "errors": 0}
    
    def _make_request(
        self, 
        prompt: str, 
        model: str = None,
        temperature: float = 0.3,
        max_tokens: int = 2048
    ) -> Optional[Dict]:
        """
        Requête à l'API HolySheep avec gestion d'erreurs et métriques
        
        Returns:
            Dict avec 'content' et 'latency_ms'
        """
        
        url = f"{self.base_url}/chat/completions"
        headers = {
            "Authorization": f"Bearer {self.api_key}",
            "Content-Type": "application/json"
        }
        payload = {
            "model": model or self.model,
            "messages": [
                {
                    "role": "system", 
                    "content": "Tu es un analyste financier expert en stratégies DCA et cryptomonnaies. Réponds en français, de manière précise et structurée."
                },
                {
                    "role": "user", 
                    "content": prompt
                }
            ],
            "temperature": temperature,
            "max_tokens": max_tokens
        }
        
        start_time = time.time()
        
        try:
            response = requests.post(
                url, 
                headers=headers, 
                json=payload, 
                timeout=30
            )
            latency_ms = (time.time() - start_time) * 1000
            
            self.session_stats["calls"] += 1
            self.session_stats["total_latency"] += latency_ms
            
            if response.status_code == 200:
                data = response.json()
                return {
                    "content": data["choices"][0]["message"]["content"],
                    "latency_ms": round(latency_ms, 2),
                    "model": data.get("model", model),
                    "usage": data.get("usage", {})
                }
            else:
                self.session_stats["errors"] += 1
                print(f"❌ Erreur API {response.status_code}: {response.text}")
                return None
                
        except requests.exceptions.Timeout:
            self.session_stats["errors"] += 1
            print("❌ Timeout - l'API n'a pas répondu dans les 30 secondes")
            return None
        except Exception as e:
            self.session_stats["errors"] += 1
            print(f"❌ Erreur inattendue: {e}")
            return None
    
    def analyze_market_sentiment(
        self, 
        asset: str, 
        recent_prices: List[float],
        volume_data: List[float] = None
    ) -> Dict:
        """
        Analyse le sentiment de marché pour un actif
        
        Utilise GPT-4.1 pour une analyse nuancée
        Coût: ~$0.08 par appel (2048 tokens)
        """
        
        price_change = ((recent_prices[-1] - recent_prices[0]) / recent_prices[0] * 100) if len(recent_prices) > 1 else 0
        avg_volume = sum(volume_data) / len(volume_data) if volume_data else None
        
        prompt = f"""
Analyse le sentiment de marché pour {asset} en te basant sur:

1. Évolution du prix sur la période récente: {recent_prices[-5:] if len(recent_prices) >= 5 else recent_prices}
2. Variation en pourcentage: {price_change:.2f}%
3. Volume moyen: {avg_volume:.0f} si disponible

Structure ta réponse ainsi:

SENTIMENT GLOBAL

[Bullish / Bearish / Neutre] - avec justification

NIVEAU DE CONFIANCE

[Élevé / Moyen / Faible] - probabilité que cette tendance continue

RECOMMANDATION DCA

- Fréquence recommandée: [daily/weekly/monthly] - Montant suggéré: [ajuster selon volatilité] - Horizon temporel: [court/moyen/long terme]

RISQUES IDENTIFIÉS

- [3 risques principaux avec estimation de probabilité]

INDICATEURS CLÉS

- RSI approximatif: [suracheté/survendu/neutre] - Momentum: [fort/faible] - Volatilité: [haute/moyenne/basse] Sois précis et base tes recommandations sur l'analyse technique. """ result = self._make_request(prompt, temperature=0.3) if result: return { "asset": asset, "timestamp": datetime.now().isoformat(), "analysis": result["content"], "latency_ms": result["latency_ms"], "confidence": self._extract_confidence(result["content"]) } return None def generate_investment_report( self, backtest_result, market_context: str = "" ) -> Dict: """ Génère un rapport d'investissement personnalisé Utilise l'analyse DCA et le contexte de marché pour recommander Coût: ~$0.10 par rapport complet """ prompt = f""" Génère un rapport d'investissement détaillé basé sur ces données de backtest:

RÉSULTATS BACKTEST

- Actif: {backtest_result.asset} - Stratégie: {backtest_result.strategy_name} - Période: {backtest_result.period} - Total investi: ${backtest_result.total_invested:,.2f} - Valeur finale: ${backtest_result.final_value:,.2f} - Rendement total: {backtest_result.total_return_pct:.2f}% - Rendement annualisé: {backtest_result.annualized_return:.2f}% - Sharpe Ratio: {backtest_result.sharpe_ratio:.2f} - Drawdown maximum: {backtest_result.max_drawdown_pct:.2f}% - Volatilité annualisée: {backtest_result.volatility:.2f}% - Nombre de transactions: {backtest_result.num_transactions}

CONTEXTE DE MARCHÉ

{market_context} Structure le rapport ainsi:

📊 RAPPORT D'INVESTISSEMENT DCA

1. RÉSUMÉ EXÉCUTIF

[Synthèse en 3 lignes des performances et recommandations clés]

2. ANALYSE DES PERFORMANCES

[Avaluation détaillée des métriques, forces et faiblesses]

3. INDICATEURS DE RISQUE

[Analyse du drawdown, volatilité, Sharpe ratio]

4. RECOMMANDATIONS STRATÉGIQUES

- Position sizing optimal - Ajustements de fréquence - Allocation de portefeuille

5. PRÉDICTIONS COURT TERME (3 mois)

[basées sur les données historiques]

6. PLAN D'ACTION

[checklist concrète pour l'investisseur] Sois exhaustif mais concis. Utilise des emojis pour la lisibilité. """ result = self._make_request(prompt, max_tokens=2500, temperature=0.4) if result: return { "asset": backtest_result.asset, "generated_at": datetime.now().isoformat(), "report": result["content"], "latency_ms": result["latency_ms"], "cost_estimate": self._estimate_cost(result) } return None def optimize_dca_params( self, asset: str, historical_results: List[Dict], risk_tolerance: str = "medium" ) -> Dict: """ Optimise les paramètres DCA via IA Retourne les paramètres optimaux selon le profil de risque """ prompt = f""" Optimise les paramètres DCA pour l'actif {asset} selon le profil de risque "{risk_tolerance}". Historique des tests effectués: {json.dumps(historical_results, indent=2)} Profil de risque: {risk_tolerance} (low/medium/high) Réponds au format JSON suivant: {{ "frequency": "weekly|monthly|...", "amount_percentage": "X% du revenu", "rebalance_frequency": "quarterly|...", "emergency_buffer": "X mois de dépenses", "rationale": "explication détaillée", "expected_annual_return": "X%", "warnings": ["risque 1", "risque 2"] }} """ result = self._make_request(prompt, temperature=0.2) if result: try: # Extraction JSON si possible return { "asset": asset, "risk_profile": risk_tolerance, "optimized_params": result["content"], "latency_ms": result["latency_ms"] } except: return { "asset": asset, "risk_profile": risk_tolerance, "optimized_params": result["content"], "latency_ms": result["latency_ms"] } return None def _extract_confidence(self, text: str) -> str: """Extrait le niveau de confiance de l'analyse""" text_lower = text.lower() if any(word in text_lower for word in ['élevé', 'fort', 'haute confiance', 'confiance élevée']): return "Élevée" elif any(word in text_lower for word in ['faible', 'basse', 'incertain']): return "Faible" return "Moyenne" def _estimate_cost(self, result: Dict) -> Dict: """Estime le coût de la requête en dollars""" if "usage" in result and "usage" in result["usage"]: tokens = result["usage"].get("total_tokens", 2500) # GPT-4.1: $8/1M tokens cost = tokens * 8 / 1_000_000 return {"tokens": tokens, "estimated_cost_usd": round(cost, 4)} return {"tokens": 2500, "estimated_cost_usd": 0.02} def get_session_stats(self) -> Dict: """Retourne les statistiques de session""" avg_latency = ( self.session_stats["total_latency"] / self.session_stats["calls"] if self.session_stats["calls"] > 0 else 0 ) error_rate = ( self.session_stats["errors"] / self.session_stats["calls"] * 100 if self.session_stats["calls"] > 0 else 0 ) return { "total_calls": self.session_stats["calls"], "avg_latency_ms": round(avg_latency, 2), "total_latency_ms": round(self.session_stats["total_latency"], 2), "errors": self.session_stats["errors"], "error_rate_pct": round(error_rate, 2) }

=== EXEMPLE D'UTILISATION ===

if __name__ == "__main__": import os from dotenv import load_dotenv load_dotenv() api_key = os.getenv("HOLYSHEEP_API_KEY") if api_key: analyzer = HolySheepAnalyzer(api_key) # Exemple d'analyse de sentiment sentiment = analyzer.analyze_market_sentiment( asset="BTC-USD", recent_prices=[42000, 43500, 42800, 44100, 45200], volume_data=[25000000000, 28000000000, 26500000000, 29500000000, 31000000000] ) if sentiment: print(f"\n📊 Analyse de sentiment BTC-USD") print(f"⏱️ Latence: {sentiment['latency_ms']}ms") print(f"📈 Confiance: {sentiment['confidence']}") print(f"\n{'-'*50}") print(sentiment['analysis']) # Statistiques de session print(f"\n📈 Statistiques HolySheep API:") stats = analyzer.get_session_stats() for key, value in stats.items(): print(f" {key}: {value}") else: print("⚠️ HOLYSHEEP_API_KEY non configurée")

Visualisation des Résultats

# visualizer.py
import matplotlib.pyplot as plt
import matplotlib.dates as mdates
from matplotlib.figure import Figure
import pandas as pd
from typing import List, Dict, Optional
from datetime import datetime
import io
import base64

class DCAVisualizer:
    """
    Visualiseur complet pour résultats DCA backtesting
    
    Génère des graphiques publication-ready pour rapports d'investissement
    """
    
    def __init__(self, style: str = "seaborn-v0_8-darkgrid"):
        plt.style.use(style)
        self.colors = {
            'primary': '#2E86AB',
            'secondary': '#A23B72',
            'success': '#28A745',
            'danger': '#DC3545',
            'warning': '#FFC107',
            'neutral': '#6C757D',
            'portfolio': '#3498DB',
            'invested': '#E74C3C',
            'btc': '#F7931A',
            'eth': '#627EEA',
            'spy': '#4682B4'
        }
    
    def plot_portfolio_growth(
        self, 
        transactions: List, 
        save_path: str = None
    ) -> str:
        """
        Génère le graphique d'évolution du portefeuille
        
        Args:
            transactions: Liste de DCATransaction
            save_path: Chemin de sauvegarde (optionnel)
        
        Returns:
            Image encodée en base64
        """
        
        dates = [t.date for t in transactions]
        portfolio_values = [t.portfolio_value for t in transactions]
        invested_amounts = [t.cumulative_invested for t in transactions]
        
        fig, ax = plt.subplots(figsize=(14, 8))
        
        # Zones de différence
        ax.fill_between(
            dates, 
            invested_amounts, 
            portfolio_values, 
            alpha=0.3, 
            color=self.colors['success'] if portfolio_values[-1] > invested_amounts[-1] else self.colors['danger'],
            label='Plus-value / Moins-value'
        )
        
        # Lignes
        ax.plot(dates, portfolio_values, color=self.colors['portfolio'], linewidth=2.5, label='Valeur portefeuille')
        ax.plot(dates, invested_amounts, color=self.colors['invested'], linewidth=2, linestyle='--', label='Total investi')
        
        # Points d'achat
        ax.scatter(
            dates[::max(1, len(dates)//20)], 
            [portfolio_values[i] for i in range(0, len(portfolio_values), max(1, len(portfolio_values)//20))],
            color=self.colors['primary'], 
            s=50, 
            zorder=5, 
            label='Points d\'achat DCA'
        )
        
        # Annotations
        final_pnl = portfolio_values[-1] - invested_amounts[-1]
        final_pnl_pct = (final_pnl / invested_amounts[-1]) * 100
        
        ax.annotate(
            f'Final: ${portfolio_values[-1]:,.0f}\n(+{final_pnl_pct:.1f}%)',
            xy=(dates[-1], portfolio_values[-1]),
            xytext=(10, 10),
            textcoords='offset points',
            fontsize=11,
            fontweight='bold',
            color=self.colors['success'] if final_pnl > 0 else self.colors['danger'],
            bbox=dict(boxstyle='round,pad=0.5', facecolor='white', edgecolor='gray', alpha=0.9)
        )
        
        ax.set_xlabel('Date', fontsize=12)
        ax.set_ylabel('Valeur (USD)', fontsize=12)
        ax.set_title('📈 Évolution du Portefeuille DCA', fontsize=16, fontweight='bold', pad=20)
        ax.legend(loc='upper left', fontsize=10)
        ax.grid(True, alpha=0.3)
        
        # Format dates
        ax.xaxis.set_major_formatter(mdates.DateFormatter('%Y-%m'))
        ax.xaxis.set_major_locator(mdates.MonthLocator(interval=3))
        plt.xticks(rotation=45)
        
        plt.tight_layout()
        
        if save_path:
            plt.savefig(save_path, dpi=150, bbox_inches='tight')
            print(f"✅ Graphique sauvegardé: {save_path}")
        
        # Conversion base64
        img_buffer = io.BytesIO()
        plt.savefig(img_buffer, format='png', dpi=150, bbox_inches='tight')
        img_buffer.seek(0)
        img_base64 = base64.b64encode(img_buffer.read()).decode()
        plt.close()
        
        return f"data:image/png;base64,{img_base64}"
    
    def plot_comparison_matrix(
        self, 
        results_df: pd.DataFrame, 
        metric: str = "Rendement_%"
    ) -> str:
        """
        Génère une matrice de comparaison des stratégies
        """
        
        fig, ax = plt.subplots(figsize=(12, 6))
        
        # Pivot pour heatmap
        pivot_data = results_df.pivot_table(
            values=metric, 
            index='Montant', 
            columns='Fréquence',
            aggfunc='first'
        )
        
        # Nettoyage des valeurs
        for col in pivot_data.columns:
            pivot_data[col] = pivot_data[col].apply(
                lambda x: float(str(x).replace('%', '').replace('$', '').replace(',', '')) 
                if pd.notna(x) else 0
            )
        
        im