The 2010 Flash Crash, Minute by Minute
May 6, 2010: about a trillion dollars of market value vanished and mostly returned inside 36 minutes. The minute tape of the flash crash, receipted.
On May 6, 2010, the US stock market collapsed and then healed inside little more than half an hour, the flash crash, still the strangest stretch of trading in modern market history. Close to a trillion dollars of market value vanished and mostly came back; along the way household names printed at $0.01 and at $100000 a share. Below: the minute tape, the clock times, the named stocks, and a stored query behind every number. Expand any panel for the SQL.
The day, on one row
| prior_close | rth_open | gap_pct | rth_low | low_et | rth_close | day_change_pct | low_vs_prior_pct | day_shares_m | rth_minute_bars |
|---|---|---|---|---|---|---|---|---|---|
| 116.83 | 116.26 | -0.5 | 105 | 14:45 | 112.88 | -3.4 | -10.1 | 639.9 | 390 |
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 = 'SPY'
AND window_start >= toDateTime('2010-05-05 00:00:00') AND window_start < toDateTime('2010-05-06 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(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 = 'SPY'
AND window_start >= toDateTime('2010-05-06 04:00:00') AND window_start < toDateTime('2010-05-06 23:59:00')The day was already nervous, a Greek-debt headline tape all morning, and SPY drifted lower from its $116.26 open. Then the floor vanished: the day's low of $105 printed at 14:45 ET, -10.1% below the prior close of $116.83. By the bell SPY had clawed back to $112.88, -3.4% on the day, yet far above the bottom of the air pocket. Volume: 639.9 million shares, one of the heaviest SPY sessions of its era.
The crash, minute by minute
Half-hour charts hide this event; at one-minute resolution it is unmistakable. Here is SPY from 2:00 to 3:30 pm ET:
| et_minute | close_usd | low_usd | shares_m |
|---|---|---|---|
| 14:00 | 114.83 | 114.7 | 0.67 |
| 14:01 | 114.8 | 114.76 | 0.93 |
| 14:02 | 114.75 | 114.71 | 0.93 |
| 14:03 | 114.68 | 114.63 | 1.19 |
| 14:04 | 114.58 | 114.53 | 1.5 |
| 14:05 | 114.58 | 114.56 | 1.44 |
| 14:06 | 114.4 | 114.4 | 1.73 |
| 14:07 | 114.52 | 114.37 | 1.48 |
| 14:08 | 114.33 | 114.31 | 1.41 |
| 14:09 | 114.25 | 114.21 | 2.27 |
| 14:10 | 114.12 | 114.1 | 1.91 |
| 14:11 | 113.91 | 113.85 | 3.93 |
| 14:12 | 114.16 | 113.87 | 2.9 |
| 14:13 | 114.25 | 114.1 | 2.44 |
| 14:14 | 114.21 | 114.14 | 1.49 |
| 14:15 | 114.19 | 114.16 | 1.31 |
| 14:16 | 114.07 | 114.05 | 1.49 |
| 14:17 | 113.82 | 113.82 | 2.07 |
| 14:18 | 113.86 | 113.6 | 3.3 |
| 14:19 | 113.67 | 113.66 | 1.61 |
The exact SQL behind every number
SELECT
formatDateTime(toTimeZone(window_start, 'America/New_York'), '%H:%i') AS et_minute,
round(toFloat64(close), 2) AS close_usd,
round(toFloat64(low), 2) AS low_usd,
round(toFloat64(volume) / 1e6, 2) AS shares_m
FROM global_markets.delayed_stocks_minute_aggs
WHERE ticker = 'SPY'
AND window_start >= toDateTime('2010-05-06 18:00:00') AND window_start < toDateTime('2010-05-06 19:30:00')
ORDER BY window_startThe joint SEC–CFTC post-mortem clocks the event from about 2:32 pm ET, when a large automated sell program began working an order of roughly 75,000 E-mini S&P 500 futures contracts (about $4.1 billion), to roughly 3:08 pm, when broad prices had retraced most of the plunge, thirty-six minutes end to end. The tape above matches that arc. SPY ended the 14:32 minute at $113.15. Twelve minutes later, the 14:44 minute traded 9.21 million shares, against 1.44 million in a calm minute like 14:05, and the 14:45 minute printed the day's low of $105. By the close of the 15:08 minute, SPY stood at $113.63, above where the slide began. A full round trip in about half an hour.
One halt fired all day, and it lasted five seconds. At 2:45:28 pm ET, inside the same minute as the low above, the CME's Stop Logic functionality paused E-mini futures trading, per the official report, and bids returned when it lifted. The market-wide circuit breakers of 2010 were keyed to the Dow at 10, 20 and 30 percent thresholds, and the rules then in force specified no halt at all for a ten-percent decline that late in the session; none fired. Single-stock circuit breakers did not exist yet, they are among this day's inventions.
What set the selling off
The seller was never a mystery to regulators. The SEC–CFTC report described a single mutual-fund complex, identified in press accounts as Waddell & Reed, using an automated algorithm selling E-mini contracts at a rate keyed to volume, with no regard for price or time. As volume exploded, the algorithm sold faster, into a market where high-frequency intermediaries had stopped absorbing inventory and begun passing it back and forth, the report's famous "hot potato" volume. Five years later a second name attached itself to the day: US authorities arrested London trader Navinder Singh Sarao in April 2015, for spoofing the E-mini order book, layering orders he never intended to execute, on May 6 and hundreds of other days. He pleaded guilty in 2016 and was sentenced in January 2020. How much his orders mattered that afternoon remains debated; the structural lesson below does not depend on it.
Where the selling was sharpest
| et_minute | minute_open | minute_low | drop_within_minute_usd | shares_m |
|---|---|---|---|---|
| 14:49 | 112.52 | 106.8 | 5.72 | 4.98 |
| 14:48 | 111.31 | 106.01 | 5.3 | 4.75 |
| 15:09 | 113.63 | 108.37 | 5.26 | 3.51 |
| 14:50 | 112.02 | 108.22 | 3.8 | 2.22 |
| 14:45 | 108.69 | 105 | 3.69 | 7.24 |
The exact SQL behind every number
SELECT
formatDateTime(toTimeZone(window_start, 'America/New_York'), '%H:%i') AS et_minute,
round(toFloat64(open), 2) AS minute_open,
round(toFloat64(low), 2) AS minute_low,
round(toFloat64(open) - toFloat64(low), 2) AS drop_within_minute_usd,
round(toFloat64(volume) / 1e6, 2) AS shares_m
FROM global_markets.delayed_stocks_minute_aggs
WHERE ticker = 'SPY'
AND window_start >= toDateTime('2010-05-06 18:00:00') AND window_start < toDateTime('2010-05-06 19:30:00')
ORDER BY drop_within_minute_usd DESC
LIMIT 5The single worst minute dropped $5.72 from its open to its low, at 14:49 ET, on 4.98 million shares in sixty seconds. What the tape shows is simple and durable: prices need standing buyers, and for a few minutes there weren't any. This is the strongest argument ever recorded for understanding what a market order does, every stop-loss that fired in that window became a market order hunting for bids that did not exist. The NBBO is a market's heartbeat, and that afternoon it flatlined.
A penny and $100,000: the absurd prints
When the real bids vanished, incoming orders walked down to whatever quotes remained, including market-maker placeholder "stub quotes" parked far from any plausible price. The raw trade tape still holds those prints:
| ticker | lowest_print | highest_print | penny_prints | hundred_k_prints | prints |
|---|---|---|---|---|---|
| AAPL | 199.25 | 100000 | 0 | 2 | 97158 |
| ACN | 0.01 | 41.53 | 43 | 0 | 10912 |
| BID | 27.85 | 100000 | 0 | 2 | 8015 |
| PG | 39.37 | 62.59 | 0 | 0 | 56242 |
The exact SQL behind every number
SELECT
ticker,
round(min(toFloat64(price)), 2) AS lowest_print,
round(max(toFloat64(price)), 2) AS highest_print,
countIf(toFloat64(price) <= 0.05) AS penny_prints,
countIf(toFloat64(price) >= 99999) AS hundred_k_prints,
count() AS prints
FROM global_markets.stocks_trades
WHERE ticker IN ('ACN', 'AAPL', 'BID', 'PG')
AND sip_timestamp >= toDateTime('2010-05-06 18:30:00') AND sip_timestamp < toDateTime('2010-05-06 19:30:00')
GROUP BY ticker
ORDER BY ticker ASCAccenture (ACN), a consultancy that had traded near $41.49 that afternoon, recorded 43 prints at five cents or less, bottoming at $0.01. In the other direction, Apple printed 2 trades at $100000 a share, and Sotheby's did the same 2 times.
The exchanges later canceled every trade executed more than 60 percent away from its pre-crash reference price, and that line explains the asymmetry in the record. ACN's penny prints died, so its lowest surviving minute-bar low, $17.74, sits -57.2% below the reference, just inside the cancellation boundary. Procter & Gamble never printed at a stub: its worst trade of the window, $39.37, was -36.7% below its reference, so every stop-loss that sold there kept the fill. ACN's penny prints breached that line and were erased; P&G's worst fill stayed inside it and stood. That arbitrary boundary taught more traders about order types than any pamphlet ever printed.
How wide the damage spread
Same window, honored tape only, the minute aggregates, measured from each name's 2:30 pm price:
| ticker | pre_crash_1430 | crash_low | low_vs_1430_pct | low_et | close_vs_1430_pct |
|---|---|---|---|---|---|
| ACN | 41.49 | 17.74 | -57.2 | 14:47 | -0.9 |
| PG | 62.17 | 39.37 | -36.7 | 14:47 | -2.3 |
| MMM | 84.45 | 67.98 | -19.5 | 14:46 | -0.3 |
| SPY | 113.35 | 105 | -7.4 | 14:45 | -0.4 |
| DIA | 106.05 | 99.16 | -6.5 | 14:47 | -0.7 |
| IWM | 66.84 | 63.36 | -5.2 | 14:45 | 0.7 |
The exact SQL behind every number
SELECT
ticker,
round(argMaxIf(toFloat64(close), window_start, (toHour(toTimeZone(window_start, 'America/New_York')) * 60 + toMinute(toTimeZone(window_start, 'America/New_York'))) BETWEEN 570 AND 870), 2) AS pre_crash_1430,
round(minIf(toFloat64(low), (toHour(toTimeZone(window_start, 'America/New_York')) * 60 + toMinute(toTimeZone(window_start, 'America/New_York'))) BETWEEN 871 AND 915), 2) AS crash_low,
round((minIf(toFloat64(low), (toHour(toTimeZone(window_start, 'America/New_York')) * 60 + toMinute(toTimeZone(window_start, 'America/New_York'))) BETWEEN 871 AND 915) / argMaxIf(toFloat64(close), window_start, (toHour(toTimeZone(window_start, 'America/New_York')) * 60 + toMinute(toTimeZone(window_start, 'America/New_York'))) BETWEEN 570 AND 870) - 1) * 100, 1) AS low_vs_1430_pct,
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 871 AND 915), 'America/New_York'), '%H:%i') AS low_et,
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) / argMaxIf(toFloat64(close), window_start, (toHour(toTimeZone(window_start, 'America/New_York')) * 60 + toMinute(toTimeZone(window_start, 'America/New_York'))) BETWEEN 570 AND 870) - 1) * 100, 1) AS close_vs_1430_pct
FROM global_markets.delayed_stocks_minute_aggs
WHERE ticker IN ('ACN', 'PG', 'MMM', 'IWM', 'DIA', 'SPY')
AND window_start >= toDateTime('2010-05-06 04:00:00') AND window_start < toDateTime('2010-05-06 23:59:00')
GROUP BY ticker
ORDER BY low_vs_1430_pct ASCThe dispersion is the story. ACN's surviving low sat -57.2% under its reference and P&G's -36.7%, while the broad ETFs bottomed single-digit percentages down, DIA, the Dow-tracking ETF, -6.5% and IWM -5.2%. Those DIA minutes were reported around the world as the largest intraday point drop in the Dow's history to that date. The last column is the recovery receipt: each of the six closed within about three percent of its 2:30 pm level, as if the middle hour had been a rumor.
How May 6 compares to other crash days
Set the flash crash against the other famous one-day wrecks in the minute-bar era (this equities tape reaches back to 2003; Black Monday 1987 predates it):
| session | low_vs_prior_pct | close_vs_prior_pct | bounce_off_low_pct |
|---|---|---|---|
| 2008-09-29 | -8.6 | -7 | 1.7 |
| 2010-05-06 | -10.1 | -3.4 | 7.5 |
| 2015-08-24 | -7.7 | -4.1 | 3.9 |
| 2018-02-05 | -4.4 | -4.1 | 0.3 |
| 2020-03-16 | -12.4 | -11.6 | 0.9 |
The exact SQL behind every number
WITH daily AS (
SELECT
toDate(toTimeZone(window_start, 'America/New_York')) AS et_date,
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,
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 rth_close
FROM global_markets.delayed_stocks_minute_aggs
WHERE ticker = 'SPY'
AND ((window_start >= toDateTime('2008-09-26 04:00:00') AND window_start < toDateTime('2008-09-30 00:00:00'))
OR (window_start >= toDateTime('2010-05-05 04:00:00') AND window_start < toDateTime('2010-05-07 00:00:00'))
OR (window_start >= toDateTime('2015-08-21 04:00:00') AND window_start < toDateTime('2015-08-25 00:00:00'))
OR (window_start >= toDateTime('2018-02-02 04:00:00') AND window_start < toDateTime('2018-02-06 00:00:00'))
OR (window_start >= toDateTime('2020-03-13 04:00:00') AND window_start < toDateTime('2020-03-17 00:00:00')))
GROUP BY et_date
)
SELECT
toString(et_date) AS session,
round((rth_low / prev_close - 1) * 100, 1) AS low_vs_prior_pct,
round((rth_close / prev_close - 1) * 100, 1) AS close_vs_prior_pct,
round((rth_close / rth_low - 1) * 100, 1) AS bounce_off_low_pct
FROM (
SELECT et_date, rth_low, rth_close,
lagInFrame(rth_close) OVER (ORDER BY et_date ASC ROWS BETWEEN 1 PRECEDING AND CURRENT ROW) AS prev_close
FROM daily
)
WHERE et_date IN (toDate('2008-09-29'), toDate('2010-05-06'), toDate('2015-08-24'), toDate('2018-02-05'), toDate('2020-03-16'))
ORDER BY et_dateMay 6, 2010 is not the deepest hole in the table: March 16, 2020 traded -12.4% below its prior close against -10.1% for the flash crash. But no other day comes close on the bounce. SPY closed 7.5% above its May 6 low, versus 3.9% for the next-best recovery in the set (August 24, 2015, itself a mini flash crash) and just 1.7% and 0.9% on the Lehman-era and COVID crash days. A repricing stays down into the close; a liquidity hole snaps back the same afternoon.
Did the V hold?
Two questions decide what a crash was: how fast the old price came back, and whether the panic low ever traded again. Both have receipted answers:
| flash_crash_low | may5_close | first_close_above_may5 | sessions_until_reclaim | first_close_below_low | close_that_session | sessions_until_break |
|---|---|---|---|---|---|---|
| 105 | 116.83 | 2010-05-12 | 4 | 2010-06-29 | 104.22 | 37 |
The exact SQL behind every number
WITH
(
SELECT minIf(toFloat64(low), (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('2010-05-06 04:00:00') AND window_start < toDateTime('2010-05-07 00:00:00')
) AS flash_low
SELECT
round(any(flash_low), 2) AS flash_crash_low,
round(any(may5_close), 2) AS may5_close,
toString(any(first_above_date)) AS first_close_above_may5,
countIf(et_date > toDate('2010-05-06') AND et_date <= first_above_date) AS sessions_until_reclaim,
toString(any(first_below_date)) AS first_close_below_low,
round(anyIf(close_usd, et_date = first_below_date), 2) AS close_that_session,
countIf(et_date > toDate('2010-05-06') AND et_date <= first_below_date) AS sessions_until_break
FROM (
SELECT et_date, close_usd, flash_low, may5_close,
min(if(et_date > toDate('2010-05-06') AND close_usd < flash_low, et_date, toDate('2100-01-01'))) OVER () AS first_below_date,
min(if(et_date > toDate('2010-05-06') AND close_usd > may5_close, et_date, toDate('2100-01-01'))) OVER () AS first_above_date
FROM (
SELECT et_date, close_usd, flash_low,
max(if(et_date = toDate('2010-05-05'), close_usd, 0)) OVER () AS may5_close
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,
flash_low
FROM global_markets.delayed_stocks_minute_aggs
WHERE ticker = 'SPY'
AND window_start >= toDateTime('2010-05-05 04:00:00') AND window_start < toDateTime('2011-01-01 00:00:00')
GROUP BY et_date
)
)
)The reclaim came fast: SPY closed back above its May 5 close of $116.83 just 4 sessions later, on 2010-05-12. The V held, for a while. But the flash-crash low did not stay an aberration: 37 sessions after the crash, on 2010-06-29, SPY closed at $104.22, below the $105 panic low, during the European sovereign-debt selloff that dominated that summer. The difference is the whole point: the flash crash reached that price in minutes, on no news, and bounced; the summer got there over weeks and stayed. Price is what you can transact at this second; value is what survives the afternoon.
What changed afterward
The flash crash produced the modern circuit-breaker regime: single-stock Limit Up/Limit Down bands and revised market-wide halts, machinery visible in later crises (March 2020's halts are its direct descendants). Stub quotes were banned; clearly-erroneous-trade rules were formalized; large futures orders now face price-aware execution scrutiny. The market structure you trade today was substantially drafted in the aftermath of those thirty-six minutes.
Flash crash FAQ
What caused the 2010 flash crash?
The official SEC/CFTC report described a roughly $4.1 billion automated E-mini sell program executing into thin liquidity, with high-frequency intermediaries first amplifying volume and then withdrawing. Press accounts identified the seller as mutual-fund firm Waddell & Reed; trader Navinder Sarao was later prosecuted for spoofing the same market that day. The tape's version: liquidity evaporated, and prices fell to wherever the next resting bid happened to be, sometimes a penny.
What time did the flash crash start and end?
The regulators' timeline runs from about 2:32 pm ET, when the automated selling began, to roughly 3:08 pm, when broad prices had retraced most of the plunge, 36 minutes. SPY's low printed at 14:45 ET, -10.1% below the prior close, and the trip back to pre-slide levels took about 23 minutes from the bottom.
What stocks were affected in the 2010 flash crash?
Nearly everything fell, but the famous casualties were names whose bids vanished entirely: Accenture printed as low as $0.01, Apple and Sotheby's printed at $100000, and Procter & Gamble traded down to $39.37, -36.7% below its afternoon level. Trades more than 60 percent from pre-crash prices were canceled; P&G's fills stood.
Did trading halt during the flash crash?
No market-wide circuit breaker fired on May 6, 2010, the rules then in force specified no halt for a decline of that size that late in the session. The only pause of the day lasted five seconds: the CME's Stop Logic briefly halted E-mini futures at 2:45:28 pm ET, the same minute as the index low. Single-stock circuit breakers and Limit Up/Limit Down bands were created afterward.
Could a flash crash happen again?
Smaller versions have, individual symbols hit LULD halts routinely, and mini flash events recur. The 2010-scale market-wide version has so far been contained by the halt machinery it inspired. The honest answer is that the structural ingredient, liquidity that can withdraw in milliseconds, is permanent.
Data notes
Full data notes
- The minute-by-minute panel's 15:09 bar carries a stray 108.37 low minutes after the recovery, and the same bar appears among the steepest-minute table's rows, consistent with late-reported prints, which the aggregates place in the minute they arrived. May 6's tape carries many.
- Canceled trades and the aggregates: ACN's raw tape shows $0.01 prints while its minute-bar low is $17.74, the minute aggregates reflect the post-cancellation record, the raw trade tape the original prints.
- The dispersion panel's "2:30 pm reference" is each name's last regular-session minute-bar close at or before 2:30 pm ET; the crash window is 2:31–3:15 pm.
- QQQ is absent from the cross-name panels: in 2010 it traded under the ticker QQQQ.
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.