用Python寻找最优投资组合

原文地址:https://www.jianshu.com/p/2a25dfd465e9

现代投资组合理论(Modern Portfolio Theory,MPT)告诉我们投资者应该分散投资来实现最小化风险最大化投资回报。大邓刚开始学习这方面知识,用了将近一天的时候才搞懂MPT理论的推导,顺便复习了部分高中数学知识,这样会让我们更加有新信心的去使用自己编写的代码。现在我们从实战开始接触理论。

一、资产组合理论导论

1.1 风险厌恶(Risk aversion)

在投资组合理论中,我们常常使用方差来刻画资产的风险。这里举个例子,方便大家理解。

假设你现在将要进行投资,有两种策略:

资产A给你带来的收益是200元,或者0元。每种情况发生的概率也是各50%

资产B给你带来的收益是400元,或者-200元(亏损)。每种情况发生的概率也是各50%

资产的数学期望收益

两种投资策略的波动性-标准差

如果你是风险偏好者,你可能会选择B,因为B的潜在的最大收益最大。但是MPT理论认为大多数的投资者是风险厌恶者,不喜欢玩心跳,所以更倾向于选择A。

1.2资产组合池(portfolio)

假设我们将采用分散投资,每种资产的比例为 w1、w2、w3...wn,我们知道所有投资比例之和为1,即 w1+w2+w3+...+wn=1 。假设R0、R1、R2…Rn分别代表每种资产的收益,则资产组合投资收益为

我们预期的投资组合收益为

1.3 相关性

在计算资产组合风险前,我们需要先回忆一下高中数学中的方差和相关性的计算方法。方差和相关性主要是刻画任意两个变量之间的线性关系。

X和Y的方差计算公式

1.4 风险

现在我们将要计算资产组合的风险,这里使用 资产组合收益的方差 来刻画投资风险。

本来大邓直接看到推导完的结果,但是忽略了中间过程,心里怎么也不相信。所以花了很多时间用来找推导过程的教程和视频,终于找到一份比较详细的,现在贴给大家看。我们将该公式中的加总用累加zigma符号表示,一步步的进行推导

现在我们先看看简单的例子,如何将累加和的平方展开?

我们再将上面的公式抽象一下

因此,资产组合收益的方差可以表示为

最后我们将上面的符号全部用向量(或矩阵)来表示,这样后期我们使用python写代码时候可以直接使用numpy表达。

二、实战

2.1 加载数据

这里我参考JoinQuant中的一篇文章,其选取的股票的收益比较符合正太分布,我就直接拿来用吧。

'000651',##格力电器   

'600519',##贵州茅台  

'601318',##中国平安               

'000858',##五粮液  

'600887',##伊利股份  

'000333',##美的集团   

'601166',##兴业银行  

'601328',##交通银行  

'600104'##上汽集团  

我们使用tushare来获取股票数据,tushare的get_hist_data(stock_code, start, end)函数获取stock_code从start到end期间内的所有交易数据,返回的是dataframe类型。这里我们都是用close列的数据,即只用收盘价数据。

importtushareasts

deffetch_stock_data(stock_code, stock_name, start, end):

df = ts.get_hist_data(stock_code, start=start, end=end)#前复权

df = df.close

df.name = stock_name

returndf

geli = fetch_stock_data('000651','格力电器','2016-05-28','2018-03-26')

geli.head()

Run

date

2018-03-2648.13

2018-03-2348.88

2018-03-2249.90

2018-03-2151.70

2018-03-2052.20

Name: 格力电器,dtype:float64

现在我们将所有股票都的收盘数据都装进pandas.DataFrame中,每一列代表一只股票,每一行代表一天的收盘价

importpandasaspd

