← กลับไปยังบทความ
July 1, 2026
อ่าน 5 นาที

Look-Ahead Bias: ความผิดพลาดเพียงหนึ่งแท่งเทียนสร้าง Sharpe 15 จาก Noise ล้วน ๆ ได้อย่างไร

Look-Ahead Bias: ความผิดพลาดเพียงหนึ่งแท่งเทียนสร้าง Sharpe 15 จาก Noise ล้วน ๆ ได้อย่างไร
#algotrading
#backtest
#look-ahead bias
#การรั่วไหลของข้อมูล
#overfitting
#validation

เป็นส่วนหนึ่งของซีรีส์ "Backtests Without Illusions"

📄 บทความนี้ขยายกลายเป็นเปเปอร์วิจัย การรั่วไหลแบบ look-ahead ที่แนบเนียนสามแบบถูกนำมาทดสอบอย่างมีการควบคุมเทียบกับ ground truth ที่รู้ค่าจริง (ประวัติศาสตร์จำลอง 4,000 ชุด) อ่านเปเปอร์ฉบับเต็มออนไลน์ (เวอร์ชันอินเทอร์แอกทีฟ + PDF) ได้ที่ lookahead.marketmaker.cc โค้ดและข้อมูลอยู่ที่ github.com/suenot/lookahead-inflation

ไม่กี่สัปดาห์ก่อน benchmark การค้นหาพารามิเตอร์ของเรากำลังโกหกเรา และเราเกือบไม่ทันสังเกต

เอนจิ้นดูสะอาดหมดจด logic แบบ closed-bar, การแบ่ง rolling walk-forward ที่ซื่อสัตย์, การค้นหาแบบ Sobol/QMC บน parameter space, และ test window ที่กันไว้ต่างหาก การค้นหาพบ configuration ที่ดูดีบน in-sample ปัญหาเดียวคือ: บน out-of-sample แทบทุกอย่างติดลบ เราสันนิษฐานว่ากลยุทธ์อ่อนแอเฉย ๆ

จากนั้นเราก็เจอบรรทัดหนึ่ง สัญญาณถูกตัดสินใจที่ close ของแท่ง i แต่ fill กลับถูกบันทึกบนแท่ง i เดียวกัน แทนที่จะเป็น open ของแท่งถัดไป ความผิดพลาด off-by-one หนึ่งจุดใน execution index เราย้าย fill ไปที่ open[i+1] — ราคาเดียวที่คุณจะซื้อขายได้จริงหลังจากเห็น close ของแท่ง i — แล้วผลลัพธ์ out-of-sample ก็ พลิกเครื่องหมาย การค้นหาแบบ Sobol เปลี่ยนจากขาดทุนเป็นกำไร ไม่มีอะไรเกี่ยวกับกลยุทธ์เปลี่ยนแปลงเลยแม้แต่น้อย เราแค่หยุดเทรดในอดีตเท่านั้นเอง

นั่นคือ look-ahead bias และส่วนที่น่ากังวลคือความผิดพลาดนั้น เล็ก แค่ไหน แต่ผลลัพธ์กลับ ใหญ่ ขนาดไหน บทความนี้คือการตรวจสอบตัวเองแบบมีการควบคุม: เราสร้างตัวจำลองที่รู้ ground truth อยู่แล้วโดยการออกแบบ ฉีดการรั่วไหลที่แนบเนียนทีละแบบ และวัดว่าแต่ละแบบเพิ่มผลลัพธ์ backtest มากแค่ไหนอย่างแม่นยำ ประเด็นหลัก: แม้ ไม่มี edge จริงเลยแม้แต่น้อย การ fill บนแท่งเดียวกันก็สร้าง Sharpe ต่อปีที่ +14.8 จาก noise ล้วน ๆ ได้

Look-Ahead Bias คืออะไรกันแน่

