Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Transformer 在交易策略中的應用

一句話理解

Transformer 就像一個「記憶力超強的分析師」,能同時看數百根 K 線的關係,找出隱藏的規律來做交易決策。


Transformer vs 基因演算法(GA):根本差異

┌─────────────────────────────────────────────────────────┐
│              基因演算法 (Genetic Algorithm)                │
│                                                         │
│  第 1 代策略群          交叉/變異           第 N 代策略群   │
│  ┌───┐ ┌───┐ ┌───┐    篩選適者          ┌───┐           │
│  │RSI│ │MA │ │VOL│  ──────────────►     │最佳│           │
│  │>70│ │交叉│ │放大│   淘汰劣者          │組合│           │
│  └───┘ └───┘ └───┘                      └───┘           │
│                                                         │
│  白話:像抽籤試菜單,吃過幾百種組合後,                      │
│        留下最好吃的那幾道。                                │
│  專業術語:參數空間搜索 (Parameter Space Search)           │
│           適應度函數 (Fitness Function)                   │
│           交叉算子 (Crossover Operator)                   │
└─────────────────────────────────────────────────────────┘

┌─────────────────────────────────────────────────────────┐
│                  Transformer                            │
│                                                         │
│  歷史數據             自注意力機制            預測/決策    │
│  ┌──┬──┬──┬──┐      (Self-Attention)      ┌────────┐   │
│  │K1│K2│K3│K4│  ──► 每根 K 線都能        ──► │明天漲跌│   │
│  │  │  │  │  │      「看到」所有其他        │買/賣/持│   │
│  └──┴──┴──┴──┘      K 線的資訊             └────────┘   │
│                                                         │
│  白話:像一個讀了十年盤的老手,看到現在的走勢               │
│        就知道「這跟 2020/03 崩盤前好像」。                 │
│  專業術語:序列建模 (Sequence Modeling)                    │
│           注意力權重 (Attention Weights)                  │
│           表示學習 (Representation Learning)              │
└─────────────────────────────────────────────────────────┘

關鍵差異表:

面向基因演算法 (GA)Transformer
核心思路暴力搜索最佳參數組合學習數據中的模式 (Pattern)
策略形式規則型 (If-Then)黑箱型 (端對端預測)
可解釋性高(看得到規則)低(需要額外工具解釋)
過擬合風險中(靠驗證集控制)高(模型容量大,需正則化)
適應新數據需重新演化可微調 (Fine-tune)
白話比喻用排列組合找最好的食譜培養一個有經驗的廚師

四種主要應用場景

1. 時序預測 (Time Series Forecasting)

輸入序列 (Input Sequence)
┌─────┬─────┬─────┬─────┬─────┬─────┐
│Day 1│Day 2│Day 3│Day 4│Day 5│Day 6│  ← 每天的特徵向量
│OHLCV│OHLCV│OHLCV│OHLCV│OHLCV│OHLCV│    (開高低收量)
└──┬──┴──┬──┴──┬──┴──┬──┴──┬──┴──┬──┘
   │     │     │     │     │     │
   ▼     ▼     ▼     ▼     ▼     ▼
┌──────────────────────────────────────┐
│    Positional Encoding (位置編碼)     │  ← 告訴模型「誰在前誰在後」
├──────────────────────────────────────┤
│    Multi-Head Self-Attention         │  ← Day1 可以直接關注 Day6
│    (多頭自注意力機制)                  │    不需要像 RNN 一步步傳遞
├──────────────────────────────────────┤
│    Feed-Forward Network              │
│    (前饋神經網路)                      │
├──────────────────────────────────────┤
│    Output: 預測 Day 7 的收盤價         │
└──────────────────────────────────────┘

白話舉例: 想像你在看一部 120 集的連續劇(120 天的股價)。RNN 像是從第 1 集看到第 120 集,到最後已經忘了第 1 集演什麼。Transformer 則像是能同時攤開所有集數的劇本,直接發現「第 3 集的伏筆在第 118 集回收了」。

專業用詞:

  • OHLCV:Open/High/Low/Close/Volume(開盤價/最高價/最低價/收盤價/成交量)
  • Positional Encoding(位置編碼):因為 Transformer 天生不知道順序,需要額外注入時間資訊
  • Long-range Dependency(長程依賴):能捕捉相隔很遠的數據點之間的關係

