Strasmore Research
Deep Dives · Matt ConnorBy Matt Connor ·

Survivorship Bias ในข้อมูลหุ้นคืออะไรและส่งผลอย่างไร

Survivorship bias ทำให้ข้อมูลหุ้นขาดบริษัทที่ถูกเพิกถอนหรือถูกซื้อกิจการ ส่งผลให้การทำ Backtest คลาดเคลื่อนจากความเป็นจริง เรียนรู้วิธีวัดผลข้อมูลในระดับสากลเพื่อการวิเคราะห์ที่แม่นยำ

Survivorship bias ในข้อมูลหุ้นคือช่องว่างที่เกิดขึ้นเมื่อไฟล์ข้อมูลมีเฉพาะ Ticker ที่ยังคงมีการซื้อขายอยู่ในปัจจุบัน บริษัททั้งหมดที่ถูกเพิกถอนจากการเป็นหลักทรัพย์จดทะเบียน ถูกซื้อกิจการ เปลี่ยนชื่อ หรือปิดตัวลง จะหายไปอย่างเงียบเชียบ ทำให้การทำ Backtest บนชุดข้อมูลดังกล่าวเป็นการวัดผลจากกลุ่มตัวอย่างที่ถูกคัดกรองมาแล้ว ไม่ใช่สภาพตลาดที่เป็นจริงในขณะนั้น ข้อมูลในไฟล์ดูปกติทุกประการ แต่ละแถวมีราคาที่เกิดขึ้นจริง ปัญหาคือแถวข้อมูลที่ไม่มีใครบันทึกไว้นั่นเอง

Survivorship bias ในข้อมูลหุ้นคืออะไร?

ลองพิจารณาว่าชุดข้อมูล "หุ้นสหรัฐฯ ทั้งหมด" ถูกรวบรวมมาอย่างไร โดยทั่วไปจะมีกระบวนการขอรายชื่อสัญลักษณ์จากผู้ให้บริการข้อมูล แล้วจึงดึงประวัติราคาของแต่ละชื่อในรายการนั้น รายการดังกล่าวเป็นปัจจุบันโดยโครงสร้าง มันตอบคำถามว่า "ปัจจุบันมีอะไรให้เทรดบ้าง" และสัญลักษณ์ทุกตัวที่หยุดซื้อขายไปก่อนที่จะมีการร้องขอข้อมูลจะหายไปจากคำตอบ พร้อมกับประวัติทั้งหมดของมัน

คำนี้มีที่มาจากงานวิจัยของ Abraham Wald ในปี 1943 เกี่ยวกับเครื่องบินทิ้งระเบิดที่บินกลับฐาน โดยสรุปว่าควรติดเกราะในจุดที่เครื่องบินที่รอดกลับมาไม่มีรอยกระสุน เพราะเครื่องบินที่ถูกยิงในจุดเหล่านั้นไม่เคยได้กลับมาให้ตรวจวัด ฐานข้อมูลหุ้นที่สร้างจากรายชื่อหุ้นในปัจจุบันก็มีลักษณะเดียวกัน กลุ่มตัวอย่างที่คุณเห็นคือกลุ่มที่ "รอดกลับมา" และการได้เป็นส่วนหนึ่งของกลุ่มตัวอย่างนั้นถูกตัดสินด้วยผลลัพธ์ที่การทำ Backtest พยายามจะวัดผลนั่นเอง

มี Ticker จำนวนเท่าใดที่หายไปในแต่ละปี?

เริ่มต้นด้วยขนาดของช่องว่างนี้ ตารางด้านล่างจัดกลุ่มสัญลักษณ์ทั้งหมดในข้อมูลการซื้อขายรายวันตามปีปฏิทินของราคาปิดครั้งสุดท้าย โดยเก็บเฉพาะสัญลักษณ์ที่หยุดการซื้อขายไปก่อนเดือนมิถุนายน 2026