data = pd.DataFrame({'格力电器':fetch_stock_data('000651','格力电器','2016-05-28','2018-03-26'),

'贵州茅台':fetch_stock_data('600519','贵州茅台','2016-05-28','2018-03-26'),

'中国平安':fetch_stock_data('601318','中国平安','2016-05-28','2018-03-26'),

'五粮液':fetch_stock_data('000858','五粮液','2016-05-28','2018-03-26'),

'伊利股份':fetch_stock_data('600887','伊利股份','2016-05-28','2018-03-26'),

'美的集团':fetch_stock_data('000333','美的集团','2016-05-28','2018-03-26'),

'兴业银行':fetch_stock_data('601166','兴业银行','2016-05-28','2018-03-26'),

'交通银行':fetch_stock_data('601328','交通银行','2016-05-28','2018-03-26'),

'上汽集团':fetch_stock_data('600104','上汽集团','2016-05-28','2018-03-26')})

data = data.dropna()

data.head()

data.to_excel('stock_data.xlsx')

我将整理好的数据保存到了 stock_data.xlsx 中,方便大家即使无法使用tushare也有数据可供分析。

importpandasaspd

data = pd.read_excel('stock_data.xlsx')

data.head()

将每个股票价格与最初始(2016年10月24日)的价格作比较,并据此得到之后的股价走势图

#将date列从newdata中踢出

date = data.pop('date')

#data.iloc[0, :] 选取第一行的数据

newdata = (data/data.iloc[0, :])*100

昨天我们分享的可视化使用的pyplotz库,该库支持所有基于matplotlib的可视化库。今天我们用plotly这个动态可视化库来作图,正好复习下前面分享的plotly-express库,注意plotly-express是基于plotly

fromplotly.offlineimportinit_notebook_mode, iplot, plot

importplotly.graph_objsasgo

init_notebook_mode()

stocks = ['格力电器','贵州茅台','中国平安','五粮液','伊利股份','美的集团','兴业银行','交通银行','上汽集团']

deftrace(df, date, stock):

returngo.Scatter(x = date,#横坐标日期

y = df[stock],

name=stock)#纵坐标为股价与(2016年10月24日)的比值

data = [trace(newdata,date,stock)forstockinstocks]

iplot(data)

2.2 计算不同股票的均值、协方差

每年有252个交易日,用每日收益率乘以252得到年化收益率。现在需要计算每只股票的收益率,在金融领域中我们一般使用对数收益率。这里体现了pandas的强大,df.pct_change()直接就能得到股票收益率

importnumpyasnp

log_returns = np.log(newdata.pct_change()+1)

log_returns = log_returns.dropna()

log_returns.mean()*252

Run

格力电器    0.582497

贵州茅台    0.648666

中国平安    0.524031

五粮液     0.561282

伊利股份    0.352049

美的集团    0.560136

兴业银行    0.024539

交通银行    0.068539

上汽集团    0.289760

dtype:float64

2.3 进行正态检验

马科维茨的投资组合理论需要满足收益率符合正态分布,scipy.stats库为我们提供了正态性测试函数

scipy.stats.normaltest 测试样本是否与正态分布不同,返回p值。

importscipy.statsasscs

defnormality_test(array):

print('Norm test p-value %14.3f'% scs.normaltest(array)[1])

forstockinstocks:

print('\nResults for {}'.format(stock))

print('-'*32)

log_data = np.array(log_returns[stock])

normality_test(log_data)

Run

Resultsfor格力电器

--------------------------------

Normtestp-value          0.000

Resultsfor贵州茅台

--------------------------------

Normtestp-value          0.000

Resultsfor中国平安

--------------------------------

Normtestp-value          0.000

Resultsfor五粮液

--------------------------------

Normtestp-value          0.051

Resultsfor伊利股份

--------------------------------

Normtestp-value          0.000

Resultsfor美的集团

--------------------------------

Normtestp-value          0.453

Resultsfor兴业银行

--------------------------------

Normtestp-value          0.000

Resultsfor交通银行

--------------------------------

Normtestp-value          0.000

Resultsfor上汽集团

--------------------------------

Normtestp-value          0.000

从上面的检验中,伊利股份和兴业银行股票收益率不符合正态分布。

