Strasmore Research
Educación Matt ConnorPor Matt Connor · Actualizado 2026-08-04

Circuit breakers para bots de trading

Los circuit breakers detienen un bot antes de que una mala sesión se acumule. Conoce cuándo se activa el límite de pérdida diaria y cómo la volatilidad ajusta el tamaño de la posición.

Los circuit breakers para bots de trading son reglas que impiden que una estrategia automatizada envíe órdenes cuando se alcanza un límite establecido. Se ubican en una capa de riesgo entre la estrategia y el bróker, y se aplican a todas las órdenes, independientemente de que la estrategia esté de acuerdo. La estrategia decide qué operar. La capa de riesgo decide si se opera o no.

Esa separación es la base de todo el diseño. Una estrategia que se supervisa a sí misma no tiene un control independiente justo cuando sus propios supuestos dejan de cumplirse. Ese es precisamente el momento en que más vale contar con un control.

Qué hace un circuit breaker en un bot de trading

Una capa de control de riesgo tiene cuatro componentes. Cada uno existe para evitar una falla específica y previsible.

  • Límites estrictos para el tamaño de la posición, la exposición nocional por símbolo y la frecuencia de las órdenes. Acotan el daño de un error que, de otro modo, no tendría límite.
  • Un circuit breaker de drawdown que detiene las órdenes nuevas cuando la cuenta cae una cantidad definida durante la sesión o una cantidad definida desde su máximo de equity.
  • Tamaño de las órdenes escalado, calculado a partir de la volatilidad reciente o de una fracción de la apuesta de Kelly, en lugar de usar un número fijo de acciones. Así, el riesgo por operación se mantiene aproximadamente estable mientras cambia el rango del mercado.
  • Un audit log de solo adición con cada decisión, incluidas las órdenes que la capa de riesgo rechazó. Es el único registro que permite distinguir entre “la estrategia se equivocó” y “el control nunca se ejecutó”.

Todo lo que sigue es una aplicación concreta de uno de estos cuatro componentes.

What is a reasonable daily loss limit for a trading bot?

A daily loss limit halts new orders once the session's loss passes a threshold. Picking the number is calibration, not taste: set it inside ordinary market noise and the bot sits halted most weeks, set it far outside and it never fires. The starting point is how often the market itself delivers a down day of a given size.

ConsultaDías de caída de SPY por magnitud, por año, 2018 hasta julio de 2026
El SQL exacto detrás de cada cifra
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

Sessions at 1% down or worse numbered 15 in 2019 and 45 in 2020, out of roughly 250 trading days a year. The 3%-or-worse count is a different animal: 0 in 2019 against 16 in 2020. The last row of the 9 covers sessions through July 31, 2026 only.

The line is lumpy rather than steady, and the lumpiness is the design point. Hard days arrive in clusters. A bot that halts on the first day of a cluster and resumes on the second has not really halted.

Two thresholds do different jobs. A daily loss limit, commonly 2% of account equity, ends the session. A trailing drawdown limit measured from the equity high-water mark, commonly around 10%, ends the strategy pending human review. The first is routine, the second is meant to be rare, and a bot carrying only the first can grind an account down 2% at a time without ever tripping anything.

¿Cómo cambia el tamaño de la posición al escalar por volatilidad?

El targeting de volatilidad determina el tamaño de una posición de forma inversa a la volatilidad realizada reciente: cuando el rango diario se duplica, la posición se reduce aproximadamente a la mitad. Así, el riesgo en dólares por operación se mantiene cerca de un nivel constante. En este caso, la volatilidad realizada es la desviación estándar anualizada de los rendimientos diarios y puede variar más de lo que espera la mayoría de los operadores.

ConsultaVolatilidad realizada de SPY por mes y posición con un objetivo de volatilidad de 12%
El SQL exacto detrás de cada cifra
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

La volatilidad realizada medida 11.1% anualizada en 2024-01 y 12% en 2026-07, durante 31 meses. La segunda columna convierte cada lectura en el tamaño de posición que asumiría un objetivo de volatilidad de 12%, con un límite equivalente a la línea completa: 100% en 2024-01 frente a 99.7% en 2026-07. La misma estrategia y la misma convicción pueden producir una cantidad de acciones muy distinta.

El criterio de Kelly aborda el mismo problema desde el extremo opuesto. Determina el tamaño a partir de la ventaja estimada y la varianza, en lugar de basarse únicamente en la volatilidad. La mayoría de los operadores sistemáticos utiliza una fracción del criterio, como medio Kelly o un cuarto de Kelly, porque ambos datos son estimaciones obtenidas de una muestra limitada. tamaño de posición con el criterio de Kelly desarrolla ese cálculo.

