文件
開啟 Studio

教學

因子分析

本頁內容8

一個策略通常由好幾個選股條件組成,每個條件就是一個「因子」。這一頁教你量化每個因子和策略報酬的關係,用數據而不是感覺決定要增加、刪掉或修改哪個因子。

分析流程

先把策略拆成兩張表,再交給 finlab.tools.factor_analysis 的函式:

  • 特徵(features):每個月、每檔股票,各因子是否成立。
  • 標籤(labels):每檔股票下個月的報酬,減掉所有股票的平均報酬(超額報酬)。
函式輸入得到回答的問題
calc_factor_return()特徵、標籤因子報酬這個因子帶來多少超額報酬?
calc_centrality()因子報酬因子集中度這個因子是不是太擁擠?
calc_shapley_values()特徵、標籤因子貢獻度含有這個因子的組合表現好不好?
calc_ic()原始因子值、標籤因子相關性(IC)因子值能不能預測未來報酬?
calc_regression_stats()上面任一項隨時間變化的結果趨勢統計這項指標正在變強還是變弱?

把策略轉成特徵與標籤

寫出範例策略

這個策略由三個很基本的因子組成:市值、營收、動能。三個條件同時成立才買進,每月換股。

from finlab import data
from finlab.backtest import sim

marketcap = data.get('etl:market_value')
revenue = data.get('monthly_revenue:當月營收')
price = data.get('etl:adj_close')

cond1 = marketcap.rank(pct=True, axis=1) < 0.3
cond2 = (revenue.average(3) / revenue.average(12)).rank(pct=True, axis=1) > 0.7
cond3 = (price / price.shift(20)).rank(pct=True, axis=1) > 0.7

pos = cond1 & cond2 & cond3

report = sim(pos, resample='ME', upload=False)
report.creturn.plot()

營收因子是近 3 個月平均營收除以近 12 個月平均營收。

rank(pct=True, axis=1) 是每天在所有股票之間排名,換算成 0 到 1 的百分位:cond1 取市值最小的 30%,cond2、cond3 取營收成長和 20 日漲幅最高的 30%。

製作特徵與標籤

feature.combine() 把三個條件合成一張特徵表,label.excess_over_mean() 算出每檔股票未來 1 個月的超額報酬。兩者都用 resample='ME',日期才會對得上。下面的輸出和本頁的圖表都是台股範例的結果:

from finlab import data
import finlab.ml.feature as feature
import finlab.ml.label as label

features = feature.combine(
    {'marketcap': cond1, 'revenue': cond2, 'momentum': cond3}, resample='ME'
)

# 計算未來 1M 的標籤
labels = label.excess_over_mean(index=features.index, resample='ME')

features.dropna().head()
datetimeinstrumentmarketcaprevenuemomentum
2013-04-301101FalseTrueTrue
2013-04-301102FalseTrueTrue
2013-04-301103FalseTrueFalse
2013-04-301104FalseTrueFalse
2013-04-301108FalseTrueFalse

特徵表的索引是(datetime, instrument):每個月底、每檔股票一列,每個因子一欄。

labels.dropna().head()
datetime    instrument
2007-04-30  1101          0.042548
            1102          0.081764
            1103         -0.016863
            1104         -0.047492
            1108         -0.002247
dtype: float64

標籤是索引相同的 Series:每檔股票下個月的報酬減掉所有股票的平均報酬。

看每個因子帶來多少報酬

因子報酬(factor return)衡量一個因子在一段時間內帶來多少超額報酬,常用來評估因子是否有效、值不值得投資。把每個因子的報酬累加起來,就能看出它長期的表現和穩定度,作為挑選與組合因子的依據。

from finlab.tools.factor_analysis import calc_factor_return
from finlab.plot import plot_line

plot_line(
    calc_factor_return(features, labels).cumsum(),
    unit='.0%',
    title='Factor Cumulative Return',
)
因子累積報酬走勢圖
因子累積報酬走勢圖

累積報酬一路上升的因子,一直在貢獻超額報酬;走平或下滑的因子,在這段期間沒有幫上忙。圖中可以看到月營收因子的效果明顯比較好,市值因子的效果較差。

看因子是不是太擁擠

因子集中度(factor centrality)量化因子報酬之間的「共同性」,也就是這個因子有多擁擠:

Centralityi=λi∑j=1kλj\text{Centrality}_i = \frac{\lambda_i}{ \sum_{j=1}^{k}\lambda_j}

其中 λj\lambda_j 為因子對於第一主成分的佔比,$k$ 為因子總數。

集中度代表怎麼用
越大用這個因子選股,近期績效比較好;但集中度越高,未來回檔的風險也越高密切觀察
越小用這個因子選股,近期績效較差;未來回檔的風險較低在擁擠度低的時候密切觀察,等擁擠度上升初期再用這個因子選股

calc_centrality() 的第二個參數是滾動視窗的長度(幾列資料),下面用 12,也就是 12 個月:

from finlab.tools.factor_analysis import calc_centrality

plot_line(calc_centrality(calc_factor_return(features, labels), 12), title='Centrality')
因子集中度(Centrality)走勢圖
因子集中度(Centrality)走勢圖

