Dans l'écosystème des cryptomonnaies, les données de marché en haute résolution constituent le socle de toute stratégie de trading algorithmique sophistiquée. Que vous soyez un trader quantitatif cherchant à identifier des opportunités de futures basis (écart entre prix futures et prix spot) ou un chercheur en finance quantitative explorant les facteurs de risque sur dérivatives, l'accès à des ticks historiques précis représente un avantage compétitif déterminant.

Cet article présente un tutoriel pratique complet pour intégrer l'API Tardis via la plateforme HolySheep AI, avec des exemples de code exécutables en Python pour récupérer des données de marché historiques sur les contrats futures BTC/USDT, calculer le basis en temps réel et construire des facteurs d'arbitrage spot-futures.

Comparatif des Coûts API IA pour l'Analyse Quantitative en 2026

Avant de détailler l'intégration technique, il est essentiel de comprendre l'écosystème tarifaire des principaux fournisseurs d'API IA en 2026. Voici une comparaison détaillée des coûts de sortie (output) pour les modèles les plus pertinents en analyse financière :

Modèle IA Prix Output ($/MTok) Latence Typique Cas d'Usage Principal Coût Mensuel (10M tokens)
DeepSeek V3.2 0,42 $ ~35ms Analyse de données massives, backtesting 4,20 $
Gemini 2.5 Flash 2,50 $ ~45ms Inférence rapide, génération de rapports 25,00 $
GPT-4.1 8,00 $ ~60ms Analyse complexe, raisonnement financier 80,00 $
Claude Sonnet 4.5 15,00 $ ~55ms Rédaction institutionnelle, compliance 150,00 $

Source : Tarifs publics vérifiés au 10 mai 2026. Les prix sont en dollars américains.

Analyse du ROI pour une Stratégie d'Arbitrage

Pour un hedge fund ou un trader algorithmique traitant 10 millions de tokens par mois en analyse de données de marché :

Pourquoi Intégrer Tardis via HolySheep ?

Tardis est une référence dans la collecte de données de marché cryptomonnaies, proposant des flux de données historiques en ticks pour plus de 200 exchanges. HolySheep.aggrave plusieurs avantages stratégiques pour accéder à ces données :

Architecture de l'Intégration Tardis + HolySheep

La solution proposée repose sur une architecture en trois couches :

  1. Couche données : Récupération des ticks historiques depuis l'API Tardis
  2. Couche traitement : Calcul des facteurs basis et détection des opportunités d'arbitrage via scripts Python
  3. Couche IA : Utilisation de DeepSeek V3.2 via HolySheep pour analyser les patterns et générer des signaux de trading

Prérequis et Installation

# Installation des dépendances Python
pip install requests pandas numpy python-dotenv

Vérification de la version Python (minimum 3.8)

python --version

Python 3.10.14

Structure du projet

project/ ├── config.py ├── tardis_client.py ├── basis_analyzer.py ├── holy_sheep_client.py └── main.py

Configuration de l'API HolySheep

# config.py
import os
from dotenv import load_dotenv

load_dotenv()

============================================

CONFIGURATION HOLYSHEEP AI

============================================

IMPORTANT : Base URL HolySheep (NE PAS utiliser api.openai.com)

HOLYSHEEP_BASE_URL = "https://api.holysheep.ai/v1"

Votre clé API HolySheep (obtenue après inscription)

HOLYSHEEP_API_KEY = os.getenv("HOLYSHEEP_API_KEY", "YOUR_HOLYSHEEP_API_KEY")

Modèles disponibles avec prix 2026