สามจุดที่ look-ahead ซ่อนตัว — execution, normalization, และ indicator — ในฐานะช่องทางที่มีอันตรายไม่เท่ากันซึ่งป้อนเข้าสู่การตัดสินใจเทรดเดียวกัน

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

  1. Execution — คุณตัดสินใจบนแท่ง i และ fill บนแท่ง i (หรือใช้ high/low ของแท่ง i สำหรับ stop บนแท่งเดียวกับที่สร้างสัญญาณ) คุณซื้อขายที่ราคาซึ่งมีความสัมพันธ์กับสิ่งที่กระตุ้นให้คุณเทรด
  2. Normalization — คุณทำ z-score, min-max หรือ scale feature ด้วยสถิติที่คำนวณจาก ทั้งซีรีส์ รวมถึงอนาคตด้วย scaler "รู้" test set
  3. Indicators / features — คุณ smooth หรือ filter ด้วย window ที่อยู่ตรงกลาง (หรือแอบมองไปข้างหน้าด้วยวิธีอื่น) ทำให้ค่าที่แท่ง i มีชิ้นส่วนของแท่ง i+1 ปนอยู่แล้ว

ทั้งสามแบบเป็นรูปแบบของสิ่งที่วรรณกรรม machine learning เรียกว่า leakage: การปนเปื้อนของ training/evaluation ด้วยข้อมูลจากอนาคตของ target (Kaufman et al., 2012; Kapoor & Narayanan, 2023) ในโลกการเงิน ตำราหลักคือ Advances in Financial Machine Learning ของ López de Prado (2018) — purged cross-validation, embargoing, อันตรายของการ backtest วินัยแบบ point-in-time นั้นย้อนกลับไปได้อย่างน้อยถึง Fama & French (1992) ที่จงใจ lag ข้อมูลบัญชีไว้หกเดือนเพื่อให้ตัวแปรเป็นที่รู้ก่อนที่ผลตอบแทนซึ่งมันอธิบายจะเกิดขึ้น

คำถามที่บทความนี้ตอบคือคำถามเชิง ปริมาณ: ไม่ใช่ "leakage เลวร้ายหรือไม่" (ทุกคนเห็นตรงกันอยู่แล้ว) แต่คือ "แต่ละรูปแบบให้ Sharpe point เพิ่มขึ้นกี่จุด และแบบไหนอันตรายกว่ากัน?" หากไม่มีตัวเลข คุณก็ไม่สามารถให้เหตุผลกับมันได้ คุณบอกไม่ได้ว่าการเพิ่มขึ้น +0.3 เป็นแค่ noise หรือการเพิ่มขึ้น +14 คือหลักฐานมัดตัว

ตัวจำลองที่รู้ ground truth

ตลาดสังเคราะห์แบบควบคุมที่มี edge dial ที่รู้ค่า: โลกที่ไม่มี edge จริงเคียงข้างโลกที่มี edge ซึ่ง equity เพิ่มขึ้นจริง

การจะวัดการเพิ่มขึ้น (inflation) ได้ คุณต้องรู้ความจริงก่อน ข้อมูลจริงไม่เคยบอกความจริงกับคุณ — มันให้แค่ realization เดียวและไม่มี oracle ดังนั้นเราจึงสร้างตลาดสังเคราะห์ที่ เรา เป็นผู้กำหนด edge เอง

กระบวนการสร้างข้อมูล (data-generating process) เป็นแบบ causal อย่างเคร่งครัดและไม่ explosive:

gt=ϕgt1+1ϕ2  ut,utN(0,1)g_t = \phi\, g_{t-1} + \sqrt{1-\phi^2}\; u_t, \qquad u_t \sim \mathcal{N}(0,1)

rt=agt1+σεt,εtN(0,1)r_t = a\, g_{t-1} + \sigma\, \varepsilon_t, \qquad \varepsilon_t \sim \mathcal{N}(0,1)

ในที่นี้ gtg_t คือ latent drift ที่คงอยู่ต่อเนื่องแบบ exogenous (เป็น AR(1) ที่มี ϕ=0.95\phi = 0.95) และผลตอบแทนของแท่ง rtr_t มี drift เล็ก ๆ คือ agt1a\,g_{t-1} ซึ่ง รู้ล่วงหน้าหนึ่งแท่ง เนื่องจาก gg ไม่ขึ้นกับผลตอบแทนในอดีต จึงไม่มี feedback และไม่มีอะไร explode พารามิเตอร์ aa คือ dial ที่กำหนดว่ามี edge จริงมากแค่ไหน:

  • a=0a = 0null: ไม่มี edge เลยแม้แต่น้อย backtest Sharpe ที่เป็นบวกใด ๆ ล้วนเป็น artifact 100%
  • a>0a > 0edge จริงที่เทรดได้: กฎ momentum แบบซื่อสัตย์ทำกำไรได้จริง

