请 [注册] 或 [登录]  | 返回主站

量化交易吧 /  量化策略 帖子:3363205 新帖:7

组合优化在行业配置中的应用

醒掌天下权发表于:5 月 10 日 16:10回复(1)

本篇内容用于研究组合优化方法对组合收益提升的影响,偏重于优化目标方法设置,标的选择均是权益类资产,用于全A股测试,同时考虑到研究的实际意义,我们设置行业资产配置这样的组合情境(同样也可以做不同板块配置优化),基于申万一级行业数据,采用了较为常见的最小方差、最大夏普、风险平价这样的模型,应用在行业配置中,来探索组合优化模型效果。

组合优化的目的在于给予高收益,低风险的标的更多的权重,来提高组合整体表现。策略里面大部分情况下都会默认平均持仓的方法,由于没有考虑各个标的风险的不同,标的之间的相关性,并未较好的解决鸡蛋在一个篮子里的问题,若是稍加关注一下对组合权重的处理,就会考虑到各标的间的涨跌是否本身就有较强关联这样的问题,这个时候就是需要研究各标的时间序列协方差、波动率等内容的时候。

接下来将28个申万一级行业指数进行研究,给予不同权重配置,首先,获取了行业指数数据,构造收益序列效果如下图所示

d27ac93238c2d1b99ec6d968e66ac782

可以看到,各行业之间涨跌存在一定的差异
但在极端行情如2015年6月之前的牛市和之后的熊市都有着一致的表现
下面获取了行业间相关矩阵,并以热力图进行展示
d27ac93238c2d1b99ec6d968e66ac782

热力图颜色又深到浅表示相关度有低到高的过程
从图中可以明显看到银行业与非银金融与其他行业有着较为明显的不同
化工、电器设备、机械设备、纺织服装等行业都有较高的相关度

开始之前,我们先对这些优化方法进行说明

1)平均分仓
就是对28个行业进行等权重资金配置

2)等波动率
等波动率是使得每个股票贡献的表征风险相等,如组合中第i个标的的权重为wi,波动率为wiσi
满足如下条件:

w1σ1=w2σ2=......=wiσi

即权重应满足如下关系
1/σ11/σ2......:1/σi

此优化方法下的行业配置权重
d27ac93238c2d1b99ec6d968e66ac782
3)最小方差
最小方差,也可以称为最小波动或最低风险
w为投资权重向量,Σ为各标的的收益率协方差矩阵
组合的波动率满足σ=wTΣw,即寻找使得wTΣw最小的w
此优化方法下的行业配置权重
d27ac93238c2d1b99ec6d968e66ac782

4)风险平价
风险平价模型是通过衡量组合各标的对于组合的风险贡献度,来制定投资模型的各类资产权重,目标是使得组合标的达到等风险贡献
风险平价.png
此优化方法下的行业配置权重
d27ac93238c2d1b99ec6d968e66ac782
5)最大夏普
夏普率用来表示每承受一单位风险,会产生多少的超额报酬,是对策略的收益与风险进行综合考虑

SharpeRatio=Rp?Rfσp

Rp,Rp=w1r1w2r2......wiri

Rf=0.04

σpσp=wTΣw

目标函数为追求最大sharperatio

maxwWR?RfwTΣw250

即寻找目标w,使得目标(?1)?SharpeRatio最小
此优化方法下的行业配置权重
d27ac93238c2d1b99ec6d968e66ac782

以下是不同组合优化的净值表现
d27ac93238c2d1b99ec6d968e66ac782

我们调用scipy.optimize提供的优化算法,构造以上的目标函数,进行优化求解。
模型模拟了季度调仓,保存调仓时点计算出的权重结构,构造回测,并计算相应风险指标,得出如下结果

回测收益.png

研究结论

以下结论是基于对近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)
.dataframe thead tr:only-child th { text-align: right; } .dataframe thead th { text-align: left; } .dataframe tbody tr th { vertical-align: top; }
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)风险平价

风险平价模型是通过衡量组合各标的对于组合的风险贡献度,来制定投资模型的各类资产权重。

风险平价的目标是均衡配置多个资产的风险,使得组合标的达到等风险贡献