Обвал рынка в 2020 году из-за COVID-19
Анализ падения SPY в 2020 году: четыре остановки торгов за восемь дней и детальная карта обвала. Изучите поминутные данные сессии 9 марта для точного анализа.
Обвал рынка из-за COVID-19 в 2020 году привел к падению S&P 500 с рекордного уровня закрытия 19 февраля до потери в -34.2% от пика до минимума за 23 торговых дней. В этот период четыре раза срабатывали общерыночные механизмы приостановки торгов — впервые с 1997 года. На этой странице представлен полный обзор событий: карта обвала, поминутный отчет по сессии с остановкой торгов 9 марта (включая пропущенные в данных минуты, которые являются наиболее точным подтверждением событий), список наиболее пострадавших активов, а также динамика казначейских облигаций и рынка опционов. Все данные получены с помощью запросов к базе данных; вы можете развернуть любую панель, чтобы просмотреть SQL-код.
Весь обвал в одной строке
Карта событий до того самого знаменитого дня. Один запрос по ценам закрытия SPY в рамках регулярных торговых сессий фиксирует пик, дно, скорость падения и восстановление. Для сравнения приведены аналогичные данные по медвежьему рынку 2008 года.
Точный SQL-код для каждого числа
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 dailiesПиковое значение закрытия SPY составило $338.31 2020-02-19. Цена на дне составила $222.51 2020-03-23, что на -34.2% ниже пика. Медвежий рынок был сжат в 23 торговых сессиях или 33 календарных днях. Утверждение о «самом быстром падении на тридцать процентов в истории» подтверждается в той же строке: закрытие SPY впервые упало на 30% ниже пика всего через 22 сессий. Медвежьему рынку 2008 года, если измерять его так же от пика в октябре 2007 года, потребовалось 250 сессий, чтобы потерять те же 30%. То, что в 2008 году заняло примерно год, в 2020 году заняло около месяца.
Хронология обвала, все данные взяты из панелей на этой странице:
- 19 февраля 2020 года — рекордное закрытие SPY, $338.31.
- 9 марта — первое общерыночное отключение торгов (circuit breaker) с 1997 года; SPY закрывается снижением на -7.7%. Подробности по минутам приведены ниже.
- 12, 16 и 18 марта — еще три остановки торгов уровня Level-1. 16 марта закрытие составило -11.6%, это была худшая торговая сессия во время обвала.
- 23 марта — минимальное значение закрытия: $222.51, что на -34.2% ниже пика.
- 24 марта — разворот. Ралли на фоне стимулов 24 марта определяет этот период.
- 2020-08-18 — SPY снова закрывается выше февральского пика через 103 сессий после минимума.
9 марта 2020 года: сессия, вызвавшая остановку торгов
Общерыночные механизмы автоматической приостановки торгов (circuit breakers) привязаны к индексу S&P 500, поэтому в данной панели данные о приостановке отображаются через SPY — ETF на индекс S&P 500, — а не через другие прокси-инструменты. В прошедшие выходные переоценка активов из-за пандемии совпала с шоком предложения нефти (подробнее ниже), поэтому рынок не открылся в штатном режиме.
Точный SQL-код для каждого числа
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')Два ключевых показателя. Во-первых, гэп: SPY открылся на отметке $275.3, что на -7.4% ниже цены закрытия пятницы ($297.42). В первые же минуты падение S&P 500 достигло порога первого уровня (Level-1) в 7%. Во-вторых, более важный показатель: вместо стандартных 390 минутных баров в регулярной сессии зафиксировано всего 376. Торги прекратились после 09:34 по восточному времени: начиная с 09:35 ET, 14 минут подряд данные не фиксировались. Торговля не велась ни на одной площадке. Этот пробел в данных и является признаком срабатывания механизма приостановки торгов. Сессия закрылась на отметке $274.4, падение составило -7.7% при объеме в 304.6 млн акций.
Характер приостановленной сессии
Точный SQL-код для каждого числа
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_timeВ первом временном интервале одновременно произошли обвал, приостановка торгов и возобновление: минимум в $273.5 находится в пределах нескольких центов от дневного минимума в $273.45 — послеполуденная котировка в 14:44 ET лишь повторила утренний уровень. После возобновления торгов динамика практически отсутствовала: цена закрытия каждого полугодового интервала колебалась в пределах нескольких долларов от цены возобновления. Основной объем потерь был зафиксирован в первые минуты торгов. Объем торгов подтвердил эту динамику: последний полуторачасовой интервал с объемом 46.7 миллионов акций стал самым активным за день из-за закрытия позиций. Механизмы автоматической приостановки торгов (circuit breakers) созданы именно для такого сценария: остановить падение, инициировать аукцион возобновления и возобновить непрерывные торги по новой цене. Рыночные механизмы теперь работают по этой схеме постоянно.
Четыре остановки за восемь сессий
9 марта была лишь первой.
Точный SQL-код для каждого числа
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_dateМеханизмы Level-1 также срабатывали 12 марта (закрытие -9.6%), 16 марта (закрытие -11.6% на уровне $239.41 — крупнейшее однодневное процентное падение S&P с 1987 года) и 18 марта (остановка внутри дня, закрытие -5.2%). Четыре общерыночные остановки за восемь торговых дней после двадцати трех лет без подобных инцидентов. Обратите внимание на то, чего не произошло: каждая остановка соответствовала уровню Level 1 (7%). Уровни 13% и 20% не были достигнуты — даже 16 марта после остановки падение замедлилось, и сессия завершилась в пределах линии Level-2.
Кто потерял больше всех
Показатель индекса скрывает разброс цен внутри него. Расчет среднего значения по нескольким крупнейшим компаниям показывает реальный масштаб падения — как непосредственно 9 марта, так и за весь период снижения с 19 февраля по 23 марта.
Точный SQL-код для каждого числа
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 ASCНаибольшее падение в группе акций показала компания CCL (Carnival, круизный оператор), потерявшая -19.9% только 9 марта и -72.3% от пика до минимума. United Airlines потеряла -66.9% во время обвала; ExxonMobil -47.9%; JPMorgan -42.6%. На этом фоне падение SPY на -34.2% выглядит незначительным, Apple снизилась примерно на уровне рынка — на -31%, а Walmart — акции производителя товаров первой необходимости, спрос на которые сохранялся во время локдаунов, — завершили этот период, потеряв всего -2.9%. Секторы путешествий и энергетики переоценили активы из-за остановки мировой экономики; товары повседневного спроса практически не изменились в цене.
Кратко о ценовой войне на рынке нефти
Энергетический конфликт требует отдельного пояснения, так как термин «ценовая война на рынке нефти» объясняет вторую часть событий от 9 марта. Переговоры OPEC+ по объемам добычи между Саудовской Аравией и Россией завершились без соглашения в пятницу, 6 марта 2020 года. В выходные Саудовская Аравия снизила официальные цены на продажу и объявила о планах увеличить добычу. Это произошло на фоне резкого падения спроса на перевозки — двойной шок, вызванный одновременно избытком предложения и дефицитом спроса. В понедельник рынок акций отреагировал на оба фактора сразу, что отражено в таблице выше: акции ExxonMobil упали на -12.2% в этот понедельник, в то время как индекс SPY снизился на -7.7%.
Бегство в защитные активы — и неделя, когда тренд изменился
Акции составляли лишь половину картины. Вторая половина — это рынок казначейских облигаций, где классическое бегство в защитные активы достигло рекорда, а затем, в течение одной примечательной недели, развернулось в обратную сторону.
Точный SQL-код для каждого числа
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 dateДоходность 10-летних облигаций составляла 1.56% на пике рынка акций 19 февраля. К 9 марта — моменту приостановки торгов — она закрылась на уровне 0.54%, достигнув исторического минимума; покупатели соглашались на доходность в полпроцента годовых в течение десяти лет в обмен на безопасность. Затем ситуация изменилась: к 18 марта доходность 10-летних облигаций выросла до 1.18%, в то время как акции продолжали падать. Это был «рывок за наличными» — неделя, когда фонды, компании и иностранные держатели продавали любые ликвидные активы, включая казначейские облигации, чтобы получить доллары. В тот же день 3-месячные векселя закрылись на уровне 0.02% — эквиваленты наличности упали до нуля. Когда самый безопасный и самый рискованный активы падают одновременно, объектом покупки становится сама ликвидность. Кривая доходности навсегда сохранит этот эпизод в своей истории.
Что показывает лента опционов
Листинговые опционы SPY — основной инструмент хеджирования на рынке — зафиксировали начало паники, и наиболее важным является время этого события.
Точный SQL-код для каждого числа
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 sessionВ пятницу, 6 марта, до приостановки торгов, соотношение объемов проданных путов и купленных коллов достигло пика на уровне 1.95 — на каждый колл приходилось почти два пута. 9 марта объем торгов составил 4.35 млн путов против 2.38 млн коллов, что соответствует соотношению 1.83. К 12 марта — сессии с объемом -9.6% — соотношение снизилось до 1.32, так как объем торгов коллами начал расти вместе с путами. Наибольший перекос в сторону путов наблюдался в начале падения, а не в ее конце: защита закупалась заранее, и последующие более масштабные дни снижения характеризовались более сбалансированными объемами торгов опционами.
Как на самом деле работают общерыночные приостановки торгов
Механизм работы, поскольку такие случаи происходят достаточно редко, чтобы о них забывали: общерыночные предохранители активируются при снижении индекса S&P 500 относительно предыдущего закрытия. Падение на 7% (Level 1) и на 13% (Level 2) вызывает 15-минутную приостановку торгов, если это произойдет до 15:25 по восточному времени (ET). Падение на 20% (Level 3) полностью завершает торговую сессию. Во время приостановки торги на всех биржах прекращаются; ордера накапливаются для аукциона по возобновлению торгов. Этот аукцион объединяет накопленный спрос и предложение в одной цене, подобно тому, как это делает открывающий аукцион каждое утро. Такая структура была разработана после flash crash 2010 года, когда выяснилось, что старые пороги были слишком высокими и не срабатывали даже при реальном обвале.
Март 2020 года стал первым — и на данный момент единственным — реальным испытанием этой системы. Результаты отражены в панелях выше: четыре приостановки уровня Level-1, каждая из которых завершилась упорядоченным возобновлением торгов; уровни Level 2 и Level 3 достигнуты не были. Рынок продолжал функционировать в каждой из 23 сессий во время самого быстрого падения индекса в его истории. После этого правила остались без изменений: пороги 7/13/20 процентов относительно S&P 500 действуют и сегодня. Система испытала нагрузку, но механизмы сработали исправно.
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 is visible in this page's minute tape as 14 consecutive missing bars starting at 09:35 ET.
How much did the stock market fall in the COVID crash?
Measured on SPY's daily closes, -34.2% — from $338.31 on February 19, 2020 to $222.51 on March 23, 2020, across 23 trading sessions.
How many circuit breakers were hit in March 2020?
Four market-wide Level-1 halts: March 9, 12, 16, and 18. They were the first since October 1997.
Were Level 2 or Level 3 circuit breakers ever hit in 2020?
No. All four 2020 halts were Level 1 (7%). No Level-2 (13%) or Level-3 (20%) market-wide halt has ever fired under the current design — not even on March 16, the crash's worst session at -11.6%.
Did the market fully recover from the COVID crash?
Yes. SPY closed above its February 19, 2020 peak on 2020-08-18 — 103 trading sessions after the March 23 low, one of the fastest full recoveries from a bear market of this depth.
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.