1200字范文,内容丰富有趣,写作的好帮手!
1200字范文 > python量化选股策略 源码_【一点资讯】Python实现行业轮动量化选股【附完整源码】...

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

时间:2019-12-24 21:06:15

相关推荐

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

1.背景

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

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

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

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

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

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

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

具体行业轮动策略如下回测区间:.01-.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(,1,1)

enddate = datetime.date(,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]0409-海通证券-海通证券金融工程专题报告:动量策略及收益率高阶矩在行业轮动中的应用-388742关于Python金融量化

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。