Python Basic Risk & Return part 2 : Standard Deviation

หัวใจสำคัญของบทความชุด Python basic risk & return นี้คือ การวิเคราะห์หาค่าความเสี่ยงของหุ้นที่เราสนใจ จากบทความตอนที่ 1 เราได้พูดถึงการประเมินค่าความเสี่ยงและผลกำไรต่อหุ้นอย่างง่ายๆ โดยอาศัยเพียงแค่ความเข้าใจทฤษฏีการแจกแจงปกติ (Normal distribution)  เท่านั้น …ในบทความนี้ เราจะมาคำนวณหาค่าความเสี่ยงของหุ้นที่เราสนใจกันให้ลึกเข้าไปอีกนิด โดยใช้หลักการของ Standard deviation เขียนโดย Python 3.5  เช่นเคยนะครับ การคำนวณจะยังคงเป็นการคำนวณง่ายๆ ที่ไม่ซับซ้อนอีกเช่นเดิม  🙂

** พาร์ทที่ 1  ของบทความนี้สามารถอ่านได้ที่ link นี้ครับ Python Basic Risk & Return (part 1) Normal distribution **

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

import numpy as np
import pandas as pd
from pandas_datareader import data as web
import matplotlib.pyplot as plt
import seaborn as sns
sns.set()

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

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

2) โหลดดาต้าจาก Yahoo Finance, Normalization  ดาต้า และ คำนวณ Log Return 

symbols = ['BANPU.BK', 'SCCC.BK', 'TOP.BK', 'AKR.BK', 'PTT.BK']

stocks = pd.DataFrame()
for x in symbols:
    stocks[x] = web.DataReader(x, data_source='yahoo')['Adj Close']

normStocks = (stocks / stocks.ix[0] * 100)
log_return = np.log(normStocks / normStocks.shift(1))
rets = log_return.dropna()

ในส่วนนี้จะเหมือนพาร์ทที่แล้วขอไม่อธิบายซ้ำนะครับ สามารถย้อนกลับไปดูได้ที่ [Python Basic Risk & Return (part 1) Normal distribution]

3) ลองคำนวนดาต้าด้วยโอเปอร์เรเตอร์ทางคณิตศาสตร์ Standard Deviation เพื่อประเมินความเสี่ยง

01

อีกหนึ่งปัจจัยวัดความเสี่ยงที่เป็นที่นิยมกันคือ SD หรือ Standard Deviation ความหายคร่าวๆ ก็คือ ผลรวมของค่าเบี่ยงเบนจาก Mean หารด้วยจำนวนค่าทั้งหมด”นั่นเอง สามารถดูรายละเอียดที่ [Standard Deviation]

ในทางการลงทุน เรามักนำค่า SD มาใช้กับ Equity curve หรือ Return เพื่อประเมินว่ากลยุทธการลงทุนของเรามีความผันผวนมากแค่ไหนนั่นเอง ถ้าเราลงทุนเราคงไม่อยากเห็นความผันผวนแบบ เดือนแรกบวก 70% เปอร์เซ็นแล้วเดือนสองลดลง5% เดือนสามบวก 20%ต่อมาลดลง 60% ใช่ไหมครับ??? … นี่แหละครับจุดที่ Standard Deviation เข้ามามีบทบาทสำคัญ

โดยตัว SD นี้จะต่างกับ DrawDown ที่เรารู้จักกันนิดหน่อย คือ SD จะไม่ได้คิดเฉพาะช่วงที่ผลการลงทุนของพอร์ตลดลงเท่านั้น แต่จะคิดจากค่าเฉลี่ยของผลงานของพอร์ตเราทั้งหมด ในระบบที่เสถียรมากๆ (ไม่ใช่พวกที่กินกำไรจากสภาวะตลาดบางอย่างแล้วมี Big shot ให้เฮใหญ่นะครับ!) ดังนั้น ผลกำไรของมันไม่ควรแกว่งมากนักครับ เพราะแกว่งมากไม่ว่าจะทางบวกหรือลบก็ไม่ได้แปลว่ามันดีเสมอไป มันอาจจะแปลว่าระบบของเราเริ่มไม่เสถียรแล้วก็ได้ แต่ทั้งนี้ทั้งนั้นก็ต้องขึ้นกับกลยุทธ์ของเราอีกทีด้วยแหละครับว่าออกแบบไว้อย่างไร … โอเคครับ พล่ามไปเยอะพอสมควรแล้ว มาลองทำกันดีกว่า ซึ่งมันก็ทำได้ง่ายๆ ด้วยคำสั่ง

rets.std()

02

