Strasmore Research
Deep Dives · Matt ConnorBy Matt Connor ·

Apa itu Survivorship Bias dalam Data Saham?

Survivorship bias menyebabkan data saham tidak menyertakan syarikat yang telah dinyahsenarai. Ketahui kesan jurang ini terhadap backtest dan cara mengukur alam semesta penuh.

Bias kelangsungan hidup (survivorship bias) dalam data saham ialah jurang yang wujud apabila sesuatu fail hanya mengandungi ticker yang masih disenaraikan pada hari ini. Setiap syarikat yang telah dinyahsenarai, diambil alih, ditukar nama, atau ditutup secara senyap tidak disertakan, dan ujian ke belakang (backtest) yang dijalankan merentasi alam semesta tersebut mengukur sampel sejarah yang telah ditapis dan bukannya pasaran seperti yang sebenarnya berlaku. Tiada apa-apa dalam fail tersebut yang kelihatan rosak. Setiap baris membawa harga sebenar, dan masalahnya ialah baris-baris yang tidak ditulis oleh sesiapa.

Apakah bias kelangsungan hidup dalam data saham?

Fikirkan tentang cara set data "semua saham AS" dipasang. Sesuatu sistem meminta pembekal untuk mendapatkan senarai simbol, kemudian menarik sejarah harga bagi setiap nama dalam senarai tersebut. Senarai itu adalah semasa mengikut pembinaannya. Ia menjawab soalan "apa yang didagangkan sekarang", dan setiap simbol yang berhenti didagangkan sebelum permintaan dibuat hilang daripada jawapan tersebut, bersama-sama dengan keseluruhan sejarahnya.

Istilah ini berasal daripada kerja Abraham Wald pada tahun 1943 mengenai pengebom yang pulang: perisai diletakkan di tempat yang tidak menunjukkan kesan tembakan pada pesawat yang terselamat, kerana pesawat yang terkena tembakan di tempat tersebut tidak pernah pulang untuk diukur. Pangkalan data saham yang dibina daripada penyenaraian hari ini mempunyai bentuk yang sama. Sampel yang anda lihat ialah sampel yang berjaya pulang, dan keahlian di dalamnya ditentukan oleh hasil yang cuba diukur oleh ujian ke belakang.

Berapa banyak ticker yang hilang setiap tahun?

Mulakan dengan saiz lubang tersebut. Panel di bawah mengumpulkan setiap simbol pada pita ekuiti harian mengikut tahun kalendar bar terakhirnya, dengan hanya mengekalkan simbol yang telah senyap sebelum Januari 2026.

PertanyaanSimbol yang mencatatkan bar harian terakhir, mengikut tahun
SQL tepat di sebalik setiap nombor
SELECT
    toString(toYear(last_bar))  AS year,
    count()                     AS gone_dark_count,
    round(avg(bars_on_tape))    AS avg_bars_on_tape
FROM
(
    SELECT
        ticker,
        max(date) AS last_bar,
        count()   AS bars_on_tape
    FROM global_markets.stocks_daily_aggs
    WHERE date >= '2015-01-01'
      AND ticker NOT IN ('SPCX')
    GROUP BY ticker
    HAVING max(date) >= toDate('2016-01-01')
       AND max(date) <  toDate('2026-01-01')
)
GROUP BY year
ORDER BY year
Run this yourself

Dalam 2025, 1283 simbol mencetak bar harian terakhir dan tidak pernah kembali. Ini bukan syarikat cengkerang yang berusia seminggu: kumpulan itu mencatatkan purata 999 bar harian dari 2015 dan seterusnya. Merentasi 10 tahun dalam panel tersebut, jumlahnya berubah-ubah, dan tiada tahun yang berada berhampiran sifar. Simbol keluar atas sebab biasa: pengambilalihan ditutup, dana ditutup, penggabungan menggabungkan satu penyenaraian ke dalam yang lain, atau piawaian penyenaraian tidak lagi dipenuhi.

Perhatikan bias muncul dalam skrip sepuluh baris

