{"slug":"bullish-vs-bearish-meaning","qid":"bear_markets","label":"bear_markets","post_title":"bullish-vs-bearish-meaning","post_url":"/blog/lang/pcm/bullish-vs-bearish-meaning#q-bear_markets","columns":["peak","trough","decline_pct","days_top_to_bottom","new_high","days_bottom_to_new_high"],"rows":[{"peak":"Oct 2007","trough":"Mar 2009","decline_pct":-56.5,"days_top_to_bottom":517,"new_high":"Mar 2013","days_bottom_to_new_high":1466},{"peak":"Feb 2020","trough":"Mar 2020","decline_pct":-34.1,"days_top_to_bottom":33,"new_high":"Aug 2020","days_bottom_to_new_high":148},{"peak":"Jan 2022","trough":"Oct 2022","decline_pct":-25.4,"days_top_to_bottom":282,"new_high":"Jan 2024","days_bottom_to_new_high":464},{"peak":"Sep 2018","trough":"Dec 2018","decline_pct":-20.2,"days_top_to_bottom":95,"new_high":"Apr 2019","days_bottom_to_new_high":126}],"shape":"table","sql":"WITH bars AS\n(\n    SELECT\n        date,\n        toFloat64(any(close)) AS close_price\n    FROM global_markets.stocks_daily_aggs\n    WHERE ticker = 'SPY'\n      AND close > 0\n    GROUP BY date\n),\npeaks AS\n(\n    SELECT\n        date,\n        close_price,\n        max(close_price) OVER (ORDER BY date ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) AS peak\n    FROM bars\n),\nepisodes AS\n(\n    SELECT\n        date,\n        close_price,\n        peak,\n        close_price / peak - 1 AS drawdown,\n        sum(if(close_price >= peak, 1, 0)) OVER (ORDER BY date ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) AS episode_id\n    FROM peaks\n),\nepisode_stats AS\n(\n    SELECT\n        episode_id,\n        min(date)               AS peak_date,\n        argMin(date, drawdown)  AS trough_date,\n        min(drawdown)           AS worst_drawdown\n    FROM episodes\n    GROUP BY episode_id\n),\nwith_next AS\n(\n    SELECT\n        episode_id,\n        peak_date,\n        trough_date,\n        worst_drawdown,\n        leadInFrame(peak_date, 1) OVER (ORDER BY episode_id ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING) AS recovered_on\n    FROM episode_stats\n)\nSELECT\n    formatDateTime(peak_date, '%b %Y')                                                AS peak,\n    formatDateTime(trough_date, '%b %Y')                                              AS trough,\n    round(100 * worst_drawdown, 1)                                                    AS decline_pct,\n    dateDiff('day', peak_date, trough_date)                                           AS days_top_to_bottom,\n    if(recovered_on > peak_date, formatDateTime(recovered_on, '%b %Y'), 'not yet')    AS new_high,\n    if(recovered_on > peak_date, dateDiff('day', trough_date, recovered_on), NULL)    AS days_bottom_to_new_high\nFROM with_next\nWHERE worst_drawdown <= -0.20\nORDER BY worst_drawdown ASC","computed_at":"2026-09-20T15:27:30.632171+00:00","elapsed":0.004046955}