文件
開啟 Studio

策略範例

因子擁擠度分析

本頁內容6

研究因子分析,增減修改因子,不再靠感覺。這一頁用一個由市值、營收、動能三個因子組成的策略,示範 FinLab 提供的因子分析流程,適合手上已有多條件策略、想知道哪個條件真正有效的人。

分析流程

原始數據
    |
    |--- 特徵工程 -----> 因子特徵 (Features)
    |
    +--- 標籤工程 -----> 超額報酬 (Labels)
                |
                |--- calc_factor_return() -----> 因子報酬
                |      |
                |      +-- calc_centrality() --> 因子集中度
                |
                |--- calc_shapley_values() ----> 因子貢獻度
                |
                +--- calc_ic() ----------------> 因子相關性(IC)
  1. 先產生特徵與標籤(features, labels):特徵是因子,標籤是股票的超額報酬
  2. 計算因子報酬:calc_factor_return(features, labels)
  3. 計算因子擁擠度:calc_centrality(factor_return, window)
  4. 計算因子貢獻度:calc_shapley_values(features, labels)
  5. 計算因子相關性(IC):calc_ic(features, labels)
  6. 計算因子相關性(RankIC):calc_ic(features, labels, rank=True)

策略邏輯

這個策略由非常基本的三個因子組成,三個條件同時成立才買進:

  • 市值:每天在所有股票中,市值排名最小的 30%。
  • 營收:近 3 個月平均營收 ÷ 近 12 個月平均營收,排名最高的 30%。
  • 動能:近 20 日的漲幅(price / price.shift(20)),排名最高的 30%。
  • 換股:每月一次(resample='ME')。

完整程式碼

先看這個策略的績效:

strategy.py
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()

report.creturn.plot() 會畫出策略的累積報酬曲線。

分析每個因子

接著上面的程式執行:

產生特徵與標籤

用 feature.combine() 把三個因子條件整合成特徵矩陣,再用 label.excess_over_mean() 計算未來一個月的超額報酬作為標籤:

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()

想看標籤長什麼樣子:

labels.dropna().head()

算出因子報酬

因子報酬(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$ 為因子總數。這個指標反映了因子報酬的共同性:

  • 數值越大:利用此因子選股,近期績效比較好;但集中度越高,未來回檔的風險也越高,需密切觀察。
  • 數值越小:利用此因子選股,近期績效較差;集中度越低,未來回檔的風險較低。可以在因子擁擠度較低的時候密切觀察,等擁擠度上升時,馬上利用此因子選股。
from finlab.tools.factor_analysis import calc_centrality
from finlab.plot import plot_line

plot_line(calc_centrality(calc_factor_return(features, labels), 12), title='Centrality')

圖中,市值因子目前擁擠度較低,可以換一個因子,讓策略更穩定;也可以密切觀察市值因子,等擁擠度上升時,馬上利用此因子選股。

算出因子貢獻度(Shapley Values)

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

名稱雖叫 Shapley,但這不是賽局論的 Shapley 值:它沒有計算加入某個因子前後的報酬變化,各因子的值加總也不等於所有因子同時成立時的報酬。

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

plot_line(calc_shapley_values(features, labels))

算出因子 IC

因子 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,表示因子對未來報酬有很強的正向預測能力。
  • IC 越接近 -1,表示因子對未來報酬有很強的反向預測能力。
  • IC 接近 0,表示因子對未來報酬幾乎沒有預測能力。

在量化選股中,IC 是判斷因子有效性的重要依據,IC 越高的因子通常更值得納入投資組合。計算 IC 時,特徵需要使用連續數值而非布林條件,因此這裡重新定義特徵:

from finlab.tools.factor_analysis import calc_ic

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

plot_line(calc_ic(features, labels, rank=True))

怎麼調整

  • 換因子:改 feature.combine() 裡的字典,增加、刪除或修改因子,再重跑上面的分析。
  • 集中度的視窗 calc_centrality(..., 12):第二個參數是 window,計算集中度時取的期數。
  • IC 或 RankIC calc_ic(..., rank=True):rank=True 算 RankIC;不加 rank=True 就是 IC。

下一步

↑↓ 選擇 Enter 開啟 Esc 關閉