{"slug":"covid-crash-march-2020","qid":"crash_map","label":"The COVID crash, peak to trough to recovery: SPY daily closes, one row","post_title":"COVID Crash 2020: Four Halts, Peak to Trough","post_url":"/blog/covid-crash-march-2020#q-crash_map","columns":["peak_date","peak_close","trough_date","trough_close","decline_pct","sessions_peak_to_trough","calendar_days","sessions_to_minus_30","gfc_2008_sessions_to_minus_30","recovery_date","sessions_trough_to_recovery"],"rows":[{"peak_date":"2020-02-19","peak_close":338.31,"trough_date":"2020-03-23","trough_close":222.51,"decline_pct":-34.2,"sessions_peak_to_trough":23,"calendar_days":33,"sessions_to_minus_30":22,"gfc_2008_sessions_to_minus_30":250,"recovery_date":"2020-08-18","sessions_trough_to_recovery":103}],"shape":"scalar","sql":"WITH dailies AS (\n    SELECT\n        toDate(toTimeZone(window_start, 'America/New_York')) AS d,\n        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\n    FROM global_markets.delayed_stocks_minute_aggs\n    WHERE ticker = 'SPY'\n      AND window_start >= toDateTime('2020-02-01 00:00:00') AND window_start < toDateTime('2020-09-01 00:00:00')\n    GROUP BY d\n),\npeak AS (SELECT argMax(d, (c, -toInt32(d))) AS pd, max(c) AS pc FROM dailies WHERE d < toDate('2020-03-01')),\ntrough AS (SELECT argMin(d, (c, toInt32(d))) AS td, min(c) AS tc FROM dailies WHERE d < toDate('2020-04-01')),\ngfc AS (\n    SELECT\n        toDate(toTimeZone(window_start, 'America/New_York')) AS d,\n        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\n    FROM global_markets.delayed_stocks_minute_aggs\n    WHERE ticker = 'SPY'\n      AND window_start >= toDateTime('2007-06-01 00:00:00') AND window_start < toDateTime('2009-01-01 00:00:00')\n    GROUP BY d\n),\ngpeak AS (SELECT argMax(d, (c, -toInt32(d))) AS pd, max(c) AS pc FROM gfc WHERE d < toDate('2008-01-01'))\nSELECT\n    toString((SELECT pd FROM peak)) AS peak_date,\n    round((SELECT pc FROM peak), 2) AS peak_close,\n    toString((SELECT td FROM trough)) AS trough_date,\n    round((SELECT tc FROM trough), 2) AS trough_close,\n    round(((SELECT tc FROM trough) / (SELECT pc FROM peak) - 1) * 100, 1) AS decline_pct,\n    countIf(d > (SELECT pd FROM peak) AND d <= (SELECT td FROM trough)) AS sessions_peak_to_trough,\n    dateDiff('day', (SELECT pd FROM peak), (SELECT td FROM trough)) AS calendar_days,\n    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,\n    (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,\n    toString((SELECT min(d) FROM dailies WHERE d > (SELECT td FROM trough) AND c >= (SELECT pc FROM peak))) AS recovery_date,\n    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\nFROM dailies","computed_at":"2026-07-26T05:28:56.909634+00:00","elapsed":2.559666381}