请 [注册] 或 [登录]  | 返回主站

量化交易吧 /  量化平台 帖子:3364737 新帖:1

精英任务公开评审——作者:flumer

汇市风云榜发表于:7 月 1 日 14:00回复(1)

高频数据因子选股¶

1.引言¶

研究目的:本文参考广发证券《基于日内高频数据的短周期选股因子研究-高频数据因子研究系列一》,对研报内的结果进行分析,基本复制出该研报中上证500相关的结果,本文大量引用了该研报中对结果的叙述。

研究内容:在个股高频数据中,主要包括开盘价、收盘价、最高价、最低价、成交量、成交额等指标以及分笔的盘口相关的数据。本篇专题报告主要是对个股的分钟级别的成交相关的数据进行因子挖掘,希望能从中挖掘出有效的因子指标。

  • 测试时段:2007年1月到2019年3月

  • 调仓方式:在每周结束时,依据高频指标将剔除ST,*ST与停盘股后的2019年3月27日时的中证500成份股划分为5组,分别持有至周末;得分最高的组合为多头组合,得分最低的为空头组合。

具体因子指标构建如下

  • 对于每个个股在交易日$t$,首先计算个股在特定分钟频率下第$i$个的收益率$r_{t,i}$ ,$r_{t,i} = p_{t,i}-p_{t,i-1}$,其中$p_{t,i}$表示在交易日$t$,个股在第$i$个特定分钟频率下的对数价格,$p_{t,i−1}$表示在交易日$t$,个股在第$i-1$个特定分钟频率下的对数价格。

  • 对于每个个股,根据$r_{t,i}$分别计算个股在交易日$t$下的
    已实现方差(RealizedVariance),$$RDVar_t=\sum_{i=1,N}r_{t,i}^2$$已实现偏度(Realized Skewness),$$RDSkew_t=\frac{\sqrt{N}\sum_{t=1}^N r_{t,i}^3}{RDVar_t^{3/2}}$$已实现峰度(Realized kurtosis),$$RDKurt_t=\frac{N\sum_{t=1}^N r_{t,i}^4}{RDVar_t^2}$$
    $N$表示个股在交易日t中特定频率的分钟级别数据个数,如在5分钟级别下,交易日t下共有的数据个数$N$为$48(60\prod4/5=48)$。

  • 对于每个个股,在交易日t计算,其中:
    已实现波动(Realized Volatility),$$RVol_t=\left(\frac{242}{n}\sum_{i=0}^n RDVar_{t-i}\right)^{1/2}$$已实现偏度(Realized Skewness), $$RSkew_t=\frac{1}{n}\sum_{i=0}^n RDSkew_{t-i}$$已实现峰度(Realized Kurtosis),$$RKurt_t=\frac{1}{n}\sum_{i=0}^n RDKurt_{t-i}$$

  • 在每期调仓日截面上,按照上述公式计算每个个股的已实现波动(Realized Volatility)$RVol_t$ ,已实现偏度(Realized Skewness)$RSkew_t$,已实现峰度(Realized Kurtosis)$RKurt_t$指标,针对每个由高频数据计算得到的因子指标在历史上的分档组合表现,试图寻找出相对有效的因子指标。

数据说明

  • 样本区间:2007年1月1日至2019年3月27日(以下如无特别说明,2019年指的是2019年1月1日至2019年3月27日)

  • 样本范围:全市场个股、中证500历史成分股

  • 数据频率:个股每个交易日5分钟频率的收盘价、成交量、成交额等数据

策略构建

  • 实证区间:2007年1月1日至2019年3月27日

  • 选股范围:全市场、中证500历史成分股,剔除上市不满一年的股票,剔除ST股票、*ST股票,剔除交易日停牌的股票

  • 分档方式:根据当期个股计算的因子值:已实现波动(Realized Volatility) $RVol_t$,已实现偏度(Realized Skewness)$RSkew_t$ 、已实现峰度(Realized Kurtosis)$RKurt_t$,从小到大分为5档

  • 调仓周期:周频换仓,Q1档为因子值最小的,Q5档为因子值最大的。

  • 参数说明:N=48,n=5

