Probability of Backtest Overfitting: การค้นหาของคุณเอาชนะการโยนเหรียญหรือไม่?
เป็นส่วนหนึ่งของซีรีส์ "Backtests Without Illusions"
📄 บทความนี้ขยายกลายเป็นเปเปอร์วิจัย ตัวเลขทุกตัวด้านล่างมาจากสคริปต์ deterministic ตัวเดียวที่สร้าง ground truth ที่ควบคุมได้ — การค้นหาแบบ zero-edge, การค้นหาที่ปลูก edge ไว้ และ moving-average parameter grid จริงบน random walk — แล้วรัน Combinatorially Symmetric Cross-Validation (CSCV) เพื่อประมาณ Probability of Backtest Overfitting เทียบกับมัน โดยวัดโดยตรงว่ากระบวนการ selectiongeneralize ได้ดีแค่ไหน อ่านเปเปอร์ฉบับเต็มออนไลน์ (เวอร์ชันอินเทอร์แอกทีฟ + PDF) ได้ที่ pbo-search.marketmaker.cc โค้ดและข้อมูลอยู่ที่ github.com/suenot/pbo-search
Deflated Sharpe Ratio เอาผู้ชนะของคุณขึ้นศาล: ในเมื่อคุณค้นหา N configuration แล้ว Sharpe ตัวนี้เกินกว่าที่ความโชคดีจะซื้อได้ไหม? บทความนี้เอาสิ่งอื่นขึ้นศาล — การกระทำของการเลือก คุณรัน grid คุณเก็บ cell ที่ดีที่สุดไว้ แล้วก็เดินหน้าต่อ แต่ selection เองน่าเชื่อถือไหม? ถ้าคุณรัน in-sample/out-of-sample split ใหม่ทั้งหมดด้วยวิธีอื่น configuration ตัวเดิมจะยังคงออกมาเป็นที่หนึ่งไหม — หรือคุณแค่สวมมงกุฎให้เหรียญที่โชคดีที่สุดในบรรดาเหรียญร้อยเหรียญ?
Probability of Backtest Overfitting (PBO) ที่เสนอโดย Bailey, Borwein, López de Prado & Zhu (2017) ตอบคำถามนั้นเป๊ะ ๆ และมันทำแบบนั้นด้วยตัวเลขที่คนส่วนใหญ่อ่านผิดตั้งแต่แรกเห็น นี่คือประโยคที่สำคัญที่สุดเพียงประโยคเดียวในบทความนี้ อ่านมันสองรอบ:
null ของ PBO คือ 0.5 ไม่ใช่ 1 การค้นหาที่ไม่มี out-of-sample skillจะได้คะแนน PBO ≈ 0.5 ครึ่งหนึ่งไม่ใช่ "overfit ครึ่งเดียว" — ครึ่งหนึ่งคือ overfit เต็มขั้น การโยนเหรียญ คุณต้องการ PBO ใกล้ ศูนย์
จุดนี้แหละที่ทุกคนสะดุด เราถูกฝึกให้อ่านความน่าจะเป็นเทียบกับ null ของ "ไม่มีอะไรเลย" และสำหรับ overfitting สัญชาตญาณของเราบอกว่าการอ่านแบบ "บริสุทธิ์" คือ 0 แต่ไม่ใช่ PBO คือความน่าจะเป็นที่ configuration ที่คุณเลือกว่าดีที่สุดใน sampleตกไปอยู่ครึ่งล่างของสนามแข่งนอก sample ถ้าการค้นหาของคุณไม่ได้เรียนรู้อะไรที่ generalize ได้จริง ผู้ชนะแบบ in-sample นั้น เมื่ออยู่นอก sample ก็มีโอกาสเท่า ๆ กันที่จะอยู่ตรงไหนก็ได้ใน ranking — ดังนั้นมันจึงตกไปอยู่ครึ่งล่างประมาณครึ่งหนึ่งของเวลา PBO ≈ 0.5 หมายความว่ากระบวนการ selection ของคุณคือการโยนเหรียญ PBO ≈ 0 หมายความว่าผู้ชนะแบบ in-sample ยังคงเป็นผู้ชนะนอก sample ได้อย่างน่าเชื่อถือ — selection นั้นน่าเชื่อถือ ทุกอย่างด้านล่างถูกสร้างขึ้นมาเพื่อทำให้ข้อเท็จจริงเรื่อง calibration ข้อนั้นจับต้องได้ บนข้อมูลที่เรารู้ ground truth
| Regime (200 configs, T = 1000, S = 16) | มันคืออะไร | In-sample Sharpe ของผู้ชนะ | Out-of-sample Sharpe | PBO | คำตัดสิน |
|---|---|---|---|---|---|
| Zero-edge field (กลยุทธ์ iid noise 200 ตัว) | ความโชคดีล้วน ๆ ไม่มี edge ที่ไหนเลย | 1.98 | 0.06 | 0.476 | overfit — การโยนเหรียญ |
| Planted edge (20 configs มี Sharpe ต่อปี 2.38) | ฝีมือจริง แข็งแรง | 3.73 | 2.34 | 0.001 | น่าเชื่อถือ |
| MA-crossover grid บน pure random walk (170 configs) | ภาพลวงตาที่ล่อตาล่อใจ | 0.97 | 0.04 | 0.463 | overfit — การโยนเหรียญ |
Sharpe ratio ต่อปี ×√252 ทั้งสามแถวเฉลี่ย Sharpe ของกลยุทธ์ที่ถูกเลือกข้าม Monte-Carlo matrix 60 ตัว — apples to apples เพื่อให้ overfit grid ถูกให้คะแนนด้วยวิธีเดียวกับ null และ planted edge บนฐานที่เฉลี่ยแล้วนี้ in-sample Sharpe ของผู้ชนะจาก grid (0.97) นั้นต่ำกว่าตัวเลข 1.98 ที่พองขึ้นของ null จริง ๆ, out-of-sample Sharpe ของมันเป็นบวกเล็กน้อยที่ 0.04 และ PBO ของมัน (0.463) อยู่ต่ำกว่า ½ นิดเดียว — แยกไม่ออกจาก null ทางสถิติ ตัวเลขที่ดูดราม่าจาก matrix เดียว (best-in-grid in-sample Sharpe ที่ 2.33 ยุบลงเหลือ median out-of-sample −0.22, PBO 0.573) เป็นของ random-walk seed ตัวแทนหนึ่งตัว และปรากฏพร้อมป้ายกำกับชัดเจนใน Act 4 ตัวเลขทุกตัวสืบย้อนไปถึง results file ได้
สามภูมิทัศน์ บทเรียนเดียว การค้นหาที่ไม่มี edge นั่งอยู่บนเส้นโยนเหรียญ 0.5 ไม่ว่า noise จะเป็น iid (PBO 0.476) หรือแต่งตัวเป็น moving-average grid จริง (PBO 0.463) — ทั้งสองแยกไม่ออกจากกันทางสถิติ และทั้งคู่ก็เป็นคำตัดสินที่ฟ้องผิด edge จริงดัน PBO ลงมาที่ 0.001 เมื่อเฉลี่ยข้าม matrix ผู้ชนะที่ถูกเลือกจาก grid นั้นธรรมดามาก — in-sample Sharpe ที่ 0.97 ต่ำกว่าตัวเลข 1.98 ที่พองขึ้นของ null — ซึ่งนั่นเองคือการวินิจฉัยที่ซื่อสัตย์: การค้นหาที่ไม่มี edge อ่านออกมาเหมือน null ดราม่าอยู่ที่หาง บน random-walk matrix ตัวแทนหนึ่งตัว (Act 4) cell ที่ดีที่สุดของ grid ทำ in-sample Sharpe ได้ 2.33 — เท่ากับ out-of-sample 2.34 ของ planted edge โดยพื้นฐาน เสมอกันเป๊ะ — แต่นอก sample มันตกไปอยู่ครึ่งล่างบ่อยพอ ๆ กับครึ่งบน ช่องว่างระหว่าง backtest ที่สวยงามกับ selection ที่ไร้ค่านั้นมองไม่เห็นใน Sharpe ของผู้ชนะเอง และจะเห็นได้ก็ต่อเมื่อคุณให้คะแนนกระบวนการ นั่นคือสิ่งที่ PBO ทำ
Act 1 — กระบวนการขึ้นศาล: CSCV ทำอะไรกันแน่

