Introduction
Le backtesting de stratégies de trading sur les contrats perpetuels OKX nécessite des données tick fiables et une infrastructure capable de les ingérer efficacement. Tardis Machine est devenu une référence pour les traders quantitatifs qui nécessitent un accès historique de haute qualité aux données de marché des exchanges de cryptomonnaies.
Dans ce guide complet, nous explorerons comment configurer, extraire et utiliser les données tick OKX via l'API Tardis pour construire un système de backtesting robuste.
---
Prérequis
Avant de commencer, vous aurez besoin de :
- Un compte Tardis Machine avec un abonnement actif
- Python 3.9+ installé
- La bibliothèque
tardis installée (
pip install tardis-client)
- Connaissances de base en trading algorithmique
---
Installation et Configuration
# Installation des dépendances
pip install tardis-client pandas numpy
Configuration de l'API Tardis
import asyncio
from tardis_client import TardisClient, Channel
Connexion à l'API
client = TardisClient(api_key="YOUR_TARDIS_API_KEY")
Vérification de la connexion
async def verify_connection():
exchanges = await client.exchanges()
print(f"Exchanges disponibles : {[e.name for e in exchanges]}")
asyncio.run(verify_connection())
---
Extraction des Données Tick OKX Perpetual
from datetime import datetime, timedelta
Configuration pour OKX perpetual (USDT-M)
exchange_name = "okx"
channel_name = "trade" # Données de transaction tick par tick
symbol = "BTC-USDT-SWAP"
Période de test : 30 derniers jours
start_date = datetime(2026, 4, 1)
end_date = datetime(2026, 4, 30)
Récupération des données
async def fetch_ohlcv_data():
messages = client.replay(
exchange=exchange_name,
channels=[Channel(name=channel_name, symbols=[symbol])],
from_date=start_date,
to_date=end_date
)
trades = []
async for message in messages:
if message.type == "trade":
trades.append({
'timestamp': message.timestamp,
'side': message.side,
'price': float(message.price),
'size': float(message.size),
'symbol': message.symbol
})
return trades
trades = asyncio.run(fetch_ohlcv_data())
print(f"Nombre de trades récupérés : {len(trades)}")
---
Construction du Moteur de Backtesting
import pandas as pd
import numpy as np
class BacktestEngine:
def __init__(self, initial_balance=10000):
self.initial_balance = initial_balance
self.balance = initial_balance
self.position = 0
self.trades = []
self.equity_curve = []
def execute_trade(self, timestamp, price, side, size):
"""Exécution d'un trade"""
if side == "buy" and self.balance >= price * size:
self.balance -= price * size
self.position += size
elif side == "sell" and self.position >= size:
self.balance += price * size
self.position -= size
self.trades.append({
'timestamp': timestamp,
'price': price,
'side': side,
'size': size,
'balance': self.balance,
'position': self.position
})
def calculate_returns(self):
"""Calcul des métriques de performance"""
df = pd.DataFrame(self.trades)
# Rendement total
total_return = (self.balance - self.initial_balance) / self.initial_balance
# Sharpe Ratio (simplifié)
if len(df) > 1:
returns = df['balance'].pct_change().dropna()
sharpe = np.sqrt(252) * returns.mean() / returns.std()
else:
sharpe = 0
# Drawdown maximum
df['cummax'] = df['balance'].cummax()
df['drawdown'] = (df['balance'] - df['cummax']) / df['cummax']
max_drawdown = df['drawdown'].min()
return {
'total_return': total_return,
'sharpe_ratio': sharpe,
'max_drawdown': max_drawdown,
'total_trades': len(df),
'final_balance': self.balance
}
Exécution du backtest
engine = BacktestEngine(initial_balance=10000)
Stratégie simple : achat sur support, vente sur résistance
def simple_strategy(trades_df, lookback=20, tp_pct=0.02, sl_pct=0.01):
engine = BacktestEngine(initial_balance=10000)
for i, trade in trades_df.iterrows():
# Logique de stratégie (exemple simplifié)
if i > lookback:
engine.execute_trade(
trade['timestamp'],
trade['price'],
'buy',
0.01 # Taille fixe
)
return engine
result = simple_strategy(pd.DataFrame(trades))
metrics = result.calculate_returns()
print("=== Résultats du Backtest ===")
print(f"Rendement total : {metrics['total_return']:.2%}")
print(f"Sharpe Ratio : {metrics['sharpe_ratio']:.2f}")
print(f"Drawdown maximum : {metrics['max_drawdown']:.2%}")
print(f"Nombre de trades : {metrics['total_trades']}")
---
Optimisation et Validation
from itertools import product
def optimize_strategy(trades_df, param_grid):
"""Optimisation des paramètres de stratégie"""
best_params = None
best_sharpe = -np.inf
results = []
# Grid search
for lookback, tp, sl in product(
param_grid['lookback'],
param_grid['take_profit'],
param_grid['stop_loss']
):
engine = BacktestEngine(initial_balance=10000)
# Test avec ces paramètres
for i, trade in trades_df.iterrows():
if i > lookback:
engine.execute_trade(
trade['timestamp'],
trade['price'],
'buy',
0.01
)
metrics = engine.calculate_returns()
if metrics['sharpe_ratio'] > best_sharpe:
best_sharpe = metrics['sharpe_ratio']
best_params = {'lookback': lookback, 'tp': tp, 'sl': sl}
results.append({
'params': {'lookback': lookback, 'tp': tp, 'sl': sl},
'sharpe': metrics['sharpe_ratio'],
'return': metrics['total_return']
})
return best_params, results
Définition de la grille de paramètres
param_grid = {
'lookback': [10, 20, 50, 100],
'take_profit': [0.01, 0.02, 0.03, 0.05],
'stop_loss': [0.005, 0.01, 0.015, 0.02]
}
best_params, all_results = optimize_strategy(pd.DataFrame(trades), param_grid)
print(f"Meilleurs paramètres : {best_params}")
print(f"Meilleur Sharpe Ratio : {max(r['sharpe'] for r in all_results):.2f}")
---
Considérations de Latence et Performance
Pour un backtesting réaliste, gardez à l'esprit :
| Aspect | Valeur typique | Impact |
|--------|----------------|--------|
| Latence API Tardis | 50-200ms | Affecte la réplication des ordres |
| Fréquence des données | Tick par tick | Nécessite un historique complet |
| Frais de transaction | 0.05-0.10% | Impact significatif sur la stratégie |
| Slippage | 0.01-0.05% | Variable selon la liquidité |
---
Erreurs Courantes et Solutions
Erreur 1 : Limite de requêtes dépassée (429 Too Many Requests)
# Solution : Implémentation du rate limiting
import time
from functools import wraps
def rate_limit(calls_per_second=10):
"""Décorateur pour limiter les appels API"""
min_interval = 1.0 / calls_per_second
last_called = [0.0]
def decorator(func):
@wraps(func)
def wrapper(*args, **kwargs):
elapsed = time.time() - last_called[0]
if elapsed < min_interval:
time.sleep(min_interval - elapsed)
last_called[0] = time.time()
return func(*args, **kwargs)
return wrapper
return decorator
Utilisation
@rate_limit(calls_per_second=5)
async def fetch_data_safe():
messages = client.replay(...)
return messages
Erreur 2 : Symboles non reconnus
# Solution : Vérification préalable des symboles disponibles
async def get_available_symbols():
"""Liste tous les symboles OKX disponibles"""
symbols = await client.symbols(exchange="okx")
# Filtrer pour les perpetual
perpetual_symbols = [
s for s in symbols
if "-SWAP" in s.name and "USDT" in s.name
]
print("Symboles perpetual USDT disponibles :")
for s in perpetual_symbols[:10]:
print(f" - {s.name}")
return perpetual_symbols
Symbols valides pour OKX perpetual :
BTC-USDT-SWAP, ETH-USDT-SWAP, SOL-USDT-SWAP, etc.
Erreur 3 : Données incomplètes ou lacunaires
# Solution : Vérification et remplissage des données
def validate_and_fill_data(trades_df):
"""Valide et complète les données de marché"""
# Conversion des timestamps
trades_df['timestamp'] = pd.to_datetime(trades_df['timestamp'])
trades_df = trades_df.sort_values('timestamp')
# Détection des lacunes
trades_df['time_diff'] = trades_df['timestamp'].diff()
gaps = trades_df[trades_df['time_diff'] > pd.Timedelta(minutes=5)]
if len(gaps) > 0:
print(f"Attention : {len(gaps)} lacunes détectées")
print(gaps[['timestamp', 'time_diff']].head())
# Interpolation des prix manquants (si nécessaire)
trades_df['price'] = trades_df['price'].interpolate(method='linear')
return trades_df
Appliquer la validation
trades_df = validate_and_fill_data(pd.DataFrame(trades))
---
Conclusion et Prochaines Étapes
L'utilisation de Tardis API pour le backtesting de stratégies OKX perpetual offre une base solide pour développer et valider vos systèmes de trading. Les données tick par tick permettent une précision maximale dans la simulation des conditions réelles de marché.
Points clés à retenir :
1. **Qualité des données** : Tardis offre des données historiques vérifiées et complète
2. **Flexibilité** : L'API permet une personnalisation complète des stratégies
3. **Optimisation** : Le grid search permet de trouver les paramètres optimaux
4. **Validation** : Toujours tester sur plusieurs périodes pour éviter le surapprentissage
Pour aller plus loin, explorez l'ajout de données de order book pour une simulation plus réaliste des slips et de la liquidité.
Ressources connexes
Articles connexes