研究结论:已实现偏度(Realized Skewness)指标能够对个股收益率进行明显的区分,运用该分组方式采用多空策略具有小于6%的回撤率。

研究耗时

  • 数据准备部分:大约需要1个小时

  • 策略构建部分:大约10min

%matplotlib inline
import pandas as pd
import numpy as np
from  tqdm import tqdm
import datetime as dt
from collections import defaultdict
import itertools
import pickle
import warnings
warnings.filterwarnings('ignore')

N=48;n=5
base='000905.XSHG'
START_DATE='2007-1-1'
END_DATE='2019-3-27'

2.生成与导入数据¶

由于5分钟高频数据数量巨大,我将逐个股提取后计算$RDVar_{t,i}$等日度指标,将5分钟高频数据数据转变为日度数据保存,此过程同时剔除了ST股票,停盘股以及小于一年(242个交易日)的股票。数据存储于

百度盘链接: https://pan.baidu.com/s/1WcdR6KKDBSSr0A4mwx3hIA
提取码: yh34

# 生成并保存数据
Codes=get_index_stocks(base,date=END_DATE)

sts=get_extras('is_st', Codes, start_date=START_DATE,\
               end_date=END_DATE,df=True)# 记录ST股票
Codes=sts.T.index[~sts.any(axis=0)].tolist()

def SolveADay(rlist):# 根据一天计算指标
    rlist=np.diff(np.log(rlist))
    RDVar=np.sum(rlist**2)
    RDSkew=np.sqrt(N)*np.sum(rlist**3)/RDVar**1.5
    RDKurt=N*np.sum(rlist**4)/RDVar**2
    return pd.Series([RDVar,RDSkew,RDKurt])

basicFactors=[]
for code in tqdm(Codes):# 按股票代码获取数据
    codePrice=get_price(code, start_date=START_DATE, end_date=END_DATE,skip_paused=True,# 去除停牌
              frequency='5m', fields=['close'])
    codePriceResampled=codePrice['close'].resample('D')
    if (codePriceResampled.count()>0).sum()<242:# 剔除小于一年的数据
        continue
    else:
        codePrice=codePriceResampled.apply(SolveADay).unstack(level=1).dropna()#去除不存在波动的日子
        codePrice.index=pd.MultiIndex.from_product([[code],codePrice.index])
        basicFactors.append(codePrice)

basicFactors=pd.concat(basicFactors)
basicFactors.columns=['RDVar','RDSkew','RDKurt']
basicFactors.index.set_names(['code','date'],inplace=True)
basicFactors.to_csv('zz500c.csv')

3.因子特征¶

basicFactors=pd.read_csv('zz500c.csv',index_col=[0,1])
basicFactors.head()
basicFactors.index.set_levels([
    basicFactors.index.levels[0],
    pd.to_datetime(basicFactors.index.levels[1])
],inplace=True)

gWeek=pd.Grouper(freq='W', level=-1)
# 构建因子指标
RVol=np.sqrt(242/n*basicFactors['RDVar'].groupby(['code',gWeek]).sum())# 计算RVol
RSkew=1/n*basicFactors['RDSkew'].groupby(['code',gWeek]).sum()# 计算RSkew
RKurt=1/n*basicFactors['RDKurt'].groupby(['code',gWeek]).sum()# 计算RKurt
del basicFactors

中证500成分股波动率分布一览

ax=RVol.hist(bins=50,grid=False)
ax.set_ylabel('Frequency')
ax.set_title('ZZ500 Volatility')
Text(0.5, 1.0, 'ZZ500 Volatility')

中证500成分股偏度分布一览

ax=RSkew.hist(bins=50,grid=False)
ax.set_ylabel('Frequency')
ax.set_title('ZZ500 Skewness')
Text(0.5, 1.0, 'ZZ500 Skewness')

中证500成分股峰度百分位走势一览

