Strasmore Research
Aprendizado Matt ConnorPor Matt Connor · Atualizado 2026-08-04

Circuit breakers para bots de trading

Saiba como limites de perda diária interrompem um bot de trading e como o volatility scaling ajusta o tamanho das posições antes que uma sessão ruim se agrave.

Circuit breakers para bots de trading são regras que interrompem uma estratégia automatizada assim que um limite definido é atingido. Eles ficam numa camada de risco entre a estratégia e a corretora e são aplicados a todas as ordens, independentemente de a estratégia concordar ou não. A estratégia decide o que negociar. A camada de risco decide se haverá negociação.

Essa separação é a base de todo o desenho. Uma estratégia que fiscaliza a si mesma não tem uma verificação independente justamente quando as suas próprias premissas deixam de funcionar — o momento em que essa verificação é mais necessária.

O que um circuit breaker faz em um robô de trading

Uma camada de risco tem quatro partes, e cada uma existe para evitar uma falha específica e previsível.

  • Limites rígidos para o tamanho da posição, a exposição nocional por ativo e a taxa de envio de ordens. Eles limitam o prejuízo de um bug que, de outro modo, poderia não ter limite.
  • Um circuit breaker de drawdown que interrompe novas ordens quando a conta perde um valor definido na sessão ou cai um valor definido em relação ao seu pico de patrimônio.
  • Dimensionamento escalonado das ordens, definido com base na volatilidade recente ou em uma fração da aposta de Kelly, em vez de uma quantidade fixa de ações. Isso mantém o risco por operação aproximadamente estável enquanto a amplitude de variação do mercado muda.
  • Um log de auditoria somente para acréscimos com o registro de cada decisão, incluindo as ordens rejeitadas pela camada de risco. É o único artefato que permite distinguir entre “a estratégia estava errada” e “a verificação nunca foi executada”.

Tudo o que vem abaixo é uma dessas quatro partes, apresentada de forma concreta.

Qual é um limite diário de perda razoável para um bot de trading?

Um limite diário de perda interrompe novas ordens quando a perda da sessão ultrapassa um determinado nível. Escolher esse nível é uma questão de calibração, não de preferência: se ficar dentro do ruído normal do mercado, o bot permanecerá interrompido na maioria das semanas; se ficar muito acima dele, nunca será acionado. O ponto de partida é observar com que frequência o próprio mercado registra uma queda diária de determinado tamanho.

QueryDias de queda do SPY por magnitude, por ano, de 2018 a julho de 2026
O SQL exato por trás de cada número
WITH daily AS (
    SELECT toDate(toTimeZone(window_start, 'America/New_York')) AS d,
           argMax(toFloat64(close), window_start) AS close_px
    FROM global_markets.delayed_stocks_minute_aggs
    WHERE ticker = 'SPY'
      AND toDate(toTimeZone(window_start, 'America/New_York')) >= toDate('2017-12-01')
      AND toDate(toTimeZone(window_start, 'America/New_York')) <= toDate('2026-07-31')
      AND (toHour(toTimeZone(window_start, 'America/New_York')) * 60
           + toMinute(toTimeZone(window_start, 'America/New_York'))) BETWEEN 570 AND 959
    GROUP BY d
),
with_prev AS (
    SELECT d,
           close_px,
           any(close_px) OVER (ORDER BY d ASC ROWS BETWEEN 1 PRECEDING AND 1 PRECEDING) AS prev_close
    FROM daily
)
SELECT toYear(d) AS year,
       countIf(close_px / prev_close - 1 <= -0.01) AS down_1pct_days,
       countIf(close_px / prev_close - 1 <= -0.02) AS down_2pct_days,
       countIf(close_px / prev_close - 1 <= -0.03) AS down_3pct_days
FROM with_prev
WHERE prev_close > 0
  AND d >= toDate('2018-01-01')
GROUP BY year
ORDER BY year
Run this yourself

As sessões com queda de 1% ou mais somaram 15 em 2019 e 45 em 2020, de cerca de 250 pregões por ano. A contagem de sessões com queda de 3% ou mais é diferente: 0 em 2019, contra 16 em 2020. A última linha de 9 cobre apenas as sessões até 31 de julho de 2026.