¿Por qué un bot de trading vuelve a entrar después de un stop?

Se activa un stop. La posición se cierra. Noventa segundos después, la condición de entrada vuelve a cumplirse, el bot vuelve a entrar, asume la misma pérdida y repite el ciclo. Ningún componente está averiado. La estrategia hizo lo que se programó para hacer, el stop hizo lo que se programó para hacer y la cuenta sigue acumulando pérdidas en cada operación de ida y vuelta.

La frecuencia depende directamente de la trayectoria del precio. Este panel cuenta, por sesión, cuántas veces SPY pasó de estar más de 0.1% por encima de su precio de apertura a estar más de 0.1% por debajo, o volvió a cruzar en sentido contrario.

ConsultaFrecuencia con que SPY vuelve a cruzar su propio precio de apertura, por sesión y mes
El SQL exacto detrás de cada cifra
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

SPY cruzó esa banda un promedio de 0.7 veces por sesión en 2025-08 y 1.5 veces en 2026-07. En una sesión de 2026-07 registró 5 cruces. Cualquier regla que abra en un lado de un nivel y coloque el stop en el otro tiene ese número de oportunidades para activarse en un solo día.

Cuatro mecanismos limitan este comportamiento.

  • Un periodo de enfriamiento después de cada stop, medido en minutos o barras, durante el cual ninguna nueva orden para ese símbolo supera la capa de control de riesgo.
  • Un límite diario de operaciones por símbolo, que convierte un ciclo sin límite en uno acotado.
  • Una bandera de pausa que queda activada. Una vez que se alcanza el límite de pérdida diaria, permanece activa hasta que una persona la desactiva.
  • Persistencia de esa bandera fuera de la memoria del proceso. Un supervisor que reinicia un bot que se bloqueó le entrega un estado limpio, y precisamente eso es lo que la bandera debe impedir.

El último mecanismo suele pasar inadvertido para quienes implementaron correctamente los demás. Los bots de grid trading colocan escalones de órdenes por diseño, por lo que el límite de operaciones es un componente esencial, no un elemento decorativo.

¿Qué ocurre cuando un bot opera con una fuente de precios desactualizada?

Una cotización que dejó de actualizarse sigue pareciendo un número. El bot la lee, fija el precio de una orden con base en ella y envía esa orden a un mercado que ya se movió. La falla es silenciosa: no se genera ninguna excepción, no se registra ningún error y las ejecuciones solo parecen extrañas después.

La versión que se puede medir claramente es el gap overnight, cuando un precio conocido permanece sin cambios durante horas mientras el precio negociable se mueve.

ConsultaGap nocturno del cierre previo a la siguiente apertura: seis nombres, ene. de 2024 a jul. de 2026
El SQL exacto detrás de cada cifra
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

El nombre con el gap del percentil 95 más amplio fue TSLA, con 4.41%, frente a 1% para KO. Las noches habituales fueron mucho más tranquilas: gaps medianos de 1.01% y 0.24%, respectivamente. Las colas de la distribución son el motivo por el que se incorpora una capa de riesgo, y el mayor gap individual en TSLA durante el periodo analizado fue de 14.57%. Esas son las distancias a las que puede quedar expuesto un bot que actúa con base en un precio leído algún tiempo atrás. Por qué las acciones abren con gaps overnight explica la mecánica.

Las defensas son sencillas y de bajo costo. Establece una antigüedad máxima para cada cotización que la capa de riesgo vaya a utilizar para fijar precios, normalmente de unos pocos segundos en una estrategia intradía. Recibe por separado del feed una señal de heartbeat, además de los datos, para distinguir un socket silencioso de un mercado tranquilo. Trata la ausencia de datos como una interrupción de cotización, no como una cotización que debe mantenerse, porque un bot sin precios tampoco puede evaluar sus salidas.

¿Qué límites estrictos deben pertenecer a la capa de riesgo?

  • Notional máximo por símbolo como proporción del capital de la cuenta. Un límite de 10% evita que un solo símbolo comprometa toda la cuenta.
  • Notional bruto máximo de todas las posiciones abiertas. Fijarlo en 100% del capital significa no utilizar apalancamiento. Es una decisión que conviene tomar de forma explícita, en lugar de heredarla de la configuración predeterminada del bróker.
  • Tasa máxima de órdenes, por minuto y por día. Diez órdenes por minuto es un límite amplio para la mayoría de las estrategias minoristas y, aun así, acota un bucle descontrolado dentro de un minuto.
  • Tamaño máximo de la orden como proporción del volumen diario promedio del símbolo. Un límite de 1% evita que un bot mueva el precio al que intenta operar. El volumen diario promedio es el denominador.