2. 多因子選股 (Multi-Factor Stock Selection)

                        因子 = "單字"
                        股票 = "句子"

股票 A 的因子序列:
┌────────┬────────┬────────┬────────┬────────┐
│ ROE    │ 營收   │ 本益比  │ 法人   │ 融資   │
│ 25%    │ 成長20%│ PE=12  │ 買超   │ 減少   │
└───┬────┴───┬────┴───┬────┴───┬────┴───┬────┘
    │        │        │        │        │
    ▼        ▼        ▼        ▼        ▼
┌──────────────────────────────────────────────┐
│           Transformer Encoder                 │
│                                              │
│  Self-Attention 發現:                        │
│  「ROE 高 + 法人買超 + 融資減少」              │
│   這三個因子一起出現時,特別重要!              │
│   (= 注意力權重高)                            │
│                                              │
│  類似 NLP 中理解                              │
│  「銀行」在「河岸」vs「金融」語境的不同含義     │
└──────────────────┬───────────────────────────┘
                   ▼
            ┌──────────────┐
            │ 評分:0.87    │  ← 高分 = 值得買入
            │ (買入信號)     │
            └──────────────┘

白話舉例: 傳統選股像是拿一張清單逐項打勾(ROE > 15% ? 勾!PE < 20 ? 勾!)。Transformer 選股則像是一個老手看完所有數據後說:「這幾個指標一起看才有意義,光看單一指標會被騙。」它學會了因子之間的交互作用 (Interaction Effect)

專業用詞:

  • Embedding(嵌入向量):把每個因子轉成模型能理解的數字表示
  • Cross-Attention(交叉注意力):讓不同因子之間互相參照
  • Alpha Factor(超額報酬因子):能帶來超越大盤報酬的因子

3. 情緒分析 (Sentiment Analysis)

原始文本                    Transformer 處理流程
─────────                  ──────────────────

「台積電法說會釋出           Tokenization (分詞)
  正面展望,但外資              │
  卻大量賣超」                  ▼
                          ┌──────────────────┐
                          │ [正面展望] [但]    │
                          │ [外資] [大量賣超]  │
                          └────────┬─────────┘
                                   │
                          Self-Attention 發現:
                          「但」這個轉折詞讓
                          「正面展望」的影響力
                           被大幅削弱
                                   │
                                   ▼
                          ┌──────────────────┐
                          │ 情緒分數:-0.3    │
                          │ (偏空,因為行動    │
                          │  比言語更重要)     │
                          └──────────────────┘
                                   │
                                   ▼
                          交易信號:減碼 / 觀望

白話舉例: 就像你跟朋友說「這家餐廳東西很好吃,但是好貴」。人類聽得出這是偏負面的評價。Transformer 也學會了「但是」後面的內容通常更重要,所以它不會被前半段的正面詞彙騙到。

專業用詞:

  • NLP(Natural Language Processing):自然語言處理
  • Tokenization(分詞):把文本切成模型能處理的最小單位
  • Sentiment Score(情緒分數):量化市場情緒的數值,通常 -1(極度悲觀)到 +1(極度樂觀)
  • Alternative Data(替代數據):新聞、社群媒體等非傳統金融數據

4. 強化學習結合 (Transformer + Reinforcement Learning)

這是最接近「自動產生交易策略」的做法

┌──────────────────────────────────────────────────────┐
│                    交易環境 (Environment)              │
│  ┌────────────────────────────────────────────────┐  │
│  │ 市場狀態 (State)                                │  │
│  │ • 過去 N 天的 OHLCV                             │  │
│  │ • 當前持倉、資金                                 │  │
│  │ • 技術指標 (RSI, MACD, 布林通道...)              │  │
│  └───────────────────┬────────────────────────────┘  │
│                      │                               │
│                      ▼                               │
│  ┌────────────────────────────────────────────────┐  │
│  │ Transformer (策略網路 / Policy Network)         │  │
│  │                                                │  │
│  │ 用 Self-Attention 分析所有歷史狀態              │  │
│  │ 輸出動作機率分布:                               │  │
│  │   買入 30% | 賣出 10% | 持有 60%               │  │
│  └───────────────────┬────────────────────────────┘  │
│                      │                               │
│                      ▼                               │
│              ┌──────────────┐                        │
│              │ 執行動作      │                        │
│              │ (Action)     │                        │
│              └──────┬───────┘                        │
│                     │                                │
│                     ▼                                │
│              ┌──────────────┐                        │
│              │ 獲得獎勵      │  ← 賺錢 = 正獎勵      │
│              │ (Reward)     │    賠錢 = 負獎勵       │
│              └──────┬───────┘    (含交易成本)        │
│                     │                                │
│                     ▼                                │
│              反覆訓練數千回合                          │
│              (Episodes)                              │
│              直到學會穩定獲利                          │
└──────────────────────────────────────────────────────┘

