top of page
Robo Trade NotesContents
Hide contentsChapter 5
Backtesting / Optimization / Overfitting

Parameter Optimization & Overfitting

ในบทที่แล้ว เราใช้ EMA 20/200 กับ SPY แล้วได้ผลตอบแทนสะสม 367.41% ในช่วง 20 ปี คำถามถัดมาคือ ถ้าเปลี่ยน 20 กับ 200 เป็นตัวเลขอื่น ผลจะดีขึ้นไหม บทนี้จะลองทุกคู่ที่เป็นไปได้ในช่วงหนึ่ง หาคู่ที่ดีที่สุด แล้วตรวจว่าคู่ที่ "ดีที่สุด" นั้นดีจริง หรือแค่เข้ากับข้อมูลชุดที่เราใช้หาพอดี

ทำไมต้องเป็น 20 กับ 200

ตัวเลข 20 และ 200 ไม่ได้มาจากสูตร เราเลือกเพราะคนนิยมใช้ ถ้าเปลี่ยนเป็น 25/180 หรือ 65/120 กลยุทธ์ก็ยังเป็น EMA สั้นตัดยาวเหมือนเดิม ต่างกันแค่ความเร็วในการตอบสนองต่อราคา

ตัวเลขที่เราตั้งเองแบบนี้เรียกว่า parameter การปรับหา parameter ที่ทำให้ตัวชี้วัดที่เราเลือกออกมาดีที่สุดเรียกว่า Parameter Optimization วิธีที่ตรงที่สุดคือลองทุกคู่ในตาราง (grid search หรือ brute force) หนังสือ Python for Algorithmic Trading ของ Yves Hilpisch ทำแบบเดียวกันกับ SMA บน EUR/USD ปี 2010–2019 โดยให้ optimize_parameters ไล่ SMA เร็ว 30–50 กับ SMA ช้า 200–300 แล้วได้คู่ 48/238 (หน้า PDF 87–88)

เขียนเป็นสมการได้ว่า เราเลือกคู่ (nf∗,ns∗)(n_f^*, n_s^*) ที่ทำให้ Sharpe สูงที่สุดใน grid GG

(nf∗,ns∗)=arg⁡max⁡(nf, ns)∈GSharpe(nf,ns),Sharpe=rˉsr252(n_f^*, n_s^*) = \arg\max_{(n_f,\,n_s) \in G} \text{Sharpe}(n_f, n_s), \qquad \text{Sharpe} = \frac{\bar r}{s_r}\sqrt{252}

nfn_f คือจำนวนวันของ EMA เร็ว nsn_s คือจำนวนวันของ EMA ช้า rˉ\bar r กับ srs_r คือค่าเฉลี่ยและส่วนเบี่ยงเบนมาตรฐานของผลตอบแทนรายวันหลังค่าธรรมเนียม บทนี้ใช้อัตราปลอดความเสี่ยง 0% เหมือนบทที่ 4

Parameter Optimization: ลองทุกคู่

เราใช้ข้อมูลและกติกาชุดเดียวกับบทที่ 4 ทุกอย่าง คือราคา SPY รายวันจาก Webull OpenAPI ถือ SPY 100% เมื่อ EMA เร็วอยู่เหนือ EMA ช้าที่ราคาปิดวันก่อน ซื้อขายที่ราคาเปิดวันถัดไป และเสียค่าธรรมเนียมสมมติ 0.1% ต่อข้าง สิ่งเดียวที่เปลี่ยนคือคู่ EMA

สิ่งที่ลองค่า
EMA เร็ว5, 10, 15, …, 100 วัน (20 ค่า)
EMA ช้า50, 60, 70, …, 300 วัน (26 ค่า)
เงื่อนไขEMA เร็วต้องสั้นกว่า EMA ช้า
จำนวนคู่ที่ทดสอบ484 คู่
ตัวชี้วัดที่ใช้เลือกSharpe รายวัน × √252

โค้ดในไฟล์ lesson_15.py ทำแค่วนลูปเรียก backtest ของบทที่ 4 ทีละคู่

PYTHON
pythonคัดลอก
from lesson_15 import load_bars, run_grid, EVALUATION bars = load_bars()                      # SPY รายวันจาก Webull ที่ดึงไว้แล้วresults = run_grid(bars, EVALUATION)    # 484 คู่ ช่วง 2006-09-26 ถึง 2026-09-25best = max(results, key=lambda r: r['sharpe'])print(best['fast'], best['slow'], round(best['sharpe'], 2))   # 5 90 0.84

