Trend forecasting stock price for SET100

กลับมาอีกครั้งกับ BotnoiWeeklyProject หลังจากที่หายไปทั้งหมด 2 อาทิตย์ สำหรับบทความที่แล้วเป็นอย่างไรกันบ้างครับ มีเพื่อนๆคนไหนได้ทดลองใช้งาน API ของเรากันบ้างแล้วรึยัง??? สำหรับใครที่สนใจตามไปอ่านกันได้ที่ https://medium.com/botnoi-classroom/botnoiweeklyproject-ii-8ed8da17e753

สำหรับครั้งนี้เป็นครั้งที่ 3 แล้ว มารอบนี้พวกเรากลุ่มที่ 15 จะมาชี้ช่องหาทางรวยกันดีกว่าครับ !!!

เชื่อว่าหลายๆคน คงเคยได้ยินหรือบางท่านเคยมีประสบการณ์ในการลงทุนมาไม่มากก็น้อยใช่ไหมหละครับ บ้างก็กำไร บ้างก็ขาดทุน หรือบางคนอาจเคยได้ยินคำว่าติดดอยหรือเคยติดเองมาบ้างซึ่งคงไม่อยากมีใครให้เกกิดขึ้น เพราะนั่นมันหมายถึงว่าเราขาดทุนแน่ๆ T^T แต่!!! จะดีแค่ไหนที่ Project รอบนี้เราจะพาทุกๆคน ไปทำนายราคาของหุ้นโดยใช้ Trend forecasting กัน เราจะสามารถวิเคราะห์ช่วงเวลาที่เหมาะสมในการซื้อขายได้ดียิ่งขึ้น ลดโอกาสในการขาดทุนได้สูงขึ้น แต่แน่นอนว่า Project ของ Botnoi ไม่เคยมีความธรรมดาครับ ถ้าจะให้เราทำนายอย่างเดียว มันจะไปสนุกอะไร

รอบนี้เราจะเอาผลลัพธ์ที่เราทำนายได้มาซื้อหุ้นกันจริงๆเลยดีกว่า ^-^ (รวยกันเห็นๆ)

Image for post
Image for post
SET100

โจทย์

รอบนี้ทางทีม Botnoi จะให้งบแต่ละทีม ทีมละ 10000 บาท เป็นทุนในการซื้อหุ้นที่อยู่ในดัชนี SET100 ที่เราคิดว่าจะทำกำไรให้เราได้มากที่สุดภายในเวลา 7 วัน โดยให้ซื้อหุ้นที่ราคา ATO ในวันที่ 17/09/2563 และขายหุ้นที่ราคา ATO ในวันที่ 24/09/2563 ส่วนใครจะเลือกหุ้นตัวไหน ก็ตามแต่ละกลุ่มตัดสินใจ

แต่แน่นอนว่าทางทีม Botnoi ไม่ได้ใจร้ายขนาดให้แต่ละกลุ่มต้องคิดทุกอย่างเองขนาดนั้น Botnoi มีสอนพวกเราในหัวข้อเรื่อง การใช้ Trend forecasting เพื่อการทำนายราคาหุ้นมาบ้างแล้ว โดยให้ทุก ๆ กลุ่มนำไปประยุกต์ใช้

Trend Forecast Stock Price

1. Get Data

สำหรับ library ที่เราจะใช้ในการดึงข้อมูลหุ้นแต่ละตัวนั้นก็คือ “starfishX” ซึ่งเป็น Library สำหรับประมวลข้อมูลตลาดหุ้นไทยทั้งปัจจัยพื้นฐานและข้อมูลการซื้อขายรายวัน

วิธีใช้งานก็ง่ายมาก

Image for post
Image for post
Install starfishX
Image for post
Image for post
function loadHistData

ใช้ function loadHistData โดยใส่ชื่อของ หุ้น, วันที่เริ่มต้น , OHLC , Volume ลงไป

Image for post
Image for post
วิธีดึงข้อมูลหุ้น AOT

โดยจะเราใช้ function loadHistData() ในการดึงข้อมูลของหุ้นตัวที่สนใจ โดยมี argument ที่เราจะใช้หลัก ๆ คือ
1.stock คือ รายชื่อของหุ้นที่เราสนใจ
2.start คือ วันที่เริ่มต้นของข้อมูลที่เราสนใจ
3.OHLC คือ แสดงราคาที่ Open (ราคาเปิด) , High (ราคาปิด) , Low(ราคาต่ำสุด) , Close (ราคาปิด) ของแต่ละวัน
4.Volume คือ แสดงปริมาณการซื้อขายของแต่ละวัน

