[QC CLOUD] Ce notebook utilise QuantBook / AlgorithmImports et necessite un environnement QuantConnect Cloud. Les cellules de code ne sont pas executables localement.
Construire un portfolio multi-classes d’actifs (equities, bonds, commodities, crypto)
Analyser les correlations entre actifs et detecter les regimes de marche
Implementer plusieurs stratégies d’allocation (equal weight, risk parity, tactical)
Comprendre les principes de hedging avec options et futures
Créer un All-Weather Portfolio inspire de Ray Dalio
Prerequisites
Notebooks QC-Py-01 a 07 completes
Comprehension de base de la théorie de portefeuille
Familiarite avec numpy et pandas
Structure du Notebook
Introduction Multi-Asset (10 min)
Ajouter Multiple Asset Classes (10 min)
Calcul des Correlations (10 min)
Detection de Regime (10 min)
Equal Weight Portfolio (10 min)
Risk Parity (10 min)
Tactical Asset Allocation (10 min)
Hedging avec Options (10 min)
Hedging avec Futures (10 min)
All-Weather Portfolio (25 min)
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.
Pourquoi ce marqueur [REFERENCE QC Cloud] : les cellules de ce notebook sont des gabarits QuantConnect à copier dans main.py d’un projet QC Lab. Elles ne s’exécutent pas localement (kernel Jupyter classique), mais illustrent la mécanique de chaque stratégie.
Trois familles de cellules dans ce notebook : 1. Gabarits QC (marquées [REFERENCE QC]) — déployés tels quels sur QC Cloud via lean cloud push. 2. Backtests référentiels (marquées [BACKTEST QC Cloud]) — résultats de backtests précédents, avec Sharpe/CAGR/DD rapportés. 3. Notes pédagogiques (marquées > **Note technique** ou > **Note pedagogique**) — observations qualitatives sur les patterns observés.
Coût d’un backtest QC : ~5-15 min pour 10 ans daily, ~30-60 min pour intraday. Pour itérer sur la logique, utilisez d’abord les démos Python locales (cf. QC-Py-04 Research Workflow).
Mode d’emploi : Ce notebook a deux parties : 1. Sections analyse/ML (pandas, sklearn, matplotlib) : executables en Jupyter local 2. Sections integration QC (classes QCAlgorithm) : code de reference a copier dans main.py de votre projet QC Lab
Les cellules QCAlgorithm sont marquees # [REFERENCE QC] et ne sont pas executables localement.
Introduction
Ce notebook couvre les stratégies multi-actifs sur QuantConnect : comment combiner actions, obligations, commodities, et FX dans un portefeuille cohérent.
Pré-requis : - QC-Py-01 à QC-Py-07 (setup → platform → data → universes → options → multi-asset primitives → futures/forex). - Connaissance de base en statistiques (corrélation, volatilité). - Familiarité avec les notions de risque (variance, drawdown).
Objectifs pédagogiques : 1. Comprendre la matrice de corrélation entre classes d’actifs. 2. Maîtriser les méthodes d’allocation : equal weight, risk parity, tactical asset allocation, all-weather. 3. Utiliser options (protective put, collar) pour hedger un portefeuille. 4. Hedger avec futures (calcul du contrat, basis risk). 5. Implémenter une stratégie complète combinant ces éléments.
Public cible : ingénieurs quant junior qui veulent dépasser les stratégies mono-actif pour gérer un portefeuille réaliste.
Partie 1 : Fondations Multi-Asset (20 min)
1.1 Pourquoi Diversifier ?
La diversification est souvent appelee “the only free lunch in finance” (Harry Markowitz). L’idee centrale est simple : en combinant des actifs qui ne bougent pas ensemble, on peut reduire le risque sans sacrifier le rendement.
Concepts Cles
Concept
Definition
Importance
Correlation
Mesure de co-mouvement entre actifs (-1 a +1)
Faible correlation = meilleure diversification
Risk-Adjusted Returns
Rendement par unite de risque (ex: Sharpe Ratio)
Objectif : maximiser rendement/risque
Modern Portfolio Theory
Framework mathematique pour optimiser allocation
Frontiere efficiente, portfolio optimal
Classes d’Actifs Principales
Classe
Exemples QuantConnect
Caractéristiques
Equities
SPY, QQQ, IWM
Croissance, volatilite moyenne-haute
Bonds
TLT, IEF, BND
Revenus, faible volatilite, hedge contre recession
Commodities
GLD, USO, DBC
Couverture inflation, decorrelation
Forex
EURUSD, USDJPY
Liquidite, hedging devises
Crypto
BTCUSD, ETHUSD
Haute volatilite, potentiel croissance
La Magie de la Diversification
Considerons deux actifs avec le même rendement espere de 10% et volatilite de 20%.
Correlation
Volatilite Portfolio 50/50
Reduction Risque
+1.0
20%
0%
+0.5
17.3%
13.5%
0.0
14.1%
29.5%
-0.5
10%
50%
-1.0
0%
100%
Point cle : Même avec une correlation de 0, on reduit la volatilite de pres de 30% sans toucher au rendement espere !
Ancres savantes – Markowitz, H. (1952), Portfolio Sélection, The Journal of Finance 7(1):77-91. DOI 10.1111/j.1540-6261.1952.tb01525.x (théorie moderne du portefeuille MPT, frontiere efficiente, aphorisme du “free lunch”) ; Sharpe, W.F. (1966), Mutual Fund Performance, The Journal of Business 39(1):119-138. DOI 10.1086/294846 (ratio reward-to-variability / Sharpe Ratio).
Vue d’ensemble du notebook
Ce notebook est structuré en 5 parties progressives + une conclusion :
Partie
Thème
Durée
Compétence visée
1
Multi-Asset de base
25 min
Ajouter plusieurs classes
2
Corrélation + régime
30 min
Détection de stress
3
Méthodes d’allocation
30 min
Equal/RP/TAA/All-Weather
4
Hedging options + futures
25 min
Protective put, collar, ES
5
Stratégie complète
20 min
Assemblage final
Conseil de lecture : - Si vous connaissez déjà Markowitz/Equal Weight : sautez à la partie 2 (corrélation) puis revenez aux exercices. - Si vous voulez juste hedger un portefeuille existant : parties 4 + 5 directement. - Pour une compréhension complète : lecture linéaire 1 → 5.
Pré-requis technique : Python intermédiaire, notions de statistiques (corrélation, volatilité), familiarité avec QC-Py-01 à QC-Py-07.
1.2 Ajouter Multiple Asset Classes
Creons un portfolio diversifie avec QuantConnect. Nous utiliserons des ETFs comme proxies pour différentes classes d’actifs (accessibles aux investisseurs particuliers).
Pour ajouter plusieurs classes d’actifs dans une même stratégie, on multiplie les appels AddEquity/AddFuture/AddForex dans Initialize. Chaque appel enregistre un Symbol dans le framework.
Trois principes : 1. Résolution uniforme : tous les assets en daily pour cohérence temporelle. Mixing daily/intraday complique le backtest. 2. Densité de données : SPY/TLT/GLD sont denses (quotidien historique profond), EEM/VIX moins (depuis 1990/1992). 3. Coût de rebalance : chaque asset a son spread/slippage. Plus d’actifs = plus de coûts de transaction à chaque rebalance.
# Imports QuantConnectfrom AlgorithmImports import*# Imports pour analyseimport numpy as npimport pandas as pdimport matplotlib.pyplot as pltimport seaborn as snsfrom datetime import datetime, timedelta# Configuration matplotlibplt.style.use('seaborn-v0_8-darkgrid')%matplotlib inlineprint("Imports reussis")
Imports reussis
# [REFERENCE QC] Code a copier dans main.py QC Lab (non executable ici)# Classe pour multi-asset portfolioclass MultiAssetPortfolioAlgorithm(QCAlgorithm):""" Exemple de configuration multi-asset portfolio. Cette classe montre comment ajouter differentes classes d'actifs. """def Initialize(self):self.SetStartDate(2015, 1, 1)self.SetEndDate(2024, 12, 31)self.SetCash(100000)# === EQUITIES ===# Large-cap US (proxy S&P 500)self.spy =self.AddEquity("SPY", Resolution.Daily).Symbol# Tech heavy (proxy Nasdaq)self.qqq =self.AddEquity("QQQ", Resolution.Daily).Symbol# === BONDS (via ETF) ===# Long-term Treasury (20+ years) - sensible aux tauxself.tlt =self.AddEquity("TLT", Resolution.Daily).Symbol# Intermediate Treasury (7-10 years) - moins volatileself.ief =self.AddEquity("IEF", Resolution.Daily).Symbol# === COMMODITIES (via ETF) ===# Gold - valeur refuge, hedge inflationself.gld =self.AddEquity("GLD", Resolution.Daily).Symbol# Oil - cycle economique, energieself.uso =self.AddEquity("USO", Resolution.Daily).Symbol# === CRYPTO ===# Bitcoin - disponible via Coinbase/GDAX sur QuantConnectself.btc =self.AddCrypto("BTCUSD", Resolution.Daily).Symbol# Stocker tous les symboles pour faciliter les operationsself.symbols = {'equities': [self.spy, self.qqq],'bonds': [self.tlt, self.ief],'commodities': [self.gld, self.uso],'crypto': [self.btc] }self.all_symbols = [self.spy, self.qqq, self.tlt, self.ief, self.gld, self.uso, self.btc]# Afficher la structureprint("Structure Multi-Asset Portfolio:")print("="*50)print("\nEquities:")print(" - SPY: S&P 500 ETF (large-cap US)")print(" - QQQ: Nasdaq 100 ETF (tech-heavy)")print("\nBonds:")print(" - TLT: 20+ Year Treasury Bond ETF")print(" - IEF: 7-10 Year Treasury Bond ETF")print("\nCommodities:")print(" - GLD: Gold ETF")print(" - USO: United States Oil Fund")print("\nCrypto:")print(" - BTCUSD: Bitcoin (via Coinbase)")
Résultats du backtest QC Cloud (MultiAssetPortfolioAlgorithm)
La diversification réduit le risque idiosyncratique d’un actif spécifique au profit du risque systématique de la classe d’actifs. Markowitz (1952) l’a formalisé : pour un niveau de rendement donné, un portefeuille diversifié a un risque inférieur à la moyenne pondérée des risques individuels.
Trois nombres clés à retenir : - Corrélation = 1 : diversification nulle (le portefeuille est l’actif individuel). - Corrélation = 0 : diversification maximale (risque réduit par √N si les actifs sont indépendants). - Corrélation = -1 : diversification parfaite (le risque peut être annulé en combinant long/short).
En pratique : la corrélation entre actions (SPY/EEM) est ~0.7-0.9 en période calme, ~0.95 en krach. Entre actions et obligations (SPY/TLT), elle est ~-0.3 à +0.4 selon le régime. Entre toutes les classes d’actifs, la corrélation moyenne est ~0.4-0.6 en période normale.
Lecture du résultat : MultiAssetPortfolioAlgorithm
Ce que le bandeau dit réellement : 3653 jours de trading couverts, 0 ordre et 0 trade. Un backtest sans trade ne produit aucune métrique de performance — les zéros du bandeau ne sont pas une contre-performance, c’est l’absence de toute exécution.
Pourquoi : MultiAssetPortfolioAlgorithm est un algorithme de structure — il enregistre cinq Symbols (SPY/TLT/GLD/EEM + proxy VIX) dans Initialize et documente le pattern d’ajout multi-classes. Aucune règle d’allocation n’est branchée sur ces Symbols dans le code de référence, donc rien ne se déclenche.
Conséquence pédagogique : ce notebook ne mesure pas la diversification ici. La mesure existe plus loin, sur un algorithme qui trade réellement — EqualWeightPortfolioAlgorithm (cellule #35 : CAGR 7.145%, Sharpe 0.359, MaxDD 19.400%, 203 trades). C’est cette cellule-là qu’il faut lire pour la performance de l’allocation équipondérée, pas celle-ci.
Ce qui reste valide ici : l’anatomie du multi-asset (proxies ETF vs sous-jacents, résolution uniforme, coût de rebalance par actif) — c’est un livrable de structure, pas de performance.
Structure Multi-Asset dans QuantConnect
Cette classe MultiAssetPortfolioAlgorithm illustre les patterns fondamentaux pour travailler avec plusieurs classes d’actifs :
Patterns QC Demonstrés : - AddEquity() : Pour ETFs equities et bonds (TLT, IEF) - AddCrypto() : Pour crypto-monnaies (BTCUSD via Coinbase) - Symbol objects : Références persistantes pour les ordres
Proxies vs Actifs Sous-jacents : Les ETFs sont des “proxies” accessibles aux particuliers : - TLT proxy pour obligations long-terme (vs futures TBT) - GLD proxy pour or physique (vs futures GC) - USO proxy pour pétrole (vs futures CL)
En production : Les institutions utilisent souvent les futures directement pour une meilleure efficacité de capital et des coûts de transaction plus faibles.
Note technique : - Les ETFs (SPY, TLT, GLD, etc.) sont des “proxies” qui permettent aux investisseurs particuliers d’acceder a différentes classes d’actifs - La crypto est disponible via la bourse Coinbase/GDAX sur QuantConnect (tier gratuit) - En production, on pourrait aussi utiliser des futures pour commodities et bonds (plus efficient mais requiert plus de capital)
Note technique : résolution temporelle uniforme
Important : tous les assets dans une stratégie multi-actifs doivent être en même résolution temporelle (daily, hourly, etc.). Mixing daily et intraday crée des désynchronisations : à un moment donné, certains assets ont bougé et d’autres non, ce qui rend les décisions de rebalance ambiguës.
Si on a besoin de mix daily/intraday (e.g. SPY daily + futures intraday), il faut resynchroniser via une fenêtre d’agrégation (ex: futures intraday agrégés en daily à 16h avant la décision de rebalance).
Coût-bénéfice : la synchronisation ajoute 5-10% de complexité au code, mais évite les bugs subtils où un asset trade à J+1 alors que les autres sont à J.
Partie 2 : Correlations et Analyse (20 min)
2.1 Calcul des Correlations
La matrice de correlation nous permet de comprendre comment les actifs bougent les uns par rapport aux autres. C’est fondamental pour construire un portfolio bien diversifie.
Trois types de structures multi-asset
Il existe trois façons principales de structurer un portefeuille multi-actifs sur QuantConnect :
Equal Weight — chaque actif reçoit le même poids (1/N). Avantage : simplicité, robuste. Inconvénient : ne tient pas compte du risque.
Risk Parity — chaque actif reçoit un poids inverse à sa volatilité. Avantage : equalisation de la contribution au risque. Inconvénient : suppose que la volatilité est stationnaire.
Markowitz Mean-Variance — chaque actif reçoit un poids optimisé pour maximiser le ratio de Sharpe. Avantage : théoriquement optimal. Inconvénient : très sensible aux estimations d’entrée.
Stratégies dérivées : Tactical Asset Allocation (TAA) modifie dynamiquement les poids en fonction de signaux de momentum. All-Weather (Bridgewater) utilise un mix d’actifs avec des pondérations fixes basées sur le risque macro-économique.
# Fonction pour calculer la matrice de correlationdef calculate_correlation_matrix(algorithm, symbols, lookback=60):""" Calcule la matrice de correlation entre plusieurs actifs. Parameters: ----------- algorithm : QCAlgorithm instance symbols : list of Symbol objects lookback : int, nombre de jours pour le calcul Returns: -------- pd.DataFrame : matrice de correlation """# Recuperer l'historique history = algorithm.History(symbols, lookback, Resolution.Daily)# Extraire les prix de cloture closes = history['close'].unstack(level=0)# Calculer les returns journaliers returns = closes.pct_change().dropna()# Calculer la matrice de correlation correlation_matrix = returns.corr()return correlation_matrix, returnsprint("Fonction calculate_correlation_matrix() definie")
Fonction calculate_correlation_matrix() definie
Analyse de Correlation en Production
La classe CorrelationAnalysisAlgorithm montre comment intégrer l’analyse de corrélation dans un algorithme live.
Patterns QC Demonstrés : - Schedule.On() : Exécution périodique (mensuelle ici) - History[symbols] : Récupération multi-symbols en un appel - unstack(level=0) : Pivot DataFrame multi-index en colonnes
Utilisation Pratique : - Diversification monitoring : Vérifier que les corrélations n’augmentent pas trop - Regime detection : Corrélation SPY-TLT fortement négative = stress - Rebalancing triggers : Ajuster allocation si corrélations changent
Performance : Pour des portfolios avec 50+ actifs, le calcul de corrélation peut devenir coûteux. Considérer un calcul quotidien/semaine plutôt que mensuel.
# [REFERENCE QC] Code a copier dans main.py QC Lab (non executable ici)# Exemple d'utilisation dans un algorithmeclass CorrelationAnalysisAlgorithm(QCAlgorithm):def Initialize(self):self.SetStartDate(2015, 1, 1)self.SetCash(100000)# Ajouter actifsself.symbols = [self.AddEquity("SPY", Resolution.Daily).Symbol,self.AddEquity("TLT", Resolution.Daily).Symbol,self.AddEquity("GLD", Resolution.Daily).Symbol,self.AddEquity("QQQ", Resolution.Daily).Symbol, ]# Programmer calcul mensuel des correlationsself.Schedule.On(self.DateRules.MonthStart("SPY"),self.TimeRules.AfterMarketOpen("SPY", 30),self.AnalyzeCorrelations )def AnalyzeCorrelations(self):"""Analyse les correlations mensuellement.""" history =self.History(self.symbols, 60, Resolution.Daily)if history.empty:return# Calculer returns closes = history['close'].unstack(level=0) returns = closes.pct_change().dropna()# Matrice de correlation corr_matrix = returns.corr()# Log les correlations importantesself.Debug(f"\n{self.Time.date()} - Correlation Matrix:")for i, sym1 inenumerate(self.symbols):for j, sym2 inenumerate(self.symbols):if i < j: # Eviter doublons corr = corr_matrix.iloc[i, j]self.Debug(f" {sym1.Value}/{sym2.Value}: {corr:.2f}")print("CorrelationAnalysisAlgorithm defini")
Résultats du backtest QC Cloud (CorrelationAnalysisAlgorithm)
La corrélation entre deux actifs varie dans le temps selon le régime de marché. En particulier :
Périodes calmes : corrélations modérées (~0.4-0.6 entre SPY/TLT).
Périodes de stress : corrélations convergent vers 1 (toutes les actions chutent ensemble, les obligations montent mais le hedge devient partiel).
Implication pour le risk management : un hedge SPY/TLT qui fonctionne en 2017-2019 peut échouer en mars 2020 (toutes les classes d’actifs vend en même temps, sauf cash). C’est pourquoi les hedgeurs institutionnels utilisent des multi-hedges (options + futures + cash) plutôt qu’un hedge simple.
Lecture du résultat : CorrelationAnalysisAlgorithm
Les zéros du bandeau sont ici structurels, pas un échec : CorrelationAnalysisAlgorithm est un algorithme d’analyse. Il calcule une matrice de corrélation glissante 60 jours et la journalise ; il ne passe aucun ordre, donc « Orders: 0 / Trades: 0 / Sharpe: 0 » est le résultat attendu de sa nature, et non une mesure de performance.
Ce qu’un tel run produit : une série temporelle de corrélations, pas un P&L. La lecture utile est donc dans la forme de cette série — c’est l’objet de la cellule #19 (heatmap sur données typiques) et du §2.2 (rolling correlations) qui suivent.
Point de méthode : un bandeau de performance à zéro sur un algorithme d’analyse ne doit jamais être lu comme « la stratégie ne marche pas » ; inversement, un algorithme d’analyse ne démontre rien sur la performance. Deux registres distincts — ne pas confondre les deux à la lecture d’un rapport QC Cloud.
Sur la convergence des corrélations en stress (0.85-0.95 entre actions en 2020 et 2022) : c’est un fait documenté de la littérature, que ce run ne mesure pas — la cellule ne le revendique donc pas comme un résultat de backtest.
Visualisation de la Matrice de Correlation
En mode recherche (QuantBook), on peut visualiser la matrice de correlation avec une heatmap.
La matrice de corrélation est un objet central en analyse multi-actifs. Elle résume en N×N nombres les interdépendances entre N actifs.
Bonne visualisation : un heatmap (carte de chaleur) où chaque case est colorée selon la valeur de corrélation. Une palette divergente (rouge négatif, blanc zéro, bleu positif) rend les patterns immédiatement lisibles.
Exemple SPY/TLT/GLD/EEM : - SPY-TLT : -0.3 à +0.4 (hedge traditionnel). - SPY-GLD : 0.0 à 0.3 (or = hedge partiel). - SPY-EEM : 0.7 à 0.9 (actions corrélées). - TLT-GLD : -0.2 à 0.3 (peu de lien).
Lecture critique : une matrice calculée sur toute la période masque les régimes. Toujours accompagner d’une rolling correlation (cf. §2.2) pour voir l’évolution temporelle.
Phénomènes de Rolling Correlation
Le graphique montre deux zones importantes :
Période Normale : - Corrélation SPY-TLT : ~-0.2 à -0.3 - Comportement “standard” : equities et bonds bougent indépendamment
Période de Stress (zone rouge) : - Corrélation chute à ~-0.6 ou plus bas - “Flight to quality” intense : investisseurs vendent equities, achètent bonds - Relation inverse se renforce
Implications Trading : 1. Détection de crise : Corrélation < -0.5 = signal de stress marché 2. Opportunité rebalancing : Après stress, corrélations reviennent à la normale 3. Hedge dynamique : Augmenter allocation bonds quand corrélation devient très négative
Note : En crise de liquidité extrême (2008, COVID 2020), les corrélations peuvent temporairement devenir positives (“tout baisse ensemble”). Ces périodes sont rares mais dangereuses pour les stratégies de diversification classiques.
# Exemple de visualisation (donnees simulees pour illustration)# En production, utilisez qb.History() avec QuantBook# Donnees de correlation typiques entre classes d'actifsassets = ['SPY', 'QQQ', 'TLT', 'IEF', 'GLD', 'USO', 'BTC']corr_data = np.array([ [1.00, 0.92, -0.35, -0.20, 0.05, 0.40, 0.30], # SPY [0.92, 1.00, -0.30, -0.15, 0.00, 0.35, 0.35], # QQQ [-0.35, -0.30, 1.00, 0.85, 0.25, -0.10, -0.15], # TLT [-0.20, -0.15, 0.85, 1.00, 0.20, -0.05, -0.10], # IEF [0.05, 0.00, 0.25, 0.20, 1.00, 0.15, 0.20], # GLD [0.40, 0.35, -0.10, -0.05, 0.15, 1.00, 0.25], # USO [0.30, 0.35, -0.15, -0.10, 0.20, 0.25, 1.00], # BTC])corr_df = pd.DataFrame(corr_data, index=assets, columns=assets)# Visualisation heatmapplt.figure(figsize=(10, 8))sns.heatmap(corr_df, annot=True, cmap='RdYlGn_r', center=0, vmin=-1, vmax=1, fmt='.2f', square=True, linewidths=0.5, cbar_kws={'label': 'Correlation'})plt.title('Matrice de Correlation Multi-Asset\n(Donnees illustratives)', fontsize=14, fontweight='bold')plt.tight_layout()plt.show()print("\nObservations cles:")print("- SPY/QQQ: Tres correles (0.92) - meme classe d'actifs")print("- SPY/TLT: Correlation negative (-0.35) - 'flight to quality'")print("- GLD: Faible correlation avec tout - bon diversificateur")print("- BTC: Correlation moderee - classe d'actifs emergente")
Observations cles:- SPY/QQQ: Tres correles (0.92) - meme classe d'actifs- SPY/TLT: Correlation negative (-0.35) - 'flight to quality'- GLD: Faible correlation avec tout - bon diversificateur- BTC: Correlation moderee - classe d'actifs emergente
Interprétation de la Matrice de Corrélation
La heatmap révèle des patterns importants pour la construction de portefeuille :
Corrélations Élevées (> 0.8) - À éviter ensemble : - SPY/QQQ (0.92) : Même classe d’actifs (equities US) - TLT/IEF (0.85) : Tous deux Treasury bonds
Corrélations Négatives - Excellent pour diversification : - SPY/TLT (-0.35) : “Flight to quality” classique - QQQ/TLT (-0.30) : Tech vs long-term rates
Faible Corrélation - Bons diversificateurs : - GLD avec tout : L’or se comporte différemment - BTC avec equities (~0.3) : Classe d’actifs émergente
Implications pour Allocation : 1. Ne pas surpondérer SPY ET QQQ ensemble (redondant) 2. TLT + IEF offrent une bonne couverture equities 3. GLD apporte une vraie diversification 4. BTC peut compléter un portfolio mais avec volatilité élevée
Règle pratique : Pour une vraie diversification, chercher des corrélations < 0.5 entre les composants principaux du portfolio.
Détection de Régime en Production
RegimeDetectionAlgorithm démontre une approche pratique de l’adaptation au régime de marché.
Logique de Détection :
if correlation <-0.5: regime ="stress"# Flight to quality prononcéelif correlation >0.2: regime ="liquidity_crisis"# Tout baisse ensemble (rare)else: regime ="normal"
Allocations Adaptatives :
Régime
SPY
TLT
GLD
Rationale
Normal
60%
30%
10%
Allocation équilibrée
Stress
30%
50%
20%
Flight to quality
Liquidity Crisis
20%
30%
50%
Cash déguisé en or
Limitations de cette Approche : 1. Seuil arbitraire : -0.5 pourrait ne pas être optimal pour toutes les périodes 2. Lag : Corrélation sur 60 jours = détection retardée 3. Whipsaw : Changements fréquents de régime en marché latéral
Améliorations Possibles : - Utiliser des modèles HMM (Hidden Markov Models) - Ajouter le VIX comme indicateur de volatilité - Multi-indicateurs : momentum, volatilité, spread de crédit
Mise en garde : Les backtests de stratégies de régime doivent être particulièrement vigilants au look-ahead bias. La corrélation utilisée pour la décision ne doit inclure que des données antérieures au point de décision.
2.2 Rolling Correlations
Les correlations ne sont pas statiques. Elles evoluent dans le temps, notamment en periode de stress.
Phenomenes observes : - En periode normale : correlations relativement stables - En crise : correlations equities augmentent (“tout baisse ensemble”) - Flight to quality : equities down, bonds (TLT) up
Une rolling correlation est la corrélation calculée sur une fenêtre glissante de N observations (typiquement 60 jours = 3 mois en daily).
Trois usages : 1. Détection de régime : quand la corrélation passe au-dessus de 0.8, c’est un signal de stress. 2. Hedging dynamique : ajuster la taille du hedge en fonction de la corrélation rolling (plus de hedge quand la corrélation est élevée). 3. Pair trading : quand la corrélation rolling entre deux actifs dépasse 0.7, on peut les pair-trader (long/short market- neutral).
Piège : la rolling correlation a un lag structurel. Un régime de stress qui commence à J=0 n’apparaît dans la rolling 60j qu’à J=30 environ.
def calculate_rolling_correlation(returns_df, symbol1, symbol2, window=60):""" Calcule la correlation glissante entre deux actifs. Parameters: ----------- returns_df : pd.DataFrame avec returns de chaque actif symbol1, symbol2 : str, noms des colonnes window : int, fenetre de calcul en jours Returns: -------- pd.Series : correlation glissante """return returns_df[symbol1].rolling(window).corr(returns_df[symbol2])# Exemple avec donnees simuleesnp.random.seed(42)dates = pd.date_range('2020-01-01', '2023-12-31', freq='B')n =len(dates)# Simuler returns avec correlation variablespy_returns = np.random.normal(0.0004, 0.012, n)tlt_returns =-0.3* spy_returns + np.random.normal(0.0001, 0.008, n)# Ajouter periode de stress (correlation augmente en valeur absolue)stress_start =int(n *0.25)stress_end =int(n *0.30)tlt_returns[stress_start:stress_end] =-0.7* spy_returns[stress_start:stress_end] + np.random.normal(0.0001, 0.005, stress_end - stress_start)returns_sim = pd.DataFrame({'SPY': spy_returns,'TLT': tlt_returns}, index=dates)# Calculer rolling correlationrolling_corr = calculate_rolling_correlation(returns_sim, 'SPY', 'TLT', window=60)# Visualisationfig, axes = plt.subplots(2, 1, figsize=(14, 8), sharex=True)# Subplot 1: Returns cumulescumulative = (1+ returns_sim).cumprod()axes[0].plot(cumulative.index, cumulative['SPY'], label='SPY', color='navy', linewidth=1.5)axes[0].plot(cumulative.index, cumulative['TLT'], label='TLT', color='darkorange', linewidth=1.5)axes[0].axvspan(dates[stress_start], dates[stress_end], alpha=0.2, color='red', label='Periode stress')axes[0].set_title('Returns Cumules SPY vs TLT', fontsize=12, fontweight='bold')axes[0].set_ylabel('Return Cumule')axes[0].legend(loc='upper left')axes[0].grid(True, alpha=0.3)# Subplot 2: Rolling correlationaxes[1].plot(rolling_corr.index, rolling_corr, color='purple', linewidth=1.5)axes[1].axhline(y=0, color='gray', linestyle='--', alpha=0.5)axes[1].axhline(y=rolling_corr.mean(), color='red', linestyle='--', label=f'Moyenne: {rolling_corr.mean():.2f}')axes[1].axvspan(dates[stress_start], dates[stress_end], alpha=0.2, color='red')axes[1].set_title('Correlation Glissante 60 Jours (SPY-TLT)', fontsize=12, fontweight='bold')axes[1].set_xlabel('Date')axes[1].set_ylabel('Correlation')axes[1].set_ylim(-1, 0.5)axes[1].legend()axes[1].grid(True, alpha=0.3)plt.tight_layout()plt.show()print(f"\nStatistiques Rolling Correlation SPY-TLT:")print(f" Moyenne: {rolling_corr.mean():.3f}")print(f" Min: {rolling_corr.min():.3f}")print(f" Max: {rolling_corr.max():.3f}")
Statistiques Rolling Correlation SPY-TLT: Moyenne: -0.446 Min: -0.818 Max: -0.132
Avantages de l’Equal Weight : 1. Aucun paramètre à optimiser : Pas de lookback, volatilité, etc. 2. Diversification maximale : Chaque actif a le même poids 3. Rebalancement automatique : Vend les gagnants, achète les perdants 4. Performance compétitive : Souvent bat les stratégies sophistiquées
Mécanisme du Rebalancement :
Initial : SPY=100, QQQ=100, TLT=100 (1/3 chacun)
Après 3 mois : SPY=120, QQQ=80, TLT=100 (drift)
Rebalancement : Vendre SPY, acheter QQQ → retour à 1/3
Effet “Volatility Harvesting” : Le rebalancement périodique capture la “prime de rebalancement” : - En période de mean-reversion, on vend haut, achète bas - Cet effet peut ajouter 0.5-1% de rendement annuel
Fréquence Optimale : - Mensuel : Trop de coûts de transaction - Trimestriel : Bon compromis (utilisé ici) - Annuel : Trop lent, drift important
Études académiques : DeMiguel, Garlappi & Uppal (2009) montrent que 1/N bat souvent les stratégies optimisées en moyenne-variance, surtout quand les estimations de rendement/volatilité sont imprécises.
Ancre savante – DeMiguel, V., Garlappi, L. & Uppal, R. (2009), Optimal Versus Naive Diversification: How Inefficient the 1/N Portfolio Strategy?, The Review of Financial Studies 22(5):1915-1953. (Étude canonique : 1/N bat souvent l’optimisation moyenne-variance quand l’erreur d’estimation est grande. Corrige le 24-06 : la prose citait “De Graaf et al. (2019)” – attribution inverifiable, remplacée par le papier originel 1/N.)
2.3 Detection de Regime de Marche
On peut utiliser les correlations pour detecter le regime de marche et adapter l’allocation.
Trois méthodes principales pour détecter un changement de régime sur un portefeuille multi-actifs :
Hidden Markov Model (HMM) : modélise le marché comme 2-3 états cachés (calme, normal, stress) avec probabilités de transition. Méthode probabiliste standard.
Threshold sur rolling volatility : si la vol 21j dépasse 1.5× la vol 252j, on est en régime stress. Simple et robuste.
Threshold sur rolling correlation : si la corrélation moyenne SPY-EEM dépasse 0.85, on est en stress (toutes les actions se comportent pareil).
Pédagogie : la détection de régime est un outil défensif. On l’utilise pour réduire l’exposition pendant les périodes de stress (cf. QC-Py-10 Drawdown Control).
# [REFERENCE QC] Code a copier dans main.py QC Lab (non executable ici)class RegimeDetectionAlgorithm(QCAlgorithm):""" Algorithme qui detecte le regime de marche base sur les correlations et adapte l'allocation en consequence. """def Initialize(self):self.SetStartDate(2015, 1, 1)self.SetCash(100000)self.spy =self.AddEquity("SPY", Resolution.Daily).Symbolself.tlt =self.AddEquity("TLT", Resolution.Daily).Symbolself.gld =self.AddEquity("GLD", Resolution.Daily).Symbolself.symbols = [self.spy, self.tlt, self.gld]# Parametres detection regimeself.correlation_window =60self.stress_threshold =-0.5# Correlation SPY-TLT < -0.5 = stress# Stocker l'historique des returnsself.returns_history = {}self.current_regime ="normal"# Rebalancer mensuellementself.Schedule.On(self.DateRules.MonthStart("SPY"),self.TimeRules.AfterMarketOpen("SPY", 30),self.DetectRegimeAndRebalance )def DetectRegimeAndRebalance(self):"""Detecte le regime et ajuste l'allocation."""# Recuperer historique history =self.History(self.symbols, self.correlation_window, Resolution.Daily)if history.empty orlen(history) <self.correlation_window:return# Calculer returns closes = history['close'].unstack(level=0) returns = closes.pct_change().dropna()# Correlation SPY-TLT spy_returns = returns[self.spy] tlt_returns = returns[self.tlt] correlation = spy_returns.corr(tlt_returns)# Detecter regimeif correlation <self.stress_threshold: regime ="stress"# Flight to quality prononceelif correlation >0.2: regime ="liquidity_crisis"# Tout baisse ensemble (rare)else: regime ="normal"# Allocations selon regime allocations = {"normal": {self.spy: 0.60, self.tlt: 0.30, self.gld: 0.10},"stress": {self.spy: 0.30, self.tlt: 0.50, self.gld: 0.20},"liquidity_crisis": {self.spy: 0.20, self.tlt: 0.30, self.gld: 0.50} }# Appliquer allocationif regime !=self.current_regime:self.Debug(f"{self.Time}: Regime change {self.current_regime} -> {regime} (corr: {correlation:.2f})")self.current_regime = regimefor symbol, weight in allocations[regime].items():self.SetHoldings(symbol, weight)print("RegimeDetectionAlgorithm defini")print("\nRegimes et allocations:")print(" Normal: SPY 60%, TLT 30%, GLD 10%")print(" Stress: SPY 30%, TLT 50%, GLD 20%")print(" Liquidity Crisis: SPY 20%, TLT 30%, GLD 50%")
Les chiffres du run : CAGR 7.704%, Sharpe 0.347, MaxDD 25.200%, 277 trades sur 2863 jours de bourse (~11,4 ans, soit ~24 trades/an), win rate 78%, capital final $232,931.68 pour $100,000 engagés.
Le résultat contredit l’intuition défensive : la détection de régime est présentée comme un outil de réduction du drawdown — or le MaxDD mesuré (-25.2%) est le troisième plus élevé des huit runs du notebook (après -39.4% et -28.0%). Le filtre n’a pas empêché une baisse de quart du capital. Un Sharpe de 0.347 pour 277 trades signifie que la stratégie a rendu du rendement, mais avec une volatilité qui n’a pas été domptée.
Pourquoi le mécanisme peut échouer ainsi : vol_21 > 1.5 × vol_252 est un signal retardataire — la moyenne 252 jours met des mois à se relever après un choc, si bien que le seuil 1.5× se déclenche souvent une fois la baisse déjà consommée. Le régime est identifié après coup, pas avant.
Pédagogie honnête : un détecteur de régime n’est pas une protection en soi ; c’est un signal qu’il faut dimensionner et tester (fenêtres, seuils, délai de réaction). Le win rate 78% sur 277 trades indique par ailleurs que la stratégie gagne souvent de petites sommes — profil compatible avec un drawdown important malgré une majorité de trades gagnants.
Exercice 1 : Rolling Correlation personnalisee
En vous basant sur les exemples de correlation et de detection de regime ci-dessus, créez une analyse qui surveille la rolling correlation entre SPY et GLD (au lieu de SPY-TLT) et adapte l’allocation.
Objectif : implementer GoldCorrelationStrategy avec les specifications suivantes :
Charger l’historique de SPY et GLD via QuantBook
Calculer la rolling correlation SPY-GLD sur une fenêtre glissante de 20 jours
Si correlation > seuil_haute (0.7) : gold ne diversifie plus, augmenter SPY
Si correlation < seuil_basse (0.3) : gold est un bon diversificateur, augmenter GLD
Poids par defaut : SPY 60%, GLD 40%
Indices : - Utilisez qb.History() pour recuperer les données de SPY et GLD - Utilisez .rolling(window).corr() pour la correlation glissante - Stockez la correlation dans une variable d’instance pour le logging - Inspirez-vous des exemples de correlation ci-dessus pour le chargement des données
Comparaison des méthodes d’allocation
Sur la même période 2015-2024, les trois méthodes principales donnent des résultats très différents :
Méthode
CAGR typique
Sharpe typique
Max DD typique
Equal Weight 1/N
~8-10%
0.6-0.7
-15% à -18%
Risk Parity
~7-9%
0.7-0.85
-10% à -12%
Markowitz MV
~9-12%
0.5-0.9 (instable)
-18% à -25%
Buy & Hold SPY
~13%
0.55
-25%
Observation clé : Markowitz surperforme en CAGR mais sous-performe en Sharpe ajusté au risque. Equal Weight et Risk Parity sont plus robustes dans le temps.
Pédagogie : la “meilleure” méthode dépend de l’objectif — CAGR absolu (Markowitz), Sharpe/DD ajusté (Risk Parity), ou simplicité de maintenance (Equal Weight).
class GoldCorrelationStrategy:""" Exercice : Moniteur de correlation glissante SPY/GLD avec allocation adaptative. Etapes : 1. Utilisez QuantBook pour charger l'historique de SPY et GLD 2. Calculez la correlation glissante sur une fenetre de 20 jours 3. Allouez dynamiquement : correlation < seuil_basse -> plus de GLD, correlation > seuil_haute -> plus de SPY 4. Affichez les 5 dernieres correlations et l'allocation suggeree """def__init__(self):self.window =20self.seuil_basse =0.3self.seuil_haute =0.7self.spy_weight =0.6self.gld_weight =0.4# TODO etudiant : completer le constructeurdef compute_correlation(self, spy_returns, gld_returns):# TODO etudiant : calculer la correlation de Pearson glissantereturnNonedef adaptive_allocation(self, current_corr):# TODO etudiant : ajuster les poids selon les seuilsreturnself.spy_weight, self.gld_weightdef run(self):# TODO etudiant : orchestrer le calcul et afficher les resultatsprint("Exercice a completer")strategy = GoldCorrelationStrategy()strategy.run()
Risk Parity en Production
RiskParityAlgorithm implémente une approche de construction de portefeuille basée sur le risque plutôt que sur le capital.
Différence Equal Weight vs Risk Parity :
Actif
Equal Weight
Risk Parity
Pourquoi ?
SPY (Vol 19%)
20%
~12%
Moins car plus volatile
IEF (Vol 8%)
20%
~28%
Plus car moins volatile
Effet de Levier Implicite : Le vrai Risk Parity (comme Bridgewater’s “All Weather”) utilise souvent du levier sur les bonds pour atteindre la parité de risque. Cette version simplifiée n’utilise pas de levier.
Avantages du Risk Parity : - Contribution égale au risque total - Moins sensible aux rendements extrêmes d’un seul actif - Performe bien sur longue période
Inconvénients : - Nécessite des estimations de volatilité fiables - Peut surpondérer les bonds en période de taux bas - Coûts de rebalancement plus élevés (volatilité change vite)
Attention : En période de hausse rapide des taux (comme 2022), le Risk Parity peut souffrir car les bonds perdent de la valeur et leur volatilité augmente simultanément.
Note pedagogique : La detection de regime est un domaine actif de recherche. Cette implementation simple utilise uniquement la correlation SPY-TLT. Des approches plus sophistiquees utilisent des modèles Hidden Markov (HMM), des indicateurs de volatilite (VIX), ou du machine learning.
Note pédagogique : la détection de régime est un domaine actif
Trois approches académiques principales :
Hidden Markov Models (HMM) : le marché alterne entre 2-3 états cachés. Méthode probabiliste standard, voir Hamilton (1989).
Structural breaks (Bai-Perron) : la série temporelle a des ruptures structurelles qu’on peut détecter.
Regime switching models : régression avec coefficients qui varient selon le régime.
Implémentation pratique QuantConnect : la plupart des traders retail utilisent des règles simples (threshold sur rolling vol, threshold sur VIX) plutôt que des modèles statistiques sophistiqués. C’est plus robuste et plus facile à interpréter.
Anti-pattern : utiliser un HMM à 5 états avec 20 paramètres estimés sur 10 ans de données daily. Overfitting garanti, performance out-of-sample médiocre.
Tactical Asset Allocation - Momentum Simplifié
TacticalAssetAllocationAlgorithm utilise le SMA 200 comme filtre de tendance, une approche TAA très répandue.
Logique de Trading :
Prix > SMA(200) → Tendance haussière → Allouer
Prix < SMA(200) → Tendance baissière → Cash
Résultats Historiques Typiques : - Avantages : Évite les grands bear markets (2000-2002, 2008, 2022) - Inconvénients : Whipsaw en marché latéral, faux signaux
Caractéristiques du SMA 200 : - Lag moyen : ~100 jours (demi-période) - Sensibilité : Moins sensible aux fluctuations court terme - Robustesse : Fonctionne sur tous les types d’actifs
Comparaison TAA vs Buy-and-Hold :
Scénario
Buy-and-Hold
TAA SMA200
Bull market prolongé
✅ Fort gain
⚠️ Gain réduit (cash périodes)
Bear market sévère
❌ Forte perte
✅ Évité (cash)
Marché latéral
↔︎️ Flat
❌ Whipsaw (pertes)
Améliorations Possibles : 1. Confirmation multiple : SMA 50 + SMA 200 + RSI 2. Rotation d’actifs : Au lieu de cash, aller vers bonds défensifs 3. Time stops : Réduire l’exposition après X jours sous le SMA
Valeur du SMA 200 en 2023-2024 : SPY est resté au-dessus de son SMA 200 la majeure partie du temps, donc le TAA aurait été investi. Le filtre est plus utile en période de transition de marché.
Partie 3 : Stratégies d’Allocation (30 min)
3.1 Equal Weight Portfolio
La stratégie la plus simple : allouer le même poids a chaque actif. Malgre sa simplicite, elle performe souvent bien grace a la diversification et au rebalancement implicite (vendre les gagnants, acheter les perdants).
Trois variantes de Tactical Asset Allocation
Le TAA modifie les poids en fonction de signaux de momentum. Trois variantes principales :
Time-Series Momentum (TSM) : on monte sur un actif si son rendement passé 12 mois est positif, on sort sinon. Simple, robuste.
Cross-Sectional Momentum (XSM) : on monte sur les actifs qui ont le meilleur momentum parmi l’univers, on vend ceux qui ont le pire. Plus discriminatif.
Dual Momentum : combine TSM (absolu) et XSM (relatif). On n’investit que si TSM>0 ET XSM > médiane. Plus défensif.
Coût-bénéfice : TAA réduit le DD en période baissière mais augmente le turnover (rebalance mensuel typiquement). À 5-10 bps de coût par trade, sur 12 rebalances/an, on perd ~1% de CAGR.
# [REFERENCE QC] Code a copier dans main.py QC Lab (non executable ici)class EqualWeightPortfolioAlgorithm(QCAlgorithm):""" Strategie Equal Weight avec rebalancement periodique. Allocation: 1/N pour chaque actif. """def Initialize(self):self.SetStartDate(2015, 1, 1)self.SetEndDate(2024, 12, 31)self.SetCash(100000)# Ajouter actifs multi-classes tickers = ["SPY", "QQQ", "TLT", "IEF", "GLD", "USO"]self.symbols = [self.AddEquity(t, Resolution.Daily).Symbol for t in tickers]# Programmer rebalancement trimestrielself.Schedule.On(self.DateRules.MonthStart("SPY"),self.TimeRules.AfterMarketOpen("SPY", 30),self.Rebalance )self.rebalance_months = [1, 4, 7, 10] # Trimestrieldef Rebalance(self):"""Rebalance vers equal weight."""# Verifier si c'est un mois de rebalancementifself.Time.month notinself.rebalance_months:return# Calculer poids egal weight =1.0/len(self.symbols)self.Debug(f"{self.Time.date()}: Rebalancing to equal weight ({weight:.2%} each)")# Appliquer allocationfor symbol inself.symbols:self.SetHoldings(symbol, weight)print("EqualWeightPortfolioAlgorithm defini")print(f"\nAllocation: {100/6:.2f}% par actif")print("Rebalancement: Trimestriel (Janvier, Avril, Juillet, Octobre)")
Lecture du résultat : EqualWeightPortfolioAlgorithm
Les chiffres du run : CAGR 7.145%, Sharpe 0.359, MaxDD 19.400%, 203 trades, win rate 86%, capital final $199,492.52 pour $100,000 engagés sur ~10 ans (2015-2024).
Lecture : sur cette période, 1/N a doublé le capital avec un drawdown de ~19% — un profil correct mais très éloigné des chiffres souvent cités pour l’equal weight (Sharpe 0.65-0.75, DD -16%). Le Sharpe 0.359 est modeste : le rendement a été obtenu au prix d’une volatilité significative, portée par 2020 et 2022.
Le win rate de 86% n’est pas un indicateur de performance : il compte les trades gagnants, pas leur taille. Un rebalancement trimestriel gagne souvent de petits montants et perd rarement gros — c’est exactement le profil qui produit une majorité de gains et un drawdown marqué.
Ce qui survit du message académique : DeMiguel, Garlappi & Uppal (2009) montrent que 1/N bat souvent l’optimisation moyenne-variance sur des données out-of-sample — c’est un résultat de littérature, pas une mesure de ce run. Ici, la comparaison interne au notebook est directe : l’equal weight (Sharpe 0.359) bat le risk parity implémenté (0.305, cellule #41) — la robustesse de 1/N se vérifie donc sur ce corpus, ce qui est précisément l’intérêt pédagogique de la série.
3.2 Risk Parity
L’idee du Risk Parity est d’allouer de sorte que chaque actif contribue également au risque total du portfolio. Cela signifie surponderer les actifs a faible volatilite (bonds) et sous-ponderer les actifs a haute volatilite (equities).
Formule simplifiee : \[w_i = \frac{1/\sigma_i}{\sum_j 1/\sigma_j}\]
ou \(\sigma_i\) est la volatilite de l’actif \(i\).
Ancre savante – Maillard, S., Roncalli, T. & Teiletche, J. (2010), The Properties of Equally Weighted Risk Contribution Portfolios, The Journal of Portfolio Management 36(4):60-70. DOI 10.3905/jpm.2010.36.4.060. (Formalisation du Risk Parity / contribution egale au risque.)
Le Risk Parity pondère chaque actif de façon à ce que sa contribution au risque total soit égale.
Avantage vs Equal Weight : tient compte du risque, donne plus de poids aux actifs défensifs (bonds) qui réduisent la variance du portefeuille.
Limite : suppose que la volatilité est stationnaire. Un krach où toutes les vols doublent en même temps ne change pas les poids — alors que c’est précisément le moment où le hedge serait utile.
Stratégies d’Options : Protective Put vs Collar
Protective Put (ProtectivePutAlgorithm) : - Structure : Long 100 SPY + Long 1 Put (5% OTM) - Objectif : Limiter les pertes à ~5% + prime - Coût : Prime du put (réduit le rendement espéré) - Best for : Protection à court terme, incertitude élevée
Collar (CollarStrategyAlgorithm) : - Structure : Long SPY + Long Put (5% OTM) + Short Call (5% OTM) - Objectif : Financer le put avec le call - Coût net : Souvent proche de zéro (prime call ~ prime put) - Trade-off : Gains limités à ~5% aussi
Payoff Comparé :
Scénario SPY
Sans Hedge
Protective Put
Collar
-10%
-10%
~-7% (put compense)
~-7%
0%
0%
~-2% (prime payée)
~0%
+10%
+10%
+8% (prime payée)
+5% (call cap)
Quand Choisir Quoi : - Protective Put : Vous voulez protéger mais garder tout le potentiel de hausse - Collar : Vous êtes prêt à limiter le gain pour réduire le coût de la protection
Risque Important : En cas de gap down (ouverture -10%), le put peut ne pas offrir la protection attendue si le marché gape avant que vous puissiez ajuster.
Composants Clés : - β (Beta) : Sensibilité du portfolio à l’indice - β = 1 : Portfolio bouge comme l’indice - β > 1 : Portfolio plus volatile (tech-heavy) - β < 1 : Portfolio moins volatile (value stocks)
Hedge Ratio : Pourcentage du portfolio à hedger
100% : Couverture totale
50% : Couverture partielle (utilisé ici)
Multiplicateur ES : $50 par point
ES à 4000 = 1 contrat = $200,000 de valeur notionnelle
Exemple de Calcul :
Portfolio Value = $500,000
Beta = 1.2 (tech-heavy)
Hedge Ratio = 50%
ES Price = 4000
Contracts = -(500,000 × 1.2 × 0.5) / (4000 × 50)
= -300,000 / 200,000
= -1.5 → -1 ou -2 contracts
Avantages du Hedge Futures vs Options : - Coût récurrent : Juste les marges, pas de prime - Effet linéaire : Protection proportionnelle au mouvement - Liquidity : Futures très liquides
Risques Spécifiques : - Margin calls : Si le marché monte, le short ES perd et nécessite des fonds additionnels - Roll risk : Quand le contrat expire, il faut rouler vers le prochain (coût de carry) - Basis risk : ES peut ne pas parfaitement tracker le portfolio sous-jacent
Micro Futures : Pour les petits portfolios (<$100k), considérer les Micro E-mini (/MES) avec un multiplicateur de $5 au lieu de $50.
# [REFERENCE QC] Code a copier dans main.py QC Lab (non executable ici)class RiskParityAlgorithm(QCAlgorithm):""" Strategie Risk Parity: allouer en fonction de la volatilite inverse. Chaque actif contribue egalement au risque total. """def Initialize(self):self.SetStartDate(2015, 1, 1)self.SetEndDate(2024, 12, 31)self.SetCash(100000)# Ajouter actifs tickers = ["SPY", "QQQ", "TLT", "IEF", "GLD"]self.symbols = [self.AddEquity(t, Resolution.Daily).Symbol for t in tickers]# Parametresself.lookback =60# Jours pour calcul volatilite# Rebalancement mensuelself.Schedule.On(self.DateRules.MonthStart("SPY"),self.TimeRules.AfterMarketOpen("SPY", 30),self.RebalanceRiskParity )def RiskParityWeights(self, symbols, lookback=60):""" Calcule les poids Risk Parity bases sur la volatilite historique. """# Recuperer historique history =self.History(symbols, lookback, Resolution.Daily)if history.empty:# Fallback: equal weightreturn {s: 1.0/len(symbols) for s in symbols}# Extraire closes et calculer returns closes = history['close'].unstack(level=0) returns = closes.pct_change().dropna()# Volatilite annualisee volatilities = returns.std() * np.sqrt(252)# Inverse volatilite inv_vol =1/ volatilities# Normaliser weights = inv_vol / inv_vol.sum()# Convertir en dict avec Symbol comme clereturn {symbol: weights[symbol] for symbol in symbols if symbol in weights.index}def RebalanceRiskParity(self):"""Rebalance selon les poids Risk Parity.""" weights =self.RiskParityWeights(self.symbols, self.lookback)ifnot weights:returnself.Debug(f"{self.Time.date()}: Risk Parity Rebalancing")for symbol, weight in weights.items():self.SetHoldings(symbol, weight)self.Debug(f" {symbol.Value}: {weight:.2%}")print("RiskParityAlgorithm defini")
Les chiffres du run : CAGR 5.679%, Sharpe 0.305, MaxDD 22.200%, 536 trades, win rate 72%, capital final $173,811.76.
Le risk parity est dominé par l’equal weight sur ce backtest, sur les trois axes à la fois :
Equal Weight (§3.1)
Risk Parity (§3.2)
CAGR
7.145%
5.679%
Sharpe
0.359
0.305
MaxDD
19.400%
22.200%
Trades
203
536
Le risk parity rapporte moins, risque plus, et paie 2,6× plus de transactions (536 contre 203) — l’inverse du profil attendu (« meilleur Sharpe, plus faible DD »).
Pourquoi ce résultat est plausible plutôt qu’anecdotique : la version implémentée pondère par l’inverse de la volatilité mesurée sur une fenêtre de 60 jours et rebalance mensuellement. Sur 2015-2024, cette règle surpondère structurellement les obligations longues (TLT, faible vol) — et 2022 est l’année où obligations et actions ont baissé ensemble, précisément le scénario où la parité de risque souffre : le portefeuille était concentré sur l’actif qui perdait, au moment où sa volatilité montait.
Coût : la rotation rapide (536 ordres) est un facteur direct de friction. Un rebalancement trimestriel ou une fenêtre de vol plus longue aurait réduit ce coût — c’est un axe d’expérimentation pour l’exercice 2.
3.3 Tactical Asset Allocation (TAA)
La Tactical Asset Allocation ajuste dynamiquement les poids en fonction de signaux de marche. L’approche la plus simple utilise le momentum (SMA 200).
Règles : - Si prix > SMA 200 : actif en tendance haussiere -> allouer - Si prix < SMA 200 : actif en tendance baissiere -> cash (ou autre actif refuge)
Le TAA est un overlay sur une allocation stratégique. Plutôt que de maintenir des poids fixes (60% actions / 40% obligations), on ajuste dynamiquement en fonction de signaux.
Signaux courants : - Momentum (cf. §3.3 variantes) : surpondérer les actifs en tendance haussière. - Volatility : surpondérer les actifs à faible vol en période calme, sous-pondérer en stress. - Valuation : P/E ratio, yield curve, etc.
Avantage : surperforme Buy & Hold sur le long terme en réduisant le DD. Inconvénient : turnover élevé, taxable (impositions sur les gains court terme).
All-Weather Portfolio : Philosophie et Implémentation
Allocation Originale de Ray Dalio : - 30% Equities (SPY) - Croissance économique - 40% Long-Term Bonds (TLT) - Protection contre déflation - 15% Intermediate Bonds (IEF) - Stabilité revenus - 7.5% Gold (GLD) - Protection contre inflation - 7.5% Commodities (DBC) - Protection contre inflation
Pourquoi cette Allocation ?
Scénario Économique
Croissance
Inflation
Actifs Favorables
Allocation All-Weather
Rising Growth
↑
-
Equities, Corp Bonds
30% SPY
Falling Growth
↓
-
Treasury Bonds
55% TLT+IEF
Rising Inflation
-
↑
Gold, Commodities, TIPS
15% GLD+DBC
Falling Inflation
-
↓
Equities, Treasuries
Mix 30/55
Résultats Historiques Typiques (2010-2020) : - Sharpe Ratio : ~0.6-0.8 (meilleur que SPY seul) - Max Drawdown : ~15-20% (vs ~33% pour SPY) - CAGR : ~7-9% (vs ~13% pour SPY)
Avantages : - ✅ Performance décente dans tous les environnements - ✅ Faible volatilité - ✅ Peu de rebalancement nécessaire
Inconvénients : - ⚠️ Sous-performance en bull markets prolongés - ⚠️ Sensibilité aux taux d’intérêt (40% TLT) - ⚠️ Requiert un horizon long terme (5+ ans)
Note d’Implémentation : Le code AllWeatherPortfolioAlgorithm inclut un monitoring des corrélations avec alertes si la corrélation SPY-TLT change significativement (>0.3), ce qui pourrait indiquer un changement de régime nécessitant un ajustement.
# [REFERENCE QC] Code a copier dans main.py QC Lab (non executable ici)class TacticalAssetAllocationAlgorithm(QCAlgorithm):""" Tactical Asset Allocation avec signal momentum (SMA 200). Alloue uniquement aux actifs au-dessus de leur SMA 200. """def Initialize(self):self.SetStartDate(2015, 1, 1)self.SetEndDate(2024, 12, 31)self.SetCash(100000)# Ajouter actifs tickers = ["SPY", "QQQ", "TLT", "GLD", "EFA"] # EFA = MSCI EAFEself.symbols = []self.sma_indicators = {}for ticker in tickers: symbol =self.AddEquity(ticker, Resolution.Daily).Symbolself.symbols.append(symbol)# Creer indicateur SMA 200 pour chaque actifself.sma_indicators[symbol] =self.SMA(symbol, 200)# Warmup pour avoir les SMAs pretesself.SetWarmup(200)# Rebalancement mensuelself.Schedule.On(self.DateRules.MonthStart("SPY"),self.TimeRules.AfterMarketOpen("SPY", 30),self.TacticalRebalance )def TacticalRebalance(self):"""Rebalance tactiquement selon le signal momentum."""ifself.IsWarmingUp:return# Determiner quels actifs sont au-dessus de leur SMA active_symbols = []for symbol inself.symbols: sma =self.sma_indicators[symbol]ifnot sma.IsReady:continue current_price =self.Securities[symbol].Priceif current_price > sma.Current.Value: active_symbols.append(symbol)self.Debug(f" {symbol.Value}: ACTIVE (price {current_price:.2f} > SMA {sma.Current.Value:.2f})")else:self.Debug(f" {symbol.Value}: INACTIVE (price {current_price:.2f} < SMA {sma.Current.Value:.2f})")# Calculer allocationiflen(active_symbols) >0: weight =1.0/len(active_symbols)else: weight =0# 100% cash si aucun actif actifself.Debug(f"{self.Time.date()}: TAA Rebalance - {len(active_symbols)} active assets")# Appliquer allocationfor symbol inself.symbols:if symbol in active_symbols:self.SetHoldings(symbol, weight)else:self.Liquidate(symbol) # Vendre si sous SMAprint("TacticalAssetAllocationAlgorithm defini")print("\nLogique:")print(" - Prix > SMA(200) -> Allouer (momentum positif)")print(" - Prix < SMA(200) -> Cash (eviter tendance baissiere)")
Résultats du backtest QC Cloud (TacticalAssetAllocationAlgorithm)
Metrique
Valeur
Dates negociables
2516
Sharpe Ratio
0.231
CAGR
5.765%
Max Drawdown
22.900%
Net Profit
75.2%
Equity finale
$175,154
Probabilistic Sharpe Ratio
1.872%
Tactical asset allocation (SMA200 momentum) with SPY/QQQ/TLT/GLD/EFA. Backtest QC Cloud 2015-2024 (projet 33177683, bt 9d5d5f326db8a8aec24594018fe0c0ee). Net Profit / Equity finale deduits du CAGR verifie (End = $100,000 x (1+CAGR)^10)
Exercice 2 : Risk Parity personnalise avec contraintes
En vous basant sur les exemples d’equal weight, risk parity et TAA ci-dessus, créez une fonction qui calcule les poids d’un portfolio selon une approche Risk Parity modifiée avec contraintes pratiques.
Objectif : implementer ConstrainedRiskParity avec les specifications suivantes :
Calculer la volatilite annualisee de chaque actif sur les 60 derniers jours
Les poids de base sont l’inverse de la volatilite (risk parity classique)
Contrainte 1 : aucun poids ne peut depasser 40% (cap)
Contrainte 2 : aucun poids ne peut etre inferieur a 5% (floor)
Contrainte 3 : la somme des poids doit etre exactement 1.0
Afficher les poids finaux et verifier que toutes les contraintes sont respectees
Indices : - returns.std() * np.sqrt(252) pour la volatilite annualisee - Utilisez np.clip(poids, floor, cap) puis renormalisez - returns_df.dot(weights) pour calculer les returns du portfolio - Inspirez-vous de la fonction risk_parity_weights de la section 3.2
Résultat attendu : une fonction qui prend un DataFrame de returns et retourne un dictionnaire {symbol: weight} avec les poids contraints.
def constrained_risk_parity(returns_df, cap=0.40, floor=0.05, annualize=True):""" Exercice : Risk Parity avec contraintes pratiques. Etapes : 1. Calculer la volatilite annualisee de chaque actif 2. Calculer les poids inverses (1/vol) comme base 3. Appliquer np.clip pour respecter cap et floor 4. Renormaliser pour que la somme = 1.0 5. Verifier et afficher les contraintes Parameters : - returns_df : DataFrame des returns journaliers - cap : poids maximum par actif (defaut 40%) - floor : poids minimum par actif (defaut 5%) """# TODO etudiant : calculer les volatilites annualisees vols =None# remplacer par returns_df.std() * np.sqrt(252)# TODO etudiant : calculer les poids inverses inv_vol =None# remplacer par 1 / vols# TODO etudiant : normaliser raw_weights =None# remplacer par inv_vol / inv_vol.sum()# TODO etudiant : appliquer les contraintes avec np.clip constrained =None# utiliser np.clip(raw_weights, floor, cap)# TODO etudiant : renormaliser pour somme = 1.0 final_weights =None# constrained / constrained.sum()# TODO etudiant : verifier les contraintes et afficherprint("Exercice a completer")returnNone# Test avec les donnees simulees de la section Risk Parityresult = constrained_risk_parity(returns_demo)if result isnotNone:for sym, w in result.items():print(f" {sym}: {w:.2%}")
Lecture du résultat : TacticalAssetAllocationAlgorithm
Les chiffres du run : CAGR 6.983%, Sharpe 0.318, MaxDD 22.300%, 391 trades, win rate 77%, capital final $196,508.59.
Le TAA ne surperforme pas le Buy & Hold sur cette période : avec un CAGR de 6.983%, la stratégie rend moins que le S&P 500 seul (profil ~13% cité en cellule #28) et son drawdown reste à -22.3% — il n’a donc pas rempli sa promesse défensive de « réduction vs Buy & Hold ». Le filtre SMA 200 a manqué de réactivité là où il devait servir.
Pourquoi : le signal momentum est retardataire par construction (SMA 200 = demi-période de lag ~100 jours). Sur 2020, il sort après le krach et rentre après le rebond — le whipsaw coûte deux fois. Les 391 trades sur 10 ans sont la trace de ces allers-retours.
Ce que la cellule enseigne vraiment : un filtre de tendance n’est ni gratuit ni automatiquement protecteur. Sa valeur dépend de la période et du coût de friction ; ici, la friction (391 ordres) et le retard du signal ont mangé le bénéfice espéré. C’est un résultat négatif utile : il justifie la comparaison systématique des variantes (TSM/XSM/Dual) plutôt que l’adoption d’un filtre par défaut.
Avantages TAA : - Evite les grandes baisses (bear markets) - Suit les tendances de fond
Inconvenients : - “Whipsaw” en marche lateraux (faux signaux) - Late entry/exit (le SMA est un indicateur retardataire)
Avantages du TAA
Trois avantages principaux du Tactical Asset Allocation :
Réduction du DD : sortir en période baissière protège le capital psychologique. Un DD de -25% → retour à 0% demande +33% de gain, alors qu’un DD de -12% → retour à 0% demande seulement +14%.
Capture de momentum : les marchés ont tendance à persister à court-moyen terme (12 mois). Être long pendant les phases haussières et sortir pendant les phases baissières capte cette tendance.
Discipline : le signal mécanique élimine l’émotion (“cette fois c’est différent”). On suit le modèle même quand l’intuition dit autre chose.
Coût caché : le signal momentum lag la réalité. Au moment où le signal dit “sort”, la chute a déjà eu lieu. On protège le capital futur, pas le capital présent.
Partie 4 : Hedging (20 min)
4.1 Hedging avec Options
Les options permettent de proteger un portfolio contre les baisses tout en conservant le potentiel de hausse.
Stratégies principales : - Protective Put : Acheter un put pour limiter les pertes - Collar : Protective put + covered call (finance le put avec le call)
Trois stratégies de hedging avec options
Le hedging avec options permet de protéger un portefeuille sans le liquider. Trois stratégies principales :
Protective Put : acheter un put OTM sur le portefeuille. Coût : la prime du put (1-3% du notionnel). Bénéfice : protection en cas de baisse au-delà du strike.
Collar : vendre un call OTM + acheter un put OTM. Coût net proche de zéro (prime call finance la prime put). Bénéfice : protection mais plafond de gain (call vendu).
Put Spread : acheter un put ITM + vendre un put plus OTM. Coût réduit, protection réduite à un range de strikes.
Comparaison : Protective Put = protection pure (coût élevé), Collar = protection gratuite (gain plafonné), Put Spread = compromis.
# [REFERENCE QC] Code a copier dans main.py QC Lab (non executable ici)class ProtectivePutAlgorithm(QCAlgorithm):""" Strategie Protective Put: long equity + long put pour protection. """def Initialize(self):self.SetStartDate(2015, 1, 1)self.SetEndDate(2024, 12, 31)self.SetCash(100000)# Ajouter l'equity equity =self.AddEquity("SPY", Resolution.Minute) equity.SetDataNormalizationMode(DataNormalizationMode.Raw)self.spy = equity.Symbol# Ajouter options sur SPY option =self.AddOption("SPY", Resolution.Minute)self.option_symbol = option.Symbol# Filtre options: puts OTM, expiration 30-60 jours option.SetFilter(lambda universe: universe .IncludeWeeklys() .Strikes(-10, -2) # OTM puts (strikes sous le prix actuel) .Expiration(30, 60)) # 30-60 jours d'expirationself.put_contract =Nonedef OnData(self, data):# Verifier si on a des options disponiblesifnotself.Portfolio[self.spy].Invested:# Acheter SPYself.SetHoldings(self.spy, 0.90) # 90% en SPY# Si pas de put actif, en acheter unifself.put_contract isNoneornotself.Portfolio[self.put_contract].Invested:self.BuyProtectivePut(data)def BuyProtectivePut(self, data):"""Achete un put de protection."""# Recuperer la chaine d'options chain = data.OptionChains.get(self.option_symbol)if chain isNone:return# Filtrer pour puts uniquement puts = [x for x in chain if x.Right == OptionRight.Put]iflen(puts) ==0:return# Selectionner le put le plus proche de 5% OTM underlying_price =self.Securities[self.spy].Price target_strike = underlying_price *0.95# 5% OTM# Trouver le put le plus proche du target put =min(puts, key=lambda x: abs(x.Strike - target_strike))# Acheter le put qty =int(self.Portfolio[self.spy].Quantity /100) # 1 option = 100 sharesif qty >0:self.MarketOrder(put.Symbol, qty)self.put_contract = put.Symbolself.Debug(f"{self.Time}: Bought protective put - Strike: {put.Strike}, Expiry: {put.Expiry}")print("ProtectivePutAlgorithm defini")print("\nStrategic Protective Put:")print(" - Long 100 shares SPY")print(" - Long 1 put SPY (5% OTM, 30-60 DTE)")print(" - Protection: pertes limitees a ~5% + prime du put")
Résultats du backtest QC Cloud (ProtectivePutAlgorithm)
Metrique
Valeur
Dates negociables
2516
Sharpe Ratio
-0.657
CAGR
0.907%
Max Drawdown
6.400%
Net Profit
9.4%
Equity finale
$109,449
Probabilistic Sharpe Ratio
0.315%
Protective put (long SPY + long put 5% OTM), options, minute resolution. Backtest QC Cloud 2015-2024 (projet 33177683, bt c5bbbe1d538472f6f491b462e6b458ff). Net Profit / Equity finale deduits du CAGR verifie (End = $100,000 x (1+CAGR)^10)
Les chiffres du run : CAGR 0.907%, Sharpe -0.657, MaxDD 6.400%, 15 trades seulement, win rate 12%, capital final $109,461.61.
Le résultat est double, et les deux moitiés comptent :
La protection a fonctionné sur le drawdown : MaxDD -6.4%, le plus faible des huit runs du notebook — aucune autre stratégie testée ici ne descend aussi peu. Le put a rempli son rôle de plancher.
Elle a détruit le rendement : CAGR 0.907% (taux annualisé ; +9.462% sur la décennie) et un Sharpe négatif (-0.657) — le portefeuille a rendu moins qu’un placement sans risque, tout en payant la prime. Le win rate de 12% (2 trades gagnants sur 15) dit la même chose : la prime versée a dominé.
Ce que le nombre de trades impose : 15 trades en 10 ans, c’est un échantillon trop petit pour conclure à une loi générale de l’assurance optionnelle. Ce que ce run établit, c’est le coût d’une prime systématique dans un marché haussier : payer une assurance pendant 10 ans de hausse coûte le rendement, même quand l’assurance est rarement appelée.
Pédagogie : le protective put est un arbitrage explicite rendement contre plancher, pas une amélioration. Ici il achète un DD de -6.4% en payant ~12 points de CAGR par rapport au Buy & Hold. La question utile n’est pas « est-ce que ça protège ? » (oui) mais « à quel prix, et pour quel horizon ? ».
# [REFERENCE QC] Code a copier dans main.py QC Lab (non executable ici)class CollarStrategyAlgorithm(QCAlgorithm):""" Strategie Collar: Protective Put + Covered Call. La prime du call finance (partiellement) le put. """def Initialize(self):self.SetStartDate(2015, 1, 1)self.SetEndDate(2024, 12, 31)self.SetCash(100000)# Setup equity et options equity =self.AddEquity("SPY", Resolution.Minute) equity.SetDataNormalizationMode(DataNormalizationMode.Raw)self.spy = equity.Symbol option =self.AddOption("SPY", Resolution.Minute)self.option_symbol = option.Symbol# Filtre: strikes autour du prix actuel option.SetFilter(lambda universe: universe .IncludeWeeklys() .Strikes(-5, 5) .Expiration(30, 60))self.collar_active =Falsedef OnData(self, data):# Etablir position equity si necessaireifnotself.Portfolio[self.spy].Invested: shares =int(self.Portfolio.Cash *0.9/self.Securities[self.spy].Price) shares = (shares //100) *100# Arrondir a 100if shares >=100:self.MarketOrder(self.spy, shares)# Mettre en place le collar si pas encore actififnotself.collar_active andself.Portfolio[self.spy].Quantity >=100:self.SetupCollar(data)def SetupCollar(self, data):"""Met en place le collar: long put + short call.""" chain = data.OptionChains.get(self.option_symbol)if chain isNone:return underlying_price =self.Securities[self.spy].Price# Separer puts et calls puts = [x for x in chain if x.Right == OptionRight.Put] calls = [x for x in chain if x.Right == OptionRight.Call]iflen(puts) ==0orlen(calls) ==0:return# Put: 5% OTM (protection) put_target = underlying_price *0.95 selected_put =min(puts, key=lambda x: abs(x.Strike - put_target))# Call: 5% OTM (cap le gain) call_target = underlying_price *1.05 selected_call =min(calls, key=lambda x: abs(x.Strike - call_target))# Nombre de contrats qty =int(self.Portfolio[self.spy].Quantity /100)if qty >0:# Acheter put (protection)self.MarketOrder(selected_put.Symbol, qty)# Vendre call (finance le put, mais cap le gain)self.MarketOrder(selected_call.Symbol, -qty)self.collar_active =Trueself.Debug(f"{self.Time}: Collar established")self.Debug(f" Long Put: Strike {selected_put.Strike}, Premium {selected_put.LastPrice:.2f}")self.Debug(f" Short Call: Strike {selected_call.Strike}, Premium {selected_call.LastPrice:.2f}")print("CollarStrategyAlgorithm defini")print("\nStrategie Collar:")print(" - Long SPY")print(" - Long Put 5% OTM (protection downside)")print(" - Short Call 5% OTM (finance le put, cap le gain)")print("\nResultat: Pertes limitees a ~5%, Gains limites a ~5%")
Résultats du backtest QC Cloud (CollarStrategyAlgorithm)
Metrique
Valeur
Dates negociables
2516
Sharpe Ratio
0.485
CAGR
10.605%
Max Drawdown
28.000%
Net Profit
174.0%
Equity finale
$273,998
Probabilistic Sharpe Ratio
9.179%
Collar strategy (long SPY + long put 5% OTM + short call 5% OTM), options, minute resolution. Backtest QC Cloud 2015-2024 (projet 33177683, bt 4d27e53f85d9f456431e8d4cebb9c712). Net Profit / Equity finale deduits du CAGR verifie (End = $100,000 x (1+CAGR)^10)
Les chiffres du run : CAGR 10.605%, Sharpe 0.485, MaxDD 28.000%, 5 trades sur 2516 jours, win rate 50%, capital final $274,220.80.
Deux constats qui nuancent la promesse du collar :
Le drawdown n’est pas plafonné : MaxDD -28%, le deuxième plus élevé des huit runs du notebook — très loin du « -10% » attendu d’un collar. Le call vendu plafonne le gain, mais n’empêche pas la valeur du portefeuille de descendre quand la protection n’est pas en place au bon moment.
5 trades en 10 ans : l’échantillon est trop petit pour lire les métriques comme une propriété de la stratégie. Un CAGR de 10.6% sur 5 décisions relève de la trajectoire d’un marché, pas d’une preuve d’efficacité.
Ce qui reste vrai du mécanisme : le collar finance le put par le call — le coût net reste proche de zéro, et c’est sa vraie différence avec le protective put (cellule #53, Sharpe -0.657). Sur un marché haussier, ce financement se paie par le plafond de gain ; sur ce run il a néanmoins laissé 10.6% de CAGR, contre 0.9% pour le put seul.
À retenir : comparer collar et protective put sur ce notebook donne un contraste net — le put achète un plancher très bas (DD -6.4%) en sacrifiant tout le rendement ; le collar conserve du rendement mais ne garantit pas le plancher. Aucun des deux n’est « gratuit ».
Exercice 3 : Mini All-Weather personnalise
En vous basant sur le All-Weather Portfolio de Ray Dalio et les stratégies d’allocation vues precedemment, créez une version simplifiee et personnalisee du All-Weather.
Objectif : implementer MiniAllWeather avec les specifications suivantes :
Choisir 4 actifs maximum parmi ceux disponibles (SPY, QQQ, TLT, IEF, GLD, USO)
Définir une allocation cible avec au moins 2 classes d’actifs différentes
Implementer un filtre de regime simple : si prix SPY < SMA(100), reduire l’exposition equities de 50%
Simuler un rebalancement trimestriel avec ajustement selon le regime detecte
Afficher un resume de l’allocation et du regime detecte
Indices : - Le filtre SMA peut se calculer avec une moyenne mobile simple sur les données historiques - Pour le regime, comparer le prix actuel a la valeur du SMA - Les mois trimestriels sont [1, 4, 7, 10] - Utilisez un dictionnaire pour les allocations cibles (ex: {"SPY": 0.40, "TLT": 0.30})
Résultat attendu : une classe MiniAllWeather avec initialisation, detection de regime, ajustement d’allocation et affichage du resume.
class MiniAllWeather:""" Exercice : Mini All-Weather Portfolio avec filtre de regime. Etapes : 1. Choisir 4 actifs maximum (au moins 2 classes differentes) 2. Definir les allocations cibles 3. Implementer le filtre SMA(100) sur SPY 4. Implementer le rebalancement trimestriel avec ajustement de regime 5. Afficher le resume allocation + regime """def__init__(self):# TODO etudiant : choisir les actifs et definir les allocations ciblesself.target_allocations = {} # ex: {'SPY': 0.40, 'TLT': 0.30, 'GLD': 0.15, 'IEF': 0.15}self.rebalance_months = [1, 4, 7, 10]self.sma_period =100def detect_regime(self, current_price, sma_value):""" Detecte le regime base sur la position du prix vs SMA. Retourne 'bull' ou 'defensif'. """# TODO etudiant : implementer la detectionreturn"bull"def compute_adjusted_allocations(self, regime):""" Ajuste les allocations selon le regime detecte. En defensif : reduire equities de 50%. """# TODO etudiant : implementer l'ajustementreturnself.target_allocationsdef rebalance_summary(self):"""Affiche le resume de l'allocation et du regime."""# TODO etudiant : implementer l'affichageprint("Exercice a completer")portfolio = MiniAllWeather()portfolio.rebalance_summary()
4.2 Hedging avec Futures
Les futures permettent de hedger un portfolio equity en vendant des contrats futures sur indice (ex: ES pour S&P 500).
Calcul du Hedge Ratio : \[\text{Contracts} = \frac{\text{Portfolio Value} \times \beta}{\text{Futures Value}}\]
ou : - \(\beta\) = beta du portfolio vs l’indice (mesure la sensibilite) - Futures Value = Prix du future x multiplicateur (50 pour ES)
Le hedging avec futures est moins cher que le hedging avec options (pas de prime à payer), mais introduit un basis risk.
Exemple : portefeuille \(100k SPY, β=1.0, future ES à 4500\), multiplicateur 50$. - N = (1.0 × 100000) / (4500 × 50) = 0.44 contrats.
Basis risk : le future ne suit pas parfaitement le sous-jacent. L’écart (basis) varie dans le temps, créant un P&L de hedging imparfait. Pendant les stress, le basis peut diverger fortement (future surperforme ou sous-performe le cash).
Coût-bénéfice : - Pas de prime initiale (contrairement aux puts). - Mark-to-market daily (appels de marge). - Efficacité imparfaite due au basis risk.
# [REFERENCE QC] Code a copier dans main.py QC Lab (non executable ici)class FuturesHedgingAlgorithm(QCAlgorithm):""" Hedging d'un portfolio equity avec futures ES (S&P 500 E-mini). """def Initialize(self):self.SetStartDate(2015, 1, 1)self.SetEndDate(2024, 12, 31)self.SetCash(500000) # Plus de capital pour marges futures# Portfolio equityself.equities = [self.AddEquity("AAPL", Resolution.Daily).Symbol,self.AddEquity("MSFT", Resolution.Daily).Symbol,self.AddEquity("GOOGL", Resolution.Daily).Symbol,self.AddEquity("AMZN", Resolution.Daily).Symbol, ]# Futures ES (S&P 500 E-mini)self.es =self.AddFuture(Futures.Indices.SP500EMini, Resolution.Daily)self.es.SetFilter(0, 90) # Contrats expirant dans 0-90 jours# Parametres hedgingself.portfolio_beta =1.2# Beta estime du portfolio (tech-heavy = beta > 1)self.es_multiplier =50# 1 point ES = $50self.hedge_ratio =0.5# Hedger 50% du portfolioself.hedge_active =Falseself.hedge_contract =None# Programmer rebalancementself.Schedule.On(self.DateRules.MonthStart("AAPL"),self.TimeRules.AfterMarketOpen("AAPL", 30),self.RebalanceHedge )def OnData(self, data):# Etablir position equity si necessaireifnotany(self.Portfolio[s].Invested for s inself.equities): weight =0.8/len(self.equities) # 80% en equities, 20% reserve pour margesfor symbol inself.equities:self.SetHoldings(symbol, weight)def CalculateHedgeContracts(self):""" Calcule le nombre de contrats ES a shorter pour hedger le portfolio. """# Valeur du portfolio equity equity_value =sum(self.Portfolio[s].HoldingsValue for s inself.equities)if equity_value <=0:return0# Valeur a hedger value_to_hedge = equity_value *self.hedge_ratio# Ajuster par le beta beta_adjusted_value = value_to_hedge *self.portfolio_beta# Trouver le contrat ES actififself.hedge_contract isNone:return0# Valeur d'un contrat ES es_price =self.Securities[self.hedge_contract].Price es_contract_value = es_price *self.es_multiplier# Nombre de contrats (negatif car on short) contracts =-int(beta_adjusted_value / es_contract_value)return contractsdef RebalanceHedge(self):"""Rebalance la position de hedge."""# Trouver le contrat ES front-monthfor chain inself.CurrentSlice.FutureChains: contracts =list(chain.Value)iflen(contracts) >0:# Prendre le contrat le plus proche de l'expiration front_month =sorted(contracts, key=lambda x: x.Expiry)[0]self.hedge_contract = front_month.Symbolbreakifself.hedge_contract isNone:self.Debug("No ES contract found for hedging")return# Calculer contracts necessaires target_contracts =self.CalculateHedgeContracts() current_contracts =int(self.Portfolio[self.hedge_contract].Quantity)# Ajuster position delta = target_contracts - current_contractsif delta !=0:self.MarketOrder(self.hedge_contract, delta)self.Debug(f"{self.Time.date()}: Hedge adjusted by {delta} contracts")self.Debug(f" Portfolio Value: ${sum(self.Portfolio[s].HoldingsValue for s inself.equities):,.0f}")self.Debug(f" ES Contracts: {target_contracts}")print("FuturesHedgingAlgorithm defini")print("\nParametres de hedge:")print(f" Portfolio Beta: 1.2 (tech-heavy)")print(f" ES Multiplier: $50 par point")print(f" Hedge Ratio: 50% du portfolio")print("\nFormule: Contracts = (Portfolio Value * Beta * Hedge Ratio) / (ES Price * 50)")
Résultats du backtest QC Cloud (FuturesHedgingAlgorithm)
Metrique
Valeur
Dates negociables
2579
Sharpe Ratio
0.821
CAGR
24.030%
Max Drawdown
39.400%
Net Profit
761.5%
Equity finale
$4,307,621
Probabilistic Sharpe Ratio
29.549%
Futures hedging (AAPL/MSFT/GOOGL/AMZN + ES short hedge), daily resolution. Backtest QC Cloud 2015-2024, capital initial $500,000 (marges futures); NP eleve reflete la hausse tech 2015-2024 (projet 33177683, bt c75add1e8ae039d9f3544e249ccc185e). Net Profit / Equity finale deduits du CAGR verifie (End = $500,000 x (1+CAGR)^10)
Les chiffres du run, et ce qu’ils ne peuvent pas dire : CAGR 24.030%, Sharpe 0.821, MaxDD 39.400%, Net Profit 763.061%, capital final $4,315,307.37 — mais 4 trades sur 2579 jours et un win rate de 0%.
Pourquoi ce bandeau n’est pas une preuve de performance : quatre trades en dix ans ne constituent pas un échantillon. Un win rate de 0% affiché à côté d’un profit de 763% signale un problème de comptage des trades fermés, pas une stratégie gagnante par construction. Les métriques de ce run décrivent une trajectoire (celle d’un portefeuille equity partiellement couvert par un short ES pendant une décennie haussière), elles ne mesurent pas la qualité du hedge.
Le drawdown contredit la promesse du hedge : -39.4%, le plus élevé du notebook. Un hedge censé réduire le drawdown qui produit le pire drawdown de la série est un résultat à lire tel quel : le short ES réduit l’exposition en moyenne, il ne protège pas les points bas.
Ce que le notebook enseigne donc honnêtement : sur les quatre mécanismes de couverture testés (put, collar, futures, filtre de régime), un seul réduit réellement le drawdown — le protective put, à -6.4% — et il le paie d’un Sharpe négatif. Les trois autres affichent un drawdown de 22% à 39%, c’est-à-dire au niveau ou au-dessus des stratégies d’allocation non couvertes du même notebook (equal weight 19.4%, TAA 22.3%). Un hedge se juge donc sur le couple (rendement, drawdown) mesuré sur un échantillon suffisant, jamais sur le mécanisme annoncé — et son coût dépend du régime de marché traversé.
Note importante sur les Futures : - Les futures requierent des marges (capital immobilise) - Le multiplicateur ES est $50 (1 contrat ES = $50 x prix SP500) - Un contrat ES mini (/ES) represente ~$200k de valeur notionnelle - Pour les petits portfolios, considerer les micro futures (/MES, multiplicateur $5)
Note importante sur les Futures
Trois pièges courants avec les futures en hedging :
Contango/Backwardation : si le future est en contango (prix future > spot), le roll est négatif — on perd de l’argent à chaque rollover. C’est le cas typique pour les commodities (CL, NG).
Mark-to-market daily : un mouvement adverse de 5% du future génère un appel de marge de $5000 sur 1 contrat ES. Si le compte n’a pas assez de cash, margin call = liquidation forcée.
Expiry : les futures expirent. Il faut rouler la position 5-15 jours avant l’expiry (cf. QC-Py-07 Futures/Forex).
Recommandation : utiliser les futures uniquement avec un compte suffisamment capitalisé (>$50k) pour absorber les appels de marge sans liquidation forcée.
Partie 5 : All-Weather Portfolio (25 min)
5.1 Concept du All-Weather Portfolio
Le All-Weather Portfolio est une stratégie developpee par Ray Dalio (Bridgewater Associates). L’idee est de créer un portfolio qui performe dans tous les environnements economiques.
Les 4 Scénarios Economiques :
Environnement
Croissance
Inflation
Actifs Favorables
Rising Growth
Hausse
-
Equities, Corporate Bonds, Commodities
Falling Growth
Baisse
-
Treasury Bonds, TIPS
Rising Inflation
-
Hausse
Commodities, TIPS, Gold
Falling Inflation
-
Baisse
Equities, Treasury Bonds
Allocation Originale Dalio : - 30% Actions (SPY) - 40% Obligations Long Terme (TLT) - 15% Obligations Moyen Terme (IEF) - 7.5% Or (GLD) - 7.5% Commodities (DBC ou GSG)
# Visualisation de l'allocation All-Weatherallocations = {'Equities (SPY)': 30,'Long-Term Bonds (TLT)': 40,'Intermediate Bonds (IEF)': 15,'Gold (GLD)': 7.5,'Commodities (DBC)': 7.5}# Couleurs par classe d'actifscolors = ['#1f77b4', '#ff7f0e', '#ffbb78', '#ffd700', '#8c564b']fig, axes = plt.subplots(1, 2, figsize=(14, 6))# Pie chartaxes[0].pie(allocations.values(), labels=allocations.keys(), autopct='%1.1f%%', colors=colors, explode=[0.02]*5, shadow=True)axes[0].set_title('All-Weather Portfolio\n(Ray Dalio)', fontsize=14, fontweight='bold')# Bar chart par classeclasses = ['Equities', 'Bonds', 'Commodities']class_weights = [30, 55, 15] # 30% eq, 55% bonds (40+15), 15% commodities (7.5+7.5)class_colors = ['#1f77b4', '#ff7f0e', '#8c564b']bars = axes[1].bar(classes, class_weights, color=class_colors, edgecolor='black')axes[1].set_ylabel('Allocation (%)', fontsize=12)axes[1].set_title('Allocation par Classe d\'Actifs', fontsize=14, fontweight='bold')axes[1].set_ylim(0, 60)# Ajouter valeurs sur les barresfor bar, val inzip(bars, class_weights): axes[1].text(bar.get_x() + bar.get_width()/2, bar.get_height() +1,f'{val}%', ha='center', fontsize=12, fontweight='bold')plt.tight_layout()plt.show()print("\nPhilosophie All-Weather:")print(" - Surponderation bonds (55%) : stabilite, revenus reguliers")print(" - Equities modere (30%) : croissance long terme")print(" - Commodities (15%) : protection inflation, diversification")
Cette section finale assemble toutes les briques du notebook dans une stratégie cohérente multi-actifs.
Architecture cible : 1. Allocation stratégique : 60% actions / 30% obligations / 10% commodities (vol-weights via Risk Parity). 2. Overlay TAA : momentum 12m sur chaque classe → ajustement +/-10% des poids. 3. Hedging : collar 5% OTM sur la portion actions. 4. Risk management : drawdown control à -15% → réduction d’exposition 50%.
Ce que le backtest de cette architecture donne réellement (cellule #69, AllWeatherPortfolioAlgorithm, sortie committée) : CAGR 4.867%, Sharpe 0.198, MaxDD 23.500%, 159 trades. La version assemblée testée ici ne reproduit donc pas le profil espéré — et l’écart n’est pas un détail de calibrage : il vient de la partie obligataire longue (40% TLT) sur une décennie qui contient la remontée de taux de 2022.
Lire ce résultat comme un point de départ, pas comme un échec : les quatre briques sont posées et chaque levier est modifiable (fenêtre de vol, fréquence de rebalance, seuil de drawdown). La cellule #71 récapitule ce que le notebook établit et ce qu’il laisse ouvert ; les exercices 1 à 3 fournissent le cadre pour tester vos propres variantes.
# [REFERENCE QC] Code a copier dans main.py QC Lab (non executable ici)class AllWeatherPortfolioAlgorithm(QCAlgorithm):""" All-Weather Portfolio inspire de Ray Dalio. Allocation: - 30% Equities (SPY) - 40% Long-Term Bonds (TLT) - 15% Intermediate Bonds (IEF) - 7.5% Gold (GLD) - 7.5% Commodities (DBC) Features: - Rebalancement trimestriel - Risk monitoring (volatilite, correlations) - Alertes si correlations changent significativement """def Initialize(self):self.SetStartDate(2015, 1, 1)self.SetEndDate(2024, 12, 31)self.SetCash(100000)# === CONFIGURATION PORTFOLIO ===self.target_allocations = {}# Equitiesself.spy =self.AddEquity("SPY", Resolution.Daily).Symbolself.target_allocations[self.spy] =0.30# Long-Term Bondsself.tlt =self.AddEquity("TLT", Resolution.Daily).Symbolself.target_allocations[self.tlt] =0.40# Intermediate Bondsself.ief =self.AddEquity("IEF", Resolution.Daily).Symbolself.target_allocations[self.ief] =0.15# Goldself.gld =self.AddEquity("GLD", Resolution.Daily).Symbolself.target_allocations[self.gld] =0.075# Commodities (DBC = Invesco DB Commodity Index)self.dbc =self.AddEquity("DBC", Resolution.Daily).Symbolself.target_allocations[self.dbc] =0.075self.symbols =list(self.target_allocations.keys())# === PARAMETRES RISK MONITORING ===self.correlation_lookback =60# Jours pour calcul correlationself.correlation_threshold =0.3# Seuil alerte changement correlationself.baseline_correlations = {} # Correlations de reference# === REBALANCEMENT TRIMESTRIEL ===self.rebalance_months = [1, 4, 7, 10] # Janvier, Avril, Juillet, Octobreself.Schedule.On(self.DateRules.MonthStart("SPY"),self.TimeRules.AfterMarketOpen("SPY", 30),self.MonthlyCheck )# === WARMUP ===self.SetWarmup(self.correlation_lookback)def MonthlyCheck(self):"""Verification mensuelle: rebalancement et monitoring."""ifself.IsWarmingUp:return# Risk monitoring (tous les mois)self.MonitorRisk()# Rebalancement (trimestriel)ifself.Time.month inself.rebalance_months:self.Rebalance()def Rebalance(self):"""Rebalance vers les allocations cibles."""self.Debug(f"\n{'='*50}")self.Debug(f"{self.Time.date()}: QUARTERLY REBALANCE")self.Debug(f"{'='*50}") total_value =self.Portfolio.TotalPortfolioValueself.Debug(f"Portfolio Value: ${total_value:,.2f}")# Calculer drift (ecart par rapport a la cible)self.Debug(f"\nAllocations (Current -> Target):")for symbol inself.symbols: current_value =self.Portfolio[symbol].HoldingsValue current_weight = current_value / total_value if total_value >0else0 target_weight =self.target_allocations[symbol] drift = current_weight - target_weightself.Debug(f" {symbol.Value}: {current_weight:.1%} -> {target_weight:.1%} (drift: {drift:+.1%})")# Appliquer allocation cibleself.SetHoldings(symbol, target_weight)self.Debug(f"\nRebalance complete.")def MonitorRisk(self):"""Surveille les metriques de risque et alerte si changement significatif."""# Recuperer historique history =self.History(self.symbols, self.correlation_lookback, Resolution.Daily)if history.empty orlen(history) <self.correlation_lookback:return# Calculer returns et correlations closes = history['close'].unstack(level=0) returns = closes.pct_change().dropna()# Volatilite portfolio weights = np.array([self.target_allocations[s] for s inself.symbols]) portfolio_returns = returns.dot(weights) portfolio_vol = portfolio_returns.std() * np.sqrt(252)# Correlation SPY-TLT (indicateur cle) spy_tlt_corr = returns[self.spy].corr(returns[self.tlt])# Matrice de correlation complete corr_matrix = returns.corr()# Stocker comme baseline si premier calcul key ="spy_tlt"if key notinself.baseline_correlations:self.baseline_correlations[key] = spy_tlt_corrself.Debug(f"{self.Time.date()}: Baseline SPY-TLT correlation set to {spy_tlt_corr:.2f}")# Verifier changement significatif baseline =self.baseline_correlations[key] change =abs(spy_tlt_corr - baseline)if change >self.correlation_threshold:self.Debug(f"\n*** ALERT: SPY-TLT correlation changed significantly ***")self.Debug(f" Baseline: {baseline:.2f}")self.Debug(f" Current: {spy_tlt_corr:.2f}")self.Debug(f" Change: {change:.2f}")# Envoyer notification (en production, email/SMS/etc.)self.Notify.Email("trader@example.com", "All-Weather Alert: Correlation Shift",f"SPY-TLT correlation changed from {baseline:.2f} to {spy_tlt_corr:.2f}")# Mettre a jour baselineself.baseline_correlations[key] = spy_tlt_corr# Log mensuelself.Debug(f"\n{self.Time.date()}: RISK MONITOR")self.Debug(f" Portfolio Vol (ann.): {portfolio_vol:.1%}")self.Debug(f" SPY-TLT Correlation: {spy_tlt_corr:.2f}")def OnEndOfAlgorithm(self):"""Resume final."""self.Debug(f"\n{'='*50}")self.Debug("ALL-WEATHER PORTFOLIO - FINAL SUMMARY")self.Debug(f"{'='*50}")self.Debug(f"Final Portfolio Value: ${self.Portfolio.TotalPortfolioValue:,.2f}")self.Debug(f"Total Return: {(self.Portfolio.TotalPortfolioValue /100000-1) *100:.2f}%")print("AllWeatherPortfolioAlgorithm defini")
Résultats du backtest QC Cloud (AllWeatherPortfolioAlgorithm)
Metrique
Valeur
Dates negociables
2516
Sharpe Ratio
0.2
CAGR
4.889%
Max Drawdown
23.500%
Net Profit
61.2%
Equity finale
$161,176
Probabilistic Sharpe Ratio
3.029%
All-Weather portfolio (Dalio-inspired: SPY/TLT/IEF/GLD/DBC), quarterly rebalance + risk monitoring. Backtest QC Cloud 2015-2024 (projet 33177683, bt c5a9fb0fa4f70816fcc476420bb64457). Net Profit / Equity finale deduits du CAGR verifie (End = $100,000 x (1+CAGR)^10)
Lecture du résultat : AllWeatherPortfolioAlgorithm
Les chiffres du run : CAGR 4.867%, Sharpe 0.198, MaxDD 23.500%, 159 trades, win rate 83%, capital final $160,898.51 pour $100,000 engagés sur ~10 ans.
Le profil « le plus défensif du notebook » n’est pas ce que le backtest montre :
All-Weather (§5)
Equal Weight (§3.1)
CAGR
4.867%
7.145%
Sharpe
0.198
0.359
MaxDD
23.500%
19.400%
L’All-Weather rend moins que l’equal weight, avec un Sharpe plus faible et un drawdown plus élevé. Sur cette période, surpondérer les obligations longues (40% TLT) n’a pas produit la stabilité attendue : 2022 a vu actions et obligations baisser ensemble, et un portefeuille à 55% de duration en a subi le plein effet.
Pourquoi la philosophie reste valide malgré ce run : la construction de Dalio raisonne par régimes macroéconomiques (croissance/inflation, hausse/baisse) et cherche une allocation qui ne s’effondre dans aucun des quatre. Cette logique ne garantit pas un meilleur Sharpe sur une fenêtre donnée — elle vise la robustesse entre régimes. 2015-2024 contient deux régimes obligataires opposés (taux nuls puis remontée brutale), ce qui est précisément le stress test qui pénalise la version simplifiée testée ici.
Ce que le notebook démontre donc : l’allocation par régime est un choix de philosophie, pas un gain automatique. La comparaison interne est sans ambiguïté — sur ce corpus, 1/N fait mieux sur les trois axes. Comprendre pourquoi (duration, corrélation actions-obligations non stationnaire) est l’objectif pédagogique de cette section.
Ce que les backtests committés établissent (chiffres des bandeaux, cellules #27 à #69) :
Stratégie
CAGR
Sharpe
MaxDD
Trades
Equal Weight (1/N)
7.145%
0.359
19.400%
203
Risk Parity
5.679%
0.305
22.200%
536
TAA momentum
6.983%
0.318
22.300%
391
Detection de regime
7.704%
0.347
25.200%
277
Protective Put
0.907%
-0.657
6.400%
15
Collar
10.605%
0.485
28.000%
5
Futures Hedging
24.030%
0.821
39.400%
4
All-Weather
4.867%
0.198
23.500%
159
Le résultat le plus instructif du corpus est un résultat négatif : sur cette période (2015-2024), aucune des stratégies d’allocation sophistiquées ne bat l’equal weight, et parmi les mécanismes de couverture un seul réduit réellement le drawdown — le protective put, à -6.4% — en payant un Sharpe négatif ; les trois autres affichent 22% à 39%, au niveau ou au-dessus des allocations non couvertes. Les deux runs à très petit échantillon (collar : 5 trades, futures : 4 trades) ne portent aucune conclusion statistique.
Points Cles a Retenir
Concept
Point Cle
Diversification
Correlation < 1 = reduction risque sans perte de rendement
Risk Parity
Allouer par contribution au risque, pas par capital
TAA
Momentum simple (SMA 200) peut eviter les bear markets
Hedging
Options = protection avec upside, Futures = hedge lineaire
All-Weather
Surponderer bonds pour stabilite dans tous regimes
Limitations
ETFs comme proxies : Les ETFs ont des frais et tracking error vs actifs sous-jacents
Correlations non-stationnaires : Les correlations changent, surtout en crise
Covariance estimation : Difficile avec peu de données (regime shifts)
Leverage implicite Risk Parity : Pour vraie parite, faudrait leverager les bonds
Echantillons courts sur le hedging : collar (5 trades) et futures (4 trades) ne soutiennent aucune conclusion statistique
Deux bandeaux sans performance : MultiAssetPortfolioAlgorithm (#8) et CorrelationAnalysisAlgorithm (#16) n’ont pas trade — les zeros y sont structurels
Limites assumées : les backtests exécutés dans ce notebook sont référentiels (paramètres standards, période 2015-2024). Pour vos propres portefeuilles, vous devrez optimiser les poids sur votre univers d’actifs et horizon d’investissement.
Prochaines Étapes
Notebook Suivant : QC-Py-09
Nous couvrirons des sujets plus avances comme : - Optimisation de portefeuille (Mean-Variance, Black-Litterman) - Factor investing - Portfolio rebalancing avance
Branchement série : - Suivant : QC-Py-09 Order Types — utilise les méthodes d’allocation de ce notebook pour déterminer quand et comment passer les ordres. - Complémentaire : QC-Py-10 Risk Portfolio Management — applique les techniques de hedging vues ici dans un cadre risk-aware (position sizing, stop-loss, drawdown control).
Pour aller plus loin : - Bridgewater All-Weather : papier Dalio sur la philosophie d’allocation par régime macro. - Markowitz original : “Portfolio Selection” (1952) — base théorique de la diversification. - DeMiguel et al. (2009) : “Optimal Versus Naive Diversification” — pourquoi 1/N bat les méthodes sophistiquées out-of-sample.
DeMiguel, V., Garlappi, L. & Uppal, R. (2009). Optimal Versus Naive Diversification: How Inefficient the 1/N Portfolio Strategy? The Review of Financial Studies 22(5):1915-1953.
Maillard, S., Roncalli, T. & Teiletche, J. (2010). The Properties of Equally Weighted Risk Contribution Portfolios. The Journal of Portfolio Management 36(4):60-70. DOI 10.3905/jpm.2010.36.4.060.
Markowitz, H. (1952). Portfolio Sélection. The Journal of Finance 7(1):77-91. DOI 10.1111/j.1540-6261.1952.tb01525.x.
Sharpe, W.F. (1966). Mutual Fund Performance. The Journal of Business 39(1):119-138. DOI 10.1086/294846.