เมื่อมองย้อนหลังทั้ง 20 ปี คู่ที่ดีที่สุดคือ EMA 5/90 ได้ Sharpe 0.84 และ CAGR 9.49% ขาดทุนหนักสุด (Max DD) −19.01% ส่วน EMA 20/200 ได้ Sharpe 0.66 อยู่อันดับ 237 จาก 484 คู่ ตรงกลางตารางพอดี ถ้าเราเอา 5/90 ไปโชว์ ตัวเลขจะดูดีกว่าบทที่แล้วมาก แต่เราได้ 5/90 มาจากการเห็นราคาทั้ง 20 ปีก่อนแล้วค่อยเลือก ซึ่งในวันที่เริ่มเทรดจริงเราไม่มีข้อมูลนั้น

PARAMETER GRID / 484 PAIRS

ลองทุกคู่ EMA แล้วดูว่าคู่ไหนเด่น

SPY รายวัน · Webull

แต่ละช่องคือกลยุทธ์ EMA หนึ่งคู่ แกนนอนคือ EMA ช้า แกนตั้งคือ EMA เร็ว ช่องสีเข้มคือค่าที่สูงกว่าในช่วงที่เลือก ลองสลับช่วงเวลาแล้วสังเกตว่าบริเวณสีเข้มย้ายไปไหน

ทั้ง 20 ปี 2006–2026ช่วงฝึก 2006–2016ช่วงทดสอบ 2016–2026
SharpeCAGRMax DD
5080100120150200250300520406080100EMA ช้า (วัน)EMA เร็ว (วัน)
  • EMA 20/200 จากบทที่ 4
  • EMA 65/120 ดีที่สุดในช่วงฝึก
  • ดีที่สุดในมุมมองนี้ (5/90)
คู่ที่เลือก
EMA 20/200อันดับ 237 จาก 484 ตาม Sharpe
Sharpe
0.662006–2026
CAGR
8.02%ต่อปี
Max DD
-26.7%17 เทรดที่ปิดแล้ว

ช่องว่างมุมซ้ายบนคือคู่ที่ EMA เร็วยาวเท่าหรือยาวกว่า EMA ช้า ซึ่งไม่ได้ทดสอบ · สีคำนวณใหม่ตามค่าต่ำสุดถึงสูงสุดของมุมมองที่เลือก · ค่าธรรมเนียม 0.1% ต่อข้าง · Sharpe ใช้อัตราปลอดความเสี่ยง 0%

ลองกดสลับระหว่าง "ช่วงฝึก 2006–2016" กับ "ช่วงทดสอบ 2016–2026" แล้วดูบริเวณสีเข้ม ในช่วงแรก สีเข้มกองอยู่แถว EMA เร็ว 55–90 คู่กับ EMA ช้า 80–130 พอเปลี่ยนเป็นช่วงหลัง สีเข้มย้ายไปอยู่ที่ EMA เร็วสั้น ๆ 10–25 วัน คู่ที่เด่นในสิบปีหนึ่งไม่ได้เด่นในอีกสิบปี

Overfitting: จำอดีตแทนที่จะเรียนรู้

Hilpisch ยกนิยามของ Man Institute ว่า overfitting คือการที่แบบจำลองอธิบาย noise แทนที่จะอธิบาย signal ผลในข้อมูลที่ใช้ทดสอบจึงดี แต่ทำนายข้อมูลใหม่ได้น้อยหรือไม่ได้เลย (หน้า PDF 100) EMA มีแค่สองตัวเลขก็ overfit ได้ เพราะเราลอง 484 คู่แล้วเก็บคู่ที่ออกมาดีที่สุด ยิ่งลองมาก ก็ยิ่งมีโอกาสเจอคู่ที่บังเอิญเข้ากับเหตุการณ์ในอดีตพอดี

อีกคำที่มาคู่กันคือ data snooping Hilpisch อ้าง White (2000) ว่าเกิดขึ้นเมื่อเราใช้ข้อมูลชุดเดียวกันมากกว่าหนึ่งครั้งเพื่อสรุปผลหรือเลือกแบบจำลอง Grid search บนข้อมูล 20 ปีชุดเดียวคือการใช้ข้อมูลชุดเดิมซ้ำ 484 ครั้ง

แบ่งข้อมูลเป็นช่วงฝึกกับช่วงทดสอบ

วิธีพื้นฐานที่สุดในการจับ overfitting คือแบ่งข้อมูลก่อนเริ่ม optimize ส่วนแรกเรียกว่า In-sample หรือช่วงฝึก ใช้หาพารามิเตอร์ ส่วนหลังเรียกว่า Out-of-sample หรือช่วงทดสอบ เก็บไว้โดยไม่ดูจนกว่าจะเลือกเสร็จ Hilpisch ใช้การแบ่งแบบนี้กับกลยุทธ์ regression และเขียนไว้ว่ากลยุทธ์ที่มีขั้นตอน optimize หรือ fit ควรประเมินแบบนี้ เพราะช่วยลดปัญหา data snooping และ overfitting (หน้า PDF 118)

