文件
開啟 Studio

資源

常見問題

本頁內容13

依你正在做的事找問題:登入、下載資料、寫條件、回測、下單,最後是平台、訂閱與客服。每一題的第一句就是答案。第一次用 FinLab,先看快速上手。

卡住時先試這幾步

狀況先試詳細說明
無法登入或抓不到資料重新執行 finlab.login(),再測試 data.get('price:收盤價')下載資料
不知道資料表名稱用 data.search('關鍵字') 搜尋欄位finlab.data
停損停利不如預期確認 position 是不是稀疏訊號,或改用 hold_until()歷史回測
實盤下單與券商串接先完成帳戶設定,再用 OrderExecutor 預覽委託下單教學

登入

找不到登入視窗,或抓不到資料怎麼辦?

在程式最前面執行 finlab.login()(新版登入流程),登入後再下載資料。在 VSCode、終端機或其他 IDE 執行時最常遇到這個狀況。

import finlab

finlab.login()

from finlab import data

close = data.get('price:收盤價')

舊版的 finlab.login('token') 仍然相容,但不建議把 token 寫進程式碼。

下載資料

為什麼資料只到 2023 年底?

因為你還沒訂閱 VIP:免費帳號的台股資料開放到 2023-12-31(最後交易日 2023-12-29)。訂閱 VIP 後,就能使用到最新交易日的歷史資料。

為什麼下載的資料只有日期,數值全是空的?

多半是先前用 set_universe 設了股票範圍。執行 data.reset_universe() 移除它,或整個 kernel 重新啟動後再執行原本的程式即可。

data.get() 出現 DatasetNotFoundError,說找不到資料表?

先用 data.search() 確認資料表名稱沒有拼錯,再確認已經登入。常見原因有三個:

  • 資料表名稱拼錯
  • 還沒登入,或登入狀態失效
  • 非 VIP 會員存取最新資料
import finlab
from finlab import data

# 1. 確認已登入
finlab.login()

# 2. 搜尋正確的欄位名稱
print(data.search('收盤'))

# 3. 用正確名稱重新下載
close = data.get('price:收盤價')
print(f"資料範圍:{close.index[0]} ~ {close.index[-1]}")

在伺服器或排程環境,先在本機執行 python -m finlab token --env,再把輸出的環境變數設定到執行環境。更多下載錯誤見下載資料。

下載成功,表卻是空的(Empty DataFrame)?

通常是 data.universe() 的篩選條件太嚴,或類股名稱不存在;也可能是資料來源暫時沒有資料。先檢查表是不是空的:

from finlab import data

close = data.get('price:收盤價')

if close.empty:
    print("資料為空:檢查 universe 篩選條件是否過嚴,或資料表本身是否沒有資料")
else:
    print(f"資料正常:{close.shape[0]} 個交易日,{close.shape[1]} 檔股票")

怎麼把「企業基本資訊」的欄位整理成日期 × 股票的表?

把那一欄複製到每個日期,做成 index 為日期、columns 為股票代號的 FinlabDataFrame,就能和其他資料一起運算。以「已發行普通股數或TDR原發行股數」為例:

import pandas as pd
from finlab import data
from finlab.dataframe import FinlabDataFrame

basic_info = data.get('company_basic_info').set_index('stock_id')
營業收入 = data.get('financial_statement:營業收入淨額')
流通在外股數 = basic_info['已發行普通股數或TDR原發行股數']
流通在外股數 = FinlabDataFrame(
    pd.DataFrame([list(流通在外股數)], columns=basic_info.index, index=營業收入.index)
)

寫選股條件

不同頻率的資料,要先用 reindex 對齊嗎?

不需要:data.get() 拿到的資料在做四則運算或比較時,FinLab 會自動幫你對齊日期。自己 reindex 反而要花很多時間調整資料。

from finlab import data

close = data.get('price:收盤價')  # 日資料
roa = data.get('fundamental_features:ROA稅後息前')  # 季資料

close 和 roa 都是 FinlabDataFrame,所以不必先把 roa 轉成日資料:

# 不需要這樣寫
roa_daily = roa.reindex(close.index, method='ffill')
condition = (close < 30) & (roa_daily > 0)

# 直接寫就好
condition = (close < 30) & (roa > 0)

運算時 FinLab 會在背後算出 roa_daily。

為什麼不能直接改 df.index?可以只給警告嗎?

不行,這是刻意的安全保護:對 FinlabDataFrame 直接設定 index 會丟出 AttributeError,不是一般的格式轉換錯誤,也不建議改成警告。

