华泰单因子测试之估值类因子
pe(price-to-earning ratio) 市盈率,PE = 流通市值/最近4个季度的净利润;最近 4 个季度的净利润按如下方法计算: 1-4 月,最近 4 个季度的净利润=上一年度前 3 季度累计净利润+上上一年度的四季度净利润;5-8 月,最近 4 个季度的净利润=当年 1 季度净利润+前 1 年净利润-前 1 年 1 季度净利润;9-10 月,最近 4 个季度的净利润=当年中期净利润+前 1 年净利润-前 1 年中期净利润;11-12 月,最近 4 个季度的净利润=当年前 3 季度累计净利润+上1年年度净利润-上 1 年前 3 季度累计净利润。
pb(price-to-book ratio) 市净率,按照自由流通量加权的净资产倍率。 PB = 流通市值/按照流通市值计算的净资产;按照流通市值计算的净资产 = 最新净资产*流通股本/总股本。
一、估值因子在 A 股市场实证分析
1、估值因子及其描述
2、估值因子在不同行业间存在显著差异(以PE为例,聚宽就只有申万一级的,将就用,2015-01-22开始的数据肯定没问题。)
def get_sw1_valuation(start_date=None, end_date=None):
#2014-02-22之后申万一级行业28个
code = jd.get_industries(name='sw_l1',date='2014-02-22').index.tolist()
days = jd.get_trade_days(start_date,end_date)
index = jd.finance.run_query(jd.query(jd.finance.SW1_DAILY_VALUATION).filter(
jd.finance.SW1_DAILY_VALUATION.date=='2014-02-22'
).limit(1)).columns.tolist()
data = pd.DataFrame(columns = index)
for day in days:
df=jd.finance.run_query(jd.query(jd.finance.SW1_DAILY_VALUATION).filter(
jd.finance.SW1_DAILY_VALUATION.code.in_(code),
jd.finance.SW1_DAILY_VALUATION.date==day
))
name1 = set(list(map(lambda x:x[:-1],jd.get_industries(name='sw_l1',date='2014-02-22').name.tolist())))
name2 = set(df.name.tolist())
if not name1-name2:
data = pd.concat([data, df], axis = 0, sort=False)
return data
'''
df = get_sw1_valuation(start_date='2015-01-01',end_date='2020-05-01')
df = df.set_index(['date']).drop(['id'], axis=1)
df.to_csv('D:\\spyder_code\\jqfactor_analyzer01\\华泰单因子测试之估值类因子\\申万一级行业估值数据.csv',\
encoding='utf_8_sig')
'''
def plot_fig(factor):
plt.rcParams['font.sans-serif'] = ['SimHei']
index = data.unstack('name')[factor].index
for i in index:
fig, ax = plt.subplots(1,1,figsize=(14,6))
x=data.unstack('name')[factor].loc[i,:].index
height=data.unstack('name')[factor].loc[i,:].values
ax.set_title(i)
ax.bar(x=x,height=height)
ax.plot(data.unstack('name')[factor].loc[i,:])
ax.grid(True)
plt.xticks(rotation=30) # 设置x轴标签旋转角度
fig.savefig('D:\\spyder_code\\jqfactor_analyzer01\\华泰单因子测试之估值类因子'\
+'\\%s_%s.png'%(factor,i.strftime('%Y-%m-%d')))
if __name__ == '__main__':
df = pd.read_csv('D:\\spyder_code\\jqfactor_analyzer01\\华泰单因子测试之估值类因子\\申万一级行业估值数据.csv',\
index_col=['date'])
df.index = pd.to_datetime(df.index)
data = df[['pe','pb']].groupby(df['name']).resample('Y',how='mean')
plot_fig('pe')
3、市值对估值因子有显著影响(采用行业中性分层法--在每个一级行业内部按市值大小将所有个股均分为N组,再将不同行业间对应的组拼合到一起,全市场形成N个分组。是为了消除行业对估值因子的影响,仅关注市值对估值因子的影响。)尚未实现
二、ep因子获取和处理
1、因子值获取
'''
获取中证500估值类因子数据
1、市盈率(PE, TTM) 每股市价为每股收益的倍数,反映投资人对每元净利润所愿支付的价格,
用来估计股票的投资报酬和风险 市盈率(PE,TTM)=(股票在指定交易日期的收盘价 *
截止当日公司总股本)/归属于母公司股东的净利润TTM。
'''
#获取数据主函数
#输入股票池、指标名称、开始日期、结束日期
#返回行标签为日期,列表签为股票名称的dataframe表格
'''
为了适应在jqfactor_analyzer上分析,保持股票种类不变。
'''
def get_factor_data(stockPool, factor,date_start, date_end):
#获取股票池函数
def get_stock(stockPool, begin_date):
if stockPool == 'HS300':#用于获取沪深300股票池
stockList = jd.get_index_stocks('000300.XSHG', begin_date)
elif stockPool == 'ZZ500':#用于获取中证500股票池
stockList = jd.get_index_stocks('399905.XSHE', begin_date)
elif stockPool == 'ZZ800':#用于获取中证800股票池
stockList = jd.get_index_stocks('399906.XSHE', begin_date)
elif stockPool == 'A':#用于获取全部A股股票池
stockList = jd.get_index_stocks('000002.XSHG', begin_date) + jd.get_index_stocks('399107.XSHE', begin_date)
else:#自定义输入股票池
stockList = stockPool
return stockList
#从财务库获取数据
def get_factor_data1(factor,stock, date):
if factor in val:
q = jd.query(jd.valuation).filter(jd.valuation.code.in_(stock))
df = jd.get_fundamentals(q, date)
elif factor in bal:
q = jd.query(jd.balance).filter(jd.balance.code.in_(stock))
df = jd.get_fundamentals(q, date)
elif factor in cf:
q = jd.query(jd.cash_flow).filter(jd.cash_flow.code.in_(stock))
df = jd.get_fundamentals(q, date)
elif factor in inc:
q = jd.query(jd.income).filter(jd.income.code.in_(stock))
df = jd.get_fundamentals(q, date)
elif factor in ind:
q = jd.query(jd.indicator).filter(jd.indicator.code.in_(stock))
df = jd.get_fundamentals(q, date)
df.index = df['code']
data = pd.DataFrame(index = df.index)
data[date] = df[factor] #date是函数的参数,转置索引=列名,使得date(时间)成为索引
return data.T
#获取日期列表
date_list = jd.get_trade_days(start_date = date_start, end_date = date_end)
#空df预备存储数据
data = pd.DataFrame(columns = get_stock(stockPool,begin_date=date_list[0]))
#获取五张财务基础所有指标名称
val = jd.get_fundamentals(jd.query(jd.valuation).limit(1)).columns.tolist()
bal = jd.get_fundamentals(jd.query(jd.balance).limit(1)).columns.tolist()
cf = jd.get_fundamentals(jd.query(jd.cash_flow).limit(1)).columns.tolist()
inc = jd.get_fundamentals(jd.query(jd.income).limit(1)).columns.tolist()
ind = jd.get_fundamentals(jd.query(jd.indicator).limit(1)).columns.tolist()
all_columns = val+bal+cf+inc+ind
all_stocks = get_stock(stockPool, date_list[0])
#循环时间列表获取指标数据
for date in date_list:
'''
#获取股票池
all_stocks = get_stock(stockPool, date)
'''
#获取因子数据
if factor in all_columns: #可以从财务库直接取到因子值的因子
data_temp = get_factor_data1(factor,all_stocks, date)
else: #可以从因子库直接取到因子值的因子
try:
data_temp = jd.get_factor_values(all_stocks, [factor], end_date = date, count = 1)[factor]
except:
print('系统暂不能获取该因子,请获取其他因子')
break
data = pd.concat([data, data_temp], axis = 0, sorted=False)
return data
'''
df_pe=get_factor_data('ZZ500', 'pe_ratio','2016-01-01', '2020-05-01')
df_pe.to_csv('D:\\spyder_code\\jqfactor_analyzer01\\华泰单因子测试之估值类因子\\pe.csv',\
encoding='utf_8_sig')
'''
df_pe = pd.read_csv('D:\\spyder_code\\jqfactor_analyzer01\\华泰单因子测试之估值类因子\\pe.csv',\
index_col='Unnamed: 0')
上述方法存在的问题:
a)指数成分股调整问题
在研报中:虽然回溯区间是2005-04-29到2016-08-31但截面期是每个月的最后一个交易日。所以数据也就只有一百多个截面期。
在jqfactor_analyaer:截面期是滚动的(固定步长,例如30天)。
这两种方式比较:前一种可以和对应指数比较,毕竟成分股没有变化,但是数据量不够。在jqfactor_analyzer中,股票一旦选定后,中途无法更换,导致和对应指数比较时成分股不同。
我觉得可以在指数调整样本股时进行时间划分,现在量化基本面挺难的,大多用的还是量价类因子,这样周期大致在5天左右,并且每年调整样本股时进行时间划分这样还可以挑选出训练区间和测试区间。
b)动态复权问题
这里统一使用的是前复权
2、因子数据分析之原始因子
detectedactorvalues = detecte_factor_values.DetecteFactorValues(df_1_pe)
#%%
#个别股票在某些时间段内,存在连续的空值,可能是停牌了
null_value_situation = detectedactorvalues.detecte_null_value()
#%%
statistice_factor_value = detectedactorvalues.statistice_factor_value(
quantile=5,value=1,interval=(0,1))
#%%
overall_factor_value = detectedactorvalues.overall_factor_value()
#%%
detectedactorvalues.plot_scatter()
#%%
detectedactorvalues.plot_hist()
3、因子数据处理
'''
因子数据处理
'''
#处理空值
factor = df_1_pe.fillna(0)
#%%
#去极值
factor = detecte_factor_values.winsorize_med(factor, scale=3, inclusive=True, inf2nan=True, axis=1)
#%%
#标准化
factor = detecte_factor_values.standardlize(factor, inf2nan=True, axis=1)
4、因子数据分析之处理过后的因子
factor_detectedactorvalues = detecte_factor_values.DetecteFactorValues(factor)
#%%
factor_null_value_situation = factor_detectedactorvalues.detecte_null_value()
#%%
factor_statistice_factor_value = factor_detectedactorvalues.statistice_factor_value(
quantile=5,value=1,interval=(0,1))
#%%
factor_overall_factor_value = factor_detectedactorvalues.overall_factor_value()
#%%
factor_detectedactorvalues.plot_scatter()
#%%
factor_detectedactorvalues.plot_hist()