1.背景

大量研究表明,A股行业有明显的轮动现象,并且与A股相反,行业指数通常呈现动量特征,即前期涨幅高的行业,会延续上涨的趋势,比前期涨幅低的行业有明显超额收益。

此外,也有大量研究表明,A股市场存在明显的低波动异象,即前期波动率更低的行业,相比于波动率高的行业,未来有明显超额收益。因为投资者不愿意承担过高的风险。这一现象在行业上也是显著存在的。本文参考报告[1]中对波动率因子的定义,对行业上的波动因子进行测试。

最后,考虑到动量是收益率的一阶矩,波动率是收益率的二阶矩,自然而然的想到,是否收益率的三阶矩、四阶矩是否也能对行业的轮动现象做出解释。即用偏度、峰度作为因子做行业轮动。这两个因子也有一些文献做过研究。偏度反映的是数据整体相较于均值的偏离的程度,正偏度越高,表明整体高于均值的程度更高,数据右拖尾。负偏度越高,表明数据整体低于均值的程度更高,数据左拖尾,总体来说,偏度的绝对值越大,表明数据出现极端值的情况越多。 峰度则反映数据整体的集中程度,集中程度越高, 峰度越高。

综上,本文通过因子的动量、波动率、偏度、峰度进行行业轮动测试。2. 因子定义动量因子:行业过去20天收益率累计

波动率因子:参考报告[1]的VOL定义,具体如下

偏度因子:用上文高低价计算的rHL计算偏度作为偏度因子

峰度因子:用今开昨收计算的收益率计算峰度作为峰度因子

具体行业轮动策略如下回测区间:2006.01-2019.06

频率:月度

标的:中信一级行业指数

评价方法:因子IC,ICIR,分层测试曲线,FamaMacBeth回归,因子合成3. 回测结果

因子累计IC曲线如下

其中,mom为动量因子,vol为波动率因子,sk为偏度因子,kur为峰度因子。IC均值、年化ICIR如下

可以看出,月度上偏度因子、波动率因子比较有效,稳定性较高,动量因子ic较高,但稳定性较差,峰度因子一般。

四个因子的分层测试曲线如下

可以看出,波动率因子、偏度因子的分层特性非常好,Top组明显优于其他组。

各因子的Spearman相关性矩阵如下

偏度和峰度的相关性较高,偏度和波动率的相关性较高。其他各因子之间的相关性都很低。

接下来用FamaMacBeth回归来看在动量因子和波动率因子的基础上,偏度、峰度因子是否能提供额外的信息。FamaMacBeth回归的原理和代码实现参考上一篇《Fama-Macbeth回归和Newey-West调整》。

首先用动量和波动率因子做回归,结果如下,需要注意的位置已标红

再加入偏度因子回归

再用动量、波动率、峰度回归

最后用四个因子一起回归

结果如果做一个表格的话,会看的更清楚一些,这里我就直接用图解释了:首先动量因子、波动率因子都是5%显著的,只加入偏度的话,两个因子都在10%上显著,偏度在5%上显著,波动率的显著性降低较多,说明偏度包含的信息与波动率重复较多,但也有额外信息。此外模型的R2也有明显提升,说明加入偏度是有提升的,因子的方向也与前面IC方向一致。

只加入峰度的话,两因子在10%上显著,峰度不显著,并且模型R2基本没有变,说明峰度没有额外信息。

如果四个因子都包括的话,峰度不显著,波动率不显著,R2显著提升,这都可以用以上两点的结论来解释。

综上,偏度因子比较有效,峰度因子有效性不高,最后用动量、波动率、偏度因子做等权合成f1,f1的IC、ICIR、分层曲线如下

合成之后,因子稳定性显著提升,分层曲线如下

最后需要说明,量价类的因子在周度上比月度更为有效,周度上峰度、偏度都是有效的,并且相关性不高。限于篇幅,这里不给出周度的结果,有兴趣自己测试一下。4.代码import os

import pandasas pd

import numpyas np

import datetime

from linearmodelsimport FamaMacBeth

import statsmodels.apias sm

def getRet(price,freq ='d',if_shift = True):

price = price.copy

if freq =='w':

price['weeks'] = price['tradedate'].apply(lambda x:x.isocalendar[0]*100 + x.isocalendar[1])

ret = price.groupby(['weeks','classname']).last.reset_index

del ret['weeks']

elif freq =='m':

price['ym'] = price.tradedate.apply(lambda x:x.year*100 + x.month)

'ym','classname'

del ret['ym']

ret = ret[['tradedate','classname','s_dq_close']]

if if_shift:

ret = ret.groupby('classname').apply(lambda x:x.set_index('tradedate').s_dq_close.pct_change(1).shift(-1))

