Python Basic Risk & Return (part 1) Normal distribution

บทความนี้ เราจะนำเสนอวิธีการประเมินความเสี่ยงอย่างง่ายโดยไม่ต้องคำนวณทางคณิตศาสตร์มากมายอะไร คือ แสดงข้อมูลในรูปแบบ Normal Distribution เพื่อประเมินความเสี่ยงขั้นต้นนั่นเอง มาดูขั้นตอนกันเลยครับว่า จากหลักการของ Normal Distribution  เราจะคำนวณหาความเสี่ยงสำหรับหุ้นที่เราสนใจได้ยังไง

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

# import library
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

#load data
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']

ก่อนอื่นสร้างตัวแปรลิส ชื่อ symbols เพื่อเก็บชื่อหุ้นตัวที่เราต้องการจะโหลด ในที่นี้ผมจะใช้ตัวอย่างเป็นหุ้นกลุ่มพลังงานเป็นตัวอย่างในครั้งนี้ครับ

จากนั้นเราสร้างตัวแปร stocks เป็น data frame เพื่อเตรียมไว้เพื่อโหลดข้อมูลต่อไป จากนั้นเราจึงสร้าง loop for เพื่อวน loop x ทุกตัวที่อยู่ใน list sysmols ข้างต้น

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

3) Normalization  ดาต้าเพื่อให้เปรียบเทียบกันได้ด้วยสเกลเดียวกัน

ปรกติถ้าเราเอาดาต้าดิบๆ ที่โหลดมาได้ มาพล๊อตด้วยคำสั่ง

#plot raw data
stocks[['BANPU.BK', 'SCCC.BK', 'TOP.BK', 'AKR.BK', 'PTT.BK']].plot(figsize=(15, 10))
1
ข้อมูลดิบก่อนทำ Normalisation

จะเห็นว่าดาต้ามันมีปัญหาในการพล๊อตเปรียบเทียบเพราะมันะทำให้ดูไม่รู้เรื่องเนื่องจากเสกลของหุ้นแต่ละตัวมันไม่เท่ากันเราจึงต้องทำการ Normalize มันเพื่อปรับสเกลให้เป็นสเกลเดียวกัน ทำให้สามารถอ่าน และ ตีความมันได้ง่ายขึ้น

 

#normalize data
normStocks = (stocks / stocks.ix[0] * 100)
#plot again
normStocks[['BANPU.BK', 'SCCC.BK', 'TOP.BK', 'AKR.BK', 'PTT.BK']].plot(figsize=(15, 10))
2.png
ข้อมูลหลังทำ Normalisation

จะเห็นได้ว่า ข้อมูลหลังทำ normalisation ด้านบน หุ้นแต่ละตัวจะถูกปรับสเกลเป็นสเกลด้วยกันแล้ว เราใช้ normalization ให้เริ่มที่หลักเดียวกัน โดยการ เอาดาต้าทั้งหมดมาหารด้วยตัวมันเอง ณ วันแรก stocks / stocks.ix[0] หุ้นทั้งหมดจึงเริ่มที่ค่า 1 เท่ากัน จากนั้นที่ 100 เข้าไปเพื่อให้มันไปเริ่มจาก 100 เมื่อทำเสร็จแล้วลองพล๊อตดูครับ ตอนนี้เป้นสเกลเดียวกันแล้วเราจึงสามารถวิเคราะห์รูปแบบเบื้องต้นด้วยเสกลเดียวกันได้แล้ว

 

4) คำนวณ Log Return 

#calculate log return
log_return = np.log(normStocks / normStocks.shift(1))
rets = log_return.dropna()

จากนั้นเรามาคิด log return โดยการเอาราคาของมันวันนี้หารตัวราคาของมันเมื่อวาน .shift(1) คือการอ้างอิงถึงเมื่อวานของหุ้นตัวนั้นๆนั้นเองโดยเก็บผลลัพธ์ไว้ใน log_return จากนั้น เราใช้คำสั่ง.dropna() เพื่อตัดข้อมูลที่เป็น NA (ค่าที่คำนวณไม่ได้ หรือ ค่าว่าง) ออกไปโดยให้นำไปเก็บไว้ใน ตัวแปร rets

ปล. ในการหา return เนี่ยเราได้ทั้งดาต้าที่ normalize แล้ว (normStocks) หรือ ดาต้าก่อนการ normalize (stocks) ก็ได้ให้ผลเหมือนกัน(แตกต่างน้อยมากๆจนแทบไม่มีนัยสำคัญ)

5) พล๊อตดาต้าตามรูปแบบ Normal Distribution 

3

จากนี้เราจะมาดูข้อมูลจากด้าน Return และ Risk ดูบ้างด้วยพื้นฐานของ Normal  Distribution กันครับ