กลยุทธ์นี้เรียบง่ายโดยตั้งใจ — กฎ momentum แบบ sign ฟีเจอร์คือผลรวมของผลตอบแทนย้อนหลัง LL แท่ง (L=24L = 24 แท่ง) และ position คือเครื่องหมาย (sign) ของมัน:

csum = np.concatenate(([0.0], np.cumsum(r)))      # csum[k] = sum r[0..k-1]
mom = np.full(n, np.nan)
tt = np.arange(L - 1, n)
mom[tt] = csum[tt + 1] - csum[tt - L + 1]

signal = np.sign(mom)                              # position for the next bar

ฟีเจอร์ momentum นี้เป็นเครื่องมือที่สมบูรณ์แบบสำหรับศึกษาการรั่วไหลแบบ same-bar เพราะมันมีคุณสมบัติที่ indicator จริงหลายตัวมีเหมือนกัน: มันมีแท่งปัจจุบันปนอยู่โดยกลไก mom[t] มี r[t] รวมอยู่ด้วย ดังนั้นถ้าคุณบันทึก r[t] เป็นการเทรดของคุณ คุณก็กำลังเดิมพันบางส่วนกับปริมาณที่ อยู่ในสัญญาณของคุณเองอยู่แล้ว นั่นคือการรั่วไหล ในรูปแบบที่จับต้องได้

การตั้งค่า: σ=0.01\sigma = 0.01 (volatility ต่อแท่ง 1%), ค่าธรรมเนียมทางเดียว 0.00045 (round-trip 0.09% ตรงกับเอนจิ้นของเรา), Sharpe ปรับเป็นรายปีด้วย 8760\sqrt{8760} (แท่งรายชั่วโมง), ประวัติศาสตร์อิสระ 4,000 ชุด ชุดละ 4,000 แท่ง ทุกอย่างถูก seed และเป็น deterministic

Pipeline ที่ซื่อสัตย์ (ตัวเดียวที่เทรดได้จริง)

ตัดสินใจที่ close ของแท่ง t รับผลตอบแทนของแท่ง ถัดไป และจ่ายค่าธรรมเนียมเมื่อ position เปลี่ยน:

def sharpe(sig, ret_booked):
    dpos = np.abs(np.diff(np.concatenate(([0.0], sig))))
    pnl  = sig * ret_booked - FEE_ONEWAY * dpos
    return pnl.mean() / pnl.std() * np.sqrt(8760)

honest = sharpe(signal[idx], r[idx + 1])           # earn r[t+1]: tradable

การรั่วไหลสามแบบ แต่ละแบบคือการเปลี่ยนแปลงเล็ก ๆ ที่แม่นยำ

same_bar  = sharpe(signal[idx], r[idx])

z_full    = (mom - mom[valid].mean()) / mom[valid].std()
norm_full = sharpe(np.sign(z_full[idx]), r[idx + 1])

z_sm      = (mom[:-2] + mom[1:-1] + mom[2:]) / 3.0   # uses t-1, t, t+1
indicator = sharpe(np.sign(z_sm[idx]), r[idx + 1])

การรั่วไหลแต่ละแบบห่างจาก pipeline ที่ซื่อสัตย์แค่บรรทัดเดียว นั่นคือประเด็นทั้งหมด: มันไม่ใช่ความผิดพลาดแปลกประหลาด แต่เป็นสิ่งที่ผ่าน review ไปได้

ผลลัพธ์: ขนาดของการรั่วไหลแต่ละแบบ

Noise ตลาดล้วน ๆ ที่ถูกส่งผ่านการรั่วไหลแบบ same-bar จนกลายเป็น equity curve ปลอมที่พุ่งทะยานและมาตรวัดประสิทธิภาพที่ค้างอยู่ใกล้ค่าสูงสุด

