現代ポートフォリオ理論では、リスクとして分散を使いました。
バリュー・アット・リスクは、正規分布を仮定しリスクを損失に限定することで、より一般感覚に近いリスクを表現することができます。
バリュー・アット・リスク
バリュー・アット・リスク(Value at Risk、 VaR)とは、リスク分析の手法の一つ。現有資産の損失可能性を時価推移より測定する分析指標。
出典: フリー百科事典『ウィキペディア(Wikipedia)』
バリュー・アット・リスクは、過去のデータから平均\(\mu\)と分散\(\sigma\)を求め、正規分布\(N(\mu, \sigma^2)\)に従うとして、信頼水準\(c\)での最大の損失損失\(VaR\)は、以下のように定義されます。
$$ Prob (\delta \pi \leq -VaR) = 1-c $$
つまり、\(S\)を現在の資産とすると、\(\alpha(1-c) \)を標準正規分布の累積分布関数の逆関数として、\(VaR\)は以下のように求めることが出来ます。
$$ VaR = S \cdot [ \mu – \sigma \cdot \alpha(1-c)] $$
また、\(\mu\)、\(\sigma\)を日次で求め日次の\(VaR\)を計算する場合は以上で求まりますが、もし\(n\)日の \(VaR\)を求めるときは\(\mu\)、\(\sigma\) を以下のように変換します。
$$ \mu_{ndays} = n \cdot \mu_{daily} $$
$$ \sigma_{ndays} = \sqrt{n} \cdot \sigma_{daily} $$
Pythonで実装
Python で実装します。
正規分布の累積分布関数の逆関数は、scipy.stats.norm で簡単に求めることができます。
import datetime
import numpy as np
import pandas as pd
from scipy.stats import norm
from pandas_datareader import data as pdr
import yfinance as yf
import requests_cache
# override pandas_datareader
yf.pdr_override()
# casche for downloaded data
expire_after = datetime.timedelta(days=3)
session = requests_cache.CachedSession(cache_name='cache', backend='sqlite', expire_after=expire_after)
def download_data_adjclose(stock, start, end):
data = pdr.get_data_yahoo(stock, start, end)['Adj Close']
return data
def calc_daily_pct_change(data):
pct_change = data.pct_change()
return pct_change
class ValueAtRisk(object):
def __init__(self, s, c, n= 1, stock='GOOGL', start='2015-01-01', end='2020-01-01'):
self.s = s
self.c = c
start = pd.to_datetime(start)
end = pd.to_datetime(end)
data = download_data_adjclose(stock, start, end)
pct_change = calc_daily_pct_change(data)
self.mu = np.mean(pct_change) * n
self.sigma = np.std(pct_change) * np.sqrt(n)
def calc(self):
alpha = norm.ppf(1-self.c)
var = self.s * (self.mu - self.sigma*alpha)
return var
if __name__ == '__main__':
s = 1000000
c = 0.99
value_at_risk = ValueAtRisk(s, c)
# 35662.65269757571
print(value_at_risk.calc())
モンテカルロ法でのシミュレーション
モンテカルロ法でVaRを求めます。
幾何ブラウン運動の解を使い株価変動をシミュレートします。
$$ S_{t}=S_{0}\exp \left(\left(\mu -{\frac {\sigma ^{2}}{2}}\right)t+\sigma B_{t}\right) $$
import datetime
import numpy as np
import pandas as pd
from scipy.stats import norm
from pandas_datareader import data as pdr
import yfinance as yf
import requests_cache
# override pandas_datareader
yf.pdr_override()
# casche for downloaded data
expire_after = datetime.timedelta(days=3)
session = requests_cache.CachedSession(cache_name='cache', backend='sqlite', expire_after=expire_after)
def download_data_adjclose(stock, start, end):
data = pdr.get_data_yahoo(stock, start, end)['Adj Close']
return data
def calc_daily_pct_change(data):
pct_change = data.pct_change()
return pct_change
class ValueAtRisk(object):
def __init__(self, s, c, n= 1, stock='GOOGL', start='2015-01-01', end='2020-01-01'):
self.s = s
self.c = c
start = pd.to_datetime(start)
end = pd.to_datetime(end)
data = download_data_adjclose(stock, start, end)
pct_change = calc_daily_pct_change(data)
self.mu = np.mean(pct_change) * n
self.sigma = np.std(pct_change) * np.sqrt(n)
def calc(self):
alpha = norm.ppf(1-self.c)
var = self.s * (self.mu - self.sigma*alpha)
return var
def monte_carlo(self, iterations=100000):
np.random.seed(seed=0)
# generate stock prices
rands = np.random.normal(0, 1, [1, iterations])
stock_prices = self.s * np.exp((self.mu-0.5*self.sigma**2) + self.sigma*rands)
# sort an array of stock_prices and get (1-self.c)*100 percentile
# e.x. VaR with 99% confidence level, there is left 1% probability less than VaR.
stock_prices.sort()
percentile = np.percentile(stock_prices, (1-self.c)*100)
return self.s - percentile
if __name__ == '__main__':
s = 1000000
c = 0.99
value_at_risk = ValueAtRisk(s, c)
# 35662.65269757571
print(value_at_risk.calc())
# 33607.08215287316
print(value_at_risk.monte_carlo())