ภาพด้านบนแสดงให้เห็นการดึงข้อมูลหุ้นของ “AOT” ตั้งแต่วันที่ 1 มกราคม ปี 2016

Image for post
Image for post
df.head()

หากข้อมูลมีไม่ถึงวันที่ 1 มกราคม ปี 2016 ก็จะดึงข้อมูลตั้งแต่วันที่เริ่มมีข้อมูล เช่น AOT มีข้อมูลเริ่มต้นวันที่ 4 มกราคม ปี 2016 จึงไม่ต้องเป็นห่วงว่าจะไม่สามารถดึงข้อมูลของหุ้นที่เข้าตลาดหุ้นหลังจากที่เราสนใจออกมาได้

2. Cleansing & transformation

2.1 extract feature and label

เรา extract Date ออกมาเป็น feature Year , Month , Day และ EnglishDay ให้กับชุดข้อมูลของเรา เนื่องจากทางกลุ่มเรามองว่า วันที่ เดือน ปี หรือแม้กระทั่ง วันจันทร์ อังคาร พุธ พฤหัส ศุกร์ ก็มีผลต่อราคาของหุ้นได้ เนื่องจากเราเชื่อว่าพฤติกรรมการซื้อขายของคนส่วนใหญ่อาจแตกต่างกันในแต่ละวัน เช่น บางคนอาจจะไม่มีเวลามาดูหุ้นเลยจนกระทั่งถึงวันศุกร์ที่อาจจะมีเวลาว่างมากพอเนื่องจากเป็นวันสุดท้ายของสัปดาห์ เป็นต้น

Image for post
Image for post
extract feature

2.2 one-hot-encoder

แต่เท่านี้ก็ยังไม่เพียงพอเนื่องจาก การที่เราจะนำ feature เหล่านี้เข้าโมเดลของเราได้จะต้องทำให้เป็นตัวเลขเสียก่อน แต่ column EnglishDay ของเรายังคงเป็น String อยู่เลย

อีกทั้งตัวเลขของ column Month และ Day ก็น่าจะต้องทำอะไรสักอย่างเพิ่มเติม เนื่องจากในเรื่องนี้เราจัดข้อมูลวันที่และเดือนเป็นตัวเลขที่ไม่สามารถใช้การดำเนินการทางคณิตศาสตร์ได้ (< > + — * /) เราจึงไม่สามารถสร้าง column วันที่และเดือนแล้วใส่ข้อมูลลงไปเพื่อ train โมเดลได้ เพราะหากเราใส่ตัวเลขตามนี้ลงไปในโมเดล โมเดลของเราอาจตีความว่า วันที่ 4 มีค่าต่อโมเดลมากกว่าวันที่ 1 หรือ เดือนที่ 1 มีค่าต่อโมเดลน้อยกว่า เดือนที่ 2 ซึ่งในแง่ความเป็นจริงแล้วค่าที่ส่งผลของวันที่ 1 หรือวันที่ 4 ไม่ควรจะมีค่าเป็นมากกว่าหรือน้อยกว่าได้

หากใครยังงง ๆ ว่าทำไมเรื่องนี้ถึงนำค่าวันที่มาเปรียบเทียบในทางคณิตศาสตร์ไม่ได้ เราจะอธิบายโดยใช้เรื่อง Seasonality ของข้อมูล

Image for post
Image for post
กราฟแสดง Seasonality ของหุ้น ‘AOT’

จากรูปเราได้ทำการ decompose seasonality ของหุ้น AOT ออกมาเป็นระยะเวลาสามเดือน ซึ่งเราจะสังเกตได้ว่าลักษณะของกราฟมีลักษณะขึ้นลงเป็น pattern ที่ไม่มีความเกี่ยวข้องกับลำดับของวันที่

วิธีแก้ปัญหาเหล่านี้คือการทำ one-hot-encoder หรือที่เรียกอีกชื่อว่า dummy variable ซึ่งเป็นการเปลี่ยนข้อมูลแบบ Categorical data ที่อยู่ใน column เดียวกันให้แยกออกมาเป็นคอลัมน์ย่อย ๆ โดยเก็บข้อมูลแบบ Binary เสมือนเป็นการเพิ่มคอลัมน์แล้ว flag ดังภาพด้านล่าง

