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

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

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

萨达撒撒发表于:7 月 1 日 14:10回复(1)

引言¶

研究目的¶

本文参考广发证券《高频数据因子研究系列一:基于日内高频数据的短周期选股因子研究》,采用研报内的方法计算已实现方差(Realized Variance)RDVar、已实现偏度(Realized Skewness)RDSkew、已实现峰度(Realized kurtosis)RDKurt、已实现波动(Realized Volatility)RVol、已实现偏度(Realized Skewness)RSkew、已实现峰度(Realized Kurtosis)RKurt因子指标,并对已实现波动(Realized Volatility)RVol、已实现偏度(Realized Skewness)RSkew、已实现峰度(Realized Kurtosis)RKurt三个因子指标考察回测区间对个股收益率的区别度。根据研报分析,RVol、RKurt因子指标对个股收益率区分度不明显,而RSkew在中证500中对个股收益率区分度明显,并分析了对应的IC、换手率表现以及相应的年度表现。

研究内容¶

1)根据研报公式,计算对应的6个因子值,这里采用了和研报同样的参数

2)因子特征的描述,包括因子百分位数走势和因子分布一览

3)实证分析,中证500因子选股分档表现

样本范围:中证500历史成分股

数据频率:5分钟频率的数据,5日未来收益率的IC分析、n=5的平滑计算

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

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

分档方式:根据上述的三个因子分别分档,从小到大分5档

调仓周期: 周频换仓(hold_period=5个交易日),Q1档为因子值最小的,Q5档为因子值最大的

参数说明:N=48,n=5,freq=5,hold_period=5

4)因子IC表现,RSkew因子在中证500内选股的IC表现,IC走势和平滑12期IC走势,以及IC的年度表现

5)因子选股多-空策略,RSkew因子在中证500指数成分股中选股多-空策略净值走势一览以及年度表现

6)因子选股多-中证500策略净值走势表现一览,以及多头-500策略分年度表现一览,多头中证500选股换手率分年度表现一览

研究结论¶

1)利用个股高频价格数据构建了个股已实现波动(Realized Volatility)RVol,已实现偏度(Realized Skewness)RSkew、已实现峰度(Realized Kurtosis)RKurt指标

2)在中证500成分股中详细测算了已实现波动(Realized Volatility)RVol,已实现偏度(Realized Skewness)RSkew、已实现峰度(Realized Kurtosis)RKurt因子指标在选股中的效果,实证结果表明,已实现波动(Realized Volatility)RVol,已实现峰度(Realized Kurtosis)RKurt在周频换仓的情况下对个股收益率区分度不高,而已实现偏度(Realized Skewness)RSkew在全市场以及中证500成分股中的分档收益区分度明显,分档收益单调性明显

3)因子指标RSkew在中证500成分股中选股,从2013年至今,IC均值为-0.024, 负IC占比为61.61%,多头组合年化收益率为24.89%,多头组合对冲中证500 指数后年化收益率为3.03%,最大回撤为13.78%,信息比率为0.387

1.导入一些库¶

import pandas as pd
import numpy as np
import seaborn as sns
import matplotlib.pyplot as plt
import time
import warnings
from jqdata import *
warnings.filterwarnings('ignore')
sns.set_style("whitegrid")
plt.rcParams['font.sans-serif'] = ['SimHei']  # 用来正常显示中文标签
plt.rcParams['axes.unicode_minus'] = False  # 用来正常显示负号

2.数据的读取与准备¶

