行业轮动策略
1. 原理
行业轮动现象
在某一段时间内,某一行业或某几个行业组内股票价格共同上涨或下降的现象。
行业轮动策略是根据行业轮动现象做成的策略,利用行业趋势进行获利的方法,属于主动交易策略。其本质是通过一段时期的市场表现,力求抓住表现较好的行业以及投资品种,选择不同时期的强势行业进行获利。
行业轮动的原因
原因1:行业周期
行业的成长周期可以分为初创期、成长期、成熟期和衰退期,一般行业会按照这个周期运行。初创期属于行业刚刚起步阶段,风险高、收益小。成长期内风险高、收益高。处于成熟期的企业风险低、收益高。处于衰退期的企业风险低、收益低。在一段时间内,不同的行业会处于不同的行业周期,在时间维度上看会呈现行业轮动现象。
原因2:国家政策
国家政策对我国资本市场有重大影响。我国每年的财政政策和货币政策都是市场关注的热点,货币政策和财政政策会释放出影响市场的信息,如利率。当政策释放出下调利率的信号,就为资金需求量大、项目周期长的行业缓解了压力,如房地产行业,这时对于这类行业利好,相应的股价就会上涨。
原因3:重大事件
资本市场对于消息的反应是迅速的。根据有效市场理论,在半强式有效市场下,一切已公开的信息都会反映在股价当中。以疫情为例,消息一出迅速拉动医疗行业股价水平,带动行业增长。
行业轮动下资产配置
1. 美林时钟:大类资产配置
根据经济增长和通货膨胀可以将经济分为四个周期:衰退、复苏、过热、滞涨。
美林时钟分析了四个不同时期,并总结出适合投资的资产类别。
周期阶段 | 经济增长 | 通货膨胀 | 最优资产类别 | 最优股票板块 |
---|---|---|---|---|
衰退 | 下降 | 下降 | 债券 | 防御成长 |
复苏 | 上升 | 下降 | 股票 | 周期成长 |
过热 | 上升 | 上升 | 商品 | 周期价值 |
滞涨 | 下降 | 上升 | 现金 | 防御价值 |
研究宏观经济时主要关注两个变量:GDP和CPI。
其中GDP选择不变价(剔除通货膨胀的影响),关心同比值差分后的符号。如果同比值>0,差分后仍然>0,说明GDP在加速上涨;如果同比值<0,差分后仍然<0,说明GDP在加速下跌。
当经济增长速度加快时,与国家经济联系紧密的行业如钢铁、煤炭、电力等基建利润也会随之增长。
当经济增速放缓是,非周期性的行业如医药、基础消费品、基础建设等行业呈现较强的防御性。
当通货膨胀处于较低水平时,市场利率水平也处于较低水平。按照股票估值理论,此时的折现率处于低水平,价格相对而言较高。此时,金融行业的股价会呈现明显的上涨。
当通货膨胀处于较高水平时,市场利率较高,此时现金为王,原材料价格走高。与此相关的原材料行业就会表现较好,如天然气、石油等。
2. 策略设计
行业动量策略
部分研究表明,行业在日、月频率上会存在动量现象,在周频率上会存在反转现象,也就是行业间轮动。因此,在日和月频率上可以利用行业动量设计策略,如果是在周频率上可以利用反转效应设计策略。
(引自:武文超. 中国A股市场的行业轮动现象分析——基于动量和反转交易策略的检验[J]. 金融理论与实践, 2014, 000(009):111-114.)
行业因子策略
将行业变量作为一个因子放入多因子模型中,利用多因子模型预测各个行业的周期收益率,采用滚动预测方法每次得到一个样本外预测值,根据这些预测值判断该买入哪些行业,卖出哪些行业。
(引自:高波, 任若恩. 基于主成分回归模型的行业轮动策略及其业绩评价[J]. 数学的实践与认识, 2016, 46(019):82-92.)
2. 策略思路
策略示例采用第一种策略构建方法,利用行业动量设计策略。为了提高策略速度,以6个行业为例进行演示。
第一步:确定行业指数,获取行业指数收益率。
第二步:根据行业动量获取最佳行业指数。
第三步:在最佳行业中,选择最大市值的5支股票买入。
回测时间:2017-07-01 08:00:00 到 2017-10-01 16:00:00
回测标的:SHSE.000910.SHSE.000909.SHSE.000911.SHSE.000912.SHSE.000913.SHSE.000914
(300工业.300材料.300可选.300消费.300医药.300金融)
回测初始资金:1000万
3. 策略代码
# coding=utf-8
from __future__ import print_function, absolute_import, unicode_literals
import numpy as np
from gm.api import *
'''
本策略每隔1个月定时触发计算SHSE.000910.SHSE.000909.SHSE.000911.SHSE.000912.SHSE.000913.SHSE.000914
(300工业.300材料.300可选.300消费.300医药.300金融)这几个行业指数过去
20个交易日的收益率并选取了收益率最高的指数的成份股获取并获取了他们的市值数据
随后把仓位调整至市值最大的5只股票上
回测数据为:SHSE.000910.SHSE.000909.SHSE.000911.SHSE.000912.SHSE.000913.SHSE.000914和他们的成份股
回测时间为:2017-07-01 08:00:00到2017-10-01 16:00:00
'''
def init(context):
# 每月第一个交易日的09:40 定时执行algo任务(仿真和实盘时不支持该频率)
schedule(schedule_func=algo, date_rule='1m', time_rule='09:40:00')
# 用于筛选的行业指数
context.index = ['SHSE.000910', 'SHSE.000909', 'SHSE.000911', 'SHSE.000912', 'SHSE.000913', 'SHSE.000914']
# 用于统计数据的天数
context.date = 20
# 最大下单资金比例
context.ratio = 0.8
def algo(context):
# 获取当天的日期
today = context.now
# 获取上一个交易日
last_day = get_previous_trading_date(exchange='SHSE', date=today)
return_index = []
# 获取并计算行业指数收益率
for i in context.index:
return_index_his = history_n(symbol=i, frequency='1d', count=context.date, fields='close,bob',
fill_missing='Last', adjust=ADJUST_PREV, end_time=last_day, df=True)
return_index_his = return_index_his['close'].values
return_index.append(return_index_his[-1] / return_index_his[0] - 1)
# 获取指定数内收益率表现最好的行业
sector = context.index[np.argmax(return_index)]
print('最佳行业指数是: ', sector)
# 获取最佳行业指数成份股
symbols = get_history_constituents(index=sector, start_date=last_day, end_date=last_day)[0]['constituents'].keys()
# 获取当天有交易的股票
not_suspended_info = get_history_instruments(symbols=symbols, start_date=today, end_date=today)
not_suspended_symbols = [item['symbol'] for item in not_suspended_info if not item['is_suspended']]
# 获取最佳行业指数成份股的市值,从大到小排序并选取市值最大的5只股票
fin = get_fundamentals(table='trading_derivative_indicator', symbols=not_suspended_symbols, start_date=last_day,
end_date=last_day, limit=5, fields='NEGOTIABLEMV', order_by='-NEGOTIABLEMV', df=True)
fin.index = fin['symbol']
# 计算权重
percent = 1.0 / len(fin.index) * context.ratio
# 获取当前所有仓位
positions = context.account().positions()
# 如标的池有仓位,平不在标的池的仓位
for position in positions:
symbol = position['symbol']
if symbol not in fin.index:
order_target_percent(symbol=symbol, percent=0, order_type=OrderType_Market,
position_side=PositionSide_Long)
print('市价单平不在标的池的', symbol)
# 对标的池进行操作
for symbol in fin.index:
order_target_percent(symbol=symbol, percent=percent, order_type=OrderType_Market,
position_side=PositionSide_Long)
print(symbol, '以市价单调整至仓位', percent)
if __name__ == '__main__':
'''
strategy_id策略ID,由系统生成
filename文件名,请与本文件名保持一致
mode实时模式:MODE_LIVE回测模式:MODE_BACKTEST
token绑定计算机的ID,可在系统设置-密钥管理中生成
backtest_start_time回测开始时间
backtest_end_time回测结束时间
backtest_adjust股票复权方式不复权:ADJUST_NONE前复权:ADJUST_PREV后复权:ADJUST_POST
backtest_initial_cash回测初始资金
backtest_commission_ratio回测佣金比例
backtest_slippage_ratio回测滑点比例
'''
run(strategy_id='strategy_id',
filename='main.py',
mode=MODE_BACKTEST,
token='token_id',
backtest_start_time='2017-07-01 08:00:00',
backtest_end_time='2017-10-01 16:00:00',
backtest_adjust=ADJUST_PREV,
backtest_initial_cash=10000000,
backtest_commission_ratio=0.0001,
backtest_slippage_ratio=0.0001)
4. 回测结果分析与稳健性检验
设定初始资金1000万,手续费率为0.01%,滑点比率为0.01%。回测结果如下图所示。
回测期累计收益率为10.10%,年化收益率为41.43%,沪深300收益率为5.09%,策略收益跑赢指数。最大回撤为6.93%,胜率50%。
为了探究策略的稳健性,改变回测时间,观察策略收益情况。
回测期 | 时间长度 | 年化收益率 | 最大回撤 |
---|---|---|---|
2017.7.1-2017.10.1 | 4个月 | 41.43% | 6.93% |
2018.7.1-2018.10.1 | 4个月 | -23.40% | 15.54% |
2019.7.1-2019.10.1 | 4个月 | 9.98% | 9.58% |
2019.1.1-2019.12.31 | 12个月 | 63.82% | 9.98% |
2018.1.1-2019.12.31 | 24个月 | 7.63% | 36.89% |
2017.1.1-2019.12.31 | 36个月 | 22.11% | 36.94% |
通过观察回测结果发现,该策略在选择的回测期内除了2018年7月1日至2018年10月1日这段期间是负收益以外,在其他回测期内都是正收益。其中2017年7月至2017年10月和2019年1月至2019年12月这两段期间的年化收益率最高,其他时间段收益率相对较低,说明该策略在不同回测期之间收益差距较大。随着时间跨度拉长,最大回撤越来越大,最大可达到36.94%。
注:此策略只用于学习、交流、演示,不构成任何投资建议。