# [REFERENCE QC] Code a copier dans main.py QC Lab (non executable ici)
# Structure de base d'un algorithme QuantConnect
# Note: Ce code est informatif, il ne peut pas s'exécuter localement
# Il doit être copié dans l'IDE QuantConnect
from AlgorithmImports import *
class BasicLifecycleAlgorithm(QCAlgorithm):
def Initialize(self):
"""
Appelé une fois au début du backtest.
Configuration: dates, capital, securities, indicateurs.
"""
# Définir la période de backtest
self.SetStartDate(2015, 1, 1)
self.SetEndDate(2024, 12, 31)
# Définir le capital initial
self.SetCash(100000)
# Ajouter des actifs (securities)
self.symbol = self.AddEquity("SPY", Resolution.Daily).Symbol
self.Log("Algorithm initialized")
def OnData(self, data):
"""
Appelé à chaque nouvelle donnée (tick, minute, hour, daily).
Logique de trading ici.
"""
# Vérifier que les données sont disponibles
if not data.ContainsKey(self.symbol):
return
# Accéder au prix actuel
price = data[self.symbol].Close
# Exemple: acheter si on n'est pas investi
if not self.Portfolio.Invested:
self.SetHoldings(self.symbol, 1.0)
self.Log(f"Bought SPY at ${price:.2f}")
def OnEndOfAlgorithm(self):
"""
Appelé à la fin du backtest.
Nettoyage, logs finaux.
"""
final_value = self.Portfolio.TotalPortfolioValue
self.Log(f"Algorithm ended. Final Portfolio Value: ${final_value:.2f}")<< Sommaire QC | Précédent : QC-Py-01-Setup << | Suivant : QC-Py-03-Data-Management >>
QC-Py-02 : QuantConnect Platform Fundamentals - QCAlgorithm Lifecycle
Introduction
Dans ce notebook, nous allons approfondir les fondamentaux de la plateforme QuantConnect en explorant le cycle de vie de la classe QCAlgorithm, qui constitue le coeur de toute stratégie de trading algorithmique sur QuantConnect.
Récapitulatif QC-Py-01
Dans le notebook précédent, nous avons : - Créé un compte QuantConnect - Exploré l’interface web - Créé et exécuté notre premier algorithme “Hello World” - Compris la différence entre backtest et live trading
Objectifs de ce notebook
À la fin de ce notebook, vous serez capable de :
- Maîtriser le lifecycle QCAlgorithm (Initialize, OnData, OnEndOfAlgorithm)
- Comprendre la gestion des securities (AddEquity, AddCrypto, etc.)
- Créer des stratégies simples avec indicateurs techniques intégrés
- Découvrir les principales méthodes de QCAlgorithm
Prérequis
- Notebook QC-Py-01 complété (compte QuantConnect créé)
- Python niveau intermédiaire
- Notions de base en trading (optionnel mais utile)
Plan du notebook
- QCAlgorithm Lifecycle (15 min)
- Gestion des Securities (15 min)
- Indicateurs Techniques Intégrés (15 min)
- Exemple Complet : RSI Mean Reversion (15 min)
- Méthodes QCAlgorithm Essentielles (10 min)
- Conclusion (5 min)
Durée estimée : 60 minutes
Note de conception : Ce notebook contient du code de reference a copier dans QuantConnect Lab (main.py). Les cellules ne sont pas prevues pour etre executees en tant que notebook Jupyter. L’absence d’outputs (execution_count: null) est intentionnelle.
[REFERENCE QC Cloud] Ce notebook illustre du code QuantConnect a executer dans l’IDE Cloud (https://www.quantconnect.com/research). L’environnement local ne dispose pas de QuantBook ni de l’historical data feed. Pour executer : cloner le projet QC associe, ouvrir
research.ipynb, executer cellule par cellule.
Mode d’emploi : Ce notebook est un document de reference (non executable). Le code QCAlgorithm presente ici est a copier dans
main.pyde votre projet QuantConnect Lab. Ne tentez pas de faire “Run All” – les importsAlgorithmImportsn’existent pas en Jupyter local.Pour demarrer un projet QC : copiez un
main.pydepuisprojects/oupartner-course-quant-trading/templates/, puis adaptez-le.
2. QCAlgorithm Lifecycle
Vue d’ensemble du cycle de vie
Toute stratégie QuantConnect hérite de la classe QCAlgorithm et implémente trois méthodes principales qui définissent le cycle de vie de l’algorithme :
flowchart TD
S["BACKTEST START"] --> I["Initialize()<br/>• Appelé UNE SEULE FOIS au début<br/>• Configuration dates, capital<br/>• Ajout des securities<br/>• Création des indicateurs"]
I --> O["OnData(data)<br/>• Appelé à CHAQUE nouvelle donnée<br/>• Logique de trading<br/>• Décisions d'achat/vente"]
O -- "boucle principale, à chaque barre" --> O
O --> E["OnEndOfAlgorithm()<br/>• Appelé UNE FOIS à la fin<br/>• Nettoyage<br/>• Logs finaux"]
E --> T["BACKTEST END"]
Les trois méthodes clés
| Méthode | Appels | Rôle | Exemple d’utilisation |
|---|---|---|---|
Initialize() |
1 fois (début) | Configuration initiale | Définir dates, ajouter SPY, créer SMA(20) |
OnData(data) |
À chaque tick/bar | Logique de trading | Si SMA croisé, acheter/vendre |
OnEndOfAlgorithm() |
1 fois (fin) | Finalisation | Logger le rendement final |
Structure de base d’un algorithme
Voici le squelette minimal de tout algorithme QuantConnect :
Résultat du Backtest QC Cloud — BasicLifecycleAlgorithm (BT 517a23f820b3…, projet 31996105)
Sharpe CAGR MaxDD Trades 0.493 11.761% 33.5% 1
Fréquence d’appel de OnData selon Resolution
La méthode OnData est appelée selon la résolution (Resolution) choisie lors de l’ajout de la security :
| Resolution | Fréquence d’appel | Exemple d’usage |
|---|---|---|
Resolution.Tick |
À chaque tick (millisecondes) | Scalping, market making |
Resolution.Second |
Toutes les secondes | Trading haute fréquence |
Resolution.Minute |
Toutes les minutes | Day trading |
Resolution.Hour |
Toutes les heures | Swing trading |
Resolution.Daily |
Tous les jours (9h30 ET) | Position trading |
Important : Plus la résolution est fine, plus le backtest est lent et consomme de ressources. Commencez avec Resolution.Daily pour vos tests initiaux.
Debug avec self.Log() et self.Debug()
QuantConnect offre deux méthodes pour logger des informations :
# self.Log() - Visible dans les résultats du backtest
self.Log("Message important qui apparaîtra dans le rapport")
# self.Debug() - Visible dans la console de debug (temps réel)
self.Debug(f"Prix actuel: {price}")Conseil : Utilisez self.Log() pour des événements importants (achats, ventes) et self.Debug() pour du debug détaillé.
3. Gestion des Securities
Qu’est-ce qu’une Security ?
Une Security (titre financier) représente un actif tradable sur QuantConnect. Avant de pouvoir trader un actif, vous devez l’ajouter à votre algorithme avec une méthode Add*.
Principales classes d’actifs
QuantConnect supporte plusieurs classes d’actifs :
| Classe d’actif | Méthode | Exemple | Résolution recommandée |
|---|---|---|---|
| Actions (Equity) | AddEquity(ticker, resolution) |
AddEquity("AAPL", Resolution.Daily) |
Daily/Minute |
| Crypto | AddCrypto(pair, resolution) |
AddCrypto("BTCUSD", Resolution.Hour) |
Hour/Minute |
| Forex | AddForex(pair, resolution) |
AddForex("EURUSD", Resolution.Minute) |
Minute/Second |
| Options | AddOption(underlying) |
AddOption("SPY") |
Minute (détaillé en QC-Py-06) |
| Futures | AddFuture(ticker) |
AddFuture("ES") |
Minute (détaillé en QC-Py-07) |
Symbol vs String ticker
Point clé : Les méthodes Add* retournent un objet Symbol, pas une simple string.
# ❌ MAUVAIS - utiliser directement le ticker
self.AddEquity("SPY", Resolution.Daily)
# Plus tard dans OnData...
price = data["SPY"].Close # Peut causer des erreurs
# ✅ BON - stocker le Symbol
self.symbol_spy = self.AddEquity("SPY", Resolution.Daily).Symbol
# Plus tard dans OnData...
price = data[self.symbol_spy].Close # RobusteLe Symbol contient plus d’informations que le simple ticker (marché, type d’actif, etc.) et évite les ambiguïtés.
Exemples d’ajout de securities
Résultat du Backtest QC Cloud — MultiAssetAlgorithm (BT c3e7f9c329cf…, projet 31996194)
Sharpe CAGR MaxDD Trades 0.834 25.403% 40.3% 99
# [REFERENCE QC] Code a copier dans main.py QC Lab (non executable ici)
# Exemples d'ajout de différentes classes d'actifs
# Note: Code informatif pour QuantConnect IDE
from AlgorithmImports import *
class MultiAssetAlgorithm(QCAlgorithm):
def Initialize(self):
self.SetStartDate(2015, 1, 1)
self.SetEndDate(2024, 12, 31)
self.SetCash(100000)
# 1. Equity (actions)
# SPY est un ETF très liquide qui réplique le S&P 500
self.symbol_spy = self.AddEquity("SPY", Resolution.Daily).Symbol
self.Log(f"Added SPY: {self.symbol_spy}")
# 2. Crypto
# BTCUSD = Bitcoin vs USD
# Note: Résolution Hour car crypto trade 24/7
self.symbol_btc = self.AddCrypto("BTCUSD", Resolution.Hour).Symbol
self.Log(f"Added BTCUSD: {self.symbol_btc}")
# 3. Forex
# EURUSD = Euro vs US Dollar
self.symbol_eur = self.AddForex("EURUSD", Resolution.Minute).Symbol
self.Log(f"Added EURUSD: {self.symbol_eur}")
# 4. Options (aperçu - détaillé dans QC-Py-06)
# Ajoute les options disponibles sur SPY
# self.option_spy = self.AddOption("SPY")
# self.option_spy.SetFilter(-5, 5, 0, 30) # Strike range, expiry days
# 5. Futures (aperçu - détaillé dans QC-Py-07)
# ES = E-mini S&P 500 futures
# self.future_es = self.AddFuture("ES")
def OnData(self, data):
# Vérifier disponibilité des données pour chaque symbol
if data.ContainsKey(self.symbol_spy):
spy_price = data[self.symbol_spy].Close
self.Debug(f"SPY: ${spy_price:.2f}")
if data.ContainsKey(self.symbol_btc):
btc_price = data[self.symbol_btc].Close
self.Debug(f"BTC: ${btc_price:.2f}")Exercice 1 : Portefeuille multi-actifs
La section précédente montre comment ajouter différents types d’actifs (equity, crypto, forex). Maintenant, construisez votre propre portefeuille diversifie en ajoutant plusieurs actions et en les repartissant equitablement.
Principe
Vous allez créer un algorithme qui : - Ajoute 3 actions de secteurs différents (par exemple technologie, sante, energie) - Repartit le capital equitablement (1/3 dans chaque) le premier jour de trading - Affiche un resume de la repartition avec self.Log()
Objectif
Completez les méthode Initialize et OnData de la classe MultiEquityPortfolio.
Indices :
- Indice 1 : Stockez vos symbols dans une liste
self.symbols = []dansInitialize. - Indice 2 : Pour repartir equitablement, utilisez
self.SetHoldings(symbol, 1.0 / len(self.symbols))pour chaque symbol. - Indice 3 : Pour n’acheter qu’une seule fois, utilisez un flag
self.invested = Falseet verifiez-le dansOnData.
# [REFERENCE QC] Exercice a completer - Code a copier dans main.py QC Lab
# Exercice 1 : Portefeuille multi-actifs diversifie
from AlgorithmImports import *
class MultiEquityPortfolio(QCAlgorithm):
"""
Portefeuille diversifie avec 3 actions de secteurs differents.
Le capital est reparti equitablement (1/3 par position).
"""
def Initialize(self):
self.SetStartDate(2020, 1, 1)
self.SetEndDate(2024, 12, 31)
self.SetCash(100000)
# TODO etudiant : ajouter 3 actions de secteurs differents
# Exemples : AAPL (tech), JNJ (sante), XOM (energie)
# Stockez les symbols dans self.symbols = [...]
# TODO etudiant : initialiser un flag self.invested = False
pass # TODO etudiant : remplacer par votre implementation
def OnData(self, data):
# TODO etudiant : verifier que toutes les donnees sont disponibles
# (utiliser une boucle et data.ContainsKey(symbol))
# TODO etudiant : si pas encore investi, repartir le capital equitablement
# avec self.SetHoldings(symbol, 1.0 / len(self.symbols)) pour chaque symbol
# TODO etudiant : logger la repartition avec self.Log()
pass # TODO etudiant : remplacer par votre implementation
print("Exercice a completer : MultiEquityPortfolio")Warm-up period avec SetWarmUp()
Les indicateurs techniques (SMA, EMA, RSI, etc.) nécessitent un certain nombre de données historiques avant d’être prêts (“ready”). Sans warm-up, les premiers jours du backtest n’auront pas d’indicateurs valides.
Solution : Utiliser SetWarmUp() pour charger des données historiques avant le début du backtest.
def Initialize(self):
self.SetStartDate(2020, 1, 1)
self.SetCash(100000)
self.symbol = self.AddEquity("SPY", Resolution.Daily).Symbol
# Créer un SMA de 50 jours
self.sma_50 = self.SMA(self.symbol, 50)
# Warm-up de 50 jours pour remplir le SMA
self.SetWarmUp(50)
def OnData(self, data):
# Ignorer les données de warm-up
if self.IsWarmingUp:
return
# Vérifier que l'indicateur est prêt
if not self.sma_50.IsReady:
return
# À ce stade, le SMA est valide
sma_value = self.sma_50.Current.ValueConseil : Toujours utiliser SetWarmUp() avec un nombre égal à la période du plus long indicateur.
Vérifier la disponibilité : data.ContainsKey(symbol)
Dans OnData, il faut toujours vérifier que les données sont disponibles pour un symbol donné :
def OnData(self, data):
# ❌ MAUVAIS - peut crasher si les données manquent
price = data[self.symbol].Close
# ✅ BON - vérifier d'abord
if data.ContainsKey(self.symbol):
price = data[self.symbol].Close
else:
return # Pas de données, on attend la prochaine itération4. Indicateurs Techniques Intégrés
Bibliothèque d’indicateurs QuantConnect
QuantConnect intègre une bibliothèque complète d’indicateurs techniques. Plus besoin de coder les formules manuellement !
Principaux indicateurs disponibles
| Indicateur | Code | Description | Utilisation typique |
|---|---|---|---|
| SMA | self.SMA(symbol, period) |
Simple Moving Average | Tendance, support/résistance |
| EMA | self.EMA(symbol, period) |
Exponential Moving Average | Tendance (plus réactif que SMA) |
| RSI | self.RSI(symbol, period) |
Relative Strength Index | Surachat/survente (0-100) |
| MACD | self.MACD(symbol, fast, slow, signal) |
Moving Average Convergence Divergence | Momentum, changements de tendance |
| BB | self.BB(symbol, period, std_dev) |
Bollinger Bands | Volatilité, breakouts |
| ATR | self.ATR(symbol, period) |
Average True Range | Mesure de volatilité |
| STOCH | self.STO(symbol, period) |
Stochastic Oscillator | Momentum, retournements |
La liste complète : QuantConnect Indicators Documentation
Résultat du Backtest QC Cloud — IndicatorsAlgorithm (BT 150fee29c7cc…, projet 31996292)
Sharpe CAGR MaxDD Trades - - - 0 (demo)
Créer et utiliser des indicateurs
# [REFERENCE QC] Code a copier dans main.py QC Lab (non executable ici)
# Création d'indicateurs techniques dans Initialize()
# Note: Code informatif pour QuantConnect IDE
from AlgorithmImports import *
class IndicatorsAlgorithm(QCAlgorithm):
def Initialize(self):
self.SetStartDate(2015, 1, 1)
self.SetEndDate(2024, 12, 31)
self.SetCash(100000)
self.symbol = self.AddEquity("SPY", Resolution.Daily).Symbol
# 1. SMA (Simple Moving Average)
# Moyenne mobile simple sur 20 jours
self.sma_20 = self.SMA(self.symbol, 20, Resolution.Daily)
# 2. EMA (Exponential Moving Average)
# Moyenne mobile exponentielle sur 50 jours (plus réactive)
self.ema_50 = self.EMA(self.symbol, 50)
# 3. RSI (Relative Strength Index)
# Oscillateur de momentum entre 0 et 100
# Classiquement: RSI < 30 = survente, RSI > 70 = surachat
self.rsi = self.RSI(self.symbol, 14)
# 4. MACD (Moving Average Convergence Divergence)
# fast=12, slow=26, signal=9 (paramètres standards)
self.macd = self.MACD(self.symbol, 12, 26, 9)
# 5. Bollinger Bands
# period=20, std_dev=2 (paramètres standards)
self.bb = self.BB(self.symbol, 20, 2)
# Warm-up: utiliser la période du plus long indicateur
self.SetWarmUp(50)
self.Log("Indicators created successfully")
def OnData(self, data):
# Ignorer la période de warm-up
if self.IsWarmingUp:
return
# Vérifier que tous les indicateurs sont prêts
if not (self.sma_20.IsReady and self.ema_50.IsReady and
self.rsi.IsReady and self.macd.IsReady and self.bb.IsReady):
return
# Accéder aux valeurs des indicateurs
sma_value = self.sma_20.Current.Value
ema_value = self.ema_50.Current.Value
rsi_value = self.rsi.Current.Value
# MACD a plusieurs composantes
macd_value = self.macd.Current.Value # MACD line
macd_signal = self.macd.Signal.Current.Value # Signal line
macd_hist = self.macd.Histogram.Current.Value # Histogram
# Bollinger Bands a 3 bandes
bb_upper = self.bb.UpperBand.Current.Value
bb_middle = self.bb.MiddleBand.Current.Value
bb_lower = self.bb.LowerBand.Current.Value
# Afficher les valeurs (debug)
self.Debug(f"SMA(20): {sma_value:.2f} | EMA(50): {ema_value:.2f} | RSI: {rsi_value:.2f}")Exercice 2 : Tableau de bord d’indicateurs
La section précédente montre comment créer et acceder aux indicateurs techniques. Votre mission est de construire un tableau de bord qui combine plusieurs indicateurs pour produire une recommandation synthetique.
Principe
Dans OnData, vous allez calculer un signal composite a partir de 3 indicateurs : - RSI : haussier si entre 40 et 70 - SMA(20) vs prix : haussier si prix > SMA(20) - MACD : haussier si MACD line > Signal line
Le signal composite compte le nombre d’indicateurs haussiers (0 a 3).
Objectif
Completez la méthode OnData pour : 1. Verifier que tous les indicateurs sont prets 2. Evaluer chaque indicateur individuellement 3. Calculer le score composite 4. Logger le résultat avec self.Log()
Indices :
- Indice 1 : Utilisez un compteur simple (
bullish_count = 0) et incrémentez-le pour chaque condition remplie. - Indice 2 : Pour comparer le prix au SMA, utilisez
data[self.symbol].Close > self.sma_20.Current.Value. - Indice 3 : Le MACD haussier se verifie avec
self.macd.Current.Value > self.macd.Signal.Current.Value.
# [REFERENCE QC] Exercice a completer - Code a copier dans main.py QC Lab
# Exercice 2 : Tableau de bord d'indicateurs composites
from AlgorithmImports import *
class IndicatorDashboard(QCAlgorithm):
"""
Affiche un tableau de bord quotidien combinant RSI, SMA et MACD
en un score composite de 0 (tous baissiers) a 3 (tous haussiers).
"""
def Initialize(self):
self.SetStartDate(2023, 1, 1)
self.SetEndDate(2023, 12, 31)
self.SetCash(100000)
self.symbol = self.AddEquity("SPY", Resolution.Daily).Symbol
self.sma_20 = self.SMA(self.symbol, 20, Resolution.Daily)
self.rsi = self.RSI(self.symbol, 14)
self.macd = self.MACD(self.symbol, 12, 26, 9)
self.SetWarmUp(50)
def OnData(self, data):
# TODO etudiant : ignorer le warmup et verifier que tous les indicateurs sont prets
# TODO etudiant : evaluer le RSI (haussier si 40 < rsi < 70)
# TODO etudiant : evaluer le prix vs SMA(20) (haussier si prix > sma)
# TODO etudiant : evaluer le MACD (haussier si macd > signal)
# TODO etudiant : calculer le score composite (0 a 3) et logger avec self.Log()
pass # TODO etudiant : remplacer par votre implementation
print("Exercice a completer : IndicatorDashboard")Vérifier si ready : indicator.IsReady
Un indicateur n’est pas immédiatement prêt après sa création. Il faut attendre qu’il ait reçu assez de données.
# Créer un SMA(50)
self.sma_50 = self.SMA(self.symbol, 50)
# Dans OnData:
if self.sma_50.IsReady:
value = self.sma_50.Current.Value # ✅ Safe
else:
self.Debug("SMA not ready yet") # ⏳ AttendreImportant : Toujours vérifier IsReady avant d’accéder à la valeur, sinon vous risquez d’obtenir des valeurs incorrectes (NaN ou 0).
Accéder à la valeur : indicator.Current.Value
Pour obtenir la valeur actuelle d’un indicateur :
# Pour les indicateurs simples (SMA, EMA, RSI, ATR)
value = self.sma_20.Current.Value
# Pour les indicateurs complexes (MACD, BB, STOCH)
macd_line = self.macd.Current.Value
macd_signal = self.macd.Signal.Current.Value
macd_histogram = self.macd.Histogram.Current.Value
bb_upper = self.bb.UpperBand.Current.Value
bb_middle = self.bb.MiddleBand.Current.Value
bb_lower = self.bb.LowerBand.Current.ValueVisualiser avec self.Plot()
QuantConnect permet de créer des graphiques personnalisés avec self.Plot() :
def OnData(self, data):
if not self.sma_20.IsReady:
return
price = data[self.symbol].Close
# Créer un graphique avec le prix et le SMA
self.Plot("Price vs SMA", "SPY Price", price)
self.Plot("Price vs SMA", "SMA(20)", self.sma_20.Current.Value)
# Créer un graphique séparé pour le RSI
if self.rsi.IsReady:
self.Plot("RSI", "RSI(14)", self.rsi.Current.Value)
self.Plot("RSI", "Overbought", 70) # Ligne de référence
self.Plot("RSI", "Oversold", 30) # Ligne de référenceLes graphiques apparaissent dans les résultats du backtest sous l’onglet “Charts”.
5. Exemple Complet : RSI Mean Reversion
Stratégie RSI Mean Reversion
Nous allons maintenant créer une stratégie complète basée sur le RSI (Relative Strength Index), un oscillateur très populaire.
Principe de la stratégie
Le RSI mesure la force du momentum d’un actif sur une échelle de 0 à 100 :
- RSI < 30 : Zone de survente → Le prix a trop baissé, possibilité de rebond (signal d’achat)
- RSI > 70 : Zone de surachat → Le prix a trop monté, possibilité de correction (signal de vente)
Cette stratégie suppose que les prix ont tendance à revenir vers la moyenne (mean reversion) après des mouvements extrêmes.
Règles de trading
SI RSI < 30 ET pas investi :
ACHETER 100% du capital
SI RSI > 70 ET investi :
VENDRE toute la position
Diagramme de la logique
RSI(14)
│
┌────┴────┐
│ │
< 30 > 70
│ │
OVERSOLD OVERBOUGHT
│ │
BUY SELL
Code complet de la stratégie
Copiez ce code dans l’IDE QuantConnect pour l’exécuter :
# [REFERENCE QC] Code a copier dans main.py QC Lab (non executable ici)
# Stratégie RSI Mean Reversion
# À copier dans l'IDE QuantConnect
from AlgorithmImports import *
class RSIMeanReversion(QCAlgorithm):
"""
Stratégie de retour à la moyenne basée sur le RSI.
Achète lorsque RSI < 30 (survente)
Vend lorsque RSI > 70 (surachat)
"""
def Initialize(self):
# Configuration de base
self.SetStartDate(2015, 1, 1)
self.SetEndDate(2024, 12, 31)
# Capital initial explicite : c'est cette valeur, pas une propriété
# StartingCash inexistante, que OnEndOfAlgorithm relit en fin de run
self.initial_capital = 100_000
self.SetCash(self.initial_capital)
# Ajouter SPY avec résolution journalière
self.symbol = self.AddEquity("SPY", Resolution.Daily).Symbol
# Créer l'indicateur RSI(14)
# 14 est la période standard pour le RSI
self.rsi = self.RSI(self.symbol, 14)
# Warm-up de 14 jours pour initialiser le RSI
self.SetWarmUp(14)
self.Log("RSI Mean Reversion strategy initialized")
self.Log(f"Backtest period: {self.StartDate} to {self.EndDate}")
def OnData(self, data):
# 1. Ignorer la période de warm-up
if self.IsWarmingUp:
return
# 2. Vérifier que le RSI est prêt
if not self.rsi.IsReady:
return
# 3. Vérifier la présence effective d'une barre SPY avant de lire son
# prix : sans ce garde, data[self.symbol].Close lève une erreur sur
# une slice qui ne contient pas de barre pour le symbole
if self.symbol not in data or data[self.symbol] is None:
return
# 4. Obtenir la valeur actuelle du RSI
rsi_value = self.rsi.Current.Value
# 5. Obtenir le prix actuel
price = data[self.symbol].Close
# 6. Visualiser le RSI dans les graphiques
self.Plot("RSI", "RSI(14)", rsi_value)
self.Plot("RSI", "Overbought", 70)
self.Plot("RSI", "Oversold", 30)
# 7. Logique de trading
# Signal d'ACHAT : RSI < 30 (survente) et pas déjà investi
if rsi_value < 30 and not self.Portfolio.Invested:
# Acheter 100% du capital disponible
self.SetHoldings(self.symbol, 1.0)
self.Log(f"BUY: RSI={rsi_value:.2f} | Price=${price:.2f}")
# Signal de VENTE : RSI > 70 (surachat) et actuellement investi
elif rsi_value > 70 and self.Portfolio.Invested:
# Liquider toute la position
self.Liquidate(self.symbol)
self.Log(f"SELL: RSI={rsi_value:.2f} | Price=${price:.2f}")
def OnEndOfAlgorithm(self):
# Afficher les résultats finaux
final_value = self.Portfolio.TotalPortfolioValue
initial_value = self.initial_capital
total_return = ((final_value - initial_value) / initial_value) * 100
self.Log(f"")
self.Log(f"="*50)
self.Log(f"BACKTEST SUMMARY")
self.Log(f"="*50)
self.Log(f"Initial Capital: ${initial_value:,.2f}")
self.Log(f"Final Portfolio Value: ${final_value:,.2f}")
self.Log(f"Total Return: {total_return:.2f}%")
self.Log(f"="*50)Résultat du Backtest QC Cloud — RSIMeanReversion (BT 253cf5ff39f45442b5debdd79586f1aa, projet 31996392, run « RSIMeanReversion-v5-notebook-verbatim » du 2026-09-06) Période 2015-01-01 → 2024-12-31 (2 516 dates négociables) · capital initial explicite
self.initial_capital = 100_000·OnEndOfAlgorithmexécuté jusqu’au callback final
Sharpe CAGR MaxDD Trades 0.291 7.144% 28.8% 10 (20 ordres)
Historique : le run antérieur du même code avant capital explicite est conservé — BT
3b160a654712eb65581558346894dff8(« RSIMeanReversion-v3-nullcheck », 2026-05-22) : 0.291 / 7.145% / 28.7% / 20 ordres. Les deux runs encore antérieurs sans garde de barre SPY (227257a1c92ca09bdfe9c84ad0c5c85f,a55b60f90276cf41092c87f985e6c53b, projet 31996392) sont en Runtime Error : c’est le garde « barre présente » du code ci-dessus qui distingue le run qui complète.
Exercice 3 : Stratégie SMA Crossover
Dans l’exemple précédent, nous avons implemente une stratégie RSI Mean Reversion. Maintenant, a vous de créer une stratégie basee sur le croisement de moyennes mobiles (SMA Crossover), l’une des stratégies de suivi de tendance les plus classiques.
Principe
- Signal d’achat : quand la SMA courte (rapide) passe au-dessus de la SMA longue (lente)
- Signal de vente : quand la SMA courte passe en dessous de la SMA longue
Objectif
Completez la classe SMACrossoverStrategy ci-dessous en implementant la logique de trading dans OnData.
Indices :
- Indice 1 : Stockez l’etat précédent du croisement dans
self.previous_crosspour detecter le changement de signal (passage de “en dessous” a “au-dessus” ou inversement). - Indice 2 : Comparez
sma_fast.Current.Valueavecsma_slow.Current.Valuepour determiner la position relative. - Indice 3 : Utilisez
self.SetHoldings(symbol, 1.0)pour acheter etself.Liquidate(symbol)pour vendre.
# [REFERENCE QC] Exercice a completer - Code a copier dans main.py QC Lab
# Exercice 3 : Strategie SMA Crossover
from AlgorithmImports import *
class SMACrossoverStrategy(QCAlgorithm):
"""
Strategie de suivi de tendance basee sur le croisement de deux SMA.
Achetez quand la SMA rapide (20) croise au-dessus de la SMA lente (50).
Vendez quand la SMA rapide (20) croise en dessous de la SMA lente (50).
"""
def Initialize(self):
self.SetStartDate(2015, 1, 1)
self.SetEndDate(2024, 12, 31)
self.SetCash(100000)
self.symbol = self.AddEquity("SPY", Resolution.Daily).Symbol
# TODO etudiant : creer les deux indicateurs SMA
# self.sma_fast = ...
# self.sma_slow = ...
# TODO etudiant : initialiser la variable de suivi du croisement precedent
# self.previous_cross = ... # True si fast > slow, False sinon, None au debut
self.SetWarmUp(50)
self.Log("SMA Crossover strategy initialized")
def OnData(self, data):
# TODO etudiant : verifier warmup et indicateurs prets
# TODO etudiant : determiner si la SMA rapide est au-dessus de la SMA lente
# TODO etudiant : detecter le croisement (changement d'etat) et executer les ordres
# TODO etudiant : mettre a jour previous_cross
pass # TODO etudiant : remplacer par votre implementation
print("Exercice a completer : SMACrossoverStrategy")Explication détaillée du code
Dans Initialize()
- Dates et capital : Backtest sur 10 ans (2015-2024) — ce sont les dates posées par
SetStartDate(2015, 1, 1)/SetEndDate(2024, 12, 31)— avec 100 000 $ de capital initial rendu explicite parself.initial_capital. Il n’existe pas de propriétéStartingCashlisible surQCAlgorithm:startingCashest un argument deSetCash, pas une propriété publique — le code ci-dessus relit donc sa propre variable. - Security : SPY (ETF S&P 500) avec résolution Daily
- Indicateur RSI(14) : Période standard de 14 jours
- Warm-up : 14 jours pour initialiser le RSI avant le début du backtest
Dans OnData()
- Vérifications préliminaires :
- Skip la période de warm-up (
IsWarmingUp) - Vérifier que le RSI est prêt (
rsi.IsReady) - Vérifier qu’une barre SPY est bien présente dans la slice (
self.symbol in data) avant de lire son prix — les runs sans ce garde terminent en Runtime Error sur le projet QC associé
- Skip la période de warm-up (
- Récupération des données :
- Valeur du RSI :
rsi.Current.Value - Prix de clôture :
data[symbol].Close
- Valeur du RSI :
- Visualisation :
- Tracer le RSI avec les lignes de référence 30/70
- Logique de trading :
- Achat : RSI < 30 ET pas investi →
SetHoldings(symbol, 1.0) - Vente : RSI > 70 ET investi →
Liquidate(symbol)
- Achat : RSI < 30 ET pas investi →
Dans OnEndOfAlgorithm()
- Calcul du rendement total sur le capital initial explicite (
self.initial_capital) - Résumé des performances en fin de run
Lancer dans QuantConnect cloud
Pour tester cette stratégie :
- Connectez-vous à QuantConnect
- Créez un nouveau projet : “New Project” → “Python Algorithm”
- Copiez le code ci-dessus dans
main.py - Lancez le backtest : Cliquez sur “Backtest”
- Analysez les résultats :
- Onglet “Overview” : Rendement total, Sharpe ratio, Drawdown
- Onglet “Charts” : Graphique RSI, Equity curve
- Onglet “Orders” : Tous les trades exécutés
- Onglet “Logs” : Messages de self.Log()
Analyser les résultats
Une fois le backtest terminé, observez :
| Métrique | Description | Valeur typique |
|---|---|---|
| Total Return | Rendement total sur la période | Variable selon marché |
| Sharpe Ratio | Rendement ajusté au risque | > 1 = bon, > 2 = excellent |
| Max Drawdown | Plus grosse perte depuis un pic | < 20% = acceptable |
| Win Rate | % de trades gagnants | 50-60% typique pour mean reversion |
| Number of Trades | Nombre total de trades | Dépend de la volatilité |
Questions à se poser : - La stratégie bat-elle le buy-and-hold de SPY ? - Y a-t-il des périodes où elle performe mal ? - Le Sharpe ratio justifie-t-il le risque ? - Combien de trades sont exécutés par an ?
6. Méthodes QCAlgorithm Essentielles
Méthodes de trading
Voici les méthodes les plus utilisées pour exécuter des ordres :
| Méthode | Signature | Usage | Exemple |
|---|---|---|---|
| SetHoldings | SetHoldings(symbol, percentage) |
Acheter/vendre pour atteindre X% du portfolio | SetHoldings(spy, 0.5) = 50% en SPY |
| Liquidate | Liquidate(symbol) |
Vendre toute la position d’un actif | Liquidate(spy) |
| MarketOrder | MarketOrder(symbol, quantity) |
Ordre au marché (exécution immédiate) | MarketOrder(spy, 100) = acheter 100 actions |
| LimitOrder | LimitOrder(symbol, quantity, limit_price) |
Ordre limite (exécution si prix atteint) | LimitOrder(spy, 100, 450) |
| StopMarketOrder | StopMarketOrder(symbol, quantity, stop_price) |
Stop loss (vendre si prix descend) | StopMarketOrder(spy, -100, 440) |
Différences entre les types d’ordres
SetHoldings vs MarketOrder
# SetHoldings - Spécifie un % du portfolio
self.SetHoldings(self.symbol, 0.8) # Acheter pour avoir 80% du portfolio en SPY
# QuantConnect calcule automatiquement le nombre d'actions à acheter/vendre
# MarketOrder - Spécifie un nombre d'actions
self.MarketOrder(self.symbol, 100) # Acheter exactement 100 actionsConseil : Utilisez SetHoldings pour la simplicité, MarketOrder pour un contrôle précis.
MarketOrder vs LimitOrder
# MarketOrder - Exécution immédiate au meilleur prix disponible
self.MarketOrder(self.symbol, 100)
# Avantage: Garantit l'exécution
# Inconvénient: Pas de contrôle sur le prix (slippage possible)
# LimitOrder - Exécution uniquement si prix <= limite (pour achat)
self.LimitOrder(self.symbol, 100, 450)
# Avantage: Contrôle du prix maximum
# Inconvénient: Peut ne jamais s'exécuterExemples d’utilisation des méthodes
# [REFERENCE QC] Code a copier dans main.py QC Lab (non executable ici)
# Exemples d'utilisation des méthodes de trading
# Code informatif pour QuantConnect IDE
from AlgorithmImports import *
class TradingMethodsExample(QCAlgorithm):
def Initialize(self):
self.SetStartDate(2015, 1, 1)
self.SetCash(100000)
self.symbol = self.AddEquity("SPY", Resolution.Daily).Symbol
def OnData(self, data):
price = data[self.symbol].Close
# EXEMPLE 1: SetHoldings - Acheter pour 50% du portfolio
if not self.Portfolio.Invested:
self.SetHoldings(self.symbol, 0.5)
self.Log(f"SetHoldings: Acheté pour 50% du portfolio")
# EXEMPLE 2: MarketOrder - Acheter 100 actions au marché
# self.MarketOrder(self.symbol, 100)
# self.Log(f"MarketOrder: Acheté 100 actions à ~${price:.2f}")
# EXEMPLE 3: LimitOrder - Acheter 100 actions si prix <= 450$
# self.LimitOrder(self.symbol, 100, 450)
# self.Log(f"LimitOrder: Ordre d'achat de 100 actions à 450$ max")
# EXEMPLE 4: StopMarketOrder - Vendre si prix descend sous 440$
# if self.Portfolio[self.symbol].Quantity > 0:
# quantity = -self.Portfolio[self.symbol].Quantity
# self.StopMarketOrder(self.symbol, quantity, 440)
# self.Log(f"StopMarketOrder: Stop loss à 440$")
# EXEMPLE 5: Liquidate - Vendre toute la position
# if self.Portfolio.Invested:
# self.Liquidate(self.symbol)
# self.Log(f"Liquidate: Position fermée à ${price:.2f}")Résultat du Backtest QC Cloud — TradingMethodsExample (BT 0ed23be746101678a8ecbbd5c276c8fb, projet 31996441, run « TradingMethodsExample-v2-fixed » du 2026-05-22) Run historique de la classe ci-dessus (départ 2015-01-01, sans date de fin — 2 264 dates négociables), conservé tel quel. « Trades = 0 » compte les transactions clôturées : l’unique
SetHoldings(0.5)du code n’est jamais clôturé avant la fin du run (1 ordre ouvert).
Sharpe CAGR MaxDD Trades 0.379 7.139% 21.7% 0 (1 ordre ouvert)
Méthodes de logging et visualisation
| Méthode | Usage | Exemple |
|---|---|---|
| Log | Message dans les résultats du backtest | self.Log("Trade executed") |
| Debug | Message dans la console de debug | self.Debug(f"Price: {price}") |
| Plot | Créer un graphique personnalisé | self.Plot("Chart", "Series", value) |
| Error | Logger une erreur | self.Error("Something went wrong") |
Exemple de visualisation avancée
def OnData(self, data):
price = data[self.symbol].Close
# Graphique 1: Prix et moyennes mobiles
self.Plot("Price Analysis", "SPY Price", price)
if self.sma_20.IsReady:
self.Plot("Price Analysis", "SMA(20)", self.sma_20.Current.Value)
if self.sma_50.IsReady:
self.Plot("Price Analysis", "SMA(50)", self.sma_50.Current.Value)
# Graphique 2: RSI avec zones
if self.rsi.IsReady:
self.Plot("Momentum", "RSI", self.rsi.Current.Value)
self.Plot("Momentum", "Overbought", 70)
self.Plot("Momentum", "Oversold", 30)
# Graphique 3: Position size
holdings = self.Portfolio[self.symbol].Quantity
self.Plot("Portfolio", "SPY Holdings", holdings)Méthodes d’accès au Portfolio
# Vérifier si on a une position
is_invested = self.Portfolio.Invested
has_spy = self.Portfolio[self.symbol].Invested
# Quantité détenue
quantity = self.Portfolio[self.symbol].Quantity
# Valeur de la position
holding_value = self.Portfolio[self.symbol].HoldingsValue
# Prix moyen d'achat
avg_price = self.Portfolio[self.symbol].AveragePrice
# Profit/loss non réalisé
unrealized_pnl = self.Portfolio[self.symbol].UnrealizedProfit
# Valeur totale du portfolio
total_value = self.Portfolio.TotalPortfolioValue
# Cash disponible
cash = self.Portfolio.CashConclusion
Récapitulatif
Dans ce notebook, nous avons exploré les fondamentaux de la plateforme QuantConnect :
1. QCAlgorithm Lifecycle
- Initialize() : Configuration initiale (1 fois)
- OnData() : Logique de trading (à chaque nouvelle donnée)
- OnEndOfAlgorithm() : Finalisation (1 fois)
2. Gestion des Securities
- Ajouter des actifs avec
AddEquity(),AddCrypto(),AddForex() - Utiliser des objets
Symbol(pas des strings) - Configurer le warm-up avec
SetWarmUp()
3. Indicateurs Techniques
- Bibliothèque complète : SMA, EMA, RSI, MACD, BB, etc.
- Toujours vérifier
indicator.IsReady - Accéder aux valeurs avec
indicator.Current.Value
4. Stratégie RSI Mean Reversion
- Exemple complet d’une stratégie fonctionnelle
- Achat en zone de survente (RSI < 30)
- Vente en zone de surachat (RSI > 70)
5. Méthodes Essentielles
- Trading :
SetHoldings(),Liquidate(),MarketOrder(),LimitOrder() - Logging :
Log(),Debug(),Plot() - Portfolio :
Portfolio.Invested,Portfolio[symbol].Quantity
Tableau récapitulatif des méthodes principales
| Catégorie | Méthode | Usage |
|---|---|---|
| Configuration | SetStartDate(), SetEndDate(), SetCash() |
Définir période et capital |
| Securities | AddEquity(), AddCrypto(), AddForex() |
Ajouter des actifs |
| Indicateurs | SMA(), EMA(), RSI(), MACD(), BB() |
Créer des indicateurs |
| Trading | SetHoldings(), Liquidate(), MarketOrder() |
Exécuter des ordres |
| Logging | Log(), Debug(), Plot() |
Debugger et visualiser |
| Portfolio | Portfolio.Invested, Portfolio[symbol].Quantity |
Consulter l’état |
Prochaine étape : QC-Py-03-Data-Management
Dans le prochain notebook, nous approfondirons la gestion des données avec :
- History API : Récupérer des données historiques
- Consolidators : Agréger des données (ex: minute → hour)
- Scheduled Events : Exécuter du code à des heures précises
- Universe Sélection : Trader plusieurs actifs dynamiquement
Exercice suggéré
Modifiez la stratégie RSI Mean Reversion pour :
- Ajuster les seuils : Testez RSI < 25 et RSI > 75
- Ajouter un stop loss : Vendez si perte > 5%
- Tester un autre actif : Remplacez SPY par QQQ (Nasdaq 100)
- Combiner avec une moyenne mobile : N’achetez que si prix > SMA(50)
- Créer une stratégie MACD crossover :
- Achetez quand MACD croise au-dessus du signal
- Vendez quand MACD croise en-dessous du signal
Ressources supplémentaires
- QCAlgorithm API Reference
- Supported Indicators
- Strategy Library - Exemples de stratégies complètes
- Documentation officielle
Félicitations ! Vous maîtrisez maintenant les fondamentaux de la plateforme QuantConnect. Vous êtes prêt à créer vos propres stratégies de trading algorithmique.