Image for post
Image for post
One-hot-encoding with Month, Date

3. Feature engineer: Incorporate previous day price and volume features

“Feature Engineering เป็นการหาความสัมพันธ์ระหว่างข้อมูล เพื่อหา feature ที่จะนำมาใช้ในการสร้าง model ”

เนื่องจากทีมของเราตั้งสมมติฐานว่า ราคาปิด(Close) เปิด(Open) ต่ำสุด(Low) สูงสุด(High) และ ขนาด (Volume) มีผลต่อกันและกันและผลของวันก่อนหน้าก็มีผลกับราคาของวันนี้

ดังนั้น label ของเราจะเป็น ราคาปิด(Close) เปิด(Open) ต่ำสุด(Low) สูงสุด(High) และ ขนาด (Volume)

ส่วน feature ก็คือ ราคาปิด(Close) เปิด(Open) ต่ำสุด(Low) สูงสุด(High) และ ขนาด (Volume) ของวันก่อนหน้า

แล้วมันมีผลอย่างไรหากเราไม่ทำ feature engineering แน่นอนว่า หากเราไม่ทำ feature engineering หมายความว่า feature ที่เรามาใช้ทำนาย model จะมีแค่วัน และเดือน ของราคาหุ้นที่ดึงมา

Image for post
Image for post
ภาพตัวอย่างการ train model แบบไม่ใช้ feature engineering

เส้นสีส้มคือเส้นที่เรา predict เป็นอย่างไรบ้างครับ มีใครคิดจะเอาโมเดลนี้ไปซื้อหุ้นจริงๆ บ้างไหมครับ ผมแนะนำว่าอย่าดีกว่า 555

4. Model

สำหรับ model ที่เราใช้ในการ predict รอบนี้เราใช้ LinearRegression เป็นตัวพยากรณ์หลัก และสำหรับบางตัวอาจใช้เป็น RandomForestRegression ซึ่งบางคนอาจสงสัยว่า ทำไมต้องใช้หลายตัว??? เราจะมีอธิบายให้ฟังนะครับ

Image for post
Image for post
Model

ในที่นี้เราใช้ MultiOutputRegressor มาด้วย เนื่องจาก label ของเราเป็น multi-label ด้วย คือ เราทำนายทั้ง ราคาปิด เปิด สูงสุด ต่ำสุด และ ขนาด

สำหรับการตรวจสอบ performance ของ model เราใช้ RMSE (root mean square error)

เราไปดูผลกันดีกว่า

กำหนด parameter ต่าง ๆ ที่ต้องใช้โดย
stock_symbol คือ ชื่อหุ้นที่เราสนใจ
start_date คือ วันเริ่มต้นที่จะดึงข้อมูล
feat_eng = True คือทำ feature_engineer
target_buy, target_sell คือวันที่จะถือการซื้อและขาย

Parameters

ทำไมถึงใช้ OHLC = CLOSE กันล่ะ ? แล้ว PrevDays คืออะไร ?

Image for post
Image for post
ภาพตัวอย่างกรณี OHLC = ‘Close’

เดิมทีทางกลุ่มของเราต้องการทำเพื่อแยกไปเลยว่า ถ้าหากเราจะทำนาย

ราคาปิด ให้ใช้ feature เฉพาะราคาปิดของวันก่อนหน้าเท่านั้น,

ราคาเปิด ให้ใช้ feature เฉพาะราคาเปิดของวันก่อนหน้าเท่านั้น

ราคาสูงสุด ให้ใช้ feature เฉพาะราคาสูงสุดของวันก่อนหน้าเท่านั้น

ราคาต่ำสุด ให้ใช้ feature เฉพาะราคาต่ำสุดของวันก่อนหน้าเท่านั้น

ขนาด ให้ใช้ feature เฉพาะขนาดของวันก่อนหน้าเท่านั้น

แต่กลุ่มเราเกรงว่าอาจจะทำไม่ทัน จึงมองส่วนนี้เป็นสำหรับการพัฒนา Model ในขั้นต่อๆไปมากกว่า