白話舉例: 想像訓練一個打電動的 AI。遊戲畫面就是「盤面」,按鍵就是「買/賣/持有」,得分就是「賺多少錢」。AI 一開始亂按(隨機交易),但玩了幾萬局後,它摸出了一套贏錢的操作手法。這個「大腦」就是 Transformer。

與基因演算法對比:

  • GA 產生的策略:如果 RSI > 70 且 MA5 > MA20 則買入 → 人看得懂
  • Transformer + RL 產生的策略:一個神經網路權重矩陣 → 人看不懂,但可能更強

專業用詞:

  • DRL(Deep Reinforcement Learning):深度強化學習
  • Policy Network(策略網路):決定在每個狀態下該採取什麼動作的神經網路
  • Reward Shaping(獎勵設計):如何定義「好的交易行為」,例如考慮夏普比率而非單純報酬
  • Episode(回合):一次完整的模擬交易過程(如模擬一整年)
  • Action Space(動作空間):模型可以選擇的所有動作集合

常見疑惑 Q&A

Q1: Transformer 不是拿來做翻譯/ChatGPT 的嗎?怎麼能做交易?

NLP 領域                          金融領域
────────                         ────────
句子 = 一串文字 token              K 線 = 一串時間點的特徵向量
理解上下文語意                     理解價格走勢的前後關係
預測下一個字                       預測下一個時間點的價格/動作

    兩者的數學結構完全一樣!
    都是「序列 → 序列」的問題

白話: Transformer 不在乎它處理的是文字還是數字,它只看到「一排東西」,然後學習這排東西之間的關係。股價序列對它來說,跟一段英文句子沒有本質區別。


Q2: 哪種方法比較適合散戶?

方法難度資料需求算力需求散戶可行性
GA 暴力搜參數高 - 用 Python 幾百行就能跑
Transformer 預測中 - 需要 GPU,但 Colab 免費可用
Transformer + RL很高低 - 需要大量調參和算力
情緒分析需爬蟲中 - 可用預訓練模型

Q3: Transformer 做交易真的能賺錢嗎?

誠實的答案: 不保證。主要挑戰:

  1. 過擬合 (Overfitting):模型可能只是「背答案」,在歷史數據上表現很好,但實盤慘賠
  2. 市場非定態 (Non-stationary):市場規則會變,2020 年有效的模式到 2025 年可能失效
  3. 交易成本 (Transaction Cost):模型可能頻繁交易,手續費吃掉利潤
  4. 滑價 (Slippage):回測假設你能以收盤價成交,但實際上不一定
                    回測績效
                    ↑
                    │    ╱ 回測報酬(看起來很美)
                    │   ╱
                    │  ╱
                    │ ╱─────── 扣除交易成本
                    │╱
                    ├──────── 加上滑價影響
                    │╲
                    │ ╲────── 考慮過擬合
                    │  ╲
                    │   ╲  實際報酬(現實很骨感)
                    └──────────────────────► 時間

FinLab + Transformer 實戰整合

整體架構:資料流全景圖

┌─────────────────────────────────────────────────────────────────┐
│                     FinLab 資料層 (Data Layer)                   │
│                                                                 │
│  finlab.data.get('price:收盤價')          ← 日頻 OHLCV          │
│  finlab.data.get('monthly_revenue:當月營收') ← 月頻基本面        │
│  finlab.data.get('price_earning_ratio:本益比') ← 估值指標        │
│  finlab.data.get('institutional_investors:外資買賣超') ← 籌碼面  │
│  finlab.data.get('etl:adj_close')         ← 還原除權息收盤價     │
│                                                                 │
│  輸出格式:FinlabDataFrame (index=日期, columns=股票代號)         │
└───────────────────────────┬─────────────────────────────────────┘
                            │
                            ▼