A distribuição é irregular, não constante, e essa irregularidade é justamente o ponto central. Dias difíceis chegam em sequência. Um bot que interrompe as operações no primeiro dia de uma sequência e retoma no segundo não foi realmente interrompido.

Os dois limites cumprem funções diferentes. Um limite diário de perda, normalmente de 2% do patrimônio da conta, encerra a sessão. Um limite de drawdown móvel, calculado a partir do ponto máximo do patrimônio, normalmente em torno de 10%, encerra a estratégia até uma revisão humana. O primeiro é rotineiro; o segundo deve ser raro. Um bot que tenha apenas o primeiro pode reduzir a conta em 2% por vez sem nunca acionar um mecanismo de proteção.

Como o scaling de volatilidade altera o tamanho da posição?

O volatility targeting dimensiona a posição de forma inversamente proporcional à volatilidade realizada recente: quando o intervalo diário dobra, a posição cai aproximadamente pela metade, mantendo o risco financeiro por operação perto de um nível constante. Aqui, volatilidade realizada é o desvio-padrão anualizado dos retornos diários, e ela varia mais do que a maioria das pessoas imagina.

QueryVolatilidade realizada do SPY por mês e a posição que uma meta de vol de 12% carregaria
O SQL exato por trás de cada número
WITH daily AS (
    SELECT toDate(toTimeZone(window_start, 'America/New_York')) AS d,
           argMax(toFloat64(close), window_start) AS close_px
    FROM global_markets.delayed_stocks_minute_aggs
    WHERE ticker = 'SPY'
      AND toDate(toTimeZone(window_start, 'America/New_York')) >= toDate('2023-12-01')
      AND toDate(toTimeZone(window_start, 'America/New_York')) <= toDate('2026-07-31')
      AND (toHour(toTimeZone(window_start, 'America/New_York')) * 60
           + toMinute(toTimeZone(window_start, 'America/New_York'))) BETWEEN 570 AND 959
    GROUP BY d
),
rets AS (
    SELECT d,
           close_px / any(close_px) OVER (ORDER BY d ASC ROWS BETWEEN 1 PRECEDING AND 1 PRECEDING) - 1 AS ret
    FROM daily
)
SELECT formatDateTime(toStartOfMonth(d), '%Y-%m') AS month,
       round(stddevSamp(ret) * sqrt(252) * 100, 1) AS realized_vol_pct,
       round(least(100.0, 1200.0 / (stddevSamp(ret) * sqrt(252) * 100)), 1) AS vol_target_size_pct
FROM rets
WHERE d >= toDate('2024-01-01')
  AND isFinite(ret)
GROUP BY toStartOfMonth(d)
HAVING count() >= 15
ORDER BY toStartOfMonth(d)
Run this yourself

A volatilidade realizada medida 11.1% anualizada em 2024-01 e 12% em 2026-07, ao longo de 31 meses. A segunda coluna converte cada leitura na posição que uma meta de volatilidade de 12% carregaria, limitada à linha completa: 100% em 2024-01 contra 99.7% em 2026-07. A mesma estratégia, a mesma convicção, mas uma quantidade de ações muito diferente.

O critério de Kelly aborda o mesmo problema pelo outro lado. Ele dimensiona a posição a partir da vantagem estimada e da variância, em vez de considerar apenas a volatilidade. A maioria dos operadores sistemáticos usa uma fração do critério, como meio Kelly ou um quarto de Kelly, porque ambas as entradas são estimativas baseadas em uma amostra finita. dimensionamento de posições pelo critério de Kelly desenvolve esse cálculo.

Por que um trading bot continua reentrando depois de um stop?

Um stop é acionado. A posição é encerrada. Noventa segundos depois, a condição de entrada volta a ser verdadeira, o bot reentra, assume a mesma perda e repete o ciclo. Nenhum componente isolado está quebrado. A estratégia fez o que foi programada para fazer, o stop fez o que foi programado para fazer, e a conta continua acumulando perdas, uma operação completa de cada vez.

