3 กันยายน 2026 · วิศวกรรมข้อมูล

นาทีที่ไม่มีอยู่จริง: ช่องว่าง การหยุดซื้อขาย และแท่งปริมาณศูนย์ในประวัติ OHLCV

นาทีที่ไม่มีอยู่จริง: ช่องว่าง การหยุดซื้อขาย และแท่งปริมาณศูนย์ในประวัติ OHLCV

ข้อมูลแท่ง 1 นาทีตลอดปีปฏิทินควรมี 525,600 แถว ข้อมูล Perp ของ BTCUSDT ที่เราดึงมาสำหรับปี 2025 มี 525,557 แถว — ขาดไป 43 นาที คิดเป็นความครบถ้วน 99.992% ส่วน Perp ของอัลต์คอยน์มิดแคปในตลาดเดียวกัน ดึงข้อมูลด้วยวิธีเดิมและเส้นทางโค้ดเดียวกัน ขาดไป 1,247 นาที: ความครบถ้วน 99.76% และข้อมูลรายนาทีของหุ้นสหรัฐฯ สำหรับปีเดียวกันมีแถวน้อยกว่าคริปโตราว 427,000 แถว ซึ่งไม่ใช่ช่องว่างเลย แต่เป็นเพราะตลาดปิด

ตัวเลข 3 ตัวนี้ไม่มีอะไรน่าตื่นเต้น ตัวเลขที่เล่าเรื่องได้เป็นพันคำคือ 43

525,600แท่ง 1 นาทีในปีที่ไม่ใช่ปีอธิกสุรทิน
0.008%ข้อมูล BTCUSDT ปี 2025 ที่ขาดหาย
61%ของนาทีเหล่านั้นอยู่ในชั่วโมงที่มีความผันผวนสูงสุด 10%

4 สิ่งที่ต่างกัน แต่ดูเหมือนช่องโหว่ใน dataframe เหมือนกันหมด

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

ประเภทสิ่งที่เกิดขึ้นจริงข้อมูลที่ได้รับวิธีรับมือที่ถูกต้อง
ไม่มีการซื้อขายตลาดเปิดอยู่ แต่ไม่มีใครยอมข้ามส่วนต่างราคาในนาทีนั้นบาง endpoint ส่งแท่งปริมาณศูนย์ (OHLC เท่ากันทั้งหมด, trades=0) บาง endpoint ไม่มีแถวเก็บไว้ นี่คือข้อมูลจริง: ไม่มีใครต้องการซื้อขาย
ตลาดซื้อขายขัดข้องระบบจับคู่คำสั่งหยุดทำงาน ไม่ว่าจะตามกำหนดการหรือไม่ไม่มีแถว บางครั้งหายไปต่อเนื่องหลายแถวระบุว่าข้อมูลไม่เป็นปัจจุบัน อย่าซื้อขายข้ามช่วงนี้
หยุดซื้อขายตราสารราคาทะลุกรอบ LULD, รอข่าว หรือมีประกาศเพิกถอนไม่มีแถว จากนั้นมีรายการซื้อขายในการประมูลเปิดตลาดอีกครั้งระบุว่าข้อมูลไม่เป็นปัจจุบัน และถือว่าการกลับมาเปิดซื้อขายเป็นจุดเปลี่ยนแบบฉับพลัน
เป็นความผิดพลาดของเราแบ่งหน้าแล้วติด rate limit, ลองใหม่แต่ทำข้อมูลหน้าหนึ่งหล่น หรือมีบั๊กเรื่องเขตเวลาที่ขอบวันในลูปไม่มีแถว แยกไม่ออกจากสาเหตุข้างต้นตรวจหาแล้วดึงข้อมูลใหม่ นี่เป็นกรณีเดียวที่เราแก้ได้