┌─────────────────────────────────────────────────────────────────┐
│                  特徵工程層 (Feature Engineering)                 │
│                                                                 │
│  1. 對齊頻率:月營收 → resample 到日頻(前向填充 ffill)          │
│  2. 計算衍生特徵:報酬率、波動率、技術指標                        │
│  3. 組裝特徵張量:(股票數 × 時間窗口 × 特徵數)                   │
│  4. 標準化 (Normalization):Z-score 或 Min-Max                   │
└───────────────────────────┬─────────────────────────────────────┘
                            │
                            ▼
┌─────────────────────────────────────────────────────────────────┐
│                  Transformer 模型層 (Model Layer)                │
│                                                                 │
│  輸入:過去 60 天的多維特徵序列                                   │
│  輸出:未來 5 天的預測報酬 / 漲跌機率 / 選股評分                  │
└───────────────────────────┬─────────────────────────────────────┘
                            │
                            ▼
┌─────────────────────────────────────────────────────────────────┐
│               FinLab 回測層 (Backtest Layer)                     │
│                                                                 │
│  Transformer 預測結果 → 轉換成 FinlabDataFrame 的選股條件         │
│  → finlab.backtest.sim() 回測                                   │
│  → 績效報告(CAGR、Sharpe、MaxDrawdown)                        │
└─────────────────────────────────────────────────────────────────┘

白話: FinLab 負責「搬食材」(抓資料)和「試吃評分」(回測),Transformer 負責「炒菜」(學規律做預測)。兩者各司其職。


場景 A:用 Transformer 預測股價,FinLab 回測

這是最直覺的結合方式:模型預測漲跌,FinLab 驗證能不能賺錢。

import finlab
from finlab import data
import pandas as pd
import numpy as np
import torch
import torch.nn as nn

finlab.login('YOUR_API_KEY')

# =========================================
# Step 1: 用 FinLab 取得乾淨的台股數據
# =========================================

close = data.get('price:收盤價')          # FinlabDataFrame, shape: (日期, 股票代號)
high  = data.get('price:最高價')
low   = data.get('price:最低價')
vol   = data.get('price:成交股數')

# FinLab 的好處:自動處理除權息、停牌、下市
adj_close = data.get('etl:adj_close')     # 還原除權息後的收盤價

# =========================================
# Step 2: 特徵工程 — 把原始數據變成模型吃得下的格式
# =========================================

# 計算技術指標(用 FinlabDataFrame 的語法糖)
returns_1d  = adj_close.pct_change(1)     # 日報酬率
returns_5d  = adj_close.pct_change(5)     # 週報酬率
volatility  = returns_1d.rolling(20).std()  # 20 日波動率
ma_ratio    = adj_close / adj_close.rolling(20).mean()  # 股價 / 20MA 比值

# 加入基本面(FinLab 自動對齊不同頻率的數據!)
revenue = data.get('monthly_revenue:當月營收')
rev_yoy = revenue.pct_change(12)          # 營收年增率(月頻,FinLab 自動 ffill 到日頻)

# 加入籌碼面
foreign_buy = data.get('institutional_investors:外資買賣超')
特徵組裝示意圖:把多張 FinlabDataFrame 堆疊成 3D 張量

        股票 2330 的特徵矩陣(60天 × 6特徵)
        ┌─────────────────────────────────────────┐
        │ 日期    │報酬率│波動率│MA比│營收YoY│外資│  │
        ├─────────┼──────┼──────┼────┼───────┼────┤  │
        │ Day -60 │ 0.02 │ 0.15 │1.03│ 0.25  │ +5 │  │
        │ Day -59 │-0.01 │ 0.14 │1.02│ 0.25  │ -2 │  │
        │  ...    │ ...  │ ...  │... │  ...  │... │  │
        │ Day -1  │ 0.03 │ 0.18 │0.98│ 0.30  │+10 │  │
        └─────────────────────────────────────────┘
            ↑
            這就是 Transformer 的輸入序列
            序列長度 = 60(回看窗口)
            每個 token 的維度 = 6(特徵數)
# =========================================
# Step 3: 組裝 PyTorch Dataset
# =========================================