A frequência vem diretamente do caminho dos preços. Este painel contabiliza, por sessão, quantas vezes o SPY passou de mais de 0,1% acima do preço de abertura para mais de 0,1% abaixo dele, ou fez o movimento inverso.

QueryFrequência com que o SPY cruza novamente seu próprio preço de abertura, por sessão e por mês
O SQL exato por trás de cada número
WITH mins AS (
    SELECT toDate(toTimeZone(window_start, 'America/New_York')) AS d,
           window_start AS ts,
           toFloat64(close) AS px
    FROM global_markets.delayed_stocks_minute_aggs
    WHERE ticker = 'SPY'
      AND toDate(toTimeZone(window_start, 'America/New_York')) >= toDate('2025-08-01')
      AND toDate(toTimeZone(window_start, 'America/New_York')) <= toDate('2026-07-31')
      AND (toHour(toTimeZone(window_start, 'America/New_York')) * 60
           + toMinute(toTimeZone(window_start, 'America/New_York'))) BETWEEN 570 AND 959
),
opens AS (
    SELECT d, argMin(px, ts) AS open_px
    FROM mins
    GROUP BY d
),
zoned AS (
    SELECT m.d AS d,
           m.ts AS ts,
           multiIf(m.px >= o.open_px * 1.001, 1,
                   m.px <= o.open_px * 0.999, -1,
                   0) AS zone
    FROM mins AS m
    INNER JOIN opens AS o ON m.d = o.d
),
flips AS (
    SELECT d,
           zone,
           any(zone) OVER (PARTITION BY d ORDER BY ts ASC ROWS BETWEEN 1 PRECEDING AND 1 PRECEDING) AS prev_zone
    FROM zoned
    WHERE zone != 0
),
per_day AS (
    SELECT d, countIf(prev_zone != 0 AND zone != prev_zone) AS crossings
    FROM flips
    GROUP BY d
)
SELECT formatDateTime(toStartOfMonth(d), '%Y-%m') AS month,
       round(avg(crossings), 1) AS avg_crossings_per_session,
       max(crossings) AS max_crossings_in_a_session
FROM per_day
GROUP BY toStartOfMonth(d)
ORDER BY toStartOfMonth(d)
Run this yourself

O SPY cruzou essa faixa, em média, 0.7 vezes por sessão em 2025-08 e 1.5 vezes em 2026-07, com uma sessão em 2026-07 registrando 5 cruzamentos. Qualquer regra que abra uma posição de um lado de um nível e acione o stop do outro lado tem esse número de oportunidades para executar em um único dia.

Quatro mecanismos contêm o problema.

  • Um período de cooldown após cada stop, medido em minutos ou barras, durante o qual nenhuma nova ordem para aquele ticker é liberada pela camada de risco.
  • Um limite diário de operações por ticker, que transforma um loop sem limite em um processo limitado.
  • Um indicador de interrupção com retenção. Depois que o limite de perda diária é atingido, ele permanece acionado até que alguém o desative.
  • Persistência desse indicador fora da memória do processo. Um supervisor que reinicia um bot que travou entrega a ele um estado inicial limpo, exatamente o que o indicador existe para impedir.

O último mecanismo costuma surpreender quem fez todo o resto corretamente. Bots de grid trading montam escadas de ordens por definição, o que torna o limite de operações essencial, e não meramente decorativo.

O que acontece quando um bot negocia com uma cotação desatualizada?

Uma cotação que deixou de ser atualizada ainda parece um número. O bot lê esse valor, calcula uma ordem com base nele e envia a ordem para um mercado que já se movimentou. A falha é silenciosa: nada gera exceção, nenhum erro é registrado e as execuções só parecem estranhas depois.

A forma mais fácil de medir o problema é pelo gap de abertura, quando um preço conhecido permanece inalterado durante horas enquanto o preço negociável se move.