บทนี้แบ่ง 20 ปีออกเป็นสองท่อนเท่ากัน

ช่วงวันที่วันซื้อขายใช้ทำอะไร
ช่วงฝึก (In-sample)2006-09-26 ถึง 2016-09-232,517ลอง 484 คู่ แล้วเลือกคู่ที่ Sharpe สูงสุด
ช่วงทดสอบ (Out-of-sample)2016-09-26 ถึง 2026-09-252,514นำคู่ที่เลือกแล้วมารันครั้งเดียว ห้ามกลับไปเลือกใหม่

แต่ละช่วงเริ่มบัญชีใหม่ด้วยเงินสด 100,000 USD ส่วน EMA คำนวณต่อเนื่องจากราคาก่อนหน้า เส้น EMA วันแรกของช่วงทดสอบจึงใช้ราคาในอดีตได้ตามปกติ เพราะราคาเหล่านั้นเกิดขึ้นแล้วจริง

ในช่วงฝึก คู่ที่ชนะคือ EMA 65/120 ได้ Sharpe 0.72 CAGR 8.59% และ Max DD −17.31% ส่วนการซื้อแล้วถือ SPY ในช่วงเดียวกันได้ Sharpe 0.44 และ Max DD −55.19% เพราะช่วงนี้มีวิกฤตปี 2008 EMA 65/120 ออกจากตลาดได้ก่อนช่วงที่ร่วงหนัก ถ้าหยุดดูแค่ตรงนี้ เราจะสรุปว่าเจอกลยุทธ์ที่ทั้งกำไรมากกว่าและเสี่ยงน้อยกว่าตลาด

EQUITY / TRAIN THEN TEST

EMA 65/120 ชนะในช่วงฝึก แล้วช่วงทดสอบเป็นอย่างไร

ผลจำลองหลังค่าธรรมเนียม

เส้นม่วงคือคู่ที่ Sharpe ช่วงฝึกสูงสุด เราเลือกมันโดยไม่ได้เห็นข้อมูลหลังเดือนกันยายน 2016 ปุ่ม "เริ่มนับใหม่" ตั้งทุกเส้นเป็น 100 ในวันแรกของช่วงทดสอบ เพื่อเทียบเฉพาะส่วนที่ยังไม่เคยเห็น

เริ่มนับใหม่ที่ช่วงทดสอบทั้ง 20 ปี (พันดอลลาร์)
  • EMA 65/120
  • EMA 20/200
  • ซื้อแล้วถือ SPY

มูลค่าพอร์ต เริ่ม 100 ที่ 2016-09-26 · สเกล log

100200400201620182020202220242026
วันที่
2026-09-25
EMA 65/120
270.0
EMA 20/200
282.7
ซื้อแล้วถือ SPY
420.3
ช่วงทดสอบ 2016-09-26 ถึง 2026-09-25 · เริ่มบัญชีใหม่ 100,000 USD
กลยุทธ์SharpeCAGRMax DDเทรด
EMA 65/1200.7710.42%-29.4%6
EMA 20/2000.8310.93%-26.7%7
ซื้อแล้วถือ SPY0.8915.42%-33.7%—

ราคา SPY รายวันจาก Webull OpenAPI ดึงเมื่อ 2026-09-28 · ราคาปรับย้อนหลังแล้ว ปันผลอยู่ในราคา · ค่าธรรมเนียมสมมติ 0.1% ต่อข้าง ไม่รวม spread slippage และภาษี · แถบสีเทาคือช่วงฝึก

พอนำ EMA 65/120 ไปรันในช่วงทดสอบ ผลออกมาดังนี้

ช่วงทดสอบ 2016–2026SharpeCAGRMax DDอันดับ Sharpe จาก 484 คู่
EMA 65/120 (ชนะช่วงฝึก)0.7710.42%−29.36%197
EMA 20/200 (ค่าที่ใช้ในบทที่ 4)0.8310.93%−26.67%101
ซื้อแล้วถือ SPY0.8915.42%−33.72%—

Sharpe ของ EMA 65/120 ในช่วงทดสอบ (0.77) สูงกว่าในช่วงฝึก (0.72) เสียอีก ตัวเลขนี้หลอกตาได้ง่าย เพราะสิบปีหลังเป็นตลาดขาขึ้นเกือบตลอด 475 จาก 484 คู่ได้ Sharpe ในช่วงทดสอบสูงกว่าช่วงฝึก ค่ามัธยฐานของทั้ง 484 คู่ขยับจาก 0.57 ในช่วงฝึกเป็น 0.74 ในช่วงทดสอบ เวลาอ่านผลช่วงทดสอบ ให้เทียบกับทางเลือกอื่นในช่วงเวลาเดียวกัน อย่าเทียบกับตัวเลขของตัวเองในช่วงฝึก