ax=RKurt.hist(bins=50,grid=False)
ax.set_ylabel('Frequency')
ax.set_title('ZZ500 Kurtosis')
Text(0.5, 1.0, 'ZZ500 Kurtosis')
def CrossSectionalPercentiles(df):
    df=df.swaplevel().sort_index()
    df=df.groupby('date').apply(lambda x:np.percentile(x,[90,75,50,25,10]))
    df=pd.DataFrame.from_items(zip(df.index, df.values))
    df.index=[90,75,'median',25,10]
    return df.T

中证500成分股波动率百分位走势一览

CrossSectionalPercentiles(RVol).plot(figsize(8,5))
<matplotlib.axes._subplots.AxesSubplot at 0x7fbb98bcd8d0>

中证500成分股偏度百分位走势一览

CrossSectionalPercentiles(RSkew).plot(figsize=(8,5))
<matplotlib.axes._subplots.AxesSubplot at 0x7fbb98a99278>

中证500成分股峰度百分位走势一览

CrossSectionalPercentiles(RKurt).plot(figsize(8,5))
<matplotlib.axes._subplots.AxesSubplot at 0x7fbb98a88d68>

利用中证500指数成分股在分钟级别的数据计算得到的关于个股的波动率、偏度、 峰度的结果,从中可以看出,个股的波动率在不同的时间维度上变化较大,从波动率分布上可以看出,中证500指数成分股波动率分布呈现右偏分布,从时间维度上看,个股的波动率水平往往与市场的趋势较为相关;个股的偏度分布 上看,整体偏度水平保持在零附近,呈现较为明显的厚尾状态,从个股偏度不同百 分位时间序列走势上可以看出,个股偏度水平整体较为稳定;从个股的峰度分布上 看,与全市场个股的峰度分布类似,在分布上呈现右偏状态,且样本内个股的峰度 水平大部分大于3,呈现出厚尾的现象。

4.实证分析—全市场、中证 500 因子选股分档表现¶

# 提取中证500各股周收益率
Codes=list(RVol.index.levels[0])
Price=get_price(Codes, start_date=START_DATE, end_date=END_DATE,
                     skip_paused=False,frequency='1d', fields=['close'])
Price=Price.to_frame()['close']
Price.index.set_names(['date','code'],inplace=True)
Price=np.log(Price.groupby(['code',pd.Grouper(freq='W', level=0)]).last())
Return=np.exp(Price.groupby('code').diff().groupby('code').shift(-1)).swaplevel().sort_index()
del Price
# 提取各股市值
mktCap=[]
for date in Return.index.levels[0]:
    df=get_fundamentals_continuously(
        query(valuation.market_cap).filter(valuation.code.in_(Codes)),
            end_date=date,count=1).to_frame()['market_cap'] 
    df.index.set_levels([date], level=0,inplace=True)
    mktCap.append(df)
mktCap=pd.concat(mktCap)
mktCap.index.set_names(['date','code'],inplace=True)
mktCap.index.set_levels([
    pd.to_datetime(mktCap.index.levels[0]),mktCap.index.levels[1]
],inplace=True)
def GetGroup(df):# 依据指标分组
    df=df.swaplevel().sort_index()
    group=df.groupby('date').apply(lambda x:pd.qcut(x,q=5,precision=10,labels=['Q1','Q2','Q3','Q4','Q5']))
    group.name='group'
    return group
def GroupTestRet(group,mktCap,ret):# 计算分组后各组周收益率
    df=pd.concat([mktCap,group,ret],axis=1)
    df.ffill(inplace=True)
    df['market_cap*return']=df['close']*df['market_cap']
    df=df.groupby(['date','group'])['market_cap*return'].sum()/\
        df.groupby(['date','group'])['market_cap'].sum()
    return df.unstack(level=1)

因子指标RVol中证500选股分档表现

RVolLevel=GroupTestRet(GetGroup(RVol),mktCap,Return)
RVolLevel.cumprod().plot(figsize=(10,5))
<matplotlib.axes._subplots.AxesSubplot at 0x7fbb723feeb8>

因子指标RSkew中证500选股分档表现

RSkewLevel=GroupTestRet(GetGroup(RSkew),mktCap,Return)
RSkewLevel.cumprod().plot(figsize=(10,5))
<matplotlib.axes._subplots.AxesSubplot at 0x7fbb910dec88>

