Python Basic : Portfolio (Equal Weight vs Random Weight)

ในบทความนี้เราจะมาทำการทดลองในส่วนของ  “การจัดสรรพอร์ตฟอลิโอ” กันครับ มาดูว่าการจัดพอร์ตนั้นมันมีผลกับ กำไร และ ความเสี่ยง ของเรามากน้อยแค่ไหน และ มาดูกันว่า เจ้าคำพูด “Higher Risk = Higher Return” น่ะ มันจริงเท็จแค่ไหน! การทดลองนี้ผมเขียนขึ้นด้วย Python โค้ด สามารถนำไปทดลองใช้ได้เลยครับ ยินดีแบ่งปันครับ ขอแค่ได้ผลอย่างไร อย่าลืมมาพูดคุยแลกเปลี่ยนกันบ้างนะครับ ผมจะยินดีมากถ้าได้แลกเปลี่ยนความคิดเห็นกับนักวิจัยท่านอื่นๆ ครับ

มาเริ่มกันดีกว่า …

1) import libraries  ที่จำเป็นต้องใช้งาน

01

ก่อนอื่นเราต้อง import Library ที่จำเป็นต้องใช้ ในที่นี้ คือ

  • numpy คือ Library ทีเอาไว้ทำงานกับตัวเลข
  • pandas คือ Library ที่เอาไว้จัดการกับข้อมูลที่สำคัญมากตัวหนึง
  • matplotlib คือ Library สำหรับการจัดการเรื่องการพล๊อตรูปของ Python
  • pandas_datareader คือ Library ที่แยกตัวมากจาก pandas มีหน้าที่คอยดึงข้อมูลจากเวป
  • seaborn คือ คือ Library ที่ช่วยให้การแสดงผลจากสถิติสวยงามและหลากหลายขึ้น

2) โหลดดาต้าจาก Yahoo Finance และเก็บใส่ DataFrame

02

ก่อนอื่นเราจะมาโหลดดาต้า ต่างจากวันก่อนที่เราจะโหลดไปตาม Default วันนี้เราจะมากำหนดการโหลดแบบเริ่มตั้งแต่วันปัจจุบัน ย้อนหลังไป 5 ปี นะครับ โดยการสร้างตัวแปร start และ end มากำหนดเวลาที่เราต้องการจะโหลด ด้วยคำสั่ง datetime.now() ซึ่งคำสั่งนี้จะได้เวลา ณ ปัจจุบันมา ซึ่งเราเก็บในตัวแปร end จากนั้นเราก็เอาเวลาจากตัวแปร end ย้อนไป 5 ปี เราจึงต้องอ้างอิงคำสั่งเฉพาะปีมาจากตัวแปร end ที่ตอนนี้เป็นเวลา ณ วันนี้แล้ว ด้วยคำสั่ง end.year – 5 ในที่นี้ end.year จะรีเทรินออกมาเป็น 2017 เมื่อนำมา – 5 ก็จะได้ 2013 ซึ่งจะเป็นปีเริ่มของเรา จากนั้นเรานำมาต่อกับ เดือน (end.month) และ วันที่ (end.day) ผลลัพธ์จากคำสั่ง datetime() ที่เรานำมาใช้ จะทำการแปลงตัวเลข ปี เดือน และ วัน ให้กลายจากตัวเลยเฉยๆ ให้เป็นตัวแปรชนิด datetime

จากนั้นตามด้วยการโหลดหุ้นที่เราต้องการจะโหลด ด้วยการสร้าง List ของ symbols หุ้นที่เราต้องการใช้ ดังนี้ [‘BANPU.BK’, ‘SCCC.BK’, ‘TOP.BK’, ‘AKR.BK’, ‘PTT.BK’] ก่อนที่จะโหลดข้อมูลทั้งหมดนั้นมาได้เราก็ต้องมาสร้างตัวแปร Stock เป็น DataFrame  เพื่อนำมาเก็บข้อมูลหุ้นทั้ง 5 ตัว ด้วยคำสั่ง stocks = pd.DataFrame()

