{"slug":"bullish-vs-bearish-meaning","qid":"near_bears","label":"near_bears","post_title":"bullish-vs-bearish-meaning","post_url":"/blog/lang/pcm/bullish-vs-bearish-meaning#q-near_bears","columns":["peak","trough","decline_pct","days_top_to_bottom","new_high","days_bottom_to_new_high"],"rows":[{"peak":"Feb 2025","trough":"Apr 2025","decline_pct":-19,"days_top_to_bottom":48,"new_high":"Jun 2025","days_bottom_to_new_high":80}],"shape":"scalar","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\n  AND worst_drawdown <= -0.15\nORDER BY worst_drawdown ASC","computed_at":"2026-09-20T15:27:30.725136+00:00","elapsed":0.004424989}