คำสั่งดึงข้อมูลจำนวนหลักทรัพย์ที่ถูกเพิกถอนรายปี
SQL เบื้องหลังตัวเลขแต่ละตัว
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

ใน 2025 มีสัญลักษณ์ 1283 ตัวที่พิมพ์ราคาปิดรายวันเป็นครั้งสุดท้ายและไม่เคยกลับมาอีกเลย นี่ไม่ใช่หุ้นปั่นที่เพิ่งเข้าตลาดเพียงสัปดาห์เดียว กลุ่มนี้มีค่าเฉลี่ยอยู่ที่ 999 วันทำการนับตั้งแต่ปี 2015 เป็นต้นมา ตลอดระยะเวลา 10 ปีในตาราง จำนวนนี้มีการเปลี่ยนแปลงและไม่มีปีใดที่ใกล้เคียงศูนย์ สัญลักษณ์ต่างๆ หายไปจากตลาดด้วยเหตุผลปกติ เช่น การควบรวมกิจการเสร็จสิ้น กองทุนปิดตัวลง การควบรวมบริษัท หรือการไม่ผ่านเกณฑ์การจดทะเบียน

สังเกตความเอนเอียงที่เกิดขึ้นในสคริปต์สิบบรรทัด

การคำนวณทางคณิตศาสตร์นั้นเห็นภาพได้ง่ายกว่าการอธิบาย สคริปต์ด้านล่างเขียนไฟล์ CSV ขนาดเล็กสองไฟล์ ได้แก่ ไฟล์จักรวาลหุ้นที่มีเฉพาะผู้รอดชีวิต และไฟล์เดิมที่เพิ่มชื่อหุ้นที่หยุดซื้อขายกลับเข้าไป จากนั้นคำนวณค่าเฉลี่ยของแต่ละไฟล์และแสดงส่วนต่าง สคริปต์นี้ใช้เพียงไลบรารีมาตรฐานของ Python จึงไม่ต้องติดตั้งอะไรเพิ่มเติม

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

ตัวเลขผลตอบแทนทั้งเจ็ดในสคริปต์นั้นถูกสมมติขึ้น ไม่ใช่ข้อมูลตลาดจริง แต่มีไว้เพื่อให้เห็นกลไกการทำงาน ค่าเฉลี่ยของกลุ่มผู้รอดชีวิตอยู่ที่ +32.5% ในขณะที่กลุ่มจักรวาลหุ้นเต็มรูปแบบอยู่ที่ +0.9% โดยส่วนต่าง 31.6 จุดคือค่าความเอนเอียง (bias) ไม่มีข้อมูลใดในไฟล์ผู้รอดชีวิตที่เป็นเท็จ หุ้นทั้งสี่ชื่อนั้นให้ผลตอบแทนตามที่ระบุจริง เพียงแต่ไฟล์นั้นไม่ได้กล่าวถึงหุ้นอีกสามตัวที่จะฉุดค่าเฉลี่ยให้ต่ำลง

การวัดผลแบบเดียวกันในกลุ่มหุ้นจริง

คราวนี้ลองคำนวณแบบเดียวกันกับข้อมูลจริง ตารางด้านล่างสร้างจักรวาลหุ้นเริ่มต้นในเดือนมกราคมของแต่ละปีตั้งแต่ 2016 ถึง 2022 โดยเก็บเฉพาะสัญลักษณ์ที่มีราคาเปิดเดือนนั้นสูงกว่า $5 และมีปริมาณการซื้อขายเฉลี่ยต่อวันสูงกว่า 250,000 หุ้น จากนั้นติดตามหุ้นทุกตัวจนถึงราคาปิดล่าสุด หุ้นที่หยุดซื้อขายจะถูกคงไว้ที่ราคาปิดสุดท้ายแทนที่จะถูกตัดออก คอลัมน์ผู้รอดชีวิตจะเฉลี่ยเฉพาะสัญลักษณ์ที่ยังมีการซื้อขายอยู่ ส่วนคอลัมน์จักรวาลหุ้นเต็มรูปแบบจะเฉลี่ยหุ้นทุกตัวที่มีอยู่ในตอนเริ่มต้น