MODELS_CONFIG = { "deepseek_v32": { "name": "DeepSeek V3.2", "input_cost": 0.28, # $/MTok input "output_cost": 0.42, # $/MTok output "latency_ms": 35, "use_case": "Analyse de données, backtesting intensif" }, "gpt41": { "name": "GPT-4.1", "input_cost": 3.00, "output_cost": 8.00, "latency_ms": 60, "use_case": "Raisonnement financier complexe" }, "claude_sonnet_45": { "name": "Claude Sonnet 4.5", "input_cost": 4.00, "output_cost": 15.00, "latency_ms": 55, "use_case": "Compliance, rapports institutionnels" }, "gemini_25_flash": { "name": "Gemini 2.5 Flash", "input_cost": 0.80, "output_cost": 2.50, "latency_ms": 45, "use_case": "Inférence rapide, prototypes" } }

============================================

CONFIGURATION TARDIS (exemple Binance Futures)

============================================

TARDIS_CONFIG = { "exchange": "binance-futures", "symbol": "BTCUSDT", "channels": ["trades", "bookTicker"] }

Configuration taux de change pour calcul des coûts en CNY

FX_RATE_USD_CNY = 7.20 # Taux indicatif 2026

Client Python pour Appels API HolySheep

# holy_sheep_client.py
import requests
import json
import time
from typing import Optional, Dict, Any
from config import HOLYSHEEP_BASE_URL, HOLYSHEEP_API_KEY, MODELS_CONFIG

class HolySheepClient:
    """
    Client pour interagir avec l'API HolySheep AI.
    Compatible avec les modèles OpenAI-style (GPT, Claude-like).
    """
    
    def __init__(self, api_key: str = HOLYSHEEP_API_KEY):
        self.api_key = api_key
        self.base_url = HOLYSHEEP_BASE_URL
        self.headers = {
            "Authorization": f"Bearer {self.api_key}",
            "Content-Type": "application/json"
        }
    
    def chat_completion(
        self,
        model: str = "deepseek-v3.2",
        messages: list,
        temperature: float = 0.7,
        max_tokens: int = 2000,
        timeout: int = 30
    ) -> Dict[str, Any]:
        """
        Effectue un appel de chat completion vers HolySheep.
        
        Args:
            model: Identifiant du modèle (deepseek-v3.2, gpt-4.1, etc.)
            messages: Liste des messages au format [{"role": "user", "content": "..."}]
            temperature: Créativité du modèle (0-2)
            max_tokens: Limite de tokens de sortie
            timeout: Timeout en secondes
            
        Returns:
            Dict contenant la réponse et les métadonnées
            
        Raises:
            Exception: Si l'appel API échoue
        """
        endpoint = f"{self.base_url}/chat/completions"
        
        payload = {
            "model": model,
            "messages": messages,
            "temperature": temperature,
            "max_tokens": max_tokens
        }
        
        start_time = time.time()
        
        try:
            response = requests.post(
                endpoint,
                headers=self.headers,
                json=payload,
                timeout=timeout
            )
            response.raise_for_status()
            
            elapsed_ms = (time.time() - start_time) * 1000
            result = response.json()
            
            # Extraction des métadonnées de facturation
            usage = result.get("usage", {})
            prompt_tokens = usage.get("prompt_tokens", 0)
            completion_tokens = usage.get("completion_tokens", 0)
            
            return {
                "success": True,
                "content": result["choices"][0]["message"]["content"],
                "model": result.get("model"),
                "latency_ms": round(elapsed_ms, 2),
                "usage": {
                    "prompt_tokens": prompt_tokens,
                    "completion_tokens": completion_tokens,
                    "total_tokens": usage.get("total_tokens", 0)
                },
                "finish_reason": result["choices"][0].get("finish_reason")
            }
            
        except requests.exceptions.Timeout:
            raise Exception(f"Timeout après {timeout}s — Vérifiez votre connexion")
        except requests.exceptions.RequestException as e:
            raise Exception(f"Erreur API HolySheep: {str(e)}")
    
    def calculer_cout(
        self,
        model_id: str,
        prompt_tokens: int,
        completion_tokens: int
    ) -> Dict[str, float]:
        """
        Calcule le coût exact d'un appel API.
        
        Args:
            model_id: Identifiant du modèle
            prompt_tokens: Tokens d'entrée
            completion_tokens: Tokens de sortie
            
        Returns:
            Dict avec les coûts en USD et CNY
        """
        model_config = MODELS_CONFIG.get(model_id)
        if not model_config:
            raise ValueError(f"Modèle inconnu: {model_id}")
        
        cout_usd = (
            (prompt_tokens / 1_000_000) * model_config["input_cost"] +
            (completion_tokens / 1_000_000) * model_config["output_cost"]
        )
        
        return {
            "usd": round(cout_usd, 4),
            "cny": round(cout_usd * 7.20, 2)  # Taux 2026
        }

============================================

FONCTION D'ANALYSE DE BASIS AVEC IA

============================================

def analyser_basis_avec_ia( spot_price: float, futures_price: float, maturity_days: int, risk_free_rate: float = 0.05 ) -> Dict[str, Any]: """ Analyse le basis futures/spot et génère des insights via DeepSeek V3.2. Args: spot_price: Prix spot actuel futures_price: Prix contrat futures maturity_days: Jours jusqu'à expiration risk_free_rate: Taux sans risque annualisé Returns: Analyse structurée avec verdict d'arbitrage """ client = HolySheepClient() # Calcul du basis basis_absolu = futures_price - spot_price basis_pourcentage = (basis_absolu / spot_price) * 100 # Basis annualisé théorique (cost of carry) basis_annualise = basis_pourcentage * (365 / maturity_days) # Théorique fair value basis fair_basis = spot_price * (1 + risk_free_rate * maturity_days / 365) - spot_price # Construction du prompt prompt = f"""Analyse financière quantitative: Prix Spot BTC/USDT: ${spot_price:,.2f} Prix Futures BTC-USDT (expire dans {maturity_days}j): ${futures_price:,.2f} Basis actuel: {basis_absolu:+,.2f} USD ({basis_pourcentage:+.4f}%) Basis annualisé: {basis_annualise:+.2f}% (vs taux sans risque {risk_free_rate*100:.1f}%) Questions: 1. Le basis est-il favorable pour une stratégie d'arbitrage spot-futures? 2. Quel est le risque de base (basis risk)? 3. Recommandation: LONG spot + SHORT futures OU LONG futures + SHORT spot? Répondez en JSON structuré avec les clés: verdict, confiance (0-100), risque, recommandation_action.""" messages = [ {"role": "system", "content": "Vous êtes un analyste quantitatif expert en cryptomonnaies."}, {"role": "user", "content": prompt} ] # Appel API avec DeepSeek V3.2 (0,42 $/MTok output - le plus économique) result = client.chat_completion( model="deepseek-v3.2", messages=messages, temperature=0.3, max_tokens=1500 ) # Calcul du coût cout = client.calculer_cout( "deepseek_v32", result["usage"]["prompt_tokens"], result["usage"]["completion_tokens"] ) return { "basis_absolu": basis_absolu, "basis_pourcentage": basis_pourcentage, "basis_annualise": basis_annualise, "analyse_ia": result["content"], "latence_ms": result["latency_ms"], "cout_appel_usd": cout["usd"], "cout_appel_cny": cout["cny"] }

Exemple d'utilisation

if __name__ == "__main__": client = HolySheepClient() # Test de connexion test_messages = [ {"role": "user", "content": "Dites 'Connexion HolySheep réussie' et indiquez le modèle utilisé."} ] try: result = client.chat_completion( model="deepseek-v3.2", messages=test_messages ) print(f"✅ {result['content']}") print(f" Latence: {result['latency_ms']}ms") print(f" Tokens: {result['usage']}") except Exception as e: print(f"❌ Erreur: {e}")

Récupération des Données Ticks Historiques depuis Tardis

# tardis_client.py
import requests
import pandas as pd
from datetime import datetime, timedelta
from typing import List, Dict, Optional
import time

class TardisClient:
    """
    Client pour l'API Tardis (données de marché cryptomonnaies).
    Documentation: https://docs.tardis.dev/
    """
    
    def __init__(self, api_token: str):
        self.api_token = api_token
        self.base_url = "https://api.tardis.dev/v1"
        self.headers = {
            "Authorization": f"Bearer {api_token}"
        }
    
    def get_symbols(self, exchange: str) -> List[Dict]:
        """
        Liste tous les symboles disponibles pour un exchange.
        """
        url = f"{self.base_url}/exchanges/{exchange}/symbols"
        response = requests.get(url, headers=self.headers)
        response.raise_for_status()
        return response.json()
    
    def fetch_trades(
        self,
        exchange: str,
        symbol: str,
        from_time: datetime,
        to_time: datetime,
        limit: int = 1000
    ) -> pd.DataFrame:
        """
        Récupère les trades historiques pour un symbole.
        
        Args:
            exchange: Nom de l'exchange (ex: 'binance-futures')
            symbol: Symbole de trading (ex: 'BTCUSDT')
            from_time: Date de début
            to_time: Date de fin
            limit: Nombre maximum de records par requête
            
        Returns:
            DataFrame pandas avec les trades
        """
        url = f"{self.base_url}/historical/trades"
        
        params = {
            "exchange": exchange,
            "symbol": symbol,
            "from": int(from_time.timestamp() * 1000),
            "to": int(to_time.timestamp() * 1000),
            "limit": limit
        }
        
        all_trades = []
        has_more = True
        
        while has_more:
            response = requests.get(
                url,
                headers=self.headers,
                params=params
            )
            response.raise_for_status()
            data = response.json()
            
            all_trades.extend(data.get("data", []))
            
            # Pagination
            has_more = data.get("hasMore", False)
            if has_more and data.get("nextCursor"):
                params["cursor"] = data["nextCursor"]
            
            # Rate limiting
            time.sleep(0.1)
        
        if not all_trades:
            return pd.DataFrame()
        
        # Conversion en DataFrame
        df = pd.DataFrame(all_trades)
        df["timestamp"] = pd.to_datetime(df["timestamp"], unit="ms")
        df = df.sort_values("timestamp")
        
        return df
    
    def fetch_book_ticker(
        self,
        exchange: str,
        symbol: str,
        from_time: datetime,
        to_time: datetime
    ) -> pd.DataFrame:
        """
        Récupère les données bookTicker (meilleur bid/ask) historiques.
        """
        url = f"{self.base_url}/historical/book-ticker"
        
        params = {
            "exchange": exchange,
            "symbol": symbol,
            "from": int(from_time.timestamp() * 1000),
            "to": int(to_time.timestamp() * 1000)
        }
        
        response = requests.get(
            url,
            headers=self.headers,
            params=params
        )
        response.raise_for_status()
        data = response.json()
        
        if not data.get("data"):
            return pd.DataFrame()
        
        df = pd.DataFrame(data["data"])
        df["timestamp"] = pd.to_datetime(df["timestamp"], unit="ms")
        df = df.sort_values("timestamp")
        
        return df

def calculer_basis_series(
    df_spot: pd.DataFrame,
    df_futures: pd.DataFrame,
    maturity_days: int = 7
) -> pd.DataFrame:
    """
    Calcule la série temporelle du basis spot-futures.
    
    Args:
        df_spot: DataFrame avec colonnes [timestamp, price]
        df_futures: DataFrame avec colonnes [timestamp, price]
        maturity_days: Jours jusqu'à expiration du contrat
        
    Returns:
        DataFrame avec basis calculé
    """
    # Merge sur timestamp le plus proche
    df_spot = df_spot.set_index("timestamp")
    df_futures = df_futures.set_index("timestamp")
    
    # Rééchantillonnage à la minute
    df_spot_resampled = df_spot.resample("1min").last().ffill()
    df_futures_resampled = df_futures.resample("1min").last().ffill()
    
    # Alignement
    combined = pd.DataFrame({
        "spot": df_spot_resampled["price"],
        "futures": df_futures_resampled["price"]
    }).dropna()
    
    # Calcul du basis
    combined["basis_absolu"] = combined["futures"] - combined["spot"]
    combined["basis_pct"] = (combined["basis_absolu"] / combined["spot"]) * 100
    combined["basis_annualise"] = combined["basis_pct"] * (365 / maturity_days)
    
    # Statistiques mobiles
    combined["basis_mean_1h"] = combined["basis_pct"].rolling("1h").mean()
    combined["basis_std_1h"] = combined["basis_pct"].rolling("1h").std()
    
    # Z-score du basis
    combined["basis_zscore"] = (
        (combined["basis_pct"] - combined["basis_mean_1h"]) / 
        combined["basis_std_1h"]
    )
    
    return combined.reset_index()

============================================

EXEMPLE D'UTILISATION

============================================

if __name__ == "__main__": # Initialisation (remplacer par votre token) TARDIS_API_TOKEN = "your_tardis_token_here" client = TardisClient(TARDIS_API_TOKEN) # Récupération des données pour BTC end_time = datetime.now() start_time = end_time - timedelta(hours=24) print("📥 Téléchargement des trades Spot (BTC/USDT Binance)...") df_spot = client.fetch_trades( exchange="binance", symbol="BTCUSDT", from_time=start_time, to_time=end_time ) print(f" → {len(df_spot)} trades récupérés") print("📥 Téléchargement des trades Futures (BTCUSDT Binance Futures)...") df_futures = client.fetch_trades( exchange="binance-futures", symbol="BTCUSDT", from_time=start_time, to_time=end_time ) print(f" → {len(df_futures)} trades récupérés") # Calcul du basis df_basis = calculer_basis_series(df_spot, df_futures, maturity_days=7) print("\n📊 Statistiques du Basis (BTCUSDT Spot vs Futures):") print(f" Basis moyen: {df_basis['basis_pct'].mean():.4f}%") print(f" Basis max: {df_basis['basis_pct'].max():.4f}%") print(f" Basis min: {df_basis['basis_pct'].min():.4f}%") print(f" Z-score moyen: {df_basis['basis_zscore'].mean():.2f}")

Application Pratique : Détection d'Opportunités d'Arbitrage

# basis_analyzer.py
import pandas as pd
import numpy as np
from datetime import datetime, timedelta
from typing import Dict, List, Tuple
import json

class ArbitrageDetector:
    """
    Détecte les opportunités d'arbitrage spot-futures basées sur le basis.
    
    Stratégie:
    - Si basis > theoretical_basis + threshold: 
      SHORT futures + LONG spot (locking in premium)
    - Si basis < theoretical_basis - threshold:
      LONG futures + SHORT spot (buying discount)
    """
    
    def __init__(
        self,
        transaction_cost_bps: float = 5.0,  # Coût en basis points
        min_basis_zscore: float = 2.0,       # Z-score minimum pour signal
        funding_rate_estimate: float = 0.0001  # Taux de funding quotidien
    ):
        self.transaction_cost = transaction_cost_bps / 10000  # Convertir en décimal
        self.min_zscore = min_basis_zscore
        self.funding_rate = funding_rate_estimate
    
    def detect_signals(
        self,
        df_basis: pd.DataFrame,
        theoretical_basis_pct: float = 0.0
    ) -> pd.DataFrame:
        """
        Génère les signaux d'arbitrage.
        
        Returns:
            DataFrame avec colonne 'signal': 
            1 = LONG spot / SHORT futures
           -1 = SHORT spot / LONG futures
            0 = Neutre
        """
        df = df_basis.copy()
        
        # Écart par rapport au basis théorique
        df["basis_deviation"] = df["basis_pct"] - theoretical_basis_pct
        
        # Z-score de la déviation
        df["deviation_zscore"] = (
            df["basis_deviation"] - df["basis_deviation"].rolling(24).mean()
        ) / df["basis_deviation"].rolling(24).std()
        
        # Génération des signaux
        conditions = [
            df["deviation_zscore"] > self.min_zscore,
            df["deviation_zscore"] < -self.min_zscore
        ]
        choices = [1, -1]  # LONG spot / SHORT futures = 1
        
        df["signal"] = np.select(conditions, choices, default=0)
        
        # Filtrer les signaux avec risque acceptable
        df["signal_filtered"] = np.where(
            df["basis_zscore"].abs() < 4.0,  # Éviter les cas extrêmes
            df["signal"],
            0
        )
        
        return df
    
    def calculate_pnl(
        self,
        df_signals: pd.DataFrame,
        entry_basis: float,
        exit_basis: float,
        position_size_usd: float = 10000
    ) -> Dict[str, float]:
        """
        Calcule le P&L estimé d'une stratégie d'arbitrage.
        """
        basis_change = exit_basis - entry_basis
        
        # Pour un arbitrage basis:
        # - Position LONG spot / SHORT futures gagne quand le basis diminue
        # - Position SHORT spot / LONG futures gagne quand le basis augmente
        
        pnl_per_unit = -basis_change  # Négatif car positions opposées
        
        # Ajustement pour les coûts de transaction
        gross_pnl = pnl_per_unit * position_size_usd / 100
        net_pnl = gross_pnl - (2 * self.transaction_cost * position_size_usd)
        
        return {
            "gross_pnl_usd": round(gross_pnl, 2),
            "net_pnl_usd": round(net_pnl, 2),
            "net_roi_pct": round((net_pnl / position_size_usd) * 100, 4),
            "basis_change_bps": round(basis_change * 10000, 2)
        }

def generer_rapport_analyse(
    df_basis: pd.DataFrame,
    spot_symbol: str = "BTCUSDT",
    futures_symbol: str = "BTCUSDT",
    maturity_days: int = 7
) -> str:
    """
    Génère un rapport d'analyse du basis pour consumption par l'IA.
    """
    stats = {
        "periode_analyse": {
            "debut": str(df_basis["timestamp"].min()),
            "fin": str(df_basis["timestamp"].max()),
            "nb_observations": len(df_basis)
        },
        "spot": {
            "symbole": spot_symbol,
            "prix_moyen": float(df_basis["spot"].mean()),
            "prix_min": float(df_basis["spot"].min()),
            "prix_max": float(df_basis["spot"].max()),
            "volatilite": float(df_basis["spot"].pct_change().std() * 100)
        },
        "futures": {
            "symbole": futures_symbol,
            "expiration_jours": maturity_days,
            "prix_moyen": float(df_basis["futures"].mean()),
            "basis_moyen_pct": float(df_basis["basis_pct"].mean()),
            "basis_max_pct": float(df_basis["basis_pct"].max()),
            "basis_min_pct": float(df_basis["basis_pct"].min())
        },
        "basis": {
            "annualise_moyen_pct": float(df_basis["basis_annualise"].mean()),
            "zscore_moyen": float(df_basis["basis_zscore"].mean()),
            "zscore_max": float(df_basis["basis_zscore"].abs().max()),
            "opportunites_count": int((df_basis["basis_zscore"].abs() > 2.0).sum())
        }
    }
    
    return json.dumps(stats, indent=2, default=str)

Exemple d'utilisation

if __name__ == "__main__": # Simulation avec données de test dates = pd.date_range(start="2026-05-01", end="2026-05-10", freq="1h") np.random.seed(42) # Prix spot simulé spot_prices = 62500 + np.cumsum(np.random.randn(len(dates)) * 50) # Prix futures avec basis variable basis_simulation = np.sin(np.arange(len(dates)) * 0.1) * 0.1 + 0.05 futures_prices = spot_prices * (1 + basis_simulation / 100) df_test = pd.DataFrame({ "timestamp": dates, "spot": spot_prices, "futures": futures_prices, "basis_pct": basis_simulation, "basis_annualise": basis_simulation * (365 / 7) }) df_test["basis_mean_1h"] = df_test["basis_pct"].rolling(1).mean() df_test["basis_std_1h"] = df_test["basis_pct"].rolling(1).std() df_test["basis_zscore"] = 0.0 # Détection d'opportunités detector = ArbitrageDetector( transaction_cost_bps=5.0, min_basis_zscore=1.5 ) df_signals = detector.detect_signals(df_test) nb_signaux = (df_signals["signal_filtered"] != 0).sum() print(f"🎯 Opportunités détectées: {nb_signaux} sur {len(df_test)} périodes") # Génération du rapport rapport = generer_rapport_analyse(df_signals) print("\n📋 Rapport JSON généré:") print(rapport)

Pipeline Intégré : Tardis → HolySheep → Signaux de Trading

# main.py - Pipeline complet
import pandas as pd
from datetime import datetime, timedelta
from tardis_client import TardisClient, calculer_basis_series
from basis_analyzer import ArbitrageDetector, generer_rapport_analyse
from holy_sheep_client import HolySheepClient

def run_arbitrage_analysis(
    tardis_token: str,
    holy_sheep_key: str,
    symbol: str = "BTCUSDT",
    lookback_hours: int = 24,
    maturity_days: int = 7
):
    """
    Pipeline complet d'analyse d'arbitrage spot-futures.
    
    Étapes:
    1. Récupération des données Tardis
    2. Calcul du basis
    3. Détection d'opportunités
    4. Analyse IA via HolySheep (DeepSeek V3.2)
    5. Génération de recommandations
    """
    
    print("=" * 60)
    print("🔍 PIPELINE ANALYSE ARBITRAGE SPOT-FUTURES")
    print("=" * 60)
    
    # ============================================
    # ÉTAPE 1: RÉCUPÉRATION DES DONNÉES TARDIS
    # ============================================
    print("\n📡 Étape 1/5: Connexion à Tardis...")
    tardis = TardisClient(tardis_token)
    
    end_time = datetime.now()
    start_time = end_time - timedelta(hours=lookback_hours)
    
    # Téléchargement parallèle (spot + futures)
    print(f"   Téléchargement Spot ({symbol})...")
    df_spot = tardis.fetch_trades(
        exchange="binance",
        symbol=symbol,
        from_time=start_time,
        to_time=end_time
    )
    print(f"   → {len(df_spot)} trades")
    
    print(f"   Téléchargement Futures ({symbol}-PERP)...")
    df_futures = tardis.fetch_trades(
        exchange="binance-futures",
        symbol=f"{symbol}_PERP",
        from_time=start_time,
        to_time=end_time
    )
    print(f"   → {len(df_futures)} trades")
    
    # ============================================
    # ÉTAPE 2: CALCUL DU BASIS
    # ============================================
    print("\n📊 Étape 2/5: Calcul du basis Spot-Futures...")
    df_basis = calculer_basis_series(df_spot, df_futures, maturity_days)
    print(f"   → {len(df_basis)} points de données")
    print(f"   Basis moyen: {df_basis['basis_pct'].mean():.4f}%")
    print(f"   Basis annualisé moyen: {df_basis['basis_annualise'].mean():.2f}%")
    
    # ============================================
    # ÉTAPE 3: DÉTECTION D'OPPORTUNITÉS
    # ============================================
    print("\n🎯 Étape 3/5: Détection d'opportunités...")
    detector = ArbitrageDetector(
        transaction_cost_bps=5.0,
        min_basis_zscore=2.0
    )
    df_signals = detector.detect_signals(df_basis)
    
    signaux_longs