มาถึงขั้นโหลดข้อมูลกันแล้วนะครับ web.DataReader(x, data_source=’yahoo’)[‘Adj Close’] ก็คือ การใช้โมดูล web จาก library pandas_datareader เพื่อทำการโหลดดาต้าจากเวป ในที่นี้เราต้องการโหลดข้อมูลจากเวป Yahoo Finance เราจึงกำหนด data_source = yahoo เพื่อโหลดข้อมูลจาก yahoo finance ส่วน [‘Adj Close’] ด้านหลังเรากำลังบอก yahoo ว่าต้องการเฉพาะข้อมูล [‘Adj Close’] เท่านั้น

จากนั้นเราก็วน loop เพื่อเก็บข้อมูลด้วยคำสั่ง for x in symbols คือ ในแต่ละรอบของการทำงาน ตัวแปร x จะเปลี่ยนเป็นชื่อ symbol ของหุ้นแต่ละตัว ทีละตัวๆ เรียงตามลำดับ แล้วเก็บในตัวแปร stocks[x] ตัวแปร [x] คือการตั้งชื่อให้ column ของข้อมูลชุดนั้นให้เป็นตามชื่อของหุ้นนั้นๆ ครับ จะเห็นว่ามันทำได้ง่ายเลยทีเดียวใช่มั้ยครับ

ขั้นสุดท้าย เราไม่รู้ว่ามีหุ้นตัวไหนที่มันมีค่า NA แซมมาบ้างไหม ซึ่งถ้ามีมันก็อาจทำให้การคำนวณของเราในขั้นต่อๆไปผิดพลาดได้ เราจึงทำการตัดบรรทัดที่เป็น NA ทิ้งไป (นี่เป็นเพียงการจัดการอย่างง่ายๆ นะครับ การกำจัด NA อาจจะทำได้โดยการแทนที่ค่าก็ได้นะครับ)

ลองมาดูข้อมูลที่โหลดมาได้กันครับ

03.png

3) คำนวณ Indicator, Return ด้วย Loop

ในขั้นตอนนี้ เราจะมาคำนวณ Return เก็บไว้ในดาต้าเฟรมเฉพาะ  เพื่อเอาไว้ใช้ในขั้นตอนต่อไป เช่น เดิมครับ เราต้องการหาค่า Return ของหุ้นทุกตัว เราก็สามารถวนลูป ด้วยคำสั่ง for x in symbol ได้เลยครับ

04

for x in symbols ก็เหมือน Loop ด้านบนเป๊ะเลยครับ มันก็คือการวนตั้งแต่หุ้นตัวแรกใน List symbols จนถึงตัวสุดท้าย

stocks[x + ‘SMA20’]  = stocks[x].rolling(window=20).mean() และ  stocks[x + ‘SMA40’] = stocks[x].rolling(window=40).mean() คือการคำนวณอินดี้ SMA เหมือนที่เราเคยทำในพาร์ทก่อนๆ (สามารถกลับไปดูได้ที่บทความ Basic Python for Trading นะครับ) แต่ต่างจากครั้งก่อนตรงที่คราวนี้เราทำมันใน Loop

สิ่งที่แตกต่างจากครั้งก่อนอยู่นิดหน่อย ก็คือ การนำมันไปเก็บค่าในตัวแปรด้วยการสร้างชื่อคอลัมน์ใหม่ โดยใช้คำสั่ง stocks[x + ‘Rets’] ตัวอย่าง เมื่อ x ในรอบนั้นเป็นหุ้น PTT คอลั่มในรอบนั้นจะได้ stock[‘PTTRets‘]

ลองมาพล๊อตดูครับว่าได้ผลตามที่เราต้องมั้ย

05

02ลองดูค่า mean กันหน่อยครับ ว่าเป็นไงบ้าง คราวนี้จะใช้ตัวแปร log_return เนื่องจากมันเป็นตัวเดียวกับด้านบนที่ผมสร้างมาเพื่อเหตุผลด้านโปรแกรมผมจึงไม่พล๊อตนะครับ มาดู inside data มันดีกว่า เหมือนเดิมนะครับ จะดูค่าเฉลี่ยก็ง่ายๆแค่ .mean ลงไปข้างหลัง

