python实现资产配置(1)----Markowitz 投资组合模型

python实现资产配置(1)----Markowitz 投资组合模型,第1张

现假设有A, B, C, D, E五只股票的收益率数据((第二日收盘价-第一日收盘价)/第一日收盘价)), 如果投资人的目标是达到20%的年收益率,那么该如何进行资产配置,才能使得投资的风险最低?

更一般的问题,假设现有x 1 ,x 2 ,...,x n , n支风险资产,且收益率已知,如果投资人的预期收益为goalRet,那么该如何进行资产配置,才能使得投资的风险最低?

1952年,芝加哥大学的Markowitz提出现代资产组合理论(Modern Portfolio Theory,简称MPT),为现代西方证券投资理论奠定了基础。其基本思想是,证券投资的风险在于证券投资收益的不确定性。如果将收益率视为一个数学上的随机变量的话,证券的期望收益是该随机变量的数学期望(均值),而风险可以用该随机变量的方差来表示。

对于投资组合而言,如何分配各种证券上的投资比例,从而使风险最小而收益最大?

答案是将投资比例设定为变量,通过数学规划,对每一固定收益率求最小方差,对每一个固定的方差求最大收益率,这个多元方程的解可以决定一条曲线,这条曲线上的每一个点都对应着最优投资组合,即在给定风险水平下,收益率最大,这条曲线称作“有效前沿” (Efficient Frontier)。

对投资者而言,不存在比有效前沿更优的投资组合,只需要根据自己的风险偏好在有效前沿上寻找最优策略。

简化后的公式为:

其中p 为投资人的投资目标,即投资人期待的投资组合的期望值. 目标函数说明投资人资产分配的原则是在达成投资目标 p 的前提下,要将资产组合的风险最小化,这个公式就是Markowitz在1952年发表的'Portfolio Selection'一文的精髓,该文奠定了现代投资组合理论的基础,也为Markowitz赢得了1990年的诺贝尔经济学奖. 公式(1)中的决策变量为w i , i = 1,...,N, 整个数学形式是二次规划(Quadratic Programming)问题,在允许卖空的情况下(即w i 可以为负,只有等式约束)时,可以用拉格朗日(Lagrange)方法求解。

有效前缘曲线如下图:

我们考虑如下的二次规划问题

运用拉格朗日方法求解,可以得到

再看公式(1),则将目标函数由 min W T W 调整为 min 1/2(W T W), 两问题等价,写出的求解矩阵为:

工具包: CVXOPT python凸优化包

函数原型: CVXOPT.solvers.qp(P,q,G,h,A,b)

求解时,将对应的P,q,G,h,A,b写出,带入求解函数即可.值得注意的是输入的矩阵必须使用CVXOPT 中的matrix函数转化,输出的结果要使用 print(CVXOPT.solvers.qp(P,q,G,h,A,b)['x']) 函数才能输出。

这里选取五支股票2014-01-01到2015-01-01的收益率数据进行分析.

选取的五支股票分别为: 白云机场, 华夏银行, 浙能电力, 福建高速, 生益科技

先大体了解一下五支股票的收益率情况:

看来,20%的预期收益是达不到了。

接下来,我们来看五支股票的相关系数矩阵:

可以看出,白云机场和福建高速的相关性较高,因为二者同属于交通版块。在资产配置时,不利于降低非系统性风险。

接下来编写一个MeanVariance类,对于传入的收益率数据,可以进行给定预期收益的最佳持仓配比求解以及有效前缘曲线的绘制。

绘制的有效前缘曲线为:

将数据分为训练集和测试集,并将随机模拟的资产配比求得的累计收益与测试集的数据进行对比,得到:

可以看出,在前半段大部分时间用Markowitz模型计算出的收益率要高于随机模拟的组合,然而在后半段却不如随机模拟的数据,可能是训练的数据不够或者没有动态调仓造成的,在后面写策略的时候,我会加入动态调仓的部分。

股票分析部分:

Markowitz 投资组合模型求解

蔡立专:量化投资——以python为工具. 电子工业出版社

0.导入需要的包import pandas as pd

import numpy as np

import statsmodels.api as sm #统计运算

import scipy.stats as scs #科学计算

import matplotlib.pyplot as plt #绘图

1.选取几只感兴趣的股票

000413 东旭光电,000063 中兴通讯,002007 华兰生物,000001 平安银行,000002 万科A

并比较一下数据(2015-01-01至2015-12-31)

In[1]:

stock_set = ['000413.XSHE','000063.XSHE','002007.XSHE','000001.XSHE','000002.XSHE']

noa = len(stock_set)

df = get_price(stock_set, start_date = '2015-01-01', end_date ='2015-12-31', 'daily', ['close'])

data = df['close']

#规范化后时序数据

(data/data.ix[0]*100).plot(figsize = (8,5))

Out[1]:

2.计算不同证券的均值、协方差

每年252个交易日,用每日收益得到年化收益。计算投资资产的协方差是构建资产组合过程的核心部分。运用pandas内置方法生产协方差矩阵。

In [2]:

returns = np.log(data / data.shift(1))

returns.mean()*252

Out[2]:

000413.XSHE0.184516

000063.XSHE0.176790

002007.XSHE0.309077