2.4 投资组合预期收益率、波动率

我们先随机生成一维投资组合权重向量(长度为9,与股票数量相等),因为中国股市的不允许卖空,所以投资组合权重向量中的数值必须在0到1之间。

weights = np.random.random(9)

weights /= np.sum(weights)

weights

array([0.13403144,0.11703939,0.14125659,0.02530677,0.10496042,

0.16106127,0.05155371,0.10361131,0.16117911])

投资组合预期收益率等于每只股票的权重与其对应股票的年化收益率的乘积。

np.dot(weights, log_returns.mean())*252

0.4035947984701051

投资组合波动率(方差)

np.dot(weights, np.dot(log_returns.cov()*252, weights))

0.035798322938178584

投资组合收益的年化风险(标准差)

np.sqrt(np.dot(weights, np.dot(log_returns.cov()*252, weights)))

0.18920444745877033

2.5 随机生成大量的投资组合权重

生成1000种随机的投资组合,即权重weights的尺寸为(1000*9)。

importmatplotlib.pyplotasplt

%matplotlib inline

port_returns = []

port_variance = []

forpinrange(1000):

weights = np.random.random(9)

weights /=np.sum(weights)

port_returns.append(np.sum(log_returns.mean()*252*weights))

port_variance.append(np.sqrt(np.dot(weights.T, np.dot(log_returns.cov()*252, weights))))

port_returns = np.array(port_returns)

port_variance = np.array(port_variance)

#无风险利率设定为3%

risk_free =0.03

plt.figure(figsize=(8,6))

plt.scatter(port_variance, port_returns, c=(port_returns-risk_free)/port_variance, marker ='o')

plt.grid(True)

plt.xlabel('Expected Volatility')

plt.ylabel('Expected Return')

plt.colorbar(label ='Sharpe Ratio')

2.5.1 投资组合优化1—夏普率最大

建立statss函数来记录重要的投资组合统计数据(收益,方差和夏普比)。scipy.optimize可以提供给我们最小优化算法,而最大化夏普率可以转化为最小化负的夏普率。

importscipy.optimizeassco

defstats(weights):

weights = np.array(weights)

port_returns = np.sum(log_returns.mean()*weights)*252

port_variance = np.sqrt(np.dot(weights.T, np.dot(log_returns.cov()*252,weights)))

returnnp.array([port_returns, port_variance, port_returns/port_variance])

#最小化夏普指数的负值

defmin_sharpe(weights):

return-stats(weights)[2]

#给定初始权重

x0 =9*[1./9]

#权重(某股票持仓比例)限制在0和1之间。

bnds = tuple((0,1)forxinrange(9))

#权重(股票持仓比例)的总和为1。

cons = ({'type':'eq','fun':lambdax: np.sum(x)-1})

#优化函数调用中忽略的唯一输入是起始参数列表(对权重的初始猜测)。我们简单的使用平均分布。

opts = sco.minimize(min_sharpe,

x0,

method ='SLSQP',

bounds = bnds,

constraints = cons)

opts

Run

fun: -2.7277947674404794

jac: array([1.08440101e-01,-4.65214252e-05,3.44902277e-04,7.20474541e-01,

5.15412062e-01,-2.46465206e-05,3.42730999e-01,-1.48534775e-04,

-5.43147326e-04])

message:'Optimization terminated successfully.'

nfev:111

nit:10

njev:10

status:0

success: True

x: array([1.07588996e-16,4.93897426e-01,2.37878143e-01,6.47455750e-17,

5.74725095e-17,1.14655596e-01,8.60056411e-17,6.88721816e-02,

8.46966532e-02])

最优投资组合权重向量,小数点保留3位

opts['x'].round(3)

array([0.,0.494,0.238,0.,0.,0.115,0.,0.069,0.085])

sharpe最大的组合3个统计数据分别为:

stats(opts['x']).round(3)

array([0.539,0.197,2.728])

