行业指数的周期以及跑赢指数的分位数是多少?

前言

本篇是量化系列的第一篇文章。《量化十万个为什么》系列旨在讨论一些自己心中的疑问,并且通过尝试解答这些问题来提升自己对于市场的认知水平。
PS:博主水平很辣鸡,请大家轻喷,多多指教!

一、为什么提这个问题?

前面一篇文章讨论了对于所有个股来说,跑赢指数的分位数大概是多少,接下来我们看一看,对于行业指数来说,分位数是多少,并且对于行业轮动的周期一般又是多少。

二、分析

我们从选取申万一级作为行业指数,时间选取2015年至2020年

# 获取申万一级的所有代码
start_date = '20150101'
end_date = '20201231'
index_dic = index_manager.GetTotalIndexDic()
index_df = index_manager.GetTotalIndexInfo(type='DataFrame')
sw_df = index_df[index_df['IndexClassName'] == '申万一级指数']

# 从网上下载数据
data_dic= {}
for index_code in sw_df['IndexStockName']:
    tmp_code = index_code[0:6]
    index_dic[index_code].PrintMyself()
    df = index_manager.Internet.GetSWIndex(tmp_code,start_date=start_date, end_date=end_date,data_type='Day')
    data_dic.update({index_code:df})
    time.sleep(10)

(PS:这里有一个申万一级命名的小bug,申万一级的休闲服务等同于申万一级的餐饮旅游,代码:801210.SW;申万一级的电子等同于申万一级的电子元器件,代码:801080.SW)

同时还要注意下,申万一级在2016年4月18日和2016年4月19日是没有数据的(原因未知),可以使用tick数据按照当时的分类来计算。
在这里插入图片描述

然后来讨论这样子一个问题,假如你开了上帝视角,获得了未来一段时间内所有的一级行业的走势,理论上说,在T+1的规则下,我们应该每天换入涨幅最高的那个行业,但是这个对于现实操作没有任何指导意义(我们研究的目的还是在行业指数和benchmark之间找到某些周期性的规律,或者说找到相对平稳的时间序列)。无论如何,我们先来看一看,之前个股的结论在行业指数上市一个什么效果。

# 从 tushare 上获取 沪深300 数据
benchmark_df = pro.index_daily(ts_code='399300.SZ', start_date=start_date, end_date=end_date)
benchmark_df = benchmark_df.sort_values(by='trade_date').reset_index(drop=True)
benchmark_date_se = pd.to_datetime(benchmark_df['trade_date'].astype(str))
benchmark_date_se.name = 'datetime'
benchmark_df.index = benchmark_date_se

benchmark_price = benchmark_df['close']

test_day_num_lst = [1,2,3,5,10,20,30]
answer_dic = {}
for past_day_num in test_day_num_lst:
    benchmark_price = benchmark_df['close']
    benchmark_return = (benchmark_price - benchmark_price.shift(past_day_num))/benchmark_price.shift(past_day_num)
    total_answer_df = pd.DataFrame(index=benchmark_date_se)
    for stock_name in data_dic.keys():
        stock_df = copy.copy(data_dic[stock_name])
        close_price = stock_df['Close'].astype(float)
        stock_return = (close_price - close_price.shift(past_day_num))/close_price.shift(past_day_num)
        # 把结果合并比较
        tmp_df = pd.DataFrame(columns=['result','stockreturn','bmreturn'],index=benchmark_date_se)
        tmp_df['bmreturn'] = benchmark_return
        tmp_df.loc[stock_return.index,'stockreturn'] = stock_return

        stock_higher_index = tmp_df[tmp_df['stockreturn'] > tmp_df['bmreturn']].index
        stock_lower_index = tmp_df[tmp_df['bmreturn'] > tmp_df['stockreturn']].index
        tmp_df.loc[stock_higher_index,'result'] = 1
        tmp_df.loc[stock_lower_index,'result'] = 0

        total_answer_df[stock_name] = tmp_df['result']
    total_merge_df = copy.copy(total_answer_df).T
    tmp_se = total_merge_df.fillna(0).sum()/(len(total_merge_df) - total_merge_df.isnull().sum())
    answer_dic.update({past_day_num:tmp_se})

