Introduction to Backtesting with VectorBT

วันนี้เราจะมาทดลองใช้ backtesting library ที่กำลังโด่งดัง หลายปีที่ผ่าน Python มี coumunity algorithmic trading(ผมไม่เรียกว่า quant นะเพราะมันไม่ใช่) ที่ค่อนข้างแข็งแรง และมี libraryโด่งดังมากมาย ตั้งแต่ pyalgotrade ที่เริ่มจากช่วงต้นปี 2010s หลังจากปี 2010 มาก็ zipline backtrader ตอนนี้หลายๆตัวก็ได้หยุดการพัฒนาไปแล้วจนถึงเมื่อไม่กี่ปีก่อนก็จะมี backtsting.py และ vectorbt ที่ร้อนแรงขึ้นมาครับ และก็น่าจะมีแค่ 2 ตัวนี้ที่ project ต่างๆบน github ยัง active อยู่

เราเคยดู backtestingวันนี้เราจะมาดู vectorbt กันหน่อย จุดเด่นของมันก็คือการดีไซน์การเทสหุ้นแบบเป็น vector และการ optimize ความเร็วมาให้เร็วอย่างมาก ตอนนี้น่าจะเป็น lib หลักสำหรับมือใหม่แล้ว(มือเก่าๆอาจจะขี้เกียจเปลี่ยนจาก backtrader etc) ในบทความนี้เราจะดูถึงพื้นฐานของมันว่าใช้งานอย่างไร และในบทความต่อๆไป ว่า เราจะ optimization parameter อย่างไร จะเทสหุ้นแบบ multi assest อย่างไร multi strategy อย่างไร ในบทความต่อๆไปครับ

แต่มีคำกล่าวอยู่ว่า “ถ้ายังต้องพึ่ง pre-built package พวกนี้และไม่เข้าใจว่าทำงานยังไง แปลว่าคุณไม่รู้เลยว่าคุณทำอะไรอยู่” ฉะนั้นจะรู้ให้ลึกจริงก็ต้องทำเองครับ ถึงจะมีบั๊คบ้างก็ไม่เป็นไร ขอให้เรารู้ว่ามันทำงานอย่างไร แต่ละตัววัดเช่น max drawdown, sharpe ratio, cagr ทำงานอย่างไรได้ในคอร์ส Pythonfor Finance ของเราได้ครับ ที่ https://algoaddict.com/p/python-for-finance

แต่อย่างไรก็ดีเรามาดูบทความนี้กันได้เลยครับ

!pip install vectorbt

ก่อนอื่นถ้ายังไม่ได้ลง package ก็ไปลง package กันก่อนครับ

import vectorbt as vbt

ticker= "AAPL"
data = vbt.YFData.download(symbols=ticker)
data

เมื่อลงเสร็จแล้วก็ดึง vectorbt มาใช้งาน ใช้ชื่อว่า vbt เราสามารถใช้มันโหลดข้อมูลได้ด้วย return มาจาก function built-in ของมันโดยไม่ต้องไปผ่าน Library ตัวอื่นเช่น Yfinance เพราะ vbt ได้รวมมันมาให้เราใช้งานแล้ว

สิ่งหนึงที่ผมชอบ คือ vbt เค้าจะมีคอมเม้นของ data providor ให้เราด้วยเช่นถ้าผมไปเรียก vbt.YFData(yahoo finance data) เค้าก็จะมีคอมเม้นไว้บอกเล็กๆน้อยๆเป็นประโยชนืกับผู้ใช้งานด้วย เช่น ดาต้าจากเจ้านี้ไม่ค่อยดีเท่าไหร่นะ yahoo เค้าอาจจะมีการปรับปรุงค่าย้อนหลังตามใจเค้าได้

อย่างไรก็ดีหลังจากเราดึงข้อมูลหุ้นที่ต้องการมาแล้ว เก็บไว้ในตัวแปร data เราจะเห็นว่าเมื่อดูข้อมูลดาต้า สิ่งที่ return มาจาก function ไม่ใช่ข้อมูลดิบที่ออกมาเป็น dataframe แบบที่เราคุ้นชินใน package ตัวอื่น แต่จะมาเป็น object ของ class vbt อีกทีหนึง

data.data

ถ้าเราลอง data. ตามด้วยปุ่ม tap เพื่อเรียกดูว่ามี function ให้เลือกใช้หลายตัว เราจะเห็นตัวหนึงชื่อว่า data.data ตรงนี้จะเป็นการเข้าไปหาข้อมูลดิบที่มาจาก data provider

df = data.get(["Open", "High", "Low", "Close"])
df

เราสามารถใช้คำสั่ง data.get() ตามด้วยคอลัมที่เราต้องการได้