t_begin = time.time()
freq = 5 #计算时采用的分钟数据频次
N = 60*4/freq
frequency = str(freq) + 'm'
n = 5 #后续计算平滑值的窗口期
zz500 = get_index_stocks('000905.XSHG') #取出中证500股票
start_date = '2013-01-01'
end_date = '2019-03-27'
m_end_date = '2019-03-28' #由于聚宽提取分钟数据的方式,需要将提取分钟数据的日期延后一天
#读取数据上首次可能会花费很长的时间,6年多的5分钟频次的数据量比较大
#后续每个部分都有计时可以参考
t0 = time.time()
print('开始读取数据')
st = get_extras('is_st', zz500, start_date=start_date, end_date=end_date, df=True)
open_paused = get_price(zz500, start_date=start_date, end_date=end_date, frequency='daily', fields=['paused','open'], fq='pre')
paused = open_paused['paused'] == 1
nostock = open_paused['paused'].isnull()
newstock = (nostock + 0).astype('int').rolling(252, min_periods=1).max() == 1
log_minute_close = np.log(get_price(zz500, start_date=start_date, end_date=m_end_date, frequency=frequency, fields=['close'], fq='pre')['close'])
open_ = open_paused['open']
log_minute_close['date'] = log_minute_close.index.date
log_minute_close['time'] = log_minute_close.index.time
log_minute_close.set_index(['date','time'],inplace=True)
minute_return = log_minute_close.groupby(level=0).apply(lambda df:df.diff(1))
out = ~(st | paused | nostock | newstock) #剔除停牌和st股以及未上市和新上市一年内的股票
future_ret1 = open_.pct_change(1).shift(-1-1) #开盘买入以及调仓,因此以开盘价计算1日未来收益率,后续用于计算分组净值
future_ret5 = open_.pct_change(5).shift(-5-1) #同上,5日未来收益率,用于计算后续的IC
t1 = time.time()
print('数据读取完成,耗时 %s 分钟' %round(((t1-t0)/60),3))
#这里定义了一个plot table的函数,方便后续查看一些表
def print_table(table, name=None):
    """
    将 dataframe 表格更好看地输出

    参数:
    ----------
    table : pd.Series or pd.DataFrame
        用于输出的表格.
    name : str, optional
        表格左上的名.
    """
    if isinstance(table, pd.Series):
        table = pd.DataFrame(table)

    if isinstance(table, pd.DataFrame):
        table.columns.name = name

    prev_option = pd.get_option('display.float_format')

    display(table)
                     
开始读取数据
数据读取完成,耗时 9.581 分钟

3.因子的计算和调整¶

t0 = time.time()
print('因子计算开始')
RDVar = minute_return.pow(2).groupby(level=0).sum(axis=0) 
RDSkew = (np.sqrt(N) * minute_return.pow(3).groupby(level=0).sum(axis=0)) / RDVar.pow(3/2)
RDKurt = N * minute_return.pow(4).groupby(level=0).sum(axis=0) / RDVar.pow(2)
#mad法去极值处理的矩阵运算,将大于/小于 md +/- 3 * mad的部分按大小排序拉回[md+3*mad,md+3.5*mad]/[md-3.5*mad,md-3*mad]区间
def winsorize(df, nsigma=3):
    md = df.median(axis=1)
    mad = 1.483 * (df.sub(md, axis=0)).abs().median(axis=1)
    up = df.apply(lambda k: k > md + mad * nsigma)
    down = df.apply(lambda k: k < md - mad * nsigma)
    df[up] = df[up].rank(axis=1, pct=True).multiply(mad * 0.5, axis=0).add(md + mad * nsigma, axis=0)

    df[down] = df[down].rank(axis=1, pct=True).multiply(mad * 0.5, axis=0).add(md - mad * (0.5 + nsigma), axis=0)

    return df
RDVar = winsorize(RDVar)
RDSkew = winsorize(RDSkew)                          
RDKurt = winsorize(RDKurt)  
RVol = np.sqrt(RDVar.rolling(n+1).mean() * 242)  #根据研报计算,平滑用了前五天和当日的值,因此rolling窗口期用n+1
RSkew = RDSkew.rolling(n+1).mean()
RKurt = RDKurt.rolling(n+1).mean()   
#对研报中的条件进行股票剔除,并去掉所有股票都没有因子值的日期
RDVar = RDVar[out]
RDSkew = RDSkew[out]
RDKurt = RDKurt[out]
RVol = RVol[out]
RSkew = RSkew[out]
RKurt = RKurt[out]
RDVar.dropna(axis=0,how='all',inplace=True)
RDSkew.dropna(axis=0,how='all',inplace=True)
RDKurt.dropna(axis=0,how='all',inplace=True)
RVol.dropna(axis=0,how='all',inplace=True)
RSkew.dropna(axis=0,how='all',inplace=True)
RKurt.dropna(axis=0,how='all',inplace=True)
t1 = time.time()
print('因子计算完成 耗时 %s 分钟' %round(((t1-t0)/60),3))
因子计算开始
因子计算完成 耗时 5.813 分钟

