การทดสอบแบ็กเทสต์ที่มีประโยชน์อย่างหนึ่งไม่เกี่ยวกับการหาพารามิเตอร์ที่ดีกว่าเลย: หยุดโปรเซสไว้กลางทาง กู้คืน แล้วเล่นข้อมูลชุดเดิมต่อให้จบ เมื่อใช้อินพุตเหมือนกันและตัวจำลองการส่งคำสั่งที่ควบคุมไว้ การตัดสินใจ คำสั่งซื้อขาย และมูลค่าพอร์ตควรตรงกับการรันต่อเนื่องที่ไม่มีการหยุด
ถ้าผลไม่ตรงกัน แสดงว่าคุณพบปัญหาการจัดการสถานะ กลยุทธ์พึ่งพาสิ่งที่คุณไม่ได้บันทึกไว้หรือสร้างกลับคืนไม่ได้ การพึ่งพานี้สำคัญทุกครั้งที่มีการรันงานวิจัยต่อ เปลี่ยน worker หรือ deploy โค้ดใหม่ให้บริการ paper trading
ฉันชอบการทดสอบนี้เพราะคำตอบที่คาดหวังชัดเจนเป็นพิเศษ ไม่มีข้อโต้แย้งว่าตลาดเปลี่ยนไปหรือไม่ ทั้งสองรันได้รับข้อมูลตลาดชุดเดียวกัน
ต่อไปนี้คือวิธีรีสตาร์ตที่ผิด 3 แบบ ตัวเลขเป็นเพียงตัวอย่าง แต่ความล้มเหลวแต่ละแบบเกิดขึ้นได้แม้ในระบบที่กำหนดผลลัพธ์ได้แน่นอน
1. โหลดแท่งราคาไม่กี่แท่งแล้วคิดว่าอินดิเคเตอร์พร้อมใช้งาน
สมมติว่ากลยุทธ์ใช้อินดิเคเตอร์ exponential moving average 100 คาบ สูตรอัปเดตคือ:
alpha = 2 / 101
ema_next = alpha * close + (1 - alpha) * ema_previous
โปรเซสที่รันต่อเนื่องจะส่งต่อค่า EMA ที่สะสมมาแล้ว ส่วนโปรเซสที่รีสตาร์ตจะดึงแท่งราคา 100 แท่ง ตั้งต้น EMA ด้วยราคาปิดแท่งแรก แล้วคิดว่าอินดิเคเตอร์ 100 คาบต้องใช้ข้อมูลสังเกตการณ์ 100 จุด
สมมตินี้ทำให้สับสนระหว่างพารามิเตอร์การปรับเรียบของอินดิเคเตอร์กับหน้าต่างความจำที่มีขนาดตายตัว EMA ยังคงมีผลจากสถานะเริ่มต้นที่ค่อย ๆ ลดลง หาก EMA สองชุดเริ่มต้นต่างกัน 10 หน่วยราคา ราคาถัดจากนั้นที่เหมือนกันจะทำให้ความต่างลดลงดังนี้:
| จำนวนการอัปเดตตั้งแต่เริ่มต้น | ความต่างที่เหลืออยู่ | สัดส่วนของค่าคลาดเคลื่อนเริ่มต้น |
|---|---|---|
| 100 | 1.353 | 13.53% |
| 250 | 0.0674 | 0.674% |
| 500 | 0.000454 | 0.00454% |
คำนวณได้จาก 10 * (99 / 101)^k การดึงแท่งราคา 100 แท่งจะทำให้ได้เพียง 99 รอบการอัปเดต หากใช้ข้อมูลจุดแรกเป็นค่าเริ่มต้น
ผลเสียมักปรากฏใกล้จุดแบ่งเขตการตัดสินใจ การรันหนึ่งเห็นราคาสูงกว่า EMA แต่อีกการรันเห็นราคาต่ำกว่า ความต่างทางตัวเลขเพียงเล็กน้อยทำให้เกิดการซื้อขายเพิ่มขึ้นมาทั้งรายการ เมื่อเกิดขึ้นแล้ว ช่วงพักซื้อขาย เงินสดที่ใช้ได้ และการตัดสินใจหลังจากนั้นก็อาจคลาดเคลื่อนตามไปด้วย
บันทึกสถานะอินดิเคเตอร์แบบเวียนเกิด สถานะการเริ่มต้น และอีเวนต์ล่าสุดที่ประมวลผลไว้ หรือเล่นข้อมูลซ้ำจากสถานะเริ่มต้นที่ทราบแน่ชัดก็ได้ การวอร์มอัปนานขึ้นอาจให้ค่าประมาณที่ยอมรับได้ แต่ควรกำหนดระยะเวลาจากค่าความคลาดเคลื่อนที่ยอมรับได้อย่างชัดเจน และตรวจสอบว่าค่าคลาดเคลื่อนนั้นเปลี่ยนการตัดสินใจได้หรือไม่ “5 เท่าของคาบ” เป็นเพียงธรรมเนียม ไม่ใช่ข้อพิสูจน์
และประวัติก็ไม่ได้มีแค่อินดิเคเตอร์ เปอร์เซ็นไทล์แบบ rolling ต้องใช้หน้าต่างข้อมูลของมัน โมเดลออนไลน์อาจต้องใช้สถานะ optimizer กฎที่รอ 3 แท่งหลังขาดทุนก็ต้องจำทั้งการขาดทุนและตัวนับไว้
2. บันทึกสถานะโพซิชัน แต่ลืมคำสั่งที่ยังดำเนินการอยู่
โพซิชันเป้าหมายของคุณคือ 10 หน่วย มีคำสั่งซื้อ 10 หน่วยที่ดำเนินการแล้ว 4 หน่วย จึงเหลือค้างอยู่อีก 6 หน่วย คุณบันทึกจุดตรวจสอบโดยระบุโพซิชันเป็น 4 รีสตาร์ต แล้วส่งคำสั่งซื้ออีก 6 หน่วยที่ขาดไป
ถ้าคำสั่งส่วนที่เหลือเดิมและคำสั่งใหม่ดำเนินการครบทั้งคู่ คุณจะถือครอง 16 หน่วย
บั๊กแบบนี้ในแบ็กเทสต์มักซ่อนอยู่ เพราะการรีสตาร์ตระบบจับคู่คำสั่งโดยไม่แจ้งจะลบคำสั่งที่ยังทำงานอยู่ แต่ในการ paper trading ตัวจำลองหรือบริการภายนอกอาจยังเก็บคำสั่งเหล่านั้นไว้ โค้ดกู้คืนเดียวกันจึงทำให้ระดับความเสี่ยงต่างกัน ขึ้นอยู่กับว่าส่วนประกอบใดยังทำงานอยู่
| เมื่อรีสตาร์ต | สถานะจริง | สิ่งที่การกู้คืนจากสถานะโพซิชันอย่างเดียวมองเห็น |
|---|---|---|
| โพซิชันเป้าหมาย | 10 | 10 |
| โพซิชันที่ดำเนินการแล้ว | 4 | 4 |
| ปริมาณคำสั่งซื้อที่ยังค้าง | 6 | 0 |
| ปริมาณที่ต้องซื้อเพิ่ม | 0 | 6 |
ผลเสียคือคำสั่งซื้อขายจำนวนมากที่อธิบายไม่ได้เกิดขึ้นทันทีหลังการกู้คืน บางครั้งทำให้ความเสี่ยงเพิ่มเป็นสองเท่า บางครั้งก็ปิดโพซิชันทั้งที่คำสั่งป้องกันความเสี่ยงยังทำงานอยู่ ทำให้คำสั่งนั้นเปิดโพซิชันใหม่ได้ในภายหลัง
จุดตรวจสอบต้องบันทึกข้อมูลระบุตัวตนและสถานะวงจรของคำสั่งควบคู่กับโพซิชัน ก่อนสร้างการดำเนินการใหม่ การกู้คืนต้องกระทบยอดระเบียนเหล่านั้นกับระบบดำเนินการซื้อขาย คำสั่งที่ยังไม่ทราบผลต้องตรวจสอบ การถือว่า “ไม่มีบันทึกการตอบรับ” แปลว่า “ไม่เคยส่งคำสั่ง” คือสาเหตุที่ทำให้เกิดคำสั่งซ้ำ
รหัสคำสั่งซื้อขายฝั่งไคลเอนต์ที่คงที่ช่วยให้ค้นหาได้ว่าเกิดอะไรขึ้น โดยจะป้องกันคำสั่งซ้ำได้ก็ต่อเมื่อระบบปลายทางบังคับใช้กฎความไม่ซ้ำหรือ idempotency ตามที่ต้องการจริง ๆ ด้วย บันทึกรหัสการดำเนินการซื้อขายที่ประมวลผลแล้วด้วย เพื่อไม่ให้การเล่นซ้ำของรายการจับคู่คำสั่งเพิ่มโพซิชันซ้ำสองครั้ง
ฉันชอบหน้าจอสถานะคำสั่งซื้อขายที่ดูธรรมดา ๆ เป็นพิเศษ พอถึงวันที่ต้องรีสตาร์ต แถวเล็ก ๆ เหล่านั้นกลับกลายเป็นอินเทอร์เฟซที่น่าสนใจที่สุดในอาคารขึ้นมาทันที
3. กู้คืนโพซิชัน แล้วเริ่มบัญชี P&L ใหม่
ลองดูตัวอย่างการซื้อขาย spot ที่ไม่ใช้เลเวอเรจและไม่มีค่าธรรมเนียม เริ่มด้วยเงินสด $10,000 ซื้อ 10 หน่วยที่ราคา $100 และบันทึกจุดตรวจสอบเมื่อราคาประเมินอยู่ที่ $110
สถานะที่ถูกต้องคือเงินสด $9,000 บวกกับโพซิชันมูลค่า $1,100 รวมเป็นมูลค่าพอร์ต $10,100 หากการกู้คืนคืนค่า 10 หน่วย แต่รีเซ็ตเงินสดกลับเป็น $10,000 เดิม ระบบจะแสดง $11,100 คุณสร้างเงิน $1,000 ขึ้นมาได้ด้วยการรีสตาร์ตโปรเซส
กรณีอื่นอาจไม่ชัดเจนเท่านี้ การกู้คืนรักษามูลค่าพอร์ตไว้ได้ แต่รีเซ็ตราคาเข้าซื้อเป็น $110 มูลค่าพอร์ตโดยรวมอาจยังถูกต้อง ขณะที่การแยกกำไรขาดทุนที่รับรู้แล้วกับที่ยังไม่รับรู้เปลี่ยนไป หากเงื่อนไข stop หรือ exit อ้างอิงราคาเข้าซื้อ ทางลัดด้านบัญชีนี้ก็เปลี่ยนพฤติกรรมการซื้อขายไปด้วย
หรือระบบอาจลืมจุดสูงสุดเดิมของมูลค่าพอร์ต สมมติว่ามูลค่าพอร์ตเคยขึ้นถึง $10,600 ก่อนลดลงมาเหลือ $10,100 ค่า drawdown อยู่ที่ประมาณ 4.72% หากรีเซ็ตจุดสูงสุดระหว่างการกู้คืน กลยุทธ์ก็จะคิดว่า drawdown เป็นศูนย์ทันที การควบคุมความเสี่ยงที่อิง drawdown จึงถูกรีเซ็ตโดยไม่ได้รับอนุญาต
ผลเสียจึงอาจเป็นมูลค่าพอร์ตที่เปลี่ยนฉับพลัน ค่า drawdown ที่ดีขึ้นอย่างน่าสงสัย หรือกฎความเสี่ยงที่หยุดทำงานหลัง deploy ให้เก็บบัญชีบันทึกและสถานะของกลยุทธ์ที่ขึ้นกับการทำบัญชีไว้ ได้แก่ การเคลื่อนไหวของเงินสด โพซิชัน ต้นทุนตามหลักเกณฑ์ที่เกี่ยวข้อง ค่าใช้จ่ายสะสม และสถานะความจำของการควบคุมความเสี่ยง กระทบยอดมูลค่าพอร์ตที่กู้คืนกับบัญชีบันทึกโดยใช้เวลาประเมินมูลค่าเดียวกัน
จุดตรวจสอบต้องมีขอบเขตที่สอดคล้องกัน การบันทึกเงินสดหลังการจับคู่คำสั่ง แต่บันทึกปริมาณโพซิชันก่อนการจับคู่ครั้งนั้น จะได้สถานะที่ไม่เคยเกิดขึ้นจริง ให้บันทึกสถานะที่เกี่ยวข้องพร้อมกัน หรือบันทึกลำดับอีเวนต์แบบถาวรซึ่งนำมาสร้างสถานะใหม่ได้ เก็บตำแหน่งของอีเวนต์ไว้พร้อมสถานะนั้น เพื่อไม่ให้การกู้คืนข้ามรายการจับคู่หรือประมวลผลซ้ำสองครั้ง
ฉันจะเก็บการทดสอบนี้ไว้ในชุดเครื่องมือวิจัย: รันการเล่นข้อมูลอ้างอิงหนึ่งครั้งโดยไม่หยุด แล้วรีสตาร์ตการรันครั้งที่สองในจังหวะที่ชวนให้พลาด เช่น ระหว่างเริ่มต้นอินดิเคเตอร์ หลังจับคู่คำสั่งได้บางส่วน และขณะที่มีการใช้ขีดจำกัดความเสี่ยง ใช้ลำดับอีเวนต์เดียวกันและเก็บสถานะสุ่มของตัวจำลองไว้ เปรียบเทียบการตัดสินใจครั้งแรกหลังการกู้คืน ระเบียนคำสั่งและรายการจับคู่คำสั่ง รวมถึงเส้นทางมูลค่าพอร์ต ยอดเงินสุดท้ายที่ตรงกันเพียงอย่างเดียวอาจปกปิดข้อผิดพลาดที่หักล้างกันได้
สำหรับกรณีระบบขัดข้องหลังส่งคำสั่งแต่ก่อนรับการตอบรับ ชุดทดสอบยังต้องเก็บสถานะของบริการดำเนินการซื้อขายแยกจากโปรเซสกลยุทธ์ด้วย มิฉะนั้นมันจะลบความไม่แน่นอนที่คุณกำลังพยายามทดสอบทิ้งไป
ข้อกำหนดของกลยุทธ์รวมถึงสิ่งที่มันจดจำด้วย ระบุความจำนั้นให้ชัดพอที่คุณจะหยุดโปรเซสกลางทางของการเล่นข้อมูลซ้ำ แล้วแสดงให้เห็นได้อย่างชัดเจนว่ามันกลับมาทำงานต่ออย่างไร
← บทความทั้งหมด