data.get() 回傳的 FinlabDataFrame 保留月營收、財報原本的時間標記,例如 2024-M01、2024-Q1。直接覆寫 index,很容易把「資料所屬的期間」誤當成「當時已經公布、可以交易的日期」,讓回測用到當時還拿不到的資料(lookahead bias,未來資料汙染)。

要計算 AssetGrowth 這類需要對齊不同資料的因子,通常不必先手動 reindex 原始資料:

from finlab import data

close = data.get('price:收盤價')
asset = data.get('financial_statement:資產總額')

# FinlabDataFrame 會在運算時自動對齊不同頻率的資料
asset_growth = asset / asset.shift(4) - 1

# 要把最後的 position 換成交易日頻率,再對 position 做 reindex
position = (asset_growth > 0).index_str_to_date()
position = position.reindex(close.index, method='ffill')

如果你明確需要把季報或月營收的索引轉成日期,用這兩個方法:

df = df.index_str_to_date()  # 依各股實際公布日
df = df.deadline()  # 依統一公告截止日

外部 CSV 或自訂市場的資料,先用一般的 pandas.DataFrame 解析日期、清理資料,再轉成 FinlabDataFrame:

import pandas as pd
from finlab.dataframe import FinlabDataFrame

raw = pd.read_csv('factor.csv', index_col=0)
raw.index = pd.to_datetime(raw.index)

factor = FinlabDataFrame(raw)
確定要用一般 pandas 的規則處理 index

先轉成 pandas.DataFrame。這樣會離開 FinlabDataFrame 的自動對齊與防前視保護,時間標記是否正確要自己確認:

import pandas as pd

df = pd.DataFrame(df)
df.index = pd.to_datetime(df.index)

想在特定月份(例如 7、8 月)不交易?

做一個月份條件,再和其他條件取交集。以 7、8 月不要有策略訊號為例:

month_condition = close.index.to_series().apply(lambda x: x.month not in [7, 8])

怎麼更細地自訂換股頻率?

在把 position 交給回測之前,直接對 position 用 pandas 的 resample,用法見 pandas 文件。

回測

為什麼設了停損停利,卻沒有出場?

因為 position 當天有部位時,sim() 以 position 為準。只有 position 的日期是稀疏的(例如只有每月一列),系統查不到當天該持有的部位,才會用 stop_loss 判斷要不要賣出:

report = sim(position, stop_loss=0.1)

這樣設計是因為:如果停損之後 position 仍然是 True,明天究竟算已經停損,還是應該重新買進?很難定義。所以 position 是 True 時就以 position 為準,不額外停損,是最單純的做法。也因此,position 的日期若是每個交易日,stop_loss 確實不會有作用。改用 hold_until() 設定停損:

buy = close > close.average(20)
sell = close < close.average(60)

# 原本的
position = buy.hold_until(sell)
# 加上停損
position = buy.hold_until(sell, stop_loss=0.1)

回測跑完,卻沒有任何交易?

多半是進場條件太嚴,position 幾乎都是 False,get_trades() 回傳空的表。其他原因:資料日期範圍太短、篩選後的股票池太小。先看平均每天有幾檔符合條件:

from finlab.backtest import sim

# 檢查進場訊號
entry_stats = position.sum(axis=1)
print(f"平均每日進場股票數:{entry_stats.mean():.2f}")
print(f"最大進場股票數:{entry_stats.max()}")

if entry_stats.mean() < 1:
    # 進場條件過嚴:放寬條件(如價格區間),或用 .is_largest(N) 固定檔數
    # 用分數排名、不是用條件:每天都選出相對 20 日均線最強的 30 檔
    ma20 = close.average(20)
    position_fixed = (close / ma20).is_largest(30)

report = sim(position, resample='M')
trades = report.get_trades()
print(f"{len(trades)} 筆交易" if len(trades) else "沒有交易紀錄,請放寬進場條件")

回測出現 KeyError: Timestamp('2023-05-01')?

這代表某個日期不在資料裡,通常是用了多個資料源、日期範圍不一致,或 shift()、rolling() 讓前期資料缺失。三種處理方式:

from finlab import data

data.truncate_start = '2020-01-01'

更多回測錯誤見歷史回測。

回測報酬跟自己算的不一樣?

先確認三件事:有沒有用還原股價計算、策略確切的進出場時間點、成交用的是開盤價還是收盤價。還有疑慮,可以到 Discord 論壇區討論。

本機或 Colab 的績效,跟平台上跑出來的不一樣?

以在本機或 Colab 用最新版套件執行的結果為準。

可以不要把回測報表上傳到網頁嗎?