เมื่อเทียบแบบนั้น คู่ที่ชนะช่วงฝึกตกไปอยู่อันดับ 197 แพ้ EMA 20/200 ที่เราไม่ได้ optimize เลย และแพ้การซื้อแล้วถือทั้ง Sharpe และ CAGR ข้อได้เปรียบที่เห็นในช่วงฝึกมาจากการหลบปี 2008 ได้ พอไม่มีวิกฤตแบบเดิม ข้อได้เปรียบนั้นก็หายไป

อันดับที่ไม่ข้ามช่วงเวลา

ถ้าการ optimize ได้ผลจริง คู่ที่อยู่อันดับต้นในช่วงฝึกควรอยู่อันดับต้นในช่วงทดสอบด้วย เราวัดเรื่องนี้ได้ด้วย Spearman rank correlation ซึ่งเทียบอันดับของสองชุด ค่า 1 แปลว่าอันดับเหมือนกันทุกตำแหน่ง ค่า 0 แปลว่าไม่สัมพันธ์กัน และค่าติดลบแปลว่าอันดับกลับทิศ

ρ=corr⁡(rank⁡(SharpeIS), rank⁡(SharpeOOS))\rho = \operatorname{corr}\big(\operatorname{rank}(\text{Sharpe}_{\text{IS}}),\ \operatorname{rank}(\text{Sharpe}_{\text{OOS}})\big)

สำหรับ 484 คู่ในบทนี้ ρ=−0.09\rho = -0.09 รู้อันดับในช่วงฝึกแล้วก็เดาอันดับในช่วงทดสอบแทบไม่ได้

IN-SAMPLE VS OUT-OF-SAMPLE

อันดับในช่วงฝึก ข้ามไปช่วงทดสอบได้ไหม

484 คู่ · Sharpe

จุดหนึ่งจุดคือคู่ EMA หนึ่งคู่ แกนนอนคือ Sharpe ช่วงฝึก 2006–2016 แกนตั้งคือ Sharpe ของคู่เดียวกันในช่วงทดสอบ 2016–2026 ถ้าคู่ที่เก่งในช่วงฝึกยังเก่งต่อ จุดสีม่วงจะไปกองอยู่มุมขวาบน

Scatter 484 คู่ rank correlation -0.09 คู่ที่ดีที่สุดในช่วงฝึก 65/120 อยู่อันดับ 197 ในช่วงทดสอบ0.40.60.81.01.20.20.40.60.8ซื้อแล้วถือ ช่วงทดสอบ 0.89ซื้อแล้วถือ ช่วงฝึก 0.44Sharpe ช่วงฝึก 2006–2016Sharpe ช่วงทดสอบ
  • 10 คู่ที่ Sharpe ช่วงฝึกสูงสุด
  • EMA 20/200
  • คู่อื่น
เลือกคู่จาก 10 อันดับแรกของช่วงฝึกEMA 65/120 · อันดับช่วงฝึก 1EMA 70/110 · อันดับช่วงฝึก 2EMA 90/100 · อันดับช่วงฝึก 3EMA 10/60 · อันดับช่วงฝึก 4EMA 70/80 · อันดับช่วงฝึก 5EMA 60/90 · อันดับช่วงฝึก 6EMA 80/100 · อันดับช่วงฝึก 7EMA 55/130 · อันดับช่วงฝึก 8EMA 70/120 · อันดับช่วงฝึก 9EMA 75/110 · อันดับช่วงฝึก 10EMA 20/200 · อันดับช่วงฝึก 439
คู่ที่เลือก
EMA 65/120
ช่วงฝึก
0.72อันดับ 1 จาก 484
ช่วงทดสอบ
0.77อันดับ 197 จาก 484
Rank correlation
-0.09ทั้ง 484 คู่

Rank correlation (Spearman) ใกล้ 0 แปลว่ารู้อันดับในช่วงฝึกแล้วเดาอันดับในช่วงทดสอบแทบไม่ได้ · เส้นประคือ Sharpe ของการซื้อแล้วถือ SPY ในแต่ละช่วง · ช่วงทดสอบทั้งช่วงเป็นตลาดขาขึ้น จึงทำให้คู่ส่วนใหญ่ได้ Sharpe สูงขึ้นพร้อมกัน

ลองเลือกคู่จาก 10 อันดับแรกของช่วงฝึกทีละคู่ อันดับในช่วงทดสอบกระจายตั้งแต่ 23 ถึง 436 คู่ที่ดีที่สุดในช่วงทดสอบคือ EMA 10/110 ได้ Sharpe 1.08 แต่ในช่วงฝึกมันอยู่อันดับ 315 ส่วน EMA 20/200 อยู่อันดับ 439 ในช่วงฝึก ก่อนขึ้นมาอันดับ 101 ในช่วงทดสอบ