ใน python การหา Standard Deviation นั้นทำได้ง่ายๆด้วยคำสั่ง .std() จากรูปจะเห็นว่าความเสี่ยงของ หุ้น AKR นั้นมีค่าสูงสุด จะเห็นว่า ถ้าเราดูย้อนหลังจะเห็นว่ากราฟของ AKR ผันผวนสูงสุด ข้อน่าสังเกตคือ AKR หรือ เอกรัฐวิศวกรรม จำกัด นั้นเป็นบริษัทที่มูลค่าทางตลาดน้อยสุดในลิตส์นี้ และเคยมีข่าวคราวว่าหุ้นตัวนี้คือหุ้น Turn Around ตัวหนึง จึงเข้าใจได้ว่าทำไมมันจึงผันผวนสูง (เพราะวัฎจักรของหุ้นช่วงที่ย่ำแย่ และ ช่วงTurnAround) อีกข้อสังเกตหุ้นยิ่งมีมูค่าทางตลาดใหญ่มากความผันผวนยิ่งต่ำลงขณะที่ BANPU บ้านปูที่รองลงมานั้นข้อสังเกตเป็นไปตามวิกฤตของธุรกิจถ่านหินของเขา เป็นต้น

rets.mean()

03

และจากนั้นเพื่อเปรียบเทียบเราจะหา Mean ของ Return รายวันของมันด้วย .mean() ครับ นี่คือ return รายวันของหุ้นแต่ละตัว จะเห็นว่าหุ้นที่มี Return  อันดับหนึ่งอย่าง BANPU  มีความผันผวนเป็นอันดับสอง แต่ถึงอย่างนั้นมันก็ดูยากไปหน่อยยิ่งถ้าเรามีหุ้นมากๆตัวเข้าจะทำให้วิเคราะห์ได้ยากเราจึงเอามาทำพล๊อตให้ดูง่ายๆดังนี้

4) แสดงผลตารางประเมินความเสี่ยงของหุ้นแต่ละตัวในรูปแบบกราฟ

4.1) แสดงค่า Return เฉลี่ยต่อวัน ต่อ Standard Deviation

area = np.pi*100

plt.scatter(rets.std(), rets.mean(),alpha = 0.5,s =area)
plt.ylabel('Expected returns')
plt.xlabel('Risk')

for label, x, y in zip(rets.columns, rets.std(), rets.mean()):
    plt.annotate(
        label,
        xy = (x, y), xytext = (50, 50),
        textcoords = 'offset points', ha = 'right', va = 'bottom',
        arrowprops = dict(arrowstyle = '-', connectionstyle = 'arc3,rad=-0.3'))
    plt.xlim(0.010, 0.035)
    plt.ylim(0.0002,0.0008)

จากโปรแกรมด้านบน ค่าของตัวแปร label, x และ y  คือ ตัวแปรที่เราเอาไว้วนลูปเพื่อดึงข้อมูล ซึ่งมีรายละเอียดดังนี้

label  -> ดึงค่าชื่อคอลั่ม rets.columns
x         -> ดึงค่าความเสี่ยงของพอร์ต หรือ Standard Deviation ใน 1 วันออกมา จาก rets.std()
y         -> ดึงค่า Mean ของ Return ใน 1 วัน จาก rets.mean()

จากนั้นเราก็ใช้ for เพื่อลูปค่าดังกล่าวมาพล๊อตรวมกันในรูปเดียวครับ ผลลัพธ์ที่ได้จะเป็นดังนี้

05

จากกราฟด้านบน แกน x คือ Standard Deviation หรือ ความเสี่ยง แกน y คือค่า Return (รายวัน)

4.2) แสดงค่า Return เฉลี่ยต่อปี ต่อ Standard Deviation

ถ้าตัวเลขแบบวันแบบด้านบนทำให้เรามองไม่เห็นภาพได้ชัดเจนนัก เราลองมาคิดคำนวณเป็นต่อปีดีกันกว่าครับ

yearly_rest = rets.mean()*252

04

การทำ Return รายปีนั้นทำได้ง่ายๆด้วยคำสั่ง .mean() แล้วคูณด้วย 252 หรือวันทำการของตลาดหลักทรัพย์เข้าไป แล้วเก็บไว้ในตัวแปร yearly_rest

plt.scatter(rets.std(), yearly_rest, alpha=0.5, s=area)
plt.xlabel('Risk')
plt.ylabel('Expected returns')

for label, x, y in zip(rets.columns, rets.std(), yearly_rest):
    plt.annotate(
        label,
        xy=(x, y), xytext=(50, 50),
        textcoords='offset points', ha='right', va='bottom',
        arrowprops=dict(arrowstyle='-', connectionstyle='arc3,rad=-0.3'))

    plt.xlim(0.012, 0.033)
    plt.ylim(0.05,0.20)

06

รูปกราฟที่ได้ ก็แน่นอนครับว่ามีลักษณะเดียวกันกับรูปก่อนหน้า เพราะเราแค่เปลี่ยนจาก Mean ของ Return จาก “รายวัน” เป็น “รายปี” ด้วยกันแทน rets.mean() ด้วย yearly_rest เท่านั้นเอง ที่เราทำเช่นนี้ก็เพียงแค่ต้องการรวบยอดผลตอบแทนราบปีเพื่อให้วิเคราะห์ได้สะดวกขึ้นนั่นเองครับ … ถ้าจะถามว่า จำเป็นต้องทำแบบนี้ไหม? จริงๆ ก็แล้วแต่วไตล์การวิเคราะห์ของเรานะครับ

