此文章首发于微信公众号:Python for Finance

链接:https://mp.weixin.qq.com/s/uaDEnSzoalTaRmZ9GNvR0A

一、VaR的定义

假设有一投资组合,考虑如下问题:

  • 在95%或99%的置信水平下,预计在未来一个月内损失的金额最大是多少?
  • 在95%或99%的置信水平下,预计在未来一年内损失的最大百分比是多少?

VaR (Value at Risk) ,在险价值,其含义为:在一定概率水平 (置信度) 下,某一金融资产在未来特定时期内的最大可能损失。

VaR 有三个要素:

  • 置信水平:通常是95%或99%
  • 时间段:一天、一个月或一年
  • 最大可能损失:可以是绝对值(损失金额),也可以是相对值(损失百分比)。

用公式表示为:
P r o b ( Δ V ⩽ − V a R ) = 1 − α Prob(\Delta V \leqslant -VaR)=1-\alpha Prob(ΔVVaR)=1α
字母含义如下:

  • P r o b Prob Prob —— 资产价值损失小于可能损失上限的概率,即英文的Probability。
  • Δ V \Delta V ΔV—— 某一金融资产在一定持有期的价值损失额。
  • V a R VaR VaR—— 给定置信水平下的在险价值,即可能的损失上限,可以是绝对值,也可以是相对值。
  • α \alpha α—— 给定的置信水平

假设基金经理管理的证券组合在未来1天内,置信度为95%的情况下,VaR值为520万元。可以将其写作:
P r o b ( Δ V ⩽ − 520 ) = 1 − 95 % = 5 % Prob(\Delta V \leqslant -520)=1-95\%=5\% Prob(ΔV520)=195%=5%

含义:

  • 在未来24小时内,存在95%的概率,基金经理所管理的证券组合价值损失不超过520万元;
  • 有 95% 的把握判断该证券组合在下一个交易日内的损失在 520 万元以内;
  • 证券组合在一天内 ,由于市场价格变化而带来的最大损失超过 520 万元的概率为 5%;
  • 95%置信度的含意是我们预期100天中只有5天的损失会超过对应的VaR值。但VaR并没有指出在可能超过VaR损失的时间内(如95%置信度的5/100天中;或99%的1/100 天中)的实际损失会是多少。

根据巴塞尔协议的明确规定:银行需要计算持有期10天、置信水平99%的VaR。在实际计算中,通常先计算 N=1 时的VaR,在计算相同置信水平下 N>1 时的 VaR,其表达式如下:
N 天 V a R = 1 天 V a R ∗ n N天VaR=1天VaR*\sqrt n NVaR=1VaRn

二、计算单一资产的VaR

VaR 的度量主要有方差-协方差法 (Variance-Covariance Approach) 、历史模拟法 (Historical Simulation Method) 和蒙特卡罗模拟法 (Monte-Carlo Simulation) 三种方法。

2.1 数据概况

我们以中国平安 (000001) 股票为例,使用方差-协方差法、历史模拟、蒙特卡洛模拟法来计算它的 VaR 值。

计算之前,首先需要获取到中国平安的历史股价数据,这里我们使用 AKShare包。它是一个免费、开源的 Python 财经数据接口包。AKShare返回的绝大部分的数据格式都是 pandas DataFrame 类型,非常便于用 pandas/ NumPy/ Matplotlib 进行数据分析和可视化。

import pandas as pd
import numpy as np
import akshare as ak
import scipy.stats as st

# 读入中国平安 「000001」 2015-01-01 到 2021-12-31 日收盘价数据
data = ak.stock_zh_a_hist(symbol="000001", period="daily", start_date="20150101", end_date='20211231', adjust="")
data.index = pd.to_datetime(data['日期'],format='%Y-%m-%d') #设置日期索引
close = data['收盘'] #日收盘价
ret = np.log(close/close.shift(1)) #日收益率
ret = ret.dropna()

获取的数据如下:

>>>ret
日期
2015-01-06   -0.015095
2015-01-07   -0.019194
2015-01-08   -0.034169
2015-01-09    0.007989
2015-01-12   -0.020771
  