06

นี่คือค่าเฉลี่ย Return ถ้าเต็ม 1 คือ 100% นะครับ จะเห็นว่าค่าเฉลี่ยรายวันอยู่ใน range จาก 0.011% ถึง 0.056% เท่านั้น แต่ถ้ามันมาแบบนี้มันไม่มีค่าติดลบเลยซึ่งก็สอดคล้องกับข้อมูลด้านบนนะครับ ว่าณวันสุดท้ายหรือวันล่าสุดเมื่อเทียบจากวันแรก มันไม่มีหุ้นตัวไหนที่ลดต่ำลงกว่าวันแรกเลย mean มันจึงออกมารูปแบบนี้

07

อันนี้รายปีนะครับ ก็แค่ *252 เข้าไป ก็จะอยู่ใน range 2.8-14.3% ต่อปี

4) ว่าด้วย Portfolio Return  และตัวอย่างแบบ Equal Weight

4.1) Return of portfolio 

เวลาเราซื้อขายหุ้นจริงๆ เราคงไม่ซื้อขายตัวเดียวใช่ไหมครับ คราวนี้เราจะมาลองจัดพอร์ตดูกันบ้าง เราจะมาเริ่มแบบ Equal Weight กันดูก่อนนะครับ Equal Weight หรือ Equal Weighting พูดง่ายๆ ก็คือการซื้อหุ้นทุกตัวในพอร์ตในขนาดเท่ากันแค่นั้นเอง ถ้าพูดให้ยากๆ ก็ตามสมการนี้ครับ

04

สมการนี้คือ ผลรวมของ portfolio return โดยที่ i คือหุ้นนั้นๆ w คือ น้ำหนักของหุ้นตัวนั้นๆในพอร์ต หรือ เราถือหุ้นตัวนั้นไว้กี่ % นั่นเอง r คือ ผลตอยแทนreturn ของหุ้นตัวนั้นๆ โดย Equal Weighting ด้วยวิธีการนี้ น้ำหนักของหุ้นแต่ละตัวคือ w จะเท่ากันครับ

มาลองดูตัวอย่างการถือหุ้นแบบ Equal Weighting กับข้อมูลชุดนี้กันนะครับ โดยเราจะมาดูว่าค่าเฉลี่ยสำหรับการถือหุ้นพวกนี้ไปเป็นเวลา 1 ปี นี่เราจะสามารถคาดหวังกับมันได้แค่ไหน

08

log_return.mean() นี่มันก็คือ mean ของดาต้าทั้ง 5 ปีนะครับ ถ้าเราจะดูว่าเราจะคาดหวังว่าหุ้นตัวนั้นๆใน 1 ปีมีผลตอบแทนอย่างไรก็ง่ายๆ ครับเราก็ * 252 (จำนวนวันในการเทรดเข้าไป)

จากนั้นเราก็สร้างตัวแปรอาร์เรย์เพื่อเก็บค่า น้ำหนัก weights ของหุ้น ในที่นี้อาเรย์ทุกตัวมีค่าเป็น 0.2 เท่ากันหมดเพราะเรามีหุ้น 5 ตัว และเราต้องการให้มันเท่ากันนั่นเอง  เดี๋ยวมาดูผลกันเลยครับ

09.png

np.dot(weights, rets) ก็คือการคูณผลตอบแทนหุ้น (รายปี) กับน้ำหนักของหุ้นตัวนั้นๆในพอร์ตของเรา ผลลัพธ์ที่ได้ ก็คือ ค่า Return ประมาณ 7.72% ต่อปีนั้นเอง นี่คือการคำนวณแบบ Equal Weighting แต่แค่นี้มันไม่พอ เดี๋ยวเราจะมาดูกันว่า น้ำหนักพอร์ตต่างกันมันให้ผลลัพธ์ได้ดีกว่าแย่กว่าอย่างไร แค่ไหน ในขั้นตอนที่ 5 แต่ก่อนอื่นเรามาวัดความเสี่ยงกันก่อนครับ