บางคนแนะนำให้เลือกคู่ที่อยู่กลาง "ที่ราบ" ซึ่งคู่ข้างเคียงก็ได้ผลดีใกล้กัน แทนการเลือกยอดแหลมที่ดีอยู่คู่เดียว เราลองแล้วด้วยการเฉลี่ย Sharpe ของแต่ละคู่กับคู่ข้างเคียงรอบตัวในช่วงฝึก คู่ที่ได้คะแนนเฉลี่ยสูงสุดคือ EMA 70/110 แต่ในช่วงทดสอบมันอยู่อันดับ 386 ในข้อมูลชุดนี้ การเลือกที่ราบช่วยไม่ได้ เพราะทั้งที่ราบย้ายที่ไปพร้อมกับสภาพตลาด

ลองกับตลาดที่ไม่มีสัญญาณ

ถ้าเรายังไม่แน่ใจว่าผลดีในช่วงฝึกมาจากความบังเอิญได้มากขนาดไหน วิธีหนึ่งคือสร้างตลาดที่เรารู้แน่ว่าไม่มีแนวโน้มให้ EMA จับได้ แล้วทำ optimization แบบเดิม

เราสุ่มหยิบผลตอบแทนรายวันของ SPY ช่วง 2006–2026 มาเรียงใหม่แบบใส่คืน (bootstrap) สร้างเป็นโลกสมมติ 60 โลก โลกละ 2,520 วัน ขนาดการขึ้นลงรายวันยังเหมือน SPY แต่ลำดับเวลาถูกทำลาย แนวโน้มที่ EMA ใช้จึงไม่มีอยู่จริง ในแต่ละโลก เราแบ่งครึ่งแรกเป็นช่วงฝึก ครึ่งหลังเป็นช่วงทดสอบ แล้วลองเลือกคู่ที่ดีที่สุดจาก N คู่

DATA SNOOPING / RANDOM WORLDS

ลองมากขึ้น ผลย้อนหลังยิ่งดูดี ทั้งที่ตลาดไม่มีสัญญาณ

ข้อมูลสังเคราะห์

เราสุ่มผลตอบแทนรายวันของ SPY มาเรียงใหม่ 60 โลก โลกละ 2,520 วัน ลำดับเวลาถูกทำลาย EMA จึงไม่มีแนวโน้มจริงให้จับ ในแต่ละโลก เราลอง N คู่ เลือกคู่ที่ Sharpe ครึ่งแรกดีที่สุด แล้วอ่านผลครึ่งหลัง

จำนวนคู่ที่ลอง N = 74ขั้น: 1, 2, 5, 10, 20, 40, 74 คู่ · N = 74 คือลองครบทุกคู่ในโลกสมมติ
Sharpe ครึ่งแรก (ใช้เลือก)
0.57เฉลี่ย 60 โลก
Sharpe ครึ่งหลัง (ไม่เคยเห็น)
0.36เฉลี่ย 60 โลก
ส่วนต่าง
0.2137 จาก 60 โลก ครึ่งหลังแย่กว่า
-0.50.00.51.012510204074จำนวนคู่ที่ลองก่อนเลือก (N)

เส้นทึบคือค่าเฉลี่ย แถบจางคือช่วงครึ่งกลางของโลกทั้งหมด (เปอร์เซ็นไทล์ 25–75) · bootstrap ผลตอบแทนรายวัน SPY 2006–2026 แบบใส่คืน seed 20260929 · ราคาเปิดเท่ากับราคาปิดวันก่อน · ค่าธรรมเนียม 0.1% ทุกครั้งที่เปลี่ยนสถานะ · Sharpe ของครึ่งหลังที่ยังบวกมาจาก drift ของ SPY ที่ติดมากับผลตอบแทนที่สุ่ม

เมื่อลองคู่เดียว Sharpe ช่วงฝึกกับช่วงทดสอบใกล้กัน (0.30 กับ 0.31) เพราะไม่มีการเลือก พอเพิ่มเป็นการเลือกจากทั้ง 74 คู่ Sharpe ช่วงฝึกของคู่ที่ชนะเฉลี่ยขึ้นไปถึง 0.57 แต่ช่วงทดสอบอยู่ที่ 0.36 เท่านั้น ส่วนต่าง 0.21 นี้มาจากการเลือกล้วน ๆ เพราะโลกนี้ไม่มีสัญญาณให้เลือก Sharpe ช่วงทดสอบที่ยังเป็นบวกมาจากผลตอบแทนเฉลี่ยของ SPY ที่ติดมากับข้อมูลที่สุ่ม ไม่ได้มาจาก EMA