2021-12-27   -0.005213
2021-12-28   -0.002908
2021-12-29   -0.024765
2021-12-30    0.004170
2021-12-31   -0.020421
Name: 收盘, Length: 1704, dtype: float64

2.2 方差-协方差法

方差-协方差法度量风险值(VAR)的前提条件是假设风险因子的变化服从多元正态分布。

以单一资产中国平安 (000001) 股票为例,使用方差-协方差法来计算它的 VaR 值。假设持有中国平安股票的价值为100万元。

value = 1000000 #中国平安股票价值为100万元
R_mean = ret.mean() #计算均值
R_vol = ret.std() #计算标准差

def VaR_VCM(value,mu,sig,X,T):
    '''
    Parameters
    ----------
    value : 资产的价值
    mu : 资产的日均收益率
    sig : 资产的日均波动率(标准差)
    X : 置信水平
    T : 持有天数   
    '''   
    z = abs(st.norm.ppf(q=1-X))    
    return np.sqrt(T)*value*(z*sig-mu)

VaR99_1day_VCM = VaR_VCM(value,R_mean,R_vol, 0.99, 1)
VaR99_10day_VCM = VaR_VCM(value,R_mean,R_vol, 0.99, 10)
VaR95_1day_VCM = VaR_VCM(value,R_mean,R_vol, 0.95, 1)
VaR95_10day_VCM = VaR_VCM(value,R_mean,R_vol,0.95, 10)

print(f'方差-协方差法1天、99%的VaR:{VaR99_1day_VCM/10000:.2f}万元')
print(f'方差-协方差法10天、99%的VaR:{VaR99_10day_VCM/10000:.2f}万元')  
print(f'方差-协方差法1天、95%的VaR:{VaR95_1day_VCM/10000:.2f}万元')
print(f'方差-协方差法10天、95%的VaR:{VaR95_10day_VCM/10000:.2f}万元')  

结果为:

方差-协方差法1天、99%的VaR:5.16万元
方差-协方差法10天、99%的VaR:16.32万元
方差-协方差法1天、95%的VaR:3.65万元
方差-协方差法10天、95%的VaR:11.54万元

2.3 历史模拟法

基本假设:资产收益的过去变化状况会在未来完全重现。

基本思想:利用过去一段时间历史收益资料,估算投资组合变化的统计分布(经验分布),再根据不同的分位数求得相对应的置信水平的风险值。和协方差矩阵估算方法不同,历史模拟法对收益的分布不做任何假设,只用到历史经验分布,统计上用的是非参数方法。

具体方法:将历史收益由小到大排序,并给出经验分布函数,由此可以估算不同置信水平下的VaR值。例如,收益经验分布由1000笔收益数据的频率构成,则在置信水平为95%的条件下,应选取从小到大排序的第51笔收益金额
为VaR值的估计,即收益经验分布的第5%分位数;置信水平为99%则选择第1%分位数,置信水平为90%则选择第10%分位数

以单一资产中国平安 (000001) 股票为例,使用历史模拟法来计算它的 VaR 值。假设持有中国平安股票的价值为100万元。

value = 1000000 #中国平安股票价值为100万元

def VaR_history(value,ret,X,T):
    '''
    Parameters
    ----------
    value : 资产的价值
    ret : 资产的日收益率序列
    X : 置信水平
    T : 持有天数   
    '''  
    # Numpy 的 percentile 函数,可以直接返回序列相应的分位数
    return value*np.sqrt(T)*abs(np.percentile(ret,(1-X)*100))

VaR99_1day_history = VaR_history(value,ret,0.99,1)
VaR99_10day_history = VaR_history(value,ret,0.99,10)
VaR95_1day_history = VaR_history(value,ret,0.95,1)
VaR95_10day_history = VaR_history(value,ret,0.95,10)

print(f'历史模拟法1天、99%的VaR:{VaR99_1day_history/10000:.2f}万元')
print(f'历史模拟法10天、99%的VaR:{VaR99_10day_history/10000:.2f}万元')  
print(f'历史模拟法1天、95%的VaR:{VaR95_1day_history/10000:.2f}万元')
print(f'历史模拟法10天、95%的VaR:{VaR95_10day_history/10000:.2f}万元')  

结果为:

