27 สิงหาคม 2026 · วิศวกรรมข้อมูล

จักรวาลสินทรัพย์ของคุณคือไทม์แมชชีน: อคติจากการอยู่รอดในการทดสอบย้อนหลังของคริปโทเพอร์เพทชวล

จักรวาลสินทรัพย์ของคุณคือไทม์แมชชีน: อคติจากการอยู่รอดในการทดสอบย้อนหลังของคริปโทเพอร์เพทชวล

คุณส่งโน้ตบุ๊กมาให้ผมเมื่อคืนวันอาทิตย์ และผมเปิดมันค้างไว้บนจอที่สองตั้งแต่นั้น โมเมนตัมแบบภาคตัดขวาง เลือกเพอร์เพทชวล Binance USDⓈ-M 150 อันดับแรกตามปริมาณซื้อขายรายวันเป็นดอลลาร์ในช่วง 30 วัน ปรับพอร์ตทุกสัปดาห์ ซื้อกลุ่ม 10% บนสุดและขายชอร์ตกลุ่ม 10% ล่างสุด ช่วง 2021-01 ถึง 2026-06 ค่า Sharpe 2.31, การขาดทุนสูงสุด 14.2% และแบบจำลองต้นทุนที่ดูสมเหตุสมผลสำหรับผม: เข้าแบบ taker ออกแบบ taker คิด funding สะสมตามแต่ละช่วงเวลา และมีองค์ประกอบ slippage ที่ปรับตามขนาดคำสั่งของคุณเมื่อเทียบกับสมุดคำสั่ง คุณทำส่วนที่ยากได้ถูกต้องแล้ว จากนั้นคุณถามว่าทำไมการทดลองเทรดด้วยเงินจริงในบัญชีจำลองตลอด 6 สัปดาห์จึงให้ผลนิ่งสนิท และความได้เปรียบเสื่อมลงหรือเปล่า

มันไม่ได้เสื่อมลง เพราะมันไม่เคยอยู่ในการทดสอบย้อนหลังเลย ดูเซลล์ที่ 4:

info = requests.get(BASE + "/fapi/v1/exchangeInfo").json()
symbols = [s["symbol"] for s in info["symbols"]
           if s["status"] == "TRADING" and s["quoteAsset"] == "USDT"]

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

2.31 → 0.74Sharpe: รายชื่อผู้รอดเทียบกับข้อมูล ณ เวลานั้น
~1 ใน 5ของจักรวาลสินทรัพย์ปี 2021 ไม่มีอยู่แล้ว
380 KBไฟล์ snapshot exchangeInfo รายวันแบบ gzipped

สิ่งที่ endpoint ไม่ยอมบอกคุณ

ใน exchangeInfo ไม่มีประวัติ ไม่มีพารามิเตอร์ asOf ไม่มีคลังข้อมูลเก่า ไม่มีบันทึกการเปลี่ยนแปลง มันเป็นภาพถ่ายของตอนนี้ และ Binance ไม่เคยรับปากอย่างอื่น เมื่อสัญญาถูกถอดออกจากรายการ รายการนั้นก็ถูกลบจากคำตอบ และในมุมของ API ticker นั้นก็หยุดมีอยู่ Binance เคยลิสต์เพอร์เพทชวล USDⓈ-M มากกว่า 600 รายการตั้งแต่ปี 2019 และถอดออกไปมากกว่าร้อยรายการ BTS, COCOS, TOMO, RAY, FTT, SC และสัญญาอัลต์คอยน์อีกมากมายจากฤดูกาลอัลต์ปี 2021 ที่เคยรุ่งโรจน์อยู่ไตรมาสเดียว ก่อนปริมาณจะค่อย ๆ หายไปจนแพลตฟอร์มถอดออก

ลองถามตัวเองว่าตัวคัดกรองโมเมนตัม 30 วันชอบชื่อแบบไหน ไม่ใช่ BTC แต่เป็นเหรียญที่เพิ่งพุ่งขึ้น 3 เท่าจากแรงซื้อช่วงลิสต์และกระแสบน Twitter กลุ่มเหรียญแบบนั้นทับซ้อนกับกลุ่มที่ท้ายที่สุดจะถูกถอดออกอย่างมาก และตัวกรองจักรวาลของคุณก็ลบส่วนที่ทับซ้อนกันนั้นทิ้งไป

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

อีกด้านของไทม์แมชชีน: สัญลักษณ์ที่ยังไม่มีอยู่ในตอนนั้น

ฟีเจอร์ของคุณคือผลตอบแทน 90 วัน แต่หน้าต่าง rolling ของคุณใช้ min_periods=20 เพราะคุณตั้งค่าไว้ครั้งเดียวเพื่อไม่ให้ช่วง warmup กินข้อมูลไตรมาสแรกของตัวอย่าง แล้วก็ไม่ได้กลับมาทบทวนอีก สัญญาที่เพิ่งเริ่มซื้อขายเมื่อ 21 วันก่อนจึงได้คะแนนโมเมนตัมจากการเคลื่อนไหวของราคาหลังลิสต์เพียง 3 สัปดาห์ ติดกลุ่ม 10% บนสุดแทบทุกครั้งที่การลิสต์ไปได้สวย แล้วเข้าพอร์ตของคุณ