不用設定,本機與 Colab 預設就不會上傳:sim() 的 upload 預設為 None,只有設定了 FINLAB_FORCED_STRATEGY_NAME 環境變數時才會上傳,例如雲端排程、在 FinLab Studio 網站手動執行的筆記本。2.1.0 起,Studio AI 與桌面版也要傳入 upload=True 才上傳。想明確關閉,仍可傳入 upload=False:

report = sim(position, upload=False)

為什麼 display_mae_mfe_analysis 沒有顯示 Edge ratio?

要在 sim() 設定 mae_mfe_window 參數,參數說明見 finlab.backtest。

該怎麼優化與驗證策略?

概念可以參考回測 SOP 這篇文章,再用 FinLab 回測系統實作:用策略參數優化調參數,照從研究到實盤的流程,保留一段樣本外期間做驗證。

下單

可以直接在 FinLab 平台上下單嗎?

不行。要串接券商的下單 API,需要在其他環境執行。

怎麼串接券商 API 下單?

FinLab 與永豐證券、玉山 Fugle 合作:開好相關帳戶後,照下單教學開始串接,文件裡也附有教學影片,依步驟執行即可。

券商帳戶連線失敗?

多半是環境變數沒設好(API KEY、憑證路徑),或憑證檔案不存在、密碼錯誤;也可能是網路連線問題。執行 SinopacAccount() 或 EsunAccount() 出錯時,先逐項檢查:

import os
from finlab.online.sinopac_account import SinopacAccount

# 檢查環境變數
required_vars = [
    'SHIOAJI_API_KEY',
    'SHIOAJI_SECRET_KEY',
    'SHIOAJI_CERT_PATH',
    'SHIOAJI_CERT_PASSWORD',
]
for var in required_vars:
    if not os.environ.get(var):
        print(f"{var} 未設定")

# 檢查憑證檔案
cert_path = os.environ.get('SHIOAJI_CERT_PATH')
if cert_path and not os.path.exists(cert_path):
    print(f"憑證檔案不存在:{cert_path}")

# 嘗試連線
try:
    acc = SinopacAccount()
    print("帳戶連線成功")
except Exception as e:
    print(f"連線失敗:{e},請檢查 API key、憑證路徑與密碼")

各券商需要的設定見下單教學。

下單時,怎麼避免賣掉不屬於這個策略的股票?

先建一個 Position 放手單部位(和這次策略無關的持股),再把策略部位與手單部位加總,交給 OrderExecutor 下單:

from finlab.online.order_executor import OrderExecutor, Position

strategy = Position.from_report(report, 1_000_000)  # 這個策略的部位,投入 100 萬
manual = Position({'2330': 1})  # 手單部位:自己另外持有的一張 2330

executor = OrderExecutor(strategy + manual, account=account)
executor.create_orders(view_only=True)  # 先預覽

下單時顯示資金不足?

可用資金不夠買進 position 指定的股票。常見原因:配置給策略的資金太多、沒算到手續費與證交稅、帳戶餘額和預期不同。下單前先比較可用現金與配置金額:

from finlab.online.order_executor import OrderExecutor, Position

fund = 1_000_000  # 要配置給這個策略的資金(新台幣)

available_cash = account.get_cash()
print(f"可用資金:NT$ {available_cash:,.0f}")
print(f"預計配置:NT$ {fund:,.0f}")

if fund > available_cash * 0.9:  # 留 10% 緩衝
    print("資金不足:降低配置資金、追加資金,或減少持股數量")
else:
    position = Position.from_report(report, fund)
    executor = OrderExecutor(position, account=account)
    executor.create_orders(view_only=True)  # 先預覽

部分訂單被券商拒絕?

最常見的原因是處置股沒有先圈存;股票暫停交易、漲跌停鎖死也會無法成交。先列出新委託裡的處置股與警示股,圈存後再下單:

from finlab.online.order_executor import OrderExecutor

executor = OrderExecutor(position, account=account)

# 1. 列出處置股與警示股
executor.show_alerting_stocks()

# 2. 先預覽
executor.create_orders(view_only=True)

# 3. 確認後執行
if input("確認無誤後輸入 'YES' 執行下單:") == 'YES':
    executor.create_orders()

有列出處置股時,先到券商平台圈存,作法依券商而定,例如富果、永豐。

錯過策略的進出場日期,該怎麼辦?

可以用 Edge Ratio 分析判斷適不適合中途進場,詳細說明見 Ben 大的文章。

自訂市場與外部資料

自訂市場讀不到資料檔(FileNotFoundError)?