ตลาดซื้อขายแต่ละแห่งให้ข้อมูลแถวแรกในตารางนี้ไม่เหมือนกัน ซึ่งเป็นจุดที่ทำให้เกิดปัญหา endpoint แท่งเทียนของ Coinbase ข้ามช่วงเวลาที่ไม่มีรายการซื้อขายไปเลย ทำให้ประวัติของคู่ที่มีสภาพคล่องต่ำเต็มไปด้วยช่องว่างจริง ๆ ส่วน klines ของ Binance มักส่งแท่งสังเคราะห์ที่มีปริมาณ 0 และกำหนด open=high=low=close ให้เท่ากับรายการซื้อขายก่อนหน้า ข้อเท็จจริงพื้นฐานเดียวกัน แต่ข้อมูลออกมาคนละรูปแบบ และตัวโหลดที่ปรับดัชนีให้เต็มทุกนาทีจะเปลี่ยนรูปแบบหนึ่งเป็นอีกแบบโดยไม่บอกคุณ ตรวจสอบก่อนว่าตลาดซื้อขายของคุณทำงานแบบไหน แล้วค่อยเขียนตัวเติมช่องว่าง

นาทีที่หายไป 1,247 นาทีในอัลต์คอยน์เกือบทั้งหมดเป็นกรณีประเภทที่ 1: สมุดคำสั่งซื้อขายบางในวันอาทิตย์ เวลา 04:00 UTC ไม่มีใครซื้อขาย น่ารำคาญ แต่เข้าใจได้ง่าย และแทบไม่ก่อปัญหา เพราะกลยุทธ์ก็คงไม่ซื้อขายช่วงนั้นอยู่แล้ว นั่นแหละคือเหตุผลที่ผมหยุดสนใจตัวเลขนี้ แล้วกลับไปดู 43 นาที

43 นาทีนั้นไม่ได้กระจายอยู่ทั่วไป

ถ้าข้อมูลที่หายกระจายสม่ำเสมอ นาทีที่หายไป 43 นาทีตลอดทั้งปีจะปรากฏเป็นจุดเดี่ยว ๆ 43 จุด ห่างกันจุดละ 8 วันครึ่ง เป็นข้อผิดพลาดเล็กน้อยที่แทบไม่มีผล แต่นั่นไม่ใช่สิ่งที่เราเจอ นาทีที่หายไปเกิดเป็น 6 ช่วง: ช่วงหนึ่งยาว 19 นาทีติดต่อกัน อีกช่วงยาว 11 นาที สองช่วงยาว 4 นาที และอีกสองช่วงเป็นคู่ มี 6 เหตุการณ์ ไม่ใช่อุบัติเหตุ 43 ครั้ง

และเหตุการณ์เหล่านี้สัมพันธ์กับสิ่งที่คุณสนใจ ตลาดซื้อขายล่มเมื่อมีภาระงานสูง และภาระงานก็สูงตอนที่ราคากำลังเคลื่อนไหว ผมแบ่งทุกชั่วโมงของปีตามความผันผวนที่เกิดขึ้นจริง แล้วตรวจดูว่านาทีที่หายไปอยู่ช่วงไหน: 61% อยู่ในกลุ่ม 10% ที่ผันผวนสูงสุด ความน่าจะเป็นโดยรวมที่นาทีใดนาทีหนึ่งจะหายไปคือ 0.008% แต่ถ้าอยู่ในชั่วโมงที่มีความผันผวนสูงสุด 10% ความน่าจะเป็นจะอยู่ที่ราว 0.05% — สูงขึ้น 6 เท่า แถมยังกระจุกตัวอีกด้วย

ดังนั้นตัวชี้วัดความครบถ้วนบนแดชบอร์ดคุณภาพข้อมูลจึงวัดผิดจุด ค่า 99.992% ฟังดูเหมือนชุดข้อมูลที่ไม่ต้องกังวลแล้ว แต่สิ่งที่ตัวเลขนี้บอกจริง ๆ คือข้อมูลครบถ้วนในช่วงที่กลยุทธ์ไม่ทำอะไร และมีช่องโหว่ในช่วงที่กลยุทธ์ทำงานเต็มที่ ระบบโมเมนตัมที่ส่งคำสั่งเมื่อความผันผวนขยายตัวมีโอกาสเจอช่องว่างสูงกว่าที่ตัวเลขรวมบอกอย่างมีนัยสำคัญ และจะเจอช่องว่างระหว่างที่ถือสถานะอยู่

ผลของการเติมค่าจากข้อมูลก่อนหน้าในอีก 3 บรรทัดถัดมา

