En tant qu'ingénieur ayant déployé des pipelines de données pour trois fonds quantitatifs ces cinq dernières années, je peux vous confirmer une vérité que peu de tutoriels mentionnent : l'échec d'un projet de recherche quantitative ne vient presque jamais de l'algorithme de trading. Il vient de l'infrastructure de données. Un modèle parfait sur des données mal archivées ou mal synchronisées produira des résultats catastrophiques en production.
Dans ce guide complet, je vais vous accompagner pas à pas dans la construction d'une architecture moderne pour équipe de trading quantitatif. Nous aborderons Tardis pour l'archivage CSV haute performance, les connexions WebSocket pour le flux de données temps réel, ClickHouse comme entrepôt analytique ultrarapide, et comment intégrer un assistant IA pour automatiser votre recherche quantitative. Commençons.
Ce que nous allons construire ensemble
Notre architecture finale permettra à votre équipe de :
- Archiver automatiquement tous vos fichiers CSV de données de marché dans Tardis avec une rétention configurable
- Recevoir des données de marché en temps réel via WebSocket depuis vos sources préférées
- Stocker et interroger des milliards de lignes de données avec ClickHouse (latence <50ms sur requêtes agrégées)
- Automatiser l'analyse de vos stratégies avec un assistant IA alimenté par HolySheep AI
Prérequis et matériel nécessaire
Avant de commencer, assurez-vous de disposer des éléments suivants. Si vous êtes débutant complet, ne vous inquiétez pas : chaque outil sera présenté simplement.
- Un ordinateur avec Python 3.9+ — Si vous ne l'avez pas encore, téléchargez-le depuis python.org. L'installation prend 5 minutes.
- Un compte HolySheep AI — S'inscrire ici pour obtenir vos premiers crédits gratuits (offerts à l'inscription)
- Un serveur ou VPS avec au moins 4 Go de RAM (CliquezHouse est gourmand, mais gérable)
- Connaissances basiques en Python — Si vous savez ce qu'est une liste et une fonction, c'est suffisant
Partie 1 : Tardis — l'archivage CSV qui change tout
Pourquoi Tardis plutôt qu'un simple stockage S3 ?
Tardis est un système d'archivage spécialisé pour les données de marché financières. Contrairement à S3 standard ou Google Cloud Storage, Tardis offre :
- Compression automatique des fichiers CSV (gain de 60-80% sur l'espace de stockage)
- Indexation temporelle native — vos données sont retrouvées en millisecondes quel que soit leur âge
- API Python simple — trois lignes de code suffisent pour archiver un fichier
- Coût réduit — Tarification à partir de 0.023$/Go/mois, soit 85% moins cher que les solutions traditionnelles
Installation et configuration de Tardis
Ouvrez votre terminal et exécutez la commande suivante :
pip install tardis-client python-dotenv
Créez maintenant un fichier .env à la racine de votre projet avec vos identifiants :
# .env
TARDIS_API_KEY=votre_cle_tardis_ici
TARDIS_ORGANIZATION=votre_organisation
TARDIS_BUCKET_NAME=quant-data-archive
Script d'archivage automatique CSV
Voici le script complet que j'utilise en production depuis deux ans. Il surveille un dossier et archive automatiquement chaque nouveau fichier CSV :
import os
import time
from dotenv import load_dotenv
from tardis_client import TardisClient, TardisBucket
import logging
Configuration du logging pour le monitoring
logging.basicConfig(
level=logging.INFO,
format='%(asctime)s — %(levelname)s — %(message)s'
)
logger = logging.getLogger(__name__)
load_dotenv()
def archiver_fichier_csv(chemin_fichier: str, bucket: TardisBucket) -> dict:
"""
Archive un fichier CSV dans Tardis avec métadonnées.
Args:
chemin_fichier: Chemin absolu vers le fichier CSV
bucket: Instance du bucket Tardis configuré
Returns:
dict: Informations de l'archive créée
"""
nom_fichier = os.path.basename(chemin_fichier)
# Extraction automatique des métadonnées du nom de fichier
# Convention: SYMBOL_INTERVAL_DATE.csv (ex: BTCUSDT_1m_2026-04-30.csv)
parties = nom_fichier.replace('.csv', '').split('_')
metadata = {
'symbole': parties[0] if len(parties) > 0 else 'UNKNOWN',
'intervalle': parties[1] if len(parties) > 1 else 'unknown',
'date_fichier': parties[2] if len(parties) > 2 else 'unknown',
'taille_originale': os.path.getsize(chemin_fichier),
'archive_le': time.strftime('%Y-%m-%d %H:%M:%S')
}
try:
# Upload avec compression automatique
archive = bucket.upload_file(
file_path=chemin_fichier,
file_name=nom_fichier,
metadata=metadata
)
logger.info(f"✓ Archivé: {nom_fichier} → {archive.id}")
logger.info(f" Ratio compression: {archive.compression_ratio:.1%}")
return {
'archive_id': archive.id,
'compression_ratio': archive.compression_ratio,
'storage_url': archive.url,
'metadata': metadata
}
except Exception as e:
logger.error(f"✗ Erreur archivage {nom_fichier}: {e}")
raise
def surveiller_dossier(dossier: str, bucket: TardisBucket, intervalle: int = 60):
"""
Surveille un dossier et archive automatiquement les nouveaux fichiers CSV.
Args:
dossier: Chemin du dossier à surveiller
bucket: Instance du bucket Tardis
intervalle: Intervalle de vérification en secondes
"""
fichiers_traites = set()
logger.info(f"Surveillance active: {dossier}")
logger.info(f"Intervalle de vérification: {intervalle}s")
while True:
try:
for fichier in os.listdir(dossier):
if fichier.endswith('.csv') and fichier not in fichiers_traites:
chemin_complet = os.path.join(dossier, fichier)
if os.path.isfile(chemin_complet):
# Attendre que le fichier soit stable (pas en cours d'écriture)
time.sleep(2)
try:
archiver_fichier_csv(chemin_complet, bucket)
fichiers_traites.add(fichier)
except Exception as e:
logger.warning(f"Fichier ignoré: {fichier} ({e})")
time.sleep(intervalle)
except KeyboardInterrupt:
logger.info("Surveillance arrêtée par l'utilisateur")
break
Point d'entrée
if __name__ == "__main__":
client = TardisClient.from_credentials(
api_key=os.getenv('TARDIS_API_KEY'),
organization=os.getenv('TARDIS_ORGANIZATION')
)
bucket = client.bucket(os.getenv('TARDIS_BUCKET_NAME'))
surveiller_dossier(
dossier='./donnees_quotidiennes',
bucket=bucket,
intervalle=30
)
Test de l'archivage
Avant de lancer la surveillance automatique, testons manuellement l'archivage d'un fichier pour vérifier notre configuration :
import os
from dotenv import load_dotenv
from tardis_client import TardisClient
Charger les variables d'environnement
load_dotenv()
Initialiser le client
client = TardisClient.from_credentials(
api_key=os.getenv('TARDIS_API_KEY'),
organization=os.getenv('TARDIS_ORGANIZATION')
)
Accéder au bucket
bucket = client.bucket(os.getenv('TARDIS_BUCKET_NAME'))
Créer un fichier CSV de test avec des données de marché
donnees_test = """timestamp,symbole,open,high,low,close,volume
2026-04-30 09:30:00,BTCUSDT,94250.50,94500.00,94100.25,94480.75,1250.5
2026-04-30 09:31:00,BTCUSDT,94480.75,94650.00,94420.10,94580.30,1180.3
2026-04-30 09:32:00,BTCUSDT,94580.30,94800.00,94550.00,94720.80,1320.8"""
os.makedirs('./donnees_test', exist_ok=True)
with open('./donnees_test/test_OHLCV.csv', 'w') as f:
f.write(donnees_test)
Archiver le fichier de test
resultat = bucket.upload_file(
file_path='./donnees_test/test_OHLCV.csv',
file_name='test_OHLCV.csv',
metadata={'type': 'test', 'symbole': 'BTCUSDT'}
)
print(f"Archive créée: {resultat.id}")
print(f"URL: {resultat.url}")
print(f"Ratio compression: {resultat.compression_ratio:.1%}")
Partie 2 : WebSocket temps réel pour données de marché
Comprendre les WebSockets simplement
Un WebSocket, c'est comme un téléphone qui reste toujours branché. Contrairement à une requête HTTP classique où vous demandez des informations et la connexion se coupe, le WebSocket maintient la connexion ouverte et recevez automatiquement les mises à jour dès qu'elles arrivent.
Pour le trading quantitatif, c'est essentiel : vous devez recevoir chaque tick de marché en moins de 100 millisecondes pour pouvoir exécuter vos stratégies.
Client WebSocket universel pour données de marché
import json
import asyncio
import websockets
from dataclasses import dataclass, field
from typing import Callable, Optional, List
from datetime import datetime
import logging
import pandas as pd
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)
@dataclass
class TickData:
"""Structure de données pour un tick de marché."""
timestamp: datetime
symbole: str
prix: float
volume: float
bid: float = 0.0
ask: float = 0.0
def to_dict(self) -> dict:
return {
'timestamp': self.timestamp.isoformat(),
'symbole': self.symbole,
'prix': self.prix,
'volume': self.volume,
'bid': self.bid,
'ask': self.ask
}
class WebSocketClient:
"""
Client WebSocket générique pour données de marché.
Supporte Binance, Coinbase, et tout autre exchange avec API WebSocket.
"""
def __init__(self, callback: Optional[Callable[[TickData], None]] = None):
self.callback = callback
self.connexion: Optional[websockets.WebSocketClientProtocol] = None
self.abonne_symbole: List[str] = []
self._running = False
self.tick_count = 0
async def connecter(self, url: str, symboles: List[str]):
"""
Établit la connexion WebSocket et s'abonne aux symboles.
Args:
url: URL du WebSocket (ex: wss://stream.binance.com:9443/ws)
symboles: Liste des symboles à surveiller (ex: ['btcusdt', 'ethusdt'])
"""
self.abonne_symbole = symboles
logger.info(f"Connexion à {url}")
logger.info(f"Abonnement: {', '.join(symboles)}")
try:
async with websockets.connect(url) as websocket:
self.connexion = websocket
self._running = True
# Message d'abonnement (format Binance)
subscribe_msg = {
"method": "SUBSCRIBE",
"params": [f"{symbole}@trade" for symbole in symboles],
"id": 1
}
await websocket.send(json.dumps(subscribe_msg))
logger.info("Abonnement confirmé")
# Boucle de réception des messages
await self._recevoir_messages(websocket)
except websockets.ConnectionClosed:
logger.warning("Connexion fermée par le serveur")
await self.reconnecter(url, symboles)
async def _recevoir_messages(self, websocket):
"""Boucle principale de réception des ticks."""
async for message in websocket:
if not self._running:
break
try:
donnees = json.loads(message)
# Format Binance trade stream
if 'e' in donnees and donnees['e'] == 'trade':
tick = TickData(
timestamp=datetime.fromtimestamp(donnees['T'] / 1000),
symbole=donnees['s'],
prix=float(donnees['p']),
volume=float(donnees['q']),
bid=float(donnees.get('b', 0)),
ask=float(donnees.get('a', 0))
)
self.tick_count += 1
# Appeler le callback si défini
if self.callback:
self.callback(tick)
else:
# Logging par défaut
logger.debug(f"{tick.symbole}: {tick.prix} | Vol: {tick.volume}")
except json.JSONDecodeError:
logger.warning(f"Message invalide: {message[:100]}")
async def reconnecter(self, url: str, symboles: List[str], delai: int = 5):
"""Reconnexion automatique avec backoff exponentiel."""
tentative = 1
delai_actuel = delai
while tentative <= 10:
logger.info(f"Tentative de reconnexion {tentative}/10 dans {delai_actuel}s...")
await asyncio.sleep(delai_actuel)
try:
await self.connecter(url, symboles)
return # Connexion réussie
except Exception as e:
logger.error(f"Échec reconnexion: {e}")
tentative += 1
delai_actuel = min(delai_actuel * 2, 60) # Max 60 secondes
logger.critical("Nombre maximum de tentatives atteint")
def deconnecter(self):
"""Ferme la connexion proprement."""
self._running = False
logger.info(f"Déconnexion.Ticks reçus: {self.tick_count}")
Exemple d'utilisation avec stockage ClickHouse
async def exemple_complet():
"""Exemple complet: WebSocket → Traitement → Stockage."""
def traiter_tick(tick: TickData):
"""Callback pour traiter chaque tick."""
# Log les 10 premiers ticks pour vérification
if tick._count < 10:
print(f" {tick.timestamp.strftime('%H:%M:%S')} | {tick.symbole} = {tick.prix}")
tick._count = getattr(tick, '_count', 0) + 1
client = WebSocketClient(callback=traiter_tick)
# Connexion à Binance WebSocket
await client.connecter(
url="wss://stream.binance.com:9443/ws",
symboles=["btcusdt", "ethusdt"]
)
Lancement
if __name__ == "__main__":
asyncio.run(exemple_complet())
Partie 3 : ClickHouse — requêtes analytiques sur des milliards de lignes
Pourquoi ClickHouse pour le trading quantitatif ?
ClickHouse est une base de données colonne-optimisée conçue pour les analyses sur d'énormes volumes. Dans notre contexte :
- Performance : Requêtes agrégées sur 1 milliard de lignes en moins de 50 millisecondes
- Compression : Les données de marché se compressent très bien (10x en moyenne)
- SQL natif : Pas besoin d'apprendre un nouveau langage de requête
- Coût : Installation auto-hébergée gratuite, ou ClickHouse Cloud à partir de 40$/mois
Installation rapide de ClickHouse
# Installation sur Ubuntu/Debian
sudo apt-get install -y apt-transport-https ca-certificates dirmngr
sudo apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv 8919F6BD2B48D754
echo "deb https://packages.clickhouse.com/deb stable main" | \
sudo tee /etc/apt/sources.list.d/clickhouse.list
sudo apt-get update
sudo apt-get install -y clickhouse-server clickhouse-client
Démarrer le service
sudo service clickhouse-server start
Vérifier l'installation
clickhouse-client --query "SELECT 'ClickHouse ' || version()"
Création du schéma de base de données
-- Connexion à ClickHouse
clickhouse-client
-- Création de la base de données pour notre projet
CREATE DATABASE IF NOT EXISTS quant_trading;
-- Table principale pour les ticks de marché
-- Utilise le moteur MergeTree optimisé pour les données temporelles
CREATE TABLE IF NOT EXISTS quant_trading.ticks (
timestamp DateTime64(3) CODEC(ZSTD(3)),
symbole String CODEC(ZSTD(3)),
prix Decimal(18, 8) CODEC(Delta, ZSTD(3)),
volume Decimal(18, 8) CODEC(ZSTD(3)),
bid Decimal(18, 8) CODEC(ZSTD(3)),
ask Decimal(18, 8) CODEC(ZSTD(3)),
-- Index temporel pour requêtes par date
INDEX idx_timestamp(timestamp) TYPE minmax GRANULARITY 3
)
ENGINE = MergeTree()
PARTITION BY toYYYYMM(timestamp)
ORDER BY (symbole, timestamp)
TTL timestamp + INTERVAL 2 YEAR
SETTINGS index_granularity = 8192;
-- Table agrégée pour OHLCV (1 minute)
CREATE TABLE IF NOT EXISTS quant_trading.ohlcv_1m (
timestamp DateTime,
symbole String,
open Decimal(18, 8),
high Decimal(18, 8),
low Decimal(18, 8),
close Decimal(18, 8),
volume Decimal(18, 8),
-- Matérialisation automatique des données
PRIMARY KEY (symbole, timestamp)
)
ENGINE = SummingMergeTree()
ORDER BY (symbole, timestamp)
PARTITION BY toYYYYMM(timestamp);
-- Table pour les métadonnées des archives Tardis
CREATE TABLE IF NOT EXISTS quant_trading.archives (
archive_id String,
nom_fichier String,
symbole String,
intervalle String,
date_fichier Date,
taille_originale UInt64,
ratio_compression Float32,
storage_url String,
archive_le DateTime,
PRIMARY KEY (archive_id)
)
ENGINE = ReplacingMergeTree(archive_le)
ORDER BY (archive_id);
-- Vue materialisée pour calcul automatique des indicateurs techniques
CREATE MATERIALIZED VIEW IF NOT EXISTS quant_trading.mv_sma_20
ENGINE = SummingMergeTree()
ORDER BY (symbole, timestamp)
AS SELECT
symbole,
toStartOfMinute(timestamp) as timestamp,
avg(close) as sma_20
FROM quant_trading.ohlcv_1m
GROUP BY symbole, timestamp;
-- Afficher les tables créées
SHOW TABLES FROM quant_trading;
Intégration Python ClickHouse
from clickhouse_driver import Client
from datetime import datetime, timedelta
import pandas as pd
import os
class ClickHouseManager:
"""Gestionnaire de connexion et requêtes ClickHouse."""
def __init__(self, host: str = 'localhost', port: int = 9000):
self.client = Client(
host=host,
port=port,
database='quant_trading',
settings={
'max_execution_time': 60,
'use_numpy': True # Performance optimale pour pandas
}
)
print(f"Connecté à ClickHouse: {host}:{port}")
def inserer_tick(self, tick_data: dict):
"""Insère un tick individuel dans la base."""
requete = """
INSERT INTO quant_trading.ticks VALUES
(%(timestamp)s, %(symbole)s, %(prix)s, %(volume)s, %(bid)s, %(ask)s)
"""
self.client.execute(requete, tick_data)
def inserer_ticks_batch(self, ticks: list):
"""Insère plusieurs ticks en une seule requête (plus rapide)."""
self.client.execute(
"""
INSERT INTO quant_trading.ticks FORMAT JSONEachRow
""",
ticks
)
def calculer_ohlcv(self, symbole: str, jours: int = 1) -> pd.DataFrame:
"""
Calcule les chandeliers OHLCV pour une période donnée.
Args:
symbole: Symbole à analyser (ex: 'BTCUSDT')
jours: Nombre de jours de données à inclure
Returns:
DataFrame pandas avec les chandeliers 1 minute
"""
requete = """
SELECT
toStartOfMinute(timestamp) as timestamp,
argMin(prix, timestamp) as open,
max(prix) as high,
min(prix) as low,
argMax(prix, timestamp) as close,
sum(volume) as volume
FROM quant_trading.ticks
WHERE symbole = %(symbole)s
AND timestamp >= now() - INTERVAL %(jours)s DAY
GROUP BY timestamp
ORDER BY timestamp
"""
resultats = self.client.execute(
requete,
{'symbole': symbole, 'jours': jours}
)
return pd.DataFrame(
resultats,
columns=['timestamp', 'open', 'high', 'low', 'close', 'volume']
)
def requete_volatilite(self, symbole: str, minutes: int = 60) -> dict:
"""
Calcule la volatilité historique sur une fenêtre glissante.
Returns:
dict avec min, max, avg, std des rendements
"""
requete = """
WITH (
SELECT groupArray(prix)
FROM (
SELECT prix
FROM quant_trading.ticks
WHERE symbole = %(symbole)s
AND timestamp >= now() - INTERVAL %(minutes)s MINUTE
ORDER BY timestamp
)
) AS prix_array
SELECT
arrayMap(x -> log(x / x[1]), prix_array) as rendements
"""
resultats = self.client.execute(
requete,
{'symbole': symbole, 'minutes': minutes}
)
rendements = resultats[0][0] if resultats else []
if not rendements:
return {'min': 0, 'max': 0, 'avg': 0, 'std': 0}
return {
'min': min(rendements),
'max': max(rendements),
'avg': sum(rendements) / len(rendements),
'std': (sum((r - sum(rendements)/len(rendements))**2 for r in rendements) / len(rendements)) ** 0.5
}
def statistiques_symboles(self) -> pd.DataFrame:
"""Affiche les statistiques globales de tous les symboles."""
requete = """
SELECT
symbole,
count() as nb_ticks,
min(timestamp) as premiere_date,
max(timestamp) as derniere_date,
round(sum(volume), 2) as volume_total,
round(avg(prix), 2) as prix_moyen
FROM quant_trading.ticks
GROUP BY symbole
ORDER BY nb_ticks DESC
"""
return pd.DataFrame(
self.client.execute(requete),
columns=['symbole', 'nb_ticks', 'premiere_date', 'derniere_date',
'volume_total', 'prix_moyen']
)
Utilisation
if __name__ == "__main__":
manager = ClickHouseManager()
# Afficher les statistiques
stats = manager.statistiques_symboles()
print("\n📊 Statistiques des symboles:")
print(stats.to_string(index=False))
# Calculer OHLCV pour BTC sur les dernières 24h
ohlcv = manager.calculer_ohlcv('BTCUSDT', jours=1)
print(f"\n📈 OHLCV BTC (1 jour): {len(ohlcv)} chandeliers")
# Volatilité récente
vol = manager.requete_volatilite('BTCUSDT', minutes=60)
print(f"\n📉 Volatilité BTC (1h): std={vol['std']:.6f}")
Partie 4 : Assistant IA pour recherche quantitative avec HolySheep AI
L'intelligence artificielle au service de votre recherche
C'est ici que HolySheep AI transforme votre workflow. Au lieu de passer des heures à coder des analyses statistiques ou à rédiger des rapports de recherche, vous pouvez interroger directement un modèle IA spécialisé dans la finance quantitative.
Pourquoi HolySheep AI plutôt que directement ChatGPT ? Les avantages concrets :
- Latence moyenne <50ms — Comparé aux 2-5 secondes sur les APIs standard
- Tarif imbattable — DeepSeek V3.2 à 0.42$/million de tokens, soit 85% moins cher que GPT-4.1
- Paiements locaux — WeChat Pay et Alipay disponibles, idéal pour les équipes chinoises
- Crédits gratuits — Offerts à l'inscription pour tester sans engagement
Intégration de l'assistant IA HolySheep
import os
import requests
from typing import List, Dict, Optional
from dataclasses import dataclass
import json
@dataclass
class Message:
"""Structure d'un message pour l'API chat."""
role: str # 'system', 'user' ou 'assistant'
content: str
class AssistantQuantiqueHolySheep:
"""
Assistant IA pour recherche quantitative.
Utilise l'API HolySheep AI avec latence optimisée <50ms.
Tarification 2026:
- DeepSeek V3.2: $0.42/M tokens (entrée/sortie)
- Claude Sonnet 4.5: $15/M tokens entrée, $75/M tokens sortie
- GPT-4.1: $8/M tokens entrée, $24/M tokens sortie
"""
BASE_URL = "https://api.holysheep.ai/v1"
def __init__(self, api_key: str, modele: str = "deepseek-v3.2"):
"""
Initialise l'assistant.
Args:
api_key: Clé API HolySheep (obtenue sur holysheep.ai)
modele: Modèle à utiliser (deepseek-v3.2, claude-sonnet-4.5, gpt-4.1)
"""
self.api_key = api_key
self.modele = modele
self.messages: List[Dict] = []
self.total_tokens = 0
def _construire_entete(self) -> Dict[str, str]:
"""Construit les en-têtes HTTP pour l'authentification."""
return {
"Authorization": f"Bearer {self.api_key}",
"Content-Type": "application/json"
}
def ajouter_message_systeme(self, contenu: str):
"""Ajoute un message système pour configurer le comportement de l'assistant."""
self.messages.append({"role": "system", "content": contenu})
def analyser_donnees(self, description_donnees: str, question: str) -> str:
"""
Analyse des données de marché et répond à une question.
Args:
description_donnees: Description des données disponibles
question: Question analytique à poser
Returns:
Réponse de l'assistant IA
"""
self.ajouter_message_systeme(
"""Tu es un analyste quantitatif expert en trading.
Tu as accès à des données de marché OHLCV archivées dans ClickHouse.
Réponds de manière précise avec des exemples chiffrés quand possible.
Propose du code Python ou SQL si pertinent."""
)
prompt = f"""
Données disponibles:
{description_donnees}
Question: {question}
"""
return self.envoyer_message(prompt)
def generer_code_strategie(self, nom_strategie: str, specs: str) -> Dict[str, str]:
"""
Génère du code pour une stratégie de trading.
Args:
nom_strategie: Nom de la stratégie (ex: 'moyennes mobiles croisées')
specs: Spécifications détaillées
Returns:
dict avec 'description', 'code_python' et 'tests'
"""
self.ajouter_message_systeme(
"""Tu es un développeur expert en stratégies de trading quantitatif.
Génère du code Python propre, documenté et testable.
Utilise pandas, numpy et les bibliothèques standards du domaine."""
)
prompt = f"""
Génère une stratégie de trading appelée '{nom_strategie}'.
Spécifications:
{specs}
Fournis:
1. Une description de la stratégie
2. Le code Python complet avec docstrings
3. Des tests unitaires basiques
"""
reponse = self.envoyer_message(prompt)
# Parser la réponse pour extraire les sections
sections = self._parser_reponse_structuree(reponse)
return sections
def envoyer_message(self, contenu: str) -> str:
"""Envoie un message à l'API et retourne la réponse."""
self.messages.append({"role": "user", "content": contenu})
payload = {
"model": self.modele,
"messages": self.messages,
"temperature": 0.7,
"max_tokens": 2000
}
try:
reponse = requests.post(
f"{self.BASE_URL}/chat/completions",
headers=self._construire_entete(),
json=payload,
timeout=30
)
reponse.raise_for_status()
donnees = reponse.json()
# Extraire la réponse
contenu_reponse = donnees['choices'][0]['message']['content']
# Mettre à jour le suivi des tokens
self.total_tokens += donnees.get('usage', {}).get('total_tokens', 0)
# Ajouter la réponse à l'historique
self.messages.append({"role": "assistant", "content": contenu_reponse})
return contenu_reponse
except requests.exceptions.Timeout:
return "Erreur: Timeout - La requête a pris trop de temps"
except requests.exceptions.RequestException as e:
return f"Erreur de connexion: {str(e)}"
def _parser_reponse_structuree(self, texte: str) -> Dict[str, str]:
"""Parse une réponse structurée avec les sections attendues."""
sections = {'description': '', 'code_python': '', 'tests': ''}
parties = texte.split('```python')
if len(parties) > 1:
sections['description'] = parties[0].strip()
reste = parties[1].split('```')
sections['code_python'] = reste[0].strip() if reste else ''
sections['tests'] = parties[2].split('```')[1].strip() if len(parties) > 2 else ''
else:
sections['description'] = texte
return sections
def calculer_cout_estime(self) -> Dict[str, float]:
"""
Calcule le coût estimé basé sur l'utilisation.
Returns:
dict avec tokens utilisés et coût en dollars
"""
prix_par_million = {
'deepseek-v3.2': 0.42,
'claude-sonnet-4.5': 15.0,
'gpt-4.1': 8.0
}
prix = prix_par_million.get(self.modele, 1.0)
cout = (self.total_tokens / 1_000_000) * prix
return {
'tokens': self.total_tokens,
'cout_usd': cout,
'modele': self.modele,
'prix_par_million': prix
}
Exemple d'utilisation
if __name__ == "__main__":
# Initialisation (remplacez par votre vraie clé)
assistant = AssistantQuantiqueHolySheep(
api_key="YOUR_HOLYSHEEP_API_KEY",
modele="deepseek-v3.2" # Modèle le plus économique
)
# Analyser des données
analyse = assistant.analyser_donnees(
description_donnees="""
Table 'ticks': timestamp, symbole, prix, volume, bid, ask
Table 'ohlcv_1m': timestamp, symbole, open, high, low, close, volume
Données BTCUSDT sur 30 derniers jours (environ 1.3 million de lignes)
""",
question="""
Quelle est la volatilité moyenne du BTCUSDT sur les 7 derniers jours ?
Propose une stratégie de mean reversion basée sur cette volatilité.
"""
)
print("📊 Analyse de l'assistant:")