IBKR Trader Workstation para bots sistemáticos: o mapa operacional que ninguém publica
Os docs da Interactive Brokers cobrem a API, os símbolos, os order types. Não cobrem o que realmente quebra quando você roda um bot real contra uma conta paper às 3 da manhã — janelas de re-login IBC, a armadilha de trusted-IP na porta 4002, o estado unhealthy-but-Up do gateway, o wrapper de rejeição de ordem que você precisa mas não está documentado. Eis o que aprendemos shipando bots live contra a IBKR ao longo de 12 meses.
A Interactive Brokers tem documentação. Cobre as assinaturas de chamada da API, as convenções de símbolo, a matriz de order types, a semântica limit/market/bracket. O que os docs não te dizem é o que realmente quebra quando você roda um bot real contra a conta paper de produção às 3 da manhã horário de Paris numa terça, e depois faz um redeploy de uma mudança de código às 9 da manhã do dia seguinte enquanto a sessão europeia está aberta.
Aprendemos a lacuna do jeito difícil. Este artigo é o mapa operacional que gostaríamos que existisse antes de começarmos — escrito de doze meses shipando bots contra a IBKR, oito desses meses rodando 24/5 contra a conta paper, e um outage silencioso de 38 horas que nos ensinou por que o container estar Up não é a mesma coisa que o bot funcionar.
Se você está avaliando a IBKR como broker para execução sistemática, ou já está operando um e batendo em fricção que você não tem palavras para descrever, isso é para você.
A arquitetura de setup (o que roda onde)
Um stack funcional de trading sistemático com IBKR tem três peças móveis:
- IB Gateway (ou TWS) — o software do lado IBKR que segura a sessão de rede com os servidores deles. Gateway é a variante headless; TWS é a versão de UI desktop. Para bots, Gateway vence — não abre janela, não deriva em updates de versão de UI, e a imagem Docker gnzsnz/ib-gateway empacota todas as peças de bootstrap (IBC para login não-interativo, Xvfb para o framebuffer, o loop de restart forçado diário que a IBKR exige).
- Seu processo de bot — o processo Python (ou seja lá o que for) que abre uma conexão TCP ao gateway, coloca ordens, escuta fills.
- Estado — os arquivos JSON / SQLite que rastreiam posições, equity, métricas de risco, histórico de trades. Esses vivem FORA de ambos os processos, no filesystem do host, bind-mounted dentro do container do bot.
As três peças ficam no mesmo host. Bot conecta no Gateway via rede Docker interna. Estado vive em disco, bind-mounted. Simples em conceito; a fricção está nos detalhes.
A armadilha de trusted-IP da porta 4002
O erro mais comum da primeira semana: apontar seu bot para a porta 4002.
IB Gateway escuta em 127.0.0.1:4002 com uma allowlist hardcoded de trusted-IP de 127.0.0.1. Se seu bot conecta de qualquer outro IP — incluindo um container Docker irmão no mesmo host — o handshake TCP sucede, e então o handshake da API silenciosamente cai. Seu bot espera o timeout completo de 15 segundos do nextValidId, lança, retenta, e espera de novo.
O fix não está nos docs da IBKR. É um forwarder socat dentro do container do gateway: socat TCP-LISTEN:4004,fork TCP:127.0.0.1:4002. Seu bot conecta na porta 4004 externamente; socat encaminha para 4002 com o endereço de origem reescrito para 127.0.0.1; o gateway vê uma fonte de trusted-IP e aceita a sessão de API.
A imagem gnzsnz vem com esse forwarder pré-fiado. Se você builda sua própria imagem de gateway, precisa adicionar a camada socat você mesmo. Vimos isso tropeçar todo time que tenta rolar uma imagem de gateway custom no dia um.
(Também vale saber: a env var TRUSTED_IPS que alguns docs referenciam não está fiada no template jts.ini.tmpl nesta imagem. Não desperdice uma noite configurando isso — o caminho socat é o que funciona.)
IBC re-login = 90 segundos de downtime de API, toda vez
IB Gateway precisa relogar nos servidores IBKR a cada 24 horas — a IBKR força isso. A imagem gnzsnz lida com isso via um restart agendado do IBC (IB Controller). Da perspectiva do seu bot, isso significa que todo restart de container, todo re-login forçado noturno, toda mudança de docker compose que toca o gateway dispara ~90 segundos de downtime de API.
Seu bot precisa de uma política de retry sã. Connect timeout 15 segundos, backoff exponencial (30s / 60s / 120s), desistir depois de 5 tentativas e alertar. Vimos bots escritos sem essa suposição que caem permanentemente no primeiro restart diário e nunca auto-recuperam.
Efeito colateral para saber: docker compose up -d <bot-service> recria serviços dependentes. Porque tradfi-ibkr-bot depends_on ibkr-gateway, tocar o bot também derruba o gateway. Toda mudança de código custa 90 segundos de downtime de API. Planeje deploys de acordo.
"Existing session detected" — escolha um primário
Se suas credenciais paper estão logadas de uma segunda máquina — seu IB Gateway local no Win11, uma instância antiga de VPS, qualquer lugar — o novo gateway bate num diálogo modal: "Existing session detected. Escolha: assumir (primary) ou rodar secondary (read-only)."
Um gateway headless não consegue clicar. Sem a env var certa, ele fica no diálogo para sempre.
Configure EXISTING_SESSION_DETECTED_ACTION=primary na env do gateway. A nova sessão assume em ~30 segundos; a velha é desconectada. Esse é o default certo para um bot de produção — você quer que a instância deployada vença, não o que estiver aberto no seu laptop.
(Também: feche seu Gateway local quando não estiver usando ativamente. Mesmo com primary configurado, o flapping de reconexão desperdiça linhas de log.)
Gerenciamento de estado: posições NÃO estão nos seus arquivos
O maior ajuste de modelo mental: o positions_state.json do seu bot é um cache, não fonte de verdade. A fonte de verdade é o servidor da IBKR. Seu bot reconstrói o cache a partir de reqPositions() em todo cold start.
Por que isso importa: se seu bot crasha no meio de um trade, reinicia, e encontra uma posição em positions_state.json que não combina com o que reqPositions() retorna, confie na IBKR. Já shipamos um padrão de force-reconcile que apaga o estado local e reconstrói de reqPositions() na detecção de qualquer mismatch. É a única política sã.
Cinco arquivos de estado, todos bind-mounted do host dentro do container:
| Arquivo | Propósito |
|---|---|
| live_status.json | Heartbeat + equity, sobrescrito a cada ciclo. Lido pelo pusher do dashboard. |
| risk_state.json | Tracking de drawdown diário, max-equity watermark. Persiste entre restarts. |
| trade_vault.db | SQLite de todos os trades (abertos + fechados). Registro permanente. |
| analyst_metrics.json | Breakdown de performance por estratégia. |
| dynamic_pairs.json | Pares adicionados pelo processo de universe-expansion. |
Armadilha crítica de bind-mount: se o arquivo não existe no host quando docker compose up roda, o Docker cria um diretório lá, e depois se recusa a montar como arquivo. Sempre pré-toque os JSONs (echo '{}' > file.json) antes do primeiro deploy. Pule isso e você ganha erros de mount crípticos que parecem problemas de permissão.
Ordens SL/TP vivem na IBKR — não no seu bot
Isso é o que torna a IBKR genuinamente viável para execução sistemática não-atendida: stop-loss e take-profit são colocados no servidor da IBKR, não segurados na memória do seu bot. Se seu bot crasha, a posição mantém a proteção. Se seu VPS reinicia, a posição mantém a proteção. Se você perde internet por 4 horas, a posição mantém a proteção.
O padrão é: abra a posição como market ou limit; imediatamente submeta o SL e TP como filhos bracket independentes com outsideRTH=true e tif=GTC. Eles ficam na IBKR até preencherem ou você cancelar. Seu bot pode ficar offline por dias e a posição está segura.
Essa é a propriedade operacional que nos deixa rodar não-atendido overnight. Nem todo broker oferece isso — muitas venues cripto exigem que o bot segure ordens SL/TP em memória e as coloque reativamente, o que significa que um crash do bot te expõe. SL/TP do lado do broker da IBKR é uma das razões genuínas pelas quais a preferimos para tradfi.
A armadilha "unhealthy but Up" (38 horas que nunca recuperaremos)
Em maio de 2026 shipamos um postmortem sobre um outage silencioso de 38 horas: o container do gateway mostrava Up em docker ps, o bot estava rodando, mas toda chamada de API retornava not connected. O próprio TWS tinha morrido dentro do container; o PID 1 do container era o supervisor, não o TWS, então o supervisor ficou vivo e o Docker reportou o container como saudável.
A lição: um container estar Up não é a mesma coisa que a aplicação dentro estar saudável. Você precisa de um healthcheck explícito que exercite o caminho da aplicação. Para nosso IBKR gateway:
healthcheck:
test: ["CMD-SHELL", "nc -z 127.0.0.1 4002 || exit 1"]
interval: 60s
timeout: 10s
retries: 3
Isso pega o caso TWS-morto-sob-supervisor-vivo. Deployamos depois do incidente. Auto-remediation (restart on unhealthy) ainda está no backlog — queremos olhos nos primeiros eventos unhealthy antes de ligar o auto-restart.
Gotchas de roteamento de ordem
Algumas não-óbvias que batemos rodando estratégias reais:
- Rejeição com erro
EtradeOnly not supported: certos order types exigem um wrapper helper (_new_order()no nosso codebase) que tira as flags estilo e-Trade da IBKR. Sem ele, ~10% das ordens rejeitam inexplicavelmente. O fix é uma função wrapper; o bug é em não tê-lo. - A linha de log
[10349 TIF]é puramente cosmética. A spec da API da IBKR lista 10349 como mensagem informacional, não erro. Suprima no seu filtro de erro ou você vai ser paginado por nada. - Drift do universo de pares: se seu bot tem um componente "Hunter" que auto-expande o universo de trading, ele às vezes vai tentar re-adicionar tickers que você baniu via audit. Usamos uma chamada
apply_audit_locks(strategies, merged_pairs)pós-Hunter para limpar re-additions. Sem ela, sua blacklist silenciosamente vaza.
Paper para live: o que realmente muda
A tentação depois de 30 paper trades é virar a chave. Não vire.
Nossa política: 30+ paper trades E 3-6 meses de soak antes de qualquer dinheiro real. Os paper trades validam o wiring; o soak valida que nada estranho acontece em torno de earnings, dividendos, gaps de fim de semana, feriados, janelas de desconexão de exchange, timings de IBC re-login durante períodos voláteis. Coisas que não aparecem em 30 trades aparecem em 6 meses.
Quando você virar:
TRADING_MODE=paper→liveIBKR_PORT=4002→4001(gateway live escuta em porta diferente)- Troque
IBKR_GATEWAY_USEReIBKR_GATEWAY_PASSWORDpara credenciais live - Compre uma subscription de market data — cerca de $30/mês para US equities em retail, mais se precisar de feeds exchange-específicos. Configure
IBKR_MARKET_DATA_TYPE=1para dado live; 3 é atrasado. - Se sua conta é cash sub-account (não margin): mantenha
LONG_ONLY=true. O bot não deve tentar shortar. Contas cash mecanicamente não podem.
Aumente tamanho devagar. Usamos um ramp 25% → 50% → 100% em dois meses. O primeiro mês do bot em dinheiro real VAI superfar diferenças do paper — slippage de fill, partial fills, o custo real de market orders na abertura. Melhor descobrir em tamanho 25%.
Tradeoffs honestos
A IBKR é excelente para execução sistemática. Não é turnkey:
- Você está operando IBC (o controller que lida com login não-interativo do Gateway). Tem sua própria cadência de release, bugs ocasionais, e a imagem gnzsnz fica atrás do upstream em dias-a-semanas.
- Você está operando Docker num VPS. Se
docker builder prune -fnão está na sua caixa de ferramentas, esse não é seu stack ainda. - Você está lendo as release notes da API da IBKR para mudanças breaking. Acontecem. O ecossistema Python
ib_insyncajuda mas não te imuniza. - Contas pessoais de não-residentes americanos não podem sacar margin cash. Vale checar seu status de residência contra a matriz da IBKR antes de fundear capital significativo.
O payoff é um dos poucos brokers onde você pode deployar execução sistemática em capital de escala retail, contra um universo global, com uma API real e um ambiente paper que genuinamente combina com live. O tier de venue cripto tem a profundidade de API mas vive offshore. O tier de broker fintech (Robinhood, Public, eToro) tem o UX mas não tem uma API real. A IBKR senta no meio e paga de volta a curva de aprendizado operacional uma vez que você passa.
Como agir concretamente
Se você está seriamente avaliando IBKR para execução sistemática:
- Abra uma conta paper primeiro — sem custo, com acesso completo à API desde o dia um. Abra uma conta na Interactive Brokers — foi onde começamos, é onde ainda rodamos paper. Raciocínio completo e nossos tradeoffs honestos estão na nossa página de broker.
- Use a imagem Docker gnzsnz/ib-gateway como ponto de partida. Não rode uma imagem de gateway custom até entender exatamente o que IBC + Xvfb + socat estão fazendo.
- Leia os docs "Trader Workstation API" da IBKR completos, mesmo as partes chatas sobre subscriptions de market data e violações de pacing. As partes chatas são onde mora a fricção.
- Cross-referencie com nossa página de stack + o argumento de acesso ao superciclo de IA para o caso editorial ao lado do operacional.
A curva de aprendizado operacional é real. O payoff também. Depois de doze meses, nossa bot fleet senta confortavelmente nos trilhos da IBKR porque a propriedade que precisávamos — SL/TP do lado do broker que sobrevive a crashes de bot, acesso global a venues de uma conta, uma API Python real, e um ambiente paper que combina com live — existe na IBKR e essencialmente em mais nenhum lugar no varejo.
Disclosure: mantemos uma relação de referral com a Interactive Brokers. Se você abre uma conta via nosso link de referral, ganhamos uma referral fee (e o programa da IBKR atualmente dá à nova conta até $1,000 em ações da IBKR — termos aplicam). Rodamos nossa própria bot fleet na IBKR independentemente do arranjo de referral — veja a página de disclosures para o conflict statement completo.
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.