2.5.2 投资组合优化2——方差最小

接下来,我们通过方差最小来选出最优投资组合。

#但是我们定义一个函数对 方差进行最小化

defmin_variance(weights):

returnstats(weights)[1]

optv = sco.minimize(min_variance,

x0,

method ='SLSQP',

bounds = bnds,

constraints = cons)

optv

Run

fun: 0.1260429626044057

jac: array([0.15127511,0.12627605,0.1532943,0.14743594,0.12581278,

0.1258321,0.12612321,0.1258938,0.12583541])

message:'Optimization terminated successfully.'

nfev:88

nit:8

njev:8

status:0

success: True

x: array([0.00000000e+00,2.13881938e-01,0.00000000e+00,8.07576429e-18,

3.06402571e-02,1.40860179e-02,3.22191709e-01,3.65127109e-01,

5.40729695e-02])

方差最小的最优投资组合权重向量

optv['x'].round(3)

array([0.,0.214,0.,0.,0.031,0.014,0.322,0.365,0.054])

得到的投资组合预期收益率、波动率和夏普指数

stats(optv['x']).round(3)

array([0.206,0.126,1.634])

2.5.3 资产组合的有效边界

有效边界是由一系列既定的目标收益率下方差最小的投资组合点组成的。在最优化时采用两个约束,1.给定目标收益率,2.投资组合权重和为1。

defmin_variance(weights):

returnstatistics(weights)[1]

#在不同目标收益率水平(target_returns)循环时,最小化的一个约束条件会变化。

target_returns = np.linspace(0.0,0.5,50)

target_variance = []

fortarintarget_returns:

#给定限制条件:给定收益率、投资组合权重之和为1

cons = ({'type':'eq','fun':lambdax:stats(x)[0]-tar},{'type':'eq','fun':lambdax:np.sum(x)-1})

res = sco.minimize(min_variance, x0, method ='SLSQP', bounds = bnds, constraints = cons)

target_variance.append(res['fun'])

target_variance = np.array(target_variance)

下面是最优化结果的展示。

叉号:构成的曲线是有效前沿(目标收益率下最优的投资组合)

红星:sharpe最大的投资组合

黄星:方差最小的投资组合

plt.figure(figsize = (8,4))

#圆点:随机生成的投资组合散布的点

plt.scatter(port_variance, port_returns, c = port_returns/port_variance,marker ='o')

#叉号:投资组合有效边界

plt.scatter(target_variance,target_returns, c = target_returns/target_variance, marker ='x')

#红星:标记夏普率最大的组合点

plt.plot(stats(opts['x'])[1], stats(opts['x'])[0],'r*', markersize =15.0)

#黄星:标记方差最小投资组合点

plt.plot(stats(optv['x'])[1], stats(optv['x'])[0],'y*', markersize =15.0)

plt.grid(True)

plt.xlabel('expected volatility')

plt.ylabel('expected return')

plt.colorbar(label ='Sharpe ratio')

从黄色五角星到红色五角星是投资最有效的组合,这一系列的点所组成的边界就叫做 投资有效边界 。这条边界的特点是同样的风险的情况下获得的收益最大,同样的收益水平风险是最小的。从这条边界也印证了风险与收益成正比,要想更高的收益率就请承担更大的风险,但如果落在投资有效边界上,性价比最高。

参考文章

陈小米.正态性检验和蒙特卡洛完成投资组合优化. http://t.cn/EJSdrgG

定立在简书.最优风险资产组合-Python笔记. https://www.jianshu.com/p/0363bc4fdad4

王小川.《Python与量化投资:从基础到实战》

Introduction to Financial Python-Modern Portfolio Theory http://t.cn/EJSeiPo

转载来自:金融量化

推荐阅读

1.海龟交易法则策略,多读几遍少走10年路

2.一个量化策略师的自白(好文强烈推荐)

3.网格交易法,一个不容易亏钱的投资策略(附源码)

4.市面上经典的量化交易策略都在这里了!(源码)