จากการรันครอบคลุม 4,000 seed นี่คือ Sharpe ต่อปีที่แต่ละ pipeline รายงาน ทั้งภายใต้ null (ไม่มี edge) และภายใต้ edge จริง (a=0.0011a = 0.0011 ปรับให้ Sharpe ที่ซื่อสัตย์อยู่ที่ +1.57 ซึ่งน่าเชื่อถือ):

Pipeline Null (ไม่มี edge) Edge จริง
Honest (ความจริงแท้) −0.74 +1.57
Same-bar fill +14.79 +15.85
Indicator peek (1 แท่ง) +4.76 +6.62
Whole-series normalization −0.84 +1.46

ช่วงความเชื่อมั่น 95% ข้าม seed อยู่ที่ ±0.05 หรือแคบกว่านั้นในทุกช่อง; paired t-test บนค่าการเพิ่มขึ้นมีนัยสำคัญอย่างมหาศาลในจุดที่ effect เป็นจริง (t > 400, p ≈ 0)

อ่าน คอลัมน์ null ก่อน เพราะมันคือการทดลองที่สะอาดที่สุดเท่าที่จะเป็นไปได้: ไม่มี edge เลย ดังนั้น pipeline ที่ซื่อสัตย์จึงขาดทุนอย่างถูกต้อง (−0.74 คือแรงฉุดจากการจ่ายค่าธรรมเนียมเพื่อเทรด noise) ทีนี้มาดูว่าการรั่วไหลทำอะไรกับความว่างเปล่าเดียวกันนี้:

  • Same-bar fill: −0.74 → +14.79 กลยุทธ์ที่ไม่มีพลังพยากรณ์เลยแม้แต่น้อย เทรด noise แบบสุ่ม กลับรายงาน Sharpe ต่อปีเกือบ 15 นี่ไม่ใช่ bias ที่แนบเนียน แต่คือการปั้นแต่งขึ้นมา กลไกก็คือสิ่งที่เราสร้างไว้ตั้งแต่แรก: ฟีเจอร์ momentum มี r[t] ปนอยู่ ดังนั้นการบันทึก r[t] จึงเท่ากับการเดิมพันกับสัญญาณของตัวเอง
  • Indicator peek: −0.74 → +4.76 การปล่อยให้ smoother มองเห็นล่วงหน้าไปหนึ่งแท่งสร้าง Sharpe ใกล้ 5 ขึ้นมาจาก noise เพราะค่าที่ผ่านการ smooth แล้วที่ t ตอนนี้มีความสัมพันธ์กับ r[t+1] ที่คุณกำลังจะได้รับ
  • Whole-series normalization: −0.74 → −0.84 แทบไม่มีการเพิ่มขึ้นเลย นี่คือข้อค้นพบที่ซื่อสัตย์และไม่คาดคิด (รายละเอียดเพิ่มเติมด้านล่าง)

คอลัมน์ edge ส่งสารที่อันตรายกว่านั้น เมื่อมี edge จริงอยู่ (honest +1.57) การรั่วไหลไม่ได้แค่บวกค่าคงที่เข้าไปเท่านั้น — มันดัน Sharpe ที่วัดได้ ขึ้นไปถึง +15.85 และ +6.62 ซึ่งสูงกว่า +1.57 ที่คุณเทรดได้จริงมาก ดังนั้นตัวเลขที่วัดได้ แยกความสามารถออกจากการรั่วไหลไม่ได้ ตัวเลข +6 ที่มาจากการรั่วไหลกับ +6 ที่ซื่อสัตย์ดูเหมือนกันทุกประการบนรายงาน คุณจะรู้ว่าได้ตัวไหนมาก็ต่อเมื่อคุณ deploy เงินทุนไปแล้วเท่านั้น

การรั่วไหลคือ gradient ไม่ใช่สวิตช์

การรั่วไหลแบบ same-bar ในฐานะ dose-response ที่ราบเรียบ: การจับสัดส่วนที่มากขึ้นของแท่งสัญญาณยก equity curve ขึ้นอย่างต่อเนื่องข้ามเกณฑ์ที่ deploy ได้

