caída del mercado por COVID en 2020
Análisis del desplome de 2020: los cuatro circuit breakers en ocho días, el mapa del peak to trough en el SPY y los datos faltantes del 9 de marzo.
La caída por el COVID en 2020 llevó al S&P 500 desde un cierre récord el 19 de febrero hasta una pérdida de -34.2% desde el pico hasta el valle en 23 días de negociación — con cuatro interrupciones por circuit-breaker en todo el mercado, la primera desde 1997. Esta página reproduce todo el ciclo desde el registro de datos: el mapa del desplome, la sesión de interrupción del 9 de marzo minuto a minuto (incluyendo los minutos que faltan en los datos, que son el mejor comprobante en la página), los activos que más cayeron, y lo que hicieron los Treasuries y el mercado de opciones simultáneamente. Cada número proviene de una consulta almacenada; puede expandir cualquier panel para ver el SQL.
El colapso completo, en una sola fila
Antes del famoso día único, el mapa. Una consulta sobre los cierres de la sesión regular diaria de SPY identifica el pico, el valle, la velocidad y la recuperación — además de la misma medición aplicada al mercado bajista de 2008 para comparar escalas.
El SQL exacto detrás de cada cifra
WITH dailies AS (
SELECT
toDate(toTimeZone(window_start, 'America/New_York')) AS d,
argMaxIf(toFloat64(close), window_start, (toHour(toTimeZone(window_start, 'America/New_York')) * 60 + toMinute(toTimeZone(window_start, 'America/New_York'))) BETWEEN 570 AND 959) AS c
FROM global_markets.delayed_stocks_minute_aggs
WHERE ticker = 'SPY'
AND window_start >= toDateTime('2020-02-01 00:00:00') AND window_start < toDateTime('2020-09-01 00:00:00')
GROUP BY d
),
peak AS (SELECT argMax(d, (c, -toInt32(d))) AS pd, max(c) AS pc FROM dailies WHERE d < toDate('2020-03-01')),
trough AS (SELECT argMin(d, (c, toInt32(d))) AS td, min(c) AS tc FROM dailies WHERE d < toDate('2020-04-01')),
gfc AS (
SELECT
toDate(toTimeZone(window_start, 'America/New_York')) AS d,
argMaxIf(toFloat64(close), window_start, (toHour(toTimeZone(window_start, 'America/New_York')) * 60 + toMinute(toTimeZone(window_start, 'America/New_York'))) BETWEEN 570 AND 959) AS c
FROM global_markets.delayed_stocks_minute_aggs
WHERE ticker = 'SPY'
AND window_start >= toDateTime('2007-06-01 00:00:00') AND window_start < toDateTime('2009-01-01 00:00:00')
GROUP BY d
),
gpeak AS (SELECT argMax(d, (c, -toInt32(d))) AS pd, max(c) AS pc FROM gfc WHERE d < toDate('2008-01-01'))
SELECT
toString((SELECT pd FROM peak)) AS peak_date,
round((SELECT pc FROM peak), 2) AS peak_close,
toString((SELECT td FROM trough)) AS trough_date,
round((SELECT tc FROM trough), 2) AS trough_close,
round(((SELECT tc FROM trough) / (SELECT pc FROM peak) - 1) * 100, 1) AS decline_pct,
countIf(d > (SELECT pd FROM peak) AND d <= (SELECT td FROM trough)) AS sessions_peak_to_trough,
dateDiff('day', (SELECT pd FROM peak), (SELECT td FROM trough)) AS calendar_days,
countIf(d > (SELECT pd FROM peak) AND d <= (SELECT min(d) FROM dailies WHERE c <= 0.7 * (SELECT pc FROM peak))) AS sessions_to_minus_30,
(SELECT countIf(d > (SELECT pd FROM gpeak) AND d <= (SELECT min(d) FROM gfc WHERE c <= 0.7 * (SELECT pc FROM gpeak))) FROM gfc) AS gfc_2008_sessions_to_minus_30,
toString((SELECT min(d) FROM dailies WHERE d > (SELECT td FROM trough) AND c >= (SELECT pc FROM peak))) AS recovery_date,
countIf(d > (SELECT td FROM trough) AND d <= (SELECT min(d) FROM dailies WHERE d > (SELECT td FROM trough) AND c >= (SELECT pc FROM peak))) AS sessions_trough_to_recovery
FROM dailiesEl cierre en el pico de SPY fue de $338.31 el 2020-02-19. El cierre en el valle, $222.51 el 2020-03-23, se sitúa un -34.2% por debajo — un mercado bajista comprimido en 23 sesiones de negociación, 33 días calendario. La afirmación, frecuentemente citada, sobre la "caída del treinta por ciento más rápida registrada" se confirma en la misma fila: el cierre de SPY rompió por primera vez un 30% por debajo del pico tras solo 22 sesiones. El mercado bajista de 2008, medido de la misma forma desde su cierre en el pico de octubre de 2007, necesitó 250 sesiones para perder ese mismo 30%. Lo que tomó aproximadamente un año en 2008 tomó cerca de un mes en 2020.
El cronograma del colapso, con cada cifra obtenida de los paneles de esta página:
- 19 de febrero de 2020 — cierre récord de SPY, $338.31.
- 9 de marzo — el primer circuit breaker de todo el mercado desde 1997; SPY cierra un -7.7% abajo. Detallado minuto a minuto a continuación.
- 12, 16 y 18 de marzo — tres suspensiones de Level-1 adicionales. El 16 de marzo cierra un -11.6%, la peor sesión única del colapso.
- 23 de marzo — el mínimo de cierre: $222.51, -34.2% desde el pico.
- 24 de marzo — el cambio de tendencia. El rally de estímulo del 24 de marzo lidera esa tendencia.
- 2020-08-18 — SPY cierra por encima del pico de febrero nuevamente, 103 sesiones después del mínimo.
9 de marzo de 2020: la sesión que activó el interruptor
Los circuit breakers del mercado se basan en el S&P 500, por lo que este panel registra la suspensión a través de SPY —el ETF del S&P 500— en lugar de cualquier otro proxy. Durante el fin de semana anterior, la revalorización por la pandemia coincidió con un choque de oferta de petróleo (más detalles abajo), y el mercado nunca abrió de manera ordenada.
El SQL exacto detrás de cada cifra
WITH
(
SELECT argMaxIf(toFloat64(close), window_start, (toHour(toTimeZone(window_start, 'America/New_York')) * 60 + toMinute(toTimeZone(window_start, 'America/New_York'))) BETWEEN 570 AND 959)
FROM global_markets.delayed_stocks_minute_aggs
WHERE ticker = 'SPY'
AND window_start >= toDateTime('2020-03-06 00:00:00') AND window_start < toDateTime('2020-03-09 04:00:00')
) AS prior_rth_close,
(
SELECT (formatDateTime(toTimeZone(addMinutes(prev_bar, 1), 'America/New_York'), '%H:%i'), gap_minutes - 1)
FROM (
SELECT
lagInFrame(window_start) OVER (ORDER BY window_start ASC ROWS BETWEEN 1 PRECEDING AND CURRENT ROW) AS prev_bar,
dateDiff('minute', prev_bar, window_start) AS gap_minutes
FROM global_markets.delayed_stocks_minute_aggs
WHERE ticker = 'SPY'
AND window_start >= toDateTime('2020-03-09 04:00:00') AND window_start < toDateTime('2020-03-09 23:59:00')
AND (toHour(toTimeZone(window_start, 'America/New_York')) * 60 + toMinute(toTimeZone(window_start, 'America/New_York'))) BETWEEN 570 AND 959
)
WHERE prev_bar > toDateTime('2020-03-09 00:00:00')
ORDER BY gap_minutes DESC
LIMIT 1
) AS halt_gap
SELECT
round(prior_rth_close, 2) AS prior_close,
round(toFloat64(argMinIf(open, window_start, (toHour(toTimeZone(window_start, 'America/New_York')) * 60 + toMinute(toTimeZone(window_start, 'America/New_York'))) BETWEEN 570 AND 959)), 2) AS rth_open,
round((toFloat64(argMinIf(open, window_start, (toHour(toTimeZone(window_start, 'America/New_York')) * 60 + toMinute(toTimeZone(window_start, 'America/New_York'))) BETWEEN 570 AND 959)) / prior_rth_close - 1) * 100, 1) AS gap_pct,
halt_gap.1 AS halt_first_missing_et,
halt_gap.2 AS halt_missing_minutes,
round(minIf(toFloat64(low), (toHour(toTimeZone(window_start, 'America/New_York')) * 60 + toMinute(toTimeZone(window_start, 'America/New_York'))) BETWEEN 570 AND 959), 2) AS rth_low,
formatDateTime(toTimeZone(argMinIf(window_start, (toFloat64(low), toInt64(toUnixTimestamp(window_start))), (toHour(toTimeZone(window_start, 'America/New_York')) * 60 + toMinute(toTimeZone(window_start, 'America/New_York'))) BETWEEN 570 AND 959), 'America/New_York'), '%H:%i') AS low_et,
round(toFloat64(argMaxIf(close, window_start, (toHour(toTimeZone(window_start, 'America/New_York')) * 60 + toMinute(toTimeZone(window_start, 'America/New_York'))) BETWEEN 570 AND 959)), 2) AS rth_close,
round((toFloat64(argMaxIf(close, window_start, (toHour(toTimeZone(window_start, 'America/New_York')) * 60 + toMinute(toTimeZone(window_start, 'America/New_York'))) BETWEEN 570 AND 959)) / prior_rth_close - 1) * 100, 1) AS day_change_pct,
round(toFloat64(sum(volume)) / 1e6, 1) AS day_shares_m,
countIf((toHour(toTimeZone(window_start, 'America/New_York')) * 60 + toMinute(toTimeZone(window_start, 'America/New_York'))) BETWEEN 570 AND 959) AS rth_minute_bars
FROM global_markets.delayed_stocks_minute_aggs
WHERE ticker = 'SPY'
AND window_start >= toDateTime('2020-03-09 04:00:00') AND window_start < toDateTime('2020-03-09 23:59:00')Dos cifras iniciales. Primero, el gap: SPY abrió a $275.3, -7.4% por debajo del cierre del viernes de $297.42 —y en los primeros minutos la caída del S&P 500 alcanzó el umbral del 7% del Level-1—. Segundo, y mejor: 376 barras de un minuto en la sesión regular en lugar de las 390 habituales. El tape se detiene después de las 9:34 am: a partir de las 09:35 ET, 14 minutos consecutivos no registran barras. No hubo operaciones en ningún lugar. El vacío en los datos es el circuit breaker. La sesión cerró a $274.4, -7.7% abajo, con 304.6 millones de acciones.
La estructura de una sesión con suspensión
El SQL exacto detrás de cada cifra
SELECT
formatDateTime(toStartOfInterval(toTimeZone(window_start, 'America/New_York'), INTERVAL 30 MINUTE), '%H:%i') AS et_time,
round(toFloat64(argMax(close, window_start)), 2) AS bucket_close,
round(min(toFloat64(low)), 2) AS bucket_low,
round(toFloat64(sum(volume)) / 1e6, 1) AS shares_m
FROM global_markets.delayed_stocks_minute_aggs
WHERE ticker = 'SPY'
AND window_start >= toDateTime('2020-03-09 04:00:00') AND window_start < toDateTime('2020-03-09 23:59:00')
AND (toHour(toTimeZone(window_start, 'America/New_York')) * 60 + toMinute(toTimeZone(window_start, 'America/New_York'))) BETWEEN 570 AND 959
GROUP BY et_time
ORDER BY et_timeEl primer bloque de datos contiene el desplome, la suspensión y la reanudación de forma simultánea: su mínimo de $273.5 está a pocos centavos del mínimo de toda la jornada de $273.45 — la impresión de la tarde a las 14:44 ET solo volvió a probar el nivel de la mañana. Tras la reapertura, el tape casi no tuvo movimiento: cada cierre de media hora se mantuvo a pocos dólares del precio de reapertura, y el daño del día ocurrió en los primeros minutos de la sesión. El volumen mostró la misma tendencia a la inversa: la última media hora, con 46.7 millones de acciones, fue el bloque con mayor volumen del día debido al flujo de cierre. Los circuit breakers están diseñados para producir exactamente esta estructura: detener la caída, forzar una subasta de reapertura y permitir que el trading continuo se reanude al nivel de precio ajustado. El reloj del mercado tiene este mecanismo integrado permanentemente ahora.
Cuatro suspensiones en ocho sesiones
El 9 de marzo fue solo la primera.
El SQL exacto detrás de cada cifra
SELECT
toString(et_date) AS session,
close_usd,
change_pct,
shares_m
FROM (
SELECT et_date, close_usd, shares_m,
round((close_usd / lagInFrame(close_usd) OVER (ORDER BY et_date ASC ROWS BETWEEN 1 PRECEDING AND CURRENT ROW) - 1) * 100, 1) AS change_pct
FROM (
SELECT
toDate(toTimeZone(window_start, 'America/New_York')) AS et_date,
round(argMaxIf(toFloat64(close), window_start, (toHour(toTimeZone(window_start, 'America/New_York')) * 60 + toMinute(toTimeZone(window_start, 'America/New_York'))) BETWEEN 570 AND 959), 2) AS close_usd,
round(toFloat64(sum(volume)) / 1e6, 1) AS shares_m
FROM global_markets.delayed_stocks_minute_aggs
WHERE ticker = 'SPY'
AND window_start >= toDateTime('2020-03-06 00:00:00') AND window_start < toDateTime('2020-03-19 00:00:00')
GROUP BY et_date
)
)
WHERE toString(et_date) IN ('2020-03-09', '2020-03-12', '2020-03-16', '2020-03-18')
ORDER BY et_dateLos breakers de Nivel-1 también se activaron el 12 de marzo (cerró -9.6%), el 16 de marzo (cerró -11.6% a $239.41 — la mayor caída porcentual diaria del S&P desde 1987) y el 18 de marzo (suspensión intradía, cerró -5.2%). Cuatro suspensiones en todo el mercado en ocho días de negociación, tras veintitrés años sin ninguna. Observe lo que nunca ocurrió: cada suspensión fue de Nivel 1, el tramo del 7%. Nunca se alcanzaron los tramos del 13% y 20% — incluso el 16 de marzo, la cascada se frenó tras la suspensión y la sesión terminó dentro de la línea de Nivel-2.
Who fell hardest
An index number hides the dispersion underneath it. The same daily-close arithmetic, run across a handful of representative large-caps, shows where the crash actually landed — both on March 9 itself and across the full February 19 to March 23 slide.
El SQL exacto detrás de cada cifra
WITH dailies AS (
SELECT
ticker,
toDate(toTimeZone(window_start, 'America/New_York')) AS d,
argMaxIf(toFloat64(close), window_start, (toHour(toTimeZone(window_start, 'America/New_York')) * 60 + toMinute(toTimeZone(window_start, 'America/New_York'))) BETWEEN 570 AND 959) AS c
FROM global_markets.delayed_stocks_minute_aggs
WHERE ticker IN ('UAL', 'CCL', 'XOM', 'JPM', 'AAPL', 'WMT', 'SPY')
AND window_start >= toDateTime('2020-02-18 00:00:00') AND window_start < toDateTime('2020-03-25 00:00:00')
GROUP BY ticker, d
)
SELECT
ticker,
round((anyIf(c, d = toDate('2020-03-09')) / anyIf(c, d = toDate('2020-03-06')) - 1) * 100, 1) AS mar9_pct,
round((anyIf(c, d = toDate('2020-03-23')) / anyIf(c, d = toDate('2020-02-19')) - 1) * 100, 1) AS crash_pct
FROM dailies
GROUP BY ticker
ORDER BY crash_pct ASCThe hardest-hit name in the panel, CCL (Carnival, the cruise operator), lost -19.9% on March 9 alone and -72.3% peak to trough. United Airlines gave up -66.9% over the crash; ExxonMobil -47.9%; JPMorgan -42.6%. Against them, SPY's -34.2% looks almost gentle, Apple fell roughly with the market at -31%, and Walmart — groceries, essentials, open through lockdowns — finished the same window at just -2.9%. Travel and energy repriced for a stopped world; staples barely moved.
La guerra de precios del petróleo, brevemente
El conflicto energético mencionado anteriormente requiere una explicación de un párrafo, ya que el término "guerra de precios del petróleo" representa la otra mitad de la noticia del 9 de marzo. Las conversaciones de producción de la OPEP+ entre Arabia Saudita y Rusia terminaron sin un acuerdo el viernes 6 de marzo de 2020. Durante el fin de semana, Arabia Saudita redujo sus precios oficiales de venta y anunció planes para aumentar la producción en un contexto donde la demanda de viajes ya estaba colapsando; un choque de oferta sumado a un choque de demanda. La apertura de las acciones el lunes reflejó ambos fenómenos simultáneamente, y el resultado se muestra en el panel anterior: ExxonMobil cayó un -12.2% ese lunes frente al -7.7% de SPY.
La búsqueda de refugio — y la semana en que se rompió
Las acciones representaban solo la mitad de la historia. La otra mitad fue el mercado de los bonos del Tesoro, donde la clásica búsqueda de refugio alcanzó un récord —y luego, durante una semana extraordinaria, se invirtió—.
El SQL exacto detrás de cada cifra
SELECT
toString(date) AS date,
round(yield_10_year, 2) AS ten_year_pct,
round(yield_3_month, 2) AS three_month_pct
FROM global_markets.treasury_yields
WHERE date >= '2020-02-18' AND date <= '2020-03-31'
AND isNotNull(yield_10_year)
ORDER BY dateEl rendimiento a diez años se situó en 1.56% en el pico bursátil del 19 de febrero. Para el 9 de marzo —la sesión de suspensión— cerró en 0.54%, un mínimo histórico, con los compradores aceptando medio punto porcentual anual por una década a cambio de seguridad. Luego el patrón se invirtió: hacia el 18 de marzo, el rendimiento a diez años subió a 1.18% mientras las acciones seguían cayendo; la "carrera por el efectivo", una semana en la que fondos, empresas y tenedores extranjeros vendieron cualquier activo líquido, incluidos los Treasuries, para recaudar dólares. Mientras tanto, el papel a tres meses cerró ese día en 0.02% —equivalentes de efectivo anclados a cero—. Cuando el activo más seguro y el más riesgoso caen juntos, lo que se está comprando es la liquidez misma. La curva de rendimientos registra este episodio permanentemente en su historia.
Lo que muestra la cinta de opciones
Las opciones listadas de SPY —el principal mecanismo de cobertura del mercado— reflejaron el pánico, y su cronología es el aspecto más relevante.
El SQL exacto detrás de cada cifra
SELECT
toString(toDate(toTimeZone(sip_timestamp, 'America/New_York'))) AS session,
round(sumIf(toFloat64(size), substring(ticker, 12, 1) = 'P') / 1e6, 2) AS put_contracts_m,
round(sumIf(toFloat64(size), substring(ticker, 12, 1) = 'C') / 1e6, 2) AS call_contracts_m,
round(sumIf(toFloat64(size), substring(ticker, 12, 1) = 'P') / sumIf(toFloat64(size), substring(ticker, 12, 1) = 'C'), 2) AS put_call_ratio
FROM global_markets.options_trades
WHERE ticker >= 'O:SPY2' AND ticker < 'O:SPY3'
AND sip_timestamp >= toDateTime('2020-03-02 00:00:00') AND sip_timestamp < toDateTime('2020-03-14 00:00:00')
GROUP BY session
ORDER BY sessionEl ratio de volumen put/call alcanzó su punto máximo en 1.95 —casi dos puts por cada call— el viernes 6 de marzo, antes de la sesión de suspensión. El mismo 9 de marzo, se negociaron 4.35 millones de puts frente a 2.38 millones de calls, un ratio de 1.83. Para el 12 de marzo —una sesión de -9.6%— el ratio se redujo a 1.32 conforme el volumen de calls aumentó junto al de los puts. El mayor sesgo de puts ocurrió al inicio de la caída, no en su punto más bajo: la protección se compró temprano, y las caídas posteriores y de mayor magnitud se negociaron con una cinta de opciones más equilibrada.
Cómo funciona realmente un freno general del mercado
La mecánica, dado que ocurre con poca frecuencia: los interruptores generales se activan según la caída del S&P 500 respecto al cierre anterior. Una caída del 7% (Nivel 1) y del 13% (Nivel 2) activa un freno de 15 minutos si se alcanza antes de las 3:25 pm ET; un 20% (Nivel 3) finaliza la sesión por completo. Durante el freno, no se opera en ninguna bolsa; las órdenes se acumulan para una subasta de reapertura, la cual concentra el interés acumulado en un solo precio, de la misma forma que lo hace la subasta de apertura cada mañana. Este diseño surgió tras el flash crash de 2010, cuando se demostró que los umbrales anteriores eran demasiado amplios para activarse incluso en una caída en cascada real.
Marzo de 2020 fue la primera —y hasta ahora única— prueba real de ese diseño, y el veredicto se muestra en los paneles anteriores: cuatro frenos de Nivel 1, cada uno seguido de una reapertura ordenada, sin alcanzar nunca el Nivel 2 o el Nivel 3, y un mercado funcional en cada una de las 23 sesiones de la caída más rápida en la historia del índice. Las reglas se mantuvieron: los mismos umbrales de 7/13/20, referenciados al S&P 500, siguen vigentes hoy. El sistema se flexionó; la infraestructura resistió.
Preguntas frecuentes sobre el desplome por COVID
¿Qué activó el circuit breaker el 9 de marzo de 2020?
La caída del S&P 500 de un 7% respecto al cierre anterior —el umbral de Nivel 1 para todo el mercado— durante los primeros minutos de la apertura. El trading se detuvo durante 15 minutos en todo el mercado y luego se reabrió mediante subasta. La pausa se observa en la cinta de minutos de esta página como 14 barras consecutivas faltantes a partir de las 09:35 ET.
¿Cuánto cayó la bolsa durante el desplome por COVID?
Medido según los cierres diarios de SPY, -34.2% —desde $338.31 el 19 de febrero de 2020 hasta $222.51 el 23 de marzo de 2020, a lo largo de 23 sesiones de trading.
¿Cuántos circuit breakers se activaron en marzo de 2020?
Cuatro interrupciones de Nivel 1 en todo el mercado: el 9, 12, 16 y 18 de marzo. Fueron las primeras desde octubre de 1997.
¿Se activaron circuit breakers de Nivel 2 o Nivel 3 en 2020?
No. Las cuatro interrupciones de 2020 fueron de Nivel 1 (7%). Nunca se ha activado una interrupción de Nivel 2 (13%) o Nivel 3 (20%) para todo el mercado bajo el diseño actual, ni siquiera el 16 de marzo, la peor sesión del desplome con un -11.6%.
¿Se recuperó el mercado totalmente del desplome por COVID?
Sí. SPY cerró por encima de su máximo del 19 de febrero de 2020 en 2020-08-18 —103 sesiones de trading después del mínimo del 23 de marzo, una de las recuperaciones totales más rápidas desde un bear market de esta profundidad.
Cada panel anterior es una consulta almacenada y versionada sobre la cinta histórica; expanda el SQL para ver cada medición. ¿Desea experimentar este día en lugar de leerlo? Es uno de los escenarios jugables en el simulador de trading de Strasmore Labs.