Aritmetik lebih mudah dilihat daripada diterangkan. Skrip di bawah menulis dua fail CSV kecil: satu alam semesta yang hanya mengandungi syarikat yang terselamat, dan alam semesta yang sama dengan nama yang berhenti didagangkan ditambah semula. Ia mengira purata setiap fail dan mencetak perbezaannya. Ia hanya menggunakan pustaka standard Python, jadi tiada apa yang perlu dipasang.

python3 <<'PY'
import csv, statistics

survivors = [("AAA", 0.42), ("BBB", 0.18), ("CCC", 0.63), ("DDD", 0.07)]
delisted  = [("EEE", -1.00), ("FFF", -0.55), ("GGG", 0.31)]

def write_csv(path, rows):
    with open(path, "w", newline="") as f:
        w = csv.writer(f)
        w.writerow(["ticker", "total_return"])
        w.writerows(rows)

def mean_return(path):
    with open(path, newline="") as f:
        return statistics.mean(float(r["total_return"]) for r in csv.DictReader(f))

write_csv("survivors_only.csv", survivors)
write_csv("full_universe.csv", survivors + delisted)

a = mean_return("survivors_only.csv")
b = mean_return("full_universe.csv")
print(f"survivors only: {a:+.1%} across {len(survivors)} names")
print(f"full universe: {b:+.1%} across {len(survivors) + len(delisted)} names")
print(f"survivorship bias: {a - b:+.1%}")
PY

Tujuh angka pulangan dalam skrip itu adalah rekaan, bukan data pasaran. Ia wujud untuk menjadikan mekanismenya kelihatan. Purata syarikat yang terselamat sahaja ialah +32.5%, purata alam semesta penuh ialah +0.9%, dan perbezaan sebanyak 31.6 mata ialah bias tersebut. Tiada apa-apa dalam fail syarikat yang terselamat sahaja yang palsu. Setiap daripada empat nama itu benar-benar memberikan pulangan seperti yang dinyatakan. Fail tersebut hanya tidak menyebut tiga nama yang akan menurunkan purata tersebut.

Pengukuran yang sama pada kohort sebenar

Sekarang jalankan pengiraan yang sama pada pita tersebut. Panel di bawah membina alam semesta permulaan pada Januari setiap tahun dari 2016 hingga 2022, mengekalkan simbol yang dibuka pada bulan tersebut melebihi $5 dengan purata volum harian melebihi 250,000 saham, kemudian mengikuti setiap satu daripadanya sehingga penutupan terkini. Nama yang berhenti didagangkan dikekalkan pada harga cetakan terakhirnya dan bukannya digugurkan. Lajur syarikat yang terselamat mengira purata hanya simbol yang masih mencetak bar sekarang. Lajur alam semesta penuh mengira purata semua orang yang berada di sana pada permulaan.

PertanyaanPurata hanya yang bertahan lwn purata seluruh kohort, mengikut tahun permulaan
SQL tepat di sebalik setiap nombor
WITH
    entry AS
    (
        SELECT
            ticker,
            toYear(date)                   AS cohort_start,
            argMin(toFloat64(close), date) AS entry_close
        FROM global_markets.stocks_daily_aggs
        WHERE toMonth(date) = 1
          AND date BETWEEN '2016-01-01' AND '2022-01-31'
          AND ticker NOT IN ('SPCX')
        GROUP BY ticker, cohort_start
        HAVING argMin(toFloat64(close), date) >= 5
           AND avg(volume) >= 250000
    ),
    outcome AS
    (
        SELECT
            ticker,
            argMax(toFloat64(close), date) AS final_close,
            max(date)                      AS last_bar
        FROM global_markets.stocks_daily_aggs
        WHERE date >= '2016-01-01'
        GROUP BY ticker
    )
SELECT
    toString(e.cohort_start)                                                             AS cohort_year,
    count()                                                                              AS cohort_size,
    countIf(o.last_bar < today() - 45)                                                   AS gone_count,
    round(100 * avgIf(o.final_close / e.entry_close - 1, o.last_bar >= today() - 45), 1) AS survivors_only_pct,
    round(100 * avg(o.final_close / e.entry_close - 1), 1)                               AS full_universe_pct,
    round(100 * (avgIf(o.final_close / e.entry_close - 1, o.last_bar >= today() - 45)
                 - avg(o.final_close / e.entry_close - 1)), 1)                           AS gap_pct