4.2) ตัวชี้วัดด้านความเสี่ยง

ต่อไปนีเราจะมาคำนวณตัววัดความเสี่ยง Volatility นะครับ ขอให้เข้าใจว่ามันก็คือ ตัววัดความเสี่ยงตัวหนึงที่เอาไว้วัดว่าการลงทุนของเรานั้นมีการเหวี่ยงขึ้นๆ ลงๆ แค่ไหน โดยปกติแล้วเราคงไม่คาดหวังให้การลงทุนของเรามีการเหวี่ยงมากๆแบบ เช่น เดือนนี้กำไรมากมาย +50% เดือนต่อมา -30% นะครับ เพราะถ้าแบบนี้เราคงไม่สามารถนอนหลับได้เป็นแน่ เอาละครับ เพื่อป้องกันปัญหานี้ เรามาคำนวณค่านี้กันก่อน

10.png

การที่เราจะคำนวณ Volatility เราหา Variance หรือ ค่าความแปรปรวนของข้อมูลกันก่อนครับ ซึ่งค่านี้ ก็คือ ตัววัดว่าค่าในชุดข้อมูลมีการเคลื่อนตัวออกห่างจากค่ากลางของมันมากแค่ไหน คือ ถ้าความแปรปรวนของ Equity curve ของผลกำไรของพอร์ตเรามันสูง แปลว่าค่ามันห่างจากค่ากลางมากนั่นแหละครับ ส่วนการหา Variance ของพอร์ตด้วย Python ของเรานั้นทำได้ด้วยการ np.dot(weights.T, np.dot(log_return.cov() * 252, weights)) การหามันเป็นรายปีก็ทำได้ง่ายๆด้วยการ log_return.cov() * 252, เข้ากับน้ำหนักของหุ้นตัวนั้นๆ (weights) เมื่อเราได้ Variance มาแล้วเราก็คำนวณ เราจะเห็นได้ว่าค่า Variance ของ Portfolio แบบ Equal weight มีค่า 0.0445

11.png

ต่อมาก็ มาดู Volatility กับครับ การคำนวณก็ง่ายๆ อีกแล้วนะครับ คือเอา  pvar(ค่าVariance)มา **0.5 หรือนำ Variance มาใส่ square root 2 นั้นเอง และเราก็ได้มาแล้วว่า Volatility ของ Portfolio แบบ Equal weight มีค่า 0.211

ถ้าดูแค่นี้มันก็คงบอกอะไรเราไม่ได้มากนัก ว่า แล้วการจัดพอร์ตแบบนี้มันอยู่จุดไหนละ ของความน่าจะเป็นของการจัดพอร์ตที่เป็นไปได้ทั้งหมด เดี๋ยวต่อเรามาลองจัดพอร์ตแบบสุ่มดูบ้างนะครับว่ามันจะเป็นอย่างไรบ้าง

5) ว่าด้วยการ Random Weight

จากนี้จะเป็นการสร้างความน่าจะเป็น หลายๆแบบของ การให้น้ำหนักพอร์ต” นะครับ แต่เราจะไม่ทำเฉยๆเราจะคำนวนความเสี่ยงมันด้วยแล้วกันนะครับ เพราะการให้น้ำหนักหุ้นต่างๆ กัน ก็จะส่งผลให้มีผลตอบแทน และ ความเสี่ยงต่อพอร์ตฟอลิโอไม่เท่ากันเช่นกัน เมื่อกี้เราได้คำนวณ Volatility ไว้แล้วนะครับ นี่เดี๋ยวเรามาดูพระเอกวันนี้กันเลยดีกว่า ซึ่งก็คือ การจัดพอร์ตต่างกันมันจะให้ผลยังไงบ้าง?

5.1) Random ดูซัก 1 ครั้ง

12.png

ก่อนอื่นเราจะทำการกำหนดขนาดของหุ้นในพอร์ตมาโดยการสุ่ม

