1. 风险价值(VaR)简述
风险价值(value at risk,VaR)是指在一定的持有期和给定的置信水平下,利率、汇率、股价等风险因子发生变化时可能对投资组合造成的潜在最大损失。例如:持有期 1 天、置信水平 95% 的情况下,计算得到的 VaR 值为 1000 万元,则表明该投资组合在1天中有 95%的可能性损失不会超过1000万,换句话说,1 天中,有5%的可能性损失会超过 1000 万元。
VaR的大小取决于两个参数:持有期(N)、置信水平(X)。由于 VaR 度量的是投资组合的亏损,其对应于投资组合盈亏分布的左端尾部,为了表述方便,通常 VaR 值用绝对值表示。
VaR 的数学表达式如下:
P
r
o
b
(
Δ
P
<
−
V
a
R
)
=
1
−
X
P
r
o
b
:
概
率
函
数
Δ
P
:
投
资
组
合
亏
损
金
额
X
:
置
信
水
平
Prob(\Delta P < - VaR) = 1 - X \\[10pt] Prob:概率函数 \\[10pt] \Delta P:投资组合亏损金额 \\[10pt] X:置信水平
P r o b ( Δ P < − V a R ) = 1 − X P r o b : 概 率 函 数 Δ P : 投 资 组 合 亏 损 金 额 X : 置 信 水 平
根据巴塞尔协议的明确规定:银行需要计算持有期10天、置信水平99%的VaR。在实际计算中,通常先计算 N=1 时的VaR,在计算相同置信水平下 N>1 时的 VaR,其表达式如下:
N
天
V
a
R
=
1
天
V
a
R
∗
N
N天VaR = 1天VaR*\sqrt{N}
N 天 V a R = 1 天 V a R ∗ N
上式成立的条件是:投资组合价值在不同交易日之间的变化是相互独立并服从期望值为0的相同正态分布,其他情况下,该等式只是一个近似值。
1.1 Python可视化风险价值
利用Python对VaR进行可视化,图中阴影部分右侧的临界值就是对应置信水平的VaR,其程序如下:
import numpy as np
import pandas as pd
import scipy. stats as st
'''
st.norm中的子模块
pdf:概率密度函数
cdf:累计概率分布函数
ppf:分位点函数,cdf的反函数
'''
import matplotlib. pyplot as plt
plt. rcParams[ 'font.sans-serif' ] = [ 'SimHei' ] #中文显示问题
plt. rcParams[ 'axes.unicode_minus' ] = False #负数显示问题
a = 0.95 #置信水平
z = st. norm. ppf( q= 1 - a) #返回q对应的分位点
x = np. linspace( - 4 , 4 , 200 ) #组合的盈亏数组
y = st. norm. pdf( x) #组合盈亏对应的概率密度数组
x1 = np. linspace( - 4 , z, 100 ) #组合最小亏损值与返回的分位点构成的盈亏数组
y1 = st. norm. pdf( x1)
plt. figure( figsize= ( 8 , 6 ) )
plt. plot( x, y)
plt. fill_between( x1, y1)
plt. grid( 'True' )
2. VaR值的测度方法
2.1 方差-协方差法
数学假定:
1 投资组合的各风险因子服从联合正态分布
2 线性假定,持有期内,投资组合的风险暴露与风险因子之间是线性相关的
其表达式如下:
V
a
R
=
V
p
[
z
c
σ
p
−
E
(
R
p
)
]
E
(
R
p
)
=
∑
w
i
E
(
R
i
)
σ
2
=
W
c
o
v
(
R
i
,
R
j
)
W
T
V
p
:
投
资
组
合
的
最
新
价
值
z
c
:
显
著
性
水
平
c
对
应
的
分
位
数
绝
对
值
E
(
R
p
)
:
投
资
组
合
的
期
望
收
益
VaR = V_p[z_c\sigma_p - E(R_p)] \\[10pt] E(R_p) = \sum w_iE(R_i) \\[10pt] \sigma^2 = Wcov(R_i,R_j)W^{T} \\[10pt] V_p:投资组合的最新价值 \\[10pt] z_c:显著性水平 c 对应的分位数绝对值 \\[10pt] E(R_p):投资组合的期望收益 \\[10pt]
V a R = V p [ z c σ p − E ( R p ) ] E ( R p ) = ∑ w i E ( R i ) σ 2 = W c o v ( R i , R j ) W T V p : 投 资 组 合 的 最 新 价 值 z c : 显 著 性 水 平 c 对 应 的 分 位 数 绝 对 值 E ( R p ) : 投 资 组 合 的 期 望 收 益
本文通过一个案例来说明如何运用方差-协方差法计算投资组合的 VaR,该投资组合有 5 个不同的资产构成,投资组合当前的市值为1亿元,其权重权重配比如下表:
资产名称
贵州茅台
交通银行
嘉实增强信用基金
华夏恒生ETF基金
博时标普500ETF基金
权重
0.15
0.2
0.5
0.05
0.1
案例中投资组合2015年—2018年完整的数据可以通过百度网盘 获取,提取码:zbbx。
Python程序如下:
data = pd. read_excel( r'C:\Users\Administrator\Desktop\投资组合配置的资产情况.xlsx' , header = 0 , index_col = 0 )
#初始数据的归一化处理
R = np. log( data/ data. shift( 1 ) )
#处理缺失数据
R = R. dropna( )
R_mean = R. mean( ) #计算均值
R_cov = R. cov( ) #计算协方差
R_corr = R. corr( ) #计算相关系数
R_vol = R. std( ) #计算标准差
# 方差协方差方法
def VaR_VCM ( value, Rp, Vp, X, N) :
'''
Parameters
----------
value : 投资组合的价值
Rp : 投资组合的日收益率
Vp : 投资组合的日波动率
X : 置信水平
N : 持有天数
'''
import scipy. stats as st
import numpy as np
z = np. abs ( st. norm. ppf( q= 1 - X) )
return np. sqrt( N) * value* ( z* Vp- Rp)
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) ) )
print ( '投资组合日收益率:' , Rp_daily)
print ( '投资组合日波动率:' , Vp_daily)
D1 = 1
D2 = 10
X1 = 0.99
X2 = 0.95
value_port = 100000000
VaR99_1day_VCM = VaR_VCM( value= value_port, Rp= Rp_daily, Vp= Vp_daily, X= X1, N= D1)
VaR99_10day_VCM = VaR_VCM( value= value_port, Rp= Rp_daily, Vp= Vp_daily, X= X1, N= D2)
VaR95_1day_VCM = VaR_VCM( value= value_port, Rp= Rp_daily, Vp= Vp_daily, X= X2, N= D1)
VaR95_10day_VCM = VaR_VCM( value= value_port, Rp= Rp_daily, Vp= Vp_daily, X= X2, N= D2)
print ( '1天、99%的VaR:' , VaR99_1day_VCM)
print ( '10天、99%的VaR:' , VaR99_10day_VCM)
print ( '1天、95%的VaR:' , VaR95_1day_VCM)
print ( '10天、99%的VaR:' , VaR95_10day_VCM)
计算结果表明:持有期10天、置信水平99%的VaR=464.34万;持有期10天、置信水平95%的VaR=325.83万。
2.2 历史模拟法
历史模拟法:从当前回溯一定时期投资组合的历史盈亏,并把历史盈亏按照由大到小的顺序排列,从中找出符合给定置信水平的盈亏值。例如:1天、置信水平95%的VaR,把历史盈亏由大到小排列,VaR为95%的位置对应盈亏值的绝对值,或者由小到大排列,VaR为5%位置对应盈亏值的绝对值。
本文仍然使用上述案例数据进行说明,其Python程序如下:
#各资产配置
value_asset = value_port* weights
#历史交易日投资组合的盈亏值
Return_history = np. dot( R, value_asset)
Return_history = pd. DataFrame( Return_history, index= R. index, columns= [ '投资组合模拟日收益' ] )
#盈亏数据描述
Return_history. describe( )
Return_history. plot( )
#盈亏数据分布直方图
plt. hist( Return_history, bins= 30 )
plt. grid( 'True' )
#投资组合盈亏值的正态性检验
#KS检验,返回统计量及P值
st. kstest( rvs= Return_history[ '投资组合模拟日收益' ] , cdf= 'norm' )
#AD检验,返回统计量、显著性水平对应的临界值(统计量)、显著性水平
st. anderson( x= Return_history[ '投资组合模拟日收益' ] , dist= 'norm' )
#返回统计量及P值
st. shapiro( Return_history[ '投资组合模拟日收益' ] )
#返回统计量及P值
st. normaltest( Return_history[ '投资组合模拟日收益' ] )
由频数分布直方图、正态性检验的P值(P<1%)结果可知,投资组合的日收益数据不服从正态分布,运用方差—协方差方法计算的投资组合VaR值会存在偏差。使用历史模拟法的计算程序如下:
#计算历史模拟的VaR
VaR99_1day_history = np. abs ( np. percentile( a= Return_history[ '投资组合模拟日收益' ] , q= ( 1 - X1) * 100 ) )
VaR95_1day_history = np. abs ( np. percentile( a= Return_history[ '投资组合模拟日收益' ] , q= ( 1 - X2) * 100 ) )
VaR99_10day_history = np. sqrt( 10 ) * VaR99_1day_history
VaR95_10day_history = np. sqrt( 10 ) * VaR95_1day_history
print ( '1天、99%的VaR:' , VaR99_1day_history)
print ( '10天、99%的VaR:' , VaR99_10day_history)
print ( '1天、95%的VaR:' , VaR95_1day_history)
print ( '10天、99%的VaR:' , VaR95_10day_history)
计算结果表明:持有期10天、置信水平99%的VaR=675.38万;持有期10天、置信水平95%的VaR=274.21万。
2.3 蒙特卡洛模拟法
蒙特卡洛模拟法又称随机抽样或统计试验方法,即从一个给定的分布中,随机抽取随机数并进行计算,该方法能较好地逼近实际分布情况。
在投资组合的模拟抽样步骤如下:
1 利用第
i
i
i 个资产的当前价值(最新价值)
S
i
S_i
S i 加总计算投资组合的当前价值
S
p
S_p
S p 。
2 在第
i
i
i 个资产价值的日百分比变化
X
i
X_i
X i 所服从的分布中进行一次抽样得到
x
i
x_i
x i 。
3 利用抽样获取的
x
i
x_i
x i 计算第
i
i
i 个资产下一个交易日的收益金额变动
x
i
S
i
x_iS_i
x i S i 。
4 计算本次抽样获取的下一交易日投资组合的盈亏
Δ
S
p
=
∑
x
i
S
i
\Delta S_p = \sum x_iS_i
Δ S p = ∑ x i S i 。
5 重复上述第2-4步,并将获取的
Δ
S
p
\Delta S_p
Δ S p 按大小顺序排列,从而构建投资组合在下一交易日的盈亏概率分布。
6 计算持有期 1 天、置信水平 X 的 VaR 值,然后计算
N
天
的
V
a
R
N天的VaR
N 天 的 V a R (公式:
N
天
V
a
R
=
1
天
V
a
R
∗
N
N天VaR = 1天VaR*\sqrt{N}
N 天 V a R = 1 天 V a R ∗ N
)
本文仍然使用上述案例数据进行说明,在模拟过程中,需要用到金融资产价格服从的随机过程公式,即
S
t
=
S
t
−
Δ
t
e
(
μ
−
1
2
σ
2
)
Δ
t
+
σ
ε
t
Δ
t
S_t = S_{t-\Delta t}e^{(\mu-\frac{1}{2}\sigma^2)\Delta t + \sigma \varepsilon_t\sqrt{\Delta t}}
S t = S t − Δ t e ( μ − 2 1 σ 2 ) Δ t + σ ε t Δ t
上式中
ε
t
\varepsilon_t
ε t 的模拟过程中假定服从
t
t
t 分布或者正态分布。
其服从
t
t
t 分布的 Python 程序如下:
#蒙特卡洛模拟法
import numpy. random as npr
I = 10000 #模拟次数
#从学生t分布进行I次模拟
epsilon = npr. standard_t( df= len ( R) , size= I)
#获取最新收盘价
S1 = data. iloc[ - 1 , 0 ] #第一个资产的最新价格
S2 = data. iloc[ - 1 , 1 ]
S3 = data. iloc[ - 1 , 2 ]
S4 = data. iloc[ - 1 , 3 ]
S5 = data. iloc[ - 1 , 4 ]
R_mean = R. mean( ) * 252 #投资组合各资产的年化收益
R_vol = R. std( ) * np. sqrt( 252 ) #投资组合各资产的年化波动率
dt = 1 / 252 #年化单个交易日
#模拟投资组合下一个交易日各资产的收盘价
S1_new = S1* np. exp( ( R_mean[ 0 ] - 0.5 * R_vol[ 0 ] ** 2 ) * dt + R_vol[ 0 ] * epsilon* np. sqrt( dt) )
S2_new = S2* np. exp( ( R_mean[ 1 ] - 0.5 * R_vol[ 1 ] ** 2 ) * dt + R_vol[ 1 ] * epsilon* np. sqrt( dt) )
S3_new = S3* np. exp( ( R_mean[ 2 ] - 0.5 * R_vol[ 2 ] ** 2 ) * dt + R_vol[ 2 ] * epsilon* np. sqrt( dt) )
S4_new = S4* np. exp( ( R_mean[ 3 ] - 0.5 * R_vol[ 3 ] ** 2 ) * dt + R_vol[ 3 ] * epsilon* np. sqrt( dt) )
S5_new = S5* np. exp( ( R_mean[ 4 ] - 0.5 * R_vol[ 4 ] ** 2 ) * dt + R_vol[ 4 ] * epsilon* np. sqrt( dt) )
#模拟投资组合下一个交易日各资产的盈亏
S1_delta = ( S1_new/ S1 - 1 ) * value_port* weights[ 0 ]
S2_delta = ( S2_new/ S2 - 1 ) * value_port* weights[ 1 ]
S3_delta = ( S3_new/ S3 - 1 ) * value_port* weights[ 2 ]
S4_delta = ( S4_new/ S4 - 1 ) * value_port* weights[ 3 ]
S5_delta = ( S5_new/ S5 - 1 ) * value_port* weights[ 4 ]
#计算投资组合下一个交易日的盈亏
Sp_delta = S1_delta + S2_delta + S3_delta + S4_delta + S5_delta
#下一交易日投资组合盈亏的可视化
plt. figure( figsize= ( 10 , 8 ) )
plt. hist( Sp_delta, bins= 30 )
plt. ylabel( '频数' )
plt. grid( True )
#蒙特卡洛模拟法计算VaR
VaR99_1day_MS = np. abs ( np. percentile( a= Sp_delta, q= ( 1 - X1) * 100 ) )
VaR95_1day_MS = np. abs ( np. percentile( a= Sp_delta, q= ( 1 - X2) * 100 ) )
VaR99_10day_MS = np. sqrt( 10 ) * VaR99_1day_MS
VaR95_10day_MS = np. sqrt( 10 ) * VaR95_1day_MS
#由于抽样随机数的原因,结果可能会有不同
print ( '1天、99%的VaR:' , VaR99_1day_MS)
print ( '10天、99%的VaR:' , VaR99_10day_MS)
print ( '1天、95%的VaR:' , VaR95_1day_MS)
print ( '10天、95%的VaR:' , VaR95_10day_MS)
计算结果表明:持有期10天、置信水平99%的VaR=640.81万;持有期10天、置信水平95%的VaR=436.52万。
其服从正态分布的 Python 程序如下:
#服从正态分布进行模拟
epsilon_norm = npr. standard_normal( I)
S_new = np. zeros( shape= ( I, len ( R_mean) ) )
S1_new = S1* np. exp( ( R_mean[ 0 ] - 0.5 * R_vol[ 0 ] ** 2 ) * dt + R_vol[ 0 ] * epsilon* np. sqrt( dt) )
for i in range ( len ( R_mean) ) :
S_new[ : , i] = data. iloc[ - 1 , i] * np. exp( ( R_mean[ i] - 0.5 * R_vol[ i] ** 2 ) * dt + R_vol[ i] * epsilon_norm* np. sqrt( dt) )
S = np. array( data. iloc[ - 1 ] )
Sp_delta_norm = ( np. dot( S_new/ S- 1 , weights) ) * value_port
plt. figure( figsize= ( 10 , 8 ) )
plt. hist( Sp_delta_norm, bins= 30 )
plt. ylabel( '频数' )
plt. grid( True )
VaR99_1day_MSnorm = np. abs ( np. percentile( a= Sp_delta_norm, q= ( 1 - X1) * 100 ) )
VaR95_1day_MSnorm = np. abs ( np. percentile( a= Sp_delta_norm, q= ( 1 - X2) * 100 ) )
VaR99_10day_MSnorm = np. sqrt( 10 ) * VaR99_1day_MSnorm
VaR95_10day_MSnorm = np. sqrt( 10 ) * VaR95_1day_MSnorm
print ( '1天、99%的VaR:' , VaR99_1day_MSnorm)
print ( '10天、99%的VaR:' , VaR99_10day_MSnorm)
print ( '1天、95%的VaR:' , VaR95_1day_MSnorm)
print ( '10天、95%的VaR:' , VaR95_10day_MSnorm)
计算结果表明:持有期10天、置信水平99%的VaR=626.17万;持有期10天、置信水平95%的VaR=451.86万。
3. 回溯检验
回溯检验又称为后检验,即通过模型获取的VaR与实际发生的损益进行比较,以检验模型的准确性、可靠性,并据此对模型进行改进及优化。
本文仍然使用上述案例进行说明,其Python程序如下:
#回溯检测
Return_2015 = Return_history. loc[ '2015-01-01' : '2015-12-31' ]
Return_2016 = Return_history. loc[ '2016-01-01' : '2016-12-31' ]
Return_2017 = Return_history. loc[ '2017-01-01' : '2017-12-31' ]
Return_2018 = Return_history. loc[ '2018-01-01' : '2018-12-31' ]
days_2015 = len ( Return_2015)
days_2016 = len ( Return_2016)
days_2017 = len ( Return_2017)
days_2018 = len ( Return_2018)
VaR_2015 = pd. DataFrame( - VaR95_1day_VCM* np. ones_like( Return_2015) , index= Return_2015. index)
VaR_2016 = pd. DataFrame( - VaR95_1day_VCM* np. ones_like( Return_2016) , index= Return_2016. index)
VaR_2017 = pd. DataFrame( - VaR95_1day_VCM* np. ones_like( Return_2017) , index= Return_2017. index)
VaR_2018 = pd. DataFrame( - VaR95_1day_VCM* np. ones_like( Return_2018) , index= Return_2018. index)
plt. figure( figsize= ( 9 , 15 ) )
plt. subplot( 4 , 1 , 1 )
plt. plot( Return_2015)
plt. plot( VaR_2015)
plt. ylabel( '频数' )
plt. grid( True )
plt. subplot( 4 , 1 , 2 )
plt. plot( Return_2016)
plt. plot( VaR_2016)
plt. ylabel( '频数' )
plt. grid( True )
plt. subplot( 4 , 1 , 3 )
plt. plot( Return_2017)
plt. plot( VaR_2017)
plt. ylabel( '频数' )
plt. grid( True )
plt. subplot( 4 , 1 , 4 )
plt. plot( Return_2018)
plt. plot( VaR_2018)
plt. ylabel( '频数' )
plt. grid( True )
#计算超出VaR的天数
dayexcept_2015 = len ( Return_2015[ Return_2015[ '投资组合模拟日收益' ] < - VaR95_1day_VCM] )
dayexcept_2016 = len ( Return_2016[ Return_2016[ '投资组合模拟日收益' ] < - VaR95_1day_VCM] )
dayexcept_2017 = len ( Return_2017[ Return_2017[ '投资组合模拟日收益' ] < - VaR95_1day_VCM] )
dayexcept_2018 = len ( Return_2018[ Return_2018[ '投资组合模拟日收益' ] < - VaR95_1day_VCM] )
print ( '2015年超出风险天数:' , dayexcept_2015)
print ( '2015年超出风险天数在全年的占比:' , dayexcept_2015/ days_2015)
print ( '2016年超出风险天数:' , dayexcept_2016)
print ( '2016年超出风险天数在全年的占比:' , dayexcept_2016/ days_2016)
print ( '2017年超出风险天数:' , dayexcept_2017)
print ( '2017年超出风险天数在全年的占比:' , dayexcept_2017/ days_2017)
print ( '2018年超出风险天数:' , dayexcept_2018)
print ( '2018年超出风险天数在全年的占比:' , dayexcept_2018/ days_2018)
计算结果表明:2015年超出风险天数为22天,全年占比为9.05%,明显高于5%;2016年超出风险天数为9天,全年占比为3.69%;2017年超出风险天数为1天,全年占比为0.4%;2018年超出风险天数为6天,全年占比为2.47%。
4. 压力VaR
压力测试是一种以定量分析为主的风险分析方法,进而测算在小概率事件及极端情况下可能面临的损失。压力情景通常分为如下两种方法:头脑风暴和历史重现法。
压力风险价值具体是指当市场变量在一定压力市场条件下通过历史模拟法计算得到的风险价值,其计算步骤与历史模拟法相同。
本文仍然使用上述案例进行说明,2015年6月持续数月的股灾及2016年1月初的股市熔断可以看做案例数据中的极端情况。其Python程序如下:
#压力测试
return_stress = Return_history. loc[ '2015-06-15' : '2016-01-07' ]
return_stress. describe( )
return_zero = pd. DataFrame( np. zeros_like( return_stress) , index = return_stress. index)
#投资组合盈亏与0的可视化比较
plt. figure( figsize= ( 8 , 6 ) )
plt. plot( return_stress)
plt. plot( return_zero)
plt. grid( True )
SVaR99_1day = np. abs ( np. percentile( a= return_stress, q= ( 1 - X1) * 100 ) )
SVaR95_1day = np. abs ( np. percentile( a= return_stress, q= ( 1 - X2) * 100 ) )
SVaR99_10day = np. sqrt( 10 ) * SVaR99_1day
SVaR95_10day = np. sqrt( 10 ) * SVaR95_1day
print ( '1天、99%的VaR:' , SVaR99_1day)
print ( '10天、99%的VaR:' , SVaR99_10day)
print ( '1天、95%的VaR:' , SVaR95_1day)
print ( '10天、95%的VaR:' , SVaR95_10day)
计算结果表明:持有期10天、置信水平99%的压力VaR=1102.12万;持有期10天、置信水平95%的压力VaR=670.14万。