{"slug":"santa-claus-rally","qid":"window_vs_december","label":"window_vs_december","post_title":"santa-claus-rally","post_url":"/blog/santa-claus-rally#q-window_vs_december","columns":["label","years_measured","up_years","hit_rate_pct","mean_pct","median_pct","worst_pct","worst_year"],"rows":[{"label":"Santa Claus window","years_measured":23,"up_years":16,"hit_rate_pct":69.6,"mean_pct":0.61,"median_pct":0.53,"worst_pct":-2.9,"worst_year":"2014"},{"label":"December before the window","years_measured":23,"up_years":17,"hit_rate_pct":73.9,"mean_pct":0.21,"median_pct":0.85,"worst_pct":-12.68,"worst_year":"2018"},{"label":"Calendar December","years_measured":23,"up_years":15,"hit_rate_pct":65.2,"mean_pct":0.45,"median_pct":0.7,"worst_pct":-9.33,"worst_year":"2018"}],"shape":"table","sql":"WITH\n    spy_days AS\n    (\n        SELECT\n            date,\n            toYear(date)                                   AS y,\n            toMonth(date)                                  AS m,\n            argMax(toFloat64(close), _ingest_time)         AS close\n        FROM global_markets.stocks_daily_aggs\n        WHERE ticker = 'SPY'\n          AND toMonth(date) IN (11, 12, 1)\n        GROUP BY date\n    ),\n    ranked AS\n    (\n        SELECT\n            date,\n            y,\n            m,\n            close,\n            row_number() OVER (PARTITION BY y, m ORDER BY date DESC) AS sessions_from_end,\n            row_number() OVER (PARTITION BY y, m ORDER BY date ASC)  AS sessions_from_start\n        FROM spy_days\n    ),\n    windows AS\n    (\n        SELECT\n            toUInt16(if(m = 1, y - 1, y))                       AS year,\n            anyIf(close, m = 11 AND sessions_from_end = 1)      AS november_close,\n            anyIf(close, m = 12 AND sessions_from_end = 6)      AS base_close,\n            anyIf(close, m = 12 AND sessions_from_end = 1)      AS december_close,\n            anyIf(close, m = 1 AND sessions_from_start = 2)     AS end_close\n        FROM ranked\n        GROUP BY year\n        HAVING countIf(m = 11 AND sessions_from_end = 1) = 1\n           AND countIf(m = 12 AND sessions_from_end = 6) = 1\n           AND countIf(m = 1 AND sessions_from_start = 2) = 1\n    ),\n    unpivoted AS\n    (\n        SELECT\n            year,\n            arrayJoin([\n                ('Santa Claus window',         (end_close / base_close - 1) * 100),\n                ('December before the window', (base_close / november_close - 1) * 100),\n                ('Calendar December',          (december_close / november_close - 1) * 100)\n            ])                                                  AS pair,\n            tupleElement(pair, 1)                               AS label,\n            tupleElement(pair, 2)                               AS return_pct\n        FROM windows\n    )\nSELECT\n    label,\n    count()                                                            AS years_measured,\n    countIf(return_pct > 0)                                            AS up_years,\n    round(countIf(return_pct > 0) * 100 / count(), 1)                  AS hit_rate_pct,\n    round(avg(return_pct), 2)                                          AS mean_pct,\n    round(quantileDeterministic(0.5)(return_pct, toUInt32(year)), 2)   AS median_pct,\n    round(min(return_pct), 2)                                          AS worst_pct,\n    toString(argMin(year, return_pct))                                 AS worst_year\nFROM unpivoted\nGROUP BY label\nORDER BY label DESC","computed_at":"2026-09-17T17:09:45.411446+00:00","elapsed":1.637095556}