การวัด VaR. และ CVaR. ด้วย Python

การทำ portfolio optimization ด้วย google colab VI

NUTHDANAI WANGPRATHAM
3 min readApr 26, 2020

วันนี้เราจะพูดถึงวิธีการทางสถิติที่สามารถนำมาใช้ในการวัดความเสี่ยงที่เรียกว่า value at risk.

Value at risk (VaR) เป็นสถิติที่ใช้วัดและกำหนดระดับความเสี่ยงของพอร์ตโฟลิโอ ตัวชี้วัดนี้ใช้เพื่อกำหนดขอบเขตและอัตราส่วนการเกิดขึ้นของการสูญเสียที่อาจเกิดขึ้นในพอร์ตการลงทุน

Conditional Value at Risk (CVaR) เป็นการประเมินความเสี่ยงที่วัดปริมาณความเสี่ยงที่พอร์ตการลงทุนมี CVaR นั้นได้มาจากการใช้ค่าเฉลี่ยถ่วงน้ำหนักของการสูญเสียที่“ รุนแรง” ในการกระจายตัวของผลตอบแทนที่เป็นไปได้นอกเหนือจากจุดตัดที่มีความเสี่ยง (VaR) ค่าความเสี่ยงแบบมีเงื่อนไขจะใช้ในการปรับพอร์ตให้เหมาะสมเพื่อการบริหารความเสี่ยง

เริ่มที่ Colab กันเลย

เหมือนกับทุกครังคือการ Import liberly

# load packages
import pandas as pd
import numpy as np
import pandas_datareader as pdr
import seaborn as sns
from matplotlib import pyplot as plt
import pandas_datareader as web
import datetime
# not needed, only to prettify the plots.import matplotlib
from IPython.display import set_matplotlib_formats
%matplotlib inline

load data

ดึงข้อมูลจาก yahoo

