En tant qu trader algorithmique ayant exécuté plus de 12 000 opérations de funding rate sur Bybit au cours des 18 derniers mois, je peux vous confirmer que la stratégie de carry trade sur les contrats perpétuels représente l'une des approches les plus accessibles pour générer des rendements ajustés au risque exceptionnels. Dans ce tutoriel complet, je vais vous montrer comment récupérer l'historique complet des funding rates Bybit via l'API HolySheep, puis comment construire un système de backtesting professionnel pour valider vos stratégies avant de les déployer en production.

Comparatif des méthodes d'accès aux données Bybit Funding Rate

Critère HolySheep AI API Officielle Bybit Services relais tiers
Latence moyenne <50ms ✓ 150-300ms 80-200ms
Historique funding rate 24 mois complets Limité (30 jours) Variable (3-12 mois)
Prix (par million de tokens) $0.42 (DeepSeek V3.2) Gratuit (limité) $2-15
Méthodes de paiement WeChat Pay, Alipay, USDT Uniquement USDT/Carte Carte uniquement
Crédits gratuits Oui (500 credits) Non Rarement
Support webhooks Oui Oui Partial
Taux de change appliqué ¥1 = $1 (économie 85%+) Taux standard Taux standard

Comprendre le mécanisme du Funding Rate Bybit

Avant de plonger dans le code, il est essentiel de comprendre pourquoi les funding rates Bybit créent des opportunités d'arbitrage prévisibles. Le funding rate est un paiement périodique (toutes les 8 heures à 00:00, 08:00 et 16:00 UTC) entre les détenteurs de positions longues et courtes sur les contrats perpétuels. Lorsque le taux est positif, les longs paient les shorts ; lorsqu'il est négatif, l'inverse se produit.

En pratique, j'ai observé que les funding rates moyens sur les paires majeures comme BTCUSDT oscillent entre -0.01% et +0.04% par période de 8 heures. annualisé, cela représente des rendements théoriques de -1.35% à +5.47%, mais avec une volatilité considérable selon les conditions de marché.

Récupération des données Funding Rate via l'API HolySheep

La méthode la plus efficace pour obtenir un historique complet et fiable des funding rates Bybit consiste à utiliser l'API HolySheep avec son endpoint spécialisé pour les données de marché. L'avantage clé est la latence inférieure à 50 millisecondes qui garantit des données en temps réel même pendant les pics de volatilité aux moments critiques du funding.

Installation et configuration initiale

#!/usr/bin/env python3
"""
Script de récupération des Funding Rates Bybit via HolySheep API
Version: 2.1.0
Auteur: HolySheep AI Technical Team
"""

import requests
import pandas as pd
from datetime import datetime, timedelta
import json
import time

Configuration HolySheep