上圖中,市值因子目前擁擠度較低,可以換一個因子,讓策略更穩定;也可以密切觀察市值因子,等擁擠度上升初期再用它選股(因子回歸效應)。

看因子在組合裡的貢獻

calc_shapley_values() 枚舉所有因子組合。每個時間點,取同時符合組合內所有因子的股票,算它們的等權平均超額報酬(沒有股票時記 0),再平分給組合內的每個因子;每個因子的貢獻度,是它參與的所有組合分到的報酬總和。

from finlab.tools.factor_analysis import calc_shapley_values

plot_line(calc_shapley_values(features, labels))
因子 Shapley 值走勢圖
因子 Shapley 值走勢圖

值越高,代表含有該因子的組合表現越好。

看因子能不能預測報酬

因子 IC(information coefficient)是衡量因子預測能力的重要指標,也就是因子分數和未來報酬之間的相關性:

IC=corr(Factor Score,Future Return)IC = \text{corr}(\text{Factor Score}, \text{Future Return})

其中 corr\text{corr} 表示相關係數(通常為皮爾森相關),Factor Score\text{Factor Score} 為因子分數,Future Return\text{Future Return} 為未來一段期間的資產報酬。IC 通常介於 -1 到 1:

IC意思
接近 1因子對未來報酬有很強的正向預測能力
接近 -1因子對未來報酬有很強的反向預測能力
接近 0因子對未來報酬幾乎沒有預測能力

IC 是判斷因子是否有效的重要依據,IC 越高的因子通常越值得納入投資組合。

IC 要用連續的因子值,不能用 True/False,所以要另外做一張原始值的特徵表。rank=True 會在每個日期把因子值和標籤都換成排名再算相關(Spearman 排名相關,也就是 Rank IC);不設時用皮爾森相關。

from finlab.tools.factor_analysis import calc_ic

features_raw = feature.combine(
    {
        'marketcap': -marketcap,  # 小市值
        'revenue': revenue.average(3) / revenue.average(12),
        'momentum': price / price.shift(20),
    },
    resample='ME',
)

plot_line(calc_ic(features_raw, labels, rank=True))
因子 Rank IC 走勢圖
因子 Rank IC 走勢圖

判斷因子正在變強還是變弱

集中度、貢獻度、IC 都是隨時間變化的數值。calc_regression_stats() 對每個因子的走勢做線性迴歸,告訴你它是上升、下降還是持平:

from finlab.tools.factor_analysis import calc_regression_stats

centrality_df = calc_centrality(calc_factor_return(features, labels), 12)
centrality_trend = calc_regression_stats(centrality_df)
slopep_valuer_squaredtail_estimatetrend
marketcap-0.0001113.102740e-170.4044680.0123down
revenue0.0000184.861743e-030.0560410.0087flat
momentum0.0000931.146515e-170.4129140.0215up
欄位說明數值範圍含義
slope線性回歸斜率(-∞, +∞)趨勢方向和強度:正值是上升(擁擠度增加),負值是下降(擁擠度減少),絕對值越大趨勢越強
p_value統計顯著性[0, 1]趨勢的可信度
r_squared決定係數[0, 1]線性模型的解釋力
tail_estimate尾部估計值(-∞, +∞)時間序列末端的預測值
trend趨勢分類"up"/"down"/"flat"簡化的趨勢判斷

只有 p_value < 0.05 而且 r_squared > 0.1 時,trend 才會是 up 或 down,否則是 flat。這兩個門檻是參數 p_value_threshold 與 r_squared_threshold 的預設值。

p_valuer_squaredslopetrend含義
小高正up強烈且穩定的上升趨勢
小高負down強烈且穩定的下降趨勢
小低任意flat趨勢存在但效果小/雜訊大
大高任意flat樣本少,雜訊高,無法判斷
大低任意flat基本無趨勢且模型無解釋力

解讀上面的結果

因子數值解讀
市值(marketcap)slope -0.000111(負值)、p_value 3.10e-17(極度顯著)、r_squared 0.40(中等偏強解釋力)、trend down集中度呈現非常強烈且統計上極度顯著的下降趨勢
營收(revenue)slope 0.000018(正值)、p_value 0.0048(顯著)、r_squared 0.056(解釋力較弱)、trend flat有統計上顯著的上升趨勢,但解釋力不足(r_squared < 0.1),被歸類為 flat
動能(momentum)slope 0.000093(顯著正值)、p_value 1.14e-17(極度顯著)、r_squared 0.41(中等偏強解釋力)、trend up集中度呈現非常強烈且統計上極度顯著的上升趨勢。這是明確的信號:動能因子非常「熱門」,大量資金正在追逐這個策略
p_value 與 r_squared 的分級

p_value(統計顯著性)

  • [0, 0.01]:極度顯著(***)
  • [0.01, 0.05]:高度顯著(**)
  • [0.05, 0.1]:邊際顯著(*)
  • [0.1, 1]:不顯著

r_squared(決定係數)

  • [0.8, 1.0]:極強解釋力
  • [0.6, 0.8):強解釋力
  • [0.4, 0.6):中等解釋力
  • [0.2, 0.4):弱解釋力
  • [0, 0.2):很弱解釋力

下一步

↑↓ 選擇 Enter 開啟 Esc 關閉