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实现行业轮动量化选股【附完整源码】 www.yidianzixun.com...相关推荐

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

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

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

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

  3. abaqus利用python实现部件合并_python脚本实现abaqus前处理2D多晶粒建模(附完整源码)-Voronoi多边形的生成...

    hello,小伙伴们大家下午好呀.今天是2020-12-03. 今天呢,还是讲python脚本,是一个我以前煞费苦心写的,熬死了不少脑细胞写的,今天呢,我就教了大家,我把源码都给你们,让你们省省事. ...

  4. Python:实现prime sieve eratosthenes埃拉托斯特尼素数筛选法算法(附完整源码)

    Python:实现prime sieve eratosthenes埃拉托斯特尼素数筛选法算法 # flake8: noqa def prime_sieve_eratosthenes(num):prim ...

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

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

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

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

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

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

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

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

  9. Python:30行代码,使用POST登录山大的教务处系统(附完整源码)

    一.抓包分析 首先我们打开浏览器的开发者工具,随便输入一个用户名和密码登录,抓包分析: 显然,我们登录的时候发送了一个POST请求,而Data里面有六个数据,分别是rsa.ul.pl.lt.execu ...

最新文章

  1. 整合Web应用与Axis2
  2. OpenGL编程指南6:顶点数组
  3. 优先级队列应用-称检测点查询
  4. 笨办法学 Linux 8~11
  5. “滚!”央视、腾讯暂停NBA季前赛转播,NBA被中国市场“下架”!
  6. 物联网哪个市场有可能诞生新一轮BAT?
  7. java开发按键精灵_一个使用JAVA编写的类似按键精灵的程序
  8. AWWWB.COM网站克隆器
  9. 找出所有全部成绩合格(分数大于等于60分)的学生姓名,找出总成绩最高的前三名同学姓名,以及他们分别的全科总分数
  10. Python爬取全网文字并词云分析(全程一键化!)
  11. 通信(服务器客户端的群聊与网络画板)
  12. 数字计算机模拟人脑,人造突触问世计算机模拟人脑不是梦
  13. bert-ancient-chinese——专注于古汉语智能处理的BERT预训练模型
  14. 内嵌资源html,go 1.16 embed 实现资源文件(html, css, js等)内嵌
  15. C语言实现TCP网络通信
  16. HDMI光纤延长器 HDMI高清视频光端机 8路HDMI光端机
  17. 整数在计算机里的表示
  18. Linux 0.00简单多任务内核boot.s超详注释
  19. 实际打印纸张的横向设置不起作用的问题
  20. iOS arc weak指针原理

热门文章

  1. 移动互联网的发展趋势ios与android哪更有前景,移动互联网是什么?移动互联网现状,发展趋势及前景...
  2. java m2文件放哪里_windows下打开.m2文件夹,没有找到setting.xml
  3. 盐城有哪些中专计算机学校,盐城有什么中专学校
  4. Ssm在线商城系统实战开发
  5. 基于JAVA+Servlet+JSP+MYSQL的读者荐购系统
  6. 永遠のゼロ       012
  7. reduce 阶段遍历对象添加到ArrayList中的问题
  8. Codeforces903E Swapping Characters
  9. MVC5应用程序目录
  10. POJ 3253-Fence Repair(堆)