ข้อโต้แย้งที่เป็นธรรมชาติ: "การบันทึกแท่งสัญญาณ ทั้งแท่ง เป็นความผิดพลาดสุดโต่งที่ไม่สมจริง" ดังนั้นเราจึงกวาด dose — สัดส่วน ff ของแท่งสัญญาณที่ถูกจับโดยการรั่วไหล ตั้งแต่ 0 (ซื่อสัตย์) จนถึง 1 (same-bar เต็มรูปแบบ):

สัดส่วนที่จับได้ ff Sharpe (Null) Sharpe (Edge)
0.00 (ซื่อสัตย์) −0.74 +1.57
0.25 +3.90 +6.41
0.50 +9.86 +12.20
1.00 (รั่วไหลเต็มรูปแบบ) +14.79 +15.85

การจับเพียง หนึ่งในสี่ ของแท่งสัญญาณก็พาให้กลยุทธ์ที่ไม่มี edge เปลี่ยนจาก −0.74 ไปเป็น +3.90 ได้แล้ว คุณไม่จำเป็นต้องมี off-by-one เต็มรูปแบบเพื่อถูกหลอก แค่ fill ที่เอื้อประโยชน์เกินไปเล็กน้อย — slippage ที่มองโลกในแง่ดีนิดหน่อยบนแท่งสัญญาณ, stop ภายในแท่งที่ตรวจสอบกับแท่งที่กระตุ้นมันเอง — ก็เพียงพอที่จะผ่านเกณฑ์ "deployable" ส่วนใหญ่ไปได้ การเพิ่มขึ้นนี้ราบเรียบและเป็น monotone ตามสัดส่วนของปัจจุบันที่คุณปล่อยให้ตัวเองเทรด

สิ่งนี้ทำให้กลยุทธ์ที่ขาดทุนเข้าสู่ production บ่อยแค่ไหน?

ตัวเลขที่ควรทำให้ practitioner กังวลคือ false-deployment rate: การรั่วไหลทำให้ configuration ที่ขาดทุนจริง ๆ ผ่านเกณฑ์ที่คุณจะใช้ไฟเขียวให้มันบ่อยแค่ไหน โดยใช้ "Sharpe ต่อปี ≥ 1.0" เป็นเกณฑ์การ deploy ภายใต้ null:

  • Same-bar fill: 68% ของกลยุทธ์ที่ไม่มี edge ดูเหมือน deploy ได้ และ ขาดทุนจริง สอง configuration จากสามที่เป็น pure noise ล้วน ๆ จะผ่าน gate ที่ Sharpe-≥-1 ได้และขาดทุนเมื่อเทรดจริง (rate นี้ถูกนิยามอย่างชัดเจนในที่นี้เพราะการรั่วไหลอยู่ที่ execution ล้วน ๆ — คู่เทียบที่ซื่อสัตย์คือสัญญาณเดียวกันที่มี fill ซื่อสัตย์)
  • Indicator peek: มันดันแทบทุก configuration ที่ไม่มี edge ให้ผ่านเกณฑ์ deploy ด้วยเช่นกัน (99.9% ผ่าน Sharpe ≥ 1) — มันจะโบกมือให้ noise เข้าสู่ production ได้โดยตรง
  • Whole-series normalization: 12% ผ่านเกณฑ์ — ซึ่งก็คือ base rate ของ noise เอง แทบไม่มี premium จากการรั่วไหลเพิ่มขึ้นมาเลย

อนุกรมวิธาน และวิธีตรวจจับแต่ละแบบ

การรั่วไหลทั้งสามแบบไม่ได้อันตรายเท่ากัน และความแตกต่างนี้ก็ให้บทเรียนที่ดี

1. Execution leakage (ตัวที่แพงที่สุด)

Off-by-one ของ same-bar fill: ลูกศร execution ที่โค้งย้อนกลับเข้าไปในแท่งที่สร้างสัญญาณ เทียบกับ fill ที่ซื่อสัตย์บน open ของแท่งถัดไป

