说明:这篇文章来自 2020-2021 年的 WordPress 备份。原始图片附件未随 XML 一起保存,旧服务器图片地址也已失效,因此这里保留正文并移除了失效图片。
价量新因子测试
研究目的:
本文参考海通证券冯佳睿、袁林青撰写的《选股因子系列研究(十八)——价格形态选股因子》,根据研报分析,主要测试了开盘冲高、盘低回升以及均价偏离这三种价格类因子。基于该文章,本文对文章中提到的三种价格类因子进行因子有效性分析,从而实现对股票未来收益的预测,为 alpha 因子的挖掘提供了一定思路。
研究内容:
(1)构建 Neg_pos_ID 因子,然后根据单因子有效性分析的方法,对该因子分别进行因子收益率显著性分析、因子 IC 分析以及分层组合回测分析; (2)构建 CO 因子,然后进行单因子有效性分析,紧接着与市值、反转、换手率、波动率这四个因子进行组合分析; (3)对这两个因子进行深入分析,增加了 6 因子(市值、反转、换手、波动、价值、营业利润同比增长率),分析因子对组合收益的贡献及其预测效果; (4)分析因子敏感性,分别从成交额替代换手率以及分析不同构建期的情况,对 CO 因子进行深入分析。
研究结论:
(1)Neg_pos_ID 在一定程度上是一种价格动量因子,且通过对该因子进行有效性分析来看,该因子有效性较高,有较好的选股效果; (2)CO 因子的选股效果并非线性,然后通过对该因子与市值、反转、换手、 波动、价值等四个因子的组合特征来看,CO 因子与市值负相关, 与反转因子正相关,与换手率因子正相关,与波动率因子正相关,然后分别对这四个因子进行控制,与原始 CO 因子选股相比,前几个组合的月均收益得到了明显提高。 (3)进一步对这两个因子进行分析,首先对 6 因子(市值、反转、换手、 波动、价值、营业利润同比增长率)与 Neg_pos_ID 以及 CO 分解得到的 CO_pos 和 CO_pos 因子进行横截面回归,得知新因子的增加会提高模型的预测能力,换言之,新因子包含了有用的额外信息。 (4)当以成交额代替换手率时,CO 因子的选股效果得到提升;当选择不同的构建期进行分析时,构建期较短时,选股效果更佳。
1 Neg_pos_ID 因子
根据研报内容,提到由 Da,Gurun 和 Warachka 在 2014 年发表的文章《Frog in the pan:Continuous information and momentum》中产生的的因子 PosID 和 NegID 。并在考虑到这两个因子在 A 股市场应用效果不好的情况,对因子进行改进,将上涨和下跌样本同等对待,从而产生了因子 Neg_pos_ID 。 在每个月最后一个交易日,获取过去一年(本文取 250 个交易日)的涨跌幅数据,计算其中收益为正的天数,记为 %pos ,计算其中收益为负的天数,记为 %neg ,然后根据这两个数据,计算新的因子数据,计算方式如下所示:
Neg_pos_ID=%neg−%pos
1.1 因子数据采集 本文设置时间区间为 2010 年至 2017 年,样本范围是全市场所有可交易股票去除 ST 股、上市不足 3 月新股。
from jqdata import *
import datetime
import pandas as pd
import numpy as np
from six import StringIO
import warnings
import time
import pickle
from jqfactor import winsorize_med
from jqfactor import neutralize
from jqfactor import standardlize
import statsmodels.api as sm
from jqfactor import get_factor_values
warnings.filterwarnings("ignore")
#获取指定周期的日期列表 'W、M、Q' def get_period_date(peroid,start_date, end_date): #设定转换周期period_type 转换为周是'W',月'M',季度线'Q',五分钟'5min',12天'12D' stock_data = get_price('000001.XSHE',start_date,end_date,'daily',fields=['close']) #记录每个周期中最后一个交易日 stock_data['date']=stock_data.index #进行转换,周线的每个变量都等于那一周中最后一个交易日的变量值 period_stock_data=stock_data.resample(peroid,how='last') date=period_stock_data.index pydate_array = date.to_pydatetime() date_only_array = np.vectorize(lambda s: s.strftime('%Y-%m-%d'))(pydate_array ) date_only_series = pd.Series(date_only_array) start_date = datetime.datetime.strptime(start_date, "%Y-%m-%d") start_date=start_date-datetime.timedelta(days=1) start_date = start_date.strftime("%Y-%m-%d") date_list=date_only_series.values.tolist() date_list.insert(0,start_date) return date_list #去除上市距beginDate不足 3 个月的股票 def delect_stop(stocks,beginDate,n=30*3): stockList = [] beginDate = datetime.datetime.strptime(beginDate, "%Y-%m-%d") for stock in stocks: start_date = get_security_info(stock).start_date if start_date < (beginDate-datetime.timedelta(days = n)).date(): stockList.append(stock) return stockList
#获取股票池 def get_stock_A(begin_date): begin_date = str(begin_date) stockList = get_index_stocks(‘000002.XSHG’,begin_date)+get_index_stocks(‘399107.XSHE’,begin_date) #剔除ST股 st_data = get_extras(‘is_st’, stockList, count = 1, end_date=begin_date) stockList = [stock for stock in stockList if not st_data[stock][0]] #剔除停牌、新股及退市股票 stockList = delect_stop(stockList, begin_date) return stockList
start = time.clock()
begin_date = '2010-01-01'
end_date = '2017-01-01'
dateList = get_period_date('M', begin_date, end_date)
factorData = {}
for date in dateList:
stockList = get_stock_A(date)
df_date = get_price(stockList, count = 251,end_date=date, frequency = '1d',fields = ['close'])['close']
#pct_change表示当前元素与先前元素的相差百分比,当然指定periods=n,表示当前元素与先前n个元素的相差百分比。默认为1
df_date = df_date.pct_change().iloc[1:] #每天相对于前一天的涨跌幅
temp = df_date[df_date<0] #收益为负的天数
Neg_Pos_ID = temp.count() - (250 - temp.count()) #收益为负的天数减去正的天数
Neg_Pos_ID = standardlize(Neg_Pos_ID, inf2nan=True, axis=0)
factorData[date] = pd.DataFrame(Neg_Pos_ID, columns = ['Neg_Pos_ID'])
elapsed = (time.clock() - start)
print("Time used:",elapsed)
print(factorData)
('Time used:', 319.861577)
1.2 因子有效性检验
1.2.1 因子收益率有效性检验
主要通过 T 检验分析,根据 APT 模型,对历史数据进行进行多元线性回归,从而得到需要分析的因子收益率的 t 值,然后进行以下两个方面的分析: (1)t 值绝对值序列的均值: 之所以要取绝对值,是因为只要 t 值显著不等于 0 即可以认为在当期,因子和收益率存在明显的相关性。但是这种相关性有的时候为正,有的时候为负,如果不取绝对值,则很多正负抵消,会低估因子的有效性; (2)t 值绝对值序列大于2的比例: 检验 |t| > 2 的比例主要是为了保证 |t| 平均值的稳定性, 避免出现少数数值特别大的样本值拉高均值。
def factor_t_test(factorData, Field, begin_date, end_date):
dateList = get_period_date('M', begin_date, end_date)
WLS_params = {}
WLS_t_test = {}
for date in dateList[:-1]:
R_T = pd.DataFrame()
#取股票池
stockList = list(factorData[date].index)
#获取横截面收益率
df_close = get_price(stockList, date, dateList[dateList.index(date)+1], 'daily', ['close'])
if df_close.empty:
continue
df_pchg=df_close['close'].iloc[-1,:]/df_close['close'].iloc[0,:]-1
R_T['pchg'] = df_pchg
#获取因子数据
factor_data = factorData[date][Field]
R_T['factor'] = factor_data
R_T = R_T.dropna()
X = R_T['factor']
y = R_T['pchg']
# WLS回归
wls = sm.OLS(y, X)
result = wls.fit()
WLS_params[date] = result.params[-1]
WLS_t_test[date] = result.tvalues[-1]
t_test = pd.Series(WLS_t_test).dropna()
print 't值序列绝对值平均值: ',np.sum(np.abs(t_test.values))/len(t_test)
n = [x for x in t_test.values if np.abs(x)>2]
print 't值序列绝对值大于2的占比——判断因子的显著性是否稳定',len(n)/float(len(t_test))
print 't值序列均值的绝对值除以t值序列的标准差: ',np.abs(t_test.mean())/t_test.std()
return WLS_t_test
WLS_t_test = factor_t_test(factorData, 'Neg_pos_ID', begin_date, end_date)
输出: t值序列绝对值平均值: 3.68306186203 t值序列绝对值大于2的占比——判断因子的显著性是否稳定 0.666666666667 t值序列均值的绝对值除以t值序列的标准差: 0.379593650011
根据上面结果分析,t 值绝对值序列的均值为 3.68,符合大于 2 的特征,且 t 值绝对值序列大于 2 的比例为 66.67%,根据因子收益率显著性检验的标准,该因子为有效因子。
1.2.2 因子 IC 分析
因子 k 的 IC 值一般是指个股第 T 期在因子 k 上的暴露度与 T + 1 期的收益率的相关系数。当得到因子 IC 值序列后,根据以下分析方法进行计算: (1)IC 值序列的均值及绝对值均值: 判断因子有效性; (2)IC 值序列的标准差:判断因子稳定性; (3)IC 值系列的均值与标准差比值(IR):分析分析有效性 (4)IC 值序列大于零(或小于零)的占比:判断因子效果的一致性。
import scipy.stats as st
def factor_IC_analysis(factorData, Field, begin_date, end_date, rule = 'normal'):
dateList = get_period_date('M', begin_date, end_date)
IC = {}
R_T = pd.DataFrame()
for date in dateList[:-1]:
stockList = list(factorData[date].index)
df_close = get_price(stockList, date, dateList[dateList.index(date)+1], 'daily', ['close'])
if df_close.empty:
continue
df_pchg=df_close['close'].iloc[-1,:]/df_close['close'].iloc[0,:]-1
R_T['pchg']=df_pchg
factor_data = factorData[date][Field]
factor_data = standardlize(factor_data, inf2nan = True,axis = 0) #inf:infinite无限大
R_T['factor'] = factor_data
R_T = R_T.dropna()
if rule == 'normal':
# IC[date] = st.pearsonr(R_T.pchg, R_T['factor'])[0]
IC[date] = st.pearsonr(R_T['pchg'], R_T['factor'])[0]
elif rule == 'rank':
# IC[date] = st.pearsonr(R_T.pchg.rank(), R_T['factor'].rank())[0]
IC[date] = st.pearsonr(R_T['pchg'].rank(), R_T['factor'].rank())[0]
IC = pd.Series(IC).dropna()
print('IC值序列均值大小', IC.mean())
print('IC值序列绝对值的均值大小', np.mean(np.abs(IC)))
print('IC值序列的标准差', IC.std())
print('IR比率', IC.mean()/IC.std())
n = [x for x in IC.values if x>0]
print("IC值序列大于零的比例", len(n)/float(len(IC)))
factor_IC_analysis(factorData, 'Neg_Pos_ID', begin_date, end_date)
输出: IC值序列均值大小 -0.043363427016258914 IC值序列绝对值的均值大小 0.07778510125754556 IC值序列的标准差 0.08850819835610833 IR比率 -0.4899368399951869 IC值序列大于零的比例 0.36904761904761907
上表给出每月因子与次月收益的相关系数 IC,IC均值为-0.043,IR比率为-0.49.在2010到2017年间,IC值小于0的占比为0.63,总体倾向为负,表明当月因子值越小,次月收益越高、
1.2.3 分层组合回测分析
为了进一步分析因子有效性,本文对该因子进行分层回测分析,策略步骤如下所示: (1)在每个月最后一个交易日,统计全 A 股因子的值; (2)根据因子值按照从小到大的顺序排序,并将其等分为 5 组 (3)每个调仓日对每组股票池进行调仓交易,从而获得 5 组股票组合的月均收益 注:设置每次交易手续费为千 2
def GetPchg(i, Field):
pchg = []
cost = 0.002
for date in dateList[:-1]:
tempData = factorData[date].copy()
tempData = tempData.sort_values(Field)
stockList = list(tempData.index)
stocks = stockList[int(len(stockList)*(i-1)/5):int(len(stockList)*i/5)]
df_close = get_price(stocks, date, dateList[dateList.index(date)+1], 'daily', ['close'])['close']
pchg.append(np.mean(df_close.iloc[-1] / df_close.iloc[0] -1) - cost)
return pchg
tempPchg = []
for i in range(1,6):
tempPchg.append(np.mean(GetPchg(i, 'Neg_Pos_ID')))
fig = plt.figure(figsize=(20,8))
ax = fig.add_subplot(111)
plt.bar(range(len(tempPchg)), tempPchg, 0.3)
# 添加图例
plt.legend()
plt.show()
2 CO 因子
根据研报内容,由 Suk,Sonya 和 Sang 在 2014 年发表的文章 《Overreaction and Stock Return Predictability》中提出了持续过度反应度量指标 CO。研报分析由于 A 股和美国股市的投资者结构存在本质区别,因此按照日度形式计算该指标。计算方式如下所示:在每个月最后一个交易日,分析过去一个月的个股的涨跌情况和换手率情况,然后根据以下公式实现 CO 指标的计算。
用上涨时的交易活跃度(换手率、或成交额)与下跌时交易活跃度之差表示
2.1 因子数据采集
本文设置时间区间为 2010 年至 2017 年,样本范围是全市场所有可交易股票去除 ST 股、上市不足 3 月新股。
start = time.clock()
begin_date = '2010-01-01'
end_date = '2017-01-01'
# 市值 最近一个月的股价增长率 20日平均换手率 20日夏普比率(每承受一单位的总风险,会产生多少超额的报酬)
Fields = ["market_cap", "Price1M", "VOL20", "sharpe_ratio_20"]
dateList = get_period_date('M',begin_date, end_date)
for date in dateList:
stockList = get_stock_A(date)
#取前一个月的数据
df_close = get_price(stockList, count = 21, end_date = date, frequency='1d', fields=['close'])['close']
df_pchg = df_close.pct_change().iloc[1:]
#df_pchg.index为日期(date前一个月的开市日)
temp_turnover = pd.DataFrame(index = df_pchg.index, columns = stockList)
for day in df_pchg.index:
#get_fundamentals:查询财务数据 code股票代码 turnover_ratio换手率
#get_fundamentals返回一个dataframe,columns索引为要查询的字段
df_turnover = get_fundamentals(query(valuation.code, \
valuation.turnover_ratio).filter\
(valuation.code.in_(stockList)),\
date = day)
#设index为code,股票代码
df_turnover = df_turnover.set_index(['code'])
#将df_turnover的turnover_ratio(换手率)列复制到temp_turnover的day行
temp_turnover.loc[day] = df_turnover['turnover_ratio']
tempSum = 0
for i in range(len(temp_turnover)): #len(temp_turnover)为行数(前一个月开市天数)
tempSum += i+1
for i in range(len(temp_turnover)): #乘以Wi(CO公式)
temp_turnover.iloc[i] = temp_turnover.iloc[i] * float(i+1) / tempSum
CO = temp_turnover[df_pchg>0].sum() + (-1*temp_turnover[df_pchg<0]).sum() #CO公式
#获取因子值 返回一个 dict: key 是因子名称, value 是 pandas.dataframe。
#dataframe 的 index 是日期, column 是股票代码
temp = get_factor_values(stockList, Fields, end_date = date, count = 1)
factorData[date]["CO"] = CO
factorData[date]["market_cap"] = temp["market_cap"].T
factorData[date]["Price1M"] = temp["Price1M"].T
factorData[date]["VOL20"] = temp["VOL20"].T
factorData[date]["sharpe_ratio_20"] = temp["sharpe_ratio_20"].T
for Field in Fields:
#中性化函数:将序列与 how 中输入的相关变量做回归取残差,去除序列与相关变量的相关性
factorData[date][Field+"_neu"] = neutralize(factorData[date]["CO"], how=[Field], date=date, axis=0)
factorData[date] = standardlize(factorData[date], inf2nan=True, axis=0)
elapsed = (time.clock() - start)
print("Time used:",elapsed)
print(factorData)
2.2 因子有效性检验
2.2.1 因子收益率显著性检验
#见1.2.1
WLS_t_test = factor_t_test(factorData, 'CO', begin_date, end_date)
输出: t值序列绝对值平均值: 3.1390689096177615 t值序列绝对值大于2的占比——判断因子的显著性是否稳定 0.5952380952380952 t值序列均值的绝对值除以t值序列的标准差: 0.5315264180334107
t值绝对值平均值为3.14, 符合大于2的特征,且t值序列绝对值大于2的占比为59.5%,根据因子收益率显著性检验的标准,该因子为有效因子
2.2.2 因子 IC 分析
#见1.2.2
factor_IC_analysis(factorData, 'CO', begin_date, end_date)
输出: IC值序列均值大小 -0.04150199179395132 IC值序列绝对值的均值大小 0.07374672739993604 IC值序列的标准差 0.08452195586246326 IR比率 -0.4910202487680792 IC值序列大于零的比例 0.27380952380952384
IC值序列均值大小为-0.041,IR比率达到了-0.49.IC值小于零的占比为0.73.总体倾向为负,表明当月因子值越小,次月收益越高。
2.2.3 分层组合回测分析
为了进一步分析因子有效性,本文对该因子进行分层回测分析,策略步骤如下所示: (1)在每个月最后一个交易日,统计全 A 股因子的值; (2)根据因子值按照从小到大的顺序排序,并将其等分为 5 组 (3)每个调仓日对每组股票池进行调仓交易,从而获得 5 组股票组合的月均收益 注:设置每次交易手续费为千 2
tempPchg = []
for i in range(1, 6):
#GetPchg见1.2.3 返回第i组的平均收益率
tempPchg.append(np.mean(GetPchg(i, 'CO')))
fig = plt.figure(figsize= (20,8))
ax = fig.add_subplot(111)
plt.bar(range(len(tempPchg)), tempPchg, 0.3)
plt.legend()
plt.show()
从上图来看,5个组合的收益与CO指标值呈现出较为明显的负相关性,但在前两组间负单调性并不明显。从组合1和组合5来看,组合1 的越军收益显然高于5,但是考虑单调性较差,因此本文对该因子进行更深入的分析。
2.3因子组合特征
本文对该因子进行更深入的分析,在每个月最后一个交易日,分别采集个股的市值、反转、换手率、波动这四个因子,根据原始因子 CO 的值分析该因子与这四个因子的相关性,具体分析如下。
Fields = ["market_cap", "Price1M", "VOL20", "sharpe_ratio_20"]
def GetCorr(i):
resultCorr = pd.DataFrame()
for date in dateList:
tempData = factorData[date].copy()
tempData.sort_values('CO') #升序
temp = tempData.iloc[int(len(tempData)*(i-1)/5):int(len(tempData)*(i)/5),:]
tempCorr = temp.corr()["CO"]
#corr()返回dataframe,columns为各个因子,index也为这些因子,value为横轴与纵轴因子的相关系数
resultCorr[date] = tempCorr[Fields] #co与各因子的相关系数代表相关性
return resultCorr #index为除co的因子,columns为date,values为co与各因子的相关系数
# GetCorr(1)
result = pd.DataFrame()
for i in range(1,6):
result[i] = GetCorr(i).mean(axis =1) #行平均值(所有日期的系数的平均值)
result
由上可以看出,CO 因子与其余因子呈现出一定的相关性,具体如下所示: (1)CO 因子与市值负相关,且 CO 值较小的组合对应股票市值较大,因此,如果对市值因子进行控制,那么能够实现对 CO 因子的优化; (2)CO 因子与反转因子正相关,且相关性较高,CO 最小的股票前期跌幅最大,因此,如果对反转因子进行控制,那么能够实现对 CO 因子的优化; (3)CO 因子与换手率因子正相关,因此,如果对换手率因子进行控制,那么能够实现对 CO 因子的优化; (4)CO 因子与波动率因子正相关,CO 最小的股票波动率最大,因此,如果对反转因子进行控制,那么能够实现对 CO 因子的优化。
实现了上述分析后,接下来对 CO 因子进行这四种因子的控制后的情况进行分析。
Fields = ["market_cap", "Price1M", "VOL20", "sharpe_ratio_20"]
resultPchg = pd.DataFrame(index = range(1,6), columns = Fields)
for i in range(1,6):
for Field in Fields:
resultPchg.loc[i, Field] = np.mean(GetPchg(i, Field))
resultPchg
上表展示分别对这四种因子进行控制后,进行分层回测的月均收益表,基本上符合随着 CO 增加,组合收益率呈现先增加后减小的态势。但是与原始 CO 因子选股相比,前几个组合的月均收益得到了明显提高。
3因子深入研究
3.1横截面收益率回归
前文内容对因子 Neg_pos_ID 和因子 CO 进行了分析,根据研报介绍,参考因子 Neg_pos_ID 的构建方式,对因子 CO 进行分解为 CO_pos 和 CO_neg。当 CO > 0 时则将该股票的 CO_pos 设为 CO,CO_neg 设为 0;当 CO < 0 时将该股票的 CO_neg 设为 CO,CO_pos 设为 0。 接下来根据研报内容,进行横截面收益率回归分析,具体回归方程如下所示:
$ r_{i,t} = \alphat + \beta{1,t}Size{i,t} + \beta{2,t}Pret{i,t} + \beta{3,t}Turn{i,t} + \beta{4,t}Vol{i,t} + \beta{5,t}PB{i,t} + \beta{6,t}YOY_OP{i,t} + \epsilon{i,t} $
其中,Size 表示市值,Pret 表示反转,Turn 表示换手率,Vol 表示波动率,PB 表示价值,YOY_OP 表示营业利润同比增长率。 接下来根据研报分析情况实现横截面收益率回归,并进行分析。
for date in dateList:
stockList = list(factorData[date].index)
#pb_ratioa为市盈率 indicator.inc_operation_profit_year_on_year营业利润同比增长率
df = get_fundamentals(query(valuation.code, valuation.pb_ratio,\
indicator.inc_operation_profit_year_on_year).\
filter(valuation.code.in_(stockList)), date = date)
df = df.set_index(['code'])
factorData[date]['pb_ratio'] = df['pb_ratio']
factorData[date]['inc_operation_profit_year_on_year'] = df['inc_operation_profit_year_on_year']
factorData[date]['CO_neg'] = [i if i<0 else 0 for i in factorData[date]['CO']]
factorData[date]['CO_pos'] = [i if i>0 else 0 for i in factorData[date]['CO']]
factorData[date] = standardlize(factorData[date], inf2nan = True, axis = 0)
def LRData(factorData, Fields):
result_t = pd.DataFrame()
result_params = pd.DataFrame()
result_r2 = []
for date in dateList[:-1]:
R_T = pd.DataFrame()
stockList = list(factorData[date].index)
df_close = get_price(stockList, date, dateList[dateList.index(date)+1], 'daily', ['close'])
if df_close.empty:
continue
df_pchg = df_close['close'].iloc[-1,:]/df_close['close'].iloc[0,:] - 1
R_T['pchg'] = df_pchg
factor_data = factorData[date]
R_T[Fields] = factor_data[Fields]
R_T = R_T.dropna()
x = R_T[Fields]
y = R_T['pchg']
wls = sm.OLS(y, x)
result = wls.fit()
tvalues = result.tvalues
params = result.params
r2 = result.rsquared_adj #校正决定系数用于判定一个多元线性回归方程的拟合程度(0-1)越大越好
result_t[date] = tvalues
result_params[date] = params
result_r2.append(r2)
result = pd.DataFrame()
result['t统计量'] = result_t.mean(axis = 1)#行平均值
result['参数估计'] = result_params.mean(axis = 1)
print("调整 r2 值: ", np.nanmean(result_r2))
return result.T
Fields = ["market_cap", "Price1M", "VOL20", "sharpe_ratio_20", "pb_ratio", "inc_operation_profit_year_on_year"]
LRData(factorData, Fields)
调整 r2 值: 0.03425017596289862
Fields = ["market_cap", "Price1M", "VOL20", "sharpe_ratio_20", "pb_ratio", \
"inc_operation_profit_year_on_year", "Neg_Pos_ID"]
LRData(factorData,Fields)
调整 r2 值: 0.0423381606029065
Fields = ["market_cap", "Price1M", "VOL20", "sharpe_ratio_20", "pb_ratio", \
"inc_operation_profit_year_on_year", "CO_neg", "CO_pos"]
LRData(factorData,Fields)
调整 r2 值: 0.03736065330273979
Fields = ["market_cap", "Price1M", "VOL20", "sharpe_ratio_20", "pb_ratio", \
"inc_operation_profit_year_on_year", "Neg_Pos_ID", "CO_neg", "CO_pos"]
LRData(factorData, Fields)
调整 r2 值: 0.04523055137630058
以上四个表格分别对应研报中表格中提到的方程1 - 方程4,从以上表格数据中可以得到以下结论: (1)包含市值、反转、换手率、波动率、价值、YOY_OP 的 6 因子模型,在 2010-2017 年的调整 R 方为 3.42%,其中,换手率因子(VOL20)的因子收益率最高,回归得到的系数为 -0.0039,检验 t 值为 -1.99;价值因子(PB)与营业利润同比增长率因子 (inc_operation_profit_year_on_year)因子收益率最低,在整个样本期间效果不显著。 (2)从第二个表格来看,调整 R 方略微增加,从 3.42% 增加到 4.23%,增加 Neg_pos_ID 因子后,模型预测能力得到提高;从第三个表格来看,与表格一对比,调整 R 方从 3.42% 增加到 3.76%,但是略低于表格二,可见 CO 两个因子选股效果低于 Neg_pos_ID 因子。 (3)从表格 4 来看,其调整 R 方值最大,达到了 4.55%。从 t 统计量和因子收益率来看,因子 Neg_pos_ID 预测能力最好,其次是换手率因子以及反转因子。
3.2 因子其他计算形式
参考研报,分别对以下两方面进行分析: (1)以成交额代替换手率计算 CO 指标; (2)采用不同时间长度计算因子值。
3.2.1 以成交额代替换手率计算 CO
start = time.clock()
begin_date = '2010-01-01'
end_date = '2017-01-01'
dateList = get_period_date('M',begin_date, end_date)
for date in dateList:
stockList = get_stock_A(date)
df_data = get_price(stockList, count = 21, end_date = date, frequency='1d', fields=['close', 'money'])
df_pchg = df_data["close"].pct_change().iloc[1:]
temp_amount = df_data["money"].iloc[1:]
tempSum = 0
for i in range(len(temp_amount)):
tempSum += i+1
for i in range(len(temp_amount)):
temp_amount.iloc[i] = temp_amount.iloc[i] * float(i+1) / tempSum
CO = temp_amount[df_pchg>0].sum() + (-1*temp_amount[df_pchg<0]).sum()
factorData[date]["CO_amount"] = CO
factorData[date]['CO_amount_neg'] = [i if i<0 else 0 for i in factorData[date]['CO_amount']]
factorData[date]['CO_amount_pos'] = [i if i>0 else 0 for i in factorData[date]['CO_amount']]
factorData[date] = standardlize(factorData[date], inf2nan=True, axis=0)
elapsed = (time.clock() - start)
print("Time used:",elapsed)
Fields = ["market_cap", "Price1M", "VOL20", "sharpe_ratio_20", "pb_ratio", "inc_operation_profit_year_on_year", "Neg_pos_ID", "CO_amount_neg", "CO_amount_pos"]
LRData(factorData, Fields)
调整 r2 值: 0.0482490866918
| 指标 | market_cap | Price1M | VOL20 | sharpe_ratio_20 | pb_ratio | inc_operation_profit_year_on_year | Neg_pos_ID | CO_amount_neg | CO_amount_pos |
|---|---|---|---|---|---|---|---|---|---|
| t 统计量 | -0.293751 | -1.142062 | -1.946750 | 0.673835 | 0.111308 | 0.168377 | -1.400564 | 1.035387 | -1.434746 |
| 参数估计 | -0.000868 | -0.003306 | -0.004074 | 0.002023 | -0.000085 | 0.000148 | -0.003454 | 0.002783 | -0.004192 |
根据上述结果,参考利用换手率计算 CO 因子的结果,采用成交额计算 CO 因子,在以下方面得到了改进: (1)从调整 R 方来看,采用成交额的方法有了明显增加; (2)从 CO_neg 和 CO_pos 这两个因子来看,CO 因子收益率得到了提升。
3.2.2 构建期长短对选股效果的影响
将dataList换成以2W、Q为周期的 求出相应的factorData
Fields = ["market_cap", "Price1M", "VOL20", "sharpe_ratio_20", "pb_ratio", \
"inc_operation_profit_year_on_year", "Neg_Pos_ID", "CO_neg", "CO_pos"]
LRData(factorData, Fields)
以上面的代码求出r2和每个因子对应的t值和参数,得到结论
上面结果分别实现了以周为周期和以季度为周期的因子分析结果,与月度为周期的分析结果相比,可以得到如下结论: (1)构建期为 2 周时,调整 R 方值达到了最大,为 4.25%,构建期为季度时,调整 R 方值最小,为 3.42%,可见构建期较短时能够获得更好地预测结果; (2)构建期较短时,CO 因子的因子收益率更高,且不管构建期多长,换手率因子和反转因子均具有较好的因子预测性。
结论
海通金工本篇报告的研究主要测试了两种价量类新因子的选股效果,即 Neg_pos_ID 和 CO。其中前者是下跌天数与上涨天数之差的衡量,后者是上涨时的换手率与下跌时的换手率之差。 本文通过上述分析,得到了以下结论: (1)Neg_pos_ID 在一定程度上是一种价格动量因子,且通过对该因子进行有效性分析来看,该因子有效性较高,有较好的选股效果; (2)CO 因子的选股效果并非线性,然后通过对该因子与市值、反转、换手、 波动、价值等四个因子的组合特征来看,CO 因子与市值负相关, 与反转因子正相关,与换手率因子正相关,与波动率因子正相关,然后分别对这四个因子进行控制,与原始 CO 因子选股相比,前几个组合的月均收益得到了明显提高。 (3)进一步对这两个因子进行分析,首先对 6 因子(市值、反转、换手、 波动、价值、营业利润同比增长率)与 Neg_pos_ID 以及 CO 分解得到的 CO_pos 和 CO_pos 因子进行横截面回归,得知新因子的增加会提高模型的预测能力,换言之,新因子包含了有用的额外信息。 (4)当以成交额代替换手率时,CO 因子的选股效果得到提升;当选择不同的构建期进行分析时,构建期较短时,选股效果更佳。