4.中证500因子特征¶

t0 = time.time()
print('因子特征报告生成开始')
def create_characteristic(factor, fac_name): 
    """
    创建因子特征报告,包括因子分布一览和百分位走势一览

    参数:
    ----------
    factor : pd.DataFrame
        用于计算特征的因子.
    fac_name : str,
        因子名,用于绘图时的标题.
    """
    fig,axes = plt.subplots(figsize=(18,12),ncols=2,nrows=1)
    quantile = pd.DataFrame()
    quantiles = [90,75,50,25,10]
    for q in quantiles:
        quantile[q] = factor.quantile(q/100,axis=1)
    quantile.plot(ax=axes[1])
    axes[1].set_title(fac_name + ' 中证500因子百分位走势一览')
    sns.distplot(factor.stack(),ax=axes[0])
    axes[0].set_title(fac_name + ' 中证500因子分布一览')
    return axes

create_characteristic(factor=RVol, fac_name='RVol')
create_characteristic(factor=RSkew, fac_name='RSkew')
create_characteristic(factor=RKurt, fac_name='RKurt')
t1 = time.time()
print('因子特征报告生成完成 耗时 %s 秒' %round((t1-t0),3))
因子特征报告生成开始
因子特征报告生成完成 耗时 2.441 秒

5.中证500因子选股分档表现¶

t0 = time.time()
print('因子选股分档表现报告开始')
groups = 5
hold_period = 5
def create_groupret_report(factor,groups,hold_period,fac_name):
    """
    创建因子选股分档报告,因子分档的净值走势

    参数:
    ----------
    factor : pd.DataFrame
        用于计算的因子.
    groups : int
        分档的组数
    hold_period : int
        持仓期(调仓周期)
    fac_name : str,
        因子名,用于绘图时的标题.
    """
    grouped = factor.rank(axis=1,pct=True) * groups // 1  #因子分层
    period_group = pd.DataFrame()
    #这里取出每隔调仓期日期的分组情况并重复5次,因为调仓后在下一次调仓前各组持有的股票是不变的
    to_append = grouped.iloc[::hold_period,:]
    for i in range(5):
        period_group = period_group.append(to_append)
    period_group = period_group.sort_index().iloc[:factor.shape[0],:]
    period_group.index = factor.index[:period_group.shape[0]]  #把index调整回来
    period_group = period_group.reindex_like(factor)
    #计算分组收益率
    group_rets = {}
    for i in range(5):
        group_rets[i] = future_ret1[period_group == i].mean(axis=1)
    group_ret = pd.DataFrame.from_dict(group_rets,'columns')
    group_ret.dropna(axis=0,how='all',inplace=True)
    #在起点对齐,计算净值
    group_ret.iloc[0,:] = 0
    net_value = (group_ret + 1).cumprod()
    net_value.columns = ['Q1','Q2','Q3','Q4','Q5']
    fig,ax = plt.subplots(figsize=(18,12))
    net_value.plot(ax=ax,lw=2)
    ax.set_title(fac_name + ' 中证500因子指标选股分档表现')
    ax.set_xlabel('date')
    ax.set_ylabel('net value')
    return ax

create_groupret_report(factor=RVol, groups=groups, hold_period=hold_period, fac_name='RVol')
create_groupret_report(factor=RSkew, groups=groups, hold_period=hold_period, fac_name='RSkew')
create_groupret_report(factor=RKurt, groups=groups, hold_period=hold_period, fac_name='RKurt')
t1 = time.time()
print('因子选股分档表现报告完成 耗时 %s 秒' %round((t1-t0),3))
因子选股分档表现报告开始
因子选股分档表现报告完成 耗时 1.475 秒

6.实证分析 中证500选股 IC部分¶

