第10课 Python金融学基础——资产优化问题

在上一节课当中主要讲了几个重要的概念,包括股票的日回报率,日回报率的均值,累积回报率,波动(也就是标准差),进而以日回报率的均值和波动来计算夏普比率,并说明夏普比率可以用来衡量每份风险背后所具有的收益,以此来定量衡量我们投资决策的真实收益大小。

在这节课,我们要解决第二个问题,给定一定的股票,比如说我给你四只股票的一系列的时间序列数据,以及给你限定10000元的投资,那么如何根据所给的股票数据来划分每只股票各投资多少占比以求得最大收益,也就是我要计算出股票购买所占资本的权重,这个权重可以实现总体的夏普比率最大化。
总体大纲:


流程

一、加载数据及预处理

1.1 计算累积收益率,对数收益率和算术收益率

import pandas as pd
import matplotlib.pyplot as plt
import numpy as np
AAPL=pd.read_csv('AAPL_CLOSE',index_col='Date',parse_dates=True)
AMZN=pd.read_csv('AMZN_CLOSE',index_col='Date',parse_dates=True)
CISCO=pd.read_csv('CISCO_CLOSE',index_col='Date',parse_dates=True)
IBM=pd.read_csv('IBM_CLOSE',index_col='Date',parse_dates=True)
AAPL.head()
输出结果

接下来我们把这四只股票的收盘价都合在一张dataframe里面

stock=pd.concat([AAPL, AMZN, CISCO, IBM],axis=1)
stock.columns=['AAPL', 'AMZN', 'CISCO', 'IBM']
stock.head()
输出结果

然后我们计算一下日收益率的平均值

stock.pct_change(1).mean()
输出结果

我们接下来看一下日收益率的偏相关性所引起的偏方差。关于这里的偏相关性我是这么理解的,因为这是股票市场,并且这几只股票都是科技股票,它们的市场是趋同的,因此在这里应该是认为彼此的价格变化是有相互影响的,因此在计算波动的时候,要考虑权重转变后其他股票对单只股票的影响。偏方差函数是cov()

stock.pct_change(1).cov()

在这里以第一行为例子,AAPL是苹果股票的代号,它除了有自身波动的影响,即第一列。也有其他三只股票的影响,也就是其他三列,这部分的相关性导致的波动即为偏方差。

我们绘制一下股票的累积收益率的变化情况。

normed_ret=stock/stock.iloc[0]
normed_ret.plot(kind='line',figsize=(12,8),grid=True)
plt.legend(loc='best')

我们在这里引入对数收益率,原因是对数收益率相对于我们上面提到的算术收益率来说更加适合模型的计算,具体原因我觉得知乎这个回答是不错的,一方面是为了让我们的数据符合模型的平稳性假设,另外一方面应该是方便计算表达,而且可以发现对数收益率和算术收益率其实差的不多。关于对数收益率我还有些不理解的地方,还得继续研究下。我们绘制下对数收益率和算术收益率的分布吧。

#算术收益率
stock.pct_change(1).plot.hist(bins=100)
plt.legend(loc='best')
#对数收益率
log_ret=np.log(stock/stock.shift(1))
log_ret.plot.hist(bins=100)
plt.legend(loc='best')
算术收益率的分布
对数收益率的分布

可以看到差别并不是很大。

1.2 计算252天的平均收益率的总和以及偏方差矩阵

#252天的四只股票的平均对数收益率总和
total_mean_ret=log_ret.mean()*252
#252天的四只股票构成的偏方差矩阵
total_covariance=log_ret.cov()*252
print total_mean_ret
print total_covariance

在这里计算资产组合的最优化权重,我们可以用到两个方法:一个是蒙特卡罗模拟法,另外一个是限制条件下的数学优化方法。我们分别来看一下。

二、蒙特卡罗模拟法

2.1 原理

这个方法听起来很高大上,其实方法很接地气,也很暴力。就是举出很多很多权重组合(多到几乎穷举),然后计算所有的点的夏普比率,哪个最高就取哪个点的均值和波动。

2.2 方法实现

2.2.1 计算单个权重的夏普比率

在这里我们先用代码实现一组投资资产权重的计算夏普比率,穷举只用在单次的外围加上一个遍历就可以了。


numpy的random函数用法
#在这里我们用numpy的random.randn函数实现随机取四个数,这个方法是从[0, 1]中取出特定个数的数字的方法
weight=np.random.random(4)
print weight

在这里我们看到的确是返回了四个随机的[0, 1]的值,但是要注意我们的权重之和是需要等于1的,所以要进行归一化。