FROM entry AS e
INNER JOIN outcome AS o ON o.ticker = e.ticker
GROUP BY e.cohort_start
HAVING countIf(o.last_bar >= today() - 45) > 0
ORDER BY e.cohort_start
Run this yourself

Bagi kohort 2016, 968 daripada 2728 nama telah meninggalkan pita tersebut pada masa halaman ini dibina. Mengira purata hanya syarikat yang terselamat memberikan 212%. Mengira purata keseluruhan kohort memberikan 149.3%. Lajur jurang mengandungi jarak antara kedua-dua bacaan tersebut: 62.7 mata bagi kohort itu, dan 9.1 mata bagi kohort 2022, yang mempunyai masa yang jauh lebih singkat untuk kehilangan ahli. Ini adalah jumlah pulangan sepanjang tempoh yang berbeza, jadi baca ke bawah lajur jurang dan bukannya membandingkan tahap merentasi baris. Soalan alam semesta yang sama terletak di bawah cara pulangan bulanan diukur dan di bawah setiap purata wajaran sama.

Ke mana perginya nama-nama yang hilang

Penyahsenaraian tidak bermakna kejatuhan automatik. Panel di bawah mengambil satu alam semesta permulaan, Januari 2019, dan menyusunnya mengikut apa yang berlaku seterusnya.

PertanyaanUniveres Januari 2019, dikumpulkan mengikut nasib setiap nama
SQL tepat di sebalik setiap nombor
WITH
    entry AS
    (
        SELECT
            ticker,
            argMin(toFloat64(close), date) AS entry_close
        FROM global_markets.stocks_daily_aggs
        WHERE date BETWEEN '2019-01-01' AND '2019-01-31'
          AND ticker NOT IN ('SPCX')
        GROUP BY ticker
        HAVING argMin(toFloat64(close), date) >= 5
           AND avg(volume) >= 250000
    ),
    outcome AS
    (
        SELECT
            ticker,
            argMax(toFloat64(close), date) AS final_close,
            max(date)                      AS last_bar
        FROM global_markets.stocks_daily_aggs
        WHERE date >= '2019-01-01'
        GROUP BY ticker
    )
SELECT
    if(o.last_bar >= today() - 45,
       'Still trading',
       concat('Last bar in ', toString(toYear(o.last_bar))))            AS outcome_label,
    count()                                                             AS cohort_size,
    round(100 * avg(o.final_close / e.entry_close - 1), 1)              AS avg_return_pct,
    round(100 * countIf(o.final_close > e.entry_close) / count(), 1)    AS share_above_entry_pct
FROM entry AS e
INNER JOIN outcome AS o ON o.ticker = e.ticker
GROUP BY outcome_label
ORDER BY outcome_label
Run this yourself

Kumpulan Still trading memegang 1954 nama, dengan purata 139.8% daripada harga kemasukan Januari 2019, dengan 72.1% daripadanya melebihi harga permulaan. Baca baris 9 dan jalan keluar kelihatan bercampur-campur. Syarikat yang dibeli pada premium boleh meninggalkan pita pada cetakan akhir yang kukuh, manakala syarikat yang pudar ke arah notis penyahsenaraian meninggalkan pita pada cetakan yang lemah. Kedua-duanya hilang daripada fail syarikat yang terselamat sahaja. Masukkan semula nama-nama tersebut dan purata akan berubah.

Bias kelangsungan hidup lwn bias pandang ke hadapan

Kedua-dua ini difailkan bersama, dan ia merosakkan ujian ke belakang di tempat yang berbeza. Bias pandang ke hadapan ialah masalah masa: ujian menggunakan fakta pada saat tiada sesiapa yang memilikinya lagi, seperti angka pendapatan yang dinyatakan semula atau pelarasan yang digunakan beberapa bulan kemudian. Bias kelangsungan hidup ialah masalah keahlian: senarai yang didagangkan oleh ujian ialah senarai yang wujud sekarang, bukan senarai yang wujud pada masa itu. Ujian ke belakang boleh menjadi sempurna dari segi masa tetapi masih mendagangkan alam semesta yang dipasang dengan pandangan retrospektif. Pelarasan harga membawa perangkap yang berkaitan, memandangkan sejarah harga diselaraskan pecahan menulis semula tahap setiap bar sebelum tarikh pecahan.