因子指标RKurt因中证500选股分档表现

RKurtLevel=GroupTestRet(GetGroup(RKurt),mktCap,Return)
RKurtLevel.cumprod().plot(figsize=(10,5))
<matplotlib.axes._subplots.AxesSubplot at 0x7fbb908619e8>

从图中可以看出,在周频调仓频率的结果下,因子指标RVol、RKurt在全市场中的分档不明显,对个股收益率区分度较差,而因子指标RSkew在 全市场中的分档收益表现明显,对个股收益率区分度明显,分档收益在单调性结果上显著。

中证500RSkew因子IC值走势一览

IC_RSkew=pd.concat([RSkew.swaplevel(),Return],axis=1).\
    groupby('date').corr('pearson').iloc[0::2,-1].reset_index(level=1, drop=True)
IC_RSkew.name='IC'
ax=IC_RSkew.plot(figsize=(16,4),legend=True)
IC_RSkewRolling=IC_RSkew.rolling(12).mean()
IC_RSkewRolling.name='IC average(12 terms)'
IC_RSkewRolling.plot(ax=ax,color='red',legend=True)
<matplotlib.axes._subplots.AxesSubplot at 0x7fbb6d5419e8>

中证 500 指数内选股-IC 表现

aggOperator={'mean':np.mean,
             'std':np.std,
             'min':np.min,
             'max':np.max,
             'negative percentile':lambda x:'%.2f'%(np.sum(x<0)/x.shape[0]*100)+'%'}
IC_RSkewSummary=IC_RSkew.agg(aggOperator)
IC_RSkewSummary=pd.DataFrame(IC_RSkewSummary).T.\
                applymap(lambda x:np.round(x,3) if isinstance(x,float) else x)
IC_RSkewSummary
.dataframe tbody tr th:only-of-type { vertical-align: middle; } .dataframe tbody tr th { vertical-align: top; } .dataframe thead th { text-align: right; }
mean std min max negative percentile
IC -0.039 0.106 -0.453 0.432 65.60%

中证 500 选股-IC 分年度表现一览

IC_RSkewSummary=IC_RSkew.resample('Y').agg(aggOperator).unstack(level=1).T
IC_RSkewSummary.set_index(IC_RSkewSummary.index.year,inplace=True)
IC_RSkewSummary=IC_RSkewSummary[list(aggOperator)].\
                applymap(lambda x:np.round(x,3) if isinstance(x,float) else x)
IC_RSkewSummary
.dataframe tbody tr th:only-of-type { vertical-align: middle; } .dataframe tbody tr th { vertical-align: top; } .dataframe thead th { text-align: right; }
mean std min max negative percentile
date
2007 -0.039 0.107 -0.255 0.282 62.00%
2008 -0.043 0.126 -0.299 0.254 62.75%
2009 -0.056 0.096 -0.334 0.132 68.63%
2010 -0.054 0.139 -0.453 0.432 74.51%
2011 -0.046 0.082 -0.248 0.085 64.71%
2012 -0.038 0.109 -0.264 0.254 68.63%
2013 -0.030 0.102 -0.274 0.174 60.78%
2014 -0.014 0.086 -0.200 0.224 59.62%
2015 -0.044 0.118 -0.334 0.205 63.46%
2016 -0.045 0.107 -0.375 0.177 66.00%
2017 -0.008 0.086 -0.164 0.354 63.46%
2018 -0.044 0.088 -0.243 0.147 74.51%
2019 -0.055 0.148 -0.381 0.220 58.33%

RSkew因子指标在中证500指数成分股中选股,从2007年开始至今IC均值为-0.039,标准差为0.106,在周频调仓的情况下, 负IC占比为65.60%。在滚动12期IC的均值也基本上处以零以下的位置,分年度统计中,大部分年度IC均值均为负,且在分年度统计中可以看出,大部分年度负IC占比基本上在60%以上。

5.中证500指数成分股RSkew因子选股分档后各策略表现¶

多空策略的制定可以参考https://medium.com/auquan/long-short-equity-trading-strategy-daa41d00a036