Simulation Basic

ticker = ["AAPL"]
start = '2015-12-31'  
end = '2020-12-31'

df = vbt.YFData.download(symbols=ticker, start=start, end=end, missing_index="drop").get("Close")

เรามาเริ่มใช้งาน vbt กันโดยผมจะดึงข้อมูลหุ้น APPLE มาจากปี 2016 ถึงปี 2020

rsi = vbt.RSI.run(close=df, window=[14])
print(rsi)

vbt มี function built-in สำหรับสร้าง indicator หลักๆอยู่ไม่กี่ตัวให้เราเลือกใช้ ผมเลือกใช้ .RSI ที่เอาไว้สร้าง indicator relative strenght index(rsi) ส่วน parameter ที่เค้ากำหนดให้ใส่ก็มีแค่ ราคาปิดที่เอาไว้ใช้คำนวณ กับ window คือระยะเวลาที่นำมาคำนวณ rsi,

*14 เป็นตัวเลขที่ผมเลือกไปเล่นๆ ความจริงความคิดเบื้องหลังตัวเลขนี้คือจำนวน วันในการเทรดสินทรัพย์ที่มีการเทรดทุกวัน คนคิดเค้าเลยเลือก 14 แต่หลายครั้งที่เราสร้างกัน เรามักจะเลือกตัวเลขนี้แม้จะไม่มีเหตุผลมารองรับอะไร ในที่นี้เราใช้ APPLE ที่เป็นหุ้น มีการเทรด 5 วันต่อสัปดาห์ ถ้าสมมุติฐานของคุณคือ 2 อาทิตย์เลขนี้ใช้ไม่ได้นะครับ

*อันที่จริงเราก็ไม่ควรเสียเวลามากไปที่จะต้องไปนั่งหา indicator เทพอยู่แล้วนะครับเพราะสิ่งสำคัญคือการหา feature ที่มี edge หรืออีกคำหนึงคือ สามารถสร้าง alpha และ test สมมุติฐานนั้นมากกว่า คิดค้น indicator เทพที่ส่วนใหญ่จะเป็นการใช้ backtest เป็น researching tool มากกว่า(ปัญหาใหญ่ของการ backtest ผิดวิธี ซึ่งนิยมกันมากทั้งไทยและเทศ)

rsi.rsi_stats()

เหมือนกับตัวอื่น สิ่งที่ return มาจาก function ของ vbt คือจะไม่ได้ return เป็นข้อมูลดิบ แต่จะเป็น object ให้เราใช้ฟังก์ชั่นอื่นๆต่อได้ เช่น ผมทดลองใช้ rsi.rsi_stats() เพื่อดูค่าสถิติของ indicator ตัวนี้เบื้องต้น เราก็จะเห็นภาพรวมว่าสินทรัพย์ตัวนี้มีหน้าตายังไง(ผ่าน indicator)

rsi.rsi

ค่าของ rsi ดิบๆจะอยู่ใน rsi.rsi

*อันนี้ต้องคอมเม้นคนเขียน package นี้นะครับ ว่าทำไมไม่ใช้คำกลางอย่าง value เพราะต่อไปเมื่อคุณไปคำนวณตัวอื่นเช่น moving average ค่าของมันก็จะเปลี่ยนไปเป็น .ma อีก ซึ่งตามหลักการทำฟังก์ชั่นมันจะไปเพิ่มความวุ่นวายโดยไม่จำเป็นได้ในบางกรณี

entry = rsi.rsi_crossed_below(30) 
entry[entry == True]

rsi.rsi_crossed_below() เป็น function built-in อีกตัวของ indicator ตระกูล vbt ซึ่งมีหลายแบบ เช่น ตัดลงล่าง ตัดขึ้นบน ปิดบน ปิดล่าง อะไรพวกนี้ อย่างไรก็ดีผมเลือกค่า 80 สำหรับ overbought ค่าที่ return มาเป็น boolean ว่าวันนั้นๆ rsi ไปเกินค่านี้จริงๆไหม ผมเรียกเฉพาะวันที่เป็นจริงมาดู ก็จะพบว่ามีตรงตามเงื่อนไขประมาณ 29 วัน เราจะใช้ตรงนี้ปเป็นสัญญาณ์ซื้อขาย

exit = rsi.rsi_crossed_above(80)
exit[exit == True]

rsi.rsi_crossed_above() ก็เช่นเดียวกัน มีตรงตามเงื่อนไข 40 กว่าวัน

pf = vbt.Portfolio.from_signals(close=df, entries=entry, exits=exit, freq="1d")
pf.stats()