นี่คือปัญหาที่ทำให้ผมเขียนบทความนี้ ลองดูช่วงที่หายไป 19 นาที วิธีจัดการมาตรฐานคือปรับดัชนีให้ครบทุกนาที เติมค่า OHLC ด้วยราคาปิดล่าสุด แล้วกำหนดปริมาณเป็นศูนย์ ตอนนี้ข้อมูลต่อเนื่องแล้ว และตัวชี้วัดของคุณก็ทำงานได้โดยไม่มี NaN ให้เห็น

แท่งทั้ง 19 แท่งมี high == low == close ค่า True range จึงเป็นศูนย์ทุกแท่ง ค่า ATR(14) ที่คำนวณในช่วงนี้ ซึ่งก่อนตลาดขัดข้องอยู่ที่ประมาณ 240 USDT จะค่อย ๆ ลดลงจนเหลือราว 34 เมื่อตลาดกลับมา เพราะค่าเฉลี่ยทั้งหมดถูกฉุดลงด้วยแท่งจริงเพียง 5 แท่งที่ยังเหลืออยู่ในช่วงนั้น จากนั้นป้อนค่านี้เข้าเครื่องคำนวณขนาดสถานะที่ปรับตามความผันผวน ซึ่งเป็นรูปแบบปกติของ size = risk_budget / ATR ขนาดสถานะเพิ่มขึ้น 7 เท่า

แท่งจริงถัดไปคือรายการซื้อขายตอนตลาดกลับมาเปิด และไม่ใช่แท่งที่สงบนิ่ง ในกรณีของเรา ราคาเปิดห่างจากราคาปิดก่อนตลาดขัดข้อง 1.8% การทดสอบย้อนหลังกลับเปิดสถานะขนาด 7 เท่าอย่างไม่สะทกสะท้าน ท่ามกลางช่องว่างราคา 1.8% ด้วยราคาที่ไม่อาจซื้อขายได้จริง และไม่มีใครเสนอราคานั้น การซื้อขายสังเคราะห์ครั้งเดียวนี้มีมูลค่ามากกว่า P&L ที่เกิดขึ้นจริงตลอด 1 เดือนในเส้นทุนสะสม แถมยังไปในทิศทางผิด — และมันเกิดขึ้นจากบรรทัดทำความสะอาดข้อมูลที่เขียนขึ้นเพียงเพื่อจัด dataframe ให้เรียบร้อย

การลบแถวแทนการเติมค่าก็ไม่ใช่วิธีแก้ แต่เป็นบั๊กเดิมที่เปลี่ยนหน้าตาไป ลบแถวแล้ว lookback ที่ใช้ดัชนีจำนวนเต็มก็ทำให้เข้าใจผิด: ตอนนี้ "EMA 20 แท่ง" ครอบคลุมเวลา 39 นาทีตามนาฬิกาจริงในช่วงตลาดขัดข้อง ผลตอบแทนระหว่างแท่งที่ข้ามช่วงนั้นกลายเป็นการกระโดด 1.8% เต็ม ๆ ซึ่งถูกนับว่าเกิดขึ้นใน 1 นาที และค่าประมาณความผันผวนต่อแท่งก็อ่านเป็นเหตุการณ์ระดับ 60 ซิกมา ไม่มีอะไรแจ้งเตือนคุณ ดัชนียังคงเรียงจากน้อยไปมาก

การปรับความถี่ข้อมูลทำให้ปัญหาหายไปจากสายตา

งานวิจัยส่วนใหญ่ไม่ได้ใช้แท่ง 1 นาที แต่ใช้ข้อมูลที่รวมช่วงเวลาแล้ว และการรวมข้อมูลก็กลบปัญหาไว้ เมื่อปรับเป็น 5 นาที ช่องว่าง 19 นาทีจะกลายเป็น 4 แท่ง โดยแท่งแรกและแท่งสุดท้ายมีข้อมูลเพียงบางส่วน Pandas จะคำนวณ OHLC ที่ดูสมเหตุสมผลจาก 2 นาทีที่ยังเหลือ แล้วติดป้ายเหมือนกับแท่งที่สร้างจากข้อมูลครบ 5 นาที ไม่มีอะไรในผลลัพธ์ที่บอกความแตกต่าง

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

นโยบายที่เรามีตอนนี้