Cara membetulkan bias kelangsungan hidup dalam ujian ke belakang

  1. Bina semula alam semesta pada setiap tarikh. Keahlian mengikut titik masa bermakna senarai dibina semula untuk hari ujian dijalankan, daripada rekod yang wujud pada hari itu. Ujian kemudian memegang nama yang aktif pada masa itu, termasuk nama yang hilang kemudiannya.
  2. Simpan induk sekuriti. Induk sekuriti memetakan pengecam dari semasa ke semasa: simbol mana yang merujuk kepada syarikat mana antara tarikh mana, dan apa yang menggantikannya selepas penggabungan atau pertukaran nama. Fail harga menggunakan simbol sebagai kunci. Syarikat tidak menggunakan simbol sebagai kunci.
  3. Tutup nama yang dinyahsenarai pada harga sebenar terakhirnya. Apabila sesuatu nama meninggalkan pita, kedudukan tersebut keluar pada cetakan terakhirnya dan bukannya hilang daripada purata. Pemadaman adalah perkara yang membuka jurang dalam panel di atas. Ujian ke belakang yang boleh dihasilkan semula menetapkan peraturan itu dalam kod di sebelah alam semesta.
  4. Baca dokumentasi pembekal mengenai simbol yang mati. Vendor yang mengambil serius perkara ini akan menuliskannya. Massive mendedahkan simbol yang dinyahsenarai melalui titik akhir tickernya dengan bendera aktif, dan membawa perubahan simbol, penyahsenaraian, penggabungan dan pengambilalihan sebagai rekod dalam titik akhir peristiwa ticker yang berasingan.
"Kami tidak menukar data atau menggabungkan perubahan ticker ke dalam satu siri agregat."
Pangkalan pengetahuan Massive, "Bagaimanakah Massive mengendalikan perubahan ticker dan pengambilalihan?", dibaca Ogos 2026.

Jika dokumentasi pembekal tidak menyatakan apa-apa tentang simbol yang dinyahsenarai, anggap muat turun tersebut sebagai syarikat yang terselamat sahaja sehingga anda menyemaknya. Liputan sangat berbeza antara sumber: lihat nota kami mengenai API data pasaran saham percuma.

Perangkap ticker kitar semula

Simbol kembali ke dalam edaran selepas penyenaraian berakhir. Panel di bawah mengira simbol yang mencetak sekurang-kurangnya 200 bar harian, senyap selama sekurang-kurangnya 250 hari kalendar, kemudian muncul semula sebagai penyenaraian baharu dari 2022 dan seterusnya.

PertanyaanSimbol yang disenaraikan semula di bawah penerbit baharu selepas tempoh tidak aktif yang lama
SQL tepat di sebalik setiap nombor
SELECT
    toString(toYear(relisted_on)) AS year,
    count()                       AS relisting_count,
    round(avg(dark_stretch))      AS avg_dark_stretch,
    max(dark_stretch)             AS longest_dark_stretch
FROM
(
    SELECT
        ticker,
        any(relist_date)                             AS relisted_on,
        dateDiff('day', max(date), any(relist_date)) AS dark_stretch
    FROM
    (
        SELECT
            d.ticker      AS ticker,
            d.date        AS date,
            i.relist_date AS relist_date
        FROM global_markets.stocks_daily_aggs AS d
        INNER JOIN
        (
            SELECT
                ticker,
                min(toDate(listing_date)) AS relist_date
            FROM global_markets.stocks_ipos
            WHERE toDate(listing_date) BETWEEN '2022-01-01' AND today()
              AND ticker NOT IN ('SPCX')
            GROUP BY ticker
        ) AS i ON i.ticker = d.ticker
        WHERE d.date < i.relist_date
    )
    GROUP BY ticker
    HAVING dateDiff('day', max(date), any(relist_date)) >= 250
       AND count() >= 200
)
GROUP BY year
ORDER BY year
Run this yourself