QueryGap overnight do fechamento anterior à próxima abertura: seis nomes, de jan. de 2024 a jul. de 2026
O SQL exato por trás de cada número
WITH daily AS (
    SELECT ticker,
           toDate(toTimeZone(window_start, 'America/New_York')) AS d,
           argMin(toFloat64(open), window_start) AS open_px,
           argMax(toFloat64(close), window_start) AS close_px
    FROM global_markets.delayed_stocks_minute_aggs
    WHERE ticker IN ('SPY', 'KO', 'MSFT', 'AAPL', 'NVDA', 'TSLA')
      AND toDate(toTimeZone(window_start, 'America/New_York')) >= toDate('2024-01-01')
      AND toDate(toTimeZone(window_start, 'America/New_York')) <= toDate('2026-07-31')
      AND (toHour(toTimeZone(window_start, 'America/New_York')) * 60
           + toMinute(toTimeZone(window_start, 'America/New_York'))) BETWEEN 570 AND 959
    GROUP BY ticker, d
),
gaps AS (
    SELECT ticker,
           d,
           open_px,
           any(close_px) OVER (PARTITION BY ticker ORDER BY d ASC ROWS BETWEEN 1 PRECEDING AND 1 PRECEDING) AS prev_close
    FROM daily
)
SELECT ticker,
       round(quantileDeterministic(0.5)(abs(open_px / prev_close - 1) * 100, cityHash64(ticker, d)), 2) AS median_gap_pct,
       round(quantileDeterministic(0.95)(abs(open_px / prev_close - 1) * 100, cityHash64(ticker, d)), 2) AS p95_gap_pct,
       round(max(abs(open_px / prev_close - 1) * 100), 2) AS max_gap_pct
FROM gaps
WHERE prev_close > 0
GROUP BY ticker
ORDER BY p95_gap_pct DESC
Run this yourself

O ativo com o maior gap no percentil 95 foi TSLA, em 4.41%, contra 1% para KO. As noites típicas foram muito mais tranquilas: gaps medianos de 1.01% e 0.24%, respectivamente. É para essas caudas que se cria uma camada de risco, e o maior gap individual em TSLA durante a janela analisada foi de 14.57%. Essas são as distâncias que um bot pode enfrentar ao agir com base em um preço lido algum tempo antes. Por que as ações abrem com gap durante a noite explica a mecânica.

As defesas são simples e baratas. Defina uma idade máxima para cada cotação que a camada de risco usará como referência para precificar, normalmente de alguns segundos em uma estratégia intradiária. Monitore separadamente o heartbeat do feed e os dados recebidos. Isso permite distinguir um socket silencioso de um mercado sem movimentação. Trate dados ausentes como uma interrupção da negociação, e não como uma cotação mantida, pois um bot sem preços também não consegue avaliar suas saídas.

Quais limites rígidos devem pertencer à camada de risco?

  • Notional máximo por ticker como proporção do patrimônio da conta. Um teto de 10% impede que uma única posição problemática comprometa toda a conta.
  • Notional bruto máximo de todas as posições abertas. Fixá-lo em 100% do patrimônio significa não usar alavancagem. Essa é uma decisão que deve ser tomada explicitamente, em vez de ser herdada da configuração padrão da corretora.
  • Taxa máxima de ordens por minuto e por dia. Dez ordens por minuto é um limite amplo para a maioria das estratégias de varejo e ainda contém um loop descontrolado em menos de um minuto.
  • Tamanho máximo da ordem como proporção do volume médio diário do ticker. Um teto de 1% impede que o bot mova o preço do ativo que está tentando negociar, e o volume médio diário é o denominador.

Todos esses limites pertencem à camada de risco, não à estratégia, e todos devem seguir o mesmo caminho de código no backtest, no paper trading e na operação ao vivo. Um limite que existe apenas na operação ao vivo é um limite que ninguém testou.

O que um log de auditoria de um robô de trading precisa conter?

Um log somente para acréscimo registra um evento por decisão e nunca edita nem exclui registros. Cada registro contém o carimbo de data e hora, a cotação utilizada e a sua idade, todas as verificações de limites executadas com o respetivo resultado, a ordem enviada e a resposta da corretora. Ordens rejeitadas são registradas com a mesma importância que as ordens executadas.