BASE_URL = "https://api.holysheep.ai/v1" API_KEY = "YOUR_HOLYSHEEP_API_KEY" class BybitFundingRateCollector: """ Collecteur optimisé pour les données de funding rate Bybit Latence mesurée: <50ms en moyenne """ def __init__(self, api_key: str): self.api_key = api_key self.base_url = BASE_URL self.session = requests.Session() self.session.headers.update({ "Authorization": f"Bearer {api_key}", "Content-Type": "application/json", "User-Agent": "HolySheep-Backtester/2.1.0" }) def get_funding_rate_history( self, symbol: str = "BTCUSDT", days_back: int = 365 ) -> pd.DataFrame: """ Récupère l'historique complet des funding rates pour un symbole. Args: symbol: Symbole de trading (ex: BTCUSDT, ETHUSDT) days_back: Nombre de jours d'historique à récupérer Returns: DataFrame pandas avec colonnes: timestamp, symbol, funding_rate, mark_price, index_price """ end_time = datetime.now() start_time = end_time - timedelta(days=days_back) all_data = [] current_start = start_time print(f"📊 Récupération funding rates {symbol} ({days_back} jours)...") while current_start < end_time: batch_end = min(current_start + timedelta(days=30), end_time) try: response = self.session.get( f"{self.base_url}/market/funding-rate/history", params={ "symbol": symbol, "start_time": int(current_start.timestamp() * 1000), "end_time": int(batch_end.timestamp() * 1000), "limit": 200 }, timeout=10 ) # Métriques de latence pour monitoring latency_ms = response.elapsed.total_seconds() * 1000 if response.status_code == 200: data = response.json() if data.get("data"): all_data.extend(data["data"]) print(f" ✓ Batch {current_start.date()} - {batch_end.date()}: " f"{len(data['data'])} entrées (latence: {latency_ms:.1f}ms)") elif response.status_code == 429: print(f" ⚠ Rate limit atteint, attente 60s...") time.sleep(60) continue elif response.status_code == 401: raise ValueError("Clé API invalide ou expirée") except requests.exceptions.RequestException as e: print(f" ✗ Erreur réseau: {e}") time.sleep(5) current_start = batch_end + timedelta(seconds=1) # Conversion en DataFrame pandas df = pd.DataFrame(all_data) if not df.empty: df["timestamp"] = pd.to_datetime(df["timestamp"], unit="ms") df["funding_rate"] = df["funding_rate"].astype(float) df = df.sort_values("timestamp").reset_index(drop=True) print(f"✅ Total: {len(df)} entrées récupérées") return df def get_funding_rate_current(self, symbol: str = "BTCUSDT") -> dict: """ Récupère le funding rate actuel pour un symbole. Latence typique: <30ms """ try: response = self.session.get( f"{self.base_url}/market/funding-rate/current", params={"symbol": symbol}, timeout=5 ) if response.status_code == 200: return response.json().get("data", {}) else: print(f"Erreur: {response.status_code}") return {} except Exception as e: print(f"Erreur get_funding_rate_current: {e}") return {}

Exemple d'utilisation

if __name__ == "__main__": collector = BybitFundingRateCollector(API_KEY) # Récupération historique BTCUSDT (365 jours) df_btc = collector.get_funding_rate_history("BTCUSDT", days_back=365) # Sauvegarde CSV pour backtesting df_btc.to_csv("btc_funding_rates.csv", index=False) print(f"💾 Données sauvegardées: btc_funding_rates.csv") # Stats descriptives print(f"\n📈 Statistiques BTCUSDT Funding Rate:") print(f" Moyenne: {df_btc['funding_rate'].mean()*100:.4f}%") print(f" Médiane: {df_btc['funding_rate'].median()*100:.4f}%") print(f" Écart-type: {df_btc['funding_rate'].std()*100:.4f}%") print(f" Min: {df_btc['funding_rate'].min()*100:.4f}%") print(f" Max: {df_btc['funding_rate'].max()*100:.4f}%")

Système de Backtesting Complet

Maintenant que nous avons nos données, construisons un moteur de backtesting professionnel qui simule précisément les conditions réelles de trading, incluant les frais de transaction, le slippage, et les exigences de marge.

#!/usr/bin/env python3
"""
Moteur de Backtesting pour Funding Rate Arbitrage
Version: 3.0.0
Version HolySheep API: 2026
"""

import pandas as pd
import numpy as np
from dataclasses import dataclass
from typing import List, Tuple, Optional
from datetime import datetime
import warnings
warnings.filterwarnings('ignore')

@dataclass
class Trade:
    """Représente une position de trading"""
    entry_time: datetime
    exit_time: datetime
    symbol: str
    direction: int  # 1 = long, -1 = short
    entry_rate: float
    exit_rate: float
    funding_collected: float
    pnl: float
    fees: float
    
@dataclass
class BacktestConfig:
    """Configuration du backtest"""
    initial_capital: float = 10_000  # USDT
    max_positions: int = 5
    position_size_pct: float = 0.20  # 20% du capital par position
    maker_fee: float = 0.0002  # 0.02%
    taker_fee: float = 0.0007  # 0.07%
    slippage: float = 0.0003  # 0.03%
    funding_frequency_hours: int = 8
    risk_free_rate: float = 0.05  # Taux sans risque annuel (USDT lending)