历史模拟法1天、99%的VaR:6.13万元
历史模拟法10天、99%的VaR:19.37万元
历史模拟法1天、95%的VaR:3.17万元
历史模拟法10天、95%的VaR:10.02万元

2.4 蒙特卡罗模拟法

基本思想:蒙特卡罗模拟法(Monte Carlo Simulation, MCS)是在一定的统计分布假设下模拟风险因子的变化情况。首先假设资产收益为某一随机过程,并根据所设定的价格变动过程,大量模拟未来各种可能发生的情境,然后将某一情境下投资组合变化值排序,给出投资组合变化的分布,据此就可以估算不同置信水平下的VaR值。

基本步骤

每一次蒙特卡洛模拟,对资产组合中的每一资产按照随机过程公式模拟出下一个交易日的价格,公式中的ε可以假定服从t分布或正态分布(即资产收益率服从的分布),然后可以得到每一资产的收益率,乘以各自的权重和市值就能得到每一资产在下一个交易日的收益,全部相加就是该资产组合在下一个交易日的模拟收益。

比如蒙特卡罗模拟法的抽样次数是10000次,通过重复以上的步骤可以得到组合收益的10000个不同的样本值,持有期1天、置信水平95%的投资组合风险价值就对应于样本数值中排在第500位最大损失的取值。

假设股票价格符合几何布朗运动,即
d S t = μ t S t d t + σ t S t d W t dS_t=\mu_tS_tdt+\sigma_tS_tdW_t dSt=μtStdt+σtStdWt
简化处理,得到特定时期(0,T)资产价格变化过程:
Δ S t = S t ( μ Δ t + σ ε t Δ t ) , t = 1 , 2 , . . . , N , N Δ t = T \Delta S_t=S_t(\mu\Delta t+\sigma\varepsilon_t\sqrt{\Delta t}),t=1,2,...,N,N\Delta t=T ΔSt=St(μΔt+σεtΔt ),t=1,2,...,N,NΔt=T
于是得到:
S t + 1 = S t + S t ( μ Δ t + σ ε t Δ t ) , t = 1 , 2 , . . . , N , N Δ t = T S_{t+1}=S_t+S_t(\mu\Delta t+\sigma\varepsilon_t\sqrt{\Delta t}),t=1,2,...,N,N\Delta t=T St+1=St+St(μΔt+σεtΔt ),t=1,2,...,N,NΔt=T
也可表示为:
S t + 1 = S t e ( μ − σ 2 2 ) Δ t + σ ε t Δ t ) S_{t+1}=S_te^{(\mu-\frac{\sigma^2}2)\Delta t+\sigma\varepsilon_t\sqrt{\Delta t})} St+1=Ste(μ2σ2)Δt+σεtΔt )
其中 μ \mu μ为收益率均值, σ 2 \sigma^2 σ2为收益率方差, ε \varepsilon ε服从t分布或正态分布。

以单一资产中国平安 (000001) 股票为例,使用蒙特卡罗模拟法来计算它的 VaR 值。假设持有中国平安股票的价值为100万元。

value = 1000000 #中国平安股票价值为100万元

m = 10000 #模拟次数
e1 = np.random.standard_t(df=len(ret),size=m)  #自由度为收益率数据长度的t分布
#e1 = np.random.standard_normal(size=m) #若服从正态分布,则此代码代替上行代码
R_mean_year = ret.mean()*252  #计算每一资产的年化平均收益率
R_vol_year = ret.std()*np.sqrt(252) #计算每一资产的年化波动率
dt=1/252 #时间间隔
S0=1
S=np.zeros(m) #存放模拟次数个模拟价格数据
#代入随机过程
S=S0*(np.exp((R_mean_year-0.5*R_vol_year**2)*dt+R_vol_year*e1*np.sqrt(dt)))
F_ret=S/S0-1  #模拟未来收益率
  
#蒙特卡洛模拟法计算VaR
VaR99_1day_MS = value*abs(np.percentile(F_ret,1))    
VaR99_10day_MS = np.sqrt(10)*VaR99_1day_MS
VaR95_1day_MS = value*abs(np.percentile(F_ret,5))
VaR95_10day_MS = np.sqrt(10)*VaR95_1day_MS

