La plupart des projets data démarrent avec un fichier CSV ou une base existante. Ce projet part d’un flux live : les trades BTC/USDT de Binance, en temps réel, événement par événement.
L’objectif n’est pas de construire un système de trading. C’est de maîtriser l’architecture d’un pipeline de streaming de bout en bout — ingestion, transport, stockage, exposition, visualisation — en construisant chaque couche de façon isolée et documentée.
Chaque phase correspond à un composant réel utilisé en production dans les équipes data : WebSocket pour l’ingestion temps réel, Kafka comme bus de messages découplé, TimescaleDB pour les séries temporelles, FastAPI pour l’exposition, Grafana pour la visualisation.
Flux temps réel BTC/USDT — endpoint btcusdt@trade — ~1–2 events/s
Voir la méthodologie
Le projet est construit en 5 phases successives. Chaque phase ajoute un composant au pipeline existant sans modifier ce qui précède. Ce découpage est intentionnel : il force à comprendre chaque couche isolément avant de l’intégrer.
La Phase 1 connecte Python à Binance et affiche les trades en console. La Phase 2 introduit Kafka comme couche de transport. Les Phases 3 à 5 ajoutent stockage, API et visualisation. L’infrastructure Docker Compose évolue à chaque phase pour intégrer les nouveaux services.
Challenges rencontrés
Problème : L'API Binance utilise des champs nommés T, p, q — sans documentation, ils sont illisibles. Propager ces noms dans Kafka contaminerait tous les consommateurs.
Solution : Normalisation des noms au niveau du producteur, avant l'entrée dans Kafka : T → timestamp, p → prix, q → quantite. Un seul point de modification si la source change.
Problème : Kafka démarre correctement mais les clients ne peuvent pas publier. Le broker communique une adresse inconnue aux producers.
Solution : Séparation explicite de deux listeners dans Docker Compose — PLAINTEXT_INTERNAL pour la communication inter-containers, PLAINTEXT_LOCAL pour les connexions depuis le host (localhost:9092).
Arbitrages techniques
Kafka retenu malgré la complexité de setup. Une connexion directe couperait les données si le consommateur est indisponible et forcerait à modifier le producteur pour chaque nouveau consommateur. Kafka résout les deux problèmes avec un bus persistant.
TimescaleDB choisi pour rester dans l'écosystème PostgreSQL. SQL standard, extensions connues, pas de query language propriétaire. InfluxDB impose Flux ou InfluxQL — une dépendance supplémentaire à apprendre.
asyncio utilisé car le WebSocket est I/O-bound — le programme passe la majorité du temps à attendre le prochain message. L'async permet de gérer plusieurs flux en parallèle sans bloquer le thread principal.
Voir les snippets
import asyncio, json, websockets
from kafka import KafkaProducer
from datetime import datetime
URL = "wss://stream.binance.com:9443/ws/btcusdt@trade"
producer_kafka = KafkaProducer(
bootstrap_servers="localhost:9092",
value_serializer=lambda x: json.dumps(x).encode("utf-8")
)
async def connect():
async with websockets.connect(URL) as ws:
async for message in ws:
data = json.loads(message)
payload = {
"timestamp": datetime.utcfromtimestamp(data["T"] / 1000).isoformat(),
"prix": float(data["p"]),
"quantite": float(data["q"])
}
producer_kafka.send("crypto-trades", value=payload)
print(payload)
asyncio.run(connect())
version: "3.8"
services:
zookeeper:
image: confluentinc/cp-zookeeper:7.5.0
environment:
ZOOKEEPER_CLIENT_PORT: 2181
kafka:
image: confluentinc/cp-kafka:7.5.0
depends_on: [zookeeper]
ports:
- "9092:9092"
environment:
KAFKA_ZOOKEEPER_CONNECT: zookeeper:2181
KAFKA_ADVERTISED_LISTENERS: PLAINTEXT_LOCAL://localhost:9092,PLAINTEXT_INTERNAL://kafka:29092
KAFKA_LISTENER_SECURITY_PROTOCOL_MAP: PLAINTEXT_LOCAL:PLAINTEXT,PLAINTEXT_INTERNAL:PLAINTEXT
KAFKA_INTER_BROKER_LISTENER_NAME: PLAINTEXT_INTERNAL
KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR: 1
- Implémenter le consommateur Python + insertion TimescaleDB (Phase 3)
- Créer l'API FastAPI avec les endpoints /trades, /stats, /latest (Phase 4)
- Ajouter Grafana et compléter le Docker Compose avec tous les services (Phase 5)
- Publier l'article de projet associé sur kepsilone.com