Todos estos límites deben pertenecer a la capa de riesgo y ejecutarse mediante la misma ruta de código en el backtest, la cuenta demo y la operativa real. Un límite que solo existe en producción es un límite que nadie ha probado.

¿Qué debe incluir el registro de auditoría de un trading bot?

Un registro de solo anexado escribe un registro por cada decisión y nunca lo modifica ni elimina. Cada registro incluye la marca de tiempo, la cotización utilizada y su antigüedad, cada verificación de límites ejecutada con su resultado, la orden enviada y la respuesta del broker. Las órdenes rechazadas se registran con la misma importancia que las ejecutadas.

El objetivo es poder reconstruir lo ocurrido. Seis semanas después de una mala sesión, la pregunta nunca es cuál fue el P&L. La pregunta es qué verificación se aprobó y con qué dato de entrada. Sin ese dato registrado, terminas reconstruyendo el estado del bot a partir del estado del mercado. Es el mismo error que el sesgo de anticipación en backtesting: utilizar información que el sistema no tenía en el momento de tomar la decisión.

El proyecto riskguard es una implementación open source de esta separación. Las verificaciones de límites están en un componente al que la estrategia llama, en lugar de estar dispersas dentro de la propia estrategia. Es un diseño entre varios y conviene estudiarlo antes de adoptarlo directamente. Sea cual sea la solución de la que dependas, fija una versión etiquetada en lugar de utilizar una rama predeterminada. Una rama puede cambiar entre dos ejecuciones del mismo backtest, y una capa de riesgo que cambió silenciosamente es peor que no tener ninguna.

Por qué el primer despliegue se ejecuta con un broker de simulación

El adaptador del broker usa una cuenta de simulación por defecto. La operativa real requiere establecer una bandera de forma explícita. Esto evita un fallo común: que un archivo de configuración copiado o una variable de entorno que nunca se sobrescribió envíe órdenes reales con dinero real.

Una ejecución en simulación también produce el registro que importa: un log de decisiones generado por la misma capa de riesgo con precios en tiempo real. El registro muestra qué límites se activaron y cuáles no. Es evidencia sobre la capa de riesgo. Es una cuestión distinta de si la estrategia genera ganancias. Operativa en simulación antes de usar dinero real explica qué demuestra y qué no demuestra un registro de simulación. sistemas de trading con IA y múltiples agentes muestra por qué la autoridad para detener la operativa debe estar fuera de cada agente cuando varios pueden enviar órdenes.

Preguntas frecuentes sobre el interruptor de seguridad de un bot de trading

¿Qué es un interruptor de seguridad en un bot de trading?

Es una regla de la capa de gestión de riesgos que impide que el bot envíe nuevas órdenes cuando se alcanza un límite establecido. Lo más habitual es que ese límite sea una pérdida diaria o una caída desde el máximo del capital de la cuenta. Funciona de forma independiente de la estrategia, en cada orden, y permanece activado hasta que una acción específica lo desactiva.

¿Con qué frecuencia registra el mercado una jornada de caída del 2%?

SPY registró 5 sesiones con una caída del 2% o más en 2019 y 25 en 2020, sobre un total aproximado de 250 jornadas de trading por año. Esa diferencia entre un año tranquilo y uno de estrés explica por qué un límite de pérdidas debe calibrarse con datos históricos y no con intuición.

¿Cómo se evita que un bot vuelva a entrar después de un stop?

Una ventana de enfriamiento después de cada stop y un límite diario de operaciones por símbolo convierten un bucle ilimitado en uno acotado. La señal de detención también debe quedar fijada y persistir fuera de la memoria del proceso. De lo contrario, un supervisor que reinicie un bot que sufrió una caída le asignará un estado nuevo y no detenido.

¿Cómo puede un bot detectar que un feed de precios está desactualizado?

Debe comprobar la antigüedad de cada cotización antes de usarla para calcular el precio de una orden. También debe supervisar por separado el heartbeat del feed y los datos que este transmite. Los gaps nocturnos muestran la magnitud de lo que puede ocultar un precio desactualizado: el movimiento nocturno del percentil 95 alcanzó 4.41% en TSLA entre enero de 2024 y julio de 2026.

¿Un bot de trading pequeño realmente necesita un registro de auditoría?

Un registro de ejecuciones documenta lo que ocurrió. Un registro de decisiones documenta lo que el bot consideraba que podía hacer. Es la única forma de distinguir una estrategia incorrecta de una comprobación de riesgos que nunca se ejecutó. Un registro de solo anexado, que incluya los rechazos, es la versión mínima útil.


Todas las cifras anteriores provienen de una consulta almacenada sobre barras de un minuto, y cada panel permite abrir el SQL correspondiente. Ejecuta las mismas consultas con tu propia lista de símbolos en la terminal de Strasmore.

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