本篇内容用于研究组合优化方法对组合收益提升的影响,偏重于优化目标方法设置,标的选择均是权益类资产,用于全A股测试,同时考虑到研究的实际意义,我们设置行业资产配置这样的组合情境(同样也可以做不同板块配置优化),基于申万一级行业数据,采用了较为常见的最小方差、最大夏普、风险平价这样的模型,应用在行业配置中,来探索组合优化模型效果。
组合优化的目的在于给予高收益,低风险的标的更多的权重,来提高组合整体表现。策略里面大部分情况下都会默认平均持仓的方法,由于没有考虑各个标的风险的不同,标的之间的相关性,并未较好的解决鸡蛋在一个篮子里的问题,若是稍加关注一下对组合权重的处理,就会考虑到各标的间的涨跌是否本身就有较强关联这样的问题,这个时候就是需要研究各标的时间序列协方差、波动率等内容的时候。
接下来将28个申万一级行业指数进行研究,给予不同权重配置,首先,获取了行业指数数据,构造收益序列效果如下图所示
可以看到,各行业之间涨跌存在一定的差异
但在极端行情如2015年6月之前的牛市和之后的熊市都有着一致的表现
下面获取了行业间相关矩阵,并以热力图进行展示
热力图颜色又深到浅表示相关度有低到高的过程
从图中可以明显看到银行业与非银金融与其他行业有着较为明显的不同
化工、电器设备、机械设备、纺织服装等行业都有较高的相关度
开始之前,我们先对这些优化方法进行说明
1)平均分仓
就是对28个行业进行等权重资金配置
2)等波动率
等波动率是使得每个股票贡献的表征风险相等,如组合中第i个标的的权重为
满足如下条件:
4)风险平价
风险平价模型是通过衡量组合各标的对于组合的风险贡献度,来制定投资模型的各类资产权重,目标是使得组合标的达到等风险贡献
此优化方法下的行业配置权重
5)最大夏普
夏普率用来表示每承受一单位风险,会产生多少的超额报酬,是对策略的收益与风险进行综合考虑
目标函数为追求最大
即寻找目标
此优化方法下的行业配置权重
以下是不同组合优化的净值表现
我们调用scipy.optimize提供的优化算法,构造以上的目标函数,进行优化求解。
模型模拟了季度调仓,保存调仓时点计算出的权重结构,构造回测,并计算相应风险指标,得出如下结果
以下结论是基于对近5年申万行业指数时间序列不同组合优化方法得出
(1)组合优化处理后普遍优于平均分仓的效果
(2)最小方差组合效果显著,累计收益从39.2%提升至76.9%,年化收益从7.98%提升至12.85%,最大回撤从56.46%降低至40.51%,夏普比率从0.12提升至0.41
(3)最大夏普模型表现最差,且模型持股过于集中
(4)几种优化方法整体提升效果一般,区分度不够明显,如年化波动0.2467略微降低至0.2187,最大回撤仅从56.46%优化到47.87%
组合优化方法是分散化投资的理论工具,分散化投资降低风险,是寻找相关度低的资产作为投资标的,而A股市场标的基本都有着较高的贝塔值,这使得所有行业同样与大盘有着一致的牛熊,因此组合优化效果不尽如人意,基于组合优化方法,在更为宏观的市场领域内,选择相关度低的资产进行配置,这样的场景能更好的发挥组合优化模型的工具优势。
本篇内容用于研究组合优化方法对组合收益提升的影响,偏重于优化目标方法设置,标的选择均是权益类资产,用于全A股测试,同时考虑到研究的实际意义,我们设置行业资产配置这样的组合情境(同样也可以做不同板块配置优化),基于申万一级行业数据,采用了较为常见的最小方差、最大夏普、风险平价这样的模型,应用在行业配置中,来探索组合优化模型效果。 下面是结果展示
组合优化的目的在于给予高收益,低风险的标的更多的权重,来提高组合整体表现。策略里面大部分情况下都会默认平均持仓的方法,由于没有考虑各个标的风险的不同,标的之间的相关性,并未较好的解决鸡蛋在一个篮子里的问题,若是稍加关注一下对组合权重的处理,就会考虑到各标的间的涨跌是否本身就有较强关联这样的问题,这个时候就是需要研究各标的时间序列协方差、波动率等内容的时候。
接下来我们将28个申万一级行业指数进行研究,进行行业的不同权重配置,整个研究内容分布如下
以下结论是基于对近5年申万行业指数时间序列的不同组合优化方法得出
(1)组合优化处理后普遍优于平均分仓的效果
(2)最小方差组合效果显著,累计收益从39.2%提升至76.9%,年化收益从7.98%提升至12.85%,最大回撤从56.46%降低至40.51%,夏普比率从0.12提升至0.41
(3)最大夏普模型表现最差,且模型持股过于集中
(4)几种优化方法整体提升效果一般,区分度不够明显,如年化波动0.2467略微降低至0.2187,最大回撤仅从56.46%优化到47.87%
组合优化方法是分散化投资的理论工具,分散化投资降低风险,是寻找相关度低的资产作为投资标的,而A股市场标的基本都有着较高的贝塔值,这使得所有行业同样与大盘有着一致的牛熊,因此组合优化效果不尽如人意,基于组合优化方法,在更为宏观的市场领域内,选择相关度低的资产进行配置,这样的场景能更好的发挥组合优化模型的工具优势。
#导入需要的库
import pandas as pd
import numpy as np
from jqdata import jy
from jqdata import *
import seaborn as sns
import matplotlib as mpl
from cvxopt import solvers, matrix
import datetime as dt
from scipy.optimize import minimize
我们通过聚宽获取申万行业指数
为了统计近5年的行业配置组合优化效果,且考虑到需要一年的协方差数据,这里获取6年的行业指数准备数据
#获取所需的数据
s_date,e_date = '2012-11-10','2018-12-10'
moneyfund_price = get_price('511880.XSHG',start_date=s_date,end_date=e_date)
#通过聚源数据获取申万行业指数行情数据(输入行业指数代码)
def get_SW_index(SW_index = 801010,start_date = '2017-01-31',end_date = '2018-01-31'):
index_list = ['PrevClosePrice','OpenPrice','HighPrice','LowPrice','ClosePrice','TurnoverVolume','TurnoverValue','TurnoverDeals','ChangePCT','UpdateTime']
jydf = jy.run_query(query(jy.SecuMain).filter(jy.SecuMain.SecuCode==str(SW_index)))
link=jydf[jydf.SecuCode==str(SW_index)]
rows=jydf[jydf.SecuCode==str(SW_index)].index.tolist()
result=link['InnerCode'][rows]
df = jy.run_query(query(jy.QT_SYWGIndexQuote).filter(jy.QT_SYWGIndexQuote.InnerCode==str(result[0]), jy.QT_SYWGIndexQuote.TradingDay>=start_date, jy.QT_SYWGIndexQuote.TradingDay<=end_date
))
df.index = df['TradingDay']
df = df[index_list]
return df
hy_df = get_industries(name='sw_l1')
hy_price_dict = {}
for hy in hy_df.index[:]:
hy_price =get_SW_index(SW_index = hy,start_date = s_date,end_date = e_date)
hy_price_dict[hy] = hy_price['ClosePrice']
all_price_df = pd.concat(hy_price_dict.values(),axis=1)
all_price_df.columns = hy_df.index
#all_price_df['moneyfund'] = moneyfund_price['close']
all_price_df.head(5)
801010 | 801020 | 801030 | 801040 | 801050 | 801080 | 801110 | 801120 | 801130 | 801140 | ... | 801720 | 801730 | 801740 | 801750 | 801760 | 801770 | 801780 | 801790 | 801880 | 801890 | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
TradingDay | |||||||||||||||||||||
2012-11-12 | 1528.590 | 3962.991 | 1622.796 | 1741.893 | 3206.886 | 1176.792 | 1971.636 | 5427.914 | 1462.276 | 1261.643 | ... | 1541.14 | 2463.43 | 669.92 | 1394.66 | 415.09 | 1013.46 | 1904.07 | 954.65 | 2189.17 | 805.96 |
2012-11-13 | 1499.453 | 3884.033 | 1590.792 | 1726.088 | 3142.218 | 1155.724 | 1937.309 | 5348.192 | 1439.519 | 1237.030 | ... | 1516.85 | 2419.45 | 652.06 | 1376.12 | 408.56 | 995.20 | 1878.72 | 931.40 | 2144.75 | 789.38 |
2012-11-14 | 1497.408 | 3905.964 | 1593.432 | 1730.450 | 3190.026 | 1161.023 | 1946.278 | 5403.142 | 1440.246 | 1243.291 | ... | 1519.13 | 2427.09 | 650.20 | 1373.03 | 411.29 | 995.26 | 1881.99 | 937.51 | 2154.44 | 790.61 |
2012-11-15 | 1474.834 | 3825.147 | 1565.150 | 1706.524 | 3116.338 | 1134.229 | 1938.748 | 5330.755 | 1411.987 | 1219.751 | ... | 1498.51 | 2372.26 | 635.73 | 1351.85 | 402.49 | 971.07 | 1864.48 | 925.27 | 2113.20 | 776.73 |
2012-11-16 | 1463.087 | 3770.721 | 1556.309 | 1699.372 | 3082.460 | 1123.276 | 1926.942 | 5245.346 | 1403.346 | 1207.410 | ... | 1495.82 | 2359.95 | 628.49 | 1343.06 | 399.05 | 959.17 | 1855.31 | 917.71 | 2103.91 | 776.12 |
5 rows × 28 columns
绘制各行业指数时间序列走势图
mpl.pyplot.style.use('ggplot')
all_price_df.columns = list(hy_df['name'].values)#+['货币基金']
mpl.rcParams['font.family']='serif'
mpl.rcParams['axes.unicode_minus']=False # 处理负号
prices=all_price_df
(prices/prices.iloc[0]).plot(figsize=(18,12),grid='on')
pct_daily = prices.pct_change()
可以看到,各行业之间涨跌存在一定的差异
但在极端行情如2015年6月之前的牛市和之后的熊市都有着一致的表现
行业间相关性矩阵计算,并以热力图进行展示
#计算各行业指数相关矩阵
fig = plt.figure(figsize= (15,10))
ax = fig.add_subplot(111)
ax = sns.heatmap(pct_daily.corr(),annot=True,annot_kws={'size':9,'weight':'bold'})
热力图颜色又深到浅表示相关度有低到高的过程
从图中可以明显看到银行业与非银金融与其他行业有着较为明显的不同
化工、电器设备、机械设备、纺织服装等行业都有较高的相关度
(1)按季度调仓日期获取
我们设置季度调仓,进行组合风险平衡
#获取日期列表
#按月、季度、半年方式获取交易日列表
def get_tradeday_list(start,end,frequency=None,count=None):
if count != None:
df = get_price('000001.XSHG',end_date=end,count=count)
else:
df = get_price('000001.XSHG',start_date=start,end_date=end)
if frequency == None or frequency =='day':
return df.index
else:
df['year-month'] = [str(i)[0:7] for i in df.index]
if frequency == 'month':
return df.drop_duplicates('year-month').index
elif frequency == 'quarter':
df['month'] = [str(i)[5:7] for i in df.index]
df = df[(df['month']=='01') | (df['month']=='04') | (df['month']=='07') | (df['month']=='10') ]
return df.drop_duplicates('year-month').index
elif frequency =='halfyear':
df['month'] = [str(i)[5:7] for i in df.index]
df = df[(df['month']=='01') | (df['month']=='06')]
return df.drop_duplicates('year-month').index
get_tradeday_list(start=s_date,end=e_date,frequency="quarter",count=None)
DatetimeIndex(['2013-01-04', '2013-04-01', '2013-07-01', '2013-10-08', '2014-01-02', '2014-04-01', '2014-07-01', '2014-10-08', '2015-01-05', '2015-04-01', '2015-07-01', '2015-10-08', '2016-01-04', '2016-04-01', '2016-07-01', '2016-10-10', '2017-01-03', '2017-04-05', '2017-07-03', '2017-10-09', '2018-01-02', '2018-04-02', '2018-07-02', '2018-10-08'], dtype='datetime64[ns]', freq=None)
获取近5年按季度调仓日期
s_date,e_date = '2013-12-10','2018-12-10'
tradedays = get_tradeday_list(start=s_date,end=e_date,frequency="quarter",count=None)
tradedays
DatetimeIndex(['2014-01-02', '2014-04-01', '2014-07-01', '2014-10-08', '2015-01-05', '2015-04-01', '2015-07-01', '2015-10-08', '2016-01-04', '2016-04-01', '2016-07-01', '2016-10-10', '2017-01-03', '2017-04-05', '2017-07-03', '2017-10-09', '2018-01-02', '2018-04-02', '2018-07-02', '2018-10-08'], dtype='datetime64[ns]', freq=None)
(2)优化目标说明
1)平均分仓
平均分仓就是对28个行业进行等权重资金配置
2)等波动率
等波动率是使得每个股票贡献的表征风险相等,如组合中第i个标的的权重为$w_i$,波动率为$w_i\sigma_i$
满足如下条件: $$ w_1\sigma_1 = w_2\sigma_2=...... =w_i\sigma_i$$
即权重应满足如下关系$$1/\sigma_1:1/\sigma_2:......:1/\sigma_i$$
3)最小方差
最小方差,也可以称为最小波动或最低风险
$w$为投资权重向量,$\Sigma$为各标的的收益率协方差矩阵
组合的波动率满足$\sigma = \sqrt{w^T\Sigma w }$,只要找到$w$使得$w^T\Sigma w $最小即可
4)风险平价
风险平价模型是通过衡量组合各标的对于组合的风险贡献度,来制定投资模型的各类资产权重。
风险平价的目标是均衡配置多个资产的风险,使得组合标的达到等风险贡献