DSR เป็นแบบparametric: มันสร้างโมเดลของ distribution ของ Sharpe สูงสุดภายใต้ null แล้ว deflate significance ของผู้ชนะด้วยวิธีวิเคราะห์ CSCV คือคำตอบแบบnon-parametricสำหรับปัญหา selection-bias เดียวกัน — แทนที่จะสร้างโมเดลของค่าสูงสุด มัน resample train/test split ทุกวิธีที่เป็นไปได้ แล้วเฝ้าดูเชิงประจักษ์ว่าผู้ชนะแบบ in-sample ยังคงชนะต่อไปหรือไม่ ไม่มีข้อสมมติเรื่อง distribution ไม่มีการนับ "effective trial" มีแค่คำถามว่า: ตัวเลือกนั้น generalize ได้ไหม?
เริ่มจากวัตถุดิบ คุณ backtest configuration N = 200 ตัวของกลยุทธ์คลาสหนึ่งข้าม T = 1000 observation ที่ synchronous กัน เอา return series ของแต่ละ configuration มาเรียงเป็นคอลัมน์แล้วคุณจะได้ T × N performance matrix M — 1,000 แถวของเวลา, 200 คอลัมน์ของกลยุทธ์ นี่คือ input เดียวที่ CSCV ต้องการ
ทีนี้การสร้างมัน ในสี่ขั้นตอน:
- แบ่งเวลาออกเป็น
S = 16บล็อกที่ไม่ทับกัน ความยาวเท่ากัน (T/Sแถวต่อบล็อก) บล็อกรักษาโครงสร้างเวลาแบบ local ไว้ — การตัดสินใจเชิงออกแบบที่สำคัญทันทีที่ผลตอบแทนมี memory - เลือกทุกวิธีที่เป็นไปได้ในการใช้ครึ่งหนึ่งของบล็อกเป็น train และอีกครึ่งเป็น test ด้วย
S = 16นั่นคือทุกวิธีC(16, 8) = 12,870วิธีในการเลือก 8 จาก 16 บล็อกเป็น training set; อีก 8 บล็อกที่เหลือคือ test set นี่คือที่มาของคำว่า "combinatorially symmetric": แต่ละ split มีภาพสะท้อนในกระจกของมันเอง (สลับ train กับ test) ดังนั้น scheme นี้จึงใช้ข้อมูลของคุณแบบสมมาตร แทนที่จะเป็นการตัด past→future แบบเดียวที่ walk-forward ครั้งเดียวให้คุณได้ - บนแต่ละ split จัดอันดับ configuration ทั้ง 200 ตัวด้วย in-sample Sharpe แล้วเลือกผู้ชนะ
n*จากนั้นหาว่า configuration ตัวเดียวกันn*นั้นอยู่อันดับไหนนอก sampleบน 8 บล็อกที่ถูกกันไว้ - บันทึก relative out-of-sample rank ของผู้ชนะ แล้วแปลงมันเป็น logit PBO คือสัดส่วนของ 12,870 split ที่ logit นั้น ≤ 0
การ enumerate นี้เขียนโค้ดสั้นมาก:
from itertools import combinations
combos = list(combinations(range(S), S // 2)) # C(16, 8) = 12,870 splits
สำหรับแต่ละ split ให้ เป็น out-of-sample rank ของผู้ชนะ in-sample ท่ามกลาง configuration ทั้ง ตัว (rank 1 = แย่ที่สุด, = ดีที่สุด) normalize มันเป็น relative rank เอา logit ของมัน แล้ว integrate ข้าม split:
logit ก็แค่ไม้บรรทัดที่สะดวก หมายความว่าผู้ชนะตกไปอยู่ครึ่งบนนอก sample (relative rank สูงกว่า ½) — ความสอดคล้องระหว่าง in-sample/out-of-sample ดี หมายความว่ามันตกไปอยู่ที่หรือต่ำกว่า median นอก sample — ตัวเลือกแบบ in-sampleไม่generalize บน split นั้น PBO คือสัดส่วนของ split ที่ผู้ชนะ in-sample ไม่สามารถเอาชนะ median นอก sample ได้ matrix ทั้งหมดเป็นตัวกำหนดมัน: เมื่อกำหนด M และ S แล้ว PBO เป็น deterministic — ไม่มี resampling seed, split ทั้ง 12,870 ถูก enumerate อย่างครบถ้วน
ในโค้ด เมื่อคุณมี in-sample และ out-of-sample Sharpe ของทุก configuration บนทุก split แล้ว (matrix R_tr และ R_te แต่ละตัวขนาด 12,870 × 200) หัวใจของ estimator คือหกบรรทัด:
n_star = R_tr.argmax(axis=1) # in-sample winner, per split
oos_sh = R_te[rows, n_star] # that winner's OWN out-of-sample Sharpe
rank = (R_te <= oos_sh[:, None]).sum(axis=1) # its OOS rank among N configs, 1..N
omega = np.clip(rank / (N + 1.0), 1e-6, 1 - 1e-6) # relative OOS rank in (0,1)
lambdas = np.log(omega / (1.0 - omega)) # logit
pbo = float(np.mean(lambdas <= 0.0)) # fraction of splits with lambda <= 0
สังเกตสิ่งที่ไม่มีอยู่ตรงนี้: ไม่มี p-value, ไม่มี threshold บน Sharpe ของผู้ชนะ, ไม่มีโมเดลของ null distribution PBO ไม่เคยถามว่าผู้ชนะดีไหม มันถามว่าการเลือกตัวที่ดีที่สุดแบบ in-sampleเป็นการตัดสินใจที่รอดจากการสัมผัสกับ held-out data ได้หรือไม่ นั่นคือคุณสมบัติของการค้นหาของคุณ ไม่ใช่ของกลยุทธ์ของคุณ — ซึ่งเป็นเหตุผลพอดีว่าทำไมมันถึงจับสิ่งที่สถิติของผู้ชนะเองจับไม่ได้
Act 2 — Calibration คือข้อโต้แย้งทั้งหมด: null คือ 0.5

diagnostic ที่คุณ calibrate ไม่ได้ก็เป็นแค่ข่าวลือ ดังนั้นก่อนที่จะเชื่อ PBO กับอะไรที่เป็นจริง ให้ตรึงจุดปลายสองจุดบนข้อมูลที่รู้คำตอบอยู่แล้ว: สนามที่ไม่มี edge ที่ไหนเลย และสนามที่มีedge จริง ถ้า PBO ไม่ตกลงใกล้ 0.5 บนจุดแรก และใกล้ 0 บนจุดที่สอง มันก็ไร้ค่า
จุดปลายของ null สร้าง M จาก 200 คอลัมน์ของ Normal noise ที่เป็นอิสระต่อกัน ไม่มี drift ไม่มี edge — true Sharpe เท่ากับ 0 เป๊ะสำหรับทุก configuration — แล้วรัน CSCV เฉลี่ยข้าม matrix แบบนี้ 60 ตัว กลยุทธ์ที่ถูกเลือก (in-sample-best) ทำin-sample Sharpe ต่อปีเฉลี่ยที่ 1.98 นั่นไม่ใช่ตัวเลขเล็กน้อยเลย มันคือ selection inflation แบบเดียวกับที่บทความ DSR วัดไว้ — ตัวที่ดีที่สุดในบรรดา 200 คอลัมน์ noise ดูเหมือนกลยุทธ์ที่ระดมทุนได้ นอก sample ผู้ชนะตัวเดียวกันนั้นทำ Sharpe ต่อปีได้ 0.06 มันคืนเกือบทั้งหมดที่ได้มากลับไป และคำตัดสินต่อกระบวนการ:
นั่นคือการโยนเหรียญ ที่วัดออกมาแล้ว ข้าม 12,870 split ผู้ชนะแบบ in-sample มีโอกาสตกต่ำกว่า out-of-sample median พอ ๆ กับตกสูงกว่ามัน — 0.476 ต่ำกว่า ½ นิดเดียว แยกไม่ออกจาก 0.5 เมื่อพิจารณาการกระจายตัวแบบ Monte-Carlo diagnostic คู่หูก็เห็นตรงกัน: ความน่าจะเป็นที่ out-of-sample Sharpe ของกลยุทธ์ที่ถูกเลือกจะเป็นลบคือ 0.475 — เลือกตัวที่ดีที่สุดแบบ in-sample จาก pure noise แล้วมันขาดทุนนอก sample ประมาณครึ่งหนึ่งของเวลา ไม่มีฝีมืออยู่ใน selection เลยเพราะไม่มีฝีมือให้หา และ PBO รายงานสิ่งนั้นออกมาเป๊ะ ๆ: 0.5 คือเส้น overfitting และ pure noise นั่งอยู่บนมันพอดี
ทำไมถึงเป็น 0.5 ไม่ใช่ 1? เพราะภายใต้ null ที่แท้จริง คอลัมน์ทั้ง 200 ตัวเป็น exchangeable — เป็นการสุ่มที่สลับกันได้ทางสถิติจาก noise process เดียวกัน ผู้ชนะแบบ in-sample พิเศษก็เฉพาะใน sampleเท่านั้น; นอก sample มันก็เป็นแค่คอลัมน์อีกตัวหนึ่ง มีโอกาสเท่า ๆ กันที่จะอยู่อันดับไหนก็ได้ ดังนั้น relative out-of-sample rank ของมันจึงกระจายแบบ uniform บน , logit สมมาตรรอบ 0 และสัดส่วนที่ ลู่เข้าสู่ ½ PBO ที่เท่ากับ 1 จะแย่กว่าการโยนเหรียญ — มันจะหมายความว่าความสำเร็จแบบ in-sampleทำนายความล้มเหลวนอก sample ได้อย่างน่าเชื่อถือ ซึ่งต้องการกลไก anti-persistence ที่ทำงานอยู่จริง ไม่ใช่แค่การไม่มี edge เฉย ๆ (รายละเอียดเพิ่มเติมอยู่ในหมายเหตุความซื่อสัตย์)
จุดปลายของ edge ทีนี้สร้างสนามที่ 20 ใน 200 configuration มี edge จริงที่ปลูกไว้ — per-observation Sharpe ที่ 0.15 ซึ่งต่อปีแล้วได้ 2.38 (คำนวณ: ) — และปล่อยอีก 180 ตัวที่เหลือให้เป็น noise รัน CSCV แบบเดียวกันเป๊ะ เรื่องราวพลิกกลับทั้งหมด:
| In-sample Sharpe (ต่อปี) | Out-of-sample Sharpe (ต่อปี) | PBO | P(OOS loss) | |
|---|---|---|---|---|
| Null (edge = 0) | 1.98 | 0.06 | 0.476 | 0.475 |
| Planted edge (Sharpe 2.38) | 3.73 | 2.34 | 0.001 | 0.0006 |
ผู้ชนะของ planted edge ทำ in-sample Sharpe ต่อปีได้ 3.73 — พองขึ้นจาก selection เหมือนเดิม — แต่คราวนี้มันรักษาout-of-sample ไว้ที่ 2.34 และ PBO ยุบลงเหลือ 0.001 ข้าม split ทั้ง 12,870 ผู้ชนะแบบ in-sample แทบไม่เคยตกไปอยู่ครึ่งล่างนอก sample เลย ความน่าจะเป็นของการขาดทุนนอก sample ลดลงเหลือ 0.0006 นี่คือหน้าตาของกระบวนการ selection ที่น่าเชื่อถือ: ไม่ว่าคุณจะตัด train กับ test แบบไหน configuration ประเภทเดิมก็ยังคงชนะต่อไป เพราะมี effect ที่แข็งแรงและเป็นจริงอยู่ตรงนั้นให้การค้นหาล็อกเข้ากับมันได้ จุดปลายทั้งสอง — 0.476 บน noise, 0.001 บน edge จริง — คือ calibration PBO ใช้งานได้จริง
Act 3 — เทอร์โมมิเตอร์แบบต่อเนื่อง ไม่ใช่ test แบบใช่/ไม่ใช่

จุดปลายสองจุดพิสูจน์ว่า PBO แยก noise ออกจาก edge ได้ แต่คุณสมบัติที่ลึกกว่านั้นคือมันทำแบบนั้นอย่างราบรื่น ไล่ planted edge จากไม่มีเลยไปจนถึงแข็งแรง แล้ว PBO จะไม่กระโดดจาก 0.5 ไป 0 — มันไถลลงมาตามทางลาดแบบ monotone และ out-of-sample Sharpe ของกลยุทธ์ที่ถูกเลือกก็ไต่ขึ้นมาบรรจบมัน ทีละขั้น:
| Planted true Sharpe (ต่อปี) | PBO | OOS Sharpe ของกลยุทธ์ที่ถูกเลือก (ต่อปี) |
|---|---|---|
| 0.00 | 0.52 | −0.05 |
| 0.48 | 0.44 | 0.19 |
| 0.95 | 0.21 | 0.81 |
| 1.59 | 0.03 | 1.65 |
| 2.38 | 0.001 | 2.48 |
| 3.17 | 0.00 | 3.29 |
อ่านสองคอลัมน์ข้อมูลไปพร้อมกัน ที่ true edge เท่ากับศูนย์ PBO อยู่ที่ 0.52 และกลยุทธ์ที่ถูกเลือกทำได้ −0.05 นอก sample — การโยนเหรียญอีกครั้ง และผู้ชนะที่ขาดทุน เติม edge เข้าไปนิดหน่อย (ต่อปี 0.48) แล้ว PBO ขยับลงมาที่ 0.44 เมื่อถึง true Sharpe ต่อปีที่ 0.95 — edge ที่พอประมาณและน่าเชื่อได้จริง ๆ — PBO อยู่ที่ 0.21 แล้ว และ out-of-sample Sharpe ไต่ขึ้นไปถึง 0.81 ที่ 1.59 มันคือ 0.03; ที่ 2.38 คือ 0.001; ที่ 3.17 แทบจะเป็น 0.00 โดยที่กลยุทธ์ที่ถูกเลือกทำได้ 3.29 นอก sample PBO ลดลงแบบ monotonic เมื่อ edge จริงแข็งแรงขึ้น และผลงาน out-of-sample ของผู้ชนะก็ไต่ขึ้นไปพร้อมกัน — ทั้งสองคือข้อเท็จจริงเดียวกันที่มองจากสองมุม
นี่คือคุณสมบัติที่ทำให้ PBO ใช้งานได้จริง: มันคือเทอร์โมมิเตอร์วัด overfitting แบบต่อเนื่อง ไม่ใช่สัญญาณเตือนแบบ binary PBO ที่ 0.21 ไม่ได้แค่บอกว่า "ไม่ overfit" — มันบอกว่า selection ของคุณมี out-of-sample skillบางส่วน: ผู้ชนะแบบ in-sample เอาชนะ out-of-sample median ได้ 79% ของเวลา แต่ edge นั้นบางพอที่ splits หนึ่งในห้ายังฝังมันอยู่ คุณสามารถเฝ้าดูตัวเลขขยับได้เมื่อคุณทำให้สัญญาณแข็งแรงขึ้น จำกัด universe ให้แคบลง หรือตัดแต่ง grid ของคุณ และรู้ว่าทิศทางไหนซื่อสัตย์ กฎง่าย ๆ ของเปเปอร์เอง — ปฏิเสธเมื่อ PBO เกิน 0.05 — ตกออกมาเองอย่างเป็นธรรมชาติจากทางลาดนี้: ต่ำกว่า Sharpe ต่อปีประมาณ ~1.5 การค้นหายังไม่ผ่าน; สูงกว่า ~1.6 มันผ่านแล้ว แต่ตัวทางลาดเองให้ข้อมูลมากกว่า cutoff เดียวใด ๆ เพราะมันบอกคุณไม่ใช่แค่ว่าคุณ overfitหรือไม่ แต่บอกว่าคุณใกล้การโยนเหรียญแค่ไหน
Act 4 — กับดักที่สมจริง: backtest ที่สวยงาม รับรองว่าไร้ค่า

null แบบ iid-noise นั้นซื่อสัตย์แต่ปัดตกได้ง่าย — "กลยุทธ์ของฉันไม่ใช่คอลัมน์ Normal แบบสุ่ม" ดังนั้นนี่คือกับดักในรูปแบบที่นักปฏิบัติเดินเข้าไปจริง ๆ ลอง moving-average crossover กฎที่ถูก backtest มากที่สุดในโลก: เข้า long เมื่อ MA เร็วตัดขึ้นเหนือ MA ช้า อยู่เฉย ๆ ในกรณีอื่น ทำ grid มัน — fast length 10 ค่า slow length 17 ค่า เก็บคู่ fast-below-slow ที่ valid ไว้ ได้ K = 170 configurations ทีนี้รัน grid นั้นบนอนุกรมที่พิสูจน์ได้ว่ามี edge เป็นศูนย์: pure random walk ไม่มีอะไรให้ค้นพบเลย crossover ทำนาย random walk ไม่ได้ เรารู้อยู่แล้วว่าคำตอบคือ "ไม่มีกลยุทธ์"
แต่ grid ไม่รู้เรื่องนั้น มันยื่นผู้ชนะให้คุณ และผู้ชนะนั้นล่อตาล่อใจมาก:
| Diagnostic (random-walk matrix ตัวแทนหนึ่งตัว, seed 3000, K = 170, S = 16) | ค่า |
|---|---|
| Best in-sample Sharpe (ต่อปี) | 2.33 |
| PBO | 0.573 |
| Median out-of-sample Sharpe (ต่อปี) | −0.22 |
| ความน่าจะเป็นของการขาดทุนนอก sample | 0.63 |
| Degradation slope ของ out-of-sample เทียบกับ in-sample | −0.92 |
| Median logit | −0.25 |
นี่คือ matrix ที่ seed ไว้หนึ่งตัว เมื่อเฉลี่ยข้าม random-walk matrix ที่เป็นอิสระต่อกัน 60 ตัว diagnostic ชุดเดียวกันนี้อ่านได้ PBO 0.463 ± 0.223, in-sample Sharpe ของผู้ชนะที่ 0.97 สลายลงเหลือ 0.04 และ P(OOS loss) 0.47 — แยกไม่ออกจาก null ทางสถิติ 0.573 ของ seed 3000 คือการสุ่มหนึ่งครั้งที่อยู่ทางฝั่งสูงของ null band ~0.5 — เป็น sampling noise รอบ ๆ ค่าโยนเหรียญ อยู่ในขอบเขต ±0.223 ของการกระจายตัวระหว่าง matrix อย่างสบาย ๆ — และเรื่องราวเหมือนกันไม่ว่าจะมองแบบไหน
in-sample Sharpe ต่อปีที่ 2.33 บน moving-average crossover คือผลลัพธ์ประเภทที่จบลงใน pitch deck มันเท่ากับ Sharpe แบบout-of-sampleของ edge จริงที่ปลูกไว้ของเราจาก Act 2 โดยพื้นฐาน (2.34 — เสมอกันเป๊ะ) ถ้าคุณหยุดแค่ backtest คุณจะให้ทุนมัน CSCV ปฏิเสธ PBO เป็นการโยนเหรียญที่นี่: 0.463 เฉลี่ยข้าม 60 matrix, 0.573 บนตัวนี้โดยเฉพาะ — ทั้งสองบอกว่าการค้นหาไม่มี out-of-sample skill อย่าอ่าน 0.573 เกินความหมาย: มันอยู่สูงกว่า ½ แค่ 0.073 เป็น sampling noise รอบ null ที่ 0.5 และอยู่ใน band ±0.223 ระหว่าง matrix อย่างสบาย ๆ PBO ที่สูงกว่า 0.5 จริง ๆ — ที่ซึ่งความสำเร็จแบบ in-sample จะทำนายความล้มเหลวนอก sample ได้อย่างจริงจัง — ต้องการโครงสร้าง anti-persistence หรือ trading-cost ที่ random walk ตัวนี้ไม่มี (ดูหมายเหตุความซื่อสัตย์) บน matrix นี้ median logit ที่ −0.25 ทำให้ผู้ชนะ in-sample ตัวกลางอยู่ที่ relative out-of-sample rank ประมาณ 0.44 (คำนวณ: ) — ประมาณอันดับที่ 75 จาก 170 (คำนวณ: ) ต่ำกว่ากึ่งกลางของสนามที่มันควรจะนำอยู่นิดเดียว median out-of-sample Sharpe ของผู้ชนะตัวนั้นคือ −0.22 — ติดลบ — และมันขาดทุนนอก sample 63% ของเวลา backtest Sharpe ที่ 2.33 ที่มี out-of-sample expectation เป็นการขาดทุน: นี่คือนิยามของภาพลวงตา
degradation slope ที่ −0.92 คือมีดเล่มที่สอง regress out-of-sample Sharpe ของผู้ชนะที่ถูกเลือกในแต่ละ split เทียบกับ in-sample Sharpe ของมัน; slope ติดลบอย่างชัน — configuration ที่ดูดีกว่าใน sample ก็ยิ่งแย่กว่านอก sample นี่คือลายนิ้วมือของ overfitting บนอนุกรมที่มี memory: crossover ล็อกเข้ากับ pattern ชั่วคราวใน training block ที่ เนื่องจากเป็น artifact ของ random walk จึงกลับทิศนอก sample จุดที่ละเอียดอ่อนหนึ่งจุดที่ควรพูดไว้เพื่อไม่ให้คุณอ่าน slope เกินความหมาย: slope ที่ติดลบไม่ใช่คำตัดสินในตัวมันเอง แม้แต่ regime ของ edge จริงจาก Act 2 ก็มี degradation slope ที่ติดลบ (−0.52) — regression to the mean ดึงค่าสูงสุดที่ถูกเลือกลงมานิดหน่อยนอก sample เสมอ สิ่งที่แยกภาพลวงตาออกจาก edge จริงไม่ใช่การที่slope ติดลบ แต่คือผู้ชนะตกไปอยู่ตรงไหน: edge จริงยังคงอยู่ใกล้จุดสูงสุด (PBO 0.001) ในขณะที่คืนกลับไปนิดหน่อย; ภาพลวงตานั่งอยู่บนเส้นโยนเหรียญ (PBO 0.463 เฉลี่ย, 0.573 บน seed นี้) ผู้ชนะของมันไม่มีโอกาสอยู่เหนือ out-of-sample median มากไปกว่าอยู่ใต้มันเลย อ่าน slope เพื่อดูว่า shrinkageมากแค่ไหน; อ่าน PBO เพื่อดูว่ามัน generalize ได้หรือไม่ ภาพลวงตาล้มเหลวทั้งสองอย่าง
นี่คือเหตุผลที่ PBO สมควรมีที่ยืนข้าง ๆ backtest แบบดิบ in-sample Sharpe ที่ 2.33 ไม่ใช่คำโกหก — กลยุทธ์นั้นทำมันได้จริง ใน sample บน random walk นั้น มันคือselectionที่แต่งตัวด้วยกฎที่คุ้นเคยบน grid ที่ดูสมจริง และไม่ว่าคุณจะจ้อง equity curve นานแค่ไหนก็ไม่มีทางเห็นมัน มีแค่การให้คะแนนกระบวนการเท่านั้นที่เห็น
Act 5 — PBO กับ DSR: คำถามที่ซื่อสัตย์สองข้อ ที่ราบสูงเดียว

PBO และ Deflated Sharpe Ratio คือสองครึ่งของ honesty check เดียวกัน และพวกมันไม่ได้ซ้ำซ้อนกัน — พวกมันสอบสวนวัตถุที่ต่างกัน:
| Deflated Sharpe Ratio (DSR) | Probability of Backtest Overfitting (PBO) | |
|---|---|---|
| วัตถุที่ขึ้นศาล | ผู้ชนะ | กระบวนการ selection |
| คำถาม | Sharpe ตัวนี้เกินกว่าที่ความโชคดีจะซื้อได้ข้าม N trial ไหม? | การเลือกตัวที่ดีที่สุดแบบ in-sample generalize นอก sample ได้ไหม? |
| วิธี | parametric — deflate significance threshold | non-parametric — resample train/test split ทั้งหมด C(S, S/2) |
| ค่า Null | DSR ≈ 0.5 (ผู้ชนะเพิ่งเท่ากับ noise ceiling พอดี) | PBO ≈ 0.5 (ผู้ชนะเป็นการโยนเหรียญนอก sample) |
| คุณต้องการ | DSR ใกล้ 1 | PBO ใกล้ 0 |
| ต้องการจำนวน trial N ไหม? | ต้องการ — และ grid ที่ correlated ทำให้ N คลุมเครือ | ไม่ต้องการ — การ resample split จัดการ dependence ได้โดยธรรมชาติ |
พวกมันขัดแย้งกันได้ด้วยซ้ำ และความขัดแย้งนั้นก็เป็น diagnostic DSR ถูกหลอกได้โดย grid ที่correlatedให้ over-deflate (กับดักที่ Act สุดท้ายของบทความ DSR พูดถึงทั้งหมด — 640 cell ที่ correlated กันไม่ใช่ 640 trial ที่เป็นอิสระ และการป้อนจำนวนดิบเข้าไปทำให้ noise ceiling พองเกินจริง) PBO ไม่เคยนับ trial เลย มัน resample return matrix จริง ดังนั้น grid correlation จึงถูกฝังอยู่ใน split โดยอัตโนมัติ ในทางกลับกัน PBO บอกคุณว่ากระบวนการgeneralize ได้หรือไม่ แต่ไม่บอกว่าผู้ชนะผ่าน hurdle rate หรือเปล่า — การค้นหาสามารถมี PBO ต่ำและยังคงเลือกอะไรบางอย่างที่ out-of-sample Sharpe ของมัน แม้จะอยู่เหนือ field median อย่างน่าเชื่อถือ แต่ก็เล็กเกินกว่าจะเทรดได้ DSR ตั้งราคาให้ผู้ชนะ; PBO ตั้งราคาให้กระบวนการ รันทั้งคู่

มีสัญชาตญาณเชิงเรขาคณิตอยู่ข้างใต้ทั้งหมดนี้ และมันคือสิ่งที่มีประโยชน์ที่สุดที่ควรเก็บกลับไป edge จริงคือที่ราบสูง; overfitting คือหนามแหลม เมื่อ effect จริงขับเคลื่อน grid ของคุณ configuration ที่ดีจะจับกลุ่มกัน — fast=3/slow=55 ใช้ได้ และเพื่อนบ้านของมันก็ใช้ได้เช่นกัน เพราะทั้งหมดกำลัง sample สัญญาณเดียวกันที่อยู่ข้างใต้ ที่ราบสูงนั้นทนทานต่อ resampling: ไม่ว่าคุณจะ train บนบล็อกไหน 8 จาก 16 บล็อก ผู้ชนะแบบ in-sample ก็ถูกดึงมาจากภูมิภาคกว้าง ๆ เดียวกัน และภูมิภาคนั้นก็ยังอยู่บนสุดนอก sample split จำนวนมากเห็นตรงกัน → PBO ต่ำ เมื่อ overfitting ขับเคลื่อน grid ของคุณ "ผู้ชนะ" คือหนามแหลมโดดเดี่ยว — cell ตัวหนึ่งที่บังเอิญเข้ากับ noise ของ training block ล้อมรอบด้วยเพื่อนบ้านที่ธรรมดา ๆ หนามแหลมนั้นเปราะบาง: train/test split ที่ต่างกันจะสวมมงกุฎให้หนามแหลมโดดเดี่ยวตัวอื่น และไม่มีตัวไหนรอดไปถึง test set เลย split ไม่เห็นตรงกัน → PBO ≈ 0.5 นี่คือบทเรียนเดียวกับที่การศึกษา plateau-analysis ของเราไปถึงจากฝั่ง parameter-map; PBO ก็คือความแตกต่างระหว่าง plateau กับ spike นั่นเอง แต่วัดข้ามการ resample แบบสมมาตรทุกแบบของข้อมูลคุณพร้อมกันในคราวเดียว
มันยังอธิบายด้วยว่าทำไม CSCV ถึงเอาชนะ split แบบ walk-forward ที่นักปฏิบัติใช้เป็นค่าเริ่มต้น walk-forward ให้คุณตัด past→futureเพียงครั้งเดียวและคำตัดสินเดียว; CSCV ให้คุณตัดแบบสมมาตร 12,870 ครั้งและถามว่าผู้ชนะรอดทุกครั้งไหม หนามแหลมสามารถรอดจากการตัดแบบสุ่มครั้งเดียวได้ด้วยความโชคดี; แต่มันรอด 12,870 ครั้งไม่ได้ (Combinatorial Purged Cross-Validation ของ López de Prado หรือ CPCV ขยายแนวคิดนี้พอดีด้วยการ purging และ embargoing เพื่อกำจัด label-leakage ที่ plain resampling อาจเจอภายใต้ serial dependence — ขั้นถัดไปตามธรรมชาติเมื่อ label ของคุณทับซ้อนกัน) คำเตือนเชิงโครงสร้างเดียวกันนี้ร้อยเรียงอยู่ตลอดทั้งซีรีส์: metric ที่คุณ optimize เลือกกลยุทธ์ของคุณอย่างลับ ๆ (objective-function design), การรั่วไหลหนึ่งแท่งเทียนผลิต Sharpe 15 จาก noise (look-ahead bias), การค้นหาแบบ multiple-testing ผลิต Sharpe 1.63 จาก noise (DSR) — และที่นี่ กระบวนการ selection ที่ resample แล้วผลิตผู้ชนะที่ไร้ค่าที่มีแค่ PBO เท่านั้นที่เปิดโปงได้
หมายเหตุความซื่อสัตย์
ข้อควรระวังสี่ข้อ กล่าวอย่างตรงไปตรงมา เพราะการศึกษาแบบควบคุมจะได้ข้อสรุปมาก็ต่อเมื่อระบุขอบเขตของมันเท่านั้น
- data-generating process เป็น synthetic — โดยตั้งใจ iid Normal noise สำหรับ null, planted-Sharpe field สำหรับการไล่ edge, และ moving-average grid บน pure random walk สำหรับกับดัก ไม่มีตัวไหนเป็นข้ออ้างเรื่องความสมจริงของตลาด; แต่ละตัวถูกเลือกมาเพื่อcontrolled ground truth เราพิสูจน์ได้ว่า PBO อ่าน 0.5 บน "ไม่มีฝีมือ" และ 0 บน "ฝีมือจริง" ก็ต่อเมื่อสร้างข้อมูลที่เรารู้ว่าอันไหนเป็นอันไหนเท่านั้น ผลตอบแทนจริงมี fat tail, autocorrelated และ non-stationary; สิ่งที่ส่งมอบที่นี่คือdiagnostic ที่ผ่านการ calibrate ไม่ใช่กลยุทธ์
- null ของ PBO คือ 0.5 และนั่นคือ feature ไม่ใช่ quirk ระบุมันทุกครั้งที่คุณรายงาน PBO เพราะครึ่งหนึ่งของผู้อ่านคุณจะปฏิบัติกับ 0.5 เหมือนเป็น "ปลอดภัยครึ่งหนึ่ง" ไม่งั้น การค้นหาที่ไม่มี out-of-sample skill นั่งอยู่ที่ 0.5; edge จริงดันมันไปที่ 0 ไม่มีการอ่าน PBO ≈ 0.5 แบบ "บริสุทธิ์" — มันคือคำตัดสินแบบ overfit เต็มขั้น
- PBO > 0.5 คือย่าน "perverse" ที่เราไม่ได้บังคับให้เกิดขึ้น PBO ที่สูงกว่า0.5 อย่างเป็นระบบ หมายความว่าความสำเร็จแบบ in-sampleทำนายความล้มเหลวนอก sample ได้อย่างจริงจัง — configuration ที่แย่ที่สุดใน IS กลายเป็นดีที่สุดใน OOS นั่นต้องการโครงสร้าง anti-persistence หรือ trading-cost ไม่ใช่แค่การไม่มี edge เฉย ๆ การค้นหาแบบ overfit ของเรานั่งอยู่ที่≈ 0.5 (0.476 สำหรับ iid noise; 0.463 เฉลี่ยสำหรับ MA grid; 0.573 บน seed ฝั่งสูงหนึ่งตัว อยู่ใน band ±0.14–0.22 แบบ Monte-Carlo ข้าม 60 matrix) ซึ่งหมายความว่า "ไม่มี out-of-sample skill" อยู่แล้ว เราไม่ได้สร้างย่าน perverse ขึ้นมา เราแค่แสดงให้เห็นว่า overfitting พาคุณไปตกอยู่บนเส้นโยนเหรียญ ซึ่งก็เป็นคำตัดสินที่ฟ้องผิดมากพออยู่แล้ว
- PBO เป็น deterministic เมื่อกำหนด matrix แล้ว มีแค่ matrix เท่านั้นที่สุ่ม สำหรับ
MและS = 16ที่กำหนดตายตัว split ทั้งC(16, 8) = 12,870ถูก enumerate อย่างครบถ้วน — ไม่มี bootstrap seed และไม่มี sampling variance ใน PBO เอง การกระจายตัวที่เรารายงาน (±0.137 บน null, ±0.223 บน MA grid) คือ varianceข้าม 60 Monte-Carlo matrix ไม่ใช่ภายใน estimator เอง Sharpe ในแต่ละฝั่งถูกประมาณบน observation ประมาณ 500 ตัว — 496 ตัวหลังจาก CSCV block truncation เนื่องจากT = 1000เมื่อแบ่งเป็น 16 บล็อกเท่ากันจะเหลือ 992 แถวที่ใช้ได้ แบ่งเป็นสองครึ่งครึ่งละ 496; เพราะ Sharpe เป็น order-invariant ลำดับของแถวภายใน train หรือ test set จึงไม่สำคัญ (มันจะสำคัญสำหรับ metric แบบ path-dependent เช่น return/drawdown ratio)
สรุปประเด็นสำคัญ
- PBO ให้คะแนนกระบวนการ selection ไม่ใช่ผู้ชนะ — และ null ของมันคือ 0.5 มันคือความน่าจะเป็นที่ configuration ที่คุณเลือกว่าดีที่สุดใน sample ตกไปอยู่ครึ่งล่างนอก sample PBO ≈ 0.5 คือการโยนเหรียญ (overfit เต็มขั้น); PBO ≈ 0 คือ selection ที่น่าเชื่อถือ คุณต้องการให้มันใกล้ศูนย์ และคุณต้องพูดออกมาดัง ๆ เพราะ 0.5 อ่านเหมือน "ปลอดภัย" สำหรับสายตาที่ไม่ได้ฝึกมา และมีความหมายตรงกันข้ามเป๊ะ
- Calibration พิสูจน์ว่ามันใช้งานได้จริง บนกลยุทธ์ zero-edge iid 200 ตัว best in-sample Sharpe ต่อปีที่ 1.98 ยุบลงเหลือ 0.06 นอก sample และ PBO = 0.476 — noise นั่งอยู่บนเส้นโยนเหรียญ ขาดทุนนอก sample 47.5% ของเวลา ปลูก edge จริง (Sharpe ต่อปี 2.38) แล้ว in-sample 3.73 รอดมาเป็น out-of-sample 2.34 ในขณะที่ PBO ลดลงเหลือ 0.001 จุดปลายสองจุด diagnostic ที่ผ่านการ calibrate หนึ่งตัว
- PBO คือเทอร์โมมิเตอร์แบบต่อเนื่อง ไล่ planted edge แล้ว PBO ลดลงแบบ monotonic — 0.52 → 0.44 → 0.21 → 0.03 → 0.001 → 0.00 ที่ true Sharpe ต่อปี 0.00 / 0.48 / 0.95 / 1.59 / 2.38 / 3.17 — โดยที่ out-of-sample Sharpe ของกลยุทธ์ที่ถูกเลือกไต่ขึ้นไปพร้อมกัน (−0.05 ไปจนถึง 3.29) มันวัดว่าคุณใกล้การโยนเหรียญแค่ไหนไม่ใช่แค่ใช่/ไม่ใช่
- กับดักที่สมจริงคือประเด็นทั้งหมด moving-average grid ที่มี 170 configs บนpure random walkเฉลี่ย in-sample Sharpe ของผู้ชนะที่ถูกเลือกไว้แค่ 0.97 สลายลงเหลือ 0.04 ด้วย PBO 0.463 — แยกไม่ออกจาก null ทางสถิติ การค้นหาที่ไม่มี edge อ่านออกมาเหมือน null บน matrix ตัวแทนหนึ่งตัวภาพลวงตาชัดเจนมาก: best in-sample Sharpe ที่ 2.33 (ตัวเลขระดับ pitch-deck), median out-of-sample Sharpe ที่ −0.22, โอกาส 63% ที่จะขาดทุนนอก sample, PBO 0.573 และ degradation slope ชันที่ −0.92 backtest ที่สวยงามพร้อม out-of-sample expectation ที่เป็นลบ มองไม่เห็นในทุกสถิติที่พิมพ์ไว้ข้าง ๆ ผู้ชนะ และเห็นได้ก็ต่อเมื่อคุณให้คะแนนกระบวนการ
- จับคู่ PBO กับ Deflated Sharpe Ratio DSR ตั้งราคาให้ผู้ชนะ (Sharpe ตัวนี้เกินความโชคดีไหม เมื่อกำหนด N trial?); PBO ตั้งราคาให้กระบวนการ (selection generalize ได้ไหม?) DSR ต้องการจำนวน trial และถูกหลอกได้ด้วย grid ที่ correlated; PBO resample matrix และไม่เคยนับ trial เลย edge จริงคือที่ราบสูงกว้างที่ split จำนวนมากเห็นตรงกัน (PBO ต่ำ); หนามแหลมโดดเดี่ยวแบบ in-sample คือ overfit (split ไม่เห็นตรงกัน, PBO ≈ 0.5) รันทั้งคู่ และอ่านที่ราบสูง
ผู้ชนะของการค้นหามีความผิดจนกว่าจะพิสูจน์ได้ว่าบริสุทธิ์ — และ PBO สอบสวนตัวการค้นหาเอง ไม่ใช่ alibi ที่มันยื่นให้คุณ มันไม่สนใจว่าผู้ชนะดูดีแค่ไหนใน sample และถามแค่ว่าการเลือกมันเป็นการตัดสินใจที่รอดจากการถูกตัดใหม่ 12,870 วิธีได้หรือไม่ เมื่อมันไม่รอด — เมื่อ Sharpe 2.33 ที่สวยงามของคุณกลายเป็นตกไปอยู่ครึ่งล่างนอก sample บ่อยพอ ๆ กับไม่ตก — คุณไม่ได้พบกลยุทธ์ คุณพบเหรียญที่โชคดีที่สุด และ PBO คือตัวเลขที่จับได้ว่ามันกำลังหมุน
การทดลองแบบเต็ม — harness สำหรับ null-calibration, การไล่เทอร์โมมิเตอร์ของ planted-edge, กับดัก random-walk grid และตัวเลขทุกตัวในบทความนี้ที่สร้างซ้ำได้จากสคริปต์ deterministic ตัวเดียว — อยู่ในเปเปอร์คู่หูที่ pbo-search.marketmaker.cc พร้อมโค้ดและข้อมูลที่ github.com/suenot/pbo-search
ผู้เขียน
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.