else:

'classname').apply(lambda x:x.set_index('tradedate'1))

ret = ret.T.stack(dropna =False).reset_index

ret = ret.rename(columns = {ret.columns[2]:'ret'})

return ret

def getdate(x):

if type(x) == str:

return pd.Timestamp(x).date

else:

return datetime.date(int(str(x)[:4]),int(str(x)[4:6]),int(str(x)[6:]))

# 隔夜收益率

def ret_after_days(x):

x = x.set_index('tradedate')

r = x.s_dq_open/x.s_dq_close.shift(1) -1

return r

def GroupTestAllFactors(factors,ret,groups):

"""

一次性测试多个因子

"""

fnames = factors.columns

fall = pd.merge(factors,ret,left_on = ['classname','tradedate'],right_on = ['classname','tradedate'])

Groupret =

Groupic =

for fin fnames:# f= fnames[2]

if ((f !='classname')&(f !='tradedate')):

fuse = fall[['classname','tradedate','ret',f]]

fuse['groups'] = fuse[f].groupby(fuse.tradedate).apply(lambda x:np.ceil(x.rank/(len(x)/groups)))

result = fuse.groupby(['tradedate','groups']).apply(lambda x:x.ret.mean)

result = result.unstack.reset_index

result.insert(0,'factor',f)

Groupret.append(result)

groupic = fuse.groupby(['tradedate','groups']).apply(lambda x:x.corr(method ='spearman').values[0,1])

groupic = pd.DataFrame(groupic,columns = [f])

groupic = groupic.reset_index.pivot(index ='tradedate',columns ='groups',values = f)

groupic.insert(0,'factor',f)

Groupic.append(groupic)

# print(f)

Groupret = pd.concat(Groupret,axis =0).reset_index(drop =True)

Groupret = Groupret.fillna(0)

Groupnav = Groupret.iloc[:,2:].groupby(Groupret.factor).apply(lambda x:(1 + x).cumprod)

Groupnav = pd.concat([Groupret[['tradedate','factor']],Groupnav],axis =1)

Groupic = pd.concat(Groupic,axis =0)

return Groupnav,Groupic

def getICSeries(factors,ret,method):

# method = 'spearman';factors = fall.copy;

icall = pd.DataFrame

'tradedate','classname'],right_on = ['tradedate','classname'])

icall = fall.groupby('tradedate').apply(lambda x:x.corr(method = method)['ret']).reset_index

icall = icall.drop(['ret'],axis =1).set_index('tradedate')

return icall

'''

数据读入

'''

datas = pd.read_csv('中信一级行业指数日度行情序列.csv',encoding ='gbk')

#datas = pd.read_csv('申万一级行业指数日度行情序列.csv',encoding = 'gbk')

'''

收益率计算

'''

# 当日和前一日收盘价算的收益率

ret = datas.groupby('classname').apply(lambda x:x.set_index('tradedate'1)).T.stack(dropna =False).reset_index

ret = ret.rename(columns = {ret.columns[2]:'ret'})

ret['tradedate'] = ret.tradedate.apply(getdate)

# 开收盘价算的收益率

ret_oc = datas.groupby('classname').apply(lambda x:x.set_index('tradedate').s_dq_close/x.set_index('tradedate').s_dq_open -1).T.stack(dropna =False).reset_index

ret_oc = ret_oc.rename(columns = {ret_oc.columns[2]:'ret'})

ret_oc['tradedate'] = ret_oc.tradedate.apply(getdate)

# 高低价算的收益率

ret_hl = datas.groupby('classname').apply(lambda x:(x.set_index('tradedate').s_dq_high - x.set_index('tradedate').s_dq_low)/(x.set_index('tradedate').s_dq_high + x.set_index('tradedate').s_dq_low)/2).T.stack(dropna =False).reset_index

ret_hl = ret_hl.rename(columns = {ret_hl.columns[2]:'ret'})

ret_hl['tradedate'] = ret_hl.tradedate.apply(getdate)

# 正常的逐日收益率计算

datas['tradedate'] = datas.tradedate.apply(getdate)

ret_m = getRet(datas,freq ='m',if_shift =True)

'''

因子计算

1. 动量因子:过去一个月/周的涨跌幅

2. 波动率因子:过去一个月/周的日收益率的波动率

收益率1 :开/收 - 1

收益率2:(高 - 低)/(高 + 低)/2

波动率因子 = 波动率因子1和2打分后加总

3. 偏度因子 :收益率的偏度

4. 峰度因子:波动率的峰度

'''

N =20

# 1月动量

mom_m = ret.groupby('classname').apply(lambda x:x.set_index('tradedate').rolling(15).sum).fillna(0)

mom_m = mom_m.reset_index