def minus_p(sr):    #计算负值比例
    return sr[sr < 0].count() / sr.count()

def create_ic_report(factor,future_ret,fac_name):
    """
    创建因子IC报告,包括IC统计量和IC走势图

    参数:
    ----------
    factor : pd.DataFrame
        用于计算的因子.
    future_ret : pd.DataFrame
        用于计算IC的未来收益率,这里选用了5日未来收益率,计算5日IC值
    fac_name : str,
        因子名,用于绘图时的标题.
    """
    ic = factor.corrwith(future_ret,axis=1)
    ic.name = 'IC'
    ic = ic.to_frame()
    ic_statistic = ic.agg(['mean','std','max','min',minus_p]).T
    ic_statistic.columns = ['IC均值','IC标准差','IC最大值','IC最小值','负IC占比']
    name1 = fac_name + '因子中证500选股-IC表现'
    print_table(ic_statistic,name=name1)

    ic['IC均值(滚动12)'] = ic['IC'].rolling(12).mean()
    fig,ax = plt.subplots(figsize=(18,12))
    ic.plot(ax=ax,lw=2)
    ax.set_title('中证500选股 ' + fac_name + ' 因子IC值走势一览')
    #按年度的IC统计量
    ic['year'] = ic.index.year
    ic.set_index('year',inplace=True)
    ic_sta_byyear = ic['IC'].groupby('year').agg(['mean','std','max','min',minus_p])
    ic_sta_byyear.columns = ['IC均值','IC标准差','IC最大值','IC最小值','负IC占比']
    ic_sta_byyear.loc['整体',:] = ic_statistic.values
    name2 = fac_name + '因子中证500选股-IC分年度表现一览'
    print_table(ic_sta_byyear,name=name2)

t0 = time.time()
print('IC分析开始')
create_ic_report(factor=RSkew, future_ret=future_ret5, fac_name='RSkew')
t1 = time.time()
print('IC分析完成 耗时 %s 秒' %round((t1-t0),3))
IC分析开始
.dataframe tbody tr th:only-of-type { vertical-align: middle; } .dataframe tbody tr th { vertical-align: top; } .dataframe thead th { text-align: right; }
RSkew因子中证500选股-IC表现 IC均值 IC标准差 IC最大值 IC最小值 负IC占比
IC -0.023596 0.099904 0.344797 -0.462834 0.616101
.dataframe tbody tr th:only-of-type { vertical-align: middle; } .dataframe tbody tr th { vertical-align: top; } .dataframe thead th { text-align: right; }
RSkew因子中证500选股-IC分年度表现一览 IC均值 IC标准差 IC最大值 IC最小值 负IC占比
year
2013 -0.010123 0.111575 0.333592 -0.332714 0.562232
2014 -0.015977 0.088826 0.250565 -0.267773 0.591837
2015 -0.044375 0.107335 0.344797 -0.309277 0.700820
2016 -0.045124 0.102989 0.304066 -0.462834 0.721311
2017 -0.014759 0.079075 0.194649 -0.216877 0.557377
2018 -0.015290 0.093890 0.206621 -0.259354 0.584362
2019 -0.000763 0.128678 0.218090 -0.314600 0.500000
整体 -0.023596 0.099904 0.344797 -0.462834 0.616101
IC分析完成 耗时 0.125 秒

7. 实证分析 中证500选股 多-空策略部分(根据研报,这里仅体现RSkew因子的部分)¶

t0 = time.time()
factor = RSkew.copy()
fac_name = 'RSkew'
hold_period = 5
print('中证500选股 多-空策略部分报告开始')
def back(sr):   #计算最大回撤率
    return format((1 - sr / sr.cummax()).max(),'.2%')
def cum_ret(sr):    #计算累计收益率
    return format((sr.iloc[-1] - sr.iloc[0]) / sr.iloc[0],'.2%')
def annual_std(ret):    #计算年化波动率
    return (ret.std() * np.sqrt(252))
def annual_ret(sr):     #计算年化收益率
    return ((sr.iloc[-1]/sr.iloc[0])**(252/len(sr)) - 1)