def build_dataset(stock_id, lookback=60, horizon=5):
    """
    為單一股票建立訓練資料

    Args:
        stock_id: 股票代號,如 '2330'
        lookback: 回看天數(= Transformer 的序列長度)
        horizon:  預測天數(未來幾天的報酬)

    Returns:
        X: shape (樣本數, lookback, 特徵數) — 模型輸入
        y: shape (樣本數,) — 未來 horizon 天的報酬率
    """
    # 把各特徵 DataFrame 對齊、取出單一股票
    features = pd.DataFrame({
        'returns': returns_1d[stock_id],
        'volatility': volatility[stock_id],
        'ma_ratio': ma_ratio[stock_id],
        'rev_yoy': rev_yoy[stock_id],
        'foreign': foreign_buy[stock_id],
    }).dropna()

    # 標準化(重要!Transformer 對數值尺度敏感)
    features = (features - features.rolling(252).mean()) / features.rolling(252).std()
    features = features.dropna()

    # 未來 horizon 天的報酬作為標籤
    future_ret = adj_close[stock_id].pct_change(horizon).shift(-horizon)

    X_list, y_list = [], []
    for i in range(lookback, len(features) - horizon):
        X_list.append(features.iloc[i-lookback:i].values)
        y_list.append(future_ret.iloc[i])

    return np.array(X_list), np.array(y_list)

X, y = build_dataset('2330', lookback=60, horizon=5)
# X.shape = (樣本數, 60, 5)
# y.shape = (樣本數,)
時間軸上的滑動窗口 (Sliding Window):

Day:  1   2   3  ...  60  61  62  ...  120  121
      ├──── 樣本 1 ────┤►y1
          ├──── 樣本 2 ────┤►y2
              ├──── 樣本 3 ────┤►y3
                                ...
                        ├──── 樣本 N ────┤►yN

每個樣本 = 60 天的特徵矩陣
每個 y   = 第 61~65 天的累積報酬率
# =========================================
# Step 4: Transformer 模型定義
# =========================================

class StockTransformer(nn.Module):
    """
    用 Transformer Encoder 做股價預測

    白話:就是把一段時間的行情數據丟進去,
         模型自己學哪些天的哪些特徵最重要,
         然後輸出一個預測值。
    """
    def __init__(self, d_feature=5, d_model=64, nhead=4, num_layers=2, dropout=0.1):
        super().__init__()

        # 把原始特徵投影到模型維度
        self.input_proj = nn.Linear(d_feature, d_model)

        # 位置編碼:告訴模型「第幾天」
        self.pos_encoding = nn.Parameter(torch.randn(1, 200, d_model) * 0.02)

        # Transformer Encoder
        encoder_layer = nn.TransformerEncoderLayer(
            d_model=d_model,
            nhead=nhead,          # 多頭注意力的頭數
            dim_feedforward=128,  # FFN 隱藏層大小
            dropout=dropout,
            batch_first=True
        )
        self.transformer = nn.TransformerEncoder(encoder_layer, num_layers=num_layers)

        # 輸出頭:預測未來報酬
        self.output_head = nn.Sequential(
            nn.Linear(d_model, 32),
            nn.ReLU(),
            nn.Linear(32, 1)     # 輸出 1 個數字 = 預測報酬率
        )

    def forward(self, x):
        # x shape: (batch, seq_len, d_feature)
        x = self.input_proj(x)                          # → (batch, seq_len, d_model)
        x = x + self.pos_encoding[:, :x.size(1), :]     # 加上位置資訊
        x = self.transformer(x)                          # Self-Attention 魔法發生處
        x = x[:, -1, :]                                  # 取最後一天的隱藏狀態
        return self.output_head(x).squeeze(-1)           # → (batch,) 預測報酬
模型內部資料流:

輸入: (batch=32, seq_len=60, features=5)
          │
    ┌─────▼──────┐
    │ Linear 5→64│  Input Projection (特徵投影)
    └─────┬──────┘
          │ + Positional Encoding
    ┌─────▼──────────────────────────┐
    │ Transformer Encoder Layer × 2  │
    │                                │
    │  ┌──────────────────────┐      │
    │  │ Multi-Head Attention │      │  ← Day 1 可以直接看 Day 60
    │  │ (4 heads × 16 dim)  │      │
    │  └──────────┬───────────┘      │
    │  ┌──────────▼───────────┐      │
    │  │ Feed-Forward Network │      │
    │  │ 64 → 128 → 64       │      │
    │  └──────────┬───────────┘      │
    └─────────────┼──────────────────┘
          ┌───────▼────────┐
          │ 取最後一天的輸出 │  ← 濃縮了 60 天所有資訊
          └───────┬────────┘
          ┌───────▼────────┐
          │ Linear 64→32→1 │  Output Head
          └───────┬────────┘
                  ▼
          預測報酬: 0.023 (= 預計漲 2.3%)