เริ่มจากคำสั่ง weights = np.random.random(5) นี่คือการ random เลขมา 5 ตัว แต่ปัญหาของมันคือถ้ารวมกันแล้วมันเกิน 1 นี่สิครับ ย่อมแปลว่าเราถือหุ้นเกิน 100%ของพอร์ตซึ่งมันเป็นไปไม่ได้ เราจึงต้องเอา weights มาทำ normalization อีกทีด้วยการคำสั่ง

13.png

คือ การนำweightsแต่ละตัวมาหารด้วยผลรวมของมันทั้งหมด (เพื่อให้ผลรวมไม่เกิน 100% นั่นเองครับ) ผลรวมของตัวมันทั้งหมดเอง คราวนี้มันก็จะรวมกันได้ 1 แล้ว ค่าที่ได้คือ เราจะ weight แต่ละตัวที่ประมาณ 8.8% 39.8% 18.5% 25.1% 7.5% ตามลำดับนะครับ มาดูผลกันดีกว่า

14.png

เราจะเห็นว่าในการจัดการพอร์ต ด้วยการ random ขนาดของหุ้นแต่ละตัวในรอบแรก ค่า Return ที่ได้ คือ 6.98% ต่อปี ถ้าเรา weight แบบนี้ เหมือนจะต่างกันเล็กน้อยสินะครับ แต่ถ้าเราทำมันหลายๆครั้งเลยล่ะ เราเห็นความแตกต่างของมันแน่ๆ

15.png

และ ความเสี่ยง (Volatility)ก็เพิ่มขึ้นมาที่ 0.217 ด้วย แต่มันจะเป็นแบบนี้ตลอดเลยไหม จะสามารถสรุปได้เลยมั้ยว่า High Risk = High Return? … คำตอบคือ อย่าเพิ่งใจร้อนครับ ดูไปก่อนๆ มันยังตอบไม่ได้จากการทดลองเพียงแค่นี้ครับ นอกซะจากว่า เราจะสามารถทำการทดลองให้ครอบคลุมพอเพื่อจะเอามาตอบคำถามเราได้ มันจะมีไหมนะที่ความเสี่ยงไม่ได้เพิ่มขึ้นแต่กำไรเพิ่มขึ้น และ เพื่อตอบสนองความอยากรู้ของเรา เราจะมาทำการทดลอง ปรับค่า weight ที่ต่างๆ กันให้หุ้นแต่ละตัว อีก  1000 รอบดูนะครับ

5.2) Random ดูซัก 1000 รอบ

เอาละครับมาคราวนี้ผมจะทดสอบด้วยการ random มันซัก 1000 ครั้งเลยแล้วกัน แล้วมาดูกันซิว่าการจัดสรรพอร์ตเนี่ย มันจะมีผลกับผลกำไรและความเสี่ยงได้มากขนาดไหนกันนะ16.png

13-1.png

กราฟด้านบน แกน x คือ ความเสี่ยงหรือ Volatility แกน y คือ กำไรหรือ Return นะครับ จากกราฟนี้เราจะเห็นว่าการจัดพอร์ตที่ดีนั้นมีความสำคัญอย่างมากกับทั้ง Expected return (Return) และ Risk (Volatility) โดยที่ Return จะวิ่งอยู่ในระยะ 3% ไปถึง 13% โดยประมาณ ในด้านของความเสี่ยงวิ่งอยู่ในระยะ 0.18 ไปถึง 0.34 ปลายๆ โดยประมาณครับ