#以下和上面分档部分的计算相同
grouped = factor.rank(axis=1,pct=True) * groups // 1
period_group = pd.DataFrame()
to_append = grouped.iloc[::hold_period,:]
for i in range(5):
    period_group = period_group.append(to_append)
period_group = period_group.sort_index().iloc[:factor.shape[0],:]
period_group.index = factor.index[:period_group.shape[0]]
period_group = period_group.reindex_like(factor)
group_rets = {}
for i in range(5):
    group_rets[i] = future_ret1[period_group == i].mean(axis=1)
group_ret = pd.DataFrame.from_dict(group_rets,'columns')
group_ret.dropna(axis=0,how='all',inplace=True)
group_ret.iloc[0,:] = 0
net_value = (group_ret + 1).cumprod()
net_value.columns = ['Q1','Q2','Q3','Q4','Q5']
#计算中证500的净值走势    
zz500_bench = future_ret1.mean(axis=1).loc[net_value.index]
zz500_bench.iloc[0] = 0
zz500_bench = (zz500_bench + 1).cumprod()
#取出多头 空头 多-空 和中证500的净值    
top_bottom = net_value[['Q1','Q5']]
top_bottom['tb'] = ((group_ret.iloc[:,0] - group_ret.iloc[:,-1]) + 1).cumprod()
top_bottom['bench'] = zz500_bench.values
top_bottom.columns = ['多头净值','空头净值','多-空净值(右轴)','中证500指数']
#净值走势绘图,将多-空净值纵坐标绘制于右轴    
fig,ax = plt.subplots(figsize=(18,12))
ax1 = ax.twinx()
top_bottom[['多头净值','空头净值','中证500指数']].plot(ax=ax,lw=2)
top_bottom['多-空净值(右轴)'].plot(ax=ax1,lw=2,legend=True,color='purple')
ax.legend(loc='upper left')
ax1.legend(loc='upper right')
ax.set_title('中证500成分股' + fac_name + '因子选股多-空策略净值走势一览')

tb_ret = (group_rets[0] - group_rets[4]).dropna(how='all')
table_name = '中证500成分股' + fac_name + '因子选股多-空策略分年度表现'
def create_table(to_table_nv,col,ret):
    """
    创建策略年度表现表格,包括累计收益率 最大回撤率 年化收益率 年化波动率 信息比率

    参数:
    ----------
    to_table_nv : pd.DataFrame
        用于计算净值走势df.
    col : str
        净值走势df中的要计算年度表现策略的列名,用于将其取出
    ret : pd.Series
        策略的收益率序列,用于计算年化波动率
    """
    to_table_nv['year'] = to_table_nv.index.year   #调整index用于之后计算年度表现
    byyear = to_table_nv.set_index('year')[col]   #取出传入的净值走势df中需要的列



    ret.index = byyear.index
    ratio_table = byyear.groupby(level=0).agg([cum_ret,back,annual_ret],raw=False)
    ratio_table['annual_std'] = ret.groupby(level=0).apply(annual_std)
    ratio_table['information rate'] = ratio_table['annual_ret']/ratio_table['annual_std']
    ratio_table['annual_ret'] = ratio_table['annual_ret'].apply(lambda x:format(x,'.2%'))
    ratio_table['annual_std'] = ratio_table['annual_std'].apply(lambda x:format(x,'.2%')) #这里不在之前的函数中输出为百分数主要是为了计算信息比率
    #计算整体的表现
    allyear = byyear.agg([cum_ret,back,annual_ret],raw=False).values
    allyear = np.append(allyear,ret.std() * np.sqrt(252))
    allyear = np.append(allyear,allyear[2]/allyear[3])
    allyear[2] = format(allyear[2],'.2%')
    allyear[3] = format(allyear[3],'.2%')
    ratio_table.loc['整体',:] = allyear
    ratio_table.columns = ['累计收益率','最大回撤率','年化收益率','年化波动率','信息比率']
    #print_table(ratio_table,name=table_name)
    return ratio_table