def GroupTestRetLongShortCumprod(group,mktCap,ret):# 多空策略分组累计收益率
    df=pd.concat([mktCap,group,ret],axis=1)
    df.ffill(inplace=True)
    df['market_cap*return']=df['close']*df['market_cap']
    df=df.groupby(['date','group'])['market_cap*return'].sum()/\
        df.groupby(['date','group'])['market_cap'].sum()
    df=df.unstack(level=1)
    df.columns=list(df.columns)
    df['Q1-Q5']=1/2*(df['Q1']-df['Q5'])+1
    df=df['Q1-Q5'].cumprod()
    return df

中证500指数成分股RSkew因子选股多-空策略净值走势表现一览

# zz500Ret 周度
zz500Ret=get_price(base,start_date=START_DATE, end_date=END_DATE,fields=['close'])
zz500Ret=zz500Ret.resample('W').last()
zz500Ret=(np.exp(np.log(zz500Ret).diff().shift(-1)))
zz500Ret.ffill(inplace=True)

RSkewLevel.columns=list(RSkewLevel.columns)
RSkewLevel['Q1-Q5_cum']=GroupTestRetLongShortCumprod(GetGroup(RSkew),mktCap,Return)
RSkewLevel['zz500']=zz500Ret

ax=RSkewLevel[['Q1','Q5','zz500']].cumprod().plot(figsize=(10,5))
RSkewLevel['Q1-Q5_cum'].plot(secondary_y=True,ax=ax,label='Q1-Q5',legend=True)
<matplotlib.axes._subplots.AxesSubplot at 0x7fbb69f40d68>

回测准备

def BacktrackInformation(level,ret):#计算回测所需数据
    # 累计收益率
    accRate=level.resample('Y').last()/\
        level.resample('Y').first()-1
    # 年化收益率
    yldRate=(1+accRate)**(242/5/level.resample('Y').count())-1
    yldRate[-1]=(1+accRate[-1])**(60.5/5/level.resample('Y').count()[-1])-1#2019调整
    # 年化波动率
    sigma=np.sqrt(242/5)*ret.resample('Y').std()
    sigma[-1]*=np.sqrt(60.5/5)/np.sqrt(242/5)#2019调整
    # 最大回撤
    maxDropdown=(-1+np.exp(np.log(level).diff())).resample('Y').max()
    # 信息比率
    informationRatio=yldRate/sigma
    # 整体
    accRateTotal=level.loc[level.last_valid_index()]/level.loc[level.first_valid_index()]-1
    yldRateTotal=(1+accRateTotal)**(242/5/level.count().sum())-1
    sigmaTotal=np.sqrt(242/5)*ret.std()
    maxDropdownTotal=max(maxDropdown)
    informationRatioTotal=yldRateTotal/sigmaTotal
    return accRate,maxDropdown,yldRate,sigma,informationRatio,\
            accRateTotal,maxDropdownTotal,yldRateTotal,sigmaTotal,informationRatioTotal
def BacktrackTable(accRate,maxDropdown,yldRate,sigma,informationRatio,\
                   accRateTotal,maxDropdownTotal,yldRateTotal,sigmaTotal,informationRatioTotal):
    #罗列回测信息
    cols=['accRate','maxDropdown','yldRate','sigma','informationRatio']
    df=pd.DataFrame([accRate,maxDropdown,yldRate,sigma,informationRatio],index=cols).T
    years=list(df.index.year)
    df=df.append(pd.DataFrame(
        [accRateTotal,maxDropdownTotal,yldRateTotal,sigmaTotal,informationRatioTotal],
        index=cols).T)
    df=df.apply(lambda x:list(map(lambda z:"%.2f"%(z*100)+'%',x[:-1]))+["%.3f"%x[-1:]],axis=1)
    df=pd.DataFrame(df.values.tolist(),columns=cols,index=years+['total'])
    return df

中证500数成分股RSkew因子选股多-空策略分年度表现