#由于抽样随机数的原因,结果可能会有不同
print(f'蒙特卡罗模拟法1天、99%的VaR:{VaR99_1day_MS/10000:.2f}万元')
print(f'蒙特卡罗模拟法10天、99%的VaR:{VaR99_10day_MS/10000:.2f}万元')  
print(f'蒙特卡罗模拟法1天、95%的VaR:{VaR95_1day_MS/10000:.2f}万元')
print(f'蒙特卡罗模拟法10天、95%的VaR:{VaR95_10day_MS/10000:.2f}万元') 

由于是随机模拟,每次估算的VaR不一致,某次运行的结果为:

蒙特卡罗模拟法1天、99%的VaR:5.06万元
蒙特卡罗模拟法10天、99%的VaR:15.99万元
蒙特卡罗模拟法1天、95%的VaR:3.58万元
蒙特卡罗模拟法10天、95%的VaR:11.31万元

三、计算资产组合的VaR

3.1 数据概况

假设有一投资组合,含有5支股票:中国平安(000001)、格力电器(000651)、爱尔眼科(300015)、贵州茅台(600519)、长安汽车(000625),权重占比如下:

资产名称中国平安格力电器爱尔眼科立讯精密长安汽车
权重0.150.20.50.050.1

使用方差-协方差法、历史模拟、蒙特卡洛模拟法来计算组合的 VaR 值。

计算之前,首先需要获取到中国平安的历史股价数据,这里我们使用 AKShare包。它是一个免费、开源的 Python 财经数据接口包。AKShare返回的绝大部分的数据格式都是 pandas DataFrame 类型,非常便于用 pandas/ NumPy/ Matplotlib 进行数据分析和可视化。

import pandas as pd
import numpy as np
import akshare as ak
import scipy.stats as st

# 读入5支股票 2015-01-01 到 2021-12-31 日收盘价数据
def get_ret(code):
    data = ak.stock_zh_a_hist(symbol=code, period="daily", start_date="20150101", end_date='20211231', adjust="")
    data.index = pd.to_datetime(data['日期'],format='%Y-%m-%d') #设置日期索引
    close = data['收盘'] #日收盘价
    close.name = code
    ret = np.log(close/close.shift(1)) #日收益率
    return ret

codes=['000001','000651','300015','600519','000625']
ret = pd.DataFrame()
for code in codes:
	ret_ = get_ret(code)
	ret = pd.concat([ret,ret_],axis=1)
ret = ret.dropna()

获取数据如下:

>>>ret
              000001    000651    300015    600519    000625
2015-01-06 -0.015095  0.021099  0.083902 -0.023431  0.068956
2015-01-07 -0.019194 -0.001681 -0.029017 -0.025029  0.007205
2015-01-08 -0.034169  0.003360 -0.000342 -0.006135  0.010204
2015-01-09  0.007989 -0.017888  0.000342 -0.007590 -0.044106
2015-01-12 -0.020771  0.028848  0.042888 -0.020598  0.074108
             ...       ...       ...       ...       ...
2021-12-27 -0.005213  0.054392  0.018817 -0.028791 -0.017700
2021-12-28 -0.002908  0.005571 -0.008905  0.002979  0.024176
2021-12-29 -0.024765 -0.026268 -0.002526 -0.046515 -0.018899
2021-12-30  0.004170  0.000272  0.003672  0.016521 -0.002635
2021-12-31 -0.020421  0.005416 -0.031889 -0.012121  0.001977

[1497 rows x 5 columns]

3.2 方差-协方差法

该方法假设资产组合的收益是呈正态分布,只预估预期(或平均)收益和标准差两个因素,从而画出正态分布曲线,将正态曲线与相同的实际收益数据作对比。方差-协方差背后逻辑与历史模拟法相似,只是使用的是正态分布曲线而不是实际历史数据。

value = 100000000 #投资组合市值为1亿元
R_mean = ret.mean() #计算均值
R_cov = ret.cov() #计算协方差
R_vol = ret.std() #计算标准差
#投资组合各资产权重
weights = np.array([0.15,0.20,0.5,0.05,0.1])
#计算投资组合的期望收益率
Rp_daily = np.sum(weights*R_mean)
#计算投资组合的日波动率
Vp_daily = np.sqrt(np.dot(weights,np.dot(R_cov,weights.T)))