table = create_table(to_table_nv=top_bottom, col='多-空净值(右轴)', ret=tb_ret)
print_table(table, name=table_name)
t1 = time.time()
print('中证500选股 多-空策略部分报告完成 耗时 %s 秒' %round((t1-t0),3))
中证500选股 多-空策略部分报告开始
.dataframe tbody tr th:only-of-type { vertical-align: middle; } .dataframe tbody tr th { vertical-align: top; } .dataframe thead th { text-align: right; }
中证500成分股RSkew因子选股多-空策略分年度表现 累计收益率 最大回撤率 年化收益率 年化波动率 信息比率
year
2013 3.98% 9.49% 4.32% 10.84% 0.398285
2014 8.11% 6.36% 8.35% 10.12% 0.825069
2015 67.01% 9.68% 69.84% 16.80% 4.158160
2016 18.14% 7.73% 18.79% 9.86% 1.906341
2017 -0.13% 8.93% -0.13% 6.98% -0.018801
2018 0.56% 9.86% 0.58% 8.81% 0.065518
2019 3.43% 6.20% 17.06% 13.74% 1.242183
整体 133.08% 9.86% 15.20% 11.15% 1.363561
中证500选股 多-空策略部分报告完成 耗时 0.698 秒

8. 实证分析 中证500选股 因子选股 多-中证500策略部分¶

t0 = time.time()
print('多-中证500策略部分报告开始')
#先计算用于绘图和后续表格输出的净值走势df,包括多头净值和多-中证500净值
top = net_value['Q1'].to_frame()   
top_alpha = (group_rets[0] - future_ret1.mean(axis=1).loc[net_value.index]).dropna(how='all')  #多头相对中证500的超额收益率
top['top-bench'] = top_alpha
top['top-bench'].iloc[0] = 0
top['top-bench'] = top['top-bench'].add(1).cumprod()
top.columns = ['多头净值','多头-中证500净值']
top_alpha.name = '超额收益率(右轴)'
#净值和超额收益率绘图,超额收益率纵坐标绘制于右轴
fig,ax = plt.subplots(figsize=(18,12))
ax1 = ax.twinx()
top.plot(ax=ax,lw=3)
top_alpha.plot(ax=ax1,legend=True,color='g',lw=0.8)
ax.legend(loc='upper left')
ax.set_title('中证500成分股' + fac_name + '因子选股多-中证500策略净值走势一览')

top_ret = group_rets[0].dropna()   #多头收益率
table1 = create_table(to_table_nv=top, col='多头净值', ret=top_ret)
#调整一下多头年度策略表格的层次列名
table1.columns = pd.MultiIndex.from_product([['多头表现'],['累计收益率','最大回撤率','年化收益率','年化波动率','信息比率']])

table2 = create_table(to_table_nv=top, col='多头-中证500净值', ret=top_alpha)
#同上
table2.columns = pd.MultiIndex.from_product([['多-中证500表现'],['累计收益率','最大回撤率','年化收益率','年化波动率','信息比率']])
#拼接两个表格并输出
print_table(pd.concat([table1,table2],axis=1))

t1 = time.time()
print('多-中证500策略部分报告完成 耗时 %s 秒' %round((t1-t0),3))
多-中证500策略部分报告开始
.dataframe tbody tr th:only-of-type { vertical-align: middle; } .dataframe tbody tr th { vertical-align: top; } .dataframe thead tr th { text-align: left; } .dataframe thead tr:last-of-type th { text-align: right; }
多头表现 多-中证500表现
累计收益率 最大回撤率 年化收益率 年化波动率 信息比率 累计收益率 最大回撤率 年化收益率 年化波动率 信息比率
year
2013 23.71% 18.32% 25.88% 23.89% 1.083108 -2.86% 6.51% -3.09% 5.54% -0.557488
2014 61.20% 8.28% 63.41% 20.38% 3.111428 2.04% 4.27% 2.10% 5.26% 0.398253
2015 77.47% 52.66% 80.84% 65.04% 1.242906 19.67% 13.78% 20.37% 15.74% 1.294284
2016 4.91% 22.46% 5.08% 31.81% 0.159599 1.56% 5.52% 1.62% 5.12% 0.315250
2017 2.19% 13.21% 2.26% 15.29% 0.147730 0.31% 4.06% 0.32% 3.90% 0.082411
2018 -31.45% 37.68% -32.40% 25.51% -1.269982 -2.02% 6.04% -2.09% 4.80% -0.436375
2019 35.23% 5.22% 308.90% 25.69% 12.023586 -0.78% 4.33% -3.60% 6.59% -0.546041
整体 277.70% 52.66% 24.89% 34.26% 0.726386 19.54% 13.78% 3.03% 7.83% 0.386736
多-中证500策略部分报告完成 耗时 0.108 秒