เราจึงตั้งสมมติฐานใหม่ว่า ราคาปิด(Close) เปิด(Open) ต่ำสุด(Low) สูงสุด(High) และ ขนาด (Volume) มีผลต่อกันและกันและผลของวันก่อนหน้า x วัน ก็มีผลกับราคาของวันนี้
โดยให้พารามิเตอร์ PrevDays เป็นตัวเก็บค่า x
ซึ่งในที่นี้เราจะใช้ prevDays = 3 คือการบอกว่าเราจะดึงข้อมูลราคาปิด(Close) เปิด(Open) ต่ำสุด(Low) สูงสุด(High) และ ขนาด (Volume) ของ 3 วันก่อนหน้ามาใช้เป็น feature

Image for post
Image for post
Previous label day

เราไปดูผลลัพธ์ของการใช้ model กันดีกว่าครับ

Image for post
Image for post
AOT stock price

เส้นประสีแดงคือตัวที่แบ่งว่า เราใช้ ข้อมูลฝั่งซ้ายทั้งหมดเป็นตัว Train ขณะที่ฝั่งขวา คือฝั่ง Test

ว้าวววววว score ก็โอเคอยู่นาาา

ทีนี้เราต้องนำมันไป predict ในวันล่วงหน้าที่เราไม่ทราบอะไรเลยนี่นา ต้องทำยังไงละทีนี้???

แน่นอน กลุ่มเรามองว่า เราก็แค่นำโมเดลตัวนี้ ไปทำนายในวันถัดไปเรื่อยๆ เช่น วันนี้วันที่ 14 เราก็นำโมลเดลไปทำนายวันที่ 15 โดยหา ข้อมูลราคาปิด(Close) เปิด(Open) ต่ำสุด(Low) สูงสุด(High) และ ขนาด (Volume) ของ 3 ก่อนหน้า นั่นก็คือ วันที่ 14, 13, 12 มาใช้ หลังจากนั้นก็ update มันลงใน label พอเราต้องการทำนายในวันที่ 16 ก็นำค่าที่ทำนายได้จากวันที่ 15 และ ข้อมูลวันที่ 14, 13 มาใช้เป็น feature ทำไปเรื่อยๆ จนถึงวันที่เราจะขาย นั่นก็คืือ 2020–09–24

Image for post
Image for post
Forecasting stock price for SET100

เรารันโมเดลของเราจนครบ SET100

ปัญหาต่อไปแล้วเราจะเลือกหุ้นตัวไหนที่ได้กำไรมากที่สุดได้อย่างไร???

กำหนดวันที่ซื้อคือ 2020–09–17 และวันที่ขายคือ 2020–09–24

แน่นอนบางท่านอาจดูที่ราคาปิดของวันที่จะขาย-ราคาปิดของวันที่ซื้อเลยก็ได้ แต่ทางกลุ่มของเราเลือกที่จะดู worst case หมายความว่า

ในวันที่ 2020–09–17 กลุ่มของเราสันนิษฐานว่า เราจะซื้อได้ในราคาที่แย่ที่สุดของวัน นั่นก็คือเราซื้อได้ในราคาที่แพงที่สุด คือ ราคาสูงสุด(High)

ในวันที่ 2020–09–24 กลุ่มของเราสันนิษฐานว่า เราจะขายได้ในราคาที่ถูแย่ที่สุดของวันนั่นก็คือ เราซื้อได้ในราคาถูกที่สุด คือ ราคาต่ำสุด(Low)

ดังนั้น worst case คือ ราคาที่เราขายได้-ราคาที่ซื้อได้ แน่นอนว่าถ้า worst case ของเรายังคงกำไร นั่นหมายความว่า เราก็น่าจะมีโอกาสทำกำไรได้มากกว่านี้

Image for post
Image for post
ราคา ณ วันที่ ซื้อและขาย

ราคา ณ วันที่ ซื้อและขาย

high_buy หมายถึง ราคาสูงสุด ณ วันที่ซื้อ

low_sell หมายถึง ราคาต่ำสุด ณ วันที่ขาย

worst_profit คือกำไรต่อหุ้นที่ได้จากการซื้อหุ้นในราคาสูงสุด ณ วันที่กำหนดให้ซื้อและขายหุ้นที่ราคาต่ำสุด ณ วันที่กำหนดให้ขาย (low_sell-high_but)