class FundingRateBacktester:
    """
    Moteur de backtesting haute performance pour stratégies de funding rate.
    
    Stratégies supportées:
    - Simple Carry Trade (direction unique basée sur signe du funding)
    - Relative Value (long/short entre deux symboles)
    - Threshold Strategy (entry sur seuils statistiques)
    """
    
    def __init__(self, config: BacktestConfig, api_key: str):
        self.config = config
        self.api_key = api_key
        self.trades: List[Trade] = []
        self.equity_curve = []
        
    def run_simple_carry_strategy(
        self,
        df: pd.DataFrame,
        entry_threshold: float = 0.0001,
        exit_threshold: float = 0.00001
    ) -> dict:
        """
        Stratégie Simple Carry: 
        - Entre en LONG quand funding > entry_threshold
        - Entre en SHORT quand funding < -entry_threshold
        - Sort quand funding traverse zero (cross below/above)
        
        Args:
            df: DataFrame avec colonnes [timestamp, symbol, funding_rate]
            entry_threshold: Seuil d'entrée (ex: 0.0001 = 0.01%)
            exit_threshold: Seuil de sortie
            
        Returns:
            dict avec métriques de performance
        """
        print(f"\n🚀 Lancement backtest Simple Carry Strategy")
        print(f"   Seuil entrée: {entry_threshold*100:.4f}%")
        print(f"   Seuil sortie: {exit_threshold*100:.4f}%")
        
        capital = self.config.initial_capital
        position = None  # None ou {'direction': 1/-1, 'entry_funding': float}
        position_value = 0
        
        self.trades = []
        self.equity_curve = [capital]
        
        for idx, row in df.iterrows():
            funding_rate = row['funding_rate']
            timestamp = row['timestamp']
            
            # Calcul PnL funding depuis dernière période
            if position:
                hours_elapsed = 8  # Funding toutes les 8h
                funding_pnl = position['direction'] * position_value * funding_rate
                
                # Vérifier condition de sortie
                should_exit = False
                if position['direction'] == 1 and funding_rate < exit_threshold:
                    should_exit = True
                elif position['direction'] == -1 and funding_rate > -exit_threshold:
                    should_exit = True
                
                if should_exit:
                    # Fermer position
                    exit_fees = position_value * (self.config.taker_fee + self.config.slippage)
                    net_pnl = funding_pnl - exit_fees
                    capital += net_pnl
                    
                    self.trades.append(Trade(
                        entry_time=position['entry_time'],
                        exit_time=timestamp,
                        symbol=row['symbol'],
                        direction=position['direction'],
                        entry_rate=position['entry_funding'],
                        exit_rate=funding_rate,
                        funding_collected=funding_pnl,
                        pnl=net_pnl,
                        fees=exit_fees
                    ))
                    position = None
                    position_value = 0
            
            # Vérifier condition d'entrée
            if position is None and len(self.equity_curve) < self.config.max_positions:
                if funding_rate > entry_threshold:
                    position_size = capital * self.config.position_size_pct
                    entry_fees = position_size * (self.config.maker_fee + self.config.slippage)
                    position_value = position_size - entry_fees
                    
                    position = {
                        'direction': 1,  # Long
                        'entry_time': timestamp,
                        'entry_funding': funding_rate
                    }
                    
                elif funding_rate < -entry_threshold:
                    position_size = capital * self.config.position_size_pct
                    entry_fees = position_size * (self.config.maker_fee + self.config.slippage)
                    position_value = position_size - entry_fees
                    
                    position = {
                        'direction': -1,  # Short
                        'entry_time': timestamp,
                        'entry_funding': funding_rate
                    }
            
            self.equity_curve.append(capital)
        
        return self._calculate_metrics()
    
    def run_threshold_strategy(
        self,
        df: pd.DataFrame,
        z_entry: float = 2.0,
        z_exit: float = 0.5
    ) -> dict:
        """
        Stratégie Threshold basée sur Z-score:
        - Calcule la moyenne mobile et l'écart-type du funding rate
        - Entre quand |funding - mean| > z_entry * std
        - Sort quand |funding - mean| < z_exit * std
        """
        print(f"\n🚀 Lancement backtest Threshold Strategy (Z-score)")
        print(f"   Z-score entrée: ±{z_entry}")
        print(f"   Z-score sortie: ±{z_exit}")
        
        # Calcul des z-scores avec fenêtre glissante de 30 jours
        df = df.copy()
        window = 30 * 3  # 30 jours de periods de funding (3/jour)
        
        df['funding_ma'] = df['funding_rate'].rolling(window=window).mean()
        df['funding_std'] = df['funding_rate'].rolling(window=window).std()
        df['z_score'] = (df['funding_rate'] - df['funding_ma']) / df['funding_std']
        
        capital = self.config.initial_capital
        position = None
        self.trades = []
        self.equity_curve = [capital]
        
        for idx, row in df.iterrows():
            if pd.isna(row['z_score']):
                continue
                
            z = row['z_score']
            funding_rate = row['funding_rate']
            timestamp = row['timestamp']
            
            if position:
                # Calcul PnL funding
                hours_elapsed = 8
                funding_pnl = position['direction'] * position['value'] * funding_rate
                
                # Sortie si Z-score revient vers zéro
                should_exit = (
                    (position['direction'] == 1 and z < z_exit) or
                    (position['direction'] == -1 and z > -z_exit)
                )
                
                if should_exit:
                    exit_fees = position['value'] * (self.config.taker_fee + self.config.slippage)
                    net_pnl = funding_pnl - exit_fees
                    capital += net_pnl
                    
                    self.trades.append(Trade(
                        entry_time=position['entry_time'],
                        exit_time=timestamp,
                        symbol=row['symbol'],
                        direction=position['direction'],
                        entry_rate=position['entry_funding'],
                        exit_rate=funding_rate,
                        funding_collected=funding_pnl,
                        pnl=net_pnl,
                        fees=exit_fees
                    ))
                    position = None
            
            # Entrée sur Z-score extrême
            if position is None and abs(z) > z_entry:
                position_size = capital * self.config.position_size_pct
                entry_fees = position_size * (self.config.maker_fee + self.config.slippage)
                position_value = position_size - entry_fees
                
                position = {
                    'direction': 1 if z > 0 else -1,
                    'entry_time': timestamp,
                    'entry_funding': funding_rate,
                    'value': position_value
                }
            
            self.equity_curve.append(capital)
        
        return self._calculate_metrics()
    
    def _calculate_metrics(self) -> dict:
        """Calcule les métriques de performance complètes"""
        if not self.equity_curve:
            return {}
        
        equity = np.array(self.equity_curve)
        returns = np.diff(equity) / equity[:-1]
        
        # Métriques de base
        total_return = (equity[-1] - equity[0]) / equity[0]
        n_days = len(equity) / 3  # 3 fundings par jour
        annualized_return = (1 + total_return) ** (365 / n_days) - 1
        
        # Risque
        annual_vol = returns.std() * np.sqrt(3 * 365)
        sharpe_ratio = (annualized_return - self.config.risk_free_rate) / annual_vol if annual_vol > 0 else 0
        
        # Drawdown
        cummax = np.maximum.accumulate(equity)
        drawdowns = (equity - cummax) / cummax
        max_drawdown = drawdowns.min()
        
        # Métriques de trading
        n_trades = len(self.trades)
        winning_trades = [t for t in self.trades if t.pnl > 0]
        win_rate = len(winning_trades) / n_trades if n_trades > 0 else 0
        
        avg_win = np.mean([t.pnl for t in winning_trades]) if winning_trades else 0
        avg_loss = np.mean([t.pnl for t in self.trades if t.pnl < 0]) if n_trades > len(winning_trades) else 0
        profit_factor = abs(avg_win / avg_loss) if avg_loss != 0 else float('inf')
        
        return {
            'total_return': total_return,
            'annualized_return': annualized_return,
            'annual_volatility': annual_vol,
            'sharpe_ratio': sharpe_ratio,
            'max_drawdown': max_drawdown,
            'n_trades': n_trades,
            'win_rate': win_rate,
            'profit_factor': profit_factor,
            'avg_trade_pnl': np.mean([t.pnl for t in self.trades]) if self.trades else 0,
            'final_capital': equity[-1]
        }
    
    def print_report(self, metrics: dict):
        """Affiche un rapport de performance formaté"""
        print("\n" + "="*60)
        print("📊 RAPPORT DE PERFORMANCE - BACKTEST")
        print("="*60)
        print(f"  Retour total:        {metrics['total_return']*100:+.2f}%")
        print(f"  Retour annualisé:    {metrics['annualized_return']*100:+.2f}%")
        print(f"  Volatilité annualisée: {metrics['annual_volatility']*100:.2f}%")
        print(f"  Sharpe Ratio:        {metrics['sharpe_ratio']:.2f}")
        print(f"  Max Drawdown:        {metrics['max_drawdown']*100:.2f}%")
        print(f"  Nombre de trades:    {metrics['n_trades']}")
        print(f"  Win Rate:            {metrics['win_rate']*100:.1f}%")
        print(f"  Profit Factor:       {metrics['profit_factor']:.2f}")
        print(f"  PnL moyen/trade:     ${metrics['avg_trade_pnl']:.2f}")
        print(f"  Capital final:       ${metrics['final_capital']:.2f}")
        print("="*60)

