如何對(duì)選股因子進(jìn)行量化回測(cè)?

引言 上一篇推文《

什么是多因子量化選股模型?

》主要介紹了多因子模型產(chǎn)生的理論背景、基本原理和實(shí)現(xiàn)步驟,而《

【手把手教你】Python量化Fama-French三因子模型

》則對(duì)國內(nèi)A股市場(chǎng)的三因子模型進(jìn)行了實(shí)證分析。多因子量化模型研究的對(duì)象主要是因子,因此單因子的回測(cè)和有效性檢驗(yàn)是整個(gè)多因子模型的重要組成部分。本文結(jié)合Python開源包Alphalens,以A股市場(chǎng)真實(shí)場(chǎng)景數(shù)據(jù),手把手教你對(duì)單因子進(jìn)行量化回測(cè)。Alphalens是Quantopian公司(美帝最大的量化回測(cè)平臺(tái)之一,國內(nèi)幾個(gè)基本上都是仿他們家的)三大知名Python開源包之一,其他兩個(gè)分別是Zipline(策略回測(cè),一直沒安裝成功)和Pyfolio(策略分析)。Alphalens主要提供因子收益分析、因子IC分析、因子換手分析和事件研究等回測(cè)框架,由于簡單易上手和科學(xué)穩(wěn)定等優(yōu)點(diǎn),是量化分析師最常用的回測(cè)工具包之一。
數(shù)據(jù)預(yù)處理 數(shù)據(jù)預(yù)處理是使用Alphalens做單因子回測(cè)的最主要工作,包括獲因子數(shù)據(jù),數(shù)據(jù)歸一化和異常值處理等,將原始數(shù)據(jù)整理成符合要求的格式后,后面的分析就變得很簡單,基本上都是一行代碼搞掂。本文以市盈率(PE)指標(biāo)為例,使用tushare的每日指標(biāo)接口(daily_basic)獲取A股市場(chǎng)3000多只股票2011-2019年收盤價(jià)和市盈率數(shù)據(jù),為大家展示如何使用alphalens做單因子的歷史回測(cè)。
#先引入后面可能用到的包(package)
import?alphalens?#使用pip安裝即可
import?pandas?as?pd??
import?numpy?as?np
import?matplotlib.pyplot?as?plt
%matplotlib?inline???

#正常顯示畫圖時(shí)出現(xiàn)的中文和負(fù)號(hào)
from?pylab?import?mpl
mpl.rcParams['font.sans-serif']=['SimHei']
mpl.rcParams['axes.unicode_minus']=False
#還是使用tushare獲取數(shù)據(jù)
import?tushare?as?ts
token='輸入token'
pro=ts.pro_api(token)
#獲取當(dāng)前交易的股票代碼和名稱
def?get_code():
????df?=?pro.stock_basic(exchange='',?list_status='L')
????#剔除2017年以后上市的新股次新股
????df=df[df['list_date'].apply(int).values<20170101]
????#剔除st股
????df=df[-df['name'].apply(lambda?x:x.startswith('*ST'))]
????codes=df.ts_code.values
????return?codes?
df=pro.daily_basic(ts_code=get_code()[0],start_date='20110101')
for?code?in?get_code()[1:]:
????df1=pro.daily_basic(ts_code=code,start_date='20110101')
????df=pd.concat([df,df1])
df_new=df.loc[:,['ts_code','trade_date','close','pe_ttm']]
df_new.loc[:,'trade_date']=pd.to_datetime(df_new.trade_date)
#設(shè)定雙重重索引的數(shù)據(jù)格式
df_new=df_new.set_index(['trade_date','ts_code'])
#根據(jù)第一索引排序
df_new=df_new.sort_index()
#查看數(shù)據(jù)前幾行
df_new.head()

其中,pe_ttm是動(dòng)態(tài)市盈率,是本文重點(diǎn)考察的單因子,數(shù)據(jù)預(yù)處理的第一步是建立以日期、股票代碼的雙重索引,如上表所示。先來看下全市場(chǎng)收盤價(jià)和市盈率的描述性統(tǒng)計(jì),市盈率有440萬個(gè)觀測(cè)值(刪除了市盈率為負(fù)的觀測(cè)值),均值為206,標(biāo)準(zhǔn)差高達(dá)12698,最大值為2653832,而75%分位數(shù)才77.3,可見均值受極端值影響很大,有必要對(duì)原始數(shù)據(jù)進(jìn)行歸一化和縮尾處理。factor_new是處理后的因子數(shù)據(jù)。prices是收盤價(jià)數(shù)據(jù)(前復(fù)權(quán)),必須轉(zhuǎn)化為日期為索引,列名是相應(yīng)股票代碼或名稱格式的數(shù)據(jù)形式。

