COVID Crash: March 9, 2020 and the Halts
March 9, 2020: the first market-wide circuit breaker since 1997 — and the tape itself is missing the halt minutes. The COVID crash's opening act, receipted.
On March 9, 2020, pandemic repricing met an oil-price war, and the US market fell so fast at the open that it tripped the first market-wide circuit breaker since 1997. This page replays that session from the minute tape — including the part of the tape that isn't there, which is the best receipt on the page. Every number is a stored query; expand any panel for the SQL.
The day, on one row — count the bars
The exact SQL behind every number
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 = 'QQQ'
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
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,
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(maxIf(toFloat64(high), (toHour(toTimeZone(window_start, 'America/New_York')) * 60 + toMinute(toTimeZone(window_start, 'America/New_York'))) BETWEEN 570 AND 959), 2) AS rth_high,
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((minIf(toFloat64(low), (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 low_vs_prior_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 = 'QQQ'
AND window_start >= toDateTime('2020-03-09 04:00:00') AND window_start < toDateTime('2020-03-09 23:59:00')Two numbers up front. First, the gap: QQQ opened at $193.48, -7% below Friday's close — deep enough that the S&P's decline hit the 7% Level-1 threshold within minutes of the open. Second, and better: 376 regular-session minute bars instead of the usual 390. The missing minutes are the market-wide halt itself — trading stopped for 15 minutes, no bars printed, and the gap in the data is the event. The session's low of $192.11 came at 09:49 ET, at the halt's resumption; the close, $193.65, was -6.9% down.
The shape of a halted session
The exact SQL behind every number
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 = 'QQQ'
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_timeThe opening bucket contains the crash, the halt, and the resumption all at once. Notice what the rest of the day did: after the reopening, the tape went almost flat — the whole day's damage was done in the first twenty minutes of clock time (five of them halted). Circuit breakers are designed to produce exactly this shape: stop the waterfall, force a reopening auction, and let continuous trading resume at the repriced level. The market's clock has this machinery permanently embedded now.
March 2020's other breaker days
The exact SQL behind every number
SELECT
toString(et_date) AS session,
close_usd,
shares_m
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 = 'QQQ'
AND window_start >= toDateTime('2020-03-09 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_dateMarch 9 was only the first: Level-1 breakers also tripped on March 12, March 16 (the worst of them — the S&P's largest one-day percentage drop since 1987), and March 18. Four market-wide halts in eight trading days, after twenty-three years without one. The closes above trace the staircase down; the bottom — and the single most violent rally off it — came the following week, receipted in the March 24 stimulus rally.
The era around the day
March 9 opened a compressed bear market unlike anything before it: peak to trough in twenty-three trading days. The week prior had already swung multiple percent daily as pandemic math collided with an OPEC supply war announced that very weekend. What makes the episode historically odd is the speed symmetry — the fall was the fastest thirty-percent drawdown on record, and the recovery that began two weeks later was among the fastest ever as well. The circuit-breaker machinery inherited from 2010's lesson ran four times in eight sessions and functioned as designed each time: halt, auction, resume. The system bent; the plumbing held.
How a market-wide halt actually works
The mechanics, since they run rarely enough to be forgotten between uses: market-wide breakers key off the S&P 500's decline from the prior close — 7% (Level 1) and 13% (Level 2) each trigger a 15-minute halt if hit before 3:25 pm ET; 20% (Level 3) ends the session outright. During the halt nothing trades on any exchange; orders queue for a reopening auction, which concentrates the accumulated interest into one price the way the opening auction does each morning. That is why the missing minutes in the panel above end with the day's low at the resumption print: the auction was the bottom that day, with the whole halted market's sell pressure expressed in a single cross.
COVID crash FAQ
What triggered the circuit breaker on March 9, 2020?
The S&P 500 falling 7% from its prior close — the Level-1 market-wide threshold — within the opening minutes. Trading halted for 15 minutes market-wide, then reopened via auction. The halt minutes are literally absent from the minute tape above.
How many circuit breakers hit in March 2020?
Four market-wide Level-1 halts: March 9, 12, 16, and 18. They were the first since October 1997.
Was March 9 the bottom of the COVID crash?
No — the cascade ran two more weeks. The closing low came March 23, 2020, and the reversal the next morning was itself historic: March 24's rally carries that tape.
Every panel above is a stored, versioned query over the historical tape — expand the SQL to see each measurement. Want to feel this day instead of reading it? It is one of the playable scenarios in the Strasmore Labs trading simulator.