mom_m = mom_m.rename(columns = {mom_m.columns[2]:'mom'})

# 波动率因子

vol_m_1 = ret_oc.groupby('classname').apply(lambda x:x.set_index('tradedate').rolling(N).std).fillna(0)

vol_m_1 = vol_m_1.reset_index

vol_m_1 = vol_m_1.rename(columns = {vol_m_1.columns[2]:'vol_m_1'})

vol_m_2 = ret_hl.groupby('classname').apply(lambda x:x.set_index('tradedate'0)

vol_m_2 = vol_m_2.reset_index

vol_m_2 = vol_m_2.rename(columns = {vol_m_2.columns[2]:'vol_m_2'})

# 偏度因子

sk = ret_hl.groupby('classname').apply(lambda x:x.set_index('tradedate').rolling(N).skew).fillna(0)

sk = sk.reset_index

sk = sk.rename(columns = {sk.columns[2]:'sk'})

# 峰度因子

kur = ret.groupby('classname').apply(lambda x:x.set_index('tradedate').rolling(N).kurt).fillna(0)

kur = kur.reset_index

kur = kur.rename(columns = {kur.columns[2]:'kur'})

# 峰度因子

"""

因子合并

"""

factors_m = pd.merge(mom_m,vol_m_1,left_on = ['tradedate','classname'],right_on = ['tradedate','classname'])

factors_m = pd.merge(factors_m,vol_m_2,left_on = ['tradedate','classname'],right_on = ['tradedate','classname'])

factors_m['vol'] = factors_m.vol_m_1.groupby(factors_m.tradedate).rank(ascending =True) + factors_m.vol_m_2.groupby(factors_m.tradedate).rank(ascending =False)

factors_m = pd.merge(factors_m,sk,left_on = ['tradedate','classname'],right_on = ['tradedate','classname'])

factors_m = pd.merge(factors_m,kur,left_on = ['tradedate','classname'],right_on = ['tradedate','classname'])

factors_m = factors_m.drop(['vol_m_1','vol_m_2'],axis =1)

# factors_m['r1'] = factors_m.vol_m_1.groupby(factors_m.tradedate).rank(ascending = False)

"""

因子测试

"""

groups =5

startdate = datetime.date(2006,1,1)

enddate = datetime.date(2019,6,30)

fm = factors_m.loc[(factors_m.tradedate>= startdate) &(factors_m.tradedate <= enddate)].reset_index(drop =True).copy

fm['f1'] = fm['vol'] + fm['mom']

fm['f2'] = fm['vol'] + fm['mom'] - fm['sk'] -fm['kur']

# IC

ic_m = getICSeries(fm,ret_m,'spearman')

ic_m[['vol','mom','sk','kur']].cumsum.plot(figsize = (8,4))

ic_m.mean

ic_m.mean/ic_m.std*np.sqrt(12)

fm.iloc[:,2:].corr(method ='spearman')

nav_m,ic_m = GroupTestAllFactors(fm,ret_m,groups)

nav_m.loc[nav_m.factor =='mom'].set_index('tradedate').plot(figsize = (10,5),title ='mom')

'vol'].set_index('tradedate').plot(figsize = (10,5),title ='vol')

'sk'].set_index('tradedate').plot(figsize = (10,5),title ='sk')

'kur'].set_index('tradedate').plot(figsize = (10,5),title ='kur')

'f1'].set_index('tradedate').plot(figsize = (10,5),title ='f1')

'f2'].set_index('tradedate').plot(figsize = (10,5),title ='f2')

# FamaMacbeth

fall = pd.merge(fm,ret_m,left_on = ['tradedate','classname'],right_on = ['tradedate','classname'])

fall = fall.set_index(['classname','tradedate']).fillna(0)

fm = FamaMacBeth(dependent = fall['ret'],exog = sm.add_constant(fall[['vol','mom']]))

fm.fit

'ret''vol','mom','sk']]))

fm.fit

'ret''vol','mom','kur']]))

fm.fit

'ret''vol','mom','sk','kur']]))

fm.fit

[1]20170409-海通证券-海通证券金融工程专题报告:动量策略及收益率高阶矩在行业轮动中的应用-388742关于Python金融量化