แนวทางที่เอเจนต์ของเราใช้ในตอนนี้ เรียงตามลำดับ:

  1. ห้ามปรับดัชนีโดยไม่แจ้งให้ทราบเด็ดขาด ตัวโหลดจะสร้างรายการช่องว่าง — เวลาเริ่มต้น เวลาสิ้นสุด ความยาว และประเภทจาก 4 ประเภทที่คาดว่าเป็นสาเหตุ หากช่วงนาทีที่หายไปสั้นกว่า 3 แท่ง และปริมาณในแท่งโดยรอบต่ำ ให้ถือว่าเป็นนาทีที่ไม่มีการซื้อขาย หากนานกว่านั้นในช่วงที่ตลาดทำงาน ให้ถือว่าตลาดขัดข้องจนกว่าจะพิสูจน์ได้ว่าไม่ใช่
  2. ดึงข้อมูลใหม่ก่อนตีความ ช่องว่างครึ่งหนึ่งในระยะแรกของเรามาจากบั๊กแบ่งหน้า การดึงข้อมูลครั้งที่ 2 จาก endpoint อื่นหรือผู้ให้บริการรายอื่นช่วยแยกกรณีประเภทที่ 4 และลดขอบเขตปัญหาก่อนต้องตัดสินใจเรื่องอื่น
  3. ใช้เงื่อนไขตรวจข้อมูลเก่า แทนการเติมช่องว่าง กลยุทธ์จะได้รับอินพุต data_age พร้อมกฎตายตัว: ห้ามเปิดสถานะใหม่เมื่อรายการซื้อขายจริงล่าสุดเก่ากว่า N แท่ง และให้ปิดสถานะที่เปิดค้างเมื่อกลับมาเปิดตลาดด้วย market order เท่านั้น โดยกำหนดส่วนเผื่อความเสี่ยงจากช่องว่างราคาไว้อย่างชัดเจน การจำลองการซื้อขายที่เกิดขึ้นไม่ได้จริงเลวร้ายกว่าการไม่มีรายการซื้อขาย
  4. ให้ตัวชี้วัดเห็น NaN แทนข้อมูลแต่งขึ้น ราคาที่เติมค่าจากข้อมูลก่อนหน้าจะไม่เข้าสู่ชั้นคุณลักษณะ หากคำนวณ ATR ไม่ได้ ก็ให้ถือว่ายังไม่มีค่า และไม่มีค่าหมายถึงไม่มีสถานะ แจ้งข้อผิดพลาดให้ชัดเจนดีกว่าปล่อยให้ขนาดสถานะเพิ่ม 7 เท่าโดยไม่รู้ตัว
  5. รายงานผลการดำเนินงานโดยคำนึงถึงช่องว่าง การทดสอบย้อนหลังทุกชุดที่เราเผยแพร่จะแสดง P&L ทั้งกรณีรวมและกรณีตัดรายการซื้อขายที่อยู่ใกล้ช่วงตลาดขัดข้องออก หากรายการเหล่านั้นเป็นตัวสร้างผลลัพธ์ ผลลัพธ์นั้นก็เป็นสิ่งที่เกิดจากข้อมูล ไม่ใช่จากกลยุทธ์

วิธีตรวจสอบแบบเร็วที่ทำได้วันนี้: จัดกลุ่มนาทีที่หายไปเป็นช่วงต่อเนื่อง แล้วตรวจว่ามีการเปิดหรือปิดรายการซื้อขายในการทดสอบย้อนหลังของคุณกี่เปอร์เซ็นต์ภายใน 30 นาทีจากขอบช่วงที่ข้อมูลหาย หากต่ำกว่า 1% ช่องว่างเหล่านี้ก็น่าจะไม่มีผลอะไรมาก หากอยู่ที่ 5% ขึ้นไป เส้นทุนสะสมของคุณก็มีส่วนที่สะท้อนเรื่องตลาดซื้อขายหยุดทำงาน

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

ช่องว่าง OHLCVวิศวกรรมข้อมูลการทดสอบย้อนหลังการปรับความถี่ข้อมูลฟิวเจอร์สคริปโต
แชร์XLinkedInFacebookRedditHacker NewsWhatsAppTelegramEmail
← บทความทั้งหมด