在有效证券组合可行域的上边缘部分称为有效边界,也称“马科维兹边界”。
有效边界一定是向外凸的, 在它左方的投资组合是不可能的,而位于它右方的投资组合是没有效率的。
因为在有效边界上的投资组合较其右方与之风险相同的投资组合有较高的收益率,较其右方与之收益相同的投资组合有较低的风险。
资本市场线是指表明有效组合的期望收益率和标准差之间的一种简单的线性关系的一条射线。
它是沿着投资组合的有效边界,由风险资产和无风险资产构成的投资组合。
import pandas as pd
import numpy as np
import scipy.optimize as sco
import matplotlib.pyplot as plt
stock_list = ['600050.SH','000528.SZ','000001.SZ','002007.SZ','300033.SZ']
num = len(stock_list)
df = pd.DataFrame()
for stock in stock_list:
df_stock = get_price(stock, '20150101', '20151231', '1d', ['close'], skip_paused = False, fq = 'pre', is_panel = 0)
df[stock] = df_stock['close']
print(df.head(1))
df_returns = df.pct_change().dropna()
print(df_returns.head(2))
df_returns.plot()
df_returns.mean() *250
df_returns.cov() * 250 # 取一年250个交易日
先给每只股票分配随机权重
weights = np.random.random(num)
weights /= np.sum(weights)
weights
value1 = np.dot(df_returns.mean(), weights) * 250
print('组合年化收益率: ' + str(value1.round(4)))
value2 = np.sqrt(np.dot(weights, np.dot(df_returns.cov(), weights)) * 250)
print('组合年化标准差: ' + str(value2.round(4)))
value3 = (value1 - 0.04) / value2
print('组合夏普比: ' + str(value3.round(2)))
def portfolio_stat(weights):
risk_free_rate = 0.04
weights = np.array(weights)
port_return = np.dot(df_returns.mean(), weights) * 250
port_std = np.sqrt(np.dot(weights, np.dot(df_returns.cov(), weights.T)) * 250)
return np.array([port_return, port_std, (port_return - risk_free_rate)/port_std])
# 因为scipy.optimization只有最小化函数,所以夏普比需要变成负值
def min_sharpe(weights):
return -(portfolio_stat(weights)[2])
# 约束条件1:所有权重的总和为1
cons = ({'type':'eq', 'fun':lambda x: np.sum(x)-1})
# 约束条件2: 权重应限制在0和1之间。这些值以多个元组组成的一个元组形式提供给
bnds = tuple((0,1) for x in range(num))
# 优化函数调用中忽略的唯一输入是起始参数列表(对权重的初始猜测)。我们使用等权分布。
opts = sco.minimize(min_sharpe, num*[1./num,], method = 'SLSQP', bounds = bnds, constraints = cons)
opts
print('权重: ')
print(opts['x'].round(4))
print()
print('组合的收益率、标准差和夏普比: ')
print(portfolio_stat(opts['x']))
#但是我们定义一个函数对 方差进行最小化
def min_std(weights):
return portfolio_stat(weights)[1]
optv = sco.minimize(min_std, num*[1./num,],method = 'SLSQP', bounds = bnds, constraints = cons)
optv
print('权重: ')
print(optv['x'].round(4))
print()
print('组合的收益率、标准差和夏普比: ')
print(portfolio_stat(optv['x']))
有效边界为所有可能组合的上半边界,另外可以看出最左点应为(0.43, 0.32)附近
port_returns = []
port_std = []
for p in range(10000):
weights = np.random.random(num)
weights /=np.sum(weights)
port_returns.append(np.dot(df_returns.mean(), weights) * 250)
port_std.append(np.sqrt(np.dot(weights, np.dot(df_returns.cov(), weights.T)) * 250))
port_returns = np.array(port_returns)
port_std = np.array(port_std)
plt.figure(figsize = (8,4))
plt.scatter(port_std, port_returns, c=(port_returns - 0.04)/port_std, marker = 'o')
plt.grid(True)
plt.xlabel('excepted volatility')
plt.ylabel('expected return')
plt.colorbar(label = 'Sharpe ratio')
# 在不同目标收益率水平(target_returns)循环时,最小化的一个约束条件会变化。
frontier_returns = np.linspace(0.32, 1.4, 50)
frontier_std = []
for tar in frontier_returns:
cons = ({'type':'eq','fun':lambda x:portfolio_stat(x)[0]-tar}, {'type':'eq','fun':lambda x:np.sum(x)-1})
res = sco.minimize(min_std, num*[1./num,],method = 'SLSQP', bounds = bnds, constraints = cons)
frontier_std.append(res['fun'])
frontier_std = np.array(frontier_std)
plt.figure(figsize = (12,6))
# 圆圈:蒙特卡洛随机产生的组合分布
plt.scatter(port_std, port_returns, c = (port_returns-0.04)/port_std,marker = 'o')
# 红星:标记最高sharpe组合
plt.plot(portfolio_stat(opts['x'])[1], portfolio_stat(opts['x'])[0], 'r*', markersize = 15.0)
# 蓝星:标记最小方差组合
plt.plot(portfolio_stat(optv['x'])[1], portfolio_stat(optv['x'])[0], 'b*', markersize = 15.0)
# 蓝线:有效边界
plt.plot(frontier_std, frontier_returns, 'b')
# 红线:资本市场线
plt.plot([0, portfolio_stat(opts['x'])[1]], [0.04, portfolio_stat(opts['x'])[0]], 'r')
plt.legend(['Max Sharpe Portfolio', 'Min Variance Portfolio','Efficient Frontier', 'CML'])
plt.grid(True)
plt.xlabel('expected volatility')
plt.ylabel('expected return')
plt.colorbar(label = 'Sharpe ratio')