#基本思路:将T时刻的因子值和T到t+1区间的股票收益率进行相关性分析,寻求较高的正相关性
#实际因子测试过程中,常用spearman相关系数(秩相关),相较于peason系数,对异常值有更好的抗干扰能力
#用spearman计算出来的IC叫RankIC
import pandas as pd
import numpy as np
import alphalens
import matplotlib.pyplot as plt
import pylab as plb
import statsmodels.api as sm
%matplotlib inline
#数据获取
tpd = pd.read_hdf('total_tpd.h5', key='data')
tpd.sort_values(['secucode', 'data_date'], inplace=True)
ind_code2name = pd.read_csv('ind_name.csv', dtype={'code':'str'}).set_index('code').to_dict()['name']
-----------------------------------------------------------------------------------
#alphalens函数格式
#alphalens参数预处理:factor
#factor数据预处理
raw_factor = pd.read_hdf('./factor/size.h5')
raw_factor.data_date = pd.to_datetime(raw_factor.data_date)
raw_factor.sort_values(['data_date', 'secucode'], inplace=True)
column_name = 'size'
#去极值、标准化、中性化
def winsor(ser):
max_value, min_value = ser.mean() + 3 * ser.std(), ser.mean() - 3 * ser.std()
return pd.Series([max_value if item > max_value else (min_value if item < min_value else item) for item in ser])
def standize(ser):
return (ser - ser.mean())/ser.std()
def ind_neuted(factor_df, factor_name):
result = sm.OLS(factor_df[factor_name], factor_df[list(factor_df.ind_code.unique())], hasconst=True).fit()
return result.resid
win_factor_value = raw_factor.groupby('data_date').apply(lambda x:winsor(x[column_name]))
raw_factor['factor_wined'] = win_factor_value.values
raw_factor['factor_std'] = raw_factor.groupby('data_date').apply(lambda x:standize(x['factor_wined'])).values
neuted_factor_df = pd.merge(raw_factor,tpd[['data_date', 'secucode', 'ind_code']], on=['data_date', 'secucode'],how='left').sort_values(['data_date', 'secucode'])
neuted_factor_df = pd.concat([neuted_factor_df, pd.get_dummies(neuted_factor_df['ind_code'])], axis=1)
neuted_factor_df.dropna(inplace=True)
neuted_factor_df['factor_neuted'] = ind_neuted(neuted_factor_df, 'factor_std')
#对市值因子进行因子方向处理(市值越小,得分越高)
factor_df = neuted_factor_df[['data_date', 'secucode', 'factor_neuted']]
factor_flag = -1
factor_df = factor_df.set_index(['data_date', 'secucode'])
factor_df = factor_flag * factor_df
#alphalens参数预处理:price
#价格数据:通过日收益率计算出价格指数
tpd['close'] = tpd.groupby('secucode')['daily_return'].expanding().apply(lambda x: np.prod(x+1)).values
price_df = tpd[['data_date' ,'secucode','close']].pivot(index='data_date', columns='secucode',values='close')
#alphalens参数预处理:groupby
#股票的行业信息
#可传入字典或多维index的Series
#传入字典格式,则在整个回测的跨度中股票所属行业不会改变
#传入多维index的Series,则可以接受股票在回测区间内行业信息发生变化
group_df = tpd[['data_date','secucode','ind_code']].sort_values(['data_date','secucode']).set_index(['data_date','secucode'])
#alphalens参数预处理:Binning_by_group(设置是否按行业分别计算股票的分位数)
#如果因子已经进行过行业中性化处理,这一参数通常设置为“False”
#alphalens参数预处理:quantiles(分组测试中分组的数量,原则上按照每组股票数量相等来划分)
#alphalens参数预处理:bins(对因子值进行分组,仅以因子值为标准,不会考虑每个分组的股票数量)
#alphalens参数预处理:period(回测的调仓周期,单位为day)
#alphalens参数预处理:filter_zscore(针对收益的过滤器,用来调整一日或多日收益率的异常值)
#alphalens参数预处理:groupby_labels(传入行业名称增加可读性,格式为“行业代码: 行业名称”)
#alphalens参数预处理:max_loss(数据的可允许丢失量,设置的值为0~1的浮点数,损失比例大于设定的值时函数会抛出异常)
#在处理数据的过程中难免会需要丢掉一些异常值、缺失值等
#alphalens参数预处理:zero_aware(设置为True时,在计算分组时会首先将因子分为正负两组,再在组内进行分组,最后合并)
#要求quantiles或bins传入的参数为整型
#alphalens参数预处理:cumulative_returns(未来N日累计收益率)
#参数设置完成,调用alphalens函数
factor_data = alphalens.utils.get_clean_factor_and_forward_returns(factor_df, price_df, group_df['ind_code'],
quantiles=10,groupby_labels=ind_code2name)
-----------------------------------------------------------------------------------
#因子IC分析
#factor_data:get_clean_factor_and_forward_returns函数返回的数据集
#group_neutral:收益率是否进行行业中性化,即股票收益率是否减去行业均值
#by_group:True时,额外增加一张每个行业的IC分组收益柱状图,可用于观察在不同行业中因子是否具有差异性
#调用IC分析函数
alphalens.tears.create_information_tear_sheet(factor_data,group_neutral=False,by_group=False)
#IC的时间序列图形显示,1,5,10日的IC值表现都不错,在正区间的概率比较高
#Q-Q图观察:对于某一局部,如果图中的散点斜率大于1,则观察的分布相对于正态分布而言离散性更大,更加具有肥尾特征;反之则分布离散性更小,具有瘦尾特征
#完美的因子IC分布,应当趋近与正态分布
-----------------------------------------------------------------------------------
#收益率分析
#factor_data:get_clean_factor_and_forward_returns函数返回的数据集
#long_short:True时,将采用多空组合收益率计算方法(计算分组收益时扣除每组的平均收益率)
#group_neutral: True时,对收益率进行行业中性化
#by_group:True时,给出分行业测试的结果
alphalens.tears.create_returns_tear_sheet(factor_data, long_short=False, group_neutral=False, by_group=False)
#好的因子应具有高Alpha,低Beta,同时Top组合Bottom组的差距要尽可能大
#Factor Weighted Portfolio Cumulative Return:因子加权组合的累计收益率(使用因子加权的方法构建一个全市场的组合进行回测得到的累计收益率)
#通常实践是不会交易全市场的股票而只会交易头尾,但因子加权组合的累计收益率可以展示整体上这个因子的预测性是否足够强
#Cumulative Return by Quantile:分组别的累计收益率(好的因子会体现出筛选性,也就是分组的收益率曲线分化明显,有较强的差异性)
#Top Minus Bottom Quantile Mean Return(top组与bottom组的平均收益率差值,越大越好,且收益率差值曲线方向性越稳定越好)
-----------------------------------------------------------------------------------
#因子的换手率分析
#factor_data: get_clean_factor_and_forward_returns函数返回的数据集
#turnover_periods:设置换手率的周期(与get_clean_factor_and_forward_returns函数设置的调仓周期一致)
alphalens.tears.create_turnover_tear_sheet(factor_data, turnover_periods=['1D', '5D', '10D'])
#理想的因子值应对比较稳定,从而确保有一个较低的换手率,降低交易成本
#因子的自相关性越高,因子越稳定,换手率也会更低
股票多因子模型实战:Python核心代码解析.笔记04(单因子的测试分析)
于 2024-04-27 14:12:44 首次发布