df_new.describe().round(2)#對(duì)數(shù)據(jù)進(jìn)行縮尾處理,并進(jìn)行標(biāo)準(zhǔn)化
def?winsor_data(data):
????q=data.quantile([0.02,0.98])
????data[data<q.iloc[0]]=q.iloc[0]
????data[data>q.iloc[1]]=q.iloc[1]
????return?data
#數(shù)據(jù)標(biāo)準(zhǔn)化
def?MaxMinNormal(data):
????"""[0,1]?normaliaztion"""
????x?=?(data?-?data.min())?/?(data.max()?-?data.min())
????return?x
factor=df_new['pe_ttm'].groupby('trade_date').apply(winsor_data)
factor_new=factor.groupby('trade_date').apply(MaxMinNormal)
factor_new.hist(figsize=(12,6),bins=20)

prices=df_new['close'].unstack()

prices.head()

alphalens庫所用到是函數(shù)名稱都非常長,其中數(shù)據(jù)預(yù)處理函數(shù)是get_clean_factor_and_forward_returns,要想了解函數(shù)的詳細(xì)參數(shù)可以輸入help(函數(shù)名),這里不考慮分組(行業(yè)),其他參數(shù)均使用默認(rèn),factor_new為因子數(shù)據(jù),prices為收盤價(jià)數(shù)據(jù),quantiles=10表示將數(shù)據(jù)分成10個(gè)分位數(shù)進(jìn)行考察,period是調(diào)倉周期,10,20,60,表示分布考察10、20、60日調(diào)倉情況。進(jìn)行到這一步,表明數(shù)據(jù)清洗工作已完成,下面主要圍繞單因子的收益率、信息比率和換手率進(jìn)行回測(cè)和分析。


data=alphalens.utils.get_clean_factor_and_forward_returns(factor_new,prices,
??????????????????????????????????????????????????????????quantiles=10,periods=(10,20,60))
data.head()

收益率分析Returns Analysis

單因子的收益率分析主要圍繞10、20、60日(可自己設(shè)定)調(diào)倉周期,將因子數(shù)據(jù)從小到大排列,分為10個(gè)分位數(shù)進(jìn)行回測(cè),一行代碼便可實(shí)現(xiàn),但函數(shù)名稱實(shí)在太冗長了。第一張表報(bào)告了不同調(diào)倉期的alpha、beta、高分位(top quantile)和低分位(bottom quantile)的收益情況。柱狀圖清晰地顯示出該因子(市盈率)隨著分位的提高收益由正轉(zhuǎn)負(fù),可見市盈率因子與股票收益存在一定的反向關(guān)系,即市盈率越小,未來收益越高,與理論和直覺基本一致。小提琴圖是多收益率的分布,60日調(diào)倉的收益更加集中,方差更小,收益率更加可信。 ?由于對(duì)因子進(jìn)行了歸一化化處理,因此可以以因子值為權(quán)重構(gòu)建一個(gè)全市場(chǎng)的股票組合進(jìn)行回測(cè)。對(duì)于cumulative return by quantile的評(píng)價(jià)標(biāo)準(zhǔn)是累計(jì)收益率是否發(fā)散,越發(fā)散越好,說明因子的區(qū)分度越高,60天調(diào)倉期出現(xiàn)了明顯的發(fā)散。好的信號(hào):波動(dòng)小,在某一個(gè)方向的占比處于絕對(duì)地位,如大部分的Top minus bottom都是正的。


alphalens.tears.create_returns_tear_sheet(data)

信息分析 Information analysis

信息分析說白了是考察IC和IR指標(biāo)。IC全稱為Information Coefficient, 即信息系數(shù),代表因子預(yù)測(cè)股票收益的能力。其原理是通過計(jì)算全部股票在調(diào)倉周期期初排名和調(diào)倉周期期末收益排名的線性相關(guān)度(correlation)。 不難理解,IC絕對(duì)值越大的因子,選股能力越強(qiáng)。IC的值在[-1,1]區(qū)間內(nèi),最大值為1,表示該因子100%準(zhǔn)確,對(duì)應(yīng)的是排名分最高的股票,選出來的股票在下個(gè)調(diào)倉周期中,漲幅最大;相反,如果IC為-1,則代表排名最高的股票,在下個(gè)調(diào)倉周期中,漲幅最大,是一個(gè)完全反向指標(biāo),也是非常有意義的,使用的時(shí)候反向操作即可。一般而言,當(dāng)IC的絕對(duì)值<0.05時(shí),說明該因子對(duì)于的股票沒有任何預(yù)測(cè)能力或較差,當(dāng)IC絕對(duì)值>0.05時(shí),因子選股能力有一定的意義,當(dāng)IC大于0.5時(shí)因子穩(wěn)定獲取超額收益能力較強(qiáng)。