คำสั่งดึงข้อมูลส่วนต่างผลตอบแทนเฉลี่ยระหว่างกลุ่มที่ยังคงอยู่กับกลุ่มทั้งหมด จำแนกตามปีเริ่มต้น
SQL เบื้องหลังตัวเลขแต่ละตัว
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

สำหรับกลุ่มหุ้นปี 2016 มีหุ้น 968 จากทั้งหมด 2728 ตัวที่หายไปจากตลาด ณ เวลาที่จัดทำหน้านี้ การเฉลี่ยเฉพาะผู้รอดชีวิตให้ผลลัพธ์ 212% ในขณะที่การเฉลี่ยทั้งกลุ่มให้ผลลัพธ์ 149.3% คอลัมน์ส่วนต่างแสดงระยะห่างระหว่างค่าทั้งสอง คือ 62.7 จุดสำหรับกลุ่มปีนั้น และ 9.1 จุดสำหรับกลุ่มปี 2022 ซึ่งมีเวลาในการสูญเสียสมาชิกน้อยกว่า นี่คือผลตอบแทนรวมในช่วงเวลาที่มีความยาวต่างกัน ดังนั้นควรดูไล่ลงมาในคอลัมน์ส่วนต่างแทนการเปรียบเทียบระดับระหว่างแถว คำถามเรื่องจักรวาลหุ้นเดียวกันนี้เป็นพื้นฐานของ วิธีการวัดผลตอบแทนรายเดือน และการคำนวณค่าเฉลี่ยแบบถ่วงน้ำหนักเท่ากันทุกตัว

จุดจบของชื่อหุ้นที่หายไป

การถูกเพิกถอนไม่ได้หมายถึงการสูญเสียทั้งหมดเสมอไป ตารางด้านล่างใช้จักรวาลหุ้นเริ่มต้นในเดือนมกราคม 2019 และจัดกลุ่มตามสิ่งที่เกิดขึ้นหลังจากนั้น

คำสั่งดึงข้อมูลสถานะของหลักทรัพย์ในกลุ่มเดือนมกราคม 2019
SQL เบื้องหลังตัวเลขแต่ละตัว
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

กลุ่ม Still trading มีหุ้น 1954 ตัว ให้ผลตอบแทนเฉลี่ย 139.8% จากราคาเริ่มต้นในเดือนมกราคม 2019 โดยมี 72.1% ของหุ้นกลุ่มนี้ที่มีราคาสูงกว่าจุดเริ่มต้น ลองอ่านแถว 9 แล้วจะเห็นว่าผลลัพธ์ค่อนข้างหลากหลาย บริษัทที่ถูกซื้อกิจการด้วยราคาพรีเมียมอาจออกจากตลาดด้วยราคาปิดที่แข็งแกร่ง ในขณะที่บริษัทที่ค่อยๆ ซบเซาจนถูกเพิกถอนจะออกจากตลาดด้วยราคาที่อ่อนแอ ทั้งสองกรณีหายไปจากไฟล์ที่มีเฉพาะผู้รอดชีวิต หากใส่กลับเข้าไป ค่าเฉลี่ยก็จะเปลี่ยนไป

Survivorship bias เทียบกับ Look ahead bias