อาการ: ราคา fill มีความสัมพันธ์กับสัญญาณเพราะมันมาจากแท่งเดียวกัน ขนาด: มหาศาล (+15 จาก noise ที่ dose เต็ม, +3.9 ที่ dose หนึ่งในสี่) ทำไมมันแย่ที่สุด: สัญญาณของคุณแทบจะโดยนิยามแล้วถูกสร้างจากการเคลื่อนไหวราคาล่าสุด ดังนั้นผลตอบแทนของแท่งสัญญาณจึงเป็นสิ่งที่ feature ของคุณมีความสัมพันธ์ด้วยมากที่สุดพอดี การบันทึกมันก็เหมือนกับการแอบดูเฉลย

การตรวจจับ — one-bar shift test นี่คือการวินิจฉัยที่มีค่าที่สุดเพียงหนึ่งเดียวในบทความนี้ นำ backtest ของคุณมา shift ทุก fill ให้ช้าลงหนึ่งแท่ง (ตัดสินใจที่ i, fill ที่ open[i+1]) ถ้าผลลัพธ์แทบไม่ขยับ แสดงว่า execution ของคุณซื่อสัตย์ ถ้าผลลัพธ์ ล่มหรือพลิกเครื่องหมาย แสดงว่าคุณกำลังเทรดในอดีต นี่คือสิ่งที่เกิดขึ้นกับการค้นหาแบบ Sobol ของเราพอดี: shift fill แล้ว OOS ที่ "ทำกำไร" กลับกลายเป็นขาดทุน — หรือพูดให้ถูกต้องกว่านั้นคือ ความสัมพันธ์ ที่แท้จริง ปรากฏขึ้นเมื่อการรั่วไหลถูกกำจัดออกไป

entry_price = open_[i + 1]      # NOT close[i], NOT open[i]

2. Indicator / feature leakage (ตัวที่เงียบที่สุด)

อาการ: indicator ที่แท่ง i ขึ้นอยู่กับข้อมูลจาก i+1 หรือหลังจากนั้น — moving average แบบ centered, filter ที่ไม่มี causal delay, label แบบ peak/trough ที่ต้องใช้แท่งในอนาคตมายืนยัน, transform สไตล์ Heikin-Ashi ที่ป้อนแท่งเทียนในอนาคตเข้าไป ขนาด: ใหญ่ (+4.8 จาก noise) ทำไมมันซ่อนตัวได้: การรั่วไหลถูกฝังอยู่ในการเรียกไลบรารี scipy.signal.filtfilt เป็น zero-phase — และ zero-phase หมายถึง non-causal feature แบบ "แท่งนี้คือ local maximum" ไม่มีทางรู้ได้จนกว่าแท่งถัดไปจะปรากฏขึ้น

การตรวจจับ: สำหรับ indicator ทุกตัว ให้ถามว่า index สูงสุดที่มันอ่านคืออะไร? ถ้าการคำนวณค่าที่ t แตะ t+1 เมื่อไหร่ก็ตาม นั่นคือ non-causal คำนวณ indicator บน window แบบ expanding/rolling ที่เป็น causal แล้วตรวจสอบว่าค่าที่แท่ง t เหมือนกันไม่ว่าจะมีแท่งหลัง t อยู่ใน array หรือไม่ (การ implement HMA/ADX ของเราผ่านการทดสอบนี้: ทุก output ที่ t อ่านเฉพาะ input ที่ ≤ t เท่านั้น)

3. Normalization leakage (ตัวที่ขึ้นอยู่กับ channel)

อาการ: scaler (StandardScaler, min-max, global z-score) ถูก fit บน dataset ทั้งหมด รวมถึง test set ด้วย คำเตือนหลักในวรรณกรรม ML พูดถึงเรื่องนี้อย่างชัดเจน — Elements of Statistical Learning §7.10.2 ของ Hastie, Tibshirani & Friedman ("the wrong and right way to do cross-validation") และ common-pitfalls guide ของ scikit-learn เอง: "ค่าเฉลี่ยควรเป็นค่าเฉลี่ยของ train subset ไม่ใช่ค่าเฉลี่ยของข้อมูลทั้งหมด"

ขนาดในการทดสอบของเรา:ศูนย์ (−0.74 → −0.84) นี่คือผลลัพธ์ที่น่าประหลาดใจและซื่อสัตย์ และคุ้มค่าที่จะทำความเข้าใจมากกว่าท่องจำ

