Python学习教程(Python学习路线):Python——SciPy精讲
Python学习教程(Python学习路线):Python——SciPy精讲
SciPy 是 Python 里处理科学计算 (scientific computing) 的包,使用它遇到问题可访问它的官网 (https://www.scipy.org/).去找答案。 在使用scipy之前,需要引进它,语法如下:
importscipy
这样你就可以用scipy里面所有的内置方法 (build-in methods) 了,比如插值、积分和优化。
numpy.interpolatenumpy.integratenumpy.optimize
但是每次写scipy字数有点多,通常我们给scipy起个别名 sp,用以下语法,这样所有出现scipy的地方都可以用 sp 替代。
importscipyassp
SciPy 是建立 NumPy 基础上的,很多关于线性代数的矩阵运算在 NumPy 都能做,因此就不重复在这里讲了。此外在〖数组计算之 NumPy (下)〗也说过,数组计算比矩阵计算更通用,
本章换一种写法,我们专门针对科学计算中三个具体问题来介绍 SciPy,它们就是
插值(interpolation)
积分(integration)
优化(optimization)
对于以上每一个知识点我都会介绍一个
简单例子来明晰 SciPy 里各种函数的用法
和金融相关的实际例子
计算远期利率:在零息曲线中插值折现因子
计算期权价格:将期望写成积分并数值求解
配置资产权重:优化「风险平价」模型权重
1.插值
给定一组 (xi, yi),其中 i = 1, 2, ..., n,而且 xi是有序的,称为「标准点」。插值就是对于任何新点xnew,计算出对应的 ynew。换句话来说,插值就是在标准点之间建立分段函数(piecewise function),把它们连起来。这样给定任意连续 x 值,带入函数就能计算出任意连续 y 值。
在 SciPy 中有个专门的函数scipy.interpolate是用来插值的,首先引进它并记为 spi。
importscipy.interpolateasspi
简单例子
用 scipy.interpolate 来插值函数 sin(x) + 0.5x。
基本概念
首先定义 x 和函数 f(x):
x=np.linspace(-2*np.pi,2*np.pi,11)f=lambdax:np.sin(x)+0.5*xf(x)
array([-3.14159265,-1.56221761,-1.29717034,-1.84442231,
-1.57937505,0. ,1.57937505,1.84442231,
1.29717034,1.56221761,3.14159265])
接下来介绍 scipy.interpolate 里面两大杀器:splrep和splev。两个函数名称都是以spl开头,全称spline (样条),可以理解这两个函数都和样条有关。不同的是,两个函数名称以rep和ev结尾,它们的意思分别是:
rep:representation 的缩写,那么splrep其实生成的是一个「表示样条的对象」
ev:evaluation 的缩写,那么splev其实用于「在样条上估值」
splrep和splev像是组合拳 (one two punch)
前者将 x, y 和插值方式转换成「样条对象」tck
后者利用它在 xnew上生成ynew
一图胜千言:
接下来仔细分析一下 tck。
tck=spi.splrep(x,f(x),k=1)tck
tck 就是样条对象,以元组形式返回,tck这名字看起来很奇怪,实际指的是元组 (t,c,k) 里的三元素:
t - vector of knots (节点)
c - spline cofficients (系数)
k - degree of spline (阶数)
对照上图,tck 确实一个元组,包含两个 array和一个标量 1,其中
第一个 array 是节点,即标准点,注意到一开始 x 只有 11 个,但现在是 13 个,首尾都往外补了一个和首尾一样的 x
第二个 array 是系数,注意它就是 y 在尾部补了两个 0
标量 1 是阶数,因为在调用splrep 时就把 k 设成 1
注:前两个 array 我只是发现这个规律,但解释不清楚为什么这样。它和 matlab 里面的 spline() 的产出不太一样,希望懂的读者可以留言区解释一下。
虽然解释不清楚前两个 array,那就把 tck 当成是个黑匣子 (black-box) 直接用了。比如可用PPoly.from_spline来查看每个分段函数的系数。
pp=spi.PPoly.from_spline(tck)pp.c.T
array([[ 1.25682673, -3.14159265],
[ 1.25682673, -3.14159265],
[ 0.21091791, -1.56221761],
[-0.43548928, -1.29717034],
[ 0.21091791, -1.84442231],
[ 1.25682673, -1.57937505],
[ 1.25682673, 0. ],
[ 0.21091791, 1.57937505],
[-0.43548928, 1.84442231],
[ 0.21091791, 1.29717034],
[ 1.25682673, 1.56221761],
[ 1.25682673, 3.14159265]])
tck 的系数数组里有 13 个元素,而上面数组大小是 (12,2),12表示12段,2表示每段线性函数由2个系数确定。
把 x 和 tck 丢进splev函数,我们可以插出在 x 点对应的值 iy。
iy=spi.splev(x,tck)iy
array([-3.14159265,-1.56221761,-1.29717034,-1.84442231,
-1.57937505,0. ,1.57937505,1.84442231,
1.29717034,1.56221761,3.14159265])
用 Matplotlib 来可视化插值的 iy和原函数 f(x)发现 iy 都在 f(x) 上。Matplotlib 是之后的课题,现在读者可忽略其细节。
除了可视化,我们还可以用具体的数值结果来评估插值的效果:
np.allclose(f(x),iy)np.sum((f(x)-iy)**2)/len(x)
True
0.0
第一行allclose的结果都是 True 证明插值和原函数值完全吻合,第二行就是均方误差 (mean square error, MSE),0.0 也说明同样结果。
上面其实做的是在「标准点 x」上插值,那得到的结果当然等于「标准点 y」了。这种插值确实意义不大,但举这个例子只想让大家
明晰splrep和splev是怎么运作的
如何可视化插出来的值和原函数的值
如何用allclose来衡量插值和原函数值之间的差异
一旦弄明白了这些基础,接下来就可以秒懂更实际的例子了。
正规例子
上面在「标准点 x」上插值有点作弊,现在我们在 50 个「非标准点 xd」上线性插值得到 iyd。
xd=np.linspace(1.0,3.0,50)iyd=spi.splev(xd,tck)print(xd,'\n\n',iyd)
密密麻麻的数字啥都看不出来,可视化一下把。
这插得是个什么鬼?红色插值点在第二段和深青色原函数差别也太远了吧 (MSE 也显示有差异)。
np.sum((f(xd)-iyd)**2)/len(xd)
0.011206417290260647
问题出在哪儿呢?当「标准点 x」不密集时 (只有 11 个),分段线性函数来拟合 sin(x) + 0.5x 函数当然不会太好啦。那我们试试分段三次样条函数(k = 3)。
tck=spi.splrep(x,f(x),k=3)iyd=spi.splev(xd,tck)
可视化一下并计算 MSE 看看
np.sum((f(xd)-iyd)**2)/len(xd)
1.6073247177220542e-05
视觉效果好多了!误差小多了!
金融例子
用 scipy.interpolate 来插值折现因子来计算远期利率。
在金融市场中,每个货币都有自己相对应的折现曲线,简单来说,就是在「标准日期」上一组折现因子 (discount factor) 或零息利率 (zero rate)。
那么在「非标准日期」上折现因子或零息利率怎么求呢?插值!
本小节的知识点内容来之〖弄懂金融十大话题 (上)〗。
这里面说的插值是分段(piecewise) 插值!对于线性插值,不是说一条直线拟合上表的 9 个点,这样也是不可能做到的。但是分段线性插值就可以完美解决这个问题,因为 9 个点,有 8 段,每一段首尾两个点,可以连一条直线,全部点之间连起来不就是分段线性插值吗?三种最常见的插值方法
分段常函数
分段线性函数
分段三次样条函数
首先给出数学符号。给定 N 数据点 (xi,fi), i = 1, 2, …, N,其中x1 <x2< ... <xN。我们希望找到一个函数 f(x) 来拟合这 N 个数据点,对于分段函数,因为有 N 个数据点,需要 N -1 段函数。
分段常(piecewise constant) 函数
在这种情况,每一段函数都是一个常数,这种插值方法
优点是简单
缺点是在数据点上不连续,更不可导
适用于在某些模型的参数 (比如 Heston 模型中的均值回归率和波动率的波动率) 上插值 (模型参数通常只用常数和分段常函数,但后者比前者能更好的拟合市场数据,因为它有更多自由度)。
不适用于曲线和波动率插值
分段常函数不连续,通常称作C-1函数。
分段线性(piecewise linear) 函数
在这种情况,每一段函数都是一个线性函数,这种插值方法
优点是简单,在数据点上连续,而且形状保持性很好 (插出的值只和它相邻两个数据点有关,别的数据怎么动都不影响它的插值)
缺点是在数据点上不可导
适用于曲线和波动率插值
不适用于在 Hull-White 模型下的曲线插值 (Hull-White 模型需要对曲线求二阶导)
分段线性函数连续但是不可导,通常称作C0函数。
分段三次样条(piecewise cubic spline) 函数
在这种情况,每一段函数都是一个三次多项式函数,这种插值方法
优点是在数据点上可导甚至可导三次 (非常平滑)
缺点是有些复杂,而且形状保持性不好 (插出的值和整个数据点有关,别的数据动以下都会影响它的插值)
适用于曲线的插值
分段三次样条函数连续而且二阶可导,通常称作C2函数。
对上面曲线插值有一个概念后,首先用 pandas 读取数据。Pandas 是下帖内容,你就先把它当成一个可以用字符串来索引或切片的二维数据结构。
importpandasaspdcurve=pd.read_excel('CNYzerocurve.xlsx')curve
该曲线用于估值日 2019-04-01,上图第一个点的日期是2019-04-03,通常称为即期日,往后的日期分别是从即期日开始往后推 1W, 1M, 2M, 3M, 6M, 9M, 1Y 和 2Y 得到的。
用 Matplotlib 来可视化折现因子和零息利率。
这里用了双 y 轴来区分折现因子和零息利率,左边是折现因子,右边是零息利率,其实通过观察 y 轴的数值也可以区分出来两者。
现在实际问题是要计算从起始日 2019-08-05 到终止日 2019-11-05 的 3M 远期利率,根据其公式 (不推导):
要计算远期利率,核心问题就是计算 2019-08-05 和 2019-11-05 两天的折现因子。为了简化,我们把这两天之间的年限差近似定为 0.25≈ 3个月/12个月。具体步骤如下:
计算曲线上「标准日期」到「估值日」之间的天数差
计算「起始日」和「终止日」到「估值日」之间的天数差
插出「起始日」和「终止日」上的折现因子 (四种方法)
将折现因子带入公式计算远期利率
第一步:计算曲线上「标准日期」到「估值日」之间的天数差
today=pd.Timestamp('2019-04-01')daydiff=curve['Date']-todaydaydiff
2days
19days
232days
363days
493days
5185days
6277days
7368days
8733days
Name:Date, dtype: timedelta64[ns]
上面结果不是数值型变量 (还带个 days),用.dt.days.values得到相应的 numpy 数组。
d=daydiff.dt.days.valuesd
array([2,9,32,63,93,
185,277,368,733], dtype=int64)
第二步:计算「起始日」和「终止日」到「估值日」之间的天数差
importdatetimestart=datetime.datetime.strptime('2019-08-05','%Y-%m-%d')end=datetime.datetime.strptime('2019-11-05','%Y-%m-%d')d_s=(start-today).daysd_e=(end-today).daysprint(d_s,d_e)
126218
需要引进datetime这个库将字符型日期转成真正的 date 格式。
第三步:插出「起始日」和「终止日」上的折现因子,有多种方法,不同数据商对不同曲线也有不同的设置,常见的四种有:
在折现因子上线性插值
在折现因子上三次样条插值
在ln(折现因子)上线性插值
在零息曲线上线性插值,再计算折现因子
DF 上线性插值
tck=spi.splrep(d,curve['DiscountFactor'],k=1)DF_s=spi.splev(d_s,tck)DF_e=spi.splev(d_e,tck)print(DF_s,DF_e)
0.99094851661881770.9828538249018102
splrep里面 k 设为 1 表示线性插值。
DF 上三次样条插值
tck=spi.splrep(d,curve['DiscountFactor'],k=3)DF_s=spi.splev(d_s,tck)DF_e=spi.splev(d_e,tck)print(DF_s,DF_e)
0.99095720125970550.9827493083500931
splrep里面 k 设为 3 表示三次样条插值。
ln(DF) 上线性插值
tck=spi.splrep(d,np.log(curve['DiscountFactor']),k=1)DF_s=np.exp(spi.splev(d_s,tck))DF_e=np.exp(spi.splev(d_e,tck))print(DF_s,DF_e)
0.99094022188342390.9828472942639631
把 ln(DF) 放入splrep中,插出来也是 ln 形式,要最终得到折现因子,还需要用 exp 函数还原。
Rate 上线性插值
tck=spi.splrep(d,curve['ZeroRate(%)'],k=1)r_s=spi.splev(d_s,tck)r_e=spi.splev(d_e,tck)DF_s=np.exp(-d_s/365*r_s/100)DF_e=np.exp(-d_e/365*r_e/100)print(DF_s,DF_e)
0.99216067267778620.9843810241053533
插出来的零息利率,需要用以下公式计算出折现因子
DF = exp( -d/365× r/100)
d 除以365转换成年限,r 除以100是因为 r 单位是 %。
第四步:将折现因子带入公式计算远期利率
F=0.25*(DF_s/DF_e-1)*100
第三步中四种方法计算出来的远期利率 (%) 为
DF上线性插值 - 2.059%
折DF上三次样条插值 - 2.088%
ln(DF)上线性插值 - 2.059%
Rate上线性插值 - 1.976%
四个远期利率差别都不大,业界使用较多的是第 3 和 4 种。
2.积分
在 SciPy 中有个专门的函数scipy.integrate是用来做数值积分的,首先引进它并记为 sci。
importscipy.integrateassci
简单例子
用 scipy.integrate 来对函数 sin(x) + 0.5x 求积分。
首先定义被积函数 f(x):
f=lambdax:np.sin(x)+0.5*x
假设我们想从 x= 0.5 到 9.5 对 f(x) 求积分,可以手推出
在scipy.integrate里还有些数值积分的函数:
fixed_quad:fixed Gaussian quadrature (定点高斯积分)
quad:adaptive quadrature (自适应积分)
romberg:Romberg integration (龙贝格积分)
trapz:用 trapezoidal 法则
simps:用 Simpson’s 法则
前三个函数fixed_quad,quad,romberg的参数是被积函数、下界和上界。代码如下:
sci.fixed_quad(f,a,b)[0]sci.quad(f,a,b)[0]sci.romberg(f,a,b)
24.366995967084602
24.374754718086752
24.374754718086713
对后两个函数trapz和simps,首先在上下界之间取 n 个点 xi,再求出对应的函数值 f(xi),再把当参数f(xi)和xi传到函数中。代码如下:
xi=np.linspace(a,b,100)sci.trapz(f(xi),xi)sci.simps(f(xi),xi)
24.373463386819818
24.37474011548407
和解析解24.37475471808675比较,quad的结果最精确。一般当被积函数不规则时 (某段函数值激增),quad(自适应积分) 的结果也是最好。
金融例子
用 scipy.integrate 来以数值积分的形式给欧式期权定价。
注:本节主要将数值积分的用途,因此金融模型上的很多设置我们都用最简单的,比如常数型的模型参数等等。
股票类的Black-Scholes (BS) 模型下的 SDE 是描述股票价格 (stock price) 的走势:
其中
S(t) = 股票价格
r = 瞬时无风险利率
σ = S(t)的瞬时波动率
B(t) = 布朗运动
欧式看涨期权 (call option) 在 BS 模型下的解析解 (closed-form solution) 如下:
编写一个程序计算 call 的解析解很容易:
这里需要引入 scipy.stats 下的 norm 库,使用里面 cdf 函数来计算正态分布的累积分布概率。
假设股价 S0 = 100,行权价格 K = 95,利率为 5%,期限为 1 年,波动率为 10%,带入写好的 bscall 函数来计算期权的价值。
(S0,K,r,T,sigma)=(100,95,0.05,1,0.1)bscall(S0,K,r,T,sigma)
10.405284289598569
大概记注上面的期权值10.4053。假设我们推导能力不强或者对于更复杂的期权没有解析解,只要知道 ST的分布,我们可以试着把「期望值」写成「积分」形式,再用 x = lnST做个转换,最终可推出下式:
为了求数值积分,我们需要知道 x 是如何分布,也就是推出 x 的密度分布函数 fX(x),推导如下 (不是本帖的重点,如无兴趣可跳过下框内容):
给定 S 的随机微分方程,首先用伊藤公式推出 lnS 的随机微分方程
在 0 到 T 两边求积分,整理得到ST的解。
其中 z 是标准正态分布变量z ~ N(0, 1)。
用之前的变量转化 x = lnST得到 x 的解。
显然 x 是个正态分布,均值为 lnS0+(rT - 0.5σ2T),方差是σ2T。用 NPDF 代表正态分布 (Normal) 的密度分布函数 (PDF),可把 call 的价值写成积分形式,其中
被积函数是「支付函数」和「正态分布密度分布函数」的乘积
下界和上界分别是 lnK 和 +∞
最终表达式如下:
跟着「被积函数」的表达式敲代码
mu=np.log(S0)+(r*T-0.5*sigma**2*T)v=sigma*np.sqrt(T)f=lambdax:np.exp(-r*T)*(np.exp(x)-K)*norm.pdf(x,mu,v)
定义上界和下界
(lb,ub)=(np.log(K),7)
注意上界不要定义成 +∞。稍微分析下 x = lnST,当ST=e7≈ 1097 对于 S0= 100 已经很大了,因此上界设为 7 比较合理。
看看三个数值积分的结果如何。
sci.quad(f,lb,ub)[0]xi=np.linspace(lb,ub,1000)sci.trapz(f(xi),xi)sci.simps(f(xi),xi)
10.405284289598615
10.405170993379011
10.405287100064612
结果和之前的解析解10.4053都相当接近。
用数值积分来求解欧式期权的确有点多此一举 (ovekill),但很多复杂的产品是没有解析解的,除了用数值解的「偏微分方法有限差分法」和「蒙特卡洛法」,数值积分也是一种选择。
3.优化
在 SciPy 中有个专门的函数scipy.optimize是用来优化的,首先引进它并记为 spo。
importscipy.optimizeasspo
优化问题可分为无约束优化(unconstrained optimization) 和有约束优化(constrained optimization),我们用简单例子来介绍前者,用金融例子来介绍后者。
简单例子
用 scipy.optimize 来求出函数 sin(x) + 0.05x2 + sin(y) + 0.05y2 的最小值。
首先定义函数
f=lambdax,y:np.sin(x)+0.05*x**2+np.sin(y)+0.05*y**2
接着可视化函数
不难看出该函数有多个局部最小值 (local minimum) 和一个全局最小值 (global minimum)。我们目标是求后者,主要步骤如下:
在 (x-y) 定义域上选点,求出函数值 f(x, y),找出最小值对应的 x* 和 y*
用x* 和 y* 当初始值,求出函数全局最小值
第一步:用蛮力找函数最小值以及对应的参数
之所以用「蛮力」一词,是因为接下来要用到brute函数,而 bruteforce 就是蛮力的意思。首先定义函数 fo (其实就是上面的 f),只不过brute函数要求用一个元组把若干参数集合起来。此外我们添加一个 print() 语句,为了检查中间产出。
将 x 和 y 在 -10 到 10 以步长为 5 来切片 (回顾切片是前闭后开的,因此切片完得到的是 -10, -5, 0, 5,而不包括 10 这个点)
output=Truerranges=((-10,10,5),(-10,10,5))spo.brute(fo,rranges,finish=None)
从上面结果可看出,函数在 (0, 0) 是取最小值 0。真是最小值吗?我也不知道,但是以 5 为步长是不是太粗糙了些,接下来用 0.1 为步长。这时把 output 设为 False 是因为不想看到打印的内容。
output=Falserranges=((-10,10,0.1),(-10,10,0.1))opt1=spo.brute(fo,rranges,finish=None)opt1
array([-1.4,-1.4])
fo(opt1)
-1.7748994599769203
当步长变小,我们能在更细的网格上计算函数值,这是函数在 (-1.4, -1.4) 取最小值 -1.7749,明显比函数在 (0, 0) 上的值 0 要小。
第二步:把参数当初始值,求函数全局最小值
如果网格足够密,上面-1.7749 大概率是全局最小值而 (-1.4, -1.4) 是对应的最优解;如果网格不是足够密,那么以 (-1.4, -1.4) 当初始值,也能很大概率找到全局最小值。
用fmin函数,将刚才 opt1 传进去,并设定 x 和 f 的终止条件 xtol 和 ftol,和最多迭代次数 maxiter 和最多运行函数次数 maxfun。
output=Trueopt2=spo.fmin(fo,opt1,xtol=0.001,ftol=0.001,maxiter=15,maxfun=20)opt2
此时最优解为 (-1.42702972, -1.42876755),而对应的函数值为
output=Falsefo(opt2)
-1.7757246992239009
比刚才函数在 (-1.4, -1.4) 取的最小值 -1.7749 又小了一些。
好的初始值对求函数的最优解影响比较大。假设我们无脑的用 (2, 2) 当初始值,看看会发生什么。
output=Falseopt3=spo.fmin(fo,(2,2),maxiter=250)opt3
Optimization terminated successfully.
Currentfunctionvalue: 0.015826
Iterations:46
Function evaluations:86
array([4.2710728,4.27106945])
求得函数在 (4.2710728, 4.2710728) 取的最小值 0.015826,是不是错的太离谱了。
金融例子
用 scipy.optimize 来用「风险平价」模型为资产配置最优权重。
本小节的知识点内容来自〖资产配置〗。
投资组合的资产配置是个很重要的课题,投资者为了最大化回报或最小化风险,可以给各种资产配置不同的权重。本节我们看一个很流行的资产配置方法 - 风险平价 (Risk Parity, RP)。但首先我们先来看看它的通用版本,风险预算(Risk Budgeting, RB)。
风险预算(RB)可以基于投资者对资产未来表现(主要是风险)的具体看法,或一些通用原则来给资产来分配风险预算,而不是给资产分配权重。下图画出两者的区别。
传统的FW模型把60%分给股票而40%分给债券,但是这样的一个投资组合90%的风险都来自股票只有10%的风险来自债券。那么这个组合更容易出现股票尾部风险(tail risk)。一个风险更均衡的投资组合应该选择配置更多债券(比如75%)和更少股票(比如25%),如下图所示。
RB模型的思路就是通过分配风险(上图的风险比例)来影响权重(上图的资产权重),通常是给风险低的资产(如债券)高风险配额,而风险高的资产(如股票)低风险配额。
接下来我们看看RB模型的数学公式吧,首先回顾组合风险
对于第i个资产,其边际风险贡献(Marginal Risk Contribution, MRC)是该资产权重wi的微小变化对组合风险σp所带来的影响。用数学公式表示就是对σp求wi的偏导数。
第i个资产的总体风险贡献(Total Risk Contribution, TRC)是其MRC乘以其权重,顾名思义,这个总体贡献一方面来自MRC,一方面来自权重,数学表达式为:
根据TRCi的定义,即第i个资产对总体风险的贡献,可推出它们总和应该等于组合风险sp,从数学上也可证实此关系
上式两边同时除以σp,并定义风险预算si为TRCi的占比,可得sT1= 1
由上式看出si也类似于权重,只不过是风险上的权重,而wi是资产上的权重。下面给出si和wi之间的关系
在RB模型中,股票权重等于风险预算除以贝塔,因此,RB模型依赖于贝塔的预测质量。归一化之后的权重等于
事先将一组风险预算分配好,例如s= [20%, 30%, 50%],再数值解下面序列二次规划(Sequential Quadratic Programming, SQP)问题可以得到RB模型下的最佳权重
风险平价 (RP) 就是等量的风险预算,即为投资组合中的所有资产分配相等的风险。
类比RB,RP给每个风险配额si的分配1/n的权重,这时组合权重为
同样可得到RP模型下的优化问题(用1/n替代si)
这是一个有约束 (constrainted) 的优化问题,我们可用scipy.optimize里的minimize函数来求解 RP 的权重。首先来定义 risk_parity 函数:
该函数的两个参数 sigma 和 rho 是 n 个资产的波动率向量 (一维数组) 和相关系数矩阵 (二维数组),其中
obj 就是用 numpy 把上面目标函数用「匿名函数」的形式表示出来
限制条件有两种形式,等式 (eq) 和不等式 (ineq),分别用 dict 的形式表达,而限制条件的表达式也用「匿名函数」来表示
最后在minimize函数设定好目标函数、初始值、算法、限制条件和终止条件,得到一个 dict 类的结果 w。
两个资产
先分析简单的股票和债券两个资产组合:
股票的预期超额回报为10%,波动率为20%
债券的预期超额回报为5%,波动率为10%
它们相关系数为 -10%
mu=np.array([0.1,0.05])sigma=np.array([0.2,0.1])rho=np.array([[1,-0.1],[-0.1,1]])
运行 risk_partiy 函数
result=risk_parity(sigma,rho)result
fun:3.26901989274624e-15
jac: array([-1.86742928e-07,1.55459627e-07])
message:'Optimization terminated successfully.'
nfev:22
nit:5
njev:5
status:
success: True
x: array([0.33333332,0.66666668])
result 是一个字典:
'fun' 对应的是目标函数在最优解下的值,非常小接近于零证明找到了最优值。
'nfev' 对应的 22 表示目标函数被运行了 22 次
如果只关注最优权重,只需看 ‘x’
result.x
array([0.33333332,0.66666668])
股票和债券的最优权重为 w* =[33.33%, 66.66%]
三个资产
接着分析股票、债券和信贷三个资产组合:
股票的预期超额回报为10%,波动率为20%
债券的预期超额回报为5%,波动率为10%
信贷的预期超额回报为10%,波动率为15%
股票与债券、股票与信贷、债券与信贷的相关系数为-10%, 30%, -30%
mu=np.array([0.1,0.05,0.1])sigma=np.array([0.2,0.1,0.15])rho=np.array([[1,-0.1,0.3],[-0.1,1,-0.3],[0.3,-0.3,1]])
运行 risk_partiy 函数
result=risk_parity(sigma,rho)result.x
array([0.19117648,0.5147059,0.29411762])
股票、债券和信贷的最优权重为 w* = [19.12%, 51.47%, 29.41%]
4总结
本帖只讨论用 SciPy 可以实现的三个应用
用 scipy.interpolate 来插值(interpolation)
用 scipy.integrate 来积分(integration)
用 scipy.optimize 来优化(optimization)
学完此贴后,至少你可以解决以下具体金融问题 (training set):
在折现曲线上插出折现因子和零息利率
数值积分求解期权价值
优化出风险平价模型的权重
举一反三一下,你还可以解决新的金融问题 (test set):
在波动平面上插出波动率
数值积分求解而二维金融衍生品价值
优化出各种资产配置模型的权重 (加各种约束)
声明:本站所有文章资源内容,如无特殊说明或标注,均为采集网络资源。如若本站内容侵犯了原著者的合法权益,可联系本站删除。