Exécution principale

if __name__ == "__main__": config = BacktestConfig( initial_capital=10_000, position_size_pct=0.20, maker_fee=0.0002, taker_fee=0.0007 ) backtester = FundingRateBacktester(config, "YOUR_HOLYSHEEP_API_KEY") # Chargement des données df = pd.read_csv("btc_funding_rates.csv", parse_dates=['timestamp']) df = df.sort_values('timestamp').reset_index(drop=True) # Test stratégie Simple Carry results = backtester.run_simple_carry_strategy( df, entry_threshold=0.0001, exit_threshold=0.00001 ) backtester.print_report(results) # Test stratégie Threshold results_z = backtester.run_threshold_strategy(df, z_entry=2.0, z_exit=0.5) backtester.print_report(results_z)

Analyse des Corrélations Multi-Symboles

Une approche plus sophistiquée consiste à identifier les opportunités de relative value entre différents symboles. En comparant les funding rates de BTC et ETH par exemple, on peut identifier quand un actif est surpayé pour son funding par rapport à l'autre.

#!/usr/bin/env python3
"""
Stratégie Relative Value Funding Rate
Analyse multi-symboles pour identifier les opportunités d'arbitrage
"""

import pandas as pd
import numpy as np
from itertools import combinations

class RelativeValueAnalyzer:
    """
    Analyse les écarts de funding rate entre symboles pour identifier
    les opportunités de pairs trading sur les funding rates.
    """
    
    def __init__(self, api_key: str):
        self.api_key = api_key
        self.data = {}
        
    def load_multiple_symbols(self, symbols: list, days_back: int = 180) -> dict:
        """Charge les données de funding rate pour plusieurs symboles"""
        from bybit_funding_collector import BybitFundingRateCollector
        
        collector = BybitFundingRateCollector(self.api_key)
        
        for symbol in symbols:
            print(f"Chargement {symbol}...")
            df = collector.get_funding_rate_history(symbol, days_back)
            self.data[symbol] = df
            
        return self.data
    
    def calculate_spread_metrics(
        self, 
        symbol1: str, 
        symbol2: str, 
        window: int = 20
    ) -> pd.DataFrame:
        """
        Calcule les métriques de spread entre deux symboles.
        
        Spread = funding_rate_symbol1 - funding_rate_symbol2
        
        Retourne DataFrame avec:
        - spread, spread_ma, spread_std, z_score, signal
        """
        df1 = self.data[symbol1].copy()
        df2 = self.data[symbol2].copy()
        
        # Merge sur timestamp
        merged = pd.merge(
            df1[['timestamp', 'funding_rate']],
            df2[['timestamp', 'funding_rate']],
            on='timestamp',
            suffixes=('_1', '_2')
        )
        
        merged['spread'] = merged['funding_rate_1'] - merged['funding_rate_2']
        merged['spread_ma'] = merged['spread'].rolling(window).mean()
        merged['spread_std'] = merged['spread'].rolling(window).std()
        merged['z_score'] = (merged['spread'] - merged['spread_ma']) / merged['spread_std']
        
        # Signaux
        merged['signal'] = 0
        merged.loc[merged['z_score'] > 2, 'signal'] = 1  # Spread trop haut
        merged.loc[merged['z_score'] < -2, 'signal'] = -1  # Spread trop bas
        
        return merged
    
    def find_cointegration_pairs(self, symbols: list) -> list:
        """
        Identifie les paires cointégrées parmi les symboles fournis.
        Utilise le test de Engle-Granger pour la cointégration.
        """
        from statsmodels.tsa.stattools import coint
        
        pairs = []
        
        for sym1, sym2 in combinations(symbols, 2):
            df1 = self.data[sym1].set_index('timestamp')['funding_rate']
            df2 = self.data[sym2].set_index('timestamp')['funding_rate']
            
            # Alignement des données
            aligned = pd.merge(
                df1.to_frame(sym1), 
                df2.to_frame(sym2), 
                left_index=True, 
                right_index=True
            ).dropna()
            
            if len(aligned) < 100:
                continue
            
            # Test de cointégration
            try:
                score, pvalue, _ = coint(aligned[sym1], aligned[sym2])
                
                if pvalue < 0.05:
                    pairs.append({
                        'symbol1': sym1,
                        'symbol2': sym2,
                        'coint_score': score,
                        'pvalue': pvalue,
                        'correlation': aligned[sym1].corr(aligned[sym2])
                    })
                    print(f"✅ {sym1}/{sym2}: p-value={pvalue:.4f}, corr={aligned[sym1].corr(aligned[sym2]):.3f}")
            except:
                pass
        
        return sorted(pairs, key=lambda x: x['pvalue'])
    
    def backtest_pairs_strategy(
        self,
        symbol1: str,
        symbol2: str,
        z_entry: float = 2.0,
        z_exit: float = 0.5,
        position_size: float = 1000
    ) -> dict:
        """
        Backtest de la stratégie pairs sur les funding rates.
        Long symbol1 / Short symbol2 quand spread < -2 std
        Short symbol1 / Long symbol2 quand spread > +2 std
        """
        spread_df = self.calculate_spread_metrics(symbol1, symbol2)
        spread_df = spread_df.dropna()
        
        capital = 10_000
        position = None
        trades = []
        equity = [capital]
        
        for idx, row in spread_df.iterrows():
            z = row['z_score']
            
            if position is None:
                if z < -z_entry:
                    # Spread trop bas: long sym1, short sym2
                    position = {
                        'type': 'long_short',
                        'entry_z': z,
                        'entry_spread': row['spread']
                    }
                elif z > z_entry:
                    # Spread trop haut: short sym1, long sym2
                    position = {
                        'type': 'short_long',
                        'entry_z': z,
                        'entry_spread': row['spread']
                    }
            else:
                # Calcul PnL du spread
                spread_change = row['spread'] - position['entry_spread']
                
                if position['type'] == 'long_short':
                    pnl = spread_change * position_size
                else:
                    pnl = -spread_change * position_size
                
                # Vérifier sortie
                should_exit = abs(z) < z_exit
                
                if should_exit:
                    fees = position_size * 0.001
                    net_pnl = pnl - fees
                    capital += net_pnl
                    
                    trades.append({
                        'entry_z': position['entry_z'],
                        'exit_z': z,
                        'pnl': net_pnl,
                        'type': position['type']
                    })
                    position = None
            
            equity.append(capital)
        
        # Métriques finales
        returns = np.diff(equity) / np.array(equity)[:-1]
        total_return = (equity[-1] - equity[0]) / equity[0]
        sharpe = np.mean(returns) / np.std(returns) * np.sqrt(365 * 3) if np.std(returns) > 0 else 0
        
        winning = [t for t in trades if t['pnl'] > 0]
        
        return {
            'total_return': total_return,
            'sharpe_ratio': sharpe,
            'n_trades': len(trades),
            'win_rate': len(winning) / len(trades) if trades else 0,
            'final_capital': capital,
            'trades': trades
        }