5.期货/股票数据大全查询(历史/实时/Tick/财务等)

6.量化交易领域最重要的10本参考书推荐!

7.配对交易—这个股票策略曾年赚5000万美元

8.被动与主动的完美结合:指数增强策略的魅力

9.学了那么多技术指标为什么还不赚钱?从量化角度告诉你

10.最科学的仓位管理利器-凯利公式,从方法上胜过99%散户

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/480225.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

技术动态 | 事理图谱,下一代知识图谱

本文转载自公众号:DataHorizon。 人工智能与认知智能当前人工智能时代下,机器与人类之间的博弈一直在进行着。如图1所示,从1926年达特茅斯会议的召开标志人工智能诞生到深度学习模型在…

头条Java后台3面(含答案):事务+List集合+慢查询SQL+Redis+秒杀设计

头条Java一面 1.讲讲jvm运行时数据库区 2.讲讲你知道的垃圾回收算法 3.jvm内存模型jmm 4.内存泄漏与内存溢出的区别 5. select、epoll 的区别?底层的数据结构是什么? 6.mysql数据库默认存储引擎,有什么优点 7.优化数据库的方法&#xf…

CNN卷积神经网络(吴恩达《卷积神经网络》笔记一)

CNN 卷积网络说明卷积 Convolution填充 Padding步长 StrideRGB图像卷积池化 Pooling完整CNN为什么人们喜欢用卷积神经网络?实例分析说明 关于CNN的基础知识:卷积、填充、步长、池化、完整的深度CNN网络可以参考 链接: AI学习笔记——卷积神经网络&#…

字符串匹配算法(BM)

文章目录1. BM(Boyer-Moore)算法1.1 坏字符规则1.2 好后缀规则1.3 两种规则如何选择2. BM算法代码实现2.1 坏字符2.2 好后缀2.3 完整代码2.4 调试3. 总结1. BM(Boyer-Moore)算法 思想:有模式串中不存在的字符&#xf…

SQL 数据分析常用语句

1 基础查询 • 2 字符串数字日期时间 • 3 聚合数据查询 • 4 子查询 • 5 联接组合查询 • 6 高级查询 • 7 更新数据 阅读提醒:点击图片放大可看清晰的 1 基础查询 2 字符串数字日期时间 3 聚合数据查询 4 子查询 5 联接组合查询 6 高级查询 7 更新数据 参考资料 …

开源词法分析工具LAC重大升级!打造属于自己的个性化分词器!

星标/置顶小屋,带你解锁最萌最前沿的NLP、搜索与推荐技术文 | 匿名大佬编 | 北大小才女小轶本文将为大家介绍如何使用百度开源的词法分析工具LAC实现个性化分词(附代码),并展示LAC在各数据集上惊艳的性能表现。背景介绍分词是中文…

老刘说NLP:这几年的NLP历程回顾,以及关于NLP(知识图谱等)落地的思考

得语言者,得天下。得语言资源者,分天下。得语言逻辑者,争天下。 前言 只有把自己所思考的,所认识的东西,以文字的方式记录下来,才是对自己成长最好的记录方式。 是的,距离上一次动手写博客或者…

Redis系列教程(一):Redis缓存的设计、性能、应用与数据集群同步

Redis 是完全开源免费的,遵守BSD协议,是一个高性能的key-value数据库。Redis本质上是一个Key-Value类型的内存数据库,很像memcached,整个数据库统统加载在内存当中进行操作,定期通过异步操作把数据库数据flush到硬盘上…

论文浅尝 | 提取计数量词丰富知识库

OpenKG 祝各位读者新年快乐!论文标题:Enriching Knowledge Bases with Counting Quantifiers论文链接:https://link.springer.com/content/pdf/10.1007%2F978-3-030-00671-6_11.pdf发表会议:ISWC 2018论文源码:https:/…

RNN循环神经网络(吴恩达《序列模型》笔记一)

