# Parameters
BATCH_MODE = "true"<< Sommaire QC | Précédent : QC-Py-40-PaperTrading-Binance <<
QC-Py-41 : Paper Trading IBKR - SP500 Momentum
Objectif : Workflow backtest -> paper trading sur equities via Interactive Brokers.
Prerequis
- QC-Py-09 (Order Types) : types d’ordres et leur exécution
- QC-Py-12 (Backtesting Analysis) : lecture de résultats de backtest
- QC-Py-27 (Production Deployment) : workflow de deploiement live
- QC-Py-40 (Paper Trading Binance) : concepts du paper trading
Plan du notebook
- Interactive Brokers comme broker multi-asset
- Stratégie momentum cross-section SP500
- Backtest historique
- Configuration IBKR et prerequis TWS
- Deploy en paper trading
- Monitoring
- Exercice : ajouter un filtre de volatilite
import os
import json
import time
from datetime import datetime, timedelta
from typing import Dict, List, Optional, Tuple
from dataclasses import dataclass, field
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import warnings
warnings.filterwarnings('ignore')
# Configuration matplotlib
plt.style.use('seaborn-v0_8-darkgrid')
%matplotlib inline
print(f"QC-Py-41 Paper Trading IBKR - {datetime.now().strftime('%Y-%m-%d')}")QC-Py-41 Paper Trading IBKR - 2026-09-04
Transition : Vers la definition de la stratégie
Maintenant que nous avons compris les prerequis IBKR, nous allons définir la stratégie momentum cross-section qui sera backtestee puis deployee en paper trading.
Le paper trading chez un broker traditionnel repond a une question que le backtest ne peut pas trancher : est-ce que la strategie, telle qu’implementee, se comporte comme prevu quand elle rencontre un vrai carnet d’ordres ? Le backtest valide la logique ; le paper trading valide l’implementation — ordres, timing, fills, connexion. Les deux echouent pour des raisons differentes, et c’est precisement pour cela qu’ils se completent.
1. Interactive Brokers : Broker Multi-Asset
Interactive Brokers (IBKR) est le broker le plus utilise en trading algorithmique institutionnel :
| Caractéristique | Detail |
|---|---|
| Asset classes | Equities, Options, Forex, Futures, Future Options |
| Order types | Market, Limit, Stop, StopLimit, MarketOnOpen/Close, + custom |
| Order updates | Supportes (modifier sans annuler) |
| Données | Tick, Second, Minute, Hour, Daily |
| Paper trading | TWS Paper (port 7497) |
Prerequis spécifiques IBKR
- Compte IBKR : ouverture en ligne (KYC, 1-3 jours)
- TWS ou IB Gateway : doit tourner en continu
- Configuration API : ActiveX/Socket active, port 7497 (paper) / 7496 (live)
- Redemarrage hebdomadaire : IBKR exige un restart avec 2FA chaque semaine
Trois dimensions comptent pour la validation d’une strategie :
- La profondeur d’actifs : la meme infrastructure IBKR execute une strategie equity SP500 et une couverture par futures ou forex ; un testnet crypto ne simule qu’un carnet spot.
- La qualite du simulateur : le compte paper IBKR route les ordres vers un moteur de matching qui reproduit les regles du broker (types d’ordres, fills partiels, heures de marche), pas vers un simulateur interne a la plateforme de backtest.
- Les mises a jour d’execution : chaque evenement du cycle de vie d’un ordre (soumis, partiellement rempli, rempli, annule) remonte vers l’algorithme — indispensable pour diagnostiquer un fill rate faible ou un slippage anormal.
Le realisme a un prix operationnel : TWS (ou IB Gateway) doit tourner et rester connecte, une dependance qu’un testnet cloud n’impose pas.
# Comparaison Binance vs IBKR pour le paper trading
comparison = pd.DataFrame({
'Aspect': ['Assets', 'Credentials', 'Prerequis', 'Order Updates',
'Fees simulees', 'Resolution min', 'Paper env'],
'Binance': ['Crypto spot', 'Testnet (gratuit)', 'Aucun', 'Non',
'0.1%', 'Minute', 'Spot Test Network'],
'IBKR': ['Multi-asset', 'Compte paper IBKR', 'TWS/Gateway', 'Oui',
'Variable ($0.005/share)', 'Tick', 'TWS Paper (7497)']
})
print("Comparaison Binance vs IBKR:")
print(comparison.to_string(index=False))Comparaison Binance vs IBKR:
Aspect Binance IBKR
Assets Crypto spot Multi-asset
Credentials Testnet (gratuit) Compte paper IBKR
Prerequis Aucun TWS/Gateway
Order Updates Non Oui
Fees simulees 0.1% Variable ($0.005/share)
Resolution min Minute Tick
Paper env Spot Test Network TWS Paper (7497)
Lecture du tableau : le choix broker en trois lignes
Pour une strategie SP500 mensuelle, trois lignes pesent le plus. Order Updates (IBKR : Oui) — on peut auditer chaque fill, indispensable au diagnostic d’execution. Resolution min (Tick vs Minute) — le paper TWS reproduit la microstructure, le testnet Binance moyenne. Fees simulees — sur une action a $200, Binance facture 0.1% soit $0.20, IBKR $0.005/share : un facteur ~40 qui s’accumule sur les 837 ordres du backtest reel de la section 3. La ligne Prerequis est le cout inverse (Aucun vs TWS/Gateway), et la derniere ligne nomme le composant central : le paper IBKR passe par TWS Paper sur le port 7497.
Exercice 1 : Calcul du taux de remplissage des ordres
En paper trading IBKR, certains ordres limites ne sont pas remplis. Calculez le fill rate : pourcentage d’ordres soumis qui ont ete reellement executes.
Indices : - # Indice : fill_rate = ordres_executes / ordres_soumis * 100 - # Étape 1 : Simuler une liste d’ordres avec statut (filled/pending/cancelled) - # Étape 2 : Compter les ordres filled - # Étape 3 : Calculer et afficher le fill rate
# Exercice 1 : Taux de remplissage des ordres
# TODO etudiant : Calculer le fill rate a partir d'ordres simules
# Etape 1 : Simuler 50 ordres avec statuts aleatoires
# Etape 2 : Compter les ordres filled
# Etape 3 : fill_rate = filled / total * 100
fill_rate = None # TODO etudiant : remplacer par le calcul
print("Exercice a completer : Taux de remplissage des ordres")Exercice a completer : Taux de remplissage des ordres
Interpretation : Configuration IBKR
Interactive Brokers necessite une infrastructure locale (TWS ou Gateway) qui tourne en continu, contrairement aux brokers crypto comme Binance. Cela ajoute de la complexite mais offre un acces multi-asset professionnel.
2. Stratégie : Momentum Cross-Section SP500
La stratégie momentum selectionne les actions du SP500 avec la meilleure performance relative sur les 12 derniers mois :
- Univers : Top-50 SP500 par capitalisation
- Signal : Return sur 252 jours (12 mois)
- Sélection : Top 10 momentum (les plus performants)
- Rebalancement : Mensuel
- Poids : Equiponderable (10% par position)
Le design en trois choix deliberes :
- Lookback 12 mois : l’horizon canonique de la litterature momentum (Jegadeesh-Titman). Assez long pour capter la tendance, assez court pour se rebalancer avant l’erosion classique du signal.
- Top 10 sur top 50 : restreindre l’univers aux 50 plus grosses capitalisations du SP500 elimine les petits noms ou le slippage paper/live diverge ; n’en garder que 10 concentre le signal sans concentrer le risque sectoriel au-dela du raisonnable.
- Rebalancement mensuel, equal weight : un turnover maitrise (~10 ordres/mois en moyenne, 837 ordres sur 84 mois mesures plus bas) garde les couts de transaction dans la zone ou la comparaison backtest/paper reste interpretable.
Chaque parametre se falsifie : un lookback de 3 mois expose au reversal court terme documente ; elargir le top 10 dilue le signal.
# Algorithme LEAN : Momentum SP500 via IBKR
# (version corrigee et reellement executee sur QC Cloud -- voir section 3 :
# fix 1 = tuple (annee, mois) du rebalancement, fix 2 = history() sans filtre
# de slice, fix 3 = garde selection vide)
LEAN_ALGORITHM = '''# region imports
from AlgorithmImports import *
# endregion
class SP500MomentumIBKR(QCAlgorithm):
"""
Cross-sectional momentum on SP500 top-50.
IBKR paper trading demonstration.
Logic:
- Rank top-50 SP500 stocks by 12-month return
- Hold top 10 momentum winners
- Rebalance monthly, equal weight
"""
def initialize(self):
self.set_start_date(2018, 1, 1)
self.set_end_date(2024, 12, 31)
self.set_cash(100000)
# Brokerage IBKR
self.set_brokerage_model(
BrokerageName.INTERACTIVE_BROKERS_BROKERAGE,
AccountType.MARGIN
)
# SP500 top-50 par market cap (snapshot statique)
self.tickers = [
"AAPL", "MSFT", "NVDA", "AMZN", "GOOGL",
"META", "BRK.B", "LLY", "TSM", "AVGO",
"JPM", "XOM", "UNH", "V", "PG",
"JNJ", "MA", "HD", "COST", "ABBV",
"MRK", "CRM", "ORCL", "WMT", "BAC",
"NFLX", "AMD", "ADBE", "CVX", "KO",
"PEP", "TMO", "CSCO", "MCD", "ABT",
"INTC", "ACN", "DHR", "VZ", "WFC",
"TXN", "NEE", "PM", "NKE", "LIN",
"RTX", "BMY", "QCOM", "HON", "UPS",
]
self.symbols = []
for ticker in self.tickers:
equity = self.add_equity(ticker, Resolution.DAILY)
self.symbols.append(equity.symbol)
# Benchmark
self.set_benchmark("SPY")
self.set_warm_up(252, Resolution.DAILY)
# Parametres
self.num_holdings = 10
self.momentum_period = 252 # 12 mois
# Fix 1 (defaut de rebalancement) : la version originale comparait
# last_rebalance < self.time.month, ce qui casse au changement
# d'annee (12 < 1 est toujours faux -> plus aucun rebalancement
# apres la premiere annee). On compare le tuple (annee, mois).
self.last_rebalance = None
def on_data(self, data):
if self.is_warming_up:
return
# Rebalancement mensuel (debut du mois)
if self.time.day < 5:
current = (self.time.year, self.time.month)
if self.last_rebalance != current:
self._rebalance(data)
self.last_rebalance = current
def _rebalance(self, data):
# Calculer les retours momentum.
# Fix 2 (defaut de donnees) : la version originale filtrait
# `sym not in data` (barre du jour presente dans la slice), ce qui
# peut vider toute la selection certains jours et craser ensuite sur
# top_n[0]. self.history() est time-based et ne depend pas de la
# slice du jour : on l'interroge directement pour chaque symbole.
momentum_scores = {}
for sym in self.symbols:
history = self.history(sym, self.momentum_period, Resolution.DAILY)
if history is None or len(history) < self.momentum_period * 0.8:
continue
closes = history["close"] if isinstance(history, pd.DataFrame) else history.close
ret = (closes.iloc[-1] / closes.iloc[0]) - 1
momentum_scores[sym] = ret
# Fix 3 : ne jamais supposer la selection non vide
if not momentum_scores:
self.log("REBALANCE skipped: historique insuffisant")
return
# Selectionner top N momentum
sorted_momentum = sorted(momentum_scores.items(), key=lambda x: x[1], reverse=True)
top_n = sorted_momentum[:self.num_holdings]
target_symbols = set(sym for sym, _ in top_n)
# Liquidier les positions qui ne sont plus dans le top
for sym in self.symbols:
if self.portfolio[sym].invested and sym not in target_symbols:
self.liquidate(sym)
# Acheter les nouvelles positions
weight = 1.0 / self.num_holdings
for sym, ret in top_n:
self.set_holdings(sym, weight)
self.log(f"REBALANCE: Top-10 momentum, best={top_n[0][0].value} (+{top_n[0][1]:.1%})")
def on_end_of_algorithm(self):
final = self.portfolio.total_portfolio_value
ret = (final - 100000) / 100000
self.log(f"SP500 Momentum IBKR: Final=${final:,.2f}, Return={ret:.2%}")
'''
print("Algorithme LEAN SP500 Momentum pret.")
print(f"Taille: {len(LEAN_ALGORITHM)} caracteres")Algorithme LEAN SP500 Momentum pret.
Taille: 4209 caracteres
Lecture de l’algorithme LEAN
Le listing (4 209 caracteres) suit la structure canonique LEAN : initialize() definit l’univers (top 50 SP500, classe par rendement 12 mois), le calendrier de rebalancement mensuel equal-weight et le warm-up ; le handler de donnees ne fait que mettre a jour les scores — toute la decision vit dans le rebalancement programme. Pour le paper IBKR, rien ne change dans le code : c’est la configuration de deploiement (section 4) qui bascule le meme algorithme du node backtest vers le compte paper TWS. Un seul implementation, trois modes d’execution — c’est le point structurel du workflow QC.
Transition : Vers le backtest historique
L’algorithme LEAN est maintenant défini. Nous allons l’executer sur des données historiques SP500 (2018-2024) pour valider la performance de la stratégie momentum avant le deploiement en paper trading.
La sequence backtest -> paper -> live n’est pas une formalite : chaque etape echoue sur des causes differentes. Le backtest echoue sur le biais de donnees ; le paper trading echoue sur l’implementation (ordres, timing, connexion) ; le live echoue sur l’impact de marche. Valider les trois, c’est trier les trois familles de causes.
3. Backtest Historique
La strategie momentum est bien documentee dans la litterature academique (Jegadeesh & Titman, 1993). Sur le SP500, le momentum 12 mois a historiquement genere un premium annualise de 5-8%.
Les resultats ci-dessous sont reels : l’algorithme de la section 2 (avec ses trois correctifs documentes) a ete compile puis execute sur QuantConnect Cloud sur la fenetre annoncee 2018-01-01 -> 2024-12-31, avec deux benchmarks alignes (SPY Buy & Hold et Equal Weight Top-50, memes frais IBKR, meme fenetre). Les metriques proviennent du run qcpy41-momentum-v2-fixed-2018-2024 (backtestId 0a3ced5c, projet 36103410) et des deux runs benchmarks, lus via read_backtest. La methode de lecture – comparer au benchmark, ajuster au risque, puis douter des couts – reste la meme.
# Resultats de backtest REELS : lus via read_backtest (API QC Cloud v2)
# Runs du 2026-09-04 : momentum 0a3ced5c / equal-weight 91bd8f2b / SPY 4e691dc0
# Series completes et identifiants : qc41_backtest_results.json (a cote du notebook)
import json as _json
with open('qc41_backtest_results.json', encoding='utf-8') as _f:
_artifact = _json.load(_f)
backtest_results = {
'strategy': 'SP500 Momentum Top-10 (12m lookback) -- run reel QC Cloud',
'period': _artifact['window'],
'initial_capital': _artifact['initial_capital'],
'metrics': {k: _artifact['runs']['momentum']['statistics'][k]
for k in ('Net Profit', 'Compounding Annual Return', 'Sharpe Ratio',
'Drawdown', 'Total Orders', 'Monthly Win Rate (computed)')},
'benchmark': {
'SPY Buy & Hold (run reel)': _artifact['runs']['spy_buyhold']['statistics']['Net Profit'] + ' -- Sharpe ' + _artifact['runs']['spy_buyhold']['statistics']['Sharpe Ratio'],
'Equal Weight Top-50 (run reel)': _artifact['runs']['equalweight']['statistics']['Net Profit'] + ' -- Sharpe ' + _artifact['runs']['equalweight']['statistics']['Sharpe Ratio'],
},
}
print(f"=== Backtest: {backtest_results['strategy']} ===")
print(f"Run: {_artifact['runs']['momentum']['backtestName']} (backtestId {_artifact['runs']['momentum']['backtestId'][:8]}, {_artifact['runs']['momentum']['totalOrders']} ordres)")
print(f"Periode: {backtest_results['period']}")
print(f"Capital initial: ${backtest_results['initial_capital']:,}")
print("\nMetriques mesurees:")
for k, v in backtest_results['metrics'].items():
print(f" {k}: {v}")
print("\nBenchmarks mesures (meme fenetre, memes frais IBKR):")
for k, v in backtest_results['benchmark'].items():
print(f" {k}: {v}")=== Backtest: SP500 Momentum Top-10 (12m lookback) -- run reel QC Cloud ===
Run: qcpy41-momentum-v2-fixed-2018-2024 (backtestId 0a3ced5c, 837 ordres)
Periode: 2018-01-01 to 2024-12-31
Capital initial: $100,000
Metriques mesurees:
Net Profit: 398.142%
Compounding Annual Return: 25.764%
Sharpe Ratio: 0.821
Drawdown: 28.100%
Total Orders: 837
Monthly Win Rate (computed): 55/83 mois
Benchmarks mesures (meme fenetre, memes frais IBKR):
SPY Buy & Hold (run reel): 143.512% -- Sharpe 0.493
Equal Weight Top-50 (run reel): 219.282% -- Sharpe 0.696
Lecture du resultat : +398% total, Sharpe 0.82, MaxDD 28.1%
Les trois nombres se lisent ensemble. Le +398.1% sur 2018-2024 parait spectaculaire, et il faut le rapporter : le SPY Buy & Hold fait +143.5% et l’equal-weight top-50 +219.3% sur la meme fenetre et avec les memes frais – l’exces du momentum vaut ~255 points sur le SPY et ~179 points sur l’equal-weight, porte par la concentration dans les gagnants de la decennie (2020-2021 mega-tech). Le Sharpe 0.821 ajuste au risque : meilleur que les deux benchmarks (SPY 0.493, equal-weight 0.696) mais sous 1 – l’edge est reel sur cette fenetre, pas une machine a alpha. Le MaxDD 28.1% reste sous celui du SPY (33.6%) et proche de l’equal-weight (28.9%) : la concentration top-10 paie en trend sans creuser de pertes plus profondes que le marche sur cette periode. Enfin 837 ordres sur 84 mois (~10/mois, coherent avec un rebalancement mensuel de 10 positions) et 55 mois gagnants sur 83 (66%) : assez de bons mois pour compenser les mauvais, dans le registre attendu d’un Sharpe < 1. Honnetete : un seul chemin historique, aucune validation out-of-sample ni multi-parametre – la lecture pedagogique est la methode, pas la promesse d’un edge perpetual.
Exercice 2 : Estimation des couts de transaction IBKR
Estimez les couts de transaction mensuels pour une stratégie SP500 momentum avec IBKR : frais de commission ($0.005/action), frais SEC, et slippage estime.
Indices : - # Indice : IBKR commission = $0.005 par action, minimum $1.00 - # Indice : Estimez 4 trades par semaine, 100 actions par trade - # Étape 1 : Calculer le cout par trade (commission + SEC fee) - # Étape 2 : Multiplier par le nombre de trades mensuel - # Étape 3 : Ajouter le slippage estime (5 bps par trade)
# Exercice 2 : Couts de transaction IBKR
# TODO etudiant : Estimer les couts mensuels de transaction
# Etape 1 : Cout par trade = max($1.00, 100 * $0.005) + SEC fee
# Etape 2 : Trades/mois = 4/semaine * 4 semaines = 16
# Etape 3 : Ajouter slippage 5 bps * valeur_trade * nb_trades
monthly_costs = None # TODO etudiant : remplacer par le calcul
print("Exercice a completer : Couts de transaction IBKR")Exercice a completer : Couts de transaction IBKR
Synthese : Paper Trading avec IBKR
Ce notebook a presente le workflow complet de backtest vers paper trading sur equities via Interactive Brokers. La stratégie momentum SP500 a demontre une performance historique solide sur la fenetre testee (+398.1%, CAGR 25.8%, Sharpe 0.82 – mesures QC Cloud), mais necessite une surveillance continue du TWS pour garantir la connexion stable.
La synthese a retenir avant deploiement : le paper trading IBKR ne prouve pas que la strategie fera de l’argent (le backtest est un chemin historique unique, et le paper herite de ses hypotheses) — il prouve que la strategie s’execute comme concue : les ordres partent, se remplissent aux prix attendus, et l’equity curve paper suit la backteste a l’interieur d’une bande de bruit mesurable.
# Visualisation equity curve vs benchmark -- series REELLES du run QC Cloud
# (echantillonnage hebdomadaire depuis qc41_backtest_results.json ; le point de
# drawdown max et le dernier point sont toujours conserves)
fig, axes = plt.subplots(1, 2, figsize=(14, 5))
_wk = _artifact['runs']['momentum']['weekly']
dates = pd.to_datetime(_wk['dates'])
mom_equity = np.array(_wk['equity'])
spy_dates = pd.to_datetime(_wk['benchmark_dates'])
spy_equity = np.array(_wk['spy_equity_100k'])
axes[0].plot(dates, mom_equity, color='#2196F3', linewidth=1.5, label='Momentum Top-10 (mesure)')
axes[0].plot(spy_dates, spy_equity, color='#FF9800', linewidth=1.5, label='SPY B&H (mesure, base 100k)', alpha=0.8)
axes[0].set_title('Equity Curve vs Benchmark (run reel QC Cloud)')
axes[0].set_ylabel('Valeur Portfolio ($)')
axes[0].legend()
axes[0].tick_params(axis='x', rotation=45)
dd_dates = pd.to_datetime(_wk['dd_dates'])
drawdown = np.array(_wk['drawdown_pct'])
axes[1].fill_between(dd_dates, drawdown, 0, alpha=0.4, color='#F44336')
axes[1].set_title('Drawdown Momentum (%) -- serie native QC')
axes[1].set_ylabel('Drawdown (%)')
axes[1].tick_params(axis='x', rotation=45)
plt.tight_layout()
plt.show()
plt.close()
Lecture des courbes
La serie hebdomadaire (414 points) couvre toute la fenetre du run, du 2018-01-01 (100 000 dollars, warm-up consomme sur donnees pre-debut) au 2024-12-31 : QC sert sa serie chart native a granularite ~2,4 points/semaine (1 236 points), et l’echantillonnage un-point-sur-trois en garde ~414 en conservant toujours le dernier point et le minimum de drawdown. La figure trace les trajectoires mesurees du run : la momentum en bleu (498 142 $ a fin 2024), le SPY rebase a 100 000 $ en orange. Ce qui interesse l’oeil n’est pas la pente finale mais les episodes de divergence – 2018-2019 ou la momentum suit le marche, puis 2020-2021 ou elle creuse un ecart considerable (concentration mega-tech), ecart partiellement rend en 2022 puis reconstruit en 2024. Le panneau de droite montre la contrepartie : le drawdown max mesure (-28.1%, covid mars 2020) reste sous celui du SPY sur la meme periode. En paper trading reel, la comparaison pertinente suivante n’est pas strategy-vs-benchmark mais equity paper vs equity backtest : deux courbes issues du meme signal doivent rester dans une bande etroite ; une divergence croissante signale que l’execution (fills, timing) n’implemente pas la meme strategie que le modele.
4. Configuration IBKR et Prerequis TWS
Contrairement a Binance, IBKR necessite une infrastructure locale :
Checklist de configuration
- Compte IBKR paper trading actif
- TWS (Trader Workstation) telecharge et installe
- Configuration API dans TWS :
- Edit > Global Configuration > API > Settings
- Cocher “Enable ActiveX and Socket Clients”
- Port : 7497 (paper) / 7496 (live)
- Deconnecter “Read-Only API”
- IB Gateway (alternative a TWS, plus leger)
- Redemarrage hebdomadaire avec 2FA
Les prerequis en pratique :
- TWS ou IB Gateway : la passerelle locale qui parle le protocole IBKR. TWS (interface complete) convient au debugging ; IB Gateway (headless) a la production paper. Port 7497 en paper, 7496 en live — les confondre est l’erreur classique qui fait tourner une strategie “paper” sur un compte reel.
- Compte paper IBKR : gratuit, ouvert depuis le compte reel, capital virtuel configurable.
- Redemarrage hebdomadaire : l’API IBKR exige une re-authentification periodique (champ
ib-weekly-restart-utc-timedans la configuration ci-dessous) — un paper trading IBKR ne tourne pas indefiniment sans intervention planifiee.
# Configuration IBKR pour le deploiement via MCP QC
ibkr_config = {
"brokerage": "InteractiveBrokersBrokerage",
"paper_port": 7497,
"live_port": 7496,
"required_fields": [
"ib-user-name",
"ib-account",
"ib-password",
"ib-weekly-restart-utc-time",
],
"weekly_restart": "04:00:00", # Dimanche 04h UTC
}
# Deploy via MCP QC (reference)
deploy_reference = """
create_live_algorithm(
model={
"projectId": PROJECT_ID,
"compileId": compile_id,
"nodeId": "LN-xxxxx", # L-MICRO node
"versionId": "-1",
"brokerage": {
"id": "InteractiveBrokersBrokerage",
"ib-user-name": "<username>",
"ib-account": "DU12345", # Paper account ID
"ib-password": "<password>",
"ib-weekly-restart-utc-time": "04:00:00"
},
"dataProviders": {
"QuantConnectBrokerage": {
"id": "QuantConnectBrokerage"
}
}
}
)
"""
print("Configuration IBKR Paper Trading:")
for k, v in ibkr_config.items():
print(f" {k}: {v}")Configuration IBKR Paper Trading:
brokerage: InteractiveBrokersBrokerage
paper_port: 7497
live_port: 7496
required_fields: ['ib-user-name', 'ib-account', 'ib-password', 'ib-weekly-restart-utc-time']
weekly_restart: 04:00:00
5. Deploy en Paper Trading
Le deploiement IBKR suit le même workflow que Binance, avec des credentials différents.
La différence principale : IBKR necessite TWS qui tourne en local. Le node live QC se connecte au TWS via l’API socket.
Internet → QC Node Live → API Socket → TWS (local) → IBKR Paper Market
Le deploiement suit le chemin MCP standard (compile -> node -> create_live avec la configuration IBKR), avec une difference notoire : la configuration exige les identifiants IBKR et suppose que TWS tourne cote machine au moment du deploiement. Les identifiants restent dans les champs securises QC, jamais dans le notebook (regle secrets : aucun literal).
6. Monitoring
Les mêmes outils MCP QC que pour Binance s’appliquent : read_live_algorithm, read_live_orders, read_live_logs, read_live_portfolio.
Points spécifiques IBKR a surveiller : - Connection TWS : si TWS s’arrete, le node live perd la connexion - Weekly restart : verifier que le redemarrage automatique fonctionne - Order fills : les fills paper TWS peuvent differer legerement du live
Que surveiller, dans l’ordre de gravite :
- La connexion TWS : premier point de rupture (impact Eleve dans la table d’ecarts de cette section). Une deconnexion prolongee laisse des ordres orphelins cote broker.
- Le fill rate et le slippage : l’ecart systematique entre prix backtest et prix paper est le premier signal que l’implementation diverge du modele.
- La tracking error vs backtest : l’equity paper doit suivre la backteste a l’interieur d’une bande ; en sortir revelerait un bug de calendrier de rebalancement ou un univers desynchronise.
# Comparaison des ecarts backtest vs paper trading IBKR
ecarts_ibkr = pd.DataFrame({
'Source': ['Slippage', 'Timing', 'Liquidite', 'Market impact', 'Connection'],
'Impact estime': ['Faible', 'Moyen', 'Faible', 'Faible', 'Eleve (TWS)'],
'Description': [
'Fills paper TWS proches du live sur grosses caps',
'Latence TWS + QC node, quelques ms',
'SP500 top-50: tres liquide, pas de probleme',
'Taille portfolio petite vs volume daily',
'Risque de deconnexion si TWS plante'
]
})
print("Ecarts backtest vs paper trading (IBKR):")
print(ecarts_ibkr.to_string(index=False))Ecarts backtest vs paper trading (IBKR):
Source Impact estime Description
Slippage Faible Fills paper TWS proches du live sur grosses caps
Timing Moyen Latence TWS + QC node, quelques ms
Liquidite Faible SP500 top-50: tres liquide, pas de probleme
Market impact Faible Taille portfolio petite vs volume daily
Connection Eleve (TWS) Risque de deconnexion si TWS plante
Lecture de la table d’ecarts
Sur les cinq sources d’ecart, trois sont Faible — le privilege d’un univers SP500 top-50 tres liquide avec des positions petites face au volume quotidien. La ligne qui commande l’attention est Connection (Eleve) : TWS est un processus desktop dont depend toute la chaine ; s’il plante ou se deconnecte, les ordres en vol restent cote broker sans que l’algorithme le voie. C’est pourquoi le monitoring de la section 6 met la connexion en tete, et pourquoi IB Gateway (headless) est preferable des qu’on laisse tourner plus de quelques jours. Timing (Moyen) est le seul ecart proprement economique : quelques millisecondes de latence TWS + noeud QC, sans effet mesurable au rebalancement mensuel.
Exercice 3 : Checklist de validation paper-to-live
Créez une checklist de validation avant de passer du paper trading au live trading avec IBKR. Chaque item doit avoir un seuil quantitatif.
Indices : - # Indice : Inspirez-vous de la checklist du notebook QC-Py-27 - # Étape 1 : Lister les metriques cles (Sharpe, Drawdown, Fill Rate, Slippage) - # Étape 2 : Définir un seuil pour chaque metrique - # Étape 3 : Créer un DataFrame avec les items et les seuils
# Exercice 3 : Checklist paper-to-live IBKR
# TODO etudiant : Creer une checklist de validation avec seuils quantitatifs
# Etape 1 : Lister les metriques cles (Sharpe, MaxDD, FillRate, Slippage)
# Etape 2 : Definir des seuils (Sharpe>1, MaxDD<15%, FillRate>90%, Slippage<10bps)
# Etape 3 : Creer un DataFrame checklist
checklist = None # TODO etudiant : remplacer par le DataFrame
print("Exercice a completer : Checklist paper-to-live IBKR")Exercice a completer : Checklist paper-to-live IBKR
7. Exemple guide : Ajouter un Filtre de Volatilite
La stratégie momentum pure peut souffrir pendant les regimes de marche volatils (ex: mars 2020). L’objectif est d’ajouter un filtre de volatilite pour reduire l’exposition pendant les periodes turbulentes.
Approche proposee
- Calculer la volatilite realisee du SP500 sur 20 jours
- Si vol > seuil, reduire le nombre de positions (ou passer en cash)
- Paramètres a optimiser :
vol_period,vol_threshold,reduction_factor
Pourquoi un filtre de volatilite : le momentum concentre ses pertes exactement quand la volatilite explose — les positions momentum correlent negativement avec la vol dans les queues de distribution. Reduire l’exposition quand la vol depasse un seuil ne corrige pas la logique, elle redimensionne la queue de perte. C’est l’exemple guide le plus proche d’une amelioration que le paper trading permet de mesurer honnetement : comparer la courbe avec et sans filtre sur la meme periode.
#Exemple guide: implementer le filtre de volatilite
# Parametres du filtre
vol_config = {
"vol_period": 20, # Period pour le calcul de vol
"vol_threshold": None, # Exercice: seuil de vol (ex: 0.30 = 30% annualisee)
"reduction_factor": None, # Exercice: facteur de reduction (ex: 0.5 = moitie des positions)
}
#Exemple guide: implementer la fonction ci-dessous
def apply_vol_filter(momentum_scores: Dict, spy_vol: float,
config: Dict) -> Dict:
"""
Filtre les signaux momentum en fonction de la volatilite.
Args:
momentum_scores: {symbol: momentum_return}
spy_vol: volatilite realisee du SP500
config: parametres du filtre
Returns:
momentum_scores filtres
"""
# Exercice: implementer la logique
# Indice 1 : si spy_vol > vol_threshold, garder uniquement
# les N * reduction_factor meilleures positions
# Indice 2 : si spy_vol <= vol_threshold, ne rien changer
pass # Exercice etudiant
# Indice : volatilite annualisee = std(returns daily) * sqrt(252)
# Exemple : si SPY daily returns std = 0.015, vol annualisee = 0.015 * sqrt(252) = 23.8%
print("Filtre de volatilite a implementer.")
print("Indice: volatilite annualisee = std(daily_returns) * sqrt(252)")
print(f"Config actuelle: {vol_config}")Filtre de volatilite a implementer.
Indice: volatilite annualisee = std(daily_returns) * sqrt(252)
Config actuelle: {'vol_period': 20, 'vol_threshold': None, 'reduction_factor': None}
Lecture du guide : le filtre de volatilite en pratique
Le coeur de l’exercice tient dans la signature apply_vol_filter(momentum_scores, spy_vol, config). C’est une fonction pure : elle recoit les scores momentum ({symbol: return}), une volatilite realisee du SP500 et le config, et retourne un dict filtre — pas de broker, pas d’etat, pas de side-effect. Cette forme n’est pas anodine : elle rend le filtre testable sans deployement (on peut appeler apply_vol_filter({...}, 0.42, {...}) et verifier que le portefeuille se reduit), et c’est elle qui rend possible la comparaison honnete qu’annonce l’introduction de la section — meme signal, meme periode, avec et sans filtre.
Deux parametres sont laisses a l’exercice (vol_threshold, reduction_factor) parce que ce sont les deux degres de liberte du filtre. vol_period=20 fige la fenetre de volatilite realisee ; vol_threshold fixe le declencheur (0.30 = 30% annualisee, l’ordre de grandeur d’un marche en tension) ; reduction_factor dimensionne le retrait (0.5 = moitie des positions, en gardant les meilleurs scores). L’indice vol = std(returns daily) * sqrt(252) est la convention d’annualisation : 0.015 de volatilite quotidienne ≈ 23.8% annualisee — le passage de l’echelle journaliere a l’echelle annuelle se fait par une multiplication par la racine du nombre de jours de trading.
Le point pedagogique : un filtre de volatilite ne change pas le signal momentum, il redimensionne l’exposition. Quand spy_vol > vol_threshold, on ne corrige pas la selection, on reduit le nombre de positions ; quand spy_vol <= vol_threshold, on ne touche a rien. C’est exactement ce qui permet de repondre a la question 3 des questions de reflexion ci-dessous : le filtre ameliore le ratio de Sharpe si et seulement si le gain sur la queue (moins de pertes extremes) depasse la perte de participation (moins de hausse quand la vol est haute sans que le marche s’ecroule). C’est une question empirique que les trois parametres optimises trancheront sur la periode mesuree — rappelons le MaxDD mesure de la section 3 (28.1%, la pire queue de la fenetre 2018-2024), point exact que le filtre vise a amputer — pas une garantie a priori : l’equity curve avec et sans filtre, sur la meme fenetre, est le juge, et l’artefact JSON cote du notebook contient les series pour le refaire.
# Question de reflexion
questions = [
"1. Pourquoi le momentum fonctionne-t-il historiquement sur le SP500 ?",
"2. Quel est le risque principal d'une strategie momentum en paper trading ?",
"3. Comment le filtre de volatilite peut-il ameliorer le ratio de Sharpe ?",
"4. Quelle est la difference de comportement entre Binance et IBKR en paper trading ?",
]
#Exemple guide: repondre aux questions dans une nouvelle cellule markdown
for q in questions:
print(q)
print("\nTODO: Creez une cellule markdown ci-dessous pour vos reponses.")1. Pourquoi le momentum fonctionne-t-il historiquement sur le SP500 ?
2. Quel est le risque principal d'une strategie momentum en paper trading ?
3. Comment le filtre de volatilite peut-il ameliorer le ratio de Sharpe ?
4. Quelle est la difference de comportement entre Binance et IBKR en paper trading ?
TODO: Creez une cellule markdown ci-dessous pour vos reponses.
Conclusion
Ce notebook a couvert le workflow paper trading sur equities via IBKR :
- IBKR : broker multi-asset, prerequis TWS, configuration API
- Stratégie : momentum cross-section SP500 (top-10, 12 mois)
- Backtest : validation sur 7 ans, comparison avec SPY
- Deploy : workflow MCP QC avec credentials IBKR
- Monitoring : surveillance et ecarts backtest/paper
- Exercice : filtre de volatilite pour ameliorer la robustesse
References
- Jegadeesh & Titman (1993), “Returns to Buying Winners and Selling Losers”
- QC IBKR Brokerage Docs
- QC Paper Trading
Ce qu’il faut retenir :
- Le paper trading IBKR valide l’implementation, pas l’alpha — les metriques backtest de ce notebook sont mesurees sur un seul chemin historique (2018-2024, run QC Cloud) – une preuve d’executabilite, pas une promesse d’alpha.
- La comparaison Binance vs IBKR est une question de fidelite du simulateur (tick, order updates, multi-asset) contre simplicite d’acces (testnet gratuit, zero prerequis).
- La chaine TWS (port 7497, redemarrage hebdo) est le prix operationnel du realisme — et le premier point de panne a monitorer.
- La demarche paper-to-live se gate avec des seuils quantitatifs (fill rate, slippage, tracking error), jamais avec des impressions.
Et apres : QC-Py-27 (Production Deployment) pousse le passage au live ; le filtre de volatilite de l’exemple guide se benchmark proprement sur la periode 2018-2024 mesuree plus haut (l’artefact JSON contient les series pour le refaire).