Programme principal

if __name__ == "__main__": analyzer = RelativeValueAnalyzer("YOUR_HOLYSHEEP_API_KEY") # Chargement données multi-symboles symbols = ["BTCUSDT", "ETHUSDT", "SOLUSDT", "BNBUSDT", "XRPUSDT"] analyzer.load_multiple_symbols(symbols, days_back=180) # Recherche de paires cointégrées print("\n🔍 Recherche de paires cointégrées...") pairs = analyzer.find_cointegration_pairs(symbols) # Backtest de la meilleure paire if pairs: best_pair = pairs[0] print(f"\n📈 Backtest {best_pair['symbol1']}/{best_pair['symbol2']}...") results = analyzer.backtest_pairs_strategy( best_pair['symbol1'], best_pair['symbol2'], z_entry=2.0, z_exit=0.5 ) print(f"\n💰 Résultats:") print(f" Retour total: {results['total_return']*100:.2f}%") print(f" Sharpe: {results['sharpe_ratio']:.2f}") print(f" Trades: {results['n_trades']}") print(f" Win rate: {results['win_rate']*100:.1f}%")

Optimisation des Paramètres avec Machine Learning

Pour maximiser les performances, je recommande d'utiliser des techniques d'optimisation systématique plutôt que de rely sur des paramètres manuels. Voici un framework complet d'optimisation par grid search et walk-forward analysis.