เรามีเงินทุนอยู่ 10000 บาท จากบอทน้อย ^^

ก็มานั่งหากันดีกว่าว่า เราจะซื้อได้กี่หุ้น นั่นก็คือ

amount คือจำนวนหุ้นที่ซื้อได้ (np.floor(10000/ราคาสูงสุด ณ วันที่ซื้อ))

invest คือจำนวนเงินที่ใช้ลงทุน (จำนวนหุ้นที่ซื้อได้ * ราคาสูงสุด ณ วันที่ซื้อ)

money_back คือเงินที่ได้จากการขายหุ้น(จำนวนหุ้นที่ซื้อได้ * ราคาต่ำสุด ณ วันที่ขาย)

profit_back คือกำไรที่ได้ ( จำนวนหุ้นที่ซื้อได้ * worst_profit หรือ เงินที่ได้จากการขายหุ้น — จำนวนเงินที่ใช้ลงทุน)

Image for post
Image for post
worst_case profit

กำไรที่ให้มากที่สุดคือ หุ้น Gulf

แต่ ๆๆ !!! จำได้ไหมครับที่ผมบอกว่า แต่ละตัวจะใช้โมเดลต่างกัน ซึ่งหลังจากที่กลุ่มเราทดลองแล้วพบว่า ACE, AWC, CRC, DOHOME, GULF, RBF ชื่อหุ้นเหล่านี้คือกลุ่มที่ต้องใช้ model RandomForestRegressor ในการพยากรณ์

ถามว่า มันมีปัญหาอะไร???

หุ้นกลุ่มพวกนี้คือกลุ่มที่ เพิ่งเข้ามาในตลาดหุ้นได้ไม่นานทำให้มี Data มาใช้ Train โมเดลน้อย หรือ เข้า LinearRegression โมเดลแล้ว ทำให้ค่า ที่ predict ได้มีค่าติดลบที่บางตัวแปร เช่น Volume

Image for post
Image for post
ภาพตัวอย่างเมื่อเทรนหุ้น RBF ด้วย LinearRegression

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

Image for post
Image for post
GULF stock price

จะเห็นว่า ถึงแม้ score จะสูงมากถึง 0.96 ก็ตาม แต่ในขั้นตอนการ Test ยังมี error ที่สูงมาก อาจบ่งบอกถึงการเป็นพวก overfitting เนื่องจากข้อมูลที่น้อย ซึ่งน่าจะต้องปรับปรุงโมเดลต่อไป

กลุ่มของเราจึงคิดว่าควรตัดกลุ่มพวกนี้ออกไปก่อนในการทำ Project ครั้งนี้

ฉะนั้น หุ้นที่พวกเรามองว่าน่าจะให้กำไรมากที่สุด เมื่อซื้อวันที่ 2020–09–17 และขายวันที่ 2020–09–24 ก็คือ KBANK โดยคาดหวังกำไรที่ 1884 บาท

เป็นอย่างไรกันบ้างครับ มีใครเห็นช่องทางรวยกันแล้วบ้างครับ อิอิ

ทั้งหมดทั้งมวลนี้ กลุ่มเราคงทำเองไม่ได้เลยหากขาดความช่วยเหลือจากทีมงานบอทน้อยและทุกๆคนในกลุ่ม

Team member:

  1. เพชรกฤษณ์
  2. ธนภัทร
  3. ณัฐพจน์
  4. กัลยรัตน์
  5. ธนบดินทร์
  6. อภิสิทธิ์
  7. อรวรรณ
  8. เพิร์ธ
  9. ณัฐรัชต์
  10. ภควรรฒน์
  11. ณฐพล
  12. กมลภัทร
  13. Peerapat
  14. ธราวัฒน์
  15. Mig
  16. ภัทริญญา
  17. ปรินดา
  18. ณัฐสกล

Special Thanks : ขอบคุณทางทีมงาน Botnoi ทุกคน ที่ร่วมกันสร้าง Classroom นี้ขึ้นมา ถือเป็นการมอบโอกาสและประสบการณ์ที่ดีมากๆให้กับพวกเรา ขอบคุณครับ

Reference :

https://colab.research.google.com/drive/1yyikirkHGGpamatyNFzeAEYhia4Ta47w?usp=sharing

botnoi-classroom

This publication consists of articles related to Data…

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch

Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore

Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store