IR全稱是Information Ratio,等于IC的多周期均值/IC的標(biāo)準(zhǔn)差,或等于超額收益均值/超額收益標(biāo)準(zhǔn)差,代表因子獲取穩(wěn)定Alpha的能力。 整個(gè)回測(cè)時(shí)段由多個(gè)調(diào)倉期組成,每一個(gè)周期都會(huì)計(jì)算出一根不同的IC值,IR等于多個(gè)調(diào)倉周期的IC均值除以IC的標(biāo)準(zhǔn)差。因此IR兼顧了因子的選股能力和因子選股能力的穩(wěn)定性,IR值越大越好。


information analysis表給出了每個(gè)調(diào)倉周期下,IC的均值、方差以及t統(tǒng)計(jì)量,還有偏度、峰度以及年化的IR。因子信息分析好的評(píng)判標(biāo)準(zhǔn)是:IC的均值較高,方差小,而t統(tǒng)計(jì)量大(一般大于2)或p-value?。ㄒ话阈∮?%)。從結(jié)果上看,PE因子的IC均值都比較小,看來不是很理想的選股因子偏度和峰度主要考察和正態(tài)分布的偏離程度,主要用于分析小概率事件的影響。

alphalens.tears.create_information_tear_sheet(data)

Q-Q plot圖是查看樣本分布與正態(tài)分布的對(duì)比情況,如果滿足正態(tài)分布,Q-Q圖上的點(diǎn)應(yīng)該在y=x上。雖然有些IC的取值挺大的,但是尾部比較大,均值大是由于極度的尖峰和右偏造成的。還需具體考察IC的分布來決定一個(gè)因子的預(yù)測(cè)能力是不是可靠。


熱力圖提供了不同時(shí)間段因子的預(yù)測(cè)能力表現(xiàn),有利于進(jìn)行因子的情景分析。通過熱力圖我們可以輕易識(shí)別哪些時(shí)間段IC值比較好,哪些時(shí)間段IC比較差,有沒有突發(fā)的情況,然后可以對(duì)此進(jìn)行情景分析。

ic=alphalens.performance.mean_information_coefficient(data,by_time='1y')
from?pyecharts?import?Bar
attr=ic.index.strftime('%Y')
v1=list(ic['10D'].round(2))
v2=list(ic['20D'].round(2))
v3=list(ic['60D'].round(2))
bar=Bar('IC均值:2006-2019')
bar.add('10D',attr,v1)
bar.add('20D',attr,v2)
bar.add('60D',attr,v3)
bar



因子換手率 Turnover analysis

實(shí)際交易是有手續(xù)費(fèi)的,因子對(duì)單因子的回測(cè)還需要考慮換手率的影響,換手率越高說明交易次數(shù)越多,可能產(chǎn)生的手續(xù)費(fèi)越高。如果一個(gè)因子,今天給一個(gè)股票打了很高的分,明天就打很低的分,這將導(dǎo)致對(duì)該股頻繁的買賣,從而造成很高的手續(xù)費(fèi)。所以,因子的換手率分析也是很重要的一個(gè)部分。


還有一個(gè)角度可以查看因子造成的換手率高低,就是因子的自相關(guān)性。如果因子的自相關(guān)性低,那么就一會(huì)兒高,一會(huì)兒低,往往會(huì)造成很大的換手率。而因子的自相關(guān)性高,就不會(huì)有這樣的問題。因子的截面換手率越低,因子的自相關(guān)性越高

alphalens.tears.create_turnover_tear_sheet(data)

結(jié)語 本文以動(dòng)態(tài)市盈率作為選股因子,旨在為大家展示如何利用alphalens進(jìn)行單因子的歷史回測(cè),沒有考慮行業(yè)或市值大小的分組因素影響,實(shí)戰(zhàn)應(yīng)用到量化因子篩選時(shí)還需考慮更多因素和細(xì)節(jié)的處理,包括調(diào)倉周期和分位數(shù)確定等,本文僅提供一個(gè)因子量化回測(cè)的一般分析框架,不構(gòu)成任何投資建議。關(guān)于alphalens的詳細(xì)使用教程可參看其官網(wǎng)或東北證券金融工程研報(bào)《Alphalens使用教程》,公眾號(hào)后臺(tái)回復(fù)“Alphalens?”即可下載。
關(guān)于Python金融量化


專注于分享Python在金融量化領(lǐng)域的應(yīng)用。加入知識(shí)星球,可以免費(fèi)獲取30多g的量化投資視頻資料、公眾號(hào)文章Python完整源碼、量化投資前沿分析框架,與博主直接交流、結(jié)識(shí)圈內(nèi)朋友等。
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容