# =========================================
# Step 5: 訓練 → 預測 → 轉回 FinLab 格式回測
# =========================================

# --- 訓練(簡化版)---
model = StockTransformer(d_feature=5)
optimizer = torch.optim.Adam(model.parameters(), lr=1e-3)
loss_fn = nn.MSELoss()

# 時間序列切割(不能隨機切!要用時間前後切)
split = int(len(X) * 0.7)
X_train, X_test = X[:split], X[split:]
y_train, y_test = y[:split], y[split:]

# 訓練迴圈(實際需要更多 epoch 和 early stopping)
for epoch in range(50):
    pred = model(torch.FloatTensor(X_train))
    loss = loss_fn(pred, torch.FloatTensor(y_train))
    optimizer.zero_grad()
    loss.backward()
    optimizer.step()

# --- 對所有股票做預測,組裝成 FinlabDataFrame ---
stock_list = ['2330', '2317', '2454', '2881', '2882']  # 示例

predictions = {}
for sid in stock_list:
    X_s, _ = build_dataset(sid)
    with torch.no_grad():
        pred = model(torch.FloatTensor(X_s[-1:]))  # 用最新數據預測
        predictions[sid] = pred.item()

# 轉成 FinLab 能用的選股條件
# 核心技巧:Transformer 的預測結果 → FinlabDataFrame → backtest.sim()

場景 B:Transformer 多因子選股 + FinLab 回測

這是最適合 FinLab 生態的用法:
Transformer 當「智慧選股器」,FinLab 當「回測引擎」

┌──────────────── 每月月底 ──────────────────┐
│                                            │
│  FinLab 取數據                              │
│  ├─ 基本面:ROE、營收成長、毛利率            │
│  ├─ 估值面:本益比、股價淨值比               │
│  ├─ 技術面:月報酬率、波動率、RSI            │
│  └─ 籌碼面:外資買賣超、融資餘額             │
│           │                                │
│           ▼                                │
│  組裝成特徵向量(每檔股票一個向量)           │
│  ┌────────────────────────────────┐        │
│  │ 2330: [0.25, 0.15, 12, ...]   │        │
│  │ 2317: [0.18, 0.08, 15, ...]   │        │
│  │ 2454: [0.30, 0.22, 28, ...]   │        │
│  │ ...                           │        │
│  └────────────┬───────────────────┘        │
│               ▼                            │
│  Transformer 打分                           │
│  ┌────────────────────────────────┐        │
│  │ 2330: 0.82 分 ★                │        │
│  │ 2317: 0.45 分                  │        │
│  │ 2454: 0.91 分 ★★               │        │  ← 分數 = 預期報酬
│  │ ...                           │        │
│  └────────────┬───────────────────┘        │
│               ▼                            │
│  選前 N 名 → 產生 FinLab position           │
│  position['2454'] = True                   │
│  position['2330'] = True                   │
│               │                            │
└───────────────┼────────────────────────────┘
                ▼
        finlab.backtest.sim(position, ...)
        → 績效報告
# =========================================
# 完整範例:Transformer 多因子月頻選股
# =========================================

import finlab
from finlab import data
from finlab.backtest import sim

finlab.login('YOUR_API_KEY')

# --- 取 FinLab 數據 ---
close    = data.get('price:收盤價')
roe      = data.get('fundamental_features:ROA稅後息前')  # 季頻,FinLab 自動對齊
rev      = data.get('monthly_revenue:當月營收')
rev_yoy  = rev.pct_change(12)                            # 營收年增率
pe       = data.get('price_earning_ratio:本益比')
foreign  = data.get('institutional_investors:外資買賣超')