O objetivo é permitir a reconstrução. Seis semanas depois de uma sessão negativa, a pergunta nunca é qual foi o P&L. A questão é saber qual verificação foi aprovada e com base em qual input. Sem o input registrado, acaba-se por reconstituir o estado do robô a partir do estado do mercado. Esse é o mesmo erro que o viés de antecipação em backtesting: usar informação que o sistema não tinha no momento da decisão.

O projeto riskguard é uma implementação open source dessa separação. Nele, as verificações de limites ficam em um componente chamado pela estratégia, em vez de permanecerem dispersas na própria estratégia. É uma entre várias opções de desenho e vale a pena estudá-la antes de adotá-la. Seja qual for a dependência utilizada, fixe uma versão marcada, em vez de usar a branch padrão. Uma branch pode mudar entre duas execuções do mesmo backtest, e uma camada de risco que mudou silenciosamente é pior do que não ter nenhuma.

Por que a primeira implantação é executada em uma corretora de simulação

O adaptador da corretora usa uma conta de simulação por padrão, e a negociação em ambiente real exige, de propósito, a definição explícita de uma flag. Isso evita um erro banal: um arquivo de configuração copiado ou uma variável de ambiente que não foi substituída pode enviar ordens reais e comprometer dinheiro real.

Uma execução em simulação também produz o artefato que importa: um registro das decisões gerado pela mesma camada de risco, usando preços do mercado real. Esse registro mostra quais limites foram acionados e quais não foram. É uma evidência sobre a camada de risco, uma questão distinta de saber se a estratégia gera lucro. Negociação em simulação antes de usar dinheiro real explica o que um registro de simulação comprova e o que não comprova, enquanto sistemas de negociação com IA multiagente mostra por que a autoridade para interromper as operações precisa ficar fora de todos os agentes quando vários deles podem enviar ordens.

FAQ sobre o disjuntor de negociação de um bot

O que é um disjuntor em um bot de negociação?

É uma regra na camada de risco que interrompe o envio de novas ordens quando um limite definido é atingido. Normalmente, esse limite é uma perda diária ou um drawdown em relação ao pico do patrimônio da conta. A regra funciona de forma independente da estratégia, em todas as ordens, e permanece acionada até ser liberada.

Com que frequência o mercado registra um dia de queda de 2%?

O SPY registrou 5 sessões com queda de 2% ou mais em 2019 e 25 em 2020, de um total aproximado de 250 pregões por ano. Essa diferença entre um ano tranquilo e um ano de estresse é o motivo para calibrar o limite de perda com base no histórico, e não na intuição.

Como impedir que um bot entre novamente após um stop?

Uma janela de cooldown depois de cada stop e um limite diário de operações por ativo transformam um loop sem limite em um processo limitado. O sinal de interrupção também precisa ser persistente e ficar armazenado fora da memória do processo. Caso contrário, um supervisor que reinicie um bot encerrado inesperadamente devolverá a ele um estado novo, sem interrupção.

Como um bot pode identificar que um feed de preços ficou desatualizado?

O bot deve verificar a idade de cada cotação antes de usá-la para precificar uma ordem. Também deve monitorar separadamente o heartbeat do feed e os dados recebidos. Os gaps durante a noite mostram a dimensão do que um preço desatualizado pode ocultar: o movimento overnight no percentil 95 chegou a 4.41% em TSLA entre janeiro de 2024 e julho de 2026.

Um bot de negociação pequeno realmente precisa de um log de auditoria?

Um log de execuções registra o que aconteceu. Um log de decisões registra o que o bot acreditava estar autorizado a fazer. Essa é a única forma de distinguir uma estratégia incorreta de uma verificação de risco que nunca foi executada. Um log somente para anexação, que também inclua as rejeições, é a versão mínima útil.


Todos os números acima vêm de uma consulta armazenada sobre barras de um minuto, e cada painel abre o SQL correspondente. Execute as mesmas consultas na sua própria lista de ativos no terminal Strasmore.

#risk-management#trading-bots#drawdown#position-sizing#automation