ผลนี้ใช้อ่านผลกับ SPY จริงได้ด้วย ในช่วงฝึกเราลองไป 484 คู่ มากกว่าในโลกสมมติราวหกเท่าครึ่ง Sharpe 0.72 ของ EMA 65/120 จึงรวมส่วนที่ได้จากการเลือกไว้ด้วย ตัวเลขที่ควรรายงานคู่กับผล optimize จึงมีจำนวนคู่ที่ลองไปด้วย Hilpisch อ้างงานของ Bailey et al. (2015) ไว้ว่า การลองพารามิเตอร์จำนวนมากบนข้อมูลการเงินที่ signal-to-noise ต่ำ มักได้พารามิเตอร์ที่เข้ากับ noise ในอดีต (หน้า PDF 100)

Walk-forward: เลือกใหม่ทุกปี

การแบ่งครั้งเดียวให้ช่วงทดสอบแค่ช่วงเดียว ผลจึงขึ้นกับว่าเราตัดตรงไหน ถ้าตัดปี 2011 แทน 2016 เรื่องราวอาจเปลี่ยน Walk-forward แก้เรื่องนี้ด้วยการเลื่อนหน้าต่างไปตามเวลา ซึ่งเป็นแบบ Historical ที่เราพูดถึงในบท Introduction of Backtest

กติกาในบทนี้คือ ทุกวันที่ 26 กันยายน ตั้งแต่ปี 2011 ถึง 2025 ให้ดู Sharpe ของทั้ง 484 คู่ในห้าปีก่อนหน้า เลือกคู่ที่ดีที่สุด แล้วใช้คู่นั้นเทรดไปหนึ่งปี ปีถัดไปก็เลือกใหม่ รวมเป็น 15 ปีทดสอบที่ไม่มีปีไหนเคยถูกใช้เลือกพารามิเตอร์ของตัวเอง

WALK-FORWARD / 5 YEARS TRAIN / 1 YEAR TEST

เลือกคู่ใหม่ทุกปี จากข้อมูล 5 ปีก่อนหน้า

SPY รายวัน · Webull

ทุกวันที่ 26 กันยายน เราดู Sharpe ของทั้ง 484 คู่ในห้าปีก่อนหน้า เลือกคู่ที่ดีที่สุด แล้วใช้คู่นั้นเทรดไปหนึ่งปี กดแต่ละปีเพื่อดูว่าคู่ที่ถูกเลือกเปลี่ยนไปอย่างไร

  1. 201110/508.2%
  2. 201210/5013.8%
  3. 201310/5011.7%
  4. 201455/2400.8%
  5. 201555/80-0.3%
  6. 2016100/27017.6%
  7. 20175/27018.9%
  8. 20185/270-5.7%
  9. 20195/3005.0%
  10. 20205/16036.9%
  11. 202110/110-10.3%
  12. 202210/1103.8%
  13. 202310/11028.2%
  14. 202410/11012.3%
  15. 202540/1108.6%

ปีทดสอบ 2011-09-26 ถึง 2012-09-25: ฝึกด้วยข้อมูล 2006-09-26 ถึงวันก่อนเริ่มปี เลือก EMA 10/50 (Sharpe ช่วงฝึก 0.76) ปีถัดมาพอร์ตเปลี่ยนไป 8.19%

  • Walk-forward
  • EMA 20/200 คงที่
  • ซื้อแล้วถือ SPY

มูลค่าพอร์ต พันดอลลาร์ เริ่ม 100 ที่ 2011-09-26 · สเกล log

10020040080020122014201620182020202220242026
วันที่
2026-09-25
Walk-forward
381.2
EMA 20/200 คงที่
428.8
ซื้อแล้วถือ SPY
872.2
2011-09-26 ถึง 2026-09-25 · ค่าธรรมเนียม 0.1% ต่อข้าง
วิธีSharpeCAGRMax DDเทรด
Walk-forward0.789.33%-22.5%24
EMA 20/200 คงที่0.8110.19%-26.7%10
ซื้อแล้วถือ SPY0.9515.54%-33.7%—

คะแนนช่วงฝึกใช้ผลตอบแทนรายวันของแต่ละคู่ที่รันต่อเนื่อง · พอร์ต walk-forward เป็นบัญชีเดียว เปลี่ยนคู่ได้เฉพาะวันเริ่มปีทดสอบ และเสียค่าธรรมเนียมเมื่อสถานะเปลี่ยน · ผลรายปีคือการเปลี่ยนแปลงของมูลค่าพอร์ตในปีนั้น