# --- 月底取樣,組裝特徵 ---
features_monthly = {
    'roe': roe.resample('M').last(),
    'rev_yoy': rev_yoy.resample('M').last(),
    'pe': pe.resample('M').last(),
    'momentum': close.pct_change(20).resample('M').last(),
    'foreign_cum': foreign.rolling(20).sum().resample('M').last(),
}

# --- 對每個月、每檔股票,用 Transformer 打分 ---
# (這裡用 Cross-Sectional Transformer:同一時間點,
#   比較所有股票之間的相對關係)

class CrossSectionalTransformer(nn.Module):
    """
    橫截面 Transformer:
    不是看一檔股票的時間序列,
    而是看同一時間點所有股票的因子,
    學習「哪些因子組合代表會漲的股票」

    白話:像老師改考卷,
         看完全班的答案後給每個人排名,
         而不是只看一個人的答案。
    """
    def __init__(self, d_feature=5, d_model=32, nhead=4, num_layers=1):
        super().__init__()
        self.proj = nn.Linear(d_feature, d_model)
        encoder_layer = nn.TransformerEncoderLayer(
            d_model=d_model, nhead=nhead,
            dim_feedforward=64, batch_first=True
        )
        self.transformer = nn.TransformerEncoder(encoder_layer, num_layers)
        self.head = nn.Linear(d_model, 1)

    def forward(self, x):
        # x: (1, num_stocks, d_feature) — 一個月所有股票的因子
        x = self.proj(x)
        x = self.transformer(x)   # 股票之間互相 attend
        return self.head(x).squeeze(-1)  # (1, num_stocks) 分數

# --- 預測結果 → FinLab position ---
# 每月選分數最高的 10 檔股票
# position 是一個 FinlabDataFrame (bool),True = 持有

position = close > 0  # 初始化為全 False 的同型 DataFrame
position[:] = False

for month_end in features_monthly['roe'].index:
    # 組裝該月的特徵矩陣
    feat_matrix = pd.DataFrame({
        k: v.loc[month_end] for k, v in features_monthly.items()
    }).dropna()

    if len(feat_matrix) < 20:
        continue

    # Transformer 打分
    with torch.no_grad():
        scores = model(torch.FloatTensor(feat_matrix.values).unsqueeze(0))

    # 選前 10 名
    top_stocks = feat_matrix.index[scores.squeeze().argsort(descending=True)[:10]]

    if month_end in position.index:
        position.loc[month_end, top_stocks] = True

# --- FinLab 回測!---
report = sim(
    position,
    resample='M',           # 月頻換股
    position_limit=0.1,     # 單檔上限 10%
    fee_ratio=1.425/1000,   # 手續費
    stop_loss=0.08,         # 停損 8%
    trade_at_price='open',  # 隔日開盤價成交
    name='Transformer 多因子選股'
)

場景 C:Transformer 情緒分析 + FinLab 訊號融合

新聞/社群               Transformer (NLP)            FinLab 數據
─────────              ──────────────────           ──────────

「台積電法說          ┌──────────────────┐
  展望樂觀」    ─────►│ 情緒分數: +0.7   │
                      └────────┬─────────┘
                               │
「外資連續           ┌──────────▼─────────────────────────┐
  賣超三天」         │       訊號融合層                     │
                     │                                    │
FinLab 籌碼面 ──────►│  情緒 +0.7 × 權重 0.3              │
  外資買賣超         │  + 籌碼面 -0.5 × 權重 0.3           │
                     │  + 技術面 +0.2 × 權重 0.2           │
FinLab 技術面 ──────►│  + 基本面 +0.8 × 權重 0.2           │
  RSI / MACD         │  = 綜合分數 +0.25                  │
                     │                                    │
FinLab 基本面 ──────►│  +0.25 > 閾值 0.1 → 買入           │
  營收 / ROE         └────────────┬───────────────────────┘
                                  │
                                  ▼
                          finlab.backtest.sim()

實務注意事項

FinLab 數據頻率對齊(最容易踩的坑)

問題:不同數據的更新頻率不同!

日頻 ──── 收盤價、成交量、外資買賣超
月頻 ──── 月營收(每月 10 號公布上月)
季頻 ──── ROE、EPS(財報公布後才有)

         1/1  1/10  1/15  2/1  2/10  2/15  3/1