ทำไมมันถึงไม่เพิ่มค่า? เพราะกลยุทธ์ของเราใช้ feature ผ่าน sign เท่านั้น (threshold ที่ศูนย์) การ scale ด้วย standard deviation ไม่เคยเปลี่ยน sign และการ center ด้วยค่าเฉลี่ยทั้งซีรีส์ก็แค่ขยับจุดตัดศูนย์เล็กน้อยเท่านั้น ดังนั้นการทำ standardization ทั้งซีรีส์บนกฎแบบ sign ล้วน ๆ จึงแทบไม่เป็นอันตรายเลย

อย่าสรุปเกินไปจากตรงนี้ Normalization leakage นั้นขึ้นอยู่กับ channel ทันทีที่กลยุทธ์ของคุณใช้ ขนาด (magnitude) ของ feature — position sizing ที่เป็นสัดส่วนกับ z-score, entry threshold ที่ไม่ใช่ศูนย์ซึ่งเลือกจากการดู distribution ที่ถูก scale แล้ว, neural net ที่รับ input แบบ standardized — scaler ที่รู้อนาคตจะเริ่มมีผลกระทบ และยิ่งมีผลมากขึ้นเมื่อสถิติทั้งซีรีส์ต่างจากสถิติแบบ causal มากขึ้น ผลลัพธ์ของเราไม่ได้แปลว่า "normalization leakage ปลอดภัย" แต่หมายความว่า "ขนาดของ leakage ขึ้นอยู่กับ channel ที่ปริมาณที่รั่วไหลเข้าสู่การตัดสินใจ และคุณควรวัดมันแทนที่จะสันนิษฐานเอาเอง" กฎแบบ sign เป็นกรณีเดียวที่การรั่วไหลแบบนี้มีต้นทุนต่ำ

จุดเชื่อมโยงของบทความนี้

Look-ahead bias คือจุดเชื่อมแรกในห่วงโซ่ที่ซีรีส์นี้กำลังบันทึกไว้:

  • มันทำให้ input ของการ validation เสียหาย backtest ที่รั่วไหลจะแล่นผ่าน walk-forward split ไปได้อย่างสบาย ๆ และดูเหมือน plateau กว้าง ๆ แทนที่จะเป็นยอดแหลม overfit — เพราะการรั่วไหลสม่ำเสมอในทุก fold cross-validation จึงจับมันไม่ได้ Leakage คือ failure mode ที่อยู่ ต้นน้ำ ของ overfitting และไม่ว่า validation ปลายน้ำจะซื่อสัตย์แค่ไหนก็ช่วยคุณไม่ได้
  • มันมีปฏิสัมพันธ์กับ parameter search: การค้นหานับพัน trial บนข้อมูลที่รั่วไหลจะพบ configuration ที่ขูดรีดการรั่วไหลนั้นได้มากที่สุด "ผู้ชนะ" ก็คือตัวการที่เลวร้ายที่สุด
  • มันคือเหตุผลที่ backtest-live parity ไม่ตรงกัน การรั่วไหลคือคำอธิบายที่ชัดเจนที่สุดสำหรับช่องว่าง 30–50% ระหว่าง backtest กับบอท เพราะการเทรดจริงคือที่เดียวโดยกลไกที่คุณแอบดูไม่ได้

วินัยที่จับความผิดพลาดทั้งหมดนี้ได้คือวินัยเดียวกับที่วรรณกรรมวิชาการเรียกร้องมาหลายปี: ปฏิบัติต่อ backtest เหมือนการทดลองทางสถิติที่มีขอบเขตข้อมูลอย่างเคร่งครัด Bailey, Borwein, López de Prado & Zhu แสดงให้เห็นว่า overfitting สร้างผลงานปลอมได้ง่ายแค่ไหน (2014); backtesting protocol ของ Arnott, Harvey & Markowitz (2019) กำหนดสุขอนามัยนี้เป็นระเบียบ Look-ahead bias คือขอบเขตพื้นฐานที่สุดในบรรดาทั้งหมด — ขอบเขตใน เวลา — และเป็นสิ่งที่ละเมิดโดยไม่ตั้งใจได้ง่ายที่สุด