Dalam 2022, 16 simbol kembali dengan cara ini, selepas purata kesunyian selama 3247 hari kalendar. Tempoh senyap paling lama dalam baris itu berlangsung selama 5734 hari. Fail harga yang dikunci pada simbol sahaja menyatukan sejarah satu syarikat ke syarikat lain, dan gabungan itu kelihatan bersih sepanjang masa. Ini adalah kes yang ditangkap oleh induk sekuriti dan tidak dapat ditangkap oleh lajur ticker kosong.

Nota data dan definisi

Satu simbol dikira masih didagangkan apabila bar harian terkininya jatuh dalam tempoh 45 hari kalendar dari hari halaman ini dijana. Penampan itu meliputi ketinggalan satu hingga dua hari di hadapan pita dan nama yang jarang didagangkan yang melangkau sesi.

Pulangan adalah jumlah pulangan harga dari penutupan kemasukan Januari hingga penutupan terakhir yang tersedia untuk simbol tersebut, tanpa dividen dan tanpa pengiraan tahunan. Kohort yang lebih awal mempunyai masa yang lebih lama untuk dikompaun, jadi tahap tersebut tidak boleh dibandingkan merentasi baris. Jurang antara kedua-dua purata tersebut boleh dibandingkan.

Alam semesta kemasukan mengekalkan simbol yang berharga $5 atau lebih dengan purata volum harian sebanyak 250,000 saham atau lebih semasa bulan kemasukan. Pita harian membawa dana, unit dan waran di sebelah syarikat operasi, dan tiada apa-apa di sini yang memisahkannya, jadi penutupan dana dikira bersama penyahsenaraian korporat. Simbol dengan konflik identiti yang diketahui dalam suapan vendor dikecualikan dalam SQL daripada setiap panel dan bukannya ditapis selepas itu.

Soalan Lazim

Apakah bias kelangsungan hidup dalam ujian ke belakang saham?

Ia adalah ralat yang ditinggalkan apabila alam semesta yang boleh didagangkan dibina daripada simbol yang masih wujud hari ini. Syarikat yang dinyahsenarai atau diambil alih tidak pernah memasuki ujian, jadi strategi tersebut hanya ditawarkan nama yang berjaya sampai ke masa kini.

Bagaimanakah saya tahu jika set data saya mempunyai bias kelangsungan hidup?

Ambil alam semesta yang didakwa oleh fail anda untuk tempoh beberapa tahun yang lalu dan semak bar terakhir setiap simbol. Jika setiap nama dalam senarai 2016 masih mencetak harga hari ini, senarai itu dibina daripada penyenaraian semasa dan nama yang mati tidak pernah ada di dalamnya.

Adakah bias kelangsungan hidup sentiasa menjadikan ujian ke belakang kelihatan lebih baik?

Tidak. Arahnya bergantung pada cara nama meninggalkan pita. Pengambilalihan boleh ditutup pada premium, dan menggugurkan nama tersebut merendahkan kohort, manakala pudar yang panjang ke arah notis penyahsenaraian melakukan sebaliknya. Panel di atas mengukur bersih kedua-duanya pada kohort sebenar.

Apakah alam semesta titik masa?

Ia adalah senarai keahlian yang dibina semula pada setiap tarikh dalam ujian, menggunakan hanya rekod yang wujud pada tarikh tersebut. Ia termasuk nama yang boleh didagangkan pada masa itu dan hilang kemudiannya, serta mengecualikan nama yang belum disenaraikan lagi.

Adakah bias kelangsungan hidup sama dengan bias pandang ke hadapan?

Tidak. Bias pandang ke hadapan adalah mengenai masa: ujian menggunakan maklumat sebelum sesiapa memilikinya. Bias kelangsungan hidup adalah mengenai keahlian: ujian mendagangkan senarai yang dipasang dengan pandangan retrospektif. Ujian ke belakang boleh menjadi bersih pada satu aspek dan rosak pada aspek yang lain.


Setiap panel di sini membawa SQL yang menghasilkannya. Tukar bulan kemasukan atau longgarkan penapis kecairan, kemudian jalankannya pada terminal Strasmore untuk melihat berapa banyak jurang yang bertahan dengan peraturan alam semesta anda sendiri.