ทั้งสองเรื่องนี้มักถูกจัดอยู่ในหมวดหมู่เดียวกัน แต่สร้างความเสียหายต่อการทำ Backtest ในจุดที่ต่างกัน Look ahead bias เป็นปัญหาด้านเวลา: การทดสอบใช้ข้อเท็จจริงในช่วงเวลาที่ยังไม่มีใครทราบ เช่น ตัวเลขกำไรที่ปรับปรุงใหม่หรือการปรับเปลี่ยนที่เกิดขึ้นในหลายเดือนต่อมา ส่วน Survivorship bias เป็นปัญหาด้านสมาชิก: รายชื่อหุ้นที่ใช้ในการทดสอบคือรายชื่อที่มีอยู่ในปัจจุบัน ไม่ใช่รายชื่อที่มีอยู่ในขณะนั้น การทำ Backtest อาจแม่นยำเรื่องเวลาแต่ยังคงเทรดบนจักรวาลหุ้นที่รวบรวมด้วยความได้เปรียบของข้อมูลย้อนหลัง การปรับราคาหุ้นก็นำมาซึ่งกับดักที่เกี่ยวข้อง เนื่องจาก ประวัติราคาที่ปรับปรุงจากหุ้นแตกพาร์ จะเขียนทับระดับราคาของทุกแท่งก่อนวันแตกพาร์

วิธีแก้ไข Survivorship bias ในการทำ Backtest

  1. สร้างจักรวาลหุ้นใหม่ ณ แต่ละวันที่ทำการทดสอบ (Point in time): หมายความว่ารายชื่อหุ้นจะถูกสร้างขึ้นใหม่สำหรับวันที่ทำการทดสอบ โดยใช้บันทึกที่มีอยู่จริงในวันนั้น การทดสอบจะถือหุ้นที่ยังมีการซื้อขายอยู่ในขณะนั้น รวมถึงหุ้นที่หายไปในภายหลังด้วย
  2. จัดทำ Security master: Security master จะทำหน้าที่เชื่อมโยงตัวระบุ (Identifier) ตลอดช่วงเวลา ว่าสัญลักษณ์ใดชี้ไปยังบริษัทใดในช่วงวันที่เท่าใด และมีอะไรมาแทนที่หลังจากมีการควบรวมหรือเปลี่ยนชื่อ ไฟล์ราคาจะอ้างอิงตามสัญลักษณ์ แต่บริษัทไม่ได้อ้างอิงตามสัญลักษณ์เสมอไป
  3. ปิดสถานะหุ้นที่ถูกเพิกถอนที่ราคาจริงสุดท้าย: เมื่อหุ้นหายไปจากตลาด สถานะควรถูกปิดที่ราคาปิดสุดท้ายแทนที่จะหายไปจากค่าเฉลี่ย การลบข้อมูลออกคือสิ่งที่ทำให้เกิดช่องว่างในตารางด้านบน การทำ Backtest ที่ทำซ้ำได้ จะกำหนดกฎนี้ไว้ในโค้ดควบคู่ไปกับจักรวาลหุ้น
  4. อ่านเอกสารของผู้ให้บริการข้อมูลเกี่ยวกับสัญลักษณ์ที่ตายแล้ว: ผู้ให้บริการที่ให้ความสำคัญกับเรื่องนี้จะระบุไว้ชัดเจน Massive เปิดเผยสัญลักษณ์ที่ถูกเพิกถอนผ่านทาง Ticker endpoint พร้อมสถานะ active flag และบันทึกการเปลี่ยนสัญลักษณ์ การเพิกถอน การควบรวม และการเข้าซื้อกิจการไว้เป็นบันทึกใน Ticker events endpoint แยกต่างหาก
"เราไม่เปลี่ยนแปลงข้อมูลหรือนำการเปลี่ยนแปลง Ticker มาต่อกันเป็นชุดข้อมูลเดียว"
ฐานความรู้ Massive, "Massive จัดการกับการเปลี่ยน Ticker และการเข้าซื้อกิจการอย่างไร?", อ่านเมื่อสิงหาคม 2026