结果如下:

持仓周期均值方差
1 日0.4930.253
2 日0.490.254
3 日0.4880.256
5 日0.4850.255
10 日0.4720.251
20 日0.4580.26
30 日0.4440.252

30日概率如下图
在这里插入图片描述
接着我们来考虑策略,假设我们要尽量减少换仓次数,拉长持仓周期,对于跑赢指数这个目标来说,一般而言有 0.4*28 = 11.2个行业指数可以跑赢沪深300,因此我们假设,对于一定周期的持仓来说,只要return在前10名的,即为优秀,我们看一下优秀行业的持续时间有多少。
我们以过去20天的return取横向取rank,先看看效果怎么样:

past_day_num = 20
total_df = pd.DataFrame(index=benchmark_df.index)
for stock_name in data_dic.keys():
    stock_df = copy.copy(data_dic[stock_name])
    close_price = stock_df['Close'].astype(float)
    stock_return = (close_price - close_price.shift(past_day_num))/close_price.shift(past_day_num)
    total_df[stock_name] = stock_return
rank_df = total_df.rank(axis=1,ascending=False)

我们取两个行业来看看,分别是: 纺织服装(801130.SW)和 银行 (801780.SW)
在这里插入图片描述
可以看到,在某些时间段,行业轮动还是非常明显的,例如在2015年前半年,纺织服装就明显地要强于银行。我们把指数画出来看看。
在这里插入图片描述
从指数的走势上看,纺织服装确实在2015年非常地强势,到了2017年开始才慢慢变熊。从直观上判断,这种行业周期要持续好几年,但是这个结论显然不是我们想要的,毕竟我们很少做几年的持仓。
为了找出短时间内行业轮动的周期,我们对前面求得的rank_df做傅里叶变换,求出一个大致的行业周期。我们来看一看,计算机和纺织服装的傅里叶变换
在这里插入图片描述
我们尝试取最高能量的前3名,做加权平均,求周期

from scipy.fftpack import fft,ifft
period_dic = {}
for stock_name in rank_df.columns:
    se = rank_df[stock_name]
    y = (se.dropna()).values
    #归一化处理
    yf1=abs(fft(y))/((len(y)/2)) 
    #由于对称性,只取一半区间
    yf2 = yf1[range(int(len(y)/2))]  
    xf = np.arange(len(y)) 
    xf2 = xf[range(int(len(y)/2))]
    threshold = np.mean(yf2) + 1*np.std(yf2)
    df = pd.DataFrame({'X':xf2[1:],'Y':yf2[1:]})
    tmp_df = df.sort_values(by='Y',ascending=False).iloc[0:3].reset_index()
    ave_x = np.sum(tmp_df['X']*tmp_df['Y'])/tmp_df['Y'].sum()
    T = len(yf2)/ave_x
    period_dic.update({stock_name:T})

结果如下:

行业名称行业代码周期(日)
纺织服装801130.SW146.49
公用事业801160.SW63.52
国防军工801740.SW28.78
综合801230.SW35.16
银行801780.SW45.38
传媒801760.SW56.46
钢铁801040.SW66.43
有色金属801050.SW47.65
机械设备801890.SW61.85
房地产801180.SW34.69
建筑装饰801720.SW106.87
电气设备801730.SW53.4
非银金融801790.SW59.16
轻工制造801140.SW53.63
建筑材料801710.SW72.39
家用电器801110.SW53.54
汽车801880.SW46.61
食品饮料801120.SW49.89
农林牧渔801010.SW70.96
商业贸易801200.SW110.26
化工801030.SW53.01
交通运输801170.SW53.66
通信801770.SW46.93
医药生物801150.SW64.58
采掘801020.SW61.08
计算机801750.SW37.17
休闲服务801210.SW122.88
电子801080.SW61.95

总结

由上面的表格可以看出,可以看到,行业轮动的周期,实际上相差还是蛮大的,最快的和最慢的差了有差不多5倍之多。而且从傅里叶变换的图形来看,频域上的图形并没有特别突出的某几根线。因此这个结论是否有实践意义,还有待后面进一步的验证。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值