返回目錄
A
自由數據:用資料科學解鎖個人財務自由 - 第 3 章
第三章:特徵工程與信號發掘
發布於 2026-02-25 01:06
# 第三章:特徵工程與信號發掘
> **本章目標**:將已清理好的財務資料轉化為可供機器學習模型使用的高質量特徵,並透過特徵洞察生成投資信號,為後續模型建構與組合優化奠定基礎。
---
## 3.1 引言
特徵工程是資料科學工作流中最具創造性且最耗時的部分。對於投資而言,特徵的選擇、轉換與合成直接影響模型的預測力與交易表現。本章將從三大資料來源說明:
1. **技術指標**(技術分析產生的數值特徵)
2. **基本面因子**(公司財務數據、估值指標)
3. **文本資料**(新聞、社群、財報文字)
並示範如何將它們融合成統一的特徵集,最後以「買進/賣出」信號為例說明信號發掘的流程。
---
## 3.2 技術指標(Technical Indicators)
技術指標是以價格與成交量為基礎,透過數學函數產生的可量化訊號。常用指標分為兩大類:
| 類別 | 代表指標 | 典型用途 |
|------|----------|-----------|
| 趨勢追蹤 | SMA, EMA, MA | 判斷長短期趨勢 |
| 動量衡量 | RSI, MACD, Stochastic | 判斷過度買賣 |
| 波動度 | Bollinger Bands, ATR | 估計風險水平 |
| 交叉信號 | SMA crossover, MACD cross | 產生交易訊號 |
### 3.2.1 公式與計算
以下列出最常用的三種指標公式,並以 Pandas 及 `ta` 套件為例示範。
| 指標 | 公式 | 參數 |
|------|------|------|
| SMA | \\text{SMA}_n = \frac{1}{n}\sum_{i=0}^{n-1} P_{t-i} | n (週期) |
| EMA | \text{EMA}_n = \alpha P_t + (1-\alpha)\text{EMA}_{n,t-1} | \alpha = 2/(n+1) |
| RSI | 100 - \frac{100}{1 + \frac{\text{AvgGain}}{\text{AvgLoss}}} | 14 |
#### 範例:計算 20 日 SMA 與 MACD
python
import pandas as pd
import ta
# 假設 df 已經包含 'Close' 欄位
# 20 日簡單移動平均
sma20 = ta.trend.sma_indicator(close=df['Close'], window=20)
# MACD
macd = ta.trend.macd(close=df['Close'], window_slow=26, window_fast=12, window_sign=9)
# 加入原始 df
result = df.assign(SMA20=sma20, MACD=macd.macd(), MACD_Signal=macd.macd_signal(), MACD_Hist=macd.macd_diff())
### 3.2.2 特徵選擇與共線性
- **多重共線性**:多個技術指標往往高度相關,例如 SMA20 與 SMA50 的關係。可以使用 **VIF(Variance Inflation Factor)** 進行檢測,若 VIF>5 可考慮移除。
- **特徵重要性**:在訓練模型前,可先用 **RandomForestRegressor** 估算特徵重要度,保留前 N 個特徵。
---
## 3.3 基本面因子(Fundamental Factors)
基本面因子是從公司財務報表、估值指標中提取的數值。常見的因子包含:
| 因子 | 來源 | 代表指標 |
|------|------|----------|
| 估值 | 財報 | PE, PB, PS, EV/EBITDA |
| 成長 | 財報 | 營收成長率, 淨利成長率 |
| 獲利 | 財報 | ROE, ROA, 毛利率 |
| 負債 | 財報 | 負債比率, 負債/資本 |
### 3.3.1 資料取得
- **API**:`yfinance`, `IEX Cloud`, `Alpha Vantage` 提供基本面資料。
- **資料庫**:如 `SEC Edgar` 提供 10-K、10-Q 財報 PDF 轉 CSV。
python
import yfinance as yf
ticker = yf.Ticker('AAPL')
# 取得財務摘要
summary = ticker.get_financials()
# 取得估值指標
info = ticker.info
pe_ratio = info['trailingPE']
pb_ratio = info['priceToBook']
### 3.3.2 數值化與標準化
- **缺失處理**:基本面數據常有缺失,使用 `pandas.DataFrame.interpolate()` 或 `ffill/bfill`。
- **標準化**:為避免量級差異,使用 **z-score** 或 **Min-Max Scaling**。
python
from sklearn.preprocessing import StandardScaler
scaler = StandardScaler()
basic_features_scaled = scaler.fit_transform(basic_df)
---
## 3.4 文本資料處理(Text Data Processing)
文本資料提供了市場情緒與公司新聞的非結構化訊息,對於機器學習模型極具價值。常見來源:
| 來源 | 例子 |
|------|------|
| 財經新聞 | Reuters, Bloomberg |
| 研究報告 | Morningstar, 研究所 |
| 社群媒體 | Twitter, Reddit r/WallStreetBets |
### 3.4.1 NLP 主要流程
1. **分詞 / Tokenisation**:`spaCy`, `NLTK`。對於中文,可使用 `jieba` 或 `HanLP`。
2. **向量化**:
- **詞袋模型**(Bag‑of‑Words)+ `CountVectorizer` 或 `TfidfVectorizer`。
- **詞嵌入**:Word2Vec, GloVe, FastText。
- **Transformer**:BERT、RoBERTa、FinBERT 等。
3. **情緒分析**:
- **簡易**:使用正負詞庫(如 `snowNLP`)計算情緒分數。
- **先進**:`transformers` `pipeline('sentiment-analysis')`。
4. **主題建模**:LDA、NMF。
#### 範例:英文財經新聞情緒分數(FinBERT)
python
from transformers import pipeline
sentiment_pipeline = pipeline('sentiment-analysis', model='yiyang/finbert-base', device=0)
# 假設 news_texts 是新聞標題列表
scores = [sentiment_pipeline(t)[0]['score'] for t in news_texts]
# 加入特徵集
feature_df['news_sentiment'] = scores
#### 中文情緒分析範例(SnowNLP)
python
from snownlp import SnowNLP
def compute_sentiment(text):
s = SnowNLP(text)
return s.sentiments # 0-1 之間
sentiments = news_df['content'].apply(compute_sentiment)
news_df['sentiment'] = sentiments
### 3.4.2 特色提取
- **情緒指標**:每日平均情緒分數、正負比率。
- **事件指標**:關鍵詞頻次、關鍵事件(如併購、股東變動)標記。
- **主題分佈**:使用 `NMF` 產生主題權重,可作為額外特徵。
---
## 3.5 統一特徵構建流程
> **目標**:將價格、財務、文本三種特徵按時間戳對齊,形成完整、乾淨且可直接送入模型的特徵矩陣。
### 3.5.1 步驟
1. **資料合併**:
python
merged = price_df.join(basic_features, how='inner', on='Date')
merged = merged.join(text_features, how='left', on='Date')
2. **缺失處理**:填補 `NaN`,必要時刪除首尾缺失行。
3. **時間窗特徵**:利用 `rolling()` 計算移動平均、標準差等。
4. **編碼**:
- **類別型**:One‑Hot (`pd.get_dummies`) 或 Target Encoding。
- **序列型**:如果使用 RNN 或 LSTM,可將特徵轉成三維張量。
5. **標準化**:全局 `StandardScaler` 或 `RobustScaler`。
6. **特徵儲存**:
python
import joblib
joblib.dump(preprocessor, 'models/preprocessor.joblib')
### 3.5.2 版本化與重現性
- **配置檔**:`config.yaml` 內定義所有窗口、因子、資料來源。
- **版本控制**:使用 `git lfs` 儲存大文件;`DVC` 可管理資料版本。
---
## 3.6 信號發掘(Signal Generation)
交易信號是模型或手動規則所產出的「買進/賣出/觀望」判斷。以技術指標為例,常見的買賣訊號規則如下:
| 規則 | 條件 |
|------|------|
| SMA Crossover | \\text{SMA}_{short} > \text{SMA}_{long} → 買進 |
| MACD Cross | MACD > Signal → 買進;MACD < Signal → 賣出 |
| RSI Overbought | RSI > 70 → 賣出 |
| RSI Oversold | RSI < 30 → 買進 |
### 3.6.1 範例:生成交易訊號
python
# 以 SMA20 與 SMA50 交叉為例
condition_buy = (sma20 > sma50) & (df['Date'].shift(1) <= df['Date'].shift(1))
condition_sell = (sma20 < sma50) & (df['Date'].shift(1) <= df['Date'].shift(1))
signals = pd.Series(index=df.index, data=0)
signals[condition_buy] = 1
signals[condition_sell] = -1
df = df.assign(signal=signals)
### 3.6.2 風險調整的評估
- **夏普率**:\(\frac{E[R_p] - R_f}{\sigma_{p}}\)
- **Information Ratio**:\(\frac{E[R_p] - R_b}{\sigma_{p-b}}\)
- 在回測時,對每條信號計算其風險調整後的報酬,以篩選「有價值」的訊號。
---
## 3.7 進階特徵技術
| 技術 | 目的 | 典型實作 |
|------|------|----------|
| FFT | 週期特徵 | `np.fft.fft` 取得頻域分量 |
| Rolling Stats | 時間窗特徵 | `df.rolling(window).agg(['mean', 'std'])` |
| Lag Features | 轉換序列型資料 | `df.shift(k)` |
| 混合特徵 | 融合多來源 | `pd.concat([tech_df, basic_df, text_df], axis=1)` |
> **提醒**:使用 FFT 時需先進行 **去趨勢**(如差分)才能得到穩定的頻域特徵;滾動特徵要確保 **未泄露** 任何未來資訊。
---
## 3.8 實務技巧
1. **特徵重要性評估**
- **Permutation Importance**:隨機打亂單一特徵,觀察模型準確率變化。
- **SHAP**:提供每筆樣本的特徵貢獻度。
python
import shap
explainer = shap.TreeExplainer(model)
shap_values = explainer.shap_values(X_train)
shap.summary_plot(shap_values, X_train)
2. **避免資料泄露**
- 所有 **訓練/驗證/測試** 切分前均應在「完整」資料上進行合併與缺失填補。
- 在計算滾動特徵時,只允許 **past‑only** 的窗口。
3. **可重複性**
- 將特徵工程程式封裝成函式,並在 `requirements.txt` 與 `pyproject.toml` 明確列出依賴。
- 使用 `dvc run` 建立流水線,確保每次重新執行時能得到相同的特徵矩陣。
---
## 3.9 結語
特徵工程不是「一次性」的工作,而是投資策略開發過程中持續迭代、優化的核心。透過技術指標、基本面因子與文本特徵的深度結合,可讓模型擁有多維度的市場觀察,進而產生更可靠的交易信號。本章所示範的範例與工具,將在接下來的「模型建構」與「策略回測」章節中得到直接應用。
---
> **下一章**:在「特徵化資料」之後,我們將聚焦於**機器學習模型**(回歸、分類、強化學習)的訓練、超參數調優與模型選擇,並以「夏普率」為基準評估不同模型在投資組合中的貢獻。