{"slug":"can-a-death-cross-be-bullish","qid":"forward_record","label":"forward_record","post_title":"can-a-death-cross-be-bullish","post_url":"/blog/can-a-death-cross-be-bullish#q-forward_record","columns":["horizon","case_count","higher_count","higher_pct","all_sessions_higher_pct","avg_return_pct","median_return_pct","worst_pct","best_pct"],"rows":[{"horizon":"1 month","case_count":11,"higher_count":6,"higher_pct":55,"all_sessions_higher_pct":66,"avg_return_pct":2,"median_return_pct":2.8,"worst_pct":-8.9,"best_pct":12.1},{"horizon":"3 months","case_count":11,"higher_count":9,"higher_pct":82,"all_sessions_higher_pct":72,"avg_return_pct":5.6,"median_return_pct":7.1,"worst_pct":-10.1,"best_pct":16.4},{"horizon":"6 months","case_count":11,"higher_count":8,"higher_pct":73,"all_sessions_higher_pct":76,"avg_return_pct":10.6,"median_return_pct":11.9,"worst_pct":-11.4,"best_pct":27.7},{"horizon":"12 months","case_count":11,"higher_count":9,"higher_pct":82,"all_sessions_higher_pct":82,"avg_return_pct":14.7,"median_return_pct":19.1,"worst_pct":-41.2,"best_pct":50.9}],"shape":"table","sql":"WITH\nbars AS\n(\n    SELECT\n        date,\n        toFloat64(argMax(close, _ingest_time)) AS px\n    FROM global_markets.stocks_daily_aggs\n    WHERE ticker = 'SPY'\n    GROUP BY date\n),\nsmas AS\n(\n    SELECT\n        date,\n        px,\n        row_number() OVER (ORDER BY date) AS rn,\n        avg(px) OVER (ORDER BY date ROWS BETWEEN 49 PRECEDING AND CURRENT ROW) AS avg_50,\n        avg(px) OVER (ORDER BY date ROWS BETWEEN 199 PRECEDING AND CURRENT ROW) AS avg_200,\n        max(px) OVER (ORDER BY date ROWS BETWEEN 251 PRECEDING AND CURRENT ROW) AS high_1y,\n        leadInFrame(px, 21) OVER (ORDER BY date ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING) AS px_1m,\n        leadInFrame(px, 63) OVER (ORDER BY date ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING) AS px_3m,\n        leadInFrame(px, 126) OVER (ORDER BY date ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING) AS px_6m,\n        leadInFrame(px, 252) OVER (ORDER BY date ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING) AS px_12m\n    FROM bars\n),\nstates AS\n(\n    SELECT\n        *,\n        (avg_50 < avg_200) AS below,\n        lagInFrame((avg_50 < avg_200), 1) OVER (ORDER BY date ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING) AS prev_below\n    FROM smas\n),\nflags AS\n(\n    SELECT\n        *,\n        (rn >= 201 AND below = 1 AND prev_below = 0) AS is_death,\n        (rn >= 201 AND below = 0 AND prev_below = 1) AS is_golden\n    FROM states\n),\nunrolled AS\n(\n    SELECT\n        is_death,\n        tupleElement(h, 1) AS horizon_order,\n        tupleElement(h, 2) AS horizon,\n        tupleElement(h, 3) AS fwd_px,\n        px\n    FROM flags\n    ARRAY JOIN [(1, '1 month', px_1m), (2, '3 months', px_3m), (3, '6 months', px_6m), (4, '12 months', px_12m)] AS h\n    WHERE rn >= 201\n      AND tupleElement(h, 3) > 0\n)\nSELECT\n    horizon,\n    countIf(is_death)                                                            AS case_count,\n    countIf(is_death AND fwd_px > px)                                            AS higher_count,\n    toInt32(round(100 * countIf(is_death AND fwd_px > px) / countIf(is_death)))  AS higher_pct,\n    toInt32(round(100 * countIf(fwd_px > px) / count()))                         AS all_sessions_higher_pct,\n    round(avgIf((fwd_px / px - 1) * 100, is_death), 1)                           AS avg_return_pct,\n    round(quantileExactIf((fwd_px / px - 1) * 100, is_death), 1)                 AS median_return_pct,\n    round(minIf((fwd_px / px - 1) * 100, is_death), 1)                           AS worst_pct,\n    round(maxIf((fwd_px / px - 1) * 100, is_death), 1)                           AS best_pct\nFROM unrolled\nGROUP BY horizon_order, horizon\nHAVING countIf(is_death) > 0\nORDER BY horizon_order","computed_at":"2026-09-13T15:00:28.988357+00:00","elapsed":0.011774502}