หากเอกสารของผู้ให้บริการไม่ได้ระบุถึงสัญลักษณ์ที่ถูกเพิกถอน ให้ถือว่าข้อมูลที่ดาวน์โหลดมามีเฉพาะผู้รอดชีวิตจนกว่าคุณจะตรวจสอบ ความครอบคลุมของข้อมูลมีความแตกต่างกันมากในแต่ละแหล่งข้อมูล: ดูบันทึกของเราเกี่ยวกับ API ข้อมูลหุ้นฟรี

กับดักการนำ Ticker กลับมาใช้ใหม่

สัญลักษณ์ต่างๆ จะถูกนำกลับมาหมุนเวียนใช้ใหม่หลังจากที่การจดทะเบียนเดิมสิ้นสุดลง ตารางด้านล่างนับจำนวนสัญลักษณ์ที่มีราคาปิดรายวันอย่างน้อย 200 วัน เงียบหายไปอย่างน้อย 250 วันปฏิทิน แล้วปรากฏขึ้นมาใหม่ในฐานะหุ้นจดทะเบียนใหม่ตั้งแต่ปี 2022 เป็นต้นไป

คำสั่งดึงข้อมูลสัญลักษณ์ที่ถูกนำกลับมาใช้ใหม่โดยผู้ออกหลักทรัพย์รายใหม่หลังเว้นช่วงไปนาน
SQL เบื้องหลังตัวเลขแต่ละตัว
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

ใน 2022 มีสัญลักษณ์ 16 ตัวที่กลับมาในลักษณะนี้ หลังจากเงียบหายไปเฉลี่ย 3247 วันปฏิทิน ช่วงเวลาที่เงียบหายไปนานที่สุดในแถวนั้นคือ 5734 วัน ไฟล์ราคาที่อ้างอิงตามสัญลักษณ์เพียงอย่างเดียวจะนำประวัติของบริษัทหนึ่งไปต่อกับอีกบริษัทหนึ่ง และการเชื่อมต่อดูราบรื่นตลอดทั้งชุด นี่คือกรณีที่ Security master ตรวจพบได้ แต่คอลัมน์ Ticker เปล่าๆ ไม่สามารถทำได้

หมายเหตุข้อมูลและคำจำกัดความ

สัญลักษณ์จะถูกนับว่ายังมีการซื้อขายอยู่เมื่อราคาปิดรายวันล่าสุดอยู่ในช่วง 45 วันปฏิทินนับจากวันที่สร้างหน้านี้ บัฟเฟอร์นี้ครอบคลุมความล่าช้าหนึ่งถึงสองวันในช่วงต้นของข้อมูลและหุ้นที่มีสภาพคล่องต่ำซึ่งอาจขาดช่วงการซื้อขาย

ผลตอบแทนคือผลตอบแทนรวมของราคาจากราคาปิดของเดือนมกราคมจนถึงราคาปิดล่าสุดที่มีสำหรับสัญลักษณ์นั้น โดยไม่มีเงินปันผลและไม่มีการปรับเป็นรายปี กลุ่มหุ้นรุ่นก่อนหน้ามีเวลาในการทบต้นนานกว่า ดังนั้นระดับผลตอบแทนจึงไม่สามารถเปรียบเทียบข้ามแถวได้ แต่ส่วนต่างระหว่างค่าเฉลี่ยทั้งสองนั้นเปรียบเทียบกันได้

จักรวาลหุ้นเริ่มต้นจะเก็บเฉพาะสัญลักษณ์ที่มีราคา $5 ขึ้นไป และมีปริมาณการซื้อขายเฉลี่ยต่อวัน 250,000 หุ้นขึ้นไปในช่วงเดือนที่เริ่มต้น ข้อมูลการซื้อขายรายวันประกอบด้วยกองทุน หน่วยลงทุน และใบสำคัญแสดงสิทธิควบคู่ไปกับบริษัทดำเนินงาน และไม่มีการแยกประเภทในที่นี้ ดังนั้นการปิดกองทุนจึงถูกนับรวมไปพร้อมกับการเพิกถอนบริษัท สัญลักษณ์ที่มีความขัดแย้งของตัวตนในฟีดข้อมูลของผู้ให้บริการจะถูกแยกออกด้วย SQL จากทุกตารางแทนที่จะถูกกรองออกในภายหลัง