def VaR_VCM(value,mu,sig,X,T): 
    '''
    Parameters
    ----------
    value : 资产的价值
    mu : 资产的日均收益率
    sig : 资产的日均波动率(标准差)
    X : 置信水平
    T : 持有天数   
    '''   
    z = abs(st.norm.ppf(q=1-X))    
    return np.sqrt(T)*value*(z*sig-mu)

VaR99_1day_VCM = VaR_VCM(value,Rp_daily,Vp_daily, 0.99, 1)
VaR99_10day_VCM = VaR_VCM(value,Rp_daily,Vp_daily, 0.99, 10)
VaR95_1day_VCM = VaR_VCM(value,Rp_daily,Vp_daily, 0.95, 1)
VaR95_10day_VCM = VaR_VCM(value,Rp_daily,Vp_daily,0.95, 10)

print(f'方差-协方差法1天、99%的VaR:{VaR99_1day_VCM/10000:.2f}万元')
print(f'方差-协方差法10天、99%的VaR:{VaR99_10day_VCM/10000:.2f}万元')  
print(f'方差-协方差法1天、95%的VaR:{VaR95_1day_VCM/10000:.2f}万元')
print(f'方差-协方差法10天、95%的VaR:{VaR95_10day_VCM/10000:.2f}万元') 

结果为:

方差-协方差法1天、99%的VaR:544.65万元
方差-协方差法10天、99%的VaR:1722.32万元
方差-协方差法1天、95%的VaR:385.20万元
方差-协方差法10天、95%的VaR:1218.12万元

3.3 历史模拟法

value = 100000000 #投资组合市值为1亿元

#投资组合各资产权重
weights = np.array([0.15,0.20,0.5,0.05,0.1])
#历史交易日投资组合的收益率序列
Rp = np.dot(ret,weights)
Rp = pd.DataFrame(Rp,index=ret.index,columns=['投资组合日收益'])

def VaR_history(value,ret,X,T):
    '''
    Parameters
    ----------
    value : 资产的价值
    ret : 资产的日收益率序列
    X : 置信水平
    T : 持有天数   
    '''  
    # Numpy 的 percentile 函数,可以直接返回序列相应的分位数
    return value*np.sqrt(T)*abs(np.percentile(ret,(1-X)*100))

VaR99_1day_history = VaR_history(value,Rp,0.99,1)
VaR99_10day_history = VaR_history(value,Rp,0.99,10)
VaR95_1day_history = VaR_history(value,Rp,0.95,1)
VaR95_10day_history = VaR_history(value,Rp,0.95,10)

print(f'历史模拟法1天、99%的VaR:{VaR99_1day_history/10000:.2f}万元')
print(f'历史模拟法10天、99%的VaR:{VaR99_10day_history/10000:.2f}万元')  
print(f'历史模拟法1天、95%的VaR:{VaR95_1day_history/10000:.2f}万元')
print(f'历史模拟法10天、95%的VaR:{VaR95_10day_history/10000:.2f}万元') 

结果为:

历史模拟法1天、99%的VaR:725.59万元
历史模拟法10天、99%的VaR:2294.51万元
历史模拟法1天、95%的VaR:333.50万元
历史模拟法10天、95%的VaR:1054.62万元

3.4 蒙特卡罗模拟法

其服从 t分布的 Python 程序如下:

value = 100000000 #投资组合市值为1亿元

#投资组合各资产权重
weights = np.array([0.15,0.20,0.5,0.05,0.1])
m = 10000 #模拟次数
e1 = np.random.standard_t(df=len(ret),size=m)  #自由度为收益率数据长度的t分布
#e1 = np.random.standard_normal(size=m) #若服从正态分布,则此代码代替上行代码
R_mean_year = ret.mean()*252  #计算每一资产的年化平均收益率
R_vol_year = ret.std()*np.sqrt(252) #计算每一资产的年化波动率
dt=1/252 #时间间隔
S0=np.ones(len(weights))
S=np.zeros(shape=(m,len(weights))) #存放(模拟次数×资产数量)个模拟价格数据
for i in range(len(weights)):#代入随机过程
	S[:,i]=S0[i]*(np.exp((R_mean_year[i]-0.5*R_vol_year[i]**2)*dt+R_vol_year[i]*e1*np.sqrt(dt)))
    #每一行∑资产收益率×相应权重就得到资产组合的收益率,一共10000行