ส่วนนี้พอจะถือว่าสมเหตุสมผลได้ แต่สิ่งต่อไปนี้ไม่ใช่: ผู้ให้บริการข้อมูลของคุณเติมข้อมูลย้อนหลังให้บางสัญลักษณ์ด้วยข้อมูล spot หรือดัชนีก่อนที่เพอร์เพทชวลจะมีอยู่ ทำให้สัญญาบางรายการมีประวัติ kline ที่เก่ากว่า onboardDate ของตัวเอง คุณตรวจเรื่องนี้ได้ในเวลาประมาณ 1 นาที นำตารางราคามา join กับวันที่เริ่มลิสต์ แล้วนับแถวที่อยู่ก่อนวันลิสต์ ในข้อมูลของคุณมี 41 สัญลักษณ์ที่มีแท่งราคาก่อนลิสต์ และหนึ่งในนั้นในช่วงฤดูร้อนปี 2023 สร้างกำไร 6% ในสัปดาห์เดียวให้เส้น equity จากสถานะในสัญญาที่จะยังไม่มีอยู่ไปอีก 9 วัน

ticker string ไม่ใช่รหัสประจำตัว แต่มันเป็นป้ายชื่อที่แพลตฟอร์มให้ยืมใช้ บางครั้งก็ให้ใช้ซ้ำสองครั้ง

เรื่องนี้พาไปถึงการเปลี่ยนชื่อ MATICUSDT กลายเป็น POLUSDT ส่วน FTMUSDT กลายเป็น SUSDT หลังแปลงสัดส่วน 1:1 เหตุการณ์ LUNA ในเดือนพฤษภาคม 2022 ทิ้ง LUNCUSDT ไว้ แล้วภายหลังก็มี LUNAUSDT ตัวใหม่เอี่ยมที่ใช้ราก ticker เดียวกับสินทรัพย์ที่สูญมูลค่าไปแทบทั้งหมด หาก loader ของคุณใช้สตริง symbol เป็นคีย์แล้วนำไฟล์ที่เจอทั้งหมดมาต่อกัน คุณก็มีอย่างน้อยหนึ่งอนุกรมที่มีจุดไม่ต่อเนื่องซึ่งไม่ได้เกิดจากการเคลื่อนไหวของราคา แล้วฟีเจอร์โมเมนตัมของคุณจะอ่านจุดไม่ต่อเนื่องนั้นเป็นสัญญาณที่แรงที่สุดในกลุ่มสินทรัพย์

ข้อมูลอื่น ๆ ที่ค่อย ๆ เปลี่ยนไปโดยที่คุณไม่รู้ตัว

เมื่อยอมรับแล้วว่ารายชื่อสัญลักษณ์เปลี่ยนไปตามเวลา เหตุผลเดียวกันนี้ก็ใช้กับทุกฟิลด์อื่นในคำตอบนั้น คุณกำลังใช้ค่าของวันนี้กับทุกฟิลด์

ฟิลด์เปลี่ยนแปลงอย่างไรเกิดปัญหาอะไร
statusTRADING → SETTLING → ถูกถอดออกอคติจากการอยู่รอด; จำลองออกจากสถานะที่ราคาปิดซึ่งไม่เคยมีการซื้อขายจริง
onboardDateมีรายการใหม่ทุกสัปดาห์; ไม่มีข้อมูลสำหรับสัญลักษณ์ที่ตายไปแล้วซื้อขายสัญญาก่อนวันที่มีอยู่จริง
tickSize / stepSizeปรับใหม่เมื่อระดับราคาเปลี่ยนการปัดเศษคำสั่ง และราคาลิมิตที่ถูกปฏิเสธในเวลานั้น
minNotionalปรับเพิ่มขึ้นตามเวลาในสมุดคำสั่งที่บางสถานะขนาดเล็กที่ router จริงของคุณจะปฏิเสธ
fundingIntervalHoursใช้ 8h มาหลายปี แล้วหลายสัญลักษณ์เปลี่ยนเป็น 4h หรือ 1hคำนวณ carry ผิดไป 2–3× กับอัลต์คอยน์กลุ่มเดียวกับที่ตัวคัดกรองของคุณเลือกถือ
leverage bracketsมีการแก้ไขขั้นและ maintenance marginการจำลอง liquidation และความสามารถในการใช้ margin

