{"slug":"survivorship-bias-in-stock-data","qid":"survivorship_gap","label":"Survivors-only average vs whole-cohort average, by starting year","post_title":"Survivorship Bias in Stock Data, Explained","post_url":"/blog/survivorship-bias-in-stock-data#q-survivorship_gap","columns":["cohort_year","cohort_size","gone_count","survivors_only_pct","full_universe_pct","gap_pct"],"rows":[{"cohort_year":"2016","cohort_size":2728,"gone_count":969,"survivors_only_pct":209.9,"full_universe_pct":148.2,"gap_pct":61.8},{"cohort_year":"2017","cohort_size":2522,"gone_count":819,"survivors_only_pct":160.8,"full_universe_pct":117.3,"gap_pct":43.4},{"cohort_year":"2018","cohort_size":2669,"gone_count":824,"survivors_only_pct":121.4,"full_universe_pct":88.7,"gap_pct":32.6},{"cohort_year":"2019","cohort_size":2646,"gone_count":695,"survivors_only_pct":138.1,"full_universe_pct":112.7,"gap_pct":25.5},{"cohort_year":"2020","cohort_size":2669,"gone_count":639,"survivors_only_pct":87,"full_universe_pct":73.1,"gap_pct":13.9},{"cohort_year":"2021","cohort_size":3416,"gone_count":957,"survivors_only_pct":64.4,"full_universe_pct":48.4,"gap_pct":16},{"cohort_year":"2022","cohort_size":3378,"gone_count":694,"survivors_only_pct":34.2,"full_universe_pct":24.9,"gap_pct":9.2}],"shape":"table","sql":"WITH\n    entry AS\n    (\n        SELECT\n            ticker,\n            toYear(date)                   AS cohort_start,\n            argMin(toFloat64(close), date) AS entry_close\n        FROM global_markets.stocks_daily_aggs\n        WHERE toMonth(date) = 1\n          AND date BETWEEN '2016-01-01' AND '2022-01-31'\n          AND ticker NOT IN ('SPCX')\n        GROUP BY ticker, cohort_start\n        HAVING argMin(toFloat64(close), date) >= 5\n           AND avg(volume) >= 250000\n    ),\n    outcome AS\n    (\n        SELECT\n            ticker,\n            argMax(toFloat64(close), date) AS final_close,\n            max(date)                      AS last_bar\n        FROM global_markets.stocks_daily_aggs\n        WHERE date >= '2016-01-01'\n        GROUP BY ticker\n    )\nSELECT\n    toString(e.cohort_start)                                                             AS cohort_year,\n    count()                                                                              AS cohort_size,\n    countIf(o.last_bar < today() - 45)                                                   AS gone_count,\n    round(100 * avgIf(o.final_close / e.entry_close - 1, o.last_bar >= today() - 45), 1) AS survivors_only_pct,\n    round(100 * avg(o.final_close / e.entry_close - 1), 1)                               AS full_universe_pct,\n    round(100 * (avgIf(o.final_close / e.entry_close - 1, o.last_bar >= today() - 45)\n                 - avg(o.final_close / e.entry_close - 1)), 1)                           AS gap_pct\nFROM entry AS e\nINNER JOIN outcome AS o ON o.ticker = e.ticker\nGROUP BY e.cohort_start\nHAVING countIf(o.last_bar >= today() - 45) > 0\nORDER BY e.cohort_start","computed_at":"2026-08-22T04:22:26.944307+00:00","elapsed":2.925779645}