คำถามที่พบบ่อย (FAQ)

Survivorship bias ในการทำ Backtest หุ้นคืออะไร?

คือความผิดพลาดที่เกิดขึ้นเมื่อจักรวาลหุ้นที่ใช้เทรดถูกสร้างขึ้นจากสัญลักษณ์ที่ยังคงมีอยู่ในปัจจุบัน บริษัทที่ถูกเพิกถอนหรือถูกซื้อกิจการจะไม่เคยเข้าสู่การทดสอบ ทำให้กลยุทธ์นั้นได้รับเฉพาะรายชื่อหุ้นที่รอดมาถึงปัจจุบันเท่านั้น

ฉันจะรู้ได้อย่างไรว่าชุดข้อมูลของฉันมี Survivorship bias?

ให้ดูจักรวาลหุ้นที่ไฟล์ของคุณอ้างว่าครอบคลุมในช่วงหลายปีก่อน แล้วตรวจสอบราคาปิดสุดท้ายของแต่ละสัญลักษณ์ หากทุกชื่อในรายการปี 2016 ยังคงมีการพิมพ์ราคาอยู่ในปัจจุบัน แสดงว่ารายการนั้นถูกสร้างจากรายชื่อหุ้นปัจจุบัน และหุ้นที่ตายไปแล้วไม่เคยอยู่ในนั้น

Survivorship bias ทำให้ผลการทำ Backtest ดูดีขึ้นเสมอไปหรือไม่?

ไม่เสมอไป ทิศทางขึ้นอยู่กับว่าหุ้นออกจากตลาดอย่างไร การถูกซื้อกิจการอาจปิดสถานะด้วยราคาพรีเมียม และการตัดหุ้นเหล่านั้นออกอาจทำให้ผลตอบแทนของกลุ่มต่ำกว่าความเป็นจริง ในขณะที่การค่อยๆ ซบเซาจนถูกเพิกถอนจะให้ผลตรงกันข้าม ตารางด้านบนวัดผลสุทธิของทั้งสองกรณีในกลุ่มหุ้นจริง

จักรวาลหุ้นแบบ Point in time คืออะไร?

คือรายชื่อสมาชิกที่ถูกสร้างขึ้นใหม่ ณ แต่ละวันที่ทำการทดสอบ โดยใช้เฉพาะบันทึกที่มีอยู่จริงในวันนั้น ประกอบด้วยชื่อหุ้นที่สามารถเทรดได้ในขณะนั้นและหายไปในภายหลัง และไม่รวมชื่อหุ้นที่ยังไม่ได้เข้าจดทะเบียน

Survivorship bias เหมือนกับ Look ahead bias หรือไม่?

ไม่เหมือนกัน Look ahead bias เกี่ยวกับเวลา: การทดสอบใช้ข้อมูลก่อนที่ใครจะทราบ ส่วน Survivorship bias เกี่ยวกับสมาชิก: การทดสอบเทรดบนรายชื่อหุ้นที่รวบรวมด้วยความได้เปรียบของข้อมูลย้อนหลัง การทำ Backtest อาจสะอาดในเรื่องหนึ่งแต่พังในอีกเรื่องหนึ่ง


ทุกตารางที่นี่มี SQL ที่ใช้สร้างข้อมูลกำกับอยู่ เปลี่ยนเดือนที่เริ่มต้นหรือปรับตัวกรองสภาพคล่อง แล้วรันบน Strasmore terminal เพื่อดูว่าส่วนต่างนั้นยังคงอยู่มากน้อยเพียงใดภายใต้กฎจักรวาลหุ้นของคุณเอง