{"slug":"look-ahead-bias-in-backtesting","qid":"hindsight_ceiling","label":"The hindsight ceiling: SPY buy and hold, the same year without its biggest up days, and perfect one-day foresight","post_title":"Look-Ahead Bias: The Backtest Killer","post_url":"/blog/look-ahead-bias-in-backtesting#q-hindsight_ceiling","columns":["year","sessions","buy_and_hold_pct","without_big_up_days_pct","hindsight_ceiling_multiple"],"rows":[{"year":2016,"sessions":252,"buy_and_hold_pct":9.6,"without_big_up_days_pct":-1.8,"hindsight_ceiling_multiple":4.3},{"year":2017,"sessions":251,"buy_and_hold_pct":19.4,"without_big_up_days_pct":19.4,"hindsight_ceiling_multiple":2.2},{"year":2018,"sessions":251,"buy_and_hold_pct":-6.3,"without_big_up_days_pct":-18.7,"hindsight_ceiling_multiple":6.4},{"year":2019,"sessions":252,"buy_and_hold_pct":28.8,"without_big_up_days_pct":22,"hindsight_ceiling_multiple":4.2},{"year":2020,"sessions":253,"buy_and_hold_pct":16.2,"without_big_up_days_pct":-44.9,"hindsight_ceiling_multiple":27.7},{"year":2021,"sessions":252,"buy_and_hold_pct":27,"without_big_up_days_pct":21.5,"hindsight_ceiling_multiple":4.9},{"year":2022,"sessions":251,"buy_and_hold_pct":-19.5,"without_big_up_days_pct":-56.2,"hindsight_ceiling_multiple":19.8},{"year":2023,"sessions":250,"buy_and_hold_pct":24.3,"without_big_up_days_pct":21.5,"hindsight_ceiling_multiple":5.1},{"year":2024,"sessions":252,"buy_and_hold_pct":23.3,"without_big_up_days_pct":15.2,"hindsight_ceiling_multiple":4.4},{"year":2025,"sessions":250,"buy_and_hold_pct":16.4,"without_big_up_days_pct":-6.6,"hindsight_ceiling_multiple":6.3}],"shape":"table","sql":"WITH bars AS (\n    SELECT date,\n           toFloat64(close) AS c,\n           any(toFloat64(close)) OVER (ORDER BY date ROWS BETWEEN 1 PRECEDING AND 1 PRECEDING) AS prev_c\n    FROM global_markets.stocks_daily_aggs\n    WHERE ticker = 'SPY'\n      AND date >= toDate('2015-12-01')\n      AND date <= toDate('2025-12-31')\n),\nrets AS (\n    SELECT toYear(date) AS year,\n           c / prev_c - 1 AS r\n    FROM bars\n    WHERE date >= toDate('2016-01-01')\n      AND prev_c > 0\n)\nSELECT year,\n       count() AS sessions,\n       round(100 * (exp(sum(log(1 + r))) - 1), 1) AS buy_and_hold_pct,\n       round(100 * (exp(sumIf(log(1 + r), r < 0.02)) - 1), 1) AS without_big_up_days_pct,\n       round(exp(sum(log(1 + abs(r)))), 1) AS hindsight_ceiling_multiple\nFROM rets\nGROUP BY year\nORDER BY year","computed_at":"2026-07-31T08:39:53.835226+00:00","elapsed":0.003206617}