这幅图是金融工程学里面可以说是最重要的一副图,横轴是波动率,纵轴是收益率。这个散点图中的每一个散点表示了一个投资组合(权重向量不一样),由于其形状类似子弹,所以又被称为子弹图。因为我们追求的组合有两个标准:相同的收益下,波动性最小;相同的波动性下,收益最高,因此越靠近左上角的资产组合其实是越优的,从下文可以看出,那是有效前沿。于是本文最重要的问题出来了,在给定多个资产历史数据的条件下,我们如何确定组合权重?这不得不引出马科维茨优化和有效前沿。
马科维茨优化和有效前沿
如何获取一个既定收益下风险(波动性)最低的投资组合?于是就转化成这样一个最优化问题:
优化问题描述:
目标函数(求风险最小值):
\[w^T C w\]
约束条件(既定收益且权重之和为1):
\[$ \sum_{i}{w_i} = 1 \]
\[R^T w = \mu\]
输出结果(返回一个权重向量):
\[w\]
def optimal_portfolio(returns):
n = len(returns)
returns = np.asmatrix(returns)
N = 100
mus = [10**(5.0 * t/N - 1.0) for t in range(N)]
# 转化为cvxopt matrices
S = opt.matrix(np.cov(returns))
pbar = opt.matrix(np.mean(returns, axis=1))
# 约束条件
G = -opt.matrix(np.eye(n)) # opt默认是求最大值,因此要求最小化问题,还得乘以一个负号
h = opt.matrix(0.0, (n ,1))
A = opt.matrix(1.0, (1, n))
b = opt.matrix(1.0)
# 使用凸优化计算有效前沿
portfolios = [solvers.qp(mu*S, -pbar, G, h, A, b)['x']
## 计算有效前沿的收益率和风险
returns = [blas.dot(pbar, x) for x in portfolios]
risks = [np.sqrt(blas.dot(x, S*x)) for x in portfolios]
m1 = np.polyfit(returns, risks, 2)
x1 = np.sqrt(m1[2] / m1[0])
# 计算最优组合
wt = solvers.qp(opt.matrix(x1 * S), -pbar, G, h, A, b)['x']
return np.asarray(wt), returns, risks
weights, returns, risks = optimal_portfolio(return_vec)
plt.plot(stds, means, 'o')
plt.ylabel('mean')
plt.xlabel('std')
plt.plot(risks, returns, 'y-o')
上图,靠近左上角的黄色线条描绘了有效前沿,我们在确定组合的时候,当然是选这些黄色的点所代表的组合了。从有效前沿可以看出投资的一个定理:风险与收益基本成正比。
通过上面函数,我们可以立马知道最优组合为:
print('最优组合' ,weights)
最优组合 [[ 2.77880107e-09]
[ 3.20322848e-06]
[ 1.54301198e-06]
[ 9.99995251e-01]]
在真实股票市场的回测
上面的例子特别有趣但不是很适用。 接下来,我们将演示如何在BigQuant中创建一个策略。
本实验的目的是验证在一个买入固定5只股票的多头组合,利用马科维茨组合优化确定的投资组合是否比等权重的投资组合表现更好。
首先,使用BigQuant的D.history_data方法加载一些历史数据。
# 获取数据
start_date = '2012-02-01'
end_date = '2017-07-18'
instruments = ['000069.SZA', '002337.SZA','000333.SZA','000338.SZA','000100.SZA']
data = D.history_data(instruments,start_date,end_date,
fields=['close'])
# 整理数据
data = pd.pivot_table(data,values='close',index=['date'],columns=['instrument'])
T.plot(data)
def initialize(context):
context.days = 0
context.ins = instruments
def handle_data(context, data):
context.days += 1
if context.days < 100:
return
# 每60天调仓一次
if context.days % 30 != 0:
return
# 获取数据的时间窗口并计算收益率
prices = data.history(context.symbols(context.ins[0],context.ins[1],context.ins[2],context.ins[3],context.ins[4]), 'price',100, '1d').dropna()
returns = prices.pct_change().dropna()
try:
# 马科维茨组合优化
weights, _, _ = optimal_portfolio(returns.T)
# print(weights)
# 对持仓进行权重调整
for stock, weight in zip(prices.columns, weights):
if data.can_trade(stock):
order_target_percent(stock, weight[0])
except ValueError as e:
pass
使用马科维茨组合优化的投资组合的回测表现:
m=M.trade.v2(
instruments=instruments,
start_date='2014-01-01', # 在5只股票同时上市,并且满足100个交易日后,所以开始日期我们选择14年
end_date=end_date,
initialize=initialize,
handle_data=handle_data,
order_price_field_buy='open',
order_price_field_sell='open',
capital_base=100000,
benchmark='000300.INDX',
)
def handle_data_1(context, data):
context.days += 1
weight = 1/len(context.ins)
if context.days == 1:
for stock in context.ins:
stock = context.symbol(stock)
if data.can_trade(stock):
order_target_percent(stock, weight)
直接等权重配置的投资组合的表现:
从两个对比实验,等权重组合的收益只有使用优化技术的组合的收益的三分之一。可以看出马科维茨投资组合优化理论可以帮助我们获得更好的表现。
m=M.trade.v2(
instruments=instruments,
start_date='2014-01-01',
end_date=end_date,
initialize=initialize,
handle_data=handle_data_1,
order_price_field_buy='open',
order_price_field_sell='open',
capital_base=100000,
benchmark='000300.INDX',
)
来源:BigQuant(https://community.bigquant.com/t/%E9%87%8F%E5%8C%96%E5%AD%A6%E5%A0%82-%E7%AD%96%E7%95%A5%E5%BC%80%E5%8F%91%E4%BD%BF%E7%94%A8cvxopt%E5%8C%85%E5%AE%9E%E7%8E%B0%E9%A9%AC%E7%A7%91%E7%BB%B4%E8%8C%A8%E6%8A%95%E8%B5%84%E7%BB%84%E5%90%88%E4%BC%98%E5%8C%96%E4%BB%A5%E4%B8%80%E4%B8%AA%E8%82%A1%E7%A5%A8%E7%AD%96%E7%95%A5%E4%B8%BA%E4%BE%8B/274)