**สามารถหาอ่านเรื่อง normal distribution ได้นะครับ ถ้าใครยังไม่แม่นเรื่องนี้ (https://en.wikipedia.org/wiki/Normal_distribution) **

#plot banpu
stocks['BANPU.BK'].plot()

#histogram plot
sns.distplot(rets['BANPU.BK'], bins=50, color='blue')

7.png3

รูปแรกเป็นกราฟแสดงราคา Close ของหุ้น บางปูนะครับ ต่อมา เราจะนำข้อมูล Return ที่เราได้หาสร้างไว้แล้วจากข้อมูลดิบรูปบนมาพล๊อต distribution ตามทฎษฎีดูนะครับ ด้วยคำสั่ง sns.distplot (bins คือจำนวนแท่งที่เราต้องการให้แสดงในกราฟนะครับ)

โลกจริงก็ไม่เป็นทรงสวย bell curve ตามทฤษีเป๊ะนะครับ จากกราฟนี้จะจะเห็นว่าแกน x คือราคาเพิ่มลดในแต่ละวันของหุ้น Banpu บ้านปู แกน y คือตารางความถี่การเกิดขึ้นของมันนะครับ

จากนั้นจากดาต้าพื้นฐานนี้เราจะมาลองประเมินความเสี่ยงแบบง่ายๆดูนะครับ ด้วยวิธีการดูทางสถิติว่าในช่วงเวลาที่ผ่านมานั้น มีโอกาสเท่าไหร่ที่หุ้นตัวนี้จะเพิ่มหรือลด เป็นจำนวนกี่ % ใน 1 วัน

6) ใช้ทฤษฎี Normal Distribution เพื่อประเมินความเสี่ยงอย่างง่าย

#banpu downside 95%
sns.distplot(rets['BANPU.BK'], bins=50, color='blue')
p=np.percentile(rets['BANPU.BK'],5)
plt.title("BANPU Return")
plt.axvline(x=p, linewidth=3, color='r')
plt.figtext(0.45, 0.6, "percentile (0.95): %.4f" % p + "%",fontsize=20)

5.png

คิดที่กรณีความมั่นใจ  95%

โค้ดที่เพิ่มขึ้นมาคือ np.percentile คือการหาเปอร์เซ็นไทน์หรือความถี่แบบเปอร์เซ็นสะสม 5 ในที่นี้คือตัวอย่างที่เราจะหาเปอร์เซ็นในด้านลบของหุ้นบ้านปู ว่า มีโอกาสแค่ไหนที่หุ้นบ้านปูจะลดลงเกิน xx% ด้วยโอกาสที่ 5% คำสั่ง plt.axvline คือคำสั่งวาดเส้นแนวตั้งเป็นตัวชี้ตำแหน่งเปอร์เซ็นของเรา

ผลลัพธ์ที่ได้คือ ในระยะเวลาตั้งแต่ วันที่ 1 มกราคม 2010 จนถึง วันที่ 25 มกราคม 2017 หรือ กว่า 6 ปีกว่าๆมานี้หุ้นบ้านปูมีโอกาสเพียง 5% เท่านั้นที่ราคาจะลดลงมากกว่า 3.24% ในหนึ่งวัน 

หรือพูดอีกอย่างได้ว่า

“มีความมั่นใจ 95% ว่าหุ้นบ้านปูจะไม่มีการลดลงต่ำกว่า 3.24% ใน 1 วันนั่นเอง”

คราวนี้ลองคิดที่ความมั่นใจ 90% ดูบ้างครับ

6.png

เราลองมาดูทางด้าน upside กันบ้างนะครับ ที่ความมั่นใจ 95%9

ทางด้าน upside บอกว่า เรามีโอกาส 5% เท่านั้นที่หุ้นบ้านปูจะขึ้นใน 1 วันมากกว่า 3.6196% ครับ”

สำหรับดาต้าตัวอื่นๆ เราก็สามารถทำการเขียนโปรแกรมพล็อตกราฟได้ในทำนองเดียวกัน ดังนี้

sns.distplot(rets['SCCC.BK'], bins=50, color='blue')
 p=np.percentile(rets['SCCC.BK'],5)
 plt.title("SCCC Return")
 plt.axvline(x=p, linewidth=3, color='r')
 percent = p*100
 plt.figtext(0.45, 0.6, "percentile (0.05): %.4f" % percent + "%",fontsize=20)

sns.distplot(rets['TOP.BK'], bins=50, color='blue')
 p=np.percentile(rets['TOP.BK'],5)
 plt.title("TOP Return")
 plt.axvline(x=p, linewidth=3, color='r')
 percent = p*100
 plt.figtext(0.45, 0.6, "percentile (0.05): %.4f" % percent + "%",fontsize=20)

sns.distplot(rets['AKR.BK'], bins=50, color='blue')
 p=np.percentile(rets['AKR.BK'],5)
 plt.title("AKR Return")
 plt.axvline(x=p, linewidth=3, color='r')
 percent = p*100
 plt.figtext(0.45, 0.6, "percentile (0.05): %.4f" % percent + "%",fontsize=20)

sns.distplot(rets['PTT.BK'], bins=50, color='blue')
 p=np.percentile(rets['PTT.BK'],5)
 plt.title("PTT Return")
 plt.axvline(x=p, linewidth=3, color='r')
 percent = p*100
 plt.figtext(0.45, 0.6, "percentile (0.05): %.4f" % percent + "%",fontsize=20)

ผลลัพธ์ที่ได้

จะเห็นว่าพาร์ทนี้เป็นเพียงการ Visualization ดาต้าเพื่อประเมินความเสี่ยงแบบคร่าวๆ เท่านั้น ถ้าเราอยากจะคำนวณความเสี่ยงให้ละเอียดกว่านี้ ก็ยังมีวิธีที่จะได้ทำได้อีกครับ ซึ่งเดี๋ยวเราจะมาคำนวนเพิ่มกันนิดหน่อยในบทความหน้านะครับ

 

 

 

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