文件
開啟 Studio

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.DataFrame

The combine function takes a dictionary of features as input and combines them into a single pandas DataFrame. combine 函數接受一個特徵字典作為輸入,並將它們合併成一個 pandas DataFrame。

參數

features dict[str, pd.DataFrame | Callable]

a dictionary where values are dataframes or callables returning dataframes. 索引為日期時間,欄位 為證券代碼的 DataFrame,或可呼叫以取得 DataFrame 的函式。

resample str | pd.DatetimeIndex

Optional 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_filter pd.DataFrame

a boolean dictionary where index is date and columns are instrument representing the filter of features.

**kwargs Any 預設 {}

Additional keyword arguments to pass to the resampler function. 傳遞給重新取樣函數 resampler 的其他關鍵字引數。

回傳

pd.DataFrame

A 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()
datetimeinstrumentrsipb
2020-01-01110102
2020-01-0211021003
2020-01-0311081004

以每週最後交易日為錨點,特徵與未來兩期報酬使用同一組日期:

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.DataFrame

Calculate technical indicator values for a list of feature names.

參數

feature_names list[str] | None

A list of technical indicator feature names. Defaults to None.

factories dict[str, TalibIndicatorFactory] | None

A dictionary of factories to generate technical indicators. Defaults to {"talib": TalibIndicatorFactory()}.

resample str | None

The frequency to resample the data to. Defaults to None.

start_time str | None

The start time of the data. Defaults to None.

end_time str | None

The end time of the data. Defaults to None.

**kwargs Any 預設 {}

Additional keyword arguments to pass to the resampler function.

adj bool 預設 False
cpu int 預設 -1

回傳

pd.DataFrame

pd.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.

參數

lb int 預設 1

The lower bound of the multiplier of the default parameter for the technical indicators.

ub int 預設 10

The upper bound of the multiplier of the default parameter for the technical indicators.

n int 預設 1

The number of random samples for each technical indicator.

factory IndicatorFactory

A 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)
參數型別預設說明
indexpd.Index必填特徵的(datetime, instrument)索引
resamplestr、pd.DatetimeIndex 或 NoneNone重新取樣的頻率或日期錨點,要和特徵一致
periodint1往後算幾期。resample=None 時是交易日數,即使特徵是週頻也一樣
trade_at_pricestr'close'用哪個價格計算
bfillboolFalseTrue 時,上市期間內缺的價格用下一個有效價格補上(上市前、下市後仍是空值)
**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.Series

Calculate the percentage change of market prices over a given period.

參數

index pd.Index

A multi-level index of datetime and instrument.

resample str | None 預設 'D'

The resample frequency for the output data. Defaults to "D".

**kwargs Any 預設 {}

Additional arguments to be passed to the resampler function.

回傳

pd.Series

pd.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.Series

Calculate the maximum adverse excursion of market prices over a given period.

參數

index pd.Index

A multi-level index of datetime and instrument.

period int 預設 1

The number of periods to calculate the percentage change over. Defaults to 1.

trade_at_price str 預設 'close'

The price for execution. Defaults to close.

回傳

pd.Series

pd.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() -> WrapperModel

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

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

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

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

下一步

qlib 框架說明見 qlib 官方文件,所有技術指標見 TA-Lib 指標列表。

↑↓ 選擇 Enter 開啟 Esc 關閉