Sp_ret=(np.dot(S/S0-1,weights)) #资产组合收益率
  
#蒙特卡洛模拟法计算VaR
VaR99_1day_MS = value*abs(np.percentile(Sp_ret,1))    
VaR99_10day_MS = np.sqrt(10)*VaR99_1day_MS
VaR95_1day_MS = value*abs(np.percentile(Sp_ret,5))
VaR95_10day_MS = np.sqrt(10)*VaR95_1day_MS

#由于抽样随机数的原因,结果可能会有不同
print(f'蒙特卡罗模拟法1天、99%的VaR:{VaR99_1day_MS/10000:.2f}万元')
print(f'蒙特卡罗模拟法10天、99%的VaR:{VaR99_10day_MS/10000:.2f}万元')  
print(f'蒙特卡罗模拟法1天、95%的VaR:{VaR95_1day_MS/10000:.2f}万元')
print(f'蒙特卡罗模拟法10天、95%的VaR:{VaR95_10day_MS/10000:.2f}万元') 

由于是随机模拟,每次估算的VaR不一致,某次运行的结果为:

蒙特卡罗模拟法1天、99%的VaR:701.11万元
蒙特卡罗模拟法10天、99%的VaR:2217.12万元
蒙特卡罗模拟法1天、95%的VaR:508.27万元
蒙特卡罗模拟法10天、95%的VaR:1607.30万元

四、全套代码

4.1 计算单一资产的VaR

import pandas as pd
import numpy as np
import akshare as ak
import scipy.stats as st

# 读入中国平安 「000001」 2015-01-01 到 2021-12-31 日收盘价数据
data = ak.stock_zh_a_hist(symbol="000001", period="daily", start_date="20150101", end_date='20211231', adjust="")
data.index = pd.to_datetime(data['日期'],format='%Y-%m-%d') #设置日期索引
close = data['收盘'] #日收盘价
ret = np.log(close/close.shift(1)) #日收益率
ret = ret.dropna()

value = 1000000 #中国平安股票价值为100万元
R_mean = ret.mean() #计算均值
R_vol = ret.std() #计算标准差

#方差协方差法
def VaR_VCM(value,mu,sig,X,T):
    '''
    Parameters
    ----------
    value : 资产的价值
    mu : 资产的日均收益率
    sig : 资产的日均波动率(标准差)
    X : 置信水平
    T : 持有天数   
    '''   
    z = abs(st.norm.ppf(q=1-X))    
    return np.sqrt(T)*value*(z*sig-mu)

VaR99_1day_VCM = VaR_VCM(value,R_mean,R_vol, 0.99, 1)
VaR99_10day_VCM = VaR_VCM(value,R_mean,R_vol, 0.99, 10)
VaR95_1day_VCM = VaR_VCM(value,R_mean,R_vol, 0.95, 1)
VaR95_10day_VCM = VaR_VCM(value,R_mean,R_vol,0.95, 10)

print(f'方差-协方差法1天、99%的VaR:{VaR99_1day_VCM/10000:.2f}万元')
print(f'方差-协方差法10天、99%的VaR:{VaR99_10day_VCM/10000:.2f}万元')  
print(f'方差-协方差法1天、95%的VaR:{VaR95_1day_VCM/10000:.2f}万元')
print(f'方差-协方差法10天、95%的VaR:{VaR95_10day_VCM/10000:.2f}万元') 

#历史模拟法
def VaR_history(value,ret,X,T):
    '''
    Parameters
    ----------
    value : 资产的价值
    ret : 资产的日收益率序列
    X : 置信水平
    T : 持有天数   
    '''  
    # Numpy 的 percentile 函数,可以直接返回序列相应的分位数
    return value*np.sqrt(T)*abs(np.percentile(ret,(1-X)*100))

VaR99_1day_history = VaR_history(value,ret,0.99,1)
VaR99_10day_history = VaR_history(value,ret,0.99,10)
VaR95_1day_history = VaR_history(value,ret,0.95,1)
VaR95_10day_history = VaR_history(value,ret,0.95,10)

