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.
บทความ
ผลลบที่ซื่อสัตย์: แบ็คเทสหลายหมื่นครั้ง ห้าเหรียญหลัก ไม่พบ Edge ที่แข็งแกร่ง
บทสรุปของซีรีส์เรื่อง search-and-overfit ที่จบลงด้วยผลลบ — และเป็นผลลบที่ถูกต้อง การค้นหาแบบ dual-timeframe บนสัญลักษณ์เดียว (ETHUSDT) พบคอนฟิกที่ทำได้ +16.35% แบบ out-of-sample และ +2.62% บน holdout ที่ไม่เคยถูกแตะต้อง แต่ Deflated Sharpe Ratio ซึ่งคำนวณจากการทดลองราว 37,000 ครั้ง กลับลดค่านั้นเหลือ 0.00 การทดสอบข้ามเครื่องมือ (cross-instrument) กับห้าเหรียญหลัก (ETH/BTC/SOL/BNB/XRP ประมาณ 1.18 ล้านแท่ง 1 นาทีต่อเหรียญ) โดยคัดเลือกด้วยค่ามัธยฐาน (median) ของ out-of-sample ยิ่งตอกย้ำผลลบนี้ให้ชัดเจน: dual DSR 0.24 / PBO 0.264, triple DSR 0.14 / PBO 0.327 — ทั้งคู่ไม่ผ่านเกณฑ์ (gate) แชมป์ทำกำไรได้เพียง 1 ใน 5 สัญลักษณ์ และขาดทุนในที่เหลือ นี่คือเหตุผลที่เครื่องมือต่อต้าน overfitting มีไว้ทำ: เพื่อหยุดไม่ให้คุณนำ noise ที่ดีที่สุดไปขายเป็น alpha
การพิสูจน์ว่าไม่มี Look-Ahead ใน Multi-Timeframe Backtest: รบกวนอนาคต พิสูจน์ว่าอดีตมองไม่เห็นมัน
Multi-timeframe backtest รั่วไหลข้อมูลอนาคตผ่านแท่ง (bar) higher-timeframe ที่ยังก่อตัวไม่เสร็จ ซึ่งราคาปิดสุดท้ายยังไม่มีอยู่จริง คุณไม่สามารถอาศัยแค่การ code review เพื่อสร้างความมั่นใจได้ — ต้องทดสอบมันจริง ๆ เราจำลองกฎ closed-bar ของบอทที่รันจริง (live bot) ขึ้นมาใหม่อย่างตรงเป๊ะ แล้วพิสูจน์ว่าไม่มีการรั่วไหลด้วย shifted-future probe: รบกวนทุกแท่งในอนาคต แล้วยืนยันว่าทุกสัญญาณและทุกการเทรดในอดีตไม่เปลี่ยนแปลงแม้แต่บิตเดียว ผ่านการตรวจสอบ parity ครบ 25/25 และ probe นี้ก็มี "เขี้ยวเล็บ" จริง ๆ ด้วย
เมื่อ GPU คุ้มค่าจริง: Roofline ของการ Sweep พารามิเตอร์ ที่ตัวเลขพาดหัว 167x แท้จริงคือ 27x อัลกอริทึม คูณ 6.2x ฮาร์ดแวร์
ความได้เปรียบของ GPU เหนือ CPU เพิ่มขึ้นตามขนาด batch — จาก 54.5x เมื่อประมวลผลทีละหนึ่ง combo ต่อการเรียก ไปจนถึง 359.6x ที่ 61 combo บนการ precompute indicator แบบ multi-timeframe ของเรา — เพราะ sweep ขนาดเล็กไม่สามารถ amortize ต้นทุนคงที่จากการเปิด kernel และการถ่ายโอนข้อมูลได้ เราแยกตัวเลขพาดหัว 167x ออกเป็นชัยชนะจากอัลกอริทึม 27x ที่ช่วย CPU ได้เช่นกัน และชัยชนะจากฮาร์ดแวร์ 6.2x แสดงให้เห็นว่าความได้เปรียบที่แท้จริงของ GPU เทียบกับ CPU ที่ดีที่สุดอยู่ที่เพียง 3.2x สำหรับ single-timeframe และ 6.2x สำหรับ multi พร้อมให้แนวทางตัดสินใจว่า sweep ต้องกว้างแค่ไหนก่อนที่ GPU จะคุ้มค่าที่จะซื้อมาใช้
กับดักความแม่นยำของ GPU: เมื่อ Backtest แบบ fp32 บน Apple Metal คืนค่าขยะออกมาอย่างเงียบๆ
GPU Metal ของ Apple ไม่มี float64 เลย หากพอร์ต backtest แบบ vectorized ไปใช้อย่างไม่ระวัง สูตร WMA แบบ prefix-sum ที่ดูน่าใช้ที่สุดจะทำให้ fp32 overflow — ค่าความคลาดเคลื่อนสัมพัทธ์สูงสุดถึง 211 เท่า — แต่โปรแกรมก็ยังรันจบและคืนตัวเลขที่ดูสมเหตุสมผลออกมา ทางแก้ไม่ใช่การเพิ่มความแม่นยำ แต่คือการเปลี่ยนสูตรคำนวณ: ใช้ windowed convolution โดยตรง ซึ่งปลอดภัยกับ fp32 ถึงระดับ 8×10⁻⁷ และเร็วกว่า numba แบบ single-thread ถึง 55.9 เท่า กับดักนี้ หลักคณิตศาสตร์เบื้องหลัง และวิธีพิสูจน์ว่าคุณไม่ได้ตกหลุมพราง
ประตูความเที่ยงตรง (Fidelity Gate): การทดสอบย้อนหลังแบบหยาบไปละเอียดจะหลอกคุณได้เร็วขึ้น เว้นแต่ตัวแทนราคาถูกจะจัดอันดับเหมือนกับตัวเต็มราคาแพง
การค้นหาแบบ drill-down / multi-fidelity (ASHA, successive halving, Hyperband) คัดกรองค่าคอนฟิกหลายพันแบบด้วยต้นทุนต่ำ และเลื่อนขั้นเฉพาะผู้รอดชีวิตไปสู่การประเมินเต็มรูปแบบที่มีราคาแพง มันคือการเร่งความเร็วที่แท้จริง แต่มันจะพังลงอย่างเงียบๆ หากการจัดอันดับที่ความเที่ยงตรงต่ำไม่สอดคล้องกับการจัดอันดับที่ความเที่ยงตรงสูง เราวัดค่าสหสัมพันธ์อันดับระหว่างโฟลด์ (fold-rank correlation) พบว่าที่หนึ่งโฟลด์ ค่า Spearman ρ อาจอยู่ที่ 0.03 (จัดอันดับแทบจะสุ่ม) แล้วไต่ขึ้นไปที่ 0.43, 0.67, 0.78, 0.91 เมื่อจำนวนโฟลด์เพิ่มขึ้น ทางแก้คือประตูบังคับหนึ่งด่าน นั่นคือวัดค่า ρ(ราคาถูก, เต็มรูปแบบ) ก่อน แล้วปรับความเที่ยงตรงขั้นต่ำขึ้นโดยอัตโนมัติไปยังขั้นแรกที่ ρ ≥ 0.5
Random Search กับ Smart Search: จุดตัดอยู่ที่ต้นทุนการประเมิน ไม่ใช่ตัวอัลกอริทึม
เมื่อ backtest หนึ่งครั้งมีต้นทุนต่ำ Sobol แบบ scrambled ที่โง่ ๆ จะชนะด้วย throughput ดิบ — smart sampler (TPE, CMA-ES, ASHA) ต้องจ่าย ask/tell tax ใน Python ที่ทำให้ช้าลง 20 เท่า จึงประเมินจุดได้น้อยกว่ามากเมื่อ wall-clock เท่ากัน และแพ้ไป แต่ถ้าทำให้แต่ละ eval มีต้นทุนสูงขึ้น (multi-TF + walk-forward fold) จุดตัดก็จะพลิกกลับ เราวัดทั้งสองระบอบ และเหตุใด fold-rank fidelity (ρ@1 เพิ่มจาก 0.03→0.43) จึงเป็นเงื่อนไขที่ต้องมีก่อนที่ pruning จะให้ผลตอบแทนคุ้มค่า
พื้นที่พารามิเตอร์สองแกน: ทำไมการสวีปส่วนใหญ่ของคุณจึงควรแทบจะฟรี
พารามิเตอร์แต่ละตัวมีต้นทุนการค้นหาไม่เท่ากัน พารามิเตอร์ของกลยุทธ์แยกออกเป็นแกนราคาแพง (อินดิเคเตอร์ — ต้องคำนวณใหม่ตลอดทั้งซีรีส์ราคา) และแกนราคาถูก (ค่าเกณฑ์ตัดสินใจ — การกวาด O(n) รอบเดียวบนสัญญาณที่คำนวณล่วงหน้า) เพราะอินดิเคเตอร์ไม่แปรผันต่อค่าเกณฑ์ คุณจึงคำนวณมันครั้งเดียวแล้วสวีปคอนฟิกค่าเกณฑ์นับพันที่ ~5,600 cfg/s — ถูกกว่าการคำนวณใหม่ต่อคอนฟิกราว 1,600 เท่า นี่คือการตีราคาคำสาปแห่งมิติเสียใหม่
ภาษีของเฟรมเวิร์ก: เมื่อไลบรารี Backtest ของคุณช้ากว่าลูป Pandas แบบง่ายๆ
เราทำเบนช์มาร์กเอนจิน backtest แปดตัวบน parameter sweep ชุดเดียวกัน — 150,000 แท่งราคา, 80 คอมโบ HMA-cross, ล็อกจำนวนเทรดให้ตรงกันที่ 2707 รายการ เฟรมเวิร์ก event-driven ที่ได้รับความนิยมมากที่สุดสองตัวกลับช้ากว่าลูป pandas ที่เขียนขึ้นมาเอง ในขณะที่เอนจินแบบ vectorized/compiled ทำงานเดียวกันได้เร็วกว่าประมาณ 13,000 เท่า การศึกษาต้นทุนแฝงต่อแท่งราคาที่ไลบรารียอดนิยมไม่เคยถูกออกแบบมาให้ตัดจ่ายได้
Probability of Backtest Overfitting: การค้นหาของคุณเอาชนะการโยนเหรียญหรือไม่?
Deflated Sharpe Ratio ตั้งราคาให้กลยุทธ์ผู้ชนะ; PBO ตั้งราคาให้การค้นหาที่เลือกมันขึ้นมา Combinatorially Symmetric Cross-Validation รัน C(16,8) = 12,870 train/test split บน performance matrix ขนาด 1000x200 แล้วถามว่า: ผู้ชนะแบบ in-sample ตกไปอยู่ครึ่งล่างของ out-of-sample หรือไม่? กับดักที่แทบทุกคนมองข้าม — null ของ PBO คือ 0.5 ไม่ใช่ 1 บนกลยุทธ์ zero-edge 200 ตัว best in-sample Sharpe ต่อปีที่ 1.98 ยุบลงเหลือ 0.06 นอก sample และ PBO = 0.476: การโยนเหรียญ overfit เต็มขั้น ปลูก edge จริง (Sharpe ต่อปี 2.38) แล้ว PBO ลดลงเหลือ 0.001, in-sample 3.73 รอดมาเป็น out-of-sample 2.34 moving-average grid บน pure random walk ก็ไม่มี out-of-sample skill เช่นกัน — PBO 0.463 เฉลี่ยข้าม 60 matrix แยกไม่ออกจาก null ทางสถิติ — และบน matrix ตัวแทนหนึ่งตัว ภาพลวงตาชัดเจนมาก: best in-sample Sharpe ที่ 2.33 ยุบลงเหลือ median out-of-sample -0.22, PBO 0.573, โอกาสขาดทุน 63%
ภาษี IPC: เอา Backtest Engine ไปไว้หลัง Socket แล้วเสีย 13% — แต่แทบไม่มีส่วนไหนเลยเป็นความผิดของ Socket
เรา port numba backtest kernel ตัวหนึ่งไปเป็น Rust แบบ line-for-line แล้วเรียกมันข้าม process boundary สี่วิธี พร้อม equivalence gate ที่ยืนยันว่า PnL เหมือนกันทุกประการจนถึง trade สุดท้าย การส่ง price series ทั้งชุดขนาด 1.2 MB ผ่าน Unix socket มีค่าใช้จ่ายประมาณ 2 ms — ราว 0.1% ของงานทั้งหมด การ encode payload เดียวกันด้วย JSON แพงกว่า raw bytes ถึง 1348 เท่า การเรียกแบบ chatty ทีละ combo ส่งข้อมูลซ้ำ 80 ครั้ง และ pattern การเรียกทีละ bar จะต้องจ่าย IPC ล้วนๆ 2.1 วินาทีบนงานที่ใช้เวลา 2.0 วินาที boundary นั้นถูก ภาษีอยู่ที่วิธีที่คุณข้ามมันต่างหาก
Deflated Sharpe Ratio: 'ผู้ชนะ' จาก Backtest ของคุณกี่ตัวที่รอดจาก Multiple Testing?
parameter search คือเครื่องจักรผลิตความโชคดี บน pure noise — กลยุทธ์ 1,000 ตัวที่ไม่มี edge จริงเลย — Sharpe ต่อปีที่ดีที่สุดเฉลี่ยอยู่ที่ 1.63 และ naive significance test ฟันธงว่าพบการค้นพบ 100% ของเวลา เราสร้าง ground truth ที่ควบคุมได้และแสดงให้เห็นว่า Deflated Sharpe Ratio, Harvey-Liu haircut และ White's Reality Check คืนความซื่อสัตย์กลับมา: false discovery ลดลงจาก 1.000 เหลือ 0.001-0.057, edge จริงที่อยู่เหนือ noise ceiling ถูกเก็บไว้ด้วย power ~1 — และกับดักจริงหนึ่งอย่าง (correlated grid) ที่ raw DSR over-deflate และคำตัดสินต้องอ่านข้าม band ทั้งหมดของ effective-trial estimator ไม่ใช่แค่ตัวเดียว
การออกแบบ Objective Function: ตัวชี้วัดที่คุณ Optimize แอบเลือกกลยุทธ์ให้คุณ
การจะค้นหากลยุทธ์ที่ 'ดีที่สุด' คุณต้องนิยาม 'ดีที่สุด' ก่อน — และตัวเลขสเกลาร์ตัวนั้นเป็นผู้เลือกผู้ชนะอย่างเงียบ ๆ บนข้อมูล synthetic ที่มี edge จริงที่รู้ค่า (600 seed, T=2000, 80 threshold) per-trade Sharpe แบบ naive สวมมงกุฎให้ lottery: มันเลือกผู้ชนะที่ exposure ต่ำกว่า 5% ใน 56% ของ seed และเสื่อมสภาพ (degenerate) ใน 57% — บน seed ที่ชัดเจนที่สุด มี 8 เทรดที่ทำ in-sample Sharpe ได้ 21.09 แล้วยุบลงเหลือ 0.13 นอก sample การแก้ไขที่ซื่อสัตย์นั้นเกือบจะน่าเบื่อ: วัดบน full timeline ซึ่งไม่เคยเสื่อมสภาพเลย (out-of-sample 1.71) trade-count shrinkage (conf_k) และ exposure floor สามารถซ่อมแซม per-trade metric ได้ แต่แม้จะซ่อมเต็มที่แล้วก็ยังแค่เท่ากับ full-timeline Sharpe (1.70 เทียบกับ 1.71) — ไม่เคยเอาชนะมันได้เลย กฎของ Goodhart ในบริบทของ backtest พร้อม ground truth ที่ควบคุมได้