9. 实证部分 中证500选股 因子选股 多-中证500策略 换手率部分¶

t0 = time.time()
print('换手率报告开始')
#这里计算换手率的结果于研报在最小值上有比较大的出入,可能是采用的方式与研报不同
def cal_turnover(grouped, quantile, hold_period):
    """
    创建策略年度换手率分析表格,包括均值 最大值 最小值 标准差 累计值

    参数:
    ----------
    grouped : pd.DataFrame
        因子选股分档后的df,值为某天某股票所在的分组.
    quantile : int
        要计算的组别,根据之前的分层方式,若计算第k组,输入k-1
    hold_period : int
        调仓周期
    """
    #取出每次调仓时选中的所有股票
    quant_name_sets=grouped.iloc[::hold_period].apply(lambda k: set(k[k == quantile].index), axis=1)
    #作差,得到新一次调仓时相对于之前新增的股票
    new_names = (quant_name_sets - quant_name_sets.shift(1)).dropna()
    #新增股票相对于之前持仓股票数的比例即换手率
    quant_turnover = new_names.apply(lambda x: len(x))/\
                    quant_name_sets.apply(lambda x: len(x))
    quant_turnover.name = quantile
    return quant_turnover

def max_turn(sr):  #最大换手率
    return format(sr.max(),'.2%')
def min_turn(sr):  #最小换手率
    return format(sr.min(),'.2%')
def std_turn(sr):  #换手率标准差
    return format(sr.std(),'.2%')
def mean_turn(sr):  #换手率均值
    return format(sr.mean(),'.2%')
def cumsum_turn(sr):  #换手率累计值
    return sr.sum()
top_turnover = cal_turnover(grouped=grouped, quantile=0, hold_period=hold_period).dropna()
top_turnover.index = top_turnover.index.year
turn_table = top_turnover.groupby(level=0).agg([mean_turn,max_turn,min_turn,std_turn,cumsum_turn])
turn_table.loc['整体',:] = top_turnover.agg([mean_turn,max_turn,min_turn,std_turn,cumsum_turn]).values
turn_table.columns = ['均值','最大值','最小值','标准差','累计值']
name = '中证500选股换手率分年度表现'
print_table(turn_table, name=name)
t1 = time.time()
print('换手率报告完成 耗时 %s 秒' %round((t1-t0),3))
换手率报告开始
.dataframe tbody tr th:only-of-type { vertical-align: middle; } .dataframe tbody tr th { vertical-align: top; } .dataframe thead th { text-align: right; }
中证500选股换手率分年度表现 均值 最大值 最小值 标准差 累计值
date
2013 72.67% 85.90% 56.25% 5.54% 33.426799
2014 70.90% 81.82% 58.11% 5.22% 34.742515
2015 74.48% 87.10% 49.15% 6.33% 36.492764
2016 73.44% 88.46% 56.96% 6.27% 35.984700
2017 73.10% 87.65% 59.26% 5.24% 35.090010
2018 72.62% 82.29% 59.77% 5.08% 35.582840
2019 71.91% 87.63% 61.46% 7.01% 7.910162
整体 72.83% 88.46% 49.15% 5.74% 219.229791
换手率报告完成 耗时 0.263 秒
t_end = time.time()
print('所有报告生成完成 耗时 %s 分钟' %round(((t_end-t_begin)/60),3))
所有报告生成完成 耗时 15.523 分钟

全部回复

0/140

量化课程

    移动端课程