ret=1/2*(RSkewLevel['Q1']-RSkewLevel['Q5'])
level=RSkewLevel['Q1-Q5_cum']
BacktrackTable(*BacktrackInformation(level,ret))
.dataframe tbody tr th:only-of-type { vertical-align: middle; } .dataframe tbody tr th { vertical-align: top; } .dataframe thead th { text-align: right; }
accRate maxDropdown yldRate sigma informationRatio
2007 27.26% 3.40% 26.28% 8.86% 2.966
2008 21.69% 2.75% 20.48% 7.44% 2.752
2009 28.04% 3.32% 26.44% 6.33% 4.177
2010 11.88% 3.18% 11.24% 10.15% 1.108
2011 11.44% 1.29% 10.83% 3.92% 2.763
2012 5.44% 1.21% 5.15% 4.95% 1.040
2013 11.83% 2.95% 11.20% 6.35% 1.764
2014 5.43% 1.07% 5.05% 3.72% 1.356
2015 16.52% 4.70% 15.29% 7.78% 1.966
2016 12.87% 2.51% 12.43% 5.17% 2.405
2017 1.77% 1.19% 1.65% 3.35% 0.492
2018 7.65% 1.78% 7.25% 4.85% 1.495
2019 3.28% 3.94% 3.31% 4.72% 0.701
total 401.81% 4.70% 13.31% 6.50% 2.048

多空策略整体的年化收益率为13.31%,信息比率为2.048。分年度看,多空策略在历史上大部分年度都取得了正的收益率。

中证500指数成分股RSkew因子选股多-中证500策略净值走势表现一览

ax=RSkewLevel[['Q1','zz500']].cumprod().plot(figsize=(16,5))
RSkewLevel['Q1-zz500']=RSkewLevel['Q1']-RSkewLevel['zz500']
RSkewLevel['Q1-zz500'].plot(ax=ax,secondary_y=True,legend=True)
<matplotlib.axes._subplots.AxesSubplot at 0x7fbb7ac174e0>

中证 500 选股多头策略分年度表现一览

ret=RSkewLevel['Q1']
RSkewLevel['Q1_cum']=RSkewLevel['Q1'].cumprod()
level=RSkewLevel['Q1_cum']
BacktrackTable(*BacktrackInformation(level,ret))
.dataframe tbody tr th:only-of-type { vertical-align: middle; } .dataframe tbody tr th { vertical-align: top; } .dataframe thead th { text-align: right; }
accRate maxDropdown yldRate sigma informationRatio
2007 252.08% 16.10% 238.18% 40.94% 5.817
2008 -62.73% 22.67% -60.80% 53.87% -1.129
2009 141.43% 13.72% 130.82% 34.33% 3.811
2010 21.83% 7.96% 20.61% 25.75% 0.801
2011 -28.85% 8.92% -27.60% 22.87% -1.207
2012 2.45% 7.10% 2.32% 23.33% 0.099
2013 30.35% 5.75% 28.60% 21.12% 1.354
2014 45.82% 5.50% 42.06% 18.14% 2.319
2015 71.18% 14.39% 64.93% 46.10% 1.409
2016 1.86% 7.01% 1.80% 27.41% 0.066
2017 -2.25% 4.23% -2.10% 13.48% -0.156
2018 -31.25% 6.38% -29.93% 25.46% -1.175
2019 37.41% 7.25% 37.78% 11.63% 3.249
total 770.15% 22.67% 18.24% 32.25% 0.566

中证500选股多头-500策略分年度表现一览