收盤價    ✓     ✓     ✓    ✓    ✓     ✓    ✓     ← 每天都有
月營收    ×     ✓12月  ×    ×    ✓1月   ×    ×     ← 每月才更新
ROE      ×     ×     ×    ×    ×     ×    ×     ← 季報才更新

FinLab 的解法:自動前向填充 (Forward Fill, ffill)
月營收在 1/10 公布後,1/11~2/9 都沿用這個值
→ 這就是 FinlabDataFrame 自動對齊的好處!

但要注意:
⚠️  前瞻偏差 (Look-ahead Bias):
    月營收 1/10 才公布,你的模型不能在 1/5 就用到!
    FinLab 已經處理了這個問題,但自己處理數據時要小心。

Transformer 超參數建議(台股適用)

┌─────────────────────────────────────────────────────┐
│              推薦超參數配置                           │
│                                                     │
│  序列長度 (Sequence Length):                         │
│    日頻預測 → 60 天(約 3 個月的交易日)              │
│    月頻選股 → 12 個月                                │
│                                                     │
│  模型大小:                                           │
│    d_model = 32~64    (台股樣本少,別太大)          │
│    nhead = 4          (注意力頭數)                  │
│    num_layers = 1~2   (層數少 = 不容易過擬合)       │
│    dropout = 0.1~0.3  (台股數據少,dropout 要高)    │
│                                                     │
│  訓練:                                               │
│    batch_size = 32                                   │
│    lr = 1e-3 ~ 1e-4                                 │
│    早停 (Early Stopping) = 必須用!                  │
│    驗證集 = 用時間切割,不能隨機切!                   │
│                                                     │
│  ⚠️ 台股 vs 美股的差異:                             │
│    台股:~1700 檔,成交量集中在大型股                  │
│    美股:~5000 檔,數據更豐富                         │
│    → 台股用更小的模型 + 更強的正則化                   │
└─────────────────────────────────────────────────────┘

訓練/驗證/測試集的正確切法

                    ❌ 錯誤切法(隨機切割)
    ┌──┬──┬──┬──┬──┬──┬──┬──┬──┬──┐
    │訓│測│訓│驗│訓│測│訓│驗│訓│測│  ← 未來數據混入訓練集!
    └──┴──┴──┴──┴──┴──┴──┴──┴──┴──┘

                    ✅ 正確切法(時間序列切割)
    ┌──────────────┬────────┬──────┐
    │   訓練集      │ 驗證集  │測試集│
    │  2015~2020   │ 2021   │ 2022│
    └──────────────┴────────┴──────┘

                    ✅✅ 更好的切法(Walk-Forward Validation, 滾動驗證)

    第1輪: ├─訓練 2015~2018─┤驗證 2019┤
    第2輪: ├─訓練 2015~2019──┤驗證 2020┤
    第3輪: ├─訓練 2015~2020───┤驗證 2021┤
    第4輪: ├─訓練 2015~2021────┤驗證 2022┤
           最終用所有驗證結果的平均來評估模型

專業用詞:

  • Walk-Forward Validation(滾動前進驗證):模擬真實交易場景的驗證方式
  • Look-ahead Bias(前瞻偏差):不小心用到未來資訊,導致回測績效虛高
  • Survivorship Bias(倖存者偏差):只用現在還在的股票訓練,忽略已下市的股票
  • Feature Leakage(特徵洩漏):標籤資訊不小心混進特徵中

三種場景的比較

面向場景 A:預測股價場景 B:多因子選股場景 C:情緒分析
FinLab 角色取 OHLCV + 回測取多維因子 + 回測取量化數據做融合
Transformer 輸入時間序列 (60天×特徵)橫截面 (全部股票×因子)文本 + 數據
輸出預測報酬率股票評分/排名情緒分數
換股頻率日頻/週頻月頻事件驅動
難度中高高(需 NLP)
推薦先做★★★ 最容易入門★★ 最適合 FinLab 生態★ 進階玩法

延伸閱讀方向

方向說明關鍵字搜尋
從頭訓練預測模型用 PyTorch 建一個簡單的 Transformer 預測股價Temporal Fusion Transformer, TFT stock
用現成框架回測用 GA 或 ML 產生策略後回測Backtrader, Zipline, FinRL
論文入門了解學術界怎麼做Transformer for financial time series
實作 RL 交易端對端強化學習交易FinRL, Stable-Baselines3