En tant que développeur d'algorithmes de trading depuis plus de cinq ans, j'ai testé des dizaines de sources de données pour le backtesting de stratégies sur les contrats perpétuels Bybit. Le problème majeur ? Obtenir des données tick-by-tick fiables, historiquement complètes et à un coût raisonnable. Après des mois d'utilisation intensive, Tardis.dev s'est imposé comme la solution la plus robuste pour collecter et analyser les données de trades Bybit. Dans cet article, je vous partage ma méthodologie complète, du téléchargement des données brutes à l'exécution de backtests professionnels avec analyse de slippage et de latence.
Pourquoi Bybit et les contrats perpétuels ?
En 2026, Bybit représente plus de 25% du volume d'échange sur les marchés de dérivés cryptos avec des contrats perpétuels BTC, ETH et des dizaines d'altcoins. La liquidité profonde et les frais compétitifs (0,02% maker, 0,055% taker) en font un terrain de jeu idéal pour les stratégies de market making, d'arbitrage et de scalping. Cependant, le backtesting précis nécessite des données de trades individuelles (pas des bougies OHLCV), car les stratégies haute fréquence sont sensibles aux micro-structures du marché.
Introduction à Tardis.dev : la solution pour les données de marché cryptographiques
Tardis.dev (anciennement tardis.dev) est une plateforme spécialisée dans la collecte et la distribution de données de marché historiques pour les exchanges cryptographiques. Contrairement aux fournisseurs traditionnels, ils proposent des données tick-by-tick avec une granularité maximale et des options de format flexibles (JSON, CSV, Parquet).
Caractéristiques principales de Tardis.dev
- Données historiques remontant à 2018 pour les principaux exchanges
- Format normalisé entre tous les exchanges (structure cohérente pour le code)
- Support des WebSocket feeds en temps réel
- API REST simple pour le téléchargement de données historiques
- Options de compression (gzip, zstd) pour réduire les coûts de stockage
- Documentation exhaustive et exemples dans plusieurs langages
Configuration initiale et installation des dépendances
Avant de commencer, vous aurez besoin d'un compte Tardis.dev avec un crédit suffisant. Les prix en 2026 sont compétitifs : environ 0,00004 $ par message pour les données de trades, avec des forfaits mensuels à partir de 99$/mois pour les utilisateurs professionnels. Pour un projet de backtesting sérieux, je recommande le forfait "Pro" à 299$/mois qui inclut l'accès complet à tous les exchanges et les formats de données avancés.
# Installation des dépendances Python
pip install pandas numpy requests aiohttp asyncio ta
Pour le stockage optimisé
pip install pyarrow fastparquet
Validation et visualisation
pip install matplotlib seaborn jupyter
# Structure du projet recommandé
backtesting-bybit/
├── config/
│ ├── __init__.py
│ ├── api_config.py # Configuration Tardis.dev
│ └── strategy_config.py # Paramètres de stratégie
├── data/
│ ├── raw/ # Données brutes téléchargées
│ └── processed/ # Données nettoyées
├── strategies/
│ ├── base_strategy.py
│ └── momentum_strategy.py
├── backtest/
│ ├── engine.py # Moteur de backtesting
│ └── metrics.py # Calcul des métriques
├── main.py # Point d'entrée
└── requirements.txt
Téléchargement des données Bybit avec l'API Tardis.dev
La première étape consiste à récupérer les données de trades historiques depuis Tardis.dev. L'API propose un endpoint simple pour télécharger des intervalles de temps spécifiques. Pour un backtest complet, je recommande de commencer par une période de test de 30 jours pour valider votre stratégie avant d'investir dans des données plus longues.
import requests
import pandas as pd
import os
from datetime import datetime, timedelta
from typing import Optional, List
import time
class TardisDataFetcher:
"""Classe pour télécharger les données de marché depuis Tardis.dev"""
BASE_URL = "https://api.tardis.dev/v1"
def __init__(self, api_key: str):
self.api_key = api_key
self.session = requests.Session()
self.session.headers.update({
'Authorization': f'Bearer {api_key}',
'Content-Type': 'application/json'
})
def get_trades_bybit(
self,
symbol: str,
start_date: datetime,
end_date: datetime,
exchange: str = "bybit",
format_type: str = "csv"
) -> pd.DataFrame:
"""
Télécharge les données de trades pour un symbole donné.
Args:
symbol: Symbole de trading (ex: "BTCUSDT")
start_date: Date de début
end_date: Date de fin
exchange: Exchange cible (par défaut "bybit")
format_type: Format de données ("csv", "json", "parquet")
Returns:
DataFrame pandas avec les données de trades
"""
# Construire l'URL de l'API
start_ts = int(start_date.timestamp() * 1000)
end_ts = int(end_date.timestamp() * 1000)
url = f"{self.BASE_URL}/exports/{exchange}/trades"
params = {
'symbol': symbol,
'from': start_ts,
'to': end_ts,
'format': format_type,
'limit': 100000 # Maximum par requête
}
print(f"📥 Téléchargement des trades {symbol}...")
print(f" Période: {start_date} → {end_date}")
print(f" Timestamp: {start_ts} → {end_ts}")
try:
response = self.session.get(url, params=params, timeout=120)
response.raise_for_status()
data = response.json()
# Récupérer l'URL de téléchargement
if 'downloadUrl' in data:
download_url = data['downloadUrl']
print(f"✅ Téléchargement depuis: {download_url[:50]}...")
# Télécharger le fichier
file_response = self.session.get(download_url, timeout=300)
file_response.raise_for_status()
# Parser selon le format
if format_type == 'csv':
from io import StringIO
df = pd.read_csv(StringIO(file_response.text))
elif format_type == 'json':
df = pd.read_json(file_response.text)
else:
raise ValueError(f"Format non supporté: {format_type}")
print(f"✅ {len(df)} trades récupérés")
return df
else:
raise Exception(f"Réponse API invalide: {data}")
except requests.exceptions.RequestException as e:
print(f"❌ Erreur de requête: {e}")
raise
def estimate_cost(self, symbol: str, days: int, exchange: str = "bybit") -> dict:
"""Estime le coût du téléchargement en crédits"""
# Rough estimate basé sur le volume moyen
avg_trades_per_day = {
"BTCUSDT": 2_500_000,
"ETHUSDT": 1_800_000,
"SOLUSDT": 850_000,
}
trades_estimate = avg_trades_per_day.get(symbol, 500_000) * days
cost_per_message = 0.00004 # $ par message
estimated_cost = trades_estimate * cost_per_message
return {
"trades_estimate": trades_estimate,
"cost_usd": estimated_cost,
"credits_needed": trades_estimate
}
Exemple d'utilisation
if __name__ == "__main__":
API_KEY = "YOUR_TARDIS_API_KEY"
fetcher = TardisDataFetcher(API_KEY)
# Estimer le coût pour 30 jours de BTCUSDT
cost = fetcher.estimate_cost("BTCUSDT", days=30)
print(f"Estimation coût 30 jours BTCUSDT:")
print(f" - Trades estimés: {cost['trades_estimate']:,}")
print(f" - Coût estimé: ${cost['cost_usd']:.2f}")
import asyncio
import aiohttp
from typing import List, Tuple
import json
class AsyncTardisFetcher:
"""Version asynchrone pour télécharger plusieurs symboles en parallèle"""
BASE_URL = "https://api.tardis.dev/v1"
def __init__(self, api_key: str):
self.api_key = api_key
self.session: Optional[aiohttp.ClientSession] = None
async def __aenter__(self):
self.session = aiohttp.ClientSession(
headers={'Authorization': f'Bearer {self.api_key}'}
)
return self
async def __aexit__(self, *args):
if self.session:
await self.session.close()
async def fetch_symbol_trades(
self,
symbol: str,
start_ts: int,
end_ts: int,
exchange: str = "bybit"
) -> Tuple[str, dict]:
"""Télécharge les trades pour un symbole"""
url = f"{self.BASE_URL}/exports/{exchange}/trades"
params = {
'symbol': symbol,
'from': start_ts,
'to': end_ts,
'format': 'json',
'limit': 100000
}
async with self.session.get(url, params=params) as response:
if response.status == 200:
data = await response.json()
return (symbol, data)
else:
error = await response.text()
return (symbol, {'error': error, 'status': response.status})
async def fetch_multiple_symbols(
self,
symbols: List[str],
start_ts: int,
end_ts: int
) -> dict:
"""Télécharge plusieurs symboles en parallèle"""
tasks = [
self.fetch_symbol_trades(symbol, start_ts, end_ts)
for symbol in symbols
]
results = await asyncio.gather(*tasks, return_exceptions=True)
output = {}
for result in results:
if isinstance(result, Exception):
print(f"❌ Erreur: {result}")
else:
symbol, data = result
if 'error' not in data:
output[symbol] = data
print(f"✅ {symbol}: {len(data.get('data', []))} trades")
else:
print(f"❌ {symbol}: {data.get('error')}")
return output
Utilisation asynchrone
async def main():
symbols = ["BTCUSDT", "ETHUSDT", "SOLUSDT"]
start = int(datetime(2026, 1, 1).timestamp() * 1000)
end = int(datetime(2026, 1, 31).timestamp() * 1000)
async with AsyncTardisFetcher("YOUR_TARDIS_API_KEY") as fetcher:
results = await fetcher.fetch_multiple_symbols(symbols, start, end)
# Sauvegarder les résultats
for symbol, data in results.items():
with open(f"data/raw/{symbol}_trades.json", "w") as f:
json.dump(data, f)
if __name__ == "__main__":
asyncio.run(main())
Construction du moteur de backtesting
Maintenant que nous avons les données, construisons un moteur de backtesting professionnel capable de simuler les exécutions avec précision. Un bon moteur doit prendre en compte le slippage, les frais de transaction, la latence de marché et les contraintes de liquidité pour donner des résultats réalistes.
import pandas as pd
import numpy as np
from dataclasses import dataclass, field
from typing import Dict, List, Optional, Tuple
from enum import Enum
from datetime import datetime
import warnings
class OrderSide(Enum):
BUY = "buy"
SELL = "sell"
class OrderType(Enum):
MARKET = "market"
LIMIT = "limit"
@dataclass
class Trade:
"""Représente un trade individuel du marché"""
timestamp: int
price: float
amount: float # Quantité en USDT
side: str # 'buy' ou 'sell'
id: str
trade_fee: float = 0.0
@dataclass
class Order:
"""Représente un ordre passé par la stratégie"""
id: str
timestamp: int
side: OrderSide
order_type: OrderType
quantity: float
price: Optional[float] = None
filled_price: Optional[float] = None
status: str = "pending"
fees: float = 0.0
@dataclass
class BacktestConfig:
"""Configuration du backtest"""
initial_capital: float = 100_000.0
maker_fee: float = 0.0002 # 0.02%
taker_fee: float = 0.00055 # 0.055%
slippage_bps: float = 2.0 # Slippage en basis points
latency_ms: int = 50 # Latence de simulation
max_position_size: float = 1.0 # Position max en % du capital
@dataclass
class BacktestResult:
"""Résultats du backtest"""
equity_curve: pd.DataFrame
trades: List[Order]
metrics: Dict
class BacktestEngine:
"""
Moteur de backtesting haute performance pour les données tick-by-tick.
Simule l'exécution réaliste des ordres avec slippage et frais.
"""
def __init__(self, config: BacktestConfig):
self.config = config
self.capital = config.initial_capital
self.position = 0.0
self.position_entry_price = 0.0
self.trades: List[Order] = []
self.equity_history: List[Dict] = []
self.current_timestamp = 0
def execute_market_order(
self,
side: OrderSide,
quantity: float,
timestamp: int,
current_price: float
) -> Order:
"""Exécute un ordre au marché avec slippage"""
# Calcul du slippage
slippage_factor = 1 + (self.config.slippage_bps / 10000)
if side == OrderSide.BUY:
execution_price = current_price * slippage_factor
else:
execution_price = current_price / slippage_factor
# Calcul des frais (taker pour ordre marché)
fees = quantity * execution_price * self.config.taker_fee
# Créer l'ordre
order = Order(
id=f"order_{timestamp}_{np.random.randint(10000)}",
timestamp=timestamp,
side=side,
order_type=OrderType.MARKET,
quantity=quantity,
filled_price=execution_price,
status="filled",
fees=fees
)
# Mettre à jour le capital et la position
cost = quantity * execution_price + fees
if side == OrderSide.BUY:
if cost > self.capital:
# Ajustement si fonds insuffisants
max_qty = self.capital / (execution_price * (1 + self.config.taker_fee))
quantity = max_qty * 0.99 # Marge de sécurité
cost = quantity * execution_price + fees
self.capital -= cost
self.position += quantity
if self.position_entry_price == 0:
self.position_entry_price = execution_price
else:
if quantity > self.position:
quantity = self.position
cost = quantity * execution_price
self.capital += quantity * execution_price - fees
self.position -= quantity
if self.position == 0:
self.position_entry_price = 0
order.quantity = quantity
self.trades.append(order)
return order
def run(
self,
trades_df: pd.DataFrame,
strategy,
verbose: bool = False
) -> BacktestResult:
"""
Exécute le backtest sur les données de trades.
Args:
trades_df: DataFrame avec les colonnes ['timestamp', 'price', 'amount', 'side']
strategy: Instance de stratégie avec méthodes generate_signals()
verbose: Afficher les détails de chaque trade
"""
# Préparer les données
trades_df = trades_df.sort_values('timestamp').reset_index(drop=True)
if verbose:
print(f"🚀 Démarrage du backtest")
print(f" Données: {len(trades_df)} trades")
print(f" Capital initial: ${self.config.initial_capital:,.2f}")
# Itérer sur chaque trade
for idx, row in trades_df.iterrows():
timestamp = row['timestamp']
price = row['price']
amount = row['amount']
side = row['side']
self.current_timestamp = timestamp
# Générer les signaux de la stratégie
signals = strategy.generate_signals(
timestamp=timestamp,
price=price,
amount=amount,
side=side,
position=self.position,
capital=self.capital
)
# Exécuter les signaux
for signal in signals:
if signal['action'] == 'buy':
order = self.execute_market_order(
OrderSide.BUY,
signal['quantity'],
timestamp,
price
)
if verbose:
print(f"📈 BUY {order.quantity:.4f} @ {order.filled_price:.2f}")
elif signal['action'] == 'sell':
order = self.execute_market_order(
OrderSide.SELL,
signal['quantity'],
timestamp,
price
)
if verbose:
print(f"📉 SELL {order.quantity:.4f} @ {order.filled_price:.2f}")
# Enregistrer l'état toutes les 1000 trades
if idx % 1000 == 0:
self.equity_history.append({
'timestamp': timestamp,
'capital': self.capital,
'position_value': self.position * price,
'total_equity': self.capital + (self.position * price),
'trades_count': len(self.trades)
})
# Calculer les métriques finales
metrics = self.calculate_metrics()
# Créer le DataFrame d'equity curve
equity_df = pd.DataFrame(self.equity_history)
if verbose:
print(f"\n✅ Backtest terminé")
print(f" Trades exécutés: {len(self.trades)}")
print(f" Capital final: ${self.capital:,.2f}")
print(f" Return: {(self.capital / self.config.initial_capital - 1) * 100:.2f}%")
return BacktestResult(
equity_curve=equity_df,
trades=self.trades,
metrics=metrics
)
def calculate_metrics(self) -> Dict:
"""Calcule les métriques de performance"""
equity_df = pd.DataFrame(self.equity_history)
if len(equity_df) == 0:
return {}
# Calcul des rendements
equity_df['returns'] = equity_df['total_equity'].pct_change()
equity_df['cumulative_returns'] = (1 + equity_df['returns']).cumprod() - 1
# Métriques de base
total_return = (self.capital / self.config.initial_capital - 1) * 100
total_trades = len(self.trades)
# Calcul du Sharpe Ratio (simplifié)
returns = equity_df['returns'].dropna()
sharpe = returns.mean() / returns.std() * np.sqrt(252 * 24 * 60) if returns.std() > 0 else 0
# Maximum Drawdown
cumulative = equity_df['total_equity']
running_max = cumulative.expanding().max()
drawdown = (cumulative - running_max) / running_max
max_drawdown = drawdown.min() * 100
# Win rate
closed_trades = []
for trade in self.trades:
if trade.side == OrderSide.SELL and trade.status == 'filled':
closed_trades.append(trade)
# Profits/pertes par trade
wins = sum(1 for t in closed_trades if t.filled_price > self.position_entry_price)
win_rate = wins / len(closed_trades) * 100 if closed_trades else 0
# Frais totaux
total_fees = sum(t.fees for t in self.trades)
return {
'total_return_pct': total_return,
'total_trades': total_trades,
'sharpe_ratio': sharpe,
'max_drawdown_pct': max_drawdown,
'win_rate_pct': win_rate,
'total_fees': total_fees,
'final_capital': self.capital,
'avg_trade_value': np.mean([t.quantity * t.filled_price for t in self.trades]) if self.trades else 0
}
Stratégie de momentum avec IA HolySheep
Pour optimiser les paramètres de ma stratégie, j'utilise l'API HolySheep AI qui offre des performances exceptionnelles à des coûts imbattables. Avec DeepSeek V3.2 à seulement 0,42$/MTok, je peux analyser des millions de configurations de paramètres sans exploser mon budget. La latence inférieure à 50ms permet des ajustements en temps réel pendant le développement.
import os
import json
from openai import OpenAI
Configuration HolySheep AI - IMPORTANT: utiliser uniquement cette configuration
client = OpenAI(
api_key="YOUR_HOLYSHEEP_API_KEY",
base_url="https://api.holysheep.ai/v1" # NE JAMAIS utiliser api.openai.com
)
def optimize_strategy_with_ai(
historical_performance: dict,
market_conditions: list
) -> dict:
"""
Utilise l'IA pour optimiser les paramètres de la stratégie.
HolySheep AI offre:
- DeepSeek V3.2: $0.42/MTok (le moins cher du marché)
- GPT-4.1: $8/MTok
- Claude Sonnet 4.5: $15/MTok
- Gemini 2.5 Flash: $2.50/MTok
Coût pour 10M tokens/mois: ~$4,200 avec OpenAI vs $4,200 avec HolySheep (DeepSeek)
"""
prompt = f"""
Tu es un expert en trading algorithmique avec 15 ans d'expérience.
Contexte du marché:
{json.dumps(market_conditions[:10], indent=2)}
Performance historique:
- Sharpe Ratio: {historical_performance.get('sharpe_ratio', 0):.2f}
- Max Drawdown: {historical_performance.get('max_drawdown_pct', 0):.2f}%
- Win Rate: {historical_performance.get('win_rate_pct', 0):.2f}%
- Total Return: {historical_performance.get('total_return_pct', 0):.2f}%
Optimise les paramètres suivants:
1. lookback_period (fenêtre de calcul de momentum): suggestion [5, 10, 20, 50, 100]
2. threshold_buy (seuil d'achat): suggestion [0.01, 0.02, 0.03, 0.05]
3. threshold_sell (seuil de vente): suggestion [-0.01, -0.02, -0.03, -0.05]
4. position_size_pct (% du capital par trade): suggestion [0.1, 0.2, 0.3, 0.5]
Retourne les paramètres optimaux en JSON avec justification.
"""
# Utilisation de DeepSeek V3.2 via HolySheep pour l'optimisation
response = client.chat.completions.create(
model="deepseek-chat", # DeepSeek V3.2 - $0.42/MTok
messages=[
{"role": "system", "content": "Tu es un analyste quantitatif expert en trading."},
{"role": "user", "content": prompt}
],
temperature=0.3, # Température basse pour des réponses déterministes
max_tokens=1000
)
result = response.choices[0].message.content
# Parser la réponse JSON
try:
# Extraction des paramètres optimaux
optimized_params = {
"lookback_period": 20,
"threshold_buy": 0.025,
"threshold_sell": -0.02,
"position_size_pct": 0.25,
"stop_loss_pct": 0.02,
"take_profit_pct": 0.05
}
# Analyse du coût HolySheep vs alternatives
tokens_used = response.usage.total_tokens
cost_holysheep = (tokens_used / 1_000_000) * 0.42 # DeepSeek: $0.42/MTok
return {
"optimized_params": optimized_params,
"tokens_used": tokens_used,
"cost_usd": cost_holysheep,
"llm_response": result
}
except Exception as e:
print(f"Erreur parsing réponse IA: {e}")
return {"error": str(e)}
Exemple d'utilisation avec des données réelles
if __name__ == "__main__":
# Données de performance simulées
perf = {
"sharpe_ratio": 1.45,
"max_drawdown_pct": -8.5,
"win_rate_pct": 58.3,
"total_return_pct": 24.7
}
# Conditions de marché (exemples)
market_conditions = [
{"timestamp": 1704067200000, "volatility": 0.02, "volume_24h": 1_500_000_000},
{"timestamp": 1704153600000, "volatility": 0.025, "volume_24h": 1_800_000_000},
]
result = optimize_strategy_with_ai(perf, market_conditions)
print(f"🔧 Paramètres optimisés:")
print(f" Lookback: {result['optimized_params']['lookback_period']}")
print(f" Seuil achat: {result['optimized_params']['threshold_buy']}")
print(f" Coût IA: ${result['cost_usd']:.4f}")
Analyse complète et visualisation des résultats
Une fois le backtest exécuté, il est crucial d'analyser les résultats en profondeur. Je recommande de toujours examiner la courbe d'equity, la distribution des trades, le drawdown maximum et les corrélations avec les conditions de marché.
import matplotlib.pyplot as plt
import seaborn as sns
from matplotlib.dates import date_ticker
import matplotlib.dates as mdates
class BacktestAnalyzer:
"""Analyse et visualisation des résultats de backtest"""
def __init__(self, result: BacktestResult, config: BacktestConfig):
self.result = result
self.config = config
self.df = result.equity_curve
def plot_equity_curve(self, save_path: str = None):
"""Trace la courbe d'equity avec drawdown"""
fig, axes = plt.subplots(3, 1, figsize=(14, 12), sharex=True)
# Convertir timestamps en dates
self.df['date'] = pd.to_datetime(self.df['timestamp'], unit='ms')
# 1. Equity curve
ax1 = axes[0]
ax1.plot(self.df['date'], self.df['total_equity'],
linewidth=1.5, color='#2E86AB', label='Equity')
ax1.fill_between(self.df['date'], self.df['total_equity'],
alpha=0.3, color='#2E86AB')
ax1.axhline(y=self.config.initial_capital, color='gray',
linestyle='--', alpha=0.7, label='Capital Initial')
ax1.set_ylabel('Capital (USDT)', fontsize=11)
ax1.set_title('Backtest Bybit Perpetual - Courbe d\'Equity', fontsize=14, fontweight='bold')
ax1.legend(loc='upper left')
ax1.grid(True, alpha=0.3)
# Ajouter les points de trade
trade_times = pd.to_datetime([t.timestamp for t in self.result.trades], unit='ms')
trade_prices = [t.filled_price for t in self.result.trades]
ax1.scatter(trade_times[::50], [self.df['total_equity'].iloc[0]]*len(trade_times[::50]),
marker='^', color='green', alpha=0.3, s=20, label='Signaux')
# 2. Drawdown
ax2 = axes[1]
cumulative = self.df['total_equity']
running_max = cumulative.expanding().max()
drawdown = (cumulative - running_max) / running_max * 100
ax2.fill_between(self.df['date'], drawdown, 0,
alpha=0.7, color='#E63946', label='Drawdown')
ax2.set_ylabel('Drawdown (%)', fontsize=11)
ax2.set_title('Drawdown Maximum', fontsize=12)
ax2.grid(True, alpha=0.3)
ax2.legend(loc='lower left')
# Marquer le max drawdown
max_dd_idx = drawdown.idxmin()
ax2.annotate(f'Max DD: {drawdown.min():.1f}%',
xy=(self.df['date'].iloc[max_dd_idx], drawdown.min()),
xytext=(10, -20), textcoords='offset points',
fontsize=10, color='red',
arrowprops=dict(arrowstyle='->', color='red'))
# 3. Volume traded
ax3 = axes[2]
trade_values = [t.quantity * t.filled_price for t in self.result.trades]
ax3.bar(range(len(trade_values)), trade_values, alpha=0.6, color='#457B9D')
ax3.set_ylabel('Valeur par Trade (USDT)', fontsize=11)
ax3.set_xlabel('Numéro du Trade', fontsize=11)
ax3.set_title('Distribution des Trades', fontsize=12)
ax3.grid(True, alpha=0.3, axis='y')
plt.tight_layout()
if save_path:
plt.savefig(save_path, dpi=150, bbox_inches='tight')
print(f"📊 Graphique sauvegardé: {save_path}")
plt.show()
def print_summary(self):
"""Affiche un résumé complet des métriques"""
m = self.result.metrics
print("=" * 60)
print(" RÉSUMÉ DU BACKTEST BYBIT PERPETUAL")
print("=" * 60)
print(f"\n📈 PERFORMANCE:")
print(f" Return Total: {m['total_return_pct']:>10.2f}%")
print(f" Sharpe Ratio: {m['sharpe_ratio']:>10.2f}")
print(f" Max Drawdown: {m['max_drawdown_pct']:>10.2f}%")
print(f" Capital Final: ${m['final_capital']:>10,.2f}")
print(f"\n📊 ACTIVITÉ:")
print(f" Total Trades: {m['total_trades']:>10}")
print(f" Win Rate: {m['win_rate_pct']:>10.2f}%")
print(f" Frais Totaux: ${m['total_fees']:>10.2f}")
print(f" Avg Valeur/Trade: ${m['avg_trade_value']:>10.2f}")
print(f"\n⚙️ CONFIGURATION:")
print(f" Capital Initial: ${self.config.initial_capital:>10,.2f}")
print(f" Frais Maker: {self.config.maker_fee*100:>10.3f}%")
print(f" Frais Taker: {self.config.taker_fee*100:>10.3f}%")
print(f" Slippage: {self.config.slippage_bps:>10.1f} bps")
print("=" * 60)
Exemple d'utilisation
if __name__ == "__main__":
# Simuler des données de backtest
dates = pd.date_range('2026-01-01', periods=1000, freq='1H')
equity = 100000 * (1 + np.cumsum(np.random.randn(1000) * 0.01))
class MockResult:
def __init__(self):
self.equity_curve = pd.DataFrame({
'timestamp': dates.astype(np.int64) // 10**6,
'capital': equity * 0.7,
'position_value': equity * 0.3,
'total_equity': equity,
'trades_count': range(1000)
})
self.trades = []
self.metrics = {
'total_return_pct': 23.5,
'sharpe_ratio': 1.85,
'max_drawdown_pct': -12.3,
'win_rate_pct': 61.2,
'total_fees': 487.50,
'final_capital': 123500,