ถ้าเราดูกราฟนี้มันบอกอะไรเราบ้าง คือ มันก็ทำให้เราว่า ดูเหมือนเจ้าคำพูดที่ว่า High Risk = High Return มันไม่จริงเสมอไปครับ เพราะ ถ้าเราจัดการพอร์ตดีๆ เราก็จะสามารถเพิ่มการคาดหวังต่อกำไรได้ขณะที่ความเสี่ยงไม่ได้เพิ่มขึ้นด้วยเลยก็ได้ครับ ยิ่งไปกว่านั้นจากกราฟ เรายังสังเกตุเห็นว่า ถ้าเราจัดพอร์ตแย่มาก พอร์ตเราก็สามารถที่จะมีกำไรที่น้อยลง และ ความเสี่ยงที่เพิ่มขึ้นได้อีกด้วย ดังตัวอย่าง กลุ่มรอบๆ จุด A ในรูปคือจุดที่ความเสี่ยงเพิ่มขึ้นจากการจัดพอร์ตที่ไม่ดีนักเมื่อเทียบกับ  จุดรอบๆกลุ่ม B (เมื่อเทียบกับกลุ่ม A แล้ว B ให้กำไรมากกว่าและเสี่ยงน้อยกว่าด้วย)

อีกส่วนของกราฟเราจะเห็นว่า มีบางการทดลอง ที่เมื่อกำไรมันมากขึ้นเรื่อยๆ จนถึงจุดหนึ่ง ในขณะที่ค่าความเสี่ยงก็พุ่งขึ้นสูงซะเหลือเกิน จากข้อมูลนี้ถ้ากำไรเกิน 12% กลุ่ม C ในรูป ขึ้นไปความเสี่ยงของเรานั้นพุ่งไปที่ 30 กว่าๆแล้วทั้งนั้น คำถามก็คือ เราจะสามารถคาดหวังกำไรสูงระดับนี้โดยไม่ทำให้ความเสี่ยงเพิ่มขึ้นได้หรือไม่? คำตอบคือ “ได้ครับ” แต่ผมจะยังไม่ลงลึกไปในจุดนั้น ในบทความนี้ เนื่องจากเนื้อหาจะยาวจนเกินไป อาจจะทำให้เบื่อกับซะก่อนได้ ขออนุญาติยกไปในบทความถัดไปนะครับ …

สุดท้ายนี้เราอาจจะเห็นว่ากำไรทำไมเราคาดหวังกำไร(ซึ่งเป็นต่อปี) ได้น้อยซะเหลือเกิน 2 ไปถึง 13% ต่อปี เราต้องไม่ลืมว่านี่เราแค่ถือหุ้นเฉยๆไว้ 5 ตัว กับการปันส่วนพอร์ตเท่านั้นเองนะครับ เรายังไม่ได้ใช้เทคนิคอื่นๆ ในการเทรดเข้ามาร่วมด้วยเลย … ลองนึกดูนะครับว่า ถ้าเราแบ่งสันปันพอร์ตดีๆ ร่วมกับการเทรดด้วยกลยุทธ์ของเราล่ะจะเป็นอย่างไง ผมจะลองดูตัวอย่างให้ดูซักหนึ่งตัวอย่างนะครับ

14

กราฟนี้คือผลการจัดส่วนพอร์ตบวกกับการเทรดด้วยกลยุทธ์พื้นฐานอย่างหนึ่งครับ ส่วนแกน x คือ ความเสี่ยงหรือ Volatility แกน y คือ กำไรหรือ Return เหมือนเดิมนะครับ จะเห็นเลยว่าค่าคาดหวังกำไรกับความเสี่ยงนั้นเปลี่ยนไปมหาศาลหลือเกิน จาก 2 ไปถึง 12% คือกำไรแน่ๆ มันเพิ่มเป็น -4% ไปถึง +28% กันเลยทีเดียว ความเสี่ยงนั้นโดยภาพรวมก็ถือว่าดีขึ้นด้วย 0.14 ไปถึง 0.34  น่าสนใจไม่ใช่ย่อยใช่มั้ยครับ

อย่างที่ได้พูดไปด้านบน จากข้อมูลนี้เรายังมีวิธีที่คาดหวังกำไรปีละ เกิน 25% และ ลดความเสี่ยงให้ต่ำกว่า 0.30 ได้อีกนะครับ ซึ่งเราจะพูดถึงต่อไปนะครับ ว่าจะไปยังจุดๆ นั้นกันได้อย่างไร …

Advertisements

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 )

Google photo

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

Twitter picture

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

Facebook photo

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

Connecting to %s