0%

考虑收益与风险的组合最优

考虑收益与风险的组合最优

蒙特卡洛计算有效前沿

主要代码

来源

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
import pandas as pd
import numpy as np
import statsmodels.api as sm #统计运算
import scipy.stats as scs #科学计算
import matplotlib.pyplot as plt #绘图

#导入数据
stock_set = ['股票3','股票4','股票5','股票7','股票9']
noa = len(stock_set)
data_s = pd.read_excel(r'F:\数据集\中青杯科学数据整理\筛后数据数据.xlsx')

#每日收益得到年化收益
returns = np.log(data / data.shift(1))

#利用蒙特卡洛实现大量随机组合
port_returns = []
port_variance = []

#这里的252指的是一年以252个交易日计算
for p in range(10000):
#随机分配权重
weights = np.random.random(noa) #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

绘制有效前沿

1
2
3
4
5
6
7
8
9
#绘制有效前沿plt.figure(figsize = (8,4))
plt.scatter(port_variance, port_returns, c = 'grey',marker = 'o') #圆圈:蒙特卡洛随机产生的组合分布
plt.scatter(target_variance,target_returns, c = 'red', marker = 'X',s=50)#叉号:有效前沿

plt.plot(statistics(optv['x'])[1], statistics(optv['x'])[0], 'r*', markersize = 15.0)#红星:标记最小方差组合
plt.grid(True)
plt.xlabel('预期标准差')
plt.ylabel('预期组合收益率')
ply.show()

夏普率最大情况

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
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
#得到的最优组合权重向量为:
opts['x'].round(3)

#sharpe最大的组合3个统计数据分别为:
#预期收益率、预期波动率、最优夏普指数
statistics(opts['x']).round(3)

风险最小情况

1
2
3
4
5
6
7
8
9
10
11
def min_variance(weights):
return statistics(weights)[1]

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

optv
#方差最小的最优组合权重向量及组合的统计数据分别为:
optv['x'].round(3)

#得到的预期收益率、波动率和夏普指数
statistics(optv['x']).round(3)

绘制组合有效前沿

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
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)


#画图

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 = '+',s=200)

#红星:标记最高sharpe组合
plt.plot(statistics(opts['x'])[1], statistics(opts['x'])[0], 'r*', markersize = 15.0)
plt.grid(True)
plt.xlabel('预期标准差')
plt.ylabel('预期组合收益率')
plt.colorbar(label = '夏普率')