本文记录在可投资集已知预期收益率和预期协方差矩阵的条件下,使用程序工具求解最优投资组合的方法。
问题描述
构建最优投资组合是一个带约束的优化问题,优化目标是最大化投资组合的夏普比率,而约束是资产权重相加等于1。
Python实现
# construct optimized portfolio
import numpy as np
import pandas as pd
import scipy.optimize as sco
fcst_style_sharpe = pd.read_csv('fcst_style_sharpe.csv')
style_cov = pd.read_csv('style_cov.csv')
selected_col = ['mkt','smb','hml','vmg','rmw','gms','pmo','mom']
noa = len(selected_col)
style_ret = fcst_style_sharpe['fcst_ret'] #* 100
weights = np.random.random(noa)
weights /= np.sum(weights)
def port_ret(weights):
return np.sum(style_ret * weights) * 252
def port_vol(weights):
return np.sqrt(np.dot(weights.T, np.dot(style_cov * 252, weights)))
def min_func_sharpe(weights):
return -port_ret(weights) / port_vol(weights)
# maximize sharpe ratio
eweights = np.array(noa * [1. / noa,])
#bnds = tuple((0, 1) for x in range(noa))
bnds = tuple((-2, 2) for x in range(noa))
cons = ({'type': 'eq', 'fun': lambda x: np.sum(x) - 1})
opts = sco.minimize(min_func_sharpe, eweights,
method='SLSQP', bounds=bnds,
constraints=cons)
opt_weight = opts['x']
opt_weight.round(4)
opts_sharpe = port_ret(opt_weight) / port_vol(opt_weight)
opts_sharpe
# plot the optimized portfolio
from lets_plot import *
LetsPlot.setup_html()
fzoo = pd.read_csv('data/fzoo.csv')
opt_style = fzoo[['trading_date','mkt']].copy()
opt_style.columns = ['trading_date','style']
opt_style['style'] = np.dot(fzoo[selected_col],opt_weight)
opt_style['style_cum'] = np.log1p(opt_style['style']/100).cumsum()
x = pd.to_datetime(opt_style['trading_date'])[-1000:]
y = opt_style['style_cum'][-1000:]/opt_style['style_cum'].iloc[-1000]
ggplot({'date': x, 'port': y}, aes(x='date', y='port')) + geom_line()
# minimize volatility
optv = sco.minimize(port_vol, eweights,
method='SLSQP', bounds=bnds,
constraints=cons)
bnds = tuple((0, 1) for x in weights)
cons = ({'type': 'eq', 'fun': lambda x: port_ret(x) - tret},
{'type': 'eq', 'fun': lambda x: np.sum(x) - 1})
%time
trets = np.linspace(0.05, 0.5, 100)
tvols = []
for tret in trets:
res = sco.minimize(port_vol, eweights, method='SLSQP',
bounds=bnds, constraints=cons)
tvols.append(res['fun'])
tvols = np.array(tvols)
# plot the front
from pylab import mpl, plt
plt.style.use('seaborn-v0_8-whitegrid')
mpl.rcParams['font.family'] = 'serif'
%matplotlib inline
prets = []
pvols = []
for p in range (5000):
weights = np.random.random(noa)
weights /= np.sum(weights)
prets.append(port_ret(weights))
pvols.append(port_vol(weights))
prets = np.array(prets)
pvols = np.array(pvols)
plt.figure(figsize=(10, 6))
plt.scatter(pvols, prets, c=prets / pvols,
marker='o', cmap='coolwarm')
plt.xlabel('expected volatility')
plt.ylabel('expected return')
plt.colorbar(label='Sharpe ratio');
tvols = np.array(tvols)
plt.figure(figsize=(10, 6))
plt.scatter(pvols, prets, c=prets / pvols,
marker='.', alpha=0.8, cmap='coolwarm')
plt.plot(tvols, trets, 'b', lw=4.0)
plt.plot(port_vol(opts['x']), port_ret(opts['x']),
'y*', markersize=15.0)
plt.plot(port_vol(optv['x']), port_ret(optv['x']),
'r*', markersize=15.0)
plt.xlabel('expected volatility')
plt.ylabel('expected return')
plt.colorbar(label='Sharpe ratio')
本文由 mdnice 多平台发布