華泰單因子測試之估值類因子
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()