print(f'历史模拟法1天、99%的VaR:{VaR99_1day_history/10000:.2f}万元')
print(f'历史模拟法10天、99%的VaR:{VaR99_10day_history/10000:.2f}万元')  
print(f'历史模拟法1天、95%的VaR:{VaR95_1day_history/10000:.2f}万元')
print(f'历史模拟法10天、95%的VaR:{VaR95_10day_history/10000:.2f}万元') 

#蒙特卡洛模拟法
m = 10000 #模拟次数
e1 = np.random.standard_t(df=len(ret),size=m)  #自由度为收益率数据长度的t分布
#e1 = np.random.standard_normal(size=m) #若服从正态分布,则此代码代替上行代码
R_mean_year = ret.mean()*252  #计算每一资产的年化平均收益率
R_vol_year = ret.std()*np.sqrt(252) #计算每一资产的年化波动率
dt=1/252 #时间间隔
S0=1
S=np.zeros(m) #存放模拟次数个模拟价格数据
#代入随机过程
S=S0*(np.exp((R_mean_year-0.5*R_vol_year**2)*dt+R_vol_year*e1*np.sqrt(dt)))
F_ret=S/S0-1  #模拟未来收益率
  
#蒙特卡洛模拟法计算VaR
VaR99_1day_MS = value*abs(np.percentile(F_ret,1))    
VaR99_10day_MS = np.sqrt(10)*VaR99_1day_MS
VaR95_1day_MS = value*abs(np.percentile(F_ret,5))
VaR95_10day_MS = np.sqrt(10)*VaR95_1day_MS

#由于抽样随机数的原因,结果可能会有不同
print(f'蒙特卡罗模拟法1天、99%的VaR:{VaR99_1day_MS/10000:.2f}万元')
print(f'蒙特卡罗模拟法10天、99%的VaR:{VaR99_10day_MS/10000:.2f}万元')  
print(f'蒙特卡罗模拟法1天、95%的VaR:{VaR95_1day_MS/10000:.2f}万元')
print(f'蒙特卡罗模拟法10天、95%的VaR:{VaR95_10day_MS/10000:.2f}万元') 

4.2 计算资产组合的VaR

import pandas as pd
import numpy as np
import akshare as ak
import scipy.stats as st

# 读入5支股票 2015-01-01 到 2021-12-31 日收盘价数据
def get_ret(code):
    data = ak.stock_zh_a_hist(symbol=code, period="daily", start_date="20150101", end_date='20211231', adjust="")
    data.index = pd.to_datetime(data['日期'],format='%Y-%m-%d') #设置日期索引
    close = data['收盘'] #日收盘价
    close.name = code
    ret = np.log(close/close.shift(1)) #日收益率
    return ret

codes=['000001','000651','300015','600519','000625']
ret = pd.DataFrame()
for code in codes:
	ret_ = get_ret(code)
	ret = pd.concat([ret,ret_],axis=1)
ret = ret.dropna()

value = 100000000 #投资组合市值为1亿元
R_mean = ret.mean() #计算均值
R_cov = ret.cov() #计算协方差
R_vol = ret.std() #计算标准差
#投资组合各资产权重
weights = np.array([0.15,0.20,0.5,0.05,0.1])

#方差协方差法
#计算投资组合的期望收益率
Rp_daily = np.sum(weights*R_mean)
#计算投资组合的日波动率
Vp_daily = np.sqrt(np.dot(weights,np.dot(R_cov,weights.T)))

def VaR_VCM(value,mu,sig,X,T): 
    '''
    Parameters
    ----------
    value : 资产的价值
    mu : 资产的日均收益率
    sig : 资产的日均波动率(标准差)
    X : 置信水平
    T : 持有天数   
    '''   
    z = abs(st.norm.ppf(q=1-X))    
    return np.sqrt(T)*value*(z*sig-mu)

VaR99_1day_VCM = VaR_VCM(value,Rp_daily,Vp_daily, 0.99, 1)
VaR99_10day_VCM = VaR_VCM(value,Rp_daily,Vp_daily, 0.99, 10)
VaR95_1day_VCM = VaR_VCM(value,Rp_daily,Vp_daily, 0.95, 1)
VaR95_10day_VCM = VaR_VCM(value,Rp_daily,Vp_daily,0.95, 10)