กรณี funding กระทบหนักที่สุดสำหรับกลยุทธ์ของคุณ ลูปการสะสม funding สมมติว่ามีการจ่าย 3 ครั้งต่อวันตลอดประวัติ มีสัดส่วนสำคัญของพอร์ตอัลต์ที่เปลี่ยนไปใช้ funding ทุก 4 ชั่วโมง และสถานะขายชอร์ตในเหรียญที่มี funding สูงคือแหล่งที่มาของ PnL จำลองส่วนใหญ่ คุณไม่ได้คลาดเคลื่อนแค่จากการปัดเศษ แต่คลาดเคลื่อนเป็นเท่าตัว

การถอดเหรียญออกเป็นเหตุการณ์หนึ่ง และคุณไม่ได้จำลองมัน

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

ถ้าคุณไม่เคยเก็บ exchangeInfo ไว้ ก็ยังไม่สายเกินไป public dump ที่ data.binance.vision/data/futures/um/monthly/klines/ ยังคงเก็บไดเรกทอรีของสัญลักษณ์ที่ถูกถอดออกไปนานหลัง API ลืมมันไปแล้ว ไล่ดูรายการไดเรกทอรี แล้วใช้ไฟล์รายเดือนแรกสุดและท้ายสุดของแต่ละสัญลักษณ์เพื่อประมาณช่วงเวลาลิสต์และถอดออกได้ โดยไม่ต้องพึ่งผู้ให้บริการข้อมูล นี่เป็นการสร้างประวัติขึ้นใหม่ ไม่ใช่บันทึกจริง และกู้คืน tick size หรือช่วงเวลา funding ไม่ได้ แต่จะบอกได้ว่าสินทรัพย์ใดมีอยู่เมื่อใด ซึ่งคิดเป็น 80% ของสิ่งที่คุณต้องใช้ในวันจันทร์

สิ่งที่ผมอยากให้คุณสร้างก่อนกลับไปแตะสัญญาณอีกครั้ง

  1. ตั้ง cron ให้ดึง exchangeInfo จากทุกแพลตฟอร์มที่คุณทำวิจัยทุกวัน แล้วเขียนลง object storage โดยใช้วันที่เป็นคีย์ เมื่อ gzipped แล้วมีขนาดไม่ถึงครึ่งเมกะไบต์ เก็บไว้ 10 ปีมีค่าใช้จ่ายเล็กน้อยมากใน S3 แต่ให้ความสามารถในการทำวิจัยที่คุณซื้อทีหลังไม่ได้
  2. สร้าง asset master จาก snapshot เหล่านั้น: 1 แถวต่อ (venue, symbol, valid_from, valid_to) พร้อมฟิลด์ครบชุด เปรียบเทียบ snapshot ที่อยู่ติดกันเพื่อสร้างตารางนี้ และให้ถือว่าการเปลี่ยนแปลงใด ๆ ของฟิลด์เป็นแถวใหม่
  3. สร้างฟังก์ชันจักรวาลสินทรัพย์ที่บังคับให้ระบุ timestamp universe(ts)ต้องใช้ข้อมูล ณ เวลานั้นเสมอuniverse()อย่าใช้ค่าปัจจุบันเป็นค่าเริ่มต้น ทำให้เรียกใช้วิธีผิดพลาดไม่ได้ เพื่อไม่ให้ใครก็ตาม รวมถึงตัวคุณในอนาคตตอนตี 1 เผลอกลับไปใช้รายชื่อผู้รอด
  4. เพิ่ม assertion ใน data loader เพื่อตรวจข้อมูลก่อนลิสต์: ต้องไม่มีแท่งราคาอยู่ก่อน onboard_ts ลบ 1 วัน ให้การรันล้มเหลว อย่าแค่เตือน
  5. สร้างรหัสเครื่องมือภายในที่คงเดิมแม้มีการเปลี่ยนชื่อ และลดบทบาท ticker ให้เป็นเพียงแอตทริบิวต์ จับคู่ POL กับ MATIC ไว้ใน ID เดียว และทำเครื่องหมายการเปลี่ยนหน่วยเพื่อให้ตรรกะความต่อเนื่องปฏิเสธการเชื่อมข้อมูลข้ามช่วงดังกล่าว

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

ป.ล. เรื่องนี้ไม่ใช่ปัญหาเฉพาะของคริปโท แค่เห็นชัดกว่าเท่านั้น นักวิจัยหุ้นต่อสู้กับผลตอบแทนจากหุ้นที่ถูกถอดออกและ ticker ที่นำกลับมาใช้ใหม่มาตั้งแต่ CRSP เริ่มเผยแพร่ข้อมูล ส่วนตลาดพยากรณ์ไปไกลสุดทาง: สัญญาทุกฉบับหมดอายุตามการออกแบบ ดังนั้นจักรวาลจึงประกอบด้วยการลิสต์และการสิ้นสุดทั้งหมด หากคุณนำตัวคัดกรองนี้ไปใช้กับ Kalshi เมื่อไร ให้สร้าง asset master ก่อน เพราะประวัติที่นั่นไม่มีรูปแบบอื่น

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