อคติจากการมองไปข้างหน้า backtest
อคติจากการมองไปข้างหน้าคือข้อมูลอนาคตรั่วไหลเข้า backtest ดูว่าการตัดสินใจในแท่งเดียวกัน การอยู่รอด และตัวเลขที่แก้ไขทำให้ผลลัพธ์สูงเกินจริง และวิธีทดสอบ
อคติจากการมองไปข้างหน้าคือการใช้ข้อมูลภายใน backtest ที่ไม่มีใครสามารถมีได้ในขณะที่ทำการซื้อขายจำลอง เส้นกราฟผลตอบแทนพุ่งสูงขึ้น สถิติดูดีเยี่ยม และทั้งหมดนั้นใช้ไม่ได้ผลเมื่อต้องเผชิญกับคำสั่งซื้อขายจริง หน้านี้จะนิยามอคตินี้ อธิบายกลไกสามประการที่ก่อให้เกิดอคติ และวัดแต่ละกลไกด้วยข้อมูลตลาดจริง
อคติจากการมองไปข้างหน้าคืออะไร
backtest คือข้อกล่าวอ้างเกี่ยวกับการตัดสินใจ: ว่า ณ จุดเวลานี้ ด้วยข้อมูลนี้ กฎจะได้ผลลัพธ์เช่นนี้ ข้อกล่าวอ้างนี้จะเป็นจริงก็ต่อเมื่อทุกปัจจัยนำเข้าที่กฎใช้มีอยู่แล้ว ในรูปแบบสุดท้าย ก่อนถึงจุดเวลานั้น อคติจากการมองไปข้างหน้าคือการละเมิดเงื่อนไขนั้น และมันเกิดขึ้นอย่างเงียบๆ ไม่มีอะไรในสเปรดชีตหรือสคริปต์ที่จะร้องเรียนเมื่อแถวข้อมูลส่งคืนค่าที่ประทับเวลาช้ากว่าการตัดสินใจที่มันป้อนให้
การละเมิดสามประการครอบคลุมสิ่งที่พบเห็นได้ในทางปฏิบัติส่วนใหญ่ การตัดสินใจที่อ่านแท่งราคาที่มันก็ซื้อขายอยู่ภายในแท่งเดียวกัน จักรวาลหุ้นที่รวบรวมจากชื่อที่ยังคงจดทะเบียนอยู่ในปัจจุบัน ฟิลด์ข้อมูลที่ถูกแก้ไขหลังจากวันที่มันระบุไว้
กลไกที่หนึ่ง: การตัดสินใจบนแท่งราคาที่คุณซื้อขายอยู่ภายในแท่งนั้น
เริ่มต้นด้วยกฎที่ฟังดูไม่เป็นอันตราย ซื้อเมื่อมีแรงซื้อ นั่นคือในวันที่กองทุนปิดสูงกว่าราคาเปิด หากเขียนโค้ดอย่างไม่ระมัดระวัง การทดสอบจะเข้าซื้อที่ราคาเปิดของวันนั้นและขายที่ราคาปิดของวันนั้น ในขณะที่เงื่อนไขที่ใช้คัดกรองคือราคาปิดของแท่งราคาเดียวกัน ไม่มีใครรู้ราคาปิดเมื่อระฆังเปิดตลาด
แผงด้านล่างนี้รันทั้งสองเวอร์ชันบน SPY ซึ่งเป็นกองทุนติดตาม S&P 500 ตลอดระยะเวลาสิบปีปฏิทิน คอลัมน์แรกคือเวอร์ชันที่เป็นไปไม่ได้ คอลัมน์ที่สองเลื่อนเงื่อนไขที่เหมือนกันออกไปหนึ่งวันทำการ ซึ่งเป็นเวลาที่เร็วที่สุดที่บัญชีที่มีเงินทุนจะสามารถดำเนินการตามเงื่อนไขนั้นได้
SQL เบื้องหลังตัวเลขแต่ละตัว
WITH bars AS (
SELECT date,
toFloat64(open) AS o,
toFloat64(close) AS c,
toFloat64(close) > toFloat64(open) AS up_day
FROM global_markets.stocks_daily_aggs
WHERE ticker = 'SPY'
AND date >= toDate('2015-12-01')
AND date <= toDate('2025-12-31')
),
lagged AS (
SELECT date, o, c, up_day,
any(up_day) OVER (ORDER BY date ROWS BETWEEN 1 PRECEDING AND 1 PRECEDING) AS prior_up
FROM bars
)
SELECT toYear(date) AS year,
countIf(up_day) AS signal_days,
round(100 * avgIf(c / o - 1, up_day), 3) AS same_bar_avg_pct,
round(100 * avgIf(c / o - 1, prior_up), 3) AS lagged_avg_pct,
round(100 * (avgIf(c / o - 1, up_day) - avgIf(c / o - 1, prior_up)), 3) AS gap_pp
FROM lagged
WHERE date >= toDate('2016-01-01')
GROUP BY year
ORDER BY yearความแตกต่างนั้นไม่ใช่เรื่องเล็กน้อย ใน 2016 เวอร์ชันแท่งราคาเดียวกันมีค่าเฉลี่ย 0.447% ต่อวันที่ผ่านเกณฑ์ ในขณะที่เวอร์ชันที่เลื่อนออกไปมีค่าเฉลี่ย -0.008% ซึ่งเป็นส่วนต่าง 0.455 จุดเปอร์เซ็นต์ต่อวัน ใน 2025 ทั้งสองค่าอ่านได้ 0.636% และ -0.03% ด้วยจำนวนวันที่ผ่านเกณฑ์ประมาณ 140 วันในหนึ่งปี ส่วนต่างครึ่งจุดเปอร์เซ็นต์ต่อวันจะทบต้นเป็นเส้นกราฟผลตอบแทนที่ไม่มีบัญชีซื้อขายจริงใดเคยสร้างได้
ข้อได้เปรียบทั้งหมดเป็นไปตามนิยาม การเลือกวันที่ปิดสูงกว่าราคาเปิด แล้ววัดผลตอบแทนจากเปิดถึงปิดในวันเดียวกันนั้น เป็นการวัดปริมาณที่รับประกันว่าเป็นบวก กฎค้นพบตัวกรองของตัวเองแล้ว
รูปแบบต่างๆ ของสิ่งนี้ซ่อนอยู่ในข้อมูลรายวันตลอดเวลา: ค่าเฉลี่ยเคลื่อนที่ที่รวมราคาปิดของวันนี้และถูกใช้เพื่อซื้อขายในวันนี้ สัญญาณที่คำนวณจากราคาที่ปรับปรุงแล้วสำหรับการแตกพาร์ที่ยังไม่เกิดขึ้น จุดหยุดขาดทุนภายในวันที่วางไว้ที่จุดสูงสุดของวันที่ยังไม่เกิดขึ้น รูปแบบทั่วไปคือประทับเวลาการตัดสินใจที่อยู่ก่อนหน้าปัจจัยนำเข้าตัวใดตัวหนึ่งของมันเอง
กลไกที่สอง: จักรวาลหุ้นที่ดึงมาจากผู้รอดชีวิตในปัจจุบัน
กลไกที่สองไม่เคยแตะต้องกฎ มันอยู่ในรายชื่อที่กฎได้รับอนุญาตให้เห็น
ดึงรายชื่อหุ้นจากผู้ให้บริการข้อมูลปัจจุบัน ย้อนกลับไปสิบปี และกลุ่มตัวอย่างจะไม่รวมบริษัทที่ล้มละลาย ถูกซื้อกิจการ หรือถูกเพิกถอนหลักทรัพย์ระหว่างทางอย่างเงียบๆ กฎไม่เคยได้รับโอกาสให้เลือกหุ้นที่แพ้ แผงนี้วัดขนาดของการละเว้นนั้น: ทุกชื่อที่จดทะเบียนในสหรัฐฯ ซึ่งซื้อขายอย่างน้อย 200 ครั้งที่ราคาสูงกว่า 1 ดอลลาร์ในปีปฏิทินที่กำหนด สัดส่วนของกลุ่มนั้นที่ยังคงมีราคาในเดือนกรกฎาคม 2026 และผลตอบแทนเฉลี่ยมัธยฐานของทั้งกลุ่มในปีปฏิทิน เทียบกับค่ามัธยฐานของเฉพาะผู้รอดชีวิต
SQL เบื้องหลังตัวเลขแต่ละตัว
WITH recent AS (
SELECT DISTINCT ticker
FROM global_markets.stocks_daily_aggs
WHERE date >= toDate('2026-06-15')
AND date <= toDate('2026-07-28')
),
per_name AS (
SELECT toYear(date) AS year,
ticker,
argMin(toFloat64(close), date) AS first_close,
argMax(toFloat64(close), date) AS last_close,
count() AS sessions
FROM global_markets.stocks_daily_aggs
WHERE date >= toDate('2015-01-01')
AND date <= toDate('2024-12-31')
AND close > 1
GROUP BY year, ticker
HAVING sessions >= 200
)
SELECT year,
uniqExact(ticker) AS names_trading,
round(100 * uniqExactIf(ticker, ticker IN (SELECT ticker FROM recent)) / uniqExact(ticker), 1) AS still_listed_pct,
round(100 * quantileDeterministic(0.5)(last_close / first_close - 1, cityHash64(ticker)), 2) AS median_return_all_pct,
round(100 * quantileDeterministicIf(0.5)(last_close / first_close - 1, cityHash64(ticker), ticker IN (SELECT ticker FROM recent)), 2) AS median_return_survivors_pct,
round(100 * (quantileDeterministicIf(0.5)(last_close / first_close - 1, cityHash64(ticker), ticker IN (SELECT ticker FROM recent))
- quantileDeterministic(0.5)(last_close / first_close - 1, cityHash64(ticker))), 2) AS survivor_gap_pp
FROM per_name
GROUP BY year
ORDER BY yearจาก 7063 ชื่อที่ผ่านเกณฑ์ใน 2015 มี 52.5% ตัวที่ยังคงซื้อขายในเดือนกรกฎาคม 2026 สำหรับกลุ่ม 2024 ตัวเลขคือ 87.5% ซึ่งส่วนใหญ่เป็นเรื่องของเวลาที่ผ่านไป: ชื่อในปี 2024 มีเวลาสองปีที่จะหายไป และชื่อในปี 2015 มีเวลาสิบเอ็ดปี
คอลัมน์ผลตอบแทนมีอคติอยู่ในตัว ใน 2017 ชื่อโดยเฉลี่ยมัธยฐานให้ผลตอบแทน 7.51% ในขณะที่ผู้รอดชีวิตโดยเฉลี่ยมัธยฐานให้ผลตอบแทน 11.32% ซึ่งเป็นช่องว่าง 3.81 จุดเปอร์เซ็นต์ที่ได้รับมาโดยไม่ต้องลงทุน ช่องว่างนี้เป็นบวกในทุกๆ ปีจาก 10 ปีที่แสดง กลยุทธ์ที่ทดสอบกับผู้รอดชีวิตได้รับอคตินี้ก่อนที่จะตัดสินใจใดๆ และกฎที่คัดกรองหุ้นอ่อนแอได้รับมากกว่าค่ามัธยฐาน เนื่องจากชื่อที่หายไปนั้นอยู่ในกลุ่มนั้นอย่างไม่สมส่วน
การแก้ไขคือจักรวาลหุ้น ณ จุดเวลา: รายชื่อ ณ วันที่ปรับสมดุลแต่ละครั้ง รวมถึงหุ้นที่ถูกเพิกถอน โดยตำแหน่งที่ถูกเพิกถอนจะถูกปิดที่ราคาสุดท้าย แทนที่จะถูกลบออกจากบันทึก
กลไกที่สาม: ข้อมูลที่ถูกแก้ไขหลังจากประทับวันที่
ราคาจะถูกประทับครั้งเดียว ข้อมูลพื้นฐานไม่เป็นเช่นนั้น ตัวเลขรายไตรมาสมีวันที่สิ้นสุดไตรมาส ถูกแก้ไขโดยการยื่นเอกสารในภายหลัง และฐานข้อมูลที่เก็บเฉพาะเวอร์ชันปัจจุบันจะให้บริการตัวเลขที่แก้ไขแล้วกับวันที่เดิมอย่างเต็มใจ กฎที่คัดกรองด้วยตัวเลขนั้นกำลังอ่านการแก้ไขที่ต้องใช้เวลาหลายเดือนกว่าจะมาถึง
รูปแบบเดียวกันนี้เกิดขึ้นในรายชื่อสมาชิกดัชนีที่สร้างใหม่เป็นสมาชิกปัจจุบัน ประวัติการประมาณการของนักวิเคราะห์ที่ถูกเขียนทับด้วยฉันทามติสุดท้าย และการปรับปรุงการดำเนินการของบริษัทที่นำไปใช้ย้อนหลังกับชุดราคาทั้งหมด ในแต่ละกรณี บันทึกที่เก็บไว้คือภาพรวม ณ ปัจจุบันที่สวมวันที่ในอดีต
แนวปฏิบัติสองประการสามารถควบคุมมันได้ ใช้แหล่งข้อมูลที่เก็บรุ่นต่างๆ หมายถึงค่าที่ทราบในแต่ละวัน แทนที่จะเป็นค่าที่ทราบในปัจจุบัน เมื่อไม่มีรุ่นต่างๆ ให้ใช้ ให้ใช้ระยะเวลาล่าช้าของการรายงานที่มากพอที่จะครอบคลุมระยะเวลาจริง และปฏิบัติต่อความยาวของระยะเวลาล่าช้านั้นเป็นพารามิเตอร์ที่ควรทดสอบ แทนที่จะเป็นรายละเอียดที่ควรเดา
เส้นกราฟผลตอบแทนมีลักษณะอย่างไรเมื่อข้อมูลในอนาคตรั่วไหลเข้ามา
อคติจากการมองไปข้างหน้ามีลายเซ็น: ผลการดำเนินงานที่สูงกว่าที่ตลาดอ้างอิงเสนอไว้มาก จุดอ้างอิงที่เป็นประโยชน์คือเพดานการมองย้อนหลัง ซึ่งเป็นการเติบโตที่การคาดการณ์ล่วงหน้าหนึ่งวันที่สมบูรณ์แบบจะสร้างขึ้นได้จากเครื่องมือเดียว
SQL เบื้องหลังตัวเลขแต่ละตัว
WITH bars AS (
SELECT date,
toFloat64(close) AS c,
any(toFloat64(close)) OVER (ORDER BY date ROWS BETWEEN 1 PRECEDING AND 1 PRECEDING) AS prev_c
FROM global_markets.stocks_daily_aggs
WHERE ticker = 'SPY'
AND date >= toDate('2015-12-01')
AND date <= toDate('2025-12-31')
),
rets AS (
SELECT toYear(date) AS year,
c / prev_c - 1 AS r
FROM bars
WHERE date >= toDate('2016-01-01')
AND prev_c > 0
)
SELECT year,
count() AS sessions,
round(100 * (exp(sum(log(1 + r))) - 1), 1) AS buy_and_hold_pct,
round(100 * (exp(sumIf(log(1 + r), r < 0.02)) - 1), 1) AS without_big_up_days_pct,
round(exp(sum(log(1 + abs(r)))), 1) AS hindsight_ceiling_multiple
FROM rets
GROUP BY year
ORDER BY yearเพดานนั้นมหาศาล ใน 2020 การมองเห็นล่วงหน้าหนึ่งวันที่สมบูรณ์แบบบน SPY ทำให้เงินทุนเพิ่มขึ้น 27.7 เท่าในช่วง 253 ครั้ง เทียบกับ 16.2% สำหรับการถือครองกองทุนเพียงอย่างเดียว ในปีที่สงบที่สุดในรอบสิบปี 2017 การคาดการณ์ที่สมบูรณ์แบบเดียวกันให้ผลตอบแทน 2.2 เท่าของเงินทุน
คอลัมน์กลางแสดงให้เห็นว่าเพดานนั้นกระจุกตัวแค่ไหน ตัดวันที่เพิ่มขึ้นมากกว่า 2% ออก 2020 เปลี่ยนจาก 16.2% เป็น -44.9% การรั่วไหลที่มีค่าเพียงไม่กี่วันเหล่านั้นต่อปีจะทำให้ backtest เปลี่ยนไปไกลมาก เส้นกราฟที่มีการลดลงตื้น อัตราการชนะสูง และวันที่ดีที่สุดกระจุกตัวอยู่ในวันที่ตลาดผันผวนรุนแรงที่สุด สมควรได้รับการตรวจสอบ
การปรับเทียบมีความสำคัญมากกว่าเกณฑ์ใดเกณฑ์หนึ่ง กฎที่อ้างว่าผลตอบแทน 40% ต่อปีอย่างราบรื่นเป็นเวลาหนึ่งทศวรรษ กำลังอ้างสัดส่วนขนาดใหญ่ของเพดานการมองย้อนหลัง และคำถามที่ซื่อสัตย์คือปัจจัยนำเข้าตัวใดที่บอกกฎว่ากำลังจะเกิดอะไรขึ้น
วิธีทดสอบ backtest ว่ามีอคติจากการมองไปข้างหน้าหรือไม่
- เลื่อนทุกอย่างออกไปหนึ่งแท่ง เลื่อนสัญญาณทุกตัวไปอีกหนึ่งช่วงเวลาเต็มแล้วรันใหม่ ขอบที่แท้จริงจะลดลงเล็กน้อย การรั่วไหลจะพังทลาย มักจะเหลือศูนย์
- รบกวนอนาคต นำข้อมูลหลังจากประทับเวลาการตัดสินใจแต่ละครั้ง แทนที่ด้วยสัญญาณรบกวนหรือการสับเปลี่ยน แล้วรันตรรกะการตัดสินใจใหม่ ทุกการตัดสินใจควรออกมาเหมือนเดิม การตัดสินใจใดๆ ที่เปลี่ยนแปลงแสดงว่าได้อ่านสิ่งที่มันไม่สามารถรู้ได้
- ปฏิบัติต่อประทับเวลาเป็นข้อมูล สำหรับปัจจัยนำเข้าแต่ละตัว ให้เก็บช่วงเวลาที่ค่าพร้อมใช้งานไว้ข้างช่วงเวลาที่ค่าอธิบาย การเชื่อมต่อที่ไม่เคยเปรียบเทียบสองคอลัมน์นั้นไม่สามารถบังคับใช้กฎได้
- สร้างจักรวาลหุ้นขึ้นใหม่ ณ วันที่นั้น รวมชื่อที่ถูกเพิกถอนและถูกซื้อกิจการ ปิดที่ราคาสุดท้าย และรันกฎเดียวกันบนจักรวาลหุ้นทั้งสองเพื่อวัดขนาดของผลกระทบจากการอยู่รอด
- วัดเทียบกับเพดาน นำผลตอบแทนของกลยุทธ์ไปเทียบกับตัวเลขการถือครองและซื้อ และตัวเลขการมองเห็นล่วงหน้าที่สมบูรณ์แบบในช่วงเวลาเดียวกัน ระยะห่างจากเพดานคือบริบทที่อัตราส่วน Sharpe เพียงอย่างเดียวไม่ได้ให้
การกำหนดขนาดสถานะอยู่ downstream ของทั้งหมดนี้ อัตราการชนะที่สูงเกินจริงจะนำไปสู่ขนาดการเดิมพันที่สูงเกินจริง ซึ่งเป็นจุดที่ การกำหนดขนาดสถานะตามเกณฑ์ Kelly เปลี่ยนข้อผิดพลาดทางบัญชีให้กลายเป็นการลดลง ระเบียบวินัยเดียวกันนี้ใช้ก่อนที่เงินจริงจะเคลื่อนย้าย: การเทรดกระดาษก่อนใช้เงินจริง ทดสอบกลไกการสั่งซื้อ และระบบอัตโนมัติมีอันตรายเหมือนกันที่ความเร็วของเครื่องจักร ซึ่งครอบคลุมใน ระบบเทรด AI แบบหลายเอเจนต์ ผลกระทบที่รอดชีวิตจากการทดสอบ ณ จุดเวลาอย่างระมัดระวัง เช่น ความผิดปกติของความผันผวนต่ำ น่าสนใจด้วยเหตุผลนั้น
คำถามที่พบบ่อย
อคติจากการมองไปข้างหน้าใน backtesting คืออะไร?
คือการใช้ข้อมูลในการซื้อขายจำลองที่ไม่มีให้ใช้ในขณะที่การซื้อขายนั้นจะถูกวางไว้ รูปแบบทั่วไปคือการตัดสินใจบนแท่งราคาที่การซื้อขายก็ดำเนินการอยู่ภายในแท่งนั้น การคัดกรองจักรวาลของบริษัทที่ยังคงจดทะเบียนอยู่ในปัจจุบัน และการอ่านตัวเลขทางการเงินที่ถูกแก้ไขหลังจากวันที่แนบมากับตัวเลขนั้น
คุณจะตรวจจับอคติจากการมองไปข้างหน้าได้อย่างไร?
เลื่อนสัญญาณทุกตัวออกไปหนึ่งช่วงเวลาเต็มแล้วรันใหม่: การรั่วไหลมักจะพังทลาย ในขณะที่ขอบที่แท้จริงจะลดลงอย่างนุ่มนวล การทดสอบที่แข็งแกร่งกว่าคือการแทนที่ข้อมูลทั้งหมดหลังจากประทับเวลาการตัดสินใจแต่ละครั้งด้วยสัญญาณรบกวน และยืนยันว่าทุกการตัดสินใจออกมาไม่เปลี่ยนแปลง
อคติจากการอยู่รอด (survivorship bias) เหมือนกับอคติจากการมองไปข้างหน้าหรือไม่?
ทั้งสองอย่างแตกต่างกันแต่มักมาด้วยกัน อคติจากการอยู่รอดคือกลุ่มตัวอย่างที่สร้างจากชื่อที่อยู่รอด และการรู้ว่าชื่อใดอยู่รอดนั้นเป็นข้อมูลจากอนาคต ดังนั้นจักรวาลหุ้นเฉพาะผู้รอดชีวิตคือการรั่วไหลจากการมองไปข้างหน้าที่อยู่ในข้อมูล ไม่ใช่ในกฎ
ทำไม backtest ที่มีอคติจากการมองไปข้างหน้าถึงดูดีนัก?
การรั่วไหลเลือกผลลัพธ์แทนที่จะทำนายผลลัพธ์ ในแผงด้านบน การคัดกรองวันด้วยราคาปิดของตัวเองให้ผลตอบแทนเฉลี่ยต่อวันสูงกว่าเวอร์ชันที่เลื่อนออกไปประมาณ 0.455 จุดเปอร์เซ็นต์ใน 2016 ซึ่งเป็นข้อได้เปรียบตามนิยาม ไม่ใช่ข้อได้เปรียบที่ได้มาจากการกระทำ
ข้อมูล ณ จุดเวลา (point-in-time data) คืออะไร?
ข้อมูล ณ จุดเวลาเก็บค่าแต่ละค่าตามที่ทราบในวันที่ในประวัติศาสตร์แต่ละวัน รวมถึงตัวเลขดั้งเดิมและการแก้ไขในภายหลังทุกครั้ง พร้อมกับจักรวาลหลักทรัพย์ ณ วันนั้น เป็นข้อมูลอ้างอิงที่ backtest จำเป็นต้องใช้เพื่อแสดงว่าปัจจัยนำเข้าของมันมีอยู่ก่อนการตัดสินใจ
ทุกตัวเลขข้างต้นเป็นคำสั่งค้นหาที่ถูกจัดเก็บและมีเวอร์ชันเหนือแท่งรายวันจริง ขยาย SQL ของแผงใดๆ หรือรันการทดสอบการเลื่อนเวลาด้วยกฎของคุณเองบนเทอร์มินัล Strasmore