ใน 15 ปี คู่ที่ถูกเลือกเปลี่ยนไป 9 แบบ เริ่มจาก 10/50 ข้ามไป 55/240 และ 55/80 แล้วเป็น 100/270 ต่อด้วย 5/270 จากนั้นกลับมาอยู่ที่ 10/110 สี่ปีติด ถ้าพารามิเตอร์ที่ดีที่สุดมีอยู่จริงและคงที่ เราน่าจะเห็นคู่เดิมหรือคู่ใกล้กันถูกเลือกซ้ำ การที่คำตอบกระโดดไปมาแบบนี้บอกว่าแต่ละรอบ optimization จับลักษณะของห้าปีที่ผ่านมามากกว่าจับกลไกที่ใช้ได้ต่อ

2011-09-26 ถึง 2026-09-25SharpeCAGRMax DDเทรด
Walk-forward เลือกใหม่ทุกปี0.789.33%−22.46%24
EMA 20/200 คงที่0.8110.19%−26.67%10
ซื้อแล้วถือ SPY0.9515.54%−33.72%—

Walk-forward ขาดทุนหนักสุดน้อยกว่า EMA 20/200 แต่ Sharpe และ CAGR ต่ำกว่า และเทรดบ่อยกว่าเกินสองเท่า การ optimize ใหม่ทุกปีจึงไม่ได้ให้ผลดีกว่าการใช้คู่คงที่ที่เลือกมาโดยไม่ดูข้อมูลเลย ข้อดีของ walk-forward อยู่ที่ตัวเลขในตารางเป็นผลที่ทำได้จริง ณ เวลานั้น ทุกปีทดสอบใช้พารามิเตอร์ที่เลือกจากข้อมูลก่อนหน้าเท่านั้น จึงบอกเราได้ตรงกว่าตัวเลข 5/90 ที่เลือกหลังเห็นครบ 20 ปี

ก่อนเชื่อพารามิเตอร์ที่ดีที่สุด

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

  1. แบ่งข้อมูลก่อนเริ่ม กำหนดช่วงฝึก ช่วงทดสอบ ขอบเขตของ grid และตัวชี้วัดที่ใช้เลือกไว้ล่วงหน้า แล้วเขียนเก็บไว้ ถ้าเปิดดูช่วงทดสอบแล้วกลับไปแก้ grid ช่วงทดสอบนั้นก็กลายเป็นส่วนหนึ่งของการฝึกไปแล้ว
  2. รายงานจำนวนครั้งที่ลอง Sharpe 0.72 ที่ได้จากการเลือกจาก 484 คู่ มีความหมายต่างจาก Sharpe 0.72 ของกฎที่คิดไว้ก่อนแล้วรันครั้งเดียว
  3. เทียบกับทางเลือกที่ไม่ต้อง optimize ในบทนี้คือ EMA 20/200 และการซื้อแล้วถือ SPY ในช่วงเวลาเดียวกัน ถ้าคู่ที่ optimize แล้วยังแพ้สองทางเลือกนี้ในช่วงทดสอบ การ optimize ก็ไม่ได้เพิ่มอะไรให้
  4. ดูอันดับ ไม่ใช่แค่ระดับ ตลาดขาขึ้นทำให้ Sharpe ของทุกคู่สูงขึ้นพร้อมกัน อันดับในช่วงทดสอบบอกได้ดีกว่าว่าการเลือกของเรามีค่าหรือเปล่า
  5. ใช้ walk-forward เมื่อตั้งใจจะ optimize ซ้ำ ถ้าในการใช้งานจริงเราจะเลือกพารามิเตอร์ใหม่เป็นระยะ backtest ก็ควรทำแบบเดียวกัน และนับต้นทุนจากการเปลี่ยนคู่ด้วย
  6. ลองกับข้อมูลที่ไม่มีสัญญาณ ถ้ากระบวนการของเราหาผลที่ดูดีได้แม้ในข้อมูลสุ่ม ผลดีบนข้อมูลจริงก็ต้องหักส่วนนั้นออกก่อน

ทุกข้อข้างบนยังอยู่บน SPY ตัวเดียวกับประวัติศาสตร์ 20 ปีชุดเดียว ผลที่ผ่านทุกข้อยังเป็นผลจำลองบนราคาที่ปรับย้อนหลัง ยังไม่รวม spread slippage ภาษี และข้อจำกัดของการส่งคำสั่งจริง

ลองรันเอง

ดาวน์โหลด Notebook ที่รันแล้ว · Python example · ราคา SPY จาก Webull (CSV) · ผลคำนวณ JSON · Markdown บทนี้

lesson_15.py ใช้แค่ Python standard library อ่านราคา SPY จากไฟล์ data/spy-webull-daily.csv ชุดเดียวกับบทที่ 4 ถ้าดาวน์โหลดแยกไฟล์ ให้วาง CSV ไว้ในโฟลเดอร์ data ข้างสคริปต์ ไม่เรียก API ใหม่ รันกี่ครั้งก็ได้ตัวเลขเดิม ใช้เวลาไม่ถึง 10 วินาที

