finlab.tools
工具模組,提供事件研究與因子分析功能。
使用情境
- 分析特定事件對股價的影響(事件研究)
- 評估因子的選股能力(因子分析)
- 優化策略的因子組合
- 理解策略績效的來源
快速範例
事件研究
因子分析
from finlab import data
from finlab.tools import factor_analysis as fa
marketcap = data.get('etl:market_value')
revenue = data.get('monthly_revenue:當月營收')
close = data.get('price:收盤價')
revenue_growth = revenue.average(3) / revenue.average(12)
momentum = close / close.shift(20)
# 布林因子 → 特徵(日期 × 股票)與下個月的超額報酬標籤
factors = {
'small_cap': marketcap.rank(pct=True, axis=1) < 0.3, # 小市值
'revenue_growth': revenue_growth.rank(pct=True, axis=1) > 0.7, # 營收成長
'momentum': momentum.rank(pct=True, axis=1) > 0.7, # 動能
}
features, labels = fa.generate_features_and_labels(factors, resample='ME')
# 計算因子報酬
factor_return = fa.calc_factor_return(features, labels)
factor_return.cumsum().plot(title='因子累積報酬')
詳細教學
參考 因子分析教學,了解: - 完整因子分析流程 - 因子報酬計算 - 因子 IC 分析 - Shapley values 貢獻度分析
API Reference
event_study()
finlab.tools.event_study
create_factor_data
create factor data, which contains future return
| PARAMETER | DESCRIPTION |
|---|---|
factor
|
factor data where index is datetime and columns is asset id
TYPE:
|
adj_close
|
adj close where index is datetime and columns is asset id
TYPE:
|
days
|
future return considered
TYPE:
|
Return
Analytic plots and tables. <name>_factor_quantile is the
cross-sectional quintile of the factor on each date, labelled 1
(lowest) to 5 (highest) with equal-sized buckets, like
df.cs.bucket(5). Earlier versions labelled it 0..4 with uneven
buckets.
Warning
This function is not identical to finlab.ml.alphalens.create_factor_data
Examples:
from finlab.tools.event_study import create_factor_data
from finlab.tools.event_study import event_study
factor = data.get('price_earning_ratio:股價淨值比')
adj_close = data.get('etl:adj_close')
benchmark = data.get('benchmark_return:發行量加權股價報酬指數')
# create event dataframe
dividend_info = data.get('dividend_announcement')
v = dividend_info[['stock_id', '除權交易日']].set_index(['stock_id', '除權交易日'])
v['value'] = 1
event = v[~v.index.duplicated()].reset_index().drop_duplicates(
subset=['stock_id', '除權交易日']
).pivot(index='除權交易日', columns='stock_id', values='value').notna()
# calculate factor_data
factor_data = create_factor_data({'pb':factor}, adj_close, event=event)
r = event_study(factor_data, benchmark, adj_close)
plt.bar(r.columns, r.mean().values)
plt.plot(r.columns, r.mean().cumsum().values)
event_study
event_study(
factor_data,
benchmark_adj_close,
stock_adj_close,
sample_period=(-45, -20),
estimation_period=(-5, 20),
plot=True,
)
Run event study and returns the abnormal returns of each stock on each day.
| PARAMETER | DESCRIPTION |
|---|---|
factor_data
|
factor data where index is datetime and columns is asset id
TYPE:
|
benchmark_adj_close
|
benchmark for CAPM
TYPE:
|
stock_adj_close
|
stock price for CAPM
TYPE:
|
sample_period
|
period for fitting CAPM
TYPE:
|
estimation_period
|
period for calculating alpha (abnormal return)
TYPE:
|
plot
|
plot the result
TYPE:
|
Return
Abnormal returns of each stock on each day.
Examples:
from finlab.tools.event_study import create_factor_data
from finlab.tools.event_study import event_study
factor = data.get('price_earning_ratio:股價淨值比')
adj_close = data.get('etl:adj_close')
benchmark = data.get('benchmark_return:發行量加權股價報酬指數')
# create event dataframe
dividend_info = data.get('dividend_announcement')
v = dividend_info[['stock_id', '除權交易日']].set_index(['stock_id', '除權交易日'])
v['value'] = 1
event = v[~v.index.duplicated()].reset_index().drop_duplicates(
subset=['stock_id', '除權交易日']
).pivot(index='除權交易日', columns='stock_id', values='value').notna()
# calculate factor_data
factor_data = create_factor_data({'pb':factor}, adj_close, event=event)
r = event_study(factor_data, benchmark, adj_close)
plt.bar(r.columns, r.mean().values)
plt.plot(r.columns, r.mean().cumsum().values)
事件研究:分析特定事件對股價的影響。
使用說明
事件研究用於分析特定事件(如財報公告、法說會、股利發放)對股價的短期與長期影響。
factor_analysis
finlab.tools.factor_analysis
Factor analysis toolkit -- public facade.
All public symbols are re-exported from focused submodules so that
existing from finlab.tools.factor_analysis import ... imports keep
working unchanged.
Submodules
- :mod:
finlab.tools.factor_metrics-- IC, correlation, NDCG scoring - :mod:
finlab.tools.factor_returns-- boolean factor returns & Shapley values - :mod:
finlab.tools.factor_centrality-- PCA-based rolling centrality - :mod:
finlab.tools.factor_regression-- OLS trend analysis
calc_centrality
Compute rolling PCA centrality over return_df.
| PARAMETER | DESCRIPTION |
|---|---|
return_df
|
Time-series DataFrame (dates x assets/factors).
TYPE:
|
window_periods
|
Rolling window length in rows.
TYPE:
|
n_components
|
Number of PCA components.
TYPE:
|
| RETURNS | DESCRIPTION |
|---|---|
DataFrame
|
DataFrame of rolling centrality scores. |
calc_factor_return
Compute equal-weight portfolio returns per boolean factor.
Each column in features must be boolean. For every date the mean
label value across selected (True) stocks is returned.
| PARAMETER | DESCRIPTION |
|---|---|
features
|
Boolean feature DataFrame (MultiIndex: date x stock).
TYPE:
|
labels
|
Excess-return labels with the same MultiIndex.
TYPE:
|
| RETURNS | DESCRIPTION |
|---|---|
DataFrame
|
DataFrame of per-period returns, one column per factor, |
DataFrame
|
starting from the first fully-populated date. |
| RAISES | DESCRIPTION |
|---|---|
ValueError
|
If any feature column is not boolean. |
calc_ic
Compute per-date IC between features and labels.
| PARAMETER | DESCRIPTION |
|---|---|
features
|
MultiIndex DataFrame (date, stock) with factor columns.
TYPE:
|
labels
|
MultiIndex Series with the same index.
TYPE:
|
rank
|
If
TYPE:
|
| RETURNS | DESCRIPTION |
|---|---|
DataFrame
|
DataFrame of IC values per date and factor. The Pearson result starts |
DataFrame
|
at the first date where every factor that has any IC has one; factors |
DataFrame
|
with no IC at all stay as all-NaN columns. |
calc_metric
Compute a cross-sectional metric between factor and forward returns.
| PARAMETER | DESCRIPTION |
|---|---|
factor
|
Single factor DataFrame or dict of named factor DataFrames.
TYPE:
|
adj_close
|
Adjusted close prices.
TYPE:
|
days
|
Forward-return horizons (default
TYPE:
|
func
|
Scoring callable applied per date group (default :func:
TYPE:
|
| RETURNS | DESCRIPTION |
|---|---|
DataFrame
|
DataFrame with one column per |
calc_regression_stats
Run per-column OLS regressions and classify each trend.
| PARAMETER | DESCRIPTION |
|---|---|
df
|
Time-series DataFrame with a
TYPE:
|
p_value_threshold
|
Significance level for trend classification.
TYPE:
|
r_squared_threshold
|
Minimum R-squared for a non-flat trend.
TYPE:
|
| RETURNS | DESCRIPTION |
|---|---|
DataFrame
|
DataFrame with columns |
DataFrame
|
|
| RAISES | DESCRIPTION |
|---|---|
ValueError
|
If the DataFrame index is not a |
calc_shapley_values
Score each boolean factor by the returns of the combinations containing it.
Enumerates all 2^n - 1 non-empty subsets of factors. For each subset and date, takes the mean label of the stocks selected by every factor in the subset (0 when none) and splits it evenly among the subset's factors. A factor's value is the sum of its shares over all subsets containing it.
Despite the name, this is not the game-theoretic Shapley value: it does not measure the change in return from adding a factor, and the values do not sum to the return of the all-factor subset.
| PARAMETER | DESCRIPTION |
|---|---|
features
|
Boolean feature DataFrame (MultiIndex: date x stock).
TYPE:
|
labels
|
Excess-return labels with the same MultiIndex.
TYPE:
|
| RETURNS | DESCRIPTION |
|---|---|
DataFrame
|
DataFrame of per-date scores, one column per factor. |
| RAISES | DESCRIPTION |
|---|---|
ValueError
|
If features is empty, labels lacks a MultiIndex, or any feature column is not boolean. |
generate_features_and_labels
Build a feature matrix and an excess-return label vector.
Wraps :func:finlab.ml.feature.combine and
:func:finlab.ml.label.excess_over_mean.
| PARAMETER | DESCRIPTION |
|---|---|
dfs
|
Dict mapping factor names to DataFrames (or callables).
TYPE:
|
resample
|
Resampling frequency / index passed to the ML helpers.
TYPE:
|
| RETURNS | DESCRIPTION |
|---|---|
tuple[DataFrame, Series]
|
|
ic
Per-date IC between factor and forward returns.
| PARAMETER | DESCRIPTION |
|---|---|
factor
|
Single factor DataFrame or dict of named factor DataFrames.
TYPE:
|
adj_close
|
Adjusted close prices.
TYPE:
|
days
|
Forward-return horizons (default
TYPE:
|
method
|
TYPE:
|
| RETURNS | DESCRIPTION |
|---|---|
DataFrame
|
DataFrame with one column per |
is_boolean_series
Check whether series contains only boolean-like values (including NaN).
Handles the case where older pandas converts bool + NaN to float.
因子分析模組,用於評估因子的選股能力與貢獻度。主要函數接收 features(以 (日期, 股票) 為索引、每個因子一欄的 DataFrame)與 labels(索引相同的 Series,通常是下一期的超額報酬),可用 generate_features_and_labels() 產生,或用 finlab.ml.feature.combine() 與 finlab.ml.label.excess_over_mean() 自行組合。
| 函數 | 說明 | 用途 |
|---|---|---|
calc_factor_return(features, labels) |
每期各布林因子選出股票的等權平均報酬 | 評估因子的整體表現 |
calc_ic(features, labels, rank=False) |
每期因子值與標籤的相關係數(rank=True 為 Spearman) |
評估因子與未來報酬的相關性 |
calc_shapley_values(features, labels) |
每期各布林因子的貢獻度:每個因子組合的報酬平分給組合內的因子 | 比較含有各因子的組合表現 |
calc_centrality(return_df, window_periods) |
因子報酬的滾動 PCA 集中度 | 監控因子擁擠度 |
calc_regression_stats(df) |
各欄隨時間的線性趨勢(up/down/flat) |
偵測集中度、IC 或貢獻度的趨勢 |
calc_shapley_values 的算法與它和賽局論 Shapley 值的差異見因子分析教學。
使用範例
延續上方的快速範例:
from finlab.ml import feature
# 因子報酬:每月選出股票的等權平均超額報酬
print(factor_return.mean())
# IC 需要連續的因子值,不是布林值
features_raw = feature.combine(
{'small_cap': -marketcap, 'revenue_growth': revenue_growth, 'momentum': momentum},
resample='ME',
)
ic = fa.calc_ic(features_raw, labels, rank=True)
print(ic.mean() / ic.std()) # IC_IR
# 每月貢獻度:每個因子組合的報酬平分給組合內的因子
shapley = fa.calc_shapley_values(features, labels)
print(shapley.mean())
# 12 個月窗口的集中度(擁擠度)與其趨勢
centrality = fa.calc_centrality(factor_return, 12)
print(fa.calc_regression_stats(centrality))
因子報酬解讀: - 正值:因子有選股能力(選中的股票表現較好) - 負值:因子反向有效(應反向操作) - 接近 0:因子無選股能力
IC 評估標準:
| IC 值 | 評價 | 說明 |
|---|---|---|
| > 0.05 | 優秀 | 因子有強選股能力 |
| 0.03 ~ 0.05 | 良好 | 因子有選股能力 |
| 0.01 ~ 0.03 | 一般 | 因子弱選股能力 |
| < 0.01 | 無效 | 因子無選股能力 |
IC 分析注意事項
- IC > 0: 因子與未來報酬正相關(做多因子高分股票)
- IC < 0: 因子與未來報酬負相關(做多因子低分股票)
- IC 波動大: 因子不穩定,風險高
- IC_IR > 0.5: 因子風險調整後表現佳
最佳實踐
- 使用長時間回測驗證因子穩定性
- 對比不同市場環境(牛市、熊市)的因子表現
- 移除 Shapley 值為負或接近 0 的因子,再重新評估策略表現
完整範例:多因子策略分析
from finlab import data
from finlab.backtest import sim
from finlab.tools import factor_analysis as fa
# 步驟 1:建立因子
marketcap = data.get('etl:market_value')
revenue = data.get('monthly_revenue:當月營收')
close = data.get('price:收盤價')
revenue_growth = revenue.average(3) / revenue.average(12)
momentum = close / close.shift(20)
factors = {
'small_cap': marketcap.rank(pct=True, axis=1) < 0.3, # 小市值
'revenue_growth': revenue_growth.rank(pct=True, axis=1) > 0.7, # 營收成長
'momentum': momentum.rank(pct=True, axis=1) > 0.7, # 動能
}
# 步驟 2:特徵與標籤(下個月的超額報酬)
features, labels = fa.generate_features_and_labels(factors, resample='ME')
# 步驟 3:分析各因子表現
print("=== 平均每月因子報酬 ===")
print(fa.calc_factor_return(features, labels).mean())
# 步驟 4:計算 Shapley values(貢獻度)
print("=== 平均 Shapley 值 ===")
print(fa.calc_shapley_values(features, labels).mean())
# 步驟 5:回測策略
position = factors['small_cap'] & factors['revenue_growth'] & factors['momentum']
report = sim(position, resample='M')
report.display()
stats = report.get_stats()
print(f"年化報酬: {stats['cagr']:.2%}")
print(f"夏普率: {stats['daily_sharpe']:.2f}")
print(f"最大回撤: {stats['max_drawdown']:.2%}")
常見問題
Q: 如何判斷一個因子是否有效?
使用三個指標綜合判斷:
# 1. 因子報酬(應顯著 > 0)
print(factor_return.mean())
# 2. IC(應 > 0.02)
print(ic.mean())
# 3. IC_IR(應 > 0.5)
print(ic.mean() / ic.std())
# 結論:三個指標都合格才認為因子有效
Q: Shapley values 如何應用於策略優化?
# 各因子的平均貢獻度
contribution = shapley.mean()
# 保留貢獻度為正的因子;都不為正時保留最好的一個
kept = contribution[contribution > 0].index
if kept.empty:
kept = contribution.nlargest(1).index
print(f"原始因子數: {len(contribution)}")
print(f"優化後因子數: {len(kept)}")
# 使用優化後的因子組合
optimized_position = factors[kept[0]]
for name in kept[1:]:
optimized_position = optimized_position & factors[name]
Q: IC 和因子報酬的區別?
- IC (Information Coefficient):
- 衡量因子值與未來報酬的相關性
- 範圍 [-1, 1],越接近 1 越好
-
適合評估連續型因子(如市值、ROE)
-
因子報酬:
- 衡量因子選出的股票的平均報酬
- 適合評估二元因子(True/False)
- 直接反映選股效果
from finlab.ml import feature
pe = data.get('price_earning_ratio:本益比')
# 連續型因子用 IC
continuous = feature.combine({'pe': pe}, resample='ME')
ic = fa.calc_ic(continuous, labels, rank=True)
# 二元因子用因子報酬
binary = feature.combine({'low_pe': pe.rank(pct=True, axis=1) < 0.3}, resample='ME')
factor_return = fa.calc_factor_return(binary, labels)