จากกราฟที่เราได้ มาลองสรุปผลดูหน่อยนะครับ โดยอาศัยข้อมูลกราฟที่เราได้ในอีต ตามปรกติหุ้นที่เราต้องรีบเดินหนีก่อนเลยก็คงจะเป็น AKR  ใช่มั้ยครับ เพราะ นอกจากจะให้ค่า SD หรือ risk ที่สูงแล้ว ผลตอบแทนที่ได้ยังต่ำเตี้ยเรี่ยดินอีกด้วย แต่ ณ เวลานั้นจริงๆเราก็ไม่รู้เหรอกครับว่าต่อไปมันจะเป็นอย่างไรแต่สถิติได้บอกมาแล้วว่าหุ้นตัวนี้ผันผวนน่ากลัวต่อให้เรามี information ที่ดีเพื่อจะลงทุนในมันก็ต้องระวังตัวไว้หน่อยครับ และต่อให้ผลตอบแทนยังสูงมันก็มี SD สูงอยู่ดีเราจึงต้องชั่งใจหน่อยหนึงครับ ทั้งนี้ทั้งนั้นก็ต้องขึ้นกับกลยุทธการลงทุนของเราด้วย(บางกลยุทธก็หากินกับความผันผวนเป็นหลักอาจจะชอบหุ้นแบบนี้ก็ได้)  ส่วนหุ้นที่ความผันผวนพอสมควรอย่าง BANPU กลับให้ผลตอบแทนที่สมน้ำสมเนื้อ ทั้งนี้อาจเป็นเพราะวัฎจักรอย่างถ่านหินด้วย แต่สถิติก็คือสถิติครับ มันบอกเราได้แค่ว่า แม้จะผันผวนสูงมันก็สามารถมีผลตอบแทนที่ดีเหมือนกัน ส่วนหุ้นที่ผลตอบแทนไม่มากแต่ความผันผวนก็ไม่มากเท่าไหร่อย่าง PTT TOP SCCC และมีผลตอบแทนไม่มากนัก แต่ถ้าเรามีกลยุทธ์หรือระบบการลงทุนที่ดีพอและระบบนั้นๆของเราไม่ถูกกับความผันผวนมากนัก หุ้นเหล่านี้ถือเป็น resource ที่ดีของเราในการพัฒนาระบบไปรีดกำไรออกมาเช่น(ขึ้นกับปรเภทของระบบอีกนั่นแหละครับ)

สรุป

  • สถิติเป็นภาพ Snapshot เท่านั้น เช่น หุ้นอย่างบ้านปู BANPU ถ่านหินผู้น่าสงสารมาหลายปีของเราและเพิ่งกลับมาฟื้นได้ไม่นานนี้ ผลตอบแทนที่ให้สูงก็อาจจะเพราะช่วงนี้ด้วยเช่นกัน  AKR ถ้าย้อนกลับไปซักปี กราฟของเค้าก็ประมาณ BANPU ตอนนี้แหละครับ ต้องจำไว้ว่าสถิติเป็นภาพ Snapshot ของมันในช่วงเวลาหนึ่งที่ให้ information เราเพื่อประกอบการตัดสินใจหรือพัฒนาระบบเท่านั้นนะครับ
  • มูลค่าทางตลาดกับSD กล่าวคือ ส่วนใหญ่จะเห็นว่าหุ้นยิ่งมีมูลค่าทางตลาดใหญ่เท่าไหร่มันยิ่งมี SD น้อยไม่ผันผวนมากขณะที่ผลตอบแทนก็อาจจะไม่มากเช่นกัน แน่ล่ะเพราะมันเป็นหุ้นใหญ่อยู่แล้ว แต่จาก information นี้ถ้าเราสามารถใช้กลยุทธการลงทุนที่เหมาะกับ SD ต่ำๆมีสเถียรภาพพอ หุ้นใหญ่อืดอาดนี้จะสร้างความมั่นคงให้กับระบบเราได้ดีเลยทีเดียว

ทั้งหมดทั้งมวลด้านบนเป็นการวิเคราะห์คร่าวๆ บนพื้นฐานของกราฟที่ได้เท่านั้นนะครับ ในการวิเคราะห์จริงๆ ก็อาจจะต้องอาศัยปัจจัยอื่นๆ อีกเช่นกัน ซึ่งเราจะทยอยเขียนบทความลงเรื่อยๆ นะครับ เฉพาะความเสี่ยงนี่ก็มีอีกหลายตัวทีเดียวที่ยังไม่ได้เขียนถึง ไว้จะลองเขียนถึงในโอกาสต่อไปนะครับ ขอบคุณครับ

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