start = datetime.datetime(2000, 1, 1)
end = datetime.datetime(2020, 1, 1)
df = web.DataReader("VOO", 'yahoo', start, end)
df_daily_returns = df[['Adj Close']].pct_change().dropna()df_monthly_returns =
df[['AdjClose']].resample('M').ffill().pct_change().dropna()
df_Yearly_returns = df[['Adj
Close']].resample('Y').ffill().pct_change().dropna()
df_daily_returns.rename(columns={'Adj
Close':'daily_returns'},inplace=True)
df_monthly_returns.rename(columns={'Adj
Close':'monthly_returns'},inplace=True)
df_Yearly_returns.rename(columns={'Adj
Close':'Yearly_returns'},inplace=True)

VaR

การวัด VaR มีด้วยกันสามวิธีคือ

  1. Historic VaR
  2. Parametric Gaussian VaR
  3. Modified (Cornish-Fisher) VaR

Historic VaR

ในการคำนวณ VaR จากข้อมูลในอดีตคือการหาค่า ณ เปอร์เซ็นไทล์ที่ 5 หมายความว่ามีข้อมูลน้อยกว่านั้น 5 % numpy สามารภเรียหใช้np.percentile

if isinstance(r, pd.DataFrame):return r.aggregate(var_historic, level=level)elif isinstance(r, pd.Series):return -np.percentile(r, level)else:raise TypeError("Expected r to be a Series or DataFrame")

เรียกใช้ฟังชั่นได้ดังนี้

var_historic(df_monthly_returns, level=1)

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

Conditional VaR aka Beyond VaR

CVaR นั้นง่ายมาก ทั้งหมดที่เราต้องการคือการหาค่าเฉลี่ยของตัวเลขที่ต่ำกว่า VaR!

def cvar_historic(r, level=5):"""Computes the Conditional VaR of Series or DataFrame"""if isinstance(r, pd.Series):is_beyond = r <= -var_historic(r, level=level)return -r[is_beyond].mean()elif isinstance(r, pd.DataFrame):return r.aggregate(cvar_historic, level=level)else:raise TypeError("Expected r to be a Series or DataFrame")cvar_historic(df_monthly_returns, level=1)

Parametric Gaussian VaR

หากชุดของผลตอบแทนกระจายตามปกติเรารู้เช่นว่า 50% ของผลตอบแทนต่ำกว่าค่าเฉลี่ยและ 50% อยู่เหนือค่าเฉลี่ย

เรารู้ด้วยว่าประมาณสองในสามของผลตอบแทนอยู่ภายใน 1 ส่วนเบี่ยงเบนมาตรฐาน นั่นหมายถึงหนึ่งในสามอยู่เหนือค่าเบี่ยงเบนมาตรฐานหนึ่งค่าจากค่าเฉลี่ย เนื่องจากการกระจายตัวแบบปกตินั้นสมมาตรจะมีค่าประมาณหนึ่งในหก (ประมาณ 16%) ต่ำกว่าค่าเบี่ยงเบนมาตรฐานหนึ่งค่าจากค่าเฉลี่ย ดังนั้นถ้าเรารู้ค่าเฉลี่ยและส่วนเบี่ยงเบนมาตรฐานและถ้าเราคิดว่าผลตอบแทนปกติกระจาย 16% VaR จะเป็นค่าเฉลี่ยลบหนึ่งส่วนเบี่ยงเบนมาตรฐาน

โดยทั่วไปเราสามารถแปลงค่าเปอร์เซ็นไทล์เป็นคะแนน z ได้เสมอ (ซึ่งคือจำนวนส่วนเบี่ยงเบนมาตรฐานจากค่าเฉลี่ยที่เป็นตัวเลข) ดังนั้นหากเราสามารถแปลงระดับ VaR (เช่น 1% หรือ 5%) เป็นคะแนน z เราสามารถคำนวณระดับผลตอบแทนโดยที่เปอร์เซ็นต์ของผลตอบแทนอยู่ด้านล่าง

scipy.stat.norm มีฟังก์ชัน ppf () ซึ่งทำสิ่งนั้น มันใช้เปอร์เซ็นต์ไทล์เช่น 0.05 หรือ 0.01 และให้คะแนน z ที่สอดคล้องกับการแจกแจงแบบปกติ

from scipy.stats import normdef var_gaussian(r, level=5):# compute the Z score assuming it was Gaussianz = norm.ppf(level/100)return -(r.mean() + z*r.std(ddof=0))var_gaussian(df_monthly_returns)

Cornish-Fisher Modification

Cornish-Fisher Modificationเป็นการปรับแต่งที่เรียบง่าย คะแนน z บอกเราว่าความเบี่ยงเบนมาตรฐานอยู่ห่างจากค่าเฉลี่ยที่เราต้องไปหาค่า VaR หากผลตอบแทนไม่ปกติเรารู้ว่าคะแนน z จะให้ตัวเลขที่ไม่ถูกต้องกับเรา แนวคิดพื้นฐานคือเนื่องจากเราสามารถสังเกตความเบ้และความโด่งของข้อมูลเราสามารถปรับคะแนน z ขึ้นหรือลงเพื่อให้ได้คะแนน z ที่ปรับเปลี่ยน เช่น. สิ่งอื่น ๆ ที่เท่าเทียมกันถ้าความเบ้เป็นลบเราจะลดคะแนน z ลงอีกและถ้าความเบ้เป็นบวกเราจะผลักมันขึ้น

อย่างแรกเราต้องหาค่า skewness และ kurtosis

def skewness(r):"""Alternative to scipy.stats.skew()Computes the skewness of the supplied Series or DataFrameReturns a float or a Series"""demeaned_r = r - r.mean()# use the population standard deviation, so set dof=0sigma_r = r.std(ddof=0)exp = (demeaned_r**3).mean()return exp/sigma_r**3def kurtosis(r):"""Alternative to scipy.stats.kurtosis()Computes the kurtosis of the supplied Series or DataFrameReturns a float or a Series"""demeaned_r = r - r.mean()# use the population standard deviation, so set dof=0sigma_r = r.std(ddof=0)exp = (demeaned_r**4).mean()return exp/sigma_r**4

เราสามารถเขียนฟังชั่นได้ดังนี้

from scipy.stats import normdef var_gaussian(r, level=5, modified=False):"""Returns the Parametric Gauusian VaR of a Series or DataFrameIf "modified" is True, then the modified VaR is returned,using the Cornish-Fisher modification"""# compute the Z score assuming it was Gaussianz = norm.ppf(level/100)if modified:# modify the Z score based on observed skewness and kurtosiss = skewness(r)k = kurtosis(r)z = (z +(z**2 - 1)*s/6 +(z**3 -3*z)*(k-3)/24 -(2*z**3 - 5*z)*(s**2)/36)return -(r.mean() + z*r.std(ddof=0))

และเรียกใช้งานฟังชั่น

var_gaussian(df_monthly_returns, modified=True)

เปรียบเทียบวิธีต่างๆ

เราสามารถเปรียบเทียบการหาค่า Var จากวิธีต่างๆดังนี้

var_table_1 = [var_gaussian(df_monthly_returns),var_gaussian(df_monthly_returns, modified=True),var_historic(df_monthly_returns)]comparison_1 = pd.concat(var_table_1, axis=1)comparison_1.columns=['Gaussian', 'Cornish-Fisher', 'Historic']comparison_1

หาเราอยากหาค่า VaR ของรายวันหรือรายเดือน ทำได้ดังนี้

var_table_2 = [var_gaussian(df_daily_returns),var_gaussian(df_daily_returns, modified=True),var_historic(df_daily_returns)]comparison_2 = pd.concat(var_table_2 , axis=1)comparison_2.columns=['Gaussian', 'Cornish-Fisher', 'Historic']comparison_2var_table_3 = [var_gaussian(df_Yearly_returns),var_gaussian(df_Yearly_returns, modified=True),var_historic(df_Yearly_returns)]comparison_3 = pd.concat(var_table_3, axis=1)comparison_3.columns=['Gaussian', 'Cornish-Fisher', 'Historic']comparison_3all_comparisson =
pd.concat([comparison_2,comparison_1,comparison_3])
all_comparisson.plot()

จะเห็นได้ว่าVarแบบข้อมูลย้อนหลังค่อนข้างผิดปกติเป็นผลมาจากจำนวนข้อมูลที่ไม่มากพอ และวิธี Cornish-fisher ติดลบสูงกว่า Gaussian เนื่องจากข้อมูลเบ้ขวา

Note book;

อ่านตอนอื่นๆได้ที่

  1. การคำนวณผลตอบแทนการลงทุนด้วย Python
  2. การหาความผันผวนของพอร์ตการลงทุนด้วย Python
  3. การหา Max Drawdown ด้วย Python
  4. การวัด การเบี่ยงเบนของผลตอบแทนด้วย Python
  5. การวัด SemiDeviation ด้วย Python
  6. การวัด VaR. และ CVaR. ด้วย Python
  7. รีวิวการใช้ ffn. ใน Python
  8. การหา Top Drawdown ด้วย Python
  9. การหาค่า Sharpe ratio ด้วย Python
  10. การหากลุ่มหลักรัพย์ที่เส้นประสิทธิภาพ Efficient Frontier ด้วย Python
  11. การหา shape ratio สูงสุดและเส้น CML ด้วย Python
  12. การสร้างมูลค่าตลาดแบบถ่วงน้ำหนักด้วย PYTHON
  13. ข้อจำกัดของการกระจายความเสี่ยงและการทำประกันพอร์ตการลงทุน
  14. การจำลอง ผลตอบแทนด้วย RANDOM WALK Generation และ Montecarlo simulation
  15. Sharpe Style Analysis
  16. Factor Investing ด้วย Python
  17. วิเคราะห์ ประเภทกองทุนรวมด้วย Python
  18. การทำ Portfolio optimization
  19. สร้างแนวรับแนวต้านวิเคราะห์หุ้นด้วย Python

--

--

NUTHDANAI WANGPRATHAM
NUTHDANAI WANGPRATHAM

Written by NUTHDANAI WANGPRATHAM

I am a learner and have a multipotential life. You can contact me at nutdnuy@gmail.com

No responses yet