API 參考
finlab.ml
本頁內容8
finlab.ml 把資料整理成機器學習要的樣子:feature 把多張資料表合併成特徵,label 產生訓練目標,qlib 提供現成的模型。模型預測出來的是一張「日期 × 股票」的表,可以直接用 is_largest() 選股、交給 sim() 回測。
選哪個函式
| 你想做的事 | 用這個 |
|---|---|
| 把多張資料表合併成一張特徵表 | mlf.combine() |
| 批次計算 TA-Lib 技術指標當特徵 | mlf.ta() |
| 列出技術指標名稱(可隨機抽參數) | mlf.ta_names() |
| 產生未來 N 期的報酬率標籤 | mll.return_percentage() |
| 產生隔日開盤買、收盤賣的報酬率標籤(當沖) | mll.daytrading_percentage() |
| 產生未來 N 期的最大不利偏移標籤(風險) | mll.maximum_adverse_excursion() |
| 訓練模型並預測 | q.LGBModel()、q.XGBModel()、q.CatBoostModel()、q.LinearModel() |
範例都用這三個縮寫:from finlab.ml import feature as mlf, label as mll、import finlab.ml.qlib as q。
從特徵到回測
一個 ML 選股策略的完整流程:建立特徵 → 產生標籤 → 依時間切分訓練與測試 → 訓練 → 預測 → 轉成持股 → 回測。
from finlab import data
from finlab.ml import feature as mlf, label as mll
import finlab.ml.qlib as q
from finlab.backtest import sim
# 步驟 1:建立特徵集
print("建立特徵...")
features = mlf.combine(
{
# 基本面特徵
'pb': data.get('price_earning_ratio:股價淨值比'),
'pe': data.get('price_earning_ratio:本益比'),
'roe': data.get('fundamental_features:ROE稅後'),
# 技術指標(使用少量指標避免過擬合)
'technical': mlf.ta(mlf.ta_names(n=1)[:20]), # 只取前 20 個
},
resample='W',
)
# 步驟 2:生成標籤
print("生成標籤...")
label = mll.return_percentage(features.index, resample='W', period=1)
# 步驟 3:切分訓練/測試集
print("切分資料...")
is_train = features.index.get_level_values('datetime') < '2023-01-01'
X_train, y_train = features[is_train], label[is_train]
X_test = features[~is_train]
# 步驟 4:訓練模型
print("訓練模型...")
model = q.LGBModel()
model.fit(X_train, y_train)
# 步驟 5:預測
print("生成預測...")
pred = model.predict(X_test)
# 步驟 6:轉換為交易訊號
print("生成交易訊號...")
position = pred.is_largest(30) # 買入預測報酬率最高的 30 檔
# 步驟 7:回測
print("執行回測...")
report = sim(position, resample='W')
report.display()
# 步驟 8:分析特徵重要性(如果是樹模型)
if hasattr(model, 'feature_importances_'):
import pandas as pd
feature_importance = pd.Series(
model.feature_importances_, index=features.columns.get_level_values(1).unique()
).sort_values(ascending=False)
print("\n前 10 重要特徵:")
print(feature_importance.head(10))合併特徵
mlf.combine()
把多個特徵合併成一張表:索引是(datetime, instrument),每個特徵一欄。日期 × 股票的寬表會先對齊到所有特徵日期的聯集與共同的股票、向前填補,再疊成長表;本來就是(datetime, instrument)MultiIndex 的特徵直接接上。季頻的 FinLab 資料會先換成財報公布日。
from finlab import data
from finlab.ml import feature as mlf
close = data.get('price:收盤價')
pb = data.get('price_earning_ratio:股價淨值比')
pe = data.get('price_earning_ratio:本益比')
# 範例 1:合併基本面特徵
features = mlf.combine(
{
'pb': pb,
'pe': pe,
'roe': data.get('fundamental_features:ROE稅後'),
},
resample='W',
)
# 範例 2:合併技術指標
indicators = [
'talib.RSI__real__timeperiod__14__',
'talib.MACD__macd__fastperiod__12__slowperiod__26__signalperiod__9__',
]
technical = mlf.ta(indicators)
features = mlf.combine({'talib': technical}, resample='D')
# 範例 3:混合多種特徵
features = mlf.combine(
{
'fundamental': mlf.combine({'pb': pb, 'pe': pe}),
'technical': technical,
'custom': close / close.average(20), # 股價相對 20 日均線
},
resample='W',
)resample 可以這樣設:
'D':每日'W':每週(字串頻率標在日曆週的最後一天,也就是週日)'M':每月(月底)- 傳入
pd.DatetimeIndex:用你指定的日期當錨點,例如每週最後一個交易日
要讓特徵和標籤都落在每週最後一個交易日,把同一組日期傳給兩邊:
import pandas as pd
from finlab import data
from finlab.ml import feature as mlf, label as mll
rsi = data.indicator('RSI')
calendar = rsi.index
weekly = pd.DatetimeIndex(calendar.to_series().resample('W').last().dropna())
features = mlf.combine({'rsi': rsi}, resample=weekly)
labels = mll.return_percentage(features.index, resample=weekly, period=2)finlab.ml.combine
combine(features, resample, sample_filter, **kwargs) -> pd.DataFrameThe combine function takes a dictionary of features as input and combines them into a single pandas DataFrame. combine 函數接受一個特徵字典作為輸入,並將它們合併成一個 pandas DataFrame。
參數
featuresdict[str, pd.DataFrame | Callable]a dictionary where values are dataframes or callables returning dataframes. 索引為日期時間,欄位 為證券代碼的 DataFrame,或可呼叫以取得 DataFrame 的函式。
resamplestr | pd.DatetimeIndexOptional argument to resample the data in the features. Default is None. 選擇性的參數,用於重新取樣特徵中的資料。預設為 None。 MultiIndex features use the same resampling as wide features. String frequencies use calendar period labels (e.g. Sunday for 'W'). Pass a DatetimeIndex for explicit trading-date anchors. MultiIndex 與寬表 共用重採樣方式;字串週頻標在週日,實際交易日錨點可用指定日期。
sample_filterpd.DataFramea boolean dictionary where index is date and columns are instrument representing the filter of features.
**kwargsAny預設{}Additional keyword arguments to pass to the resampler function. 傳遞給重新取樣函數 resampler 的其他關鍵字引數。
回傳
pd.DataFrameA pandas DataFrame containing all the input features combined. 一個包含所有輸入特徵合併後的 pandas DataFrame。
範例
這段程式碼教我們如何使用finlab.ml.feature和finlab.data模組,來合併兩個特徵:RSI和股價淨值比。我們使用f.combine函數來進行合併,其中特徵的名稱是字典的鍵,對應的資料是值。 我們從data.indicator('RSI')取得'rsi'特徵,這個函數計算相對強弱指數。我們從data.get('price_earning_ratio:股價淨值比')取得'pb'特徵,這個函數獲取股價淨值比。最後,我們得到一個包含這兩個特徵的DataFrame。
from finlab import data
import finlab.ml.feature as f
import finlab.ml.qlib as q
features = f.combine({
# 用 data.get 簡單產生出技術指標
'pb': data.get('price_earning_ratio:股價淨值比'),
# 用 data.indicator 產生技術指標的特徵
'rsi': data.indicator('RSI'),
# 用 f.ta 枚舉超多種 talib 指標
'talib': f.ta(f.ta_names()),
# 利用 qlib alph158 產生技術指標的特徵(請先執行 q.init(), q.dump() 才能使用)
'qlib158': q.alpha('Alpha158')
})
features.head()| datetime | instrument | rsi | pb |
|---|---|---|---|
| 2020-01-01 | 1101 | 0 | 2 |
| 2020-01-02 | 1102 | 100 | 3 |
| 2020-01-03 | 1108 | 100 | 4 |
以每週最後交易日為錨點,特徵與未來兩期報酬使用同一組日期:
import pandas as pd
from finlab.ml import label
f158 = q.alpha('Alpha158')
calendar = f158.index.get_level_values('datetime').unique().sort_values()
weekly = pd.DatetimeIndex(calendar.to_series().resample('W').last().dropna())
features = f.combine({'qlib158': f158}, resample=weekly)
labels = label.return_percentage(features.index, resample=weekly, period=2)計算技術指標
mlf.ta()
依指標名稱批次計算 TA-Lib 技術指標,名稱格式是 talib.指標__輸出__參數__值__。
from finlab.ml import feature as mlf
# 計算特定指標
indicators = mlf.ta(
[
'talib.RSI__real__timeperiod__14__',
'talib.MACD__macd__fastperiod__12__slowperiod__26__signalperiod__9__',
'talib.BBANDS__upperband__timeperiod__20__nbdevup__2.0__nbdevdn__2.0__matype__0__',
],
resample='W',
)
# 指定指標的隨機參數組合
names = [
name
for name in mlf.ta_names(n=3)
if name.startswith(('talib.RSI__', 'talib.MACD__', 'talib.ATR__'))
]
random_indicators = mlf.ta(names, resample='W')finlab.ml.ta
ta(feature_names, factories, resample, start_time, end_time, adj, cpu, **kwargs) -> pd.DataFrameCalculate technical indicator values for a list of feature names.
參數
feature_nameslist[str] | NoneA list of technical indicator feature names. Defaults to None.
factoriesdict[str, TalibIndicatorFactory] | NoneA dictionary of factories to generate technical indicators. Defaults to {"talib": TalibIndicatorFactory()}.
resamplestr | NoneThe frequency to resample the data to. Defaults to None.
start_timestr | NoneThe start time of the data. Defaults to None.
end_timestr | NoneThe end time of the data. Defaults to None.
**kwargsAny預設{}Additional keyword arguments to pass to the resampler function.
adjbool預設Falsecpuint預設-1
回傳
pd.DataFramepd.DataFrame: technical indicator feature names and their corresponding values.
mlf.ta_names()
列出 TA-Lib 指標名稱,每個指標隨機抽 n 組參數,交給 mlf.ta() 計算。
from finlab.ml import feature as mlf
# 生成 10 種隨機參數組合的所有 TA-Lib 指標
indicator_names = mlf.ta_names(n=10)
print(f"共 {len(indicator_names)} 個指標")
# 查看範例
for name in indicator_names[:5]:
print(name)
# 輸出(參數隨機抽取;沒有參數的指標只出現一次):
# talib.HT_DCPERIOD__real__
# talib.HT_DCPHASE__real__
# talib.HT_PHASOR__inphase__
# talib.HT_PHASOR__quadrature__
# talib.HT_SINE__sine__n 決定名稱數量:
n | 意思 | 名稱數量 |
|---|---|---|
1 | 每個指標隨機抽一組參數 | TA-Lib 0.8 約 130 個 |
5 | 每個指標最多 5 組隨機參數 | 約 560 個 |
10 | 更多變化,但計算時間較長 | 約 1,070 個 |
建議先用 n=1 測試,確認可行後再增加。指標太多會讓計算時間變長、記憶體佔用變大、過度配適的風險變高,建議搭配特徵選擇(feature selection)減少指標數量。
finlab.ml.ta_names
ta_names(lb, ub, n, factory) -> list[str]Generate a list of technical indicator feature names.
參數
lbint預設1The lower bound of the multiplier of the default parameter for the technical indicators.
ubint預設10The upper bound of the multiplier of the default parameter for the technical indicators.
nint預設1The number of random samples for each technical indicator.
factoryIndicatorFactoryA factory object to generate technical indicators. Defaults to TalibIndicatorFactory.
回傳
list[str]list[str]: A list of technical indicator feature names.
範例
import finlab.ml.feature as f
# method 1: generate each indicator with random parameters
features = f.ta()
# method 2: generate specific indicator
feature_names = ['talib.MACD__macdhist__fastperiod__52__slowperiod__212__signalperiod__75__']
features = f.ta(feature_names, resample='W')
# method 3: generate some indicator
feature_names = f.ta_names()
features = f.ta(feature_names)產生訓練標籤
標籤是模型要學著預測的目標。三個函式都接受特徵的索引 features.index,回傳對齊到同一組(datetime, instrument)的 Series。
mll.return_percentage()
每個樣本在特徵日期之後 period 期的報酬率,從下一個交易日的價格起算,是最常用的標籤。
from finlab import data
from finlab.ml import feature as mlf, label as mll
features = mlf.combine({'pb': data.get('price_earning_ratio:股價淨值比')}, resample='W')
# 生成標籤:預測未來 1 週報酬率
label = mll.return_percentage(features.index, resample='W', period=1)
# 生成標籤:預測未來 4 週報酬率
label_4w = mll.return_percentage(features.index, resample='W', period=4)| 參數 | 型別 | 預設 | 說明 |
|---|---|---|---|
index | pd.Index | 必填 | 特徵的(datetime, instrument)索引 |
resample | str、pd.DatetimeIndex 或 None | None | 重新取樣的頻率或日期錨點,要和特徵一致 |
period | int | 1 | 往後算幾期。resample=None 時是交易日數,即使特徵是週頻也一樣 |
trade_at_price | str | 'close' | 用哪個價格計算 |
bfill | bool | False | True 時,上市期間內缺的價格用下一個有效價格補上(上市前、下市後仍是空值) |
**kwargs | 傳給重新取樣函式 |
period 怎麼選:
period=1:短期預測,適合週、月調倉策略period=4:中期預測,適合季度調倉period越大,預測越穩定,但訊號越遲鈍- 建議與策略的調倉頻率一致
mll.daytrading_percentage()
下一個交易日開盤買、收盤賣的報酬率,適合當沖策略。
from finlab import data
from finlab.ml import feature as mlf, label as mll
# 建立日頻特徵
features = mlf.combine({'rsi': data.indicator('RSI')}, resample='D')
# 生成標籤:下一個交易日開盤買、收盤賣的報酬率
label = mll.daytrading_percentage(features.index)finlab.ml.daytrading_percentage
daytrading_percentage(index, resample, **kwargs) -> pd.SeriesCalculate the percentage change of market prices over a given period.
參數
indexpd.IndexA multi-level index of datetime and instrument.
resamplestr | None預設'D'The resample frequency for the output data. Defaults to "D".
**kwargsAny預設{}Additional arguments to be passed to the resampler function.
回傳
pd.Seriespd.Series: A pd.Series containing the percentage change of stock prices.
mll.maximum_adverse_excursion()
未來 period 期內的最大跌幅(最大不利偏移),可用來訓練風險預測模型。
from finlab import data
from finlab.ml import feature as mlf, label as mll
features = mlf.combine({'rsi': data.indicator('RSI')}, resample='D')
# 計算未來 N 期的最大跌幅
mae_label = mll.maximum_adverse_excursion(features.index, period=5)
# 可用於訓練風險預測模型
# 負值越大表示風險越高finlab.ml.maximum_adverse_excursion
maximum_adverse_excursion(index, period, trade_at_price) -> pd.SeriesCalculate the maximum adverse excursion of market prices over a given period.
參數
indexpd.IndexA multi-level index of datetime and instrument.
periodint預設1The number of periods to calculate the percentage change over. Defaults to 1.
trade_at_pricestr預設'close'The price for execution. Defaults to
close.
回傳
pd.Seriespd.Series: A pd.Series containing the maximum adverse excursion.
訓練模型並預測
finlab.ml.qlib 把 qlib 的模型包成同一套用法:model.fit(X_train, y_train) 訓練,model.predict(X_test) 回傳日期 × 股票的 FinlabDataFrame。
from finlab import data
from finlab.ml import feature as mlf, label as mll
import finlab.ml.qlib as q
# 1. 準備特徵
features = mlf.combine(
{
'pb': data.get('price_earning_ratio:股價淨值比'),
'pe': data.get('price_earning_ratio:本益比'),
},
resample='W',
)
# 2. 準備標籤
label = mll.return_percentage(features.index, resample='W', period=1)
# 3. 切分訓練/測試集
is_train = features.index.get_level_values('datetime') < '2020-01-01'
X_train, y_train = features[is_train], label[is_train]
X_test = features[~is_train]
# 4. 建立並訓練模型
model = q.XGBModel() # 可選: q.LGBModel(), q.CatBoostModel(), q.LinearModel()
model.fit(X_train, y_train)
# 5. 預測
y_pred = model.predict(X_test)| 模型 | 說明 | 優點 | 缺點 | 適合 |
|---|---|---|---|---|
LGBModel() | LightGBM | 快速、效能好、記憶體佔用小 | 需安裝 lightgbm | 初學者推薦,平衡速度與效能 |
XGBModel() | XGBoost | 穩定、可解釋性高 | 訓練較慢 | 追求穩定 |
CatBoostModel() | CatBoost | 處理類別特徵佳、過擬合風險低 | 記憶體佔用大 | 有類別特徵 |
LinearModel() | 線性回歸 | 簡單、快速、不易過擬合 | 表現通常較差 | 快速驗證想法 |
想避免過擬合,先用 LinearModel() 建立基準,再嘗試複雜的模型。
finlab.ml.LGBModel
LGBModel() -> WrapperModelLGBModel is a wrapper model for LightGBM model.
import finlab.ml.qlib as q
# build X_train, y_train, X_test
model = q.LGBModel()
model.fit(X_train, y_train)
y_pred = model.predict(X_test)finlab.ml.XGBModel
XGBModel() -> WrapperModelXGBModel is a wrapper model for XGBoost model.
import finlab.ml.qlib as q
# build X_train, y_train, X_test
model = q.XGBModel()
model.fit(X_train, y_train)
y_pred = model.predict(X_test)finlab.ml.CatBoostModel
CatBoostModel() -> WrapperModelCatBoostModel is a wrapper model for CatBoost model.
import finlab.ml.qlib as q
# build X_train, y_train, X_test
model = q.CatBoostModel()
model.fit(X_train, y_train)
y_pred = model.predict(X_test)finlab.ml.LinearModel
LinearModel() -> WrapperModelLinearModel is a wrapper model for Linear model.
import finlab.ml.qlib as q
# build X_train, y_train, X_test
model = q.LinearModel()
model.fit(X_train, y_train)
y_pred = model.predict(X_test)常見問題
特徵和標籤的 resample 不一致會怎樣?
會導致日期錯位,無法正確配對特徵與標籤:
# ❌ 錯誤:resample 不一致
features = mlf.combine({...}, resample='W') # 週
label = mll.return_percentage(features.index, resample='D', period=1) # 日
# → 標籤算的是 1 日報酬,不是 1 週報酬
# ✅ 正確:resample 一致
features = mlf.combine({...}, resample='W')
label = mll.return_percentage(features.index, resample='W', period=1)如何避免過度配適?
# 方法 1:減少特徵數量
features = mlf.ta(mlf.ta_names(n=1)[:20]) # 只用 20 個指標
# 方法 2:使用正則化(降低模型複雜度)
# LightGBM 支援 L1/L2 正則化、樹深度限制等
# 方法 3:使用簡單模型
model = q.LinearModel() # 線性模型不易過擬合
# 方法 4:樣本外測試
# 確保測試集表現接近訓練集(不要相差超過 50%)訓練很慢怎麼辦?
# 方法 1:減少資料範圍
data.truncate_start = '2020-01-01'
# 方法 2:減少特徵數量
features = mlf.ta(mlf.ta_names(n=1)[:10]) # 只用 10 個指標
# 方法 3:使用 resample='W' 或 'M'(而非 'D')
features = mlf.combine({...}, resample='W') # 週度資料更快如何處理缺失值?
# 先檢查缺失值狀況
print(f"特徵缺失值比例: {features.isna().sum().sum() / features.size:.2%}")
print(f"標籤缺失值比例: {label.isna().sum() / len(label):.2%}")
# 方法 1:刪除有缺失值的樣本
features_clean = features.dropna()
label_clean = label.dropna()
# 方法 2:各股票依自己的歷史向前填補(適合時間序列)
features_filled = features.groupby(level='instrument').ffill()
label_filled = label.groupby(level='instrument').ffill()
# 方法 3:填 0(適合技術指標)
features_zero = features.fillna(0)預測結果如何轉換為交易訊號?
model.predict() 回傳的是日期 × 股票的 FinlabDataFrame,可以直接用選股語法:
# 方法 1:買入前 N 名
position = pred.is_largest(30)
# 方法 2:設定閾值
position = pred > pred.quantile(0.8) # 買入前 20%
# 方法 3:多空策略
long_position = pred.is_largest(20) # 做多前 20 名
short_position = pred.is_smallest(20) # 做空後 20 名
position = long_position.astype(float) - short_position.astype(float)
# 方法 4:依預測排名分配權重(每天權重加總為 1)
scores = pred.rank(axis=1, pct=True)
position = scores.div(scores.sum(axis=1), axis=0)下一步
- 機器學習策略完整流程 從特徵、標籤、訓練到回測的端到端開發指南。
- 機器學習策略開發 特徵工程、標籤設計、模型訓練與避免過度配適的詳細教學。
- 技術指標 用 TA-Lib 批次計算台股技術指標。
- 歷史回測 把預測出來的持股交給
sim()回測。
qlib 框架說明見 qlib 官方文件,所有技術指標見 TA-Lib 指標列表。
這一頁有幫到你嗎?