normed_weight=weight/np.sum(weight)
print normed_weight

可以看到归一化以后的权重确实是之和为1了。然后我们试下用这个权重去计算对应的资产组合的夏普比率。
按照权重去计算对应的252天的预期回报率和波动(也就是方差)

#预期回报率
exp_ret=np.sum(log_ret.mean()*normed_weight*252)
#波动
volat=np.sqrt(np.dot(normed_weight.T, np.dot(log_ret.cov()*252, normed_weight)))
print exp_ret, volat
#计算夏普比率
SR=exp_ret/volat
print SR
夏普比率

在这里关于波动的计算方式,我是这么理解的后面的一个点乘实际上是计算出每个股票单独的方差,然后每只股票的方差又按照权重加和得到最终整体的方差。原因是每只股票波动是跟其他三只股票都是有联系的,所以要按照权重先算出每只股票自身的波动。第一步得到的是41的矩阵,然后前面的weights在转置以后是14的矩阵,两者的点乘就是最终经过权重转化的方差,开方后为标准差。

2.2.3 生成多个随机权重进行模拟

接下来我们就在外面套上一层循环,生成许多组不同的权重序列,按照上面的方式计算每组权重所得的期望回报和波动。

#随机权重的组数为15000
num=15000
#初始化总预期收益率和波动以及夏普比率的numpy数列
exp_rets=np.zeros(num)
exp_vols=np.zeros(num)
exp_SRs=np.zeros(num)
#迭代15000次
for ind in range(num):
    weight=np.random.random(4)
    normed_weight=weight/np.sum(weight)
    #预期回报率
    exp_ret=np.sum(log_ret.mean()*normed_weight*252)
    exp_rets[ind]=exp_ret
    #波动
    volat=np.sqrt(np.dot(normed_weight.T, np.dot(log_ret.cov()*252, normed_weight)))
    exp_vols[ind]=volat
    #计算夏普比率
    SR=exp_ret/volat
    exp_SRs[ind]=SR
#我们找出Sharp Ratio对应最大的Volatility和return那个点,可以用numpy的argmax函数
max_ind=np.argmax(exp_SRs)
max_SR_vol=exp_vols[max_ind]
max_SR_ret=exp_rets[max_ind]
print 'Sharp Ratio maximum is ', exp_SRs[max_ind]
print '-'*20
print 'Volatility is ', exp_vols[max_ind]
print '-'*20
print 'Return is ', exp_rets[max_ind]

#我们以波动为横坐标,总收益率为纵坐标,夏普比率为颜色条,绘制出散点图的分布,并且标识出最大夏普比率的那个点
plt.scatter(x=exp_vols, y=exp_rets, c=exp_SRs, alpha=0.5, cmap='coolwarm')
plt.title('Volatility versus Return')
plt.xlabel('Volatility')
plt.ylabel('Return')
plt.colorbar(label='Sharp Ratio')
plt.scatter(max_SR_vol, max_SR_ret, s=50, edgecolors='black')
三个值的输出结果
可视化模拟结果

在这里我们标识出了那个夏普比率最大的那个点。

三、数学优化方法

上面是通过随机模拟的方式来找出最优的投资配置,但实际上我们也可以通过边界条件下的优化来找出最优的权重。这里要用到scipy.optimize.minimize方法。


scipy.optimize.minimize方法

这里我们可以看到这个函数要做的事情就是给定边界条件


然后计算这个一个或多个变量所决定的标量目标函数f(x)的最小值。


从官方文档可以看到有几个重要参数,我们在这里只传入几个参数:fun最小化的目标函数,x0是一开始的初始值,method是用于优化的方法,这里选用‘SLSQP’,constraints是约束条件,整体是一个字典,type有两种:‘eq’和'ineq',分别表示后面传入的约束条件函数'fun'等于0或者大于等于0。
在这里要注意两点,一个是我们是要最大化夏普比率的,所以为了用这个minimize方法,我们应该定义目标函数为夏普比率的负值;第二个是约束条件函数在这里应该是权重之和也就是自变量之和为1,要稍微调整成权重之和减1等于0的约束条件形式才满足minimize方法的要求。

#以权重作为输入,计算所得252天的总收益率,总的波动,夏普比率
def get_ret_vol_SR(weight):
    #预期回报率
    exp_ret=np.sum(log_ret.mean()*weight*252)
    #波动
    volat=np.sqrt(np.dot(weight.T, np.dot(log_ret.cov()*252, weight)))
    #计算夏普比率
    SR=exp_ret/volat
    return np.array([exp_ret,  volat, SR])