ret=1/2*(RSkewLevel['Q1']-RSkewLevel['zz500'])
RSkewLevel['Q1-zz500']=(ret+1).cumprod()
level=RSkewLevel['Q1-zz500']
BacktrackTable(*BacktrackInformation(level,ret))
.dataframe tbody tr th:only-of-type { vertical-align: middle; } .dataframe tbody tr th { vertical-align: top; } .dataframe thead th { text-align: right; }
accRate maxDropdown yldRate sigma informationRatio
2007 0.20% 4.83% 0.20% 12.27% 0.016
2008 -6.67% 2.94% -6.34% 10.05% -0.631
2009 11.36% 4.55% 10.75% 6.49% 1.657
2010 6.59% 2.19% 6.25% 4.69% 1.333
2011 8.91% 4.54% 8.44% 5.15% 1.638
2012 -2.00% 1.02% -1.90% 3.17% -0.599
2013 3.31% 1.93% 3.14% 5.29% 0.593
2014 -0.25% 0.77% -0.24% 2.58% -0.092
2015 10.47% 2.27% 9.71% 4.85% 2.002
2016 5.89% 1.80% 5.70% 3.85% 1.480
2017 -0.93% 0.72% -0.86% 2.16% -0.400
2018 -3.25% 0.91% -3.09% 6.40% -0.482
2019 8.83% 5.17% 8.90% 6.19% 1.438
total 56.05% 5.17% 3.52% 6.39% 0.550

中证 500 选股换手率分年度统计一览

选股换手率采用了 https://economictimes.indiatimes.com/wealth/invest/what-does-a-mutual-funds-portfolio-turnover-ratio-indicate/articleshow/63556068.cms?from=mdr 中描述的方法

RSkewGroup=GetGroup(RSkew)
Q1_RSkewGroup=RSkewGroup.unstack(level=1)
Q1_RSkewGroup=((Q1_RSkewGroup=='Q1')==True).astype(int)
Q1_mktCap=(mktCap.unstack()*Q1_RSkewGroup)
aggOperator={'mean':np.mean,'max':np.max,'min':np.min,'std':np.std,'acc':np.sum}

Q1_RSkewChangeBuy=(Q1_RSkewGroup.diff()>0).astype(int) #buy in
Q1_RSkewChangeBuy=(Q1_RSkewChangeBuy*Q1_mktCap).sum(axis=1)/Q1_mktCap.sum(axis=1)
Q1_RSkewChangeBuy=Q1_RSkewChangeBuy.shift(-1).iloc[:-1]

Q1_RSkewChangeSell=(Q1_RSkewGroup.diff()<0).astype(int).shift(-1) #sell out
Q1_RSkewChangeSell=(Q1_RSkewChangeSell*Q1_mktCap).sum(axis=1)/Q1_mktCap.sum(axis=1)
Q1_RSkewChangeSell=Q1_RSkewChangeSell.iloc[:-1]

Q1_RSkewChange=pd.DataFrame([Q1_RSkewChangeBuy,Q1_RSkewChangeSell]).min(axis=0)
Q1_RSkewChange=(Q1_RSkewChange.resample('Y').agg(aggOperator)).unstack(level=1).T
Q1_RSkewChange.index=Q1_RSkewChange.index.year

Q1_RSkewChange[list(aggOperator)]
.dataframe tbody tr th:only-of-type { vertical-align: middle; } .dataframe tbody tr th { vertical-align: top; } .dataframe thead th { text-align: right; }
mean max min std acc
date
2007 0.783262 0.939657 0.469278 0.093426 39.163119
2008 0.772033 0.940693 0.562965 0.085270 39.373659
2009 0.785656 0.946987 0.583867 0.080654 40.068470
2010 0.793798 0.933038 0.603333 0.078146 40.483714
2011 0.790165 0.924330 0.646079 0.068739 40.298409
2012 0.762767 0.901772 0.600531 0.069200 38.901131
2013 0.783148 0.909950 0.653015 0.061633 39.940542
2014 0.771364 0.903494 0.674218 0.059443 40.110903
2015 0.795688 0.898223 0.660018 0.056804 41.375760
2016 0.789221 0.872059 0.633282 0.052395 39.461071
2017 0.793374 0.895994 0.709930 0.047146 41.255435
2018 0.778910 0.926105 0.600036 0.054443 39.724427
2019 0.763529 0.884148 0.623627 0.080806 8.398817

在中证500选股回测中,多头中证500指数后,策略整体的年化收益率为3.52%, 信息比率为0.550,换手率均值在78%左右,策略表现并不出色。整体换手率较高,策略最大回撤为策略 的5.17%,分年度看,每一年的最大回撤都不超过6%。

全部回复

0/140

达人推荐

量化课程

    移动端课程