python量化选股策略 源码_【一点资讯】Python实现行业轮动量化选股【附完整源码】...相关推荐

  1. Python实现行业轮动量化选股【附完整源码】

    编者荐语: 所谓山不转水转,行业中的动量也存在一定的轮换效应.今天给大家分享一篇来自<量化小白上分记>的研报复现文章,基于因子动量.波动率.偏度.峰度来刻画行业轮动,对于构建行业轮动量化选 ...

  2. python选股源代码_【一点资讯】Python实现行业轮动量化选股【附完整源码】 www.yidianzixun.com...

    1.背景 大量研究表明,A股行业有明显的轮动现象,并且与A股相反,行业指数通常呈现动量特征,即前期涨幅高的行业,会延续上涨的趋势,比前期涨幅低的行业有明显超额收益. 此外,也有大量研究表明,A股市场存 ...

  3. 聊天机器人mysql数据_自己动手开发智能聊天机器人完全指南(附完整源码)

    一.前言 本文是<自己动手开发智能聊天机器人完全指南(附完整源码)>的第二篇,也是21天实战人工智能系列<知识图谱完全项目案例剖析>里面的知识图谱应用的案例.前文中实现了一个最 ...

  4. python:根据头数和脚数,求多少只兔子和多少只鸡(附完整源码)

    python:根据头数和脚数,求多少只兔子和多少只鸡 def solve(num_heads, num_legs):ns = "No solutions!"for i in ran ...

  5. 行业轮动策略(思想+源码)

    一.行业轮动策略简介 行业轮动是利用市场趋势获利的一种主动量化投资交易策略,其本质是利用不同投资品种强势时间的错位对行业品种进行切换以达到投资收益最大化的目的.通俗点讲就是根据不同行业的区间表现差异 ...

  6. 行业轮动策略(附策略源码)

    行业轮动策略简介 什么是行业轮动策略? ​ 行业轮动是利用市场趋势获利的一种主动交易策略,其本质是利用不同投资品种强势时间的错位对行业品种进行切换以达到投资收益最大化的目的.通俗点讲就是根据不同行业的 ...

  7. Python实现恩尼格玛加密算法——附完整源码

    Python实现恩尼格玛加密算法--附完整源码 恩尼格玛是第二次世界大战中德国所使用的复杂电机械式密码机.它被认为是世界上最复杂的加密设备之一.在这个项目中,我们将使用Python模拟实现恩尼格玛加密 ...

  8. Python语言打造智能语音助手——附完整源码

    Python语言打造智能语音助手--附完整源码 随着智能家居.智能办公等领域的逐渐兴起,实现语音控制与交互已成为了一种趋势.而Python语言是一门极具魅力的编程语言,其强大的库.简洁的语法以及易于学 ...

  9. 使用Python获取数字货币价格(附完整源码)

    使用Python获取数字货币价格(附完整源码) 如果你想要及时了解到加密货币的价格变化,你可以使用Python获取数字货币的实时价格.本文将介绍如何使用Python和CoinGecko API获取数字 ...

最新文章

  1. 每秒钟承载600万订单级别的无锁并行计算框架 Disruptor学习
  2. 上海市职业介绍中心长宁分中心武夷路513-517号(021)62400032-229
  3. 4由通道检测_博唐平四通道糖化血红蛋白检测仪通过上海临检中心性能验证(二)...
  4. Python实现常用的数据结构
  5. 【ROS】机器人编程实践
  6. html canvas 简单体验
  7. web简单静态页面设计【HTML+CSS】
  8. html中怎样写渐变色代码,html颜色渐变代码 怎样用css实现网页背景颜色渐变
  9. python extension package_Python Extension Packages for Windows所有包下载
  10. 国际十大炒黄金期货正规平台排名(2023精选榜)
  11. 与、或、非、与非、或非、异或、同或的区别
  12. urllib中urlparse使用技巧以及iter_content图片边下边存到硬盘使用
  13. coursera 吴恩达四卷积神经网络第四周 Art Generation with Neural Style Transfer
  14. 【计导非课系列】 第五节 二进制 进制计算 编码
  15. android调用相机分辨率,Android菜鸟笔记-获取摄像头像素值
  16. 小票打印机打印出空白
  17. 一个跨界程序员:不务正业的这几年,我如何让自己的收入翻了十倍(转)
  18. 管理:身先士卒是必要的手段
  19. vue element-ui动态横向统计表格
  20. studio二级列表查询

热门文章

  1. T51:字符流中第一个未重复的字符(Java)
  2. xposed模块编写教程_Xposed插件开发入门详解
  3. 2022考研复习第八周
  4. 彩虹屁夸人语录 夸人好看的彩虹屁合集
  5. Android 自定义Dialog 里面有EditText控件点击关闭输入法失效的问题
  6. 悲伤时唱首歌:《暗恋桃花源》剧评
  7. 五一国际劳动节知多少!祝五一劳动节快乐! Happy International Workers‘Day!
  8. jquery 录屏_Fundebug录屏插件更新至0.6.0
  9. LOJ 3090 「BJOI2019」勘破神机——斯特林数+递推式求通项+扩域
  10. C6455CSL芯片支持库:第二节 EMAC外设