#定义目标函数,返回夏普比率的负值
def get_neg_sharp_ratio(weight):
    return get_ret_vol_SR(weight)[2]*-1
#定义限制的边界函数,返回权重之和减1的计算结果,之后会用来跟0作比较,判断是否相等
def con(weight):
    return np.sum(weight)-1
#初始化四个值为0.25
x0=np.array([0.25,0.25,0.25,0.25])
#设置自变量的取值范围
bounds=((0,1),(0,1),(0,1),(0,1))
#设置限制条件的字典
constraints={'type':'eq','fun':con }
#进行边界条件下对目标函数优化的权重计算
from scipy.optimize import minimize
res=minimize(fun=get_neg_sharp_ratio, x0=x0, constraints=constraints, method='SLSQP',bounds=bounds)
print res.x#打印出最优化夏普比率后的权重

然后我们接下来根据计算得到的权重,来带入到我们计算三个值的函数当中求出总的收益率,总的波动,以及夏普比率。

print 'Epxcted return is', get_ret_vol_SR(res.x)[0]
print '-'*20
print 'Epxcted volatility is', get_ret_vol_SR(res.x)[1]
print '-'*20
print 'Epxcted return is', get_ret_vol_SR(res.x)[2]

四、有效边界

有效边界可以这么认为,是一条在给定波动也就是风险下,所能达到的最大收益的那个点,这种类似的点的组合即为有效边界。我们绘制一下这条有效边界。


#numpy的linspace方法可以生成一系列梯度渐进的值
lin_y=np.linspace(0,0.3,100)
#然后我们在这里定义的最小化的目标函数是波动,也就是在确定的return也就是y值以及权重之和为1的限制条件下,最小化波动值。
def get_vol(weight):
    return get_ret_vol_SR(weight)[1]
frontier_volatility=[]
#初始化四个值为0.25
x0=np.array([0.25,0.25,0.25,0.25])
#设置自变量的取值范围
bounds=((0,1),(0,1),(0,1),(0,1))
for y in lin_y:
    constraints2=[{'type':'eq','fun':lambda x:get_ret_vol_SR(x)[1]-y},{'type':'eq','fun':con}]
    res2=minimize(fun=get_vol, x0=x0, constraints=constraints2, method='SLSQP',bounds=bounds)
    frontier_volatility.append(res2.fun)
print frontier_volatility[:10]

输出结果


我们绘制一下边界曲线

#我们以波动为横坐标,总收益率为纵坐标,夏普比率为颜色条,绘制出散点图的分布,并且标识出最大夏普比率的那个点
plt.scatter(x=exp_vols, y=exp_rets, c=exp_SRs, alpha=0.5, cmap='coolwarm')
plt.title('Volatility versus Return')
plt.xlabel('Volatility')
plt.ylabel('Return')
plt.colorbar(label='Sharp Ratio')
plt.scatter(max_SR_vol, max_SR_ret, s=50, edgecolors='black')
plt.plot(frontier_volatility, lin_y, 'g--', linewidth=1.5)
有效边界

在这里如果从纵向上看,相同的波动情况下,有效边界上面的点能够达到最大的收益,相同的收益下,有效边界上面的点能够达到风险最小,因此有效边界上面的点都是投资配置最优化的点。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 215,012评论 6 497
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,628评论 3 389
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 160,653评论 0 350
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,485评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,574评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,590评论 1 293
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,596评论 3 414
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,340评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,794评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,102评论 2 330
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,276评论 1 344
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,940评论 5 339
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,583评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,201评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,441评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,173评论 2 366
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,136评论 2 352

推荐阅读更多精彩内容

  • 最近研究了下最优风险资产组合这个题目。本小白在金融领域是个纯粹的初学者,开始的时候,有点不知所措。 后来在网上找了...
    丁立在简书阅读 34,029评论 16 109
  • “绩效度量”是基于一本量化交易书: “Successful Algorithmic Trading”第十二章的翻译...
    北冥Master阅读 1,819评论 0 1
  • 俗话说不要将所有的鸡蛋放在同一个篮子里,在投资股票的时候我们也会多买几只以抵抗风险。本文将带领着你使用Python...
    鱼心DrFish阅读 36,740评论 11 56
  • 一键克隆代码 在多因子量化投资体系中,具有稳定的预期收益,可解释的经济驱动理论,与其他因子的低相关性是选择alph...
    Ricequant米筐阅读 3,078评论 0 8
  • “你还不如三岁孩子”这句话总被用以批评别人犯一些低级的错误或做一些特别不着调的事。稚嫩小孩,真有那么厉害吗?...
    萌耕阅读 1,557评论 12 23