1、为什么选择序列模型 2、数学符号 用1来代表人名,0来代表非人名,句子x便可以用y[1 1 0 1 1 0 0 0 0]来表示 3、循环网络模型 值得一提的是,共享特征还有助于减少神经网络中的参数数量,一定程度上减小了模型的计算复杂度。 …

字符串匹配算法(KMP)

文章目录1. KMP由来2. KMP算法基本原理3. 代码4. Leetcode 28. 实现 strStr()1. KMP由来 上一节说的BM算法是最高效、最常用的字符串匹配算法。最知名的却是KMP,它3位作者(D.E.Knuth,J.H.Morris,V.R.Pratt)&#xff0…

常用的SQL语句

常用的SQL语句 一、基础 1、说明:创建数据库 CREATE DATABASE database-name 2、说明:删除数据库 drop database dbname 3、说明:备份sql server 1 --- 创建 备份数据的 device 2 3   USE master 4 5   EXEC sp_addumpdevice disk, te…

论文浅尝 | 用对抗学习做知识表示(NAACL2018)

论文链接:https://arxiv.org/pdf/1711.04071.pdf代码链接:https:// github.com/cai-lw/KBGAN 本文针对在生成负样本时有大部分负样本可以很好地和正样本区分开,对训练的影响不大,提出了使用生成对抗网络(GANs&#xff…

为什么搜索与推荐场景用AUC评价模型好坏?

星标/置顶小屋,带你解锁最萌最前沿的NLP、搜索与推荐技术文 | 吴海波在互联网的排序业务中,比如搜索、推荐、广告等,AUC(Area under the Curve of ROC)是一个非常常见的评估指标。网上关于AUC的资料很多,知…

Redis系列教程(九):Redis的内存回收原理,及内存过期淘汰策略详解

Redis内存回收机制 Redis的内存回收主要围绕以下两个方面: 1.Redis过期策略:删除过期时间的key值 2.Redis淘汰策略:内存使用到达maxmemory上限时触发内存淘汰数据 Redis的过期策略和内存淘汰策略不是一件事,实际研发中不要弄混淆了,下面会…

字符串匹配算法(Trie树)

文章目录1. Trie树概念2. Trie树操作2.1 存储2.2 查找2.3 插入2.4 删除2.5 打印3. 完整代码4. Trie树与散列表、红黑树的比较4.1 思考题参考文章5. 练习题1. Trie树概念 Trie树,也叫字典树,它是一个树形结构。是一种专门处理字符串匹配的数据结构&#…

论文浅尝 | 基于知识图谱嵌入的 Bootstrapping 实体对齐方法

来源: IJCAI 2018链接: https://www.ijcai.org/proceedings/2018/0611.pdf本文关注基于知识图谱嵌入(后文全部简称为知识嵌入)的实体对齐工作,针对知识嵌入训练数据有限这一情况,作者提出一种 bootstrapping 策略,迭代标注出可能的实体对齐&a…

大规模领域词汇库项目DomainWordsDict:涵盖68个领域、共计916万的词汇库资源开放

项目概述 DomainWordsDict, Chinese words dict that contains more than 68 domains, which can be used as text classification、knowledge enhance task。涵盖68个领域、共计916万词的专业词典知识库,可用于文本分类、知识增强、领域词汇库扩充等自然语言处理应…

递归」与「动态规划

原文地址:https://juejin.im/post/5c2308abf265da615304ce41#heading-8 在学习「数据结构和算法」的过程中,因为人习惯了平铺直叙的思维方式,所以「递归」与「动态规划」这种带循环概念(绕来绕去)的往往是相对比较难以…

当知识图谱遇上推荐系统之DKN模型(论文笔记一)

Deep Knowledge-Aware Network for News Recommendation 类别:依次学习 首先使用知识图谱特征学习得到实体向量和关系向量,然后将这些低维向量引入推荐系统,学习得到用户向量和物品向量。 [论文下载链接]https://arxiv.org/abs/1801.08284…