CSV 檔案不存在、路徑錯誤,或用了相對路徑但工作目錄不對。讀檔前先檢查檔案在不在,錯誤訊息裡印出工作目錄:

import os
import pandas as pd
from finlab.market import Market

class CryptoMarket(Market):
    def __init__(self, data_dir='./data'):
        self.data_dir = data_dir

    def get_price(self, trade_at_price='close', adj=True):
        file_path = os.path.join(self.data_dir, f'crypto_{trade_at_price}.csv')

        if not os.path.exists(file_path):
            raise FileNotFoundError(
                f"找不到資料檔案:{file_path}\n"
                f"請確認檔案在 {self.data_dir} 目錄、檔名正確,"
                f"目前工作目錄:{os.getcwd()}"
            )

        return pd.read_csv(file_path, index_col=0, parse_dates=True)

try:
    market = CryptoMarket(data_dir='/path/to/data')
    close = market.get_price('close')
except FileNotFoundError as e:
    print(e)

完整作法見自訂市場。

資料格式錯誤(Index must be DatetimeIndex)?

index 不是 DatetimeIndex,或欄位是字串而不是數值,回測就會失敗。讀進 CSV 後先轉換:

import pandas as pd

df = pd.read_csv('data.csv', index_col=0)

# 把 index 轉成 DatetimeIndex
if not isinstance(df.index, pd.DatetimeIndex):
    try:
        df.index = pd.to_datetime(df.index)
    except Exception as e:
        print(f"無法轉換 index:{e}")
        print(f"index 範例:{df.index[:3].tolist()}")
        print("請確認第一欄是日期格式(如 2020-01-01)")

# 把非數值欄位轉成數值
non_numeric_cols = df.select_dtypes(exclude=['number']).columns.tolist()
for col in non_numeric_cols:
    df[col] = pd.to_numeric(df[col], errors='coerce')

機器學習

模型訓練時說資料量太少?

訓練集不到 1000 筆,或特徵與標籤對齊後損失大量資料。先算一下訓練集有幾筆:

from finlab import data
from finlab.ml import feature as mlf, label as mll

close = data.get('price:收盤價')
features = mlf.combine({'momentum': close / close.shift(20)}, resample='W')
labels = mll.excess_over_mean(features.index, resample='W')

# 特徵與標籤對齊
data_ml = features.join(labels.rename('label'), how='inner')

# 切分訓練集
train_data = data_ml[data_ml.index.get_level_values(0) <= '2022-12-31']
print(f"訓練集:{len(train_data)} 筆")

不到 1000 筆時:

  1. 增加歷史資料範圍
  2. 縮短 resample 的間隔(例如 'M' 改成 'W'),樣本之間隔得越短,筆數越多
  3. 減少特徵數量(降低缺失值)

完整流程見機器學習策略流程。

訓練集與測試集的日期重疊了?

沒有照時間順序切分,訓練集的最後一天晚於或等於測試集的第一天,就會造成資料洩露(data leakage)。用固定的切分日,並檢查日期順序:

train_end = '2022-12-31'
test_start = '2023-01-01'

train_data = data_ml[data_ml.index.get_level_values(0) <= train_end]
test_data = data_ml[data_ml.index.get_level_values(0) >= test_start]

train_last_date = train_data.index.get_level_values(0).max()
test_first_date = test_data.index.get_level_values(0).min()

if train_last_date >= test_first_date:
    raise ValueError(
        f"訓練集與測試集日期重疊:訓練集最後 {train_last_date},"
        f"測試集最早 {test_first_date},會造成資料洩露"
    )

模型預測結果全是 NaN?

模型訓練失敗卻沒有報錯,或預測用的資料含有 NaN、Inf。檢查預測資料,必要時補值後重新預測:

import finlab.ml.qlib as q

train = train_data.dropna()
model = q.LGBModel()
model.fit(train.drop(columns=['label']), train['label'])

all_pred = model.predict(data_ml.drop(columns=['label']))

if all_pred.isna().all().all():
    X_pred = data_ml.drop(columns=['label'])
    print(f"NaN 數量:{X_pred.isna().sum().sum()}")
    print(f"Inf 數量:{(X_pred == float('inf')).sum().sum()}")

    # 補缺失值後重新預測
    X_pred = X_pred.fillna(0).replace([float('inf'), float('-inf')], 0)
    all_pred = model.predict(X_pred)

print(f"預測完成:{all_pred.notna().sum().sum()} 筆")

安裝與執行環境

TA-Lib 要怎麼安裝?

用 pip 安裝即可,PyPI 已提供 Windows、macOS、Linux 預先編譯的版本:

pip install ta-lib

其他安裝方式見 TA-Lib 官方安裝教學。

在 Colab 怎麼匯出、匯入資料,路徑要怎麼設?

先掛接 Google 雲端硬碟:在頁面左側「檔案」選單點「掛接雲端硬碟」,或執行:

from google.colab import drive

drive.mount('/content/drive')

掛接後,/drive/MyDrive(完整路徑 /content/drive/MyDrive)就是雲端硬碟的根目錄。

在平台上執行時出現 MemoryError?

這是超過了網頁的記憶體限制,建議改到 Google Colab 執行。

可以用手機操作 FinLab 量化平台嗎?

看策略績效、個股與產業資訊,用手機沒問題;撰寫並執行策略則受記憶體限制,建議用電腦。

AI 編程助手

怎麼用 AI 助手寫 FinLab 策略?

安裝 FinLab 官方的 AI Skill:到 finlab.finance 依照指示安裝到 Claude Code、Cursor、Windsurf 等 AI 編程工具,AI 就會熟悉整套 API。安裝後你可以:

  • 問「幫我用營收成長和技術突破寫一個選股策略」,AI 會產生可執行的程式碼
  • 問「data.get 有哪些財報相關的資料表?」,AI 會列出正確的鍵名
  • 貼上錯誤訊息,AI 會幫你診斷並修正
  • 要求「幫我把這個策略加上 10% 停損並回測」,AI 會直接修改程式碼

詳細說明見快速上手。

AI Skill 和 FinLab Studio 有什麼不同?

Studio 在瀏覽器裡用,不必安裝;AI Skill 裝在你自己電腦上的 AI 編程工具裡。

  • FinLab Studio:線上 AI 平台,不需安裝任何東西,直接在瀏覽器中對話撰寫策略。
  • FinLab Skill:安裝到本機的 AI 編程工具(Claude Code、Cursor 等),AI 可以直接在你的開發環境讀寫檔案、執行程式碼、除錯。適合已經有本機開發環境的進階用戶。

FinLab 平台

怎麼設定策略每天自動更新,不用每天按?

在平台策略頁面上方按「自動更新」,選擇要更新的策略、設定時間,就會每天自動更新。

策略訊號有異動時,會通知我嗎?

會。在平台上設定自動更新的策略,訊號出現異動時會寄 email 通知,在工作日 06:00~07:00 之間寄出。

策略名稱旁的綠點是什麼意思?

表示該策略當天是訊號異動日或換股日。

策略清單的「當前統計」和「近期更動清單」差在哪?

「當前統計」是策略目前實際的持股;「近期更動清單」除了目前實際持股,也包含各檔股票近期的異動。

為什麼「近期異動清單」每天都在變?

策略用到的資料每天更新,還沒進場的選股也會跟著變。實際操作時,照原本設定的換股週期換股即可。

帳號、訂閱與客服

可以更改訂閱綁定的信用卡嗎?

可以。到平台右上角的會員頁面,點「付款設定與紀錄」分頁,再點「更改發票以及信用卡」。

註冊錯帳號了,想換成另一個 FinLab 帳號?

先用新的 Google 帳號登入,再用舊帳號的 email 寄信到 finlab.company@finlab.tw 申請更換。

有使用上的問題,要去哪裡問助教?

到 FinLab 的 Discord 聊天室:在 Discord 搜尋「Python 選股實驗室」,或從 FinLab 網頁左側「社群 > Discord 聊天室」進入。

  • 發問以平台與套件使用的疑難雜症為主;一般程式語法或與 FinLab 無關的問題,可以先試著用 Google 或 ChatGPT 解決。
  • 完整描述你遇到的狀況:使用環境、套件版本、使用時間,並附上完整程式碼與錯誤訊息。資訊越完整,助教越能快速幫你解決。
  • 策略撰寫的問題,盡量有條理地描述完整概念,能得到更有效率的解法。
  • 訊息量大時問題容易被洗掉,多利用論壇區做主題式討論,延伸討論也容易追蹤。

客服時間是?多久會回覆?

客服時間為週一至週五 9:00~17:00,在 Discord 留下的問題會盡量在兩個工作天內回覆。

想許願新功能,要去哪裡?

到 Discord 的 VIP 專屬許願池留下你的願望,限 VIP。

哪裡可以看到新的研究與教學?

研究與教學訊息會公布在 Discord 的教學資源頻道與 FB 粉專;研究成果多半以部落格文章、YouTube 影片發表,都可以直接追蹤:

下一步

↑↓ 選擇 Enter 開啟 Esc 關閉