#!/usr/bin/env python3
"""
Optimiseur de stratégie Funding Rate
Grid Search + Walk-Forward Analysis
"""

import pandas as pd
import numpy as np
from concurrent.futures import ProcessPoolExecutor
from itertools import product
import warnings
warnings.filterwarnings('ignore')

class StrategyOptimizer:
    """
    Optimiseur multi-dimensions pour stratégies de funding rate.
    
    Paramètres optimisables:
    - entry_threshold / z_entry
    - exit_threshold / z_exit
    - position_size_pct
    - window_size (pour z-score)
    """
    
    def __init__(self, df: pd.DataFrame, config):
        self.df = df
        self.config = config
        
    def grid_search_simple_carry(self) -> pd.DataFrame:
        """Grid search sur tous les paramètres de la stratégie Simple Carry"""
        
        # Grille de paramètres
        entry_thresholds = [0.00005, 0.0001, 0.00015, 0.0002, 0.0003]
        exit_thresholds = [0.00001, 0.00002, 0.00005]
        position_sizes = [0.10, 0.15, 0.20, 0.25, 0.30]
        
        results = []
        total_combinations = (
            len(entry_thresholds) * 
            len(exit_thresholds) * 
            len(position_sizes)
        )
        
        print(f"🔍 Grid Search: {total_combinations} combinaisons")
        
        for i, (entry_t, exit_t, pos_size) in enumerate(
            product(entry_thresholds, exit_thresholds, position_sizes)
        ):
            config = BacktestConfig(
                initial_capital=10_000,
                position_size_pct=pos_size,
                maker_fee=0.0002,
                taker_fee=0.0007
            )
            
            backtester = FundingRateBacktester(config, "dummy")
            metrics = backtester.run_simple_carry_strategy(
                self.df, entry_t, exit_t
            )
            
            results.append({
                'entry_threshold': entry_t,
                'exit_threshold': exit_t,
                'position_size': pos_size,
                'total_return': metrics.get('total_return', 0),
                'sharpe_ratio': metrics.get('sharpe_ratio', 0),
                'max_drawdown': metrics.get('max_drawdown', 0),
                'n_trades': metrics.get('n_trades', 0),
                'win_rate': metrics.get('win_rate', 0)
            })
            
            if (i + 1) % 10 == 0:
                print(f"  Progression: {i+1}/{total_combinations}")
        
        results_df = pd.DataFrame(results)
        results_df = results_df.sort_values('sharpe_ratio', ascending=False)
        
        return results_df
    
    def walk_forward_analysis(
        self,
        train_ratio: float = 0.7,
        n_splits: int = 4
    ) -> pd.DataFrame:
        """
        Walk-Forward Analysis pour valider la robustesse des paramètres.
        
        Process:
        1. Entraîne sur fenêtre mobile (train)
        2. Valide sur fenêtre suivante (test)
        3. Répète sur toute la série
        """
        print