IBKR Trader Workstation pour bots systématiques : la carte opérationnelle que personne ne publie
Les docs d'Interactive Brokers couvrent l'API, les symbols, les types d'ordres. Elles ne couvrent pas ce qui casse vraiment quand vous faites tourner un vrai bot contre un compte paper à 3h du matin — les fenêtres de re-login IBC, le trap du trusted-IP sur le port 4002, l'état gateway unhealthy-mais-Up, le wrapper de rejet d'ordre dont vous avez besoin mais qui n'est pas documenté. Voici ce qu'on a appris en shippant des bots live contre IBKR pendant 12 mois.
Interactive Brokers a de la documentation. Elle couvre les signatures d'appels API, les conventions de symbols, la matrice de types d'ordres, les sémantiques limit/market/bracket. Ce que les docs ne disent pas, c'est ce qui casse vraiment quand vous faites tourner un vrai bot contre le compte paper de production à 3h du matin heure de Paris un mardi, puis re-deploy un code change à 9h le matin suivant pendant que la session européenne est ouverte.
On a appris le gap à la dure. Cet article est la carte opérationnelle qu'on aurait souhaité avoir avant de commencer — écrit depuis douze mois de shipping de bots contre IBKR, huit de ces mois à les faire tourner 24/5 contre le compte paper, et une outage silencieuse de 38 heures qui nous a appris pourquoi le container étant Up n'est pas la même chose que le bot qui marche.
Si vous évaluez IBKR comme broker pour l'exécution systématique, ou que vous en opérez déjà un et que vous hittez de la friction que vous n'avez pas de mots pour, c'est pour vous.
L'architecture de setup (qu'est-ce qui tourne où)
Un stack tradfi systématique IBKR fonctionnel a trois pièces mobiles :
- IB Gateway (ou TWS) — le software côté IBKR qui tient la session network avec leurs serveurs. Gateway est la variante headless ; TWS est la version desktop avec UI. Pour les bots, Gateway gagne — pas de fenêtre qui s'ouvre, pas de drift sur les updates de version UI, et l'image Docker gnzsnz/ib-gateway bundle toutes les pièces de bootstrap (IBC pour le login non-interactif, Xvfb pour le framebuffer, la boucle de daily-forced-restart qu'IBKR mandate).
- Votre process bot — le process Python (ou n'importe quoi) qui ouvre une connection TCP à la gateway, place des ordres, écoute les fills.
- L'état — les fichiers JSON / SQLite qui trackent les positions, l'equity, les métriques de risque, l'historique des trades. Ces fichiers vivent EN-DEHORS des deux processes, sur le filesystem host, bind-mounted dans le container bot.
Les trois pièces sont sur le même host. Le bot connect à Gateway via un network Docker interne. L'état vit sur disque, bind-mounté. Simple en concept ; la friction est dans les détails.
Le trap trusted-IP du port 4002
La première-semaine erreur la plus commune : pointer votre bot vers le port 4002.
IB Gateway écoute sur 127.0.0.1:4002 avec une allowlist trusted-IP hardcodée de 127.0.0.1. Si votre bot connecte depuis n'importe quelle autre IP — incluant un container Docker sibling sur le même host — le handshake TCP succeed, puis le handshake API drop silencieusement. Votre bot attend le timeout nextValidId complet de 15 secondes, throw, retry, et attend encore.
Le fix n'est pas dans les docs d'IBKR. C'est un forwarder socat à l'intérieur du container gateway : socat TCP-LISTEN:4004,fork TCP:127.0.0.1:4002. Votre bot connecte au port 4004 externalement ; socat le forward au 4002 avec l'adresse source réécrite en 127.0.0.1 ; la gateway voit une source trusted-IP et accepte la session API.
L'image gnzsnz ship avec ce forwarder pré-wired. Si vous buildez votre propre image gateway, vous devez ajouter la couche socat vous-même. On a vu ça trip up chaque team qui essaie de roll une custom gateway image au jour un.
(À savoir aussi : la variable d'env TRUSTED_IPS que certaines docs référencent n'est pas wired dans le template jts.ini.tmpl dans cette image. Ne gaspillez pas une soirée à la setter — le chemin socat est le fonctionnel.)
Re-login IBC = 90 secondes de downtime API, à chaque fois
IB Gateway doit se re-logger aux serveurs IBKR toutes les 24 heures — IBKR le force. L'image gnzsnz gère ça via un restart IBC scheduled. Du point de vue de votre bot, ça veut dire chaque restart container, chaque re-login forcé nocturne, chaque docker compose change qui touche la gateway trigger ~90 secondes de downtime API.
Votre bot a besoin d'une retry policy saine. Connect timeout 15 secondes, exponential backoff (30s / 60s / 120s), abandonner après 5 tentatives et alerter. On a vu des bots écrits sans cette assumption fall over permanently au premier daily restart et jamais auto-recover.
Side effect à savoir : docker compose up -d <bot-service> recrée les services dépendants. Parce que tradfi-ibkr-bot depends_on ibkr-gateway, toucher le bot bounce aussi la gateway. Chaque code change coûte 90 secondes de downtime API. Planifiez les deploys en conséquence.
« Existing session detected » — pick a primary
Si vos credentials paper sont loggés depuis une deuxième machine — votre IB Gateway local Win11, une vieille instance VPS, n'importe où — la nouvelle gateway hit un dialog modal : « Existing session detected. Choose : take over (primary) ou run secondary (read-only). »
Une gateway headless ne peut pas cliquer. Sans la bonne env var, elle reste au dialog pour toujours.
Settez EXISTING_SESSION_DETECTED_ACTION=primary dans l'env gateway. La nouvelle session prend over en ~30 secondes ; l'ancienne se disconnect. C'est le bon défaut pour un bot de production — vous voulez que l'instance déployée gagne, pas n'importe quoi qui est ouvert sur votre laptop.
(Aussi : fermez votre Gateway local quand vous n'êtes pas actively en train de l'utiliser. Même avec primary set, le reconnect flapping waste des log lines.)
Gestion d'état : les positions NE SONT PAS dans vos fichiers
Le plus gros ajustement de modèle mental : le positions_state.json de votre bot est un cache, pas une source de vérité. La source de vérité c'est le serveur IBKR. Votre bot rebuild le cache depuis reqPositions() à chaque cold start.
Pourquoi ça compte : si votre bot crash mid-trade, restart, et trouve une position dans positions_state.json qui ne matche pas ce que reqPositions() return, trust IBKR. On a shippé un pattern force-reconcile qui wipe l'état local et rebuild depuis reqPositions() sur détection de n'importe quel mismatch. C'est la seule policy sensée.
Cinq fichiers d'état, tous bind-mountés du host dans le container :
| Fichier | Purpose |
|---|---|
| live_status.json | Heartbeat + equity, overwritten chaque cycle. Lu par le dashboard pusher. |
| risk_state.json | Tracking de drawdown quotidien, watermark de max-equity. Persiste à travers les restarts. |
| trade_vault.db | SQLite de tous les trades (open + closed). Record permanent. |
| analyst_metrics.json | Breakdown de performance par stratégie. |
| dynamic_pairs.json | Pairs ajoutés par le process d'expansion d'univers. |
Trap bind-mount critique : si le fichier n'existe pas sur le host quand docker compose up run, Docker crée un directory là, puis refuse de le mount-as-a-file. Toujours pré-touch les JSONs (echo '{}' > file.json) avant le premier deploy. Skip ça et vous obtenez des mount errors cryptiques qui looks comme des issues de permissions.
Les ordres SL/TP vivent chez IBKR — pas dans votre bot
C'est ce qui rend IBKR genuinement viable pour de l'exécution systématique unattended : les stop-loss et take-profit orders sont placés au serveur IBKR, pas held dans la mémoire de votre bot. Si votre bot crash, la position garde sa protection. Si votre VPS reboot, la position garde sa protection. Si vous perdez l'internet pendant 4 heures, la position garde sa protection.
Le pattern c'est : ouvrir la position comme un market ou limit order ; immédiatement submit le SL et le TP comme bracket children indépendants avec outsideRTH=true et tif=GTC. Ils sittent chez IBKR jusqu'à ce qu'ils fill ou que vous les cancel. Votre bot peut être off-line pendant des jours et la position est safe.
C'est la propriété opérationnelle qui nous laisse tourner unattended overnight. Pas tous les brokers offrent ça — beaucoup de venues crypto exigent que le bot hold les SL/TP orders en mémoire et les place réactivement, ce qui veut dire qu'un bot crash vous expose. Le SL/TP côté broker d'IBKR est une des vraies raisons pour lesquelles on le préfère pour tradfi.
Le trap « unhealthy mais Up » (38 heures qu'on ne récupérera jamais)
En mai 2026 on a shippé un postmortem sur une outage silencieuse de 38 heures : le container gateway montrait Up dans docker ps, le bot tournait, mais chaque appel API return not connected. TWS lui-même était mort dans le container ; le PID 1 du container était le supervisor, pas TWS, donc le supervisor restait alive et Docker rapportait le container comme healthy.
La leçon : un container étant Up n'est pas la même chose que l'application à l'intérieur étant healthy. Vous avez besoin d'un healthcheck explicite qui exerce le chemin application. Pour notre gateway IBKR :
healthcheck:
test: ["CMD-SHELL", "nc -z 127.0.0.1 4002 || exit 1"]
interval: 60s
timeout: 10s
retries: 3
Ça catch le cas TWS-mort-sous-supervisor-vivant. On l'a déployé après l'incident. L'auto-remediation (restart sur unhealthy) est encore dans le backlog — on veut des yeux sur les premiers events unhealthy avant d'allumer l'auto-restart.
Gotchas de routage d'ordre
Quelques non-obvious qu'on a hit en faisant tourner des vraies stratégies :
- Rejet
EtradeOnly not supported: certains types d'ordres exigent un wrapper helper (_new_order()dans notre codebase) qui strip les flags style e-Trade d'IBKR. Sans ça, ~10% des ordres reject inexplicablement. Le fix est une fonction wrapper ; le bug est de ne pas l'avoir. - La log line
[10349 TIF]est purement cosmétique. La spec API d'IBKR liste 10349 comme un message informationnel, pas une erreur. Suppress-la dans votre error filter ou vous vous ferez page pour rien. - Drift d'univers de pairs : si votre bot a un composant « Hunter » qui auto-expand l'univers de trading, il essaiera parfois de re-ajouter des tickers que vous avez audit-bannés. On utilise un call
apply_audit_locks(strategies, merged_pairs)post-Hunter pour scrub les re-additions. Sans ça, votre blacklist leak silencieusement.
Paper to live : ce qui change vraiment
La tentation après 30 paper trades est de flip le switch. Ne le faites pas.
Notre policy : 30+ paper trades ET 3-6 mois de soak avant n'importe quel vrai argent. Les paper trades valident le wiring ; le soak valide que rien de bizarre n'arrive autour des earnings, des dividendes, des weekend gaps, des holidays, des fenêtres de disconnect d'exchange, des timings de re-login IBC pendant des périodes volatiles. Du stuff qui ne show pas dans 30 trades show dans 6 mois.
Quand vous flippez :
TRADING_MODE=paper→liveIBKR_PORT=4002→4001(la live gateway écoute sur un port différent)- Swap
IBKR_GATEWAY_USERetIBKR_GATEWAY_PASSWORDaux credentials live - Achetez un abonnement market data — environ $30/mois pour les US equities en retail, plus si vous avez besoin de flux exchange-spécifiques. Settez
IBKR_MARKET_DATA_TYPE=1pour data live ; 3 c'est différé. - Si votre compte est un sub-account cash (pas margin) : gardez
LONG_ONLY=true. Le bot ne doit pas essayer de short. Les comptes cash mécaniquement ne peuvent pas.
Ramp la size lentement. On utilise un ramp 25% → 50% → 100% sur deux mois. Le premier mois du bot à vrai argent VA surface des différences avec paper — slippage de fill, fills partiels, le vrai coût des market orders dans l'open. Mieux les découvrir à 25% de size.
Tradeoffs honnêtes
IBKR est excellent pour l'exécution systématique. Ce n'est pas turnkey :
- Vous opérez IBC (le controller qui gère le login non-interactif de Gateway). Il a sa propre cadence de release, des bugs occasionnels, et l'image gnzsnz lag upstream de jours-à-semaines.
- Vous opérez Docker sur un VPS. Si
docker builder prune -fn'est pas dans votre toolbox, ce n'est pas encore votre stack. - Vous lisez les release notes de l'API IBKR pour les breaking changes. Ça arrive. L'écosystème Python
ib_insyncaide mais ne vous immunise pas. - Les comptes personnels non-US-résidents ne peuvent pas retirer la marge cash. Worth checking your residency status against IBKR's matrix avant de fonder du capital meaningful.
Le payoff c'est un des rares brokers où vous pouvez déployer de l'exécution systématique à du capital retail-scale, contre un univers global, avec une vraie API et un environnement paper qui matche genuinely le live. Le tier crypto-venue a la profondeur API mais vit offshore. Le tier fintech-broker (Robinhood, Public, eToro) a l'UX mais n'a pas de vraie API. IBKR sit au milieu et paie back la courbe d'apprentissage opérationnelle une fois passé.
Comment agir concrètement
Si vous évaluez sérieusement IBKR pour l'exécution systématique :
- Ouvrez un compte paper d'abord — sans coût, avec accès API complet dès le jour un. Ouvrez un compte Interactive Brokers — c'est là qu'on a commencé, c'est là qu'on tourne encore paper. Raisonnement complet et nos tradeoffs honnêtes sur notre page broker.
- Utilisez l'image Docker gnzsnz/ib-gateway comme point de départ. Ne roll pas une custom gateway image jusqu'à ce que vous compreniez exactement ce qu'IBC + Xvfb + socat font.
- Lisez les docs « Trader Workstation API » d'IBKR en entier, même les parties ennuyeuses sur les abonnements market data et les pacing violations. Les parties ennuyeuses sont là où la friction vit.
- Cross-référencez avec notre page stack + l'article sur l'access AI supercycle pour le case éditorial à côté du opérationnel.
La courbe d'apprentissage opérationnelle est réelle. Le payoff aussi. Après douze mois, notre bot fleet sit confortablement sur les rails IBKR parce que la propriété dont on a eu besoin — SL/TP côté broker qui survit aux bot crashes, accès venue globale depuis un compte, vraie API Python, et un environnement paper qui matche live — existe chez IBKR et essentiellement nulle part ailleurs en retail.
Disclosure : on maintient une relation de referral avec Interactive Brokers. Si vous ouvrez un compte via notre lien de referral, on touche une commission de referral (et le programme IBKR donne actuellement jusqu'à $1,000 d'action IBKR au nouveau compte — termes applicables). On fait tourner notre propre bot fleet sur IBKR indépendamment de l'arrangement de referral — voir la page mentions légales pour la déclaration complète des conflits.
Related bubbles
Get the daily digest.
One email a day · alerts + bubble shifts + new research. Free during beta.
No spam. One email per day max. Telegram alerts coming with the paid tier.