vbt.Portfolio.from_signals() จะให้เรากำหนดการสร้าง portfolio จากสินทรัพย์ที่เราเลือก เค้าก็จะให้เรากำหนด จุดเข้าซื้อ entries กับจุดออก exits จริงๆแล้วการทำงานของมันก็คือไป map กับ boolean วันที่เงื่อนไขนั้นเป็นจริงครับ

พอเสร็จแล้วเราสามารถเลือก pf.stats() เพื่อดูผลงานของ strat ที่เราสร้างมาได้ มีทั้ง ความแม่นยำ ผลกำไร เทรดกี่ตัว drawdown เป็นอย่างไร descriptive statistics ต่างๆ sharpe calmar omega sortino ratio เป็นต้น

pf.plot().show()

ถ้าพล๊อตดูก็จะได้การ visual ที่สวยงามและเป็น interactive (ซึ่งไม่จำเป็นแต่คนชอบกันมาก!!!) จาก plotly มีทั้งเทรดแต่ละรอบได้ผลกำไร/ขาดทุนแค่ไหน return หน้าตาเป็นยังไง

สรุป

ผมคิดว่า vbt เขียนมาได้ดี optimize ได้ดี การทำงานแบบ vector ซึ่งจะมาพร้อมกับความเร็ว คุณสามารถสร้าง strat 100 แบบมาเทสกับหุ้น 1000 ตัวได้อย่างรวดเร็ว ด้วย concept ของความเป็น vector นั่นเอง โค้ดเดียวกันนี้คุณลองไปเพิ่มจำนวนหุ้นดูสิ่ครับ

ticker = ["AAPL", "MSFT", "MMM"]
start = '2015-12-31'  
end = '2020-12-31'

df = vbt.YFData.download(symbols=ticker, start=start, end=end, missing_index="drop").get("Close")

จากนั้นรันใหม่ ตูม เทส 3 หุ้นแปปเดียวเสร็จ!!

นอกเรื่อง ควรใช้งานใน Colab หรือ Local Machine

จริงอยู่ว่าเราอาจจะไปลงมันบน platform อย่าง google colab ก็ได้ แต่สำหรับคนที่อยากจะจริงจังหน่อย ก็ควรลงไว้ในเครื่องตัวเองนะครับ ถ้าลงติดปัญหาอะไรก็ต้องแก้ไปส่วนใหญ่เอา Error ไปเสริชกูเกิลก็เจอแล้ว อย่างน้อยก็ควรลง package บนเครื่องตัวเองให้ได้ครับ

ความเข้าใจผิดกับวงการบ้านเรา

ผมได้รับคำถามหลังไมค์มาเสมอ ว่าทำไมไปสนใจเรื่องไม่เกี่ยว้ของ เช่น มากกว่าจะมาสนใจการใช้งาน quant library (ในมุมมองเขา) อย่าง vectorbt pyalgotrade backtrade จริงๆแล้วไอ้พวกนี้มันเป็นแค่ tool ครับ ใน industry จริงๆกับบริษัทที่ได้มาตราฐานเค้าไม่ได้ใช้กันหรอกครับ เห็นได้จากการที่ vbt เค้าพยายาม visualize ให้สวยด้สน plotly เพราะมันดู COOL!!! ในสายตาเราเท่านั้นเองครับ เป้าหมายของเค้าคือรายย่อยตั้งแต่ต้นแล้ว แม้ว่าจะดีไซน์มาได้ดีก็เถอะ

ใน industry ไม่ได้ใช้กันครับ ส่วนใหญ่จะเป็น in-house-framework ด้วยซ้ำไป ถ้าเราอยากจริงจังมากๆก็อาจจะต้องทำ simulation เองเพื่อให้ตอบสนองไอเดียเราเต็มที่จริงๆ(ข้อเสียคืออาจจะมีบั๊ก)ดังคำพูดเพื่อนในวงการ “ถ้าคุณยังต้องให้คนอื่นเขียน prebuilt function เพื่อทำสิ่งเบสิคต่างๆให้คุณเพราะคุณทำเองไม่ได้แล้วล่ะก็ แปลว่าคุณไม่ได้รู้ตัวด้วยซ้ำว่าคุณทำอะไรอยู่”

สุดท้ายแล้ว ผมว่าเราใช้ vectorbt เมื่ออยากจะได้เทสไอเดียไวๆครับ แต่ถ้าอยากจะจริงจังต้องสนใจ อย่างอื่น ครับ ไม่ใช่แค่กด api ของ tool ต่างๆให้ชำนาน มันก็ไม่ได้ทำให้สกิลของเราในด้านนี้เพิ่มขึ้นมาแต่อย่างใด!!!

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s