000001.XSHE -0.102059

000002.XSHE0.547441

In [3]:

returns.cov()*252

Out[3]:

3.给不同资产随机分配初始权重

由于A股不允许建立空头头寸,所有的权重系数均在0-1之间

In [4]:

weights = np.random.random(noa)

weights /= np.sum(weights)

weights

Out[4]:

array([ 0.37505798, 0.21652754, 0.31590981, 0.06087709, 0.03162758])

4.计算预期组合年化收益、组合方差和组合标准差

In [5]:

np.sum(returns.mean()*weights)*252

Out[5]:

0.21622558669017816

In [6]:

np.dot(weights.T, np.dot(returns.cov()*252,weights))

Out[6]:

0.23595133640121463

In [7]:

np.sqrt(np.dot(weights.T, np.dot(returns.cov()* 252,weights)))

Out[7]:

0.4857482232609962

5.用蒙特卡洛模拟产生大量随机组合

进行到此,我们最想知道的是给定的一个股票池(证券组合)如何找到风险和收益平衡的位置。

下面通过一次蒙特卡洛模拟,产生大量随机的权重向量,并记录随机组合的预期收益和方差。

In [8]:

port_returns = []

port_variance = []

for p in range(4000):

weights = np.random.random(noa)

weights /=np.sum(weights)

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

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

port_returns = np.array(port_returns)

port_variance = np.array(port_variance)

#无风险利率设定为4%

risk_free = 0.04

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

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

plt.grid(True)

plt.xlabel('excepted volatility')

plt.ylabel('expected return')

plt.colorbar(label = 'Sharpe ratio')

Out[8]:

6.投资组合优化1——sharpe最大

建立statistics函数来记录重要的投资组合统计数据(收益,方差和夏普比)

通过对约束最优问题的求解,得到最优解。其中约束是权重总和为1。

In [9]:

def statistics(weights):

weights = np.array(weights)

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

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

return np.array([port_returns, port_variance, port_returns/port_variance])

#最优化投资组合的推导是一个约束最优化问题

import scipy.optimize as sco

#最小化夏普指数的负值

def min_sharpe(weights):

return -statistics(weights)[2]

#约束是所有参数(权重)的总和为1。这可以用minimize函数的约定表达如下

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

#我们还将参数值(权重)限制在0和1之间。这些值以多个元组组成的一个元组形式提供给最小化函数

bnds = tuple((0,1) for x in range(noa))

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

opts = sco.minimize(min_sharpe, noa*[1./noa,], method = 'SLSQP', bounds = bnds, constraints = cons)

opts

Out[9]:

status: 0

success: True

njev: 4

nfev: 28

fun: -1.1623048291871221

x: array([ -3.60840218e-16, 2.24626781e-16, 1.63619563e-01,-2.27085639e-16, 8.36380437e-01])

message: 'Optimization terminated successfully.'

jac: array([ 1.81575805e-01, 5.40387481e-01, 8.18073750e-05, 1.03137662e+00, -1.60038471e-05, 0.00000000e+00])

nit: 4

得到的最优组合权重向量为:

In [10]:

opts['x'].round(3)

Out[10]:

array([-0. , 0. , 0.164, -0. , 0.836])

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

In [11]:

#预期收益率、预期波动率、最优夏普指数

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

Out[11]:

array([ 0.508, 0.437, 1.162])

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

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

In [12]:

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

def min_variance(weights):

return statistics(weights)[1]

optv = sco.minimize(min_variance, noa*[1./noa,],method = 'SLSQP', bounds = bnds, constraints = cons)

optv

Out[12]:

status: 0

success: True

njev: 7

nfev: 50

fun: 0.38542969450547221

x: array([ 1.14787640e-01, 3.28089742e-17, 2.09584008e-01, 3.53487044e-01, 3.22141307e-01])

message: 'Optimization terminated successfully.'

jac: array([ 0.3851725 , 0.43591119, 0.3861807 , 0.3849672 , 0.38553924, 0.])

nit: 7

方差最小的最优组合权重向量及组合的统计数据分别为:

In [13]:

optv['x'].round(3)

Out[13]:

array([ 0.115, 0. , 0.21 , 0.353, 0.322])

In [14]:

#得到的预期收益率、波动率和夏普指数

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

Out[14]:

array([ 0.226, 0.385, 0.587])

8.组合的有效前沿

有效前沿有既定的目标收益率下方差最小的投资组合构成。

在最优化时采用两个约束,1.给定目标收益率,2.投资组合权重和为1。

In [15]:

def min_variance(weights):

return statistics(weights)[1]

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

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

target_variance = []

for tar in target_returns:

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

res = sco.minimize(min_variance, noa*[1./noa,],method = 'SLSQP', bounds = bnds, constraints = cons)

target_variance.append(res['fun'])

target_variance = np.array(target_variance)

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

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

红星:sharpe最大的投资组合

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

In [16]:

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')

#红星:标记最高sharpe组合

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

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

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

plt.grid(True)

plt.xlabel('expected volatility')

plt.ylabel('expected return')

plt.colorbar(label = 'Sharpe ratio')


欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/yw/11942375.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2023-05-19
下一篇 2023-05-19

发表评论

登录后才能评论

评论列表(0条)

保存