print(f'方差-协方差法1天、99%的VaR:{VaR99_1day_VCM/10000:.2f}万元')
print(f'方差-协方差法10天、99%的VaR:{VaR99_10day_VCM/10000:.2f}万元')  
print(f'方差-协方差法1天、95%的VaR:{VaR95_1day_VCM/10000:.2f}万元')
print(f'方差-协方差法10天、95%的VaR:{VaR95_10day_VCM/10000:.2f}万元') 

#历史模拟法
#历史交易日投资组合的收益率序列
Rp = np.dot(ret,weights)
Rp = pd.DataFrame(Rp,index=ret.index,columns=['投资组合日收益'])

def VaR_history(value,ret,X,T):
    '''
    Parameters
    ----------
    value : 资产的价值
    ret : 资产的日收益率序列
    X : 置信水平
    T : 持有天数   
    '''  
    # Numpy 的 percentile 函数,可以直接返回序列相应的分位数
    return value*np.sqrt(T)*abs(np.percentile(ret,(1-X)*100))

VaR99_1day_history = VaR_history(value,Rp,0.99,1)
VaR99_10day_history = VaR_history(value,Rp,0.99,10)
VaR95_1day_history = VaR_history(value,Rp,0.95,1)
VaR95_10day_history = VaR_history(value,Rp,0.95,10)

print(f'历史模拟法1天、99%的VaR:{VaR99_1day_history/10000:.2f}万元')
print(f'历史模拟法10天、99%的VaR:{VaR99_10day_history/10000:.2f}万元')  
print(f'历史模拟法1天、95%的VaR:{VaR95_1day_history/10000:.2f}万元')
print(f'历史模拟法10天、95%的VaR:{VaR95_10day_history/10000:.2f}万元') 

#蒙特卡罗模拟法
m = 10000 #模拟次数
e1 = np.random.standard_t(df=len(ret),size=m)  #自由度为收益率数据长度的t分布
#e1 = np.random.standard_normal(size=m) #若服从正态分布,则此代码代替上行代码
R_mean_year = ret.mean()*252  #计算每一资产的年化平均收益率
R_vol_year = ret.std()*np.sqrt(252) #计算每一资产的年化波动率
dt=1/252 #时间间隔
S0=np.ones(len(weights))
S=np.zeros(shape=(m,len(weights))) #存放(模拟次数×资产数量)个模拟价格数据
for i in range(len(weights)):#代入随机过程
	S[:,i]=S0[i]*(np.exp((R_mean_year[i]-0.5*R_vol_year[i]**2)*dt+R_vol_year[i]*e1*np.sqrt(dt)))
    #每一行∑资产收益率×相应权重就得到资产组合的收益率,一共10000行
Sp_ret=(np.dot(S/S0-1,weights)) #资产组合收益率
  
#蒙特卡洛模拟法计算VaR
VaR99_1day_MS = value*abs(np.percentile(Sp_ret,1))    
VaR99_10day_MS = np.sqrt(10)*VaR99_1day_MS
VaR95_1day_MS = value*abs(np.percentile(Sp_ret,5))
VaR95_10day_MS = np.sqrt(10)*VaR95_1day_MS

#由于抽样随机数的原因,结果可能会有不同
print(f'蒙特卡罗模拟法1天、99%的VaR:{VaR99_1day_MS/10000:.2f}万元')
print(f'蒙特卡罗模拟法10天、99%的VaR:{VaR99_10day_MS/10000:.2f}万元')  
print(f'蒙特卡罗模拟法1天、95%的VaR:{VaR95_1day_MS/10000:.2f}万元')
print(f'蒙特卡罗模拟法10天、95%的VaR:{VaR95_10day_MS/10000:.2f}万元') 

参考资料:

https://blog.csdn.net/hzk427/article/details/104940735

https://blog.csdn.net/qq_18822147/article/details/108303843

https://blog.csdn.net/wxw_csdn/article/details/120206787?spm=1001.2014.3001.5506

Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。

更多推荐