COVID crash 2020: vier halts en peak to trough
Analyse van de COVID crash van 2020: de vier circuit breaker halts en de volledige peak to trough beweging van de SPY inclusief de halt van 9 maart.
De COVID-crash van 2020 zorgde ervoor dat de S&P 500 van een recordhoogte op 19 februari naar een -34.2% peak-to-trough verlies in 23 handelsdagen ging. Hierbij vonden vier circuit-breaker halts plaats op de gehele markt, de eerste sinds 1997. Deze pagina reproduceert het volledige verloop: de kaart van de crash, de sessie van de halt op 9 maart minuut voor minuut (inclusief de minuten die ontbreken in de data, wat de meest betrouwbare weergave op de pagina is), de namen die het hardst daalden, en de bewegingen van de Treasuries en de options tape. Elk getal is een opgeslagen query; u kunt elk paneel uitbreiden voor de SQL.
De volledige crash in één rij
De kaart voorafgaand aan de beroemde enkele dag. Eén query over de dagelijkse regular-session closes van SPY bepaalt de piek, het dieptepunt, de snelheid en het herstel — inclusief dezelfde meting uitgevoerd op de bear market van 2008 ter vergelijking.
De exacte SQL achter elk getal
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 dailiesDe piek-close van SPY was $338.31 op 2020-02-19. De laagste close, $222.51 op 2020-03-23, ligt -34.2% lager — een bear market samengeperst in 23 handelsdagen, 33 kalenderdagen. De vaak aangehaalde claim van de "snelste drawdown van dertig procent ooit" wordt bevestigd in dezelfde rij: de close van SPY zakte voor het eerst meer dan 30% onder de piek na slechts 22 sessies. De bear market van 2008, identiek gemeten vanaf de piek-close in oktober 2007, had 250 sessies nodig om hetzelfde verlies van 30% te realiseren. Wat in 2008 ongeveer een jaar duurde, duurde in 2020 ongeveer een maand.
De chronologische structuur van de crash, waarbij elk getal is afkomstig uit de panelen op deze pagina:
- 19 februari 2020 — de record-close van SPY, $338.31.
- 9 maart — de eerste market-wide circuit breaker sinds 1997; SPY sluit -7.7% lager. Hieronder minuut voor minuut weergegeven.
- 12, 16 en 18 maart — nog drie Level-1 halts. De close van 16 maart is -11.6%, de slechtste enkele sessie van de crash.
- 23 maart — het laagste punt bij de close: $222.51, -34.2% vanaf de piek.
- 24 maart — de ommekeer. De stimulus rally van 24 maart bepaalt die koers.
- 2020-08-18 — SPY sluit weer boven de piek van februari, 103 sessies na het dieptepunt.
9 maart 2020: de sessie die de circuit breaker activeerde
De circuit breakers voor de gehele markt zijn gekoppeld aan de S&P 500. Daarom toont dit overzicht de handelspauze via SPY — de S&P 500 ETF — in plaats van via een andere proxy. Gedurende het voorgaande weekend botste de herwaardering vanwege de pandemie met een schok in het olieaanbod (hierover meer hieronder), waardoor de markt nooit op een ordelijke wijze opende.
De exacte SQL achter elk getal
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')Twee cijfers vooraf. Ten eerste de gap: SPY opende op $275.3, wat -7.4% lager is dan de vrijdagsluiting van $297.42 — en binnen de eerste minuten bereikte de daling van de S&P 500 de Level-1 drempel van 7%. Ten tweede, en gunstiger: 376 minute bars tijdens de reguliere sessie in plaats van de gebruikelijke 390. De tape stopt na 09:34 uur: vanaf 09:35 ET printen 14 opeenvolgende minuten helemaal geen bars. Er werd nergens verhandeld. De gap in de data is de circuit breaker. De sessie sloot op $274.4, een daling van -7.7%, op 304.6 miljoen shares.
Het verloop van een handelspauze
De exacte SQL achter elk getal
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_timeDe openingsperiode bevat de crash, de handelspauze en de hervatting tegelijkertijd: de laagste koers van $273.5 ligt slechts enkele centen van de laagste koers van de gehele dag van $273.45 verwijderd — de print in de middag om 14:44 ET testte enkel het niveau van de ochtend. Na de hervatting was er nauwelijks beweging in de koersen: elke slotkoers per half uur lag binnen enkele dollars van de hervattingprijs. De verliezen van de dag werden in de eerste minuten behaald. Het volume bevestigde dit beeld: de laatste half uur, met 46.7 miljoen aandelen, was de drukste periode van de dag door de closing flows. Circuit breakers zijn ontworpen om precies dit verloop te creëren: de val stoppen, een heropening via een auction afdwingen, en de handel hervatten op het nieuwe prijsniveau. De klok van de markt heeft deze mechanismen nu permanent ingebouwd.
Vier handelsstops in acht sessies
9 maart was slechts de eerste.
De exacte SQL achter elk getal
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_dateLevel-1 breakers werden ook geactiveerd op 12 maart (slotkoers -9.6%), 16 maart (slotkoers -11.6% op $239.41 — de grootste dagelijkse procentuele daling van de S&P sinds 1987), en 18 maart (intraday stop, slotkoers -5.2%). Vier marktbrede halts in acht handelsdagen, na een periode van drieëntwintig jaar zonder een enkele stop. Let op wat er niet gebeurde: elke halt was een Level 1, de 7% categorie. De 13% en 20% categorieën werden nooit bereikt — zelfs op 16 maart vertraagde de cascade na de halt en eindigde de sessie binnen de Level-2 lijn.
Wie de grootste verliezen leed
Een indexcijfer verbergt de spreiding die eronder ligt. Dezelfde rekenmethode op basis van dagsluitingen, toegepast op een selectie representatieve large-caps, laat zien waar de crash daadwerkelijk plaatsvond — zowel op 9 maart zelf als gedurende de volledige daling van 19 februari tot 23 maart.
De exacte SQL achter elk getal
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 ASCDe grootste verliezer in deze groep is CCL (Carnival, de cruiseoperator), die op 9 maart alleen al -19.9% verloor en -72.3% van piek naar dal. United Airlines verloor -66.9% tijdens de crash; ExxonMobil -47.9%; JPMorgan -42.6%. In vergelijking hiermee lijkt de -34.2% van SPY bijna mild; Apple daalde ongeveer mee met de markt naar -31%, en Walmart — supermarkten, basisbehoeften, open tijdens lockdowns — eindigde in dezelfde periode met slechts -2.9%. Reizen en energie werden opnieuw gewaardeerd voor een stilgevallen wereld; basisconsumptiegoederen bewogen nauwelijks.
De olieprijsoorlog, in het kort
Het bovenstaande energieconflict verdient een korte toelichting, aangezien de term "olieprijsoorlog" de andere helft van het verhaal van 9 maart bevat. De gesprekken over de OPEC+ productie tussen Saoedi-Arabië en Rusland liepen op vrijdag 6 maart 2020 uit op een gebrek aan een overeenkomst. Gedurende het weekend verlaagde Saoedi-Arabië de officiële verkoopprijzen en kondigde het plannen aan om de productie te verhogen, terwijl de vraag naar reizen al afnam — een aanbodshock bovenop een vraagshock. De beursopening van maandag verwerkte beide factoren tegelijkertijd, wat de resultaten in de bovenstaande tabel verklaart: ExxonMobil daalde met -12.2% die maand, vergeleken met -7.7% van SPY.
De vlucht naar veiligheid — en de week waarin deze omsloeg
Aandelen vormden slechts de helft van het verhaal. De andere helft was de Treasury-markt, waar de klassieke vlucht naar veiligheid een record bereikte — om vervolgens, gedurende één opmerkelijke week, de andere kant op te bewegen.
De exacte SQL achter elk getal
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 dateDe 10-year yield stond op 1.56% tijdens de aandelenpiek van 19 februari. Op 9 maart — de dag van de handelsstop — sloot deze op 0.54%, een historisch laag niveau, waarbij kopers een half procent per jaar accepteerden voor een periode van tien jaar in ruil voor veiligheid. Daarna keerde het patroon: tot 18 maart steeg de 10-year yield naar 1.18% terwijl de aandelen bleven dalen — de "dash for cash", een week waarin fondsen, bedrijven en buitenlandse houders alles wat liquide was verkochten, inclusief Treasuries, om dollars op te halen. Ondertussen sloot de 3-month bill die dag op 0.02% — cash-equivalenten gekoppeld aan nul. Wanneer het veiligste activum en het risicovolste activum gelijktijdig dalen, is het product dat wordt gekocht de liquiditeit zelf. De yield curve zal dit episode permanent in haar geschiedenis dragen.
Wat de options tape laat zien
De genoteerde options van SPY — de officiële markt voor hedging — lieten de paniek duidelijk zien. Het timing hiervan is het meest opvallende aspect.
De exacte SQL achter elk getal
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 sessionDe put/call volume ratio bereikte een piek van 1.95 — bijna twee puts tegenover elke call — op vrijdag 6 maart, vóór de handelspauze. Op 9 maart zelf werden 4.35 miljoen puts verhandeld tegenover 2.38 miljoen calls, een ratio van 1.83. Tegen 12 maart — een -9.6% sessie — was de ratio gedaald naar 1.32, doordat het call volume toenam naast de puts. De sterkste put skew vond plaats aan het begin van de cascade, niet aan het einde: bescherming werd vroeg gekocht, terwijl de latere, grotere dalingen handelden op een meer tweezijdige options tape.
Hoe een marktbrede handelstop in de praktijk werkt
De werking is complex, aangezien deze zelden voorkomt: marktbrede breakers zijn gebaseerd op de daling van de S&P 500 ten opzichte van de vorige slotkoers — een daling van 7% (Level 1) en 13% (Level 2) activeert elk een handelstop van 15 minuten als deze wordt bereikt vóór 15:25 uur ET; een daling van 20% (Level 3) beëindigt de sessie direct. Tijdens de halt vindt er geen handel plaats op enige beurs; orders worden in een wachtrij geplaatst voor een heropening-auction, die de geaccumuleerde interesse concentreert in één prijs, vergelijkbaar met de opening auction aan het begin van elke ochtend. Dit ontwerp stamt uit de nasleep van the 2010 flash crash, waarbij bleek dat de oudere drempelwaarden te ruim waren om te activeren tijdens een echte cascade.
Maart 2020 was de eerste — en tot nu toe enige — praktijktest van dit ontwerp, en de conclusie is te vinden in de bovenstaande panelen: vier Level-1 halts, elk gevolgd door een ordelijke heropening, waarbij Level 2 of Level 3 nooit werd bereikt, en een functionerende markt tijdens elke 23 sessie van de snelste grote drawdown in de geschiedenis van de index. De regels zijn daarna gehandhaafd: dezelfde drempels van 7/13/20, gerelateerd aan de S&P 500, zijn vandaag de dag nog steeds van kracht. Het systeem boog, maar de infrastructuur bleef overeind.
COVID crash FAQ
Wat veroorzaakte de circuit breaker op 9 maart 2020?
De S&P 500 daalde binnen de eerste minuten met 7% ten opzichte van de vorige sluiting — de Level-1 drempel voor de gehele markt. De handel werd voor 15 minuten marktbreed gestaakt en werd daarna via een auction hervat. De stop is zichtbaar in de minute tape van deze pagina als 14 opeenvolgende ontbrekende bars vanaf 09:35 ET.
Hoeveel daalde de aandelenmarkt tijdens de COVID crash?
Gemeten op de dagelijkse sluitingen van SPY, is de daling -34.2% — van $338.31 op 19 februari 2020 naar $222.51 op 23 maart 2020, over 23 handelsdagen.
Hoe vaak werden er circuit breakers geactiveerd in maart 2020?
Vier marktbrede Level-1 halts: op 9, 12, 16 en 18 maart. Dit waren de eerste sinds oktober 1997.
Werden er in 2020 ooit Level 2 of Level 3 circuit breakers geactiveerd?
Nee. Alle vier de halts in 2020 waren Level 1 (7%). Er is onder het huidige ontwerp nog nooit een marktbrede Level-2 (13%) of Level-3 (20%) halt geactiveerd — zelfs niet op 16 maart, de slechtste sessie van de crash met een daling van -11.6%.
Is de markt volledig hersteld van de COVID crash?
Ja. SPY sloot op 2020-08-18 boven de piek van 19 februari 2020 — 103 handelsdagen na het dieptepunt van 23 maart. Dit is een van de snelste volledige herstelperiodes vanuit een bear market van deze omvang.
Elk paneel hierboven is een opgeslagen, versiespecifieke query over de historische tape — breid de SQL uit om elke meting te zien. Wilt u deze dag ervaren in plaats van erover te lezen? Het is een van de speelbare scenario's in de Strasmore Labs handels simulator.