สรุปประเด็นสำคัญ

การวินิจฉัยแบบ one-bar shift: การขยับทุก fill ให้ช้าลงหนึ่งแท่งทำให้เส้นโค้งปลอมที่พุ่งทะยานยุบตัวกลับลงสู่ความจริงที่ซื่อสัตย์

  1. Look-ahead bias มีขนาดใหญ่มากเชิงปริมาณแต่มองไม่เห็นเชิงคุณภาพ ความผิดพลาดของ execution แค่หนึ่งแท่งเปลี่ยน Sharpe จาก −0.74 (noise ล้วน ๆ ที่ขาดทุนอย่างถูกต้อง) ไปเป็น +14.79 ความผิดพลาดคือหนึ่งบรรทัด แต่ผลลัพธ์คือ track record ที่ถูกปั้นแต่งขึ้นมา
  2. มันคือ gradient การจับแม้แต่ 25% ของแท่งสัญญาณก็ให้ +3.90 จากความว่างเปล่า คุณไม่จำเป็นต้องมีบั๊กที่โจ่งแจ้ง — แค่มองโลกในแง่ดีกับ fill ของคุณเกินไปนิดหน่อยก็เพียงพอแล้ว
  3. ตัวเลขที่วัดได้แยกความสามารถออกจากการรั่วไหลไม่ได้ เมื่อมี edge จริงอยู่ การรั่วไหลจะเพิ่มค่าในรายงานไปไกลเกินความจริงที่เทรดได้ การป้องกันเดียวคือ กระบวนการ ไม่ใช่ metric
  4. One-bar shift test คือการวินิจฉัยที่เร็วที่สุดของคุณ ขยับทุก fill ให้ช้าลงหนึ่งแท่ง ถ้าประสิทธิภาพล่ม แสดงว่าคุณกำลังเทรดในอดีต
  5. ขนาดของ leakage ขึ้นอยู่กับ channel Execution และ indicator peek นั้นร้ายแรงมาก ส่วน whole-series normalization บนกฎแบบ sign แทบไม่มีต้นทุนเลย วัดการรั่วไหลผ่าน channel ที่มันเข้ามาจริง ๆ — อย่าสันนิษฐานเอาเอง

การศึกษาแบบควบคุมฉบับเต็ม — การรั่วไหลทั้งสามแบบ, dose sweep, การวิเคราะห์ false-deployment, วิธีการอย่างเป็นทางการ และตัวเลขทุกตัวที่ reproduce ได้จาก script แบบ deterministic เพียงตัวเดียว — อยู่ในเปเปอร์คู่กันที่ lookahead.marketmaker.cc พร้อมโค้ดและข้อมูลที่ github.com/suenot/lookahead-inflation

กลยุทธ์ในการทดลอง null ของเราไม่มี edge เลยแม้แต่น้อย แต่มันก็ยังแสดง Sharpe 15 ออกมา ถ้า backtest ของคุณดูดีเกินไป สิ่งแรกที่ควรสงสัยไม่ใช่ความอัจฉริยะของคุณ — แต่คือนาฬิกาของคุณ

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

ผู้เขียน

Eugen Soloviov
Eugen Soloviov

Trading-systems engineer

Trading-systems engineer building bots since 2017: cross-exchange arbitrage (connected up to 30 venues), cointegration-based pairs arbitrage across spot and futures, scalping, news and sentiment-driven strategies, trend algorithms, and portfolio management and balancing algorithms. Also builds sub-millisecond order execution, big-data warehouses, backtesting engines, AI agents, and trading interfaces (incl. open-source profitmaker.cc). Stack: JS/TS, Python, Rust/Zig/Go, DevOps, backend, frontend, architecture.

Newsletter

ก้าวนำหน้าตลาด

สมัครรับจดหมายข่าวของเราเพื่อรับข้อมูลเชิงลึกการเทรดด้วย AI เฉพาะ การวิเคราะห์ตลาด และการอัปเดตแพลตฟอร์ม

เราเคารพความเป็นส่วนตัวของคุณ ยกเลิกการสมัครได้ทุกเมื่อ