Terminal
shellคัดลอก
python3 lesson_15.py --output-dir output

ลองแก้แล้วรันใหม่ทีละอย่าง

  1. เปลี่ยน IN_SAMPLE และ OUT_OF_SAMPLE ให้แบ่งที่ปี 2011 หรือ 2021 คู่ที่ชนะช่วงฝึกเปลี่ยนไปไหม และอันดับในช่วงทดสอบเป็นอย่างไร
  2. ใน build_results() เปลี่ยน best(ins) เป็น best(ins, 'cagr') เพื่อเลือกด้วย CAGR แทน Sharpe คู่ที่ได้ต่างจากเดิมแค่ไหน
  3. เพิ่ม FEE เป็น 0.002 แล้วดูว่าคู่ที่ EMA เร็วสั้น ๆ อย่าง 5/90 ซึ่งเทรด 46 ครั้งใน 20 ปี เสียเปรียบขึ้นเท่าไร
  4. เปลี่ยน WALK_TRAIN_YEARS จาก 5 เป็น 3 หรือ 8 คู่ที่ถูกเลือกกระโดดน้อยลงหรือมากขึ้น
KNOWLEDGE CHECKข้อ 1 จาก 5
  1. 1ข้อ 1
  2. 2ข้อ 2
  3. 3ข้อ 3
  4. 4ข้อ 4
  5. 5ข้อ 5
ลอง EMA 484 คู่บนข้อมูล 20 ปีทั้งชุด แล้วรายงานคู่ที่ Sharpe สูงสุด 0.84 ตัวเลขนี้มีปัญหาอะไร?
ไม่มีปัญหา เพราะใช้ข้อมูลจริงจาก Webullเป็นผลที่เลือกหลังเห็นข้อมูลทั้งหมดแล้ว จึงรวมส่วนที่ได้จากความบังเอิญของการเลือกSharpe ใช้ประเมิน EMA ไม่ได้
ตรวจคำตอบ

แหล่งข้อมูลและขอบเขต

  1. Yves Hilpisch, Python for Algorithmic Trading: From Idea to Cloud Deployment, O'Reilly, 2020. ใช้ไฟล์ python for algorithmic trading book (js).pdf ที่แนบมา หน้า PDF 87–88 เรื่อง optimize_parameters แบบ brute force กับ SMA บน EUR/USD, หน้า 100–101 เรื่อง Data Snooping and Overfitting และหน้า 118 เรื่องการ fit ในช่วง in-sample แล้วประเมินใน out-of-sample เลขหน้าเป็นลำดับหน้า PDF เริ่มที่ 1
  2. White (2000) A Reality Check for Data Snooping, Bailey, Borwein, López de Prado และ Zhu (2015) The Probability of Backtest Overfitting และบทความเรื่อง overfitting ของ Man Institute อ้างผ่านหนังสือของ Hilpisch หน้า PDF 100–101 เท่านั้น เรายังไม่ได้อ่านต้นฉบับของงานเหล่านี้
  3. Webull Thailand · List Stock Historical Bars ที่มาของราคา SPY รายวัน ดึงเมื่อ 28 กันยายน 2026 ตามรายละเอียดและ checksum ในบทที่ 4 บทนี้ใช้ไฟล์เดิมโดยไม่ดึงใหม่
  4. โครงหัวข้อของคอร์ส Foundations of Alpha: The Theory Behind Algorithmic Trading (Hudson and Thames) ส่วน Research และ Risks in Models และคอร์ส Algorithmic Trading A-Z with Python, Machine Learning & AWS ส่วน Finding the optimal SMA Strategy, Overfitting และ Out-Sample Forward Testing ใช้เป็นแนวในการเรียงเนื้อหา ตรวจจากหน้าแนะนำคอร์สเมื่อ 29 กันยายน 2026 ไม่ได้ใช้เนื้อหาในวิดีโอ

การทดลองทั้งหมดในบทนี้ ทั้ง grid 484 คู่ การแบ่งช่วง ตลาดสุ่ม 60 โลก และ walk-forward เป็นการออกแบบใหม่สำหรับบทนี้ ตัวเลขทุกตัวคำนวณจาก lesson_15.py ภาพเรื่องเล่า Quantara สร้างด้วย Codex CLI โดยใช้ภาพเรื่องเล่าของบทที่ 3 เป็นต้นแบบสไตล์ เป็นภาพประกอบเรื่องสมมติ ไม่ได้แสดงข้อมูลใด ๆ

© 2023 by QuantCorner, Thailand.

bottom of page