返回目錄
A
數據驅動的投資策略:從數據清洗到模型部署 - 第 3 章
第3章 特徵工程:從乾淨資料抽取投資信號
發布於 2026-03-05 05:11
# 第3章 特徵工程:從乾淨資料抽取投資信號
> **目標**:將前章完成的「清洗後」S&P 500 日行情轉換為機器學習模型可直接使用的數值特徵。
>
> **為什麼特徵工程重要?**
>
> 在投資領域,模型的表現往往不只是演算法本身,而是資料提供的資訊量與質量。透過合適的特徵工程,我們能把金融市場的非線性、時序性、異質性轉化為模型易於學習的模式,並有效降低維度、提升可解釋性。
## 3.1 特徵工程流程總覽
| 步驟 | 內容 | 工具 | 典型範例 |
|------|------|------|-----------|
| 1. 載入乾淨資料 | 讀取 `clean_sp500.csv` | `pandas` | `pd.read_csv()` |
| 2. 基本統計特徵 | 收盤價、成交量等 | `pandas` | `Close`, `Volume` |
| 3. 時序衍生特徵 | 滾動統計、滾動回報 | `pandas` | `rolling.mean()` |
| 4. 技術指標 | 移動平均、RSI、MACD | `ta` | `ta.trend.SMAIndicator()` |
| 5. 欠采樣 / 調整尺度 | 標準化、對數變換 | `scikit-learn` | `StandardScaler()` |
| 6. 特徵選擇 | 主成分、重要性排序 | `sklearn` | `SelectKBest()` |
| 7. 檢查多重共線性 | VIF | `statsmodels` | `variance_inflation_factor()` |
> **提示**:在實際部署前,將「特徵工程」寫成可重複的函式或 pipeline,確保日後數據更新時可以自動執行。
## 3.2 基本統計特徵
python
import pandas as pd
import numpy as np
ds = pd.read_csv('clean_sp500.csv', parse_dates=['Date'])
ds.set_index('Date', inplace=True)
# 基本收盤價、成交量
features = ds[['Close', 'Volume']].copy()
# 日回報
features['Return'] = features['Close'].pct_change().fillna(0)
# 滑動對數回報(可減少極端值影響)
features['LogReturn'] = np.log(features['Close'] / features['Close'].shift(1)).fillna(0)
> **說明**:日回報是最簡單且廣泛使用的特徵,然而在高頻或高波動市場,日回報可能過於噪聲,這時可以考慮使用更長期的滾動回報或其他衍生指標。
## 3.3 滾動統計與衍生特徵
| 參數 | 說明 | 實作 |
|------|------|------|
| 20日移動平均 | 趨勢判斷 | `SMA_20` |
| 50日波動率 | 市場風險 | `Vol_50` |
| 14日RSI | 超買超賣 | `RSI_14` |
| 12日EMA - 26日EMA | MACD | `MACD` |
python
import ta
# 移動平均
features['SMA_20'] = ta.trend.sma_indicator(features['Close'], window=20)
features['EMA_20'] = ta.trend.ema_indicator(features['Close'], window=20)
# 波動率(20日標準差)
features['Vol_20'] = features['Return'].rolling(window=20).std() * np.sqrt(252)
# RSI
features['RSI_14'] = ta.momentum.rsi(features['Close'], window=14)
# MACD
macd = ta.trend.MACD(features['Close'], window_slow=26, window_fast=12, window_sign=9)
features['MACD'] = macd.macd()
features['MACD_signal'] = macd.macd_signal()
features['MACD_diff'] = macd.macd_diff()
> **備註**:`ta`(Technical Analysis Library in Python)提供了大量經典技術指標,使用時要留意參數的市場適應性。若市場結構變化,建議重新評估參數。
## 3.4 高階衍生特徵
### 3.4.1 交易量與價量關係
- **成交量動量**:量值與前期量值之差。
- **OBV(On-Balance Volume)**:累計成交量,反映買賣力量。
python
# 交易量動量
features['Volume_Momentum'] = features['Volume'].diff().fillna(0)
# OBV
obv = ta.volume.on_balance_volume(features['Close'], features['Volume'])
features['OBV'] = obv
### 3.4.2 複合指標
- **Bollinger Bands**:上下帶判斷波動極端。
- **ADX(Average Directional Index)**:趨勢強度。
python
bb = ta.volatility.BollingerBands(features['Close'], window=20, window_dev=2)
features['BB_High'] = bb.bollinger_hband()
features['BB_Low'] = bb.bollinger_lband()
features['BB_Middle'] = bb.bollinger_mavg()
adx = ta.trend.ADXIndicator(features['High'], features['Low'], features['Close'], window=14)
features['ADX'] = adx.adx()
> **小結**:這些複合指標往往需要多個子指標的協同判斷,避免單一指標的誤判。使用時可視覺化或交叉驗證以確保穩定性。
## 3.5 特徵尺度與轉換
| 轉換 | 目的 | 方式 |
|------|------|------|
| 標準化 | 消除不同量級影響 | `StandardScaler()` |
| 對數變換 | 轉化長尾分佈 | `np.log1p()` |
| 分位數轉換 | 對極端值穩健 | `QuantileTransformer()` |
python
from sklearn.preprocessing import StandardScaler, QuantileTransformer
numeric_cols = features.select_dtypes(include=[np.number]).columns
scaler = StandardScaler()
features_scaled = scaler.fit_transform(features[numeric_cols])
# 轉換後回到 DataFrame
features_scaled_df = pd.DataFrame(features_scaled, index=features.index, columns=numeric_cols)
> **實務建議**:對於回測期間的數據,不要在訓練集之外擷取統計量,以免資訊洩漏。可使用 `TimeSeriesSplit` 進行交叉驗證。
## 3.6 特徵選擇與降維
### 3.6.1 基於相關係數的篩選
python
corr_matrix = features_scaled_df.corr().abs()
upper = corr_matrix.where(np.triu(np.ones(corr_matrix.shape), k=1).astype(bool))
to_drop = [column for column in upper.columns if any(upper[column] > 0.9)]
features_reduced = features_scaled_df.drop(columns=to_drop)
### 3.6.2 主成分分析(PCA)
python
from sklearn.decomposition import PCA
pca = PCA(n_components=0.95) # 保留 95% 變異
principal_components = pca.fit_transform(features_reduced)
print(f'保留 {pca.n_components_} 個主成分')
> **注意**:PCA 會降低可解釋性,若模型需要解釋投資因子,建議保持原始特徵並用特徵重要性來篩選。
## 3.7 特徵重要性評估
以 `XGBoost` 為例,快速判斷哪些特徵對預測最有價值。
python
import xgboost as xgb
X = features_reduced
y = (features['Return'].shift(-1) > 0).astype(int) # 明日漲跌作為目標
model = xgb.XGBClassifier(n_estimators=200, learning_rate=0.05, max_depth=5, subsample=0.8, colsample_bytree=0.8)
model.fit(X[:-1], y[:-1]) # 迴避最後一筆無目標
importances = model.feature_importances_
feat_importances = pd.Series(importances, index=X.columns).sort_values(ascending=False)
print(feat_importances.head(10))
> **解讀**:高重要度特徵往往包含關鍵訊息,如 `SMA_20`、`RSI_14`、`Vol_20` 等;若某些技術指標重要度極低,可考慮移除,以減少模型複雜度。
## 3.8 特徵工程的最佳實踐
| 原則 | 說明 |
|------|------|
| **避免資訊洩漏** | 在預測之前,所有統計量(均值、標準差)都應基於訓練集計算。 |
| **自動化 pipeline** | 使用 `sklearn.pipeline` 或 `mlflow` 追蹤特徵版本。 |
| **可解釋性** | 儘量保留物理意義的特徵,避免「黑盒」特徵化。 |
| **持續監控** | 特徵分佈隨時間變化,需定期檢查概況圖。 |
| **多模型驗證** | 在多個演算法上評估特徵重要性,避免模型偏差。
## 3.9 小結
特徵工程是把市場信號轉化為機器可讀的關鍵步驟。透過「基本統計」→「技術指標」→「衍生特徵」→「尺度轉換」→「特徵選擇」的層層遞進,我們可以構建出既有解釋性又有預測力的特徵集。接下來的第4章,我們將以這些特徵為基礎,進入「模型訓練與驗證」的核心環節,探討如何選擇演算法、處理時序交叉驗證,以及避免過擬合。