跳轉到

常見問題

登入驗證相關

找不到api_token的輸入框,導致無法抓取資料怎麼辦呢?

可能您是使用VSCode、或其他IDE,在程式最前面加上以下語法,也能直接登入摟

import finlab
finlab.login('您的api_token')

資料下載

抓不到完整資料?為什麼資料範圍只到2020年呢?

因為您尚未訂閱VIP,對未訂閱VIP會員我們只開放至2020年底的資料,可以考慮訂閱起來! 即可使用截止至最新交易日的歷史資料。

為什麼載下來的資料中,只有index有值,value皆為空值呢?

您可能有設定到set_universe,執行data.universe_stocks = {} 或是整個kernal重啟後再執行原程式碼即可。

常用策略語法問題

每種財經資料的 index 沒有對齊,要用 reindex 去做運算?

不需要,這樣會花費大量時間在做資料的調整,在 finlab package 中,所有以 data.get 所拿取的資料,在做二元運算時,都會偷偷幫你自動對齊,您不需要花費任何時間來對齊資料,以下為例子。 例如我們拿取此資料:

from finlab import data

close = data.get('price:收盤價')
roa = data.get('fundamental_features:ROA稅後息前')
此時 close 以及 roa 都是 FinlabDataFrame,所以與其這樣製作條件

roa_daily = roa.reindex(close.index, method='ffill')

condition = (close < 30) & (roa_daily > 0)

您可以化簡如下

condition = (close < 30) & (roa > 0)

在做這些四則運算或不等式運算時,我們會偷偷幫你計算 roa_daily,所以您不需要親自動手寫喔!

想要在特定月份停止交易,要怎麼在回測中排除特定月份呢?

以7,8月不要有策略訊號為例,將month_condition與其他條件取交集即可。

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

要如何將資料庫中例如「企業基本資訊」的特定欄位整理成像其他回測資料同樣的格式呢?(index為date、columns為stock_id)"

以整理「已發行普通股數或TDR原發行股數」欄位為例:

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

策略回測

為何沒有正確的停損停利?

當使用

report = sim(position, stop_loss=0.1)
的時候,就算是 stop_loss 有開啟,當偵測到 position 當天有部位時,還是優先以 position 為準。除非您的 position index 是稀疏的狀況下(例如 index 只有每個月),系統查找不到當天需設定的部位時,才會以 stop_loss 來監測是否賣出。

你可能會覺得這樣不太直覺,但是設想假如停損後,還是維持 position 是 True,那明天究竟是已經停損還是應該重新買入?就會變成大哉問。所以假如 position 是 True,就以 position 為準,而不額外停損,是最單純的方式。

假如您的 position 的 index 是每個交易日,確實會導致 stop_loss 沒有作用。以下是一些解決方法:

  1. 假如您剛好有使用 hold_until 來製作 position 時,可以使用
    # 原本的
    position = buy.hold_until(sell)
    # 有停損的
    position = buy.hold_until(sell, stop_loss=0.1)
    
  2. 假如您原本就沒有使用 hold_until,則可以用以下方式來設定
    position.is_entry().hold_until(position.is_exit(), stop_loss=0.1) 
    
    來設定停損停利
該如何優化與驗證策略?

詳細概念可參考: https://www.finlab.tw/phcebus-thinking-report-part2-backtest-sop/ 並透過Finlab回測系統進行實作。

在local或colab執行回測時,可以不要印出網頁報表嗎?

在sim回測模組中設定參數upload=False即可。

report = sim(position, upload=False)

為什麼回測出來的報酬跟自己算出來的都不一樣呢?

可以先檢查是否已用還原股價計算、確認策略進出場確切時間點、是使用抓開盤或收盤價。若依然有疑慮可至Discord論壇區討論。

report的display_mae_mfe_analysis,Edge ratio為什麼沒有顯示?

需在sim回測模組中設定參數mae_mfe_window,參考 https://doc.finlab.tw/reference/backtest/ 。

怎麼進一步客製化換股頻率呢?(resample的進階應用?)"

可參考pandas文件說明 https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.resample.html ,直接使用於放入回測模組前的position。

在平台上執行遇到MemoryError要怎麼解決呢?

超過網頁記憶體限制,建議移至 Google Colab 使用。

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

表示該策略為訊號異動日或換股日。

環境相關

Talib要如何安裝?
  • 在 Google Colab: 可以使用語法!pip install ta-lib-bin 來進行安裝
  • 透過 conda 安裝:在其他 kernel: 執行 $ conda install -c conda-forge ta-lib 來進行安裝
  • MacOS 安裝:請先安裝 Homebrew,並且使用 brew install ta-lib 來進行安裝,
  • Windows 安裝:可以至 https://www.lfd.uci.edu/~gohlke/pythonlibs/#ta-lib 下載相應版本wheel,並在下載路徑執行 $ pip install 檔案全名.whl
要如何在colab將資料匯出?如何匯入資料並設定正確路徑?

需掛接google 雲端硬碟,可在頁面左側「檔案」選單中點選「掛接雲端硬碟」,或直接輸入並執行:

from google.colab import drive
drive.mount('/content/drive')
其中「/drive/MyDrive」即為google雲端硬碟根目錄。

在本機或colab跑的回測績效跟平台上跑出來的不一樣?

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

手機適合操作 FinLab 量化平台嗎?

若是查看各策略績效、其他個股、產業資訊等,用手機是可行的;不過若欲撰寫策略並執行,因記憶體限制考量,建議以電腦來使用為主。

下單進出場

在finlab平台無法直接執行下單?

是的,如需串接下單API,需使用其他環境進行操作。

要如何串接券商API下單呢?

Finlab有與永豐證券、玉山Fugle合作,可在開立相關帳戶後查閱文件開始串接,內文亦附有教學影片,依步驟執行即可: https://doc.finlab.tw/details/order_api/

下單時如何避免出掉非該策略的部位?

先創建Position物件並定義手單部位(非該次策略相關的部位),在OrderExecutor的position放入該策略部位與手單部位的加總,即可直接下單。

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

可藉由Edge Ratio分析來判斷是否適合中途進場,詳細說明請見Ben大文章解說:https://www.finlab.tw/edge-ratio-follow-application/

平台使用

哪裡能設定策略自動更新,不想每天按?

在平台策略頁面上方,按下「自動更新」,即可選擇欲更新之策略、設定時間來進行每日自動更新。

策略訊號有異動,會有通知功能嗎?

在平台上有設定自動更新之策略,將在訊號出現異動時,寄出mail通知(通知在工作日0600~0700之間寄出)。

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

當前統計清單包含策略目前實際持股,近期更動清單除了目前實際持股、也包含近期各個股的異動。

為何「近期異動清單」會每天變?

隨著策略中使用到的數據每日異動,尚未進場的選股將隨之變化,實際應用上,於原先設定之換股周期進行換股操作即可。

訂閱與其他問題

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

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

註冊錯帳號了,想更換註冊使用的FinLab帳號,怎麼辦?

可直接使用新的google帳號登入後,用舊的帳號對應到的 email 到 [email protected] 來更換即可

我有量化平台使用上的問題,請問助教系統在哪裡?該如何詢問問題?

Finlab有專屬Discord聊天室,可至Discord搜尋「Python 選股實驗室」,或是至Finlab網頁左側「社群>Discord 聊天室」進入。

發問內容以平台/套件使用相關疑難雜症為主,一般程式語法或其他與Finlab無關之障礙可先多嘗試使用google或ChatGPT協助處理。

發問時,需完整描述所遇到的狀況,可能包含使用環境、套件版本、使用時間,並附上完整程式碼與錯誤訊息,當提供的資訊越完整,助教將能越迅速為您解決。而若是針對策略撰寫的疑問,盡量以具有邏輯的方式描述完整概念,將能得到更有效率的解決方案。

為在訊息量較大時避免被洗掉、且有延伸討論時能輕易追蹤,可多善用論壇區進行主題式討論。

有建議的新功能,要在哪裡許願?

呈上,可至Discord的vip專屬許願池留下您的願望,VIP限定!

助教會在多久後回覆我的問題?客服服務時間是?

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

哪裡可以得知新的研究教學訊息?

研究教學資訊將布告於Discord的教學資源頻道與FB粉專,而研究成果多以部落格文章、Youtube影音形式為主,均可直接追蹤收藏起來!

Finlab財經實驗室Youtube頻道: https://www.youtube.com/@FinlabPython Finlab部落格: https://www.finlab.tw/

常見錯誤與解決方法

KeyError: 'price:收盤價' - 找不到資料表

現象:執行 data.get() 時拋出 KeyError

原因: - 資料表名稱拼寫錯誤 - API Token 未設定或無效 - 非 VIP 會員存取最新資料

解決方法

import finlab

# 1. 確認登入
finlab.login('YOUR_API_TOKEN')  # 至 https://ai.finlab.tw/member_info 取得

# 2. 使用 data.search() 搜尋正確的欄位名稱
from finlab import data
matching_fields = data.search('收盤')
print(matching_fields)  # 確認正確名稱

# 3. 測試資料下載
close = data.get('price:收盤價')
print(f"資料範圍:{close.index[0]} ~ {close.index[-1]}")

參考資料下載錯誤處理

資料為空(Empty DataFrame)

現象:成功下載但 DataFrame 沒有任何資料

原因: - 使用 data.universe() 時,篩選條件過於嚴格 - 類股名稱不存在 - 資料來源暫時無資料

解決方法

from finlab import data

# 檢查資料完整性
close = data.get('price:收盤價')

if close.empty:
    print("❌ 資料為空")
    print("可能原因:")
    print("1. universe 篩選條件過嚴")
    print("2. 資料表本身無資料")
else:
    print(f"✅ 資料正常:{close.shape[0]} 個交易日,{close.shape[1]} 檔股票")

參考資料下載錯誤處理

策略無任何交易記錄

現象:回測完成但無任何交易,get_trades() 回傳空 DataFrame

原因: - 進場條件過於嚴格,position 幾乎全為 False - 資料日期範圍過短 - 篩選後的股票池過小

解決方法

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:
    print("⚠️ 警告:進場條件過嚴")
    print("建議:")
    print("1. 放寬條件(如價格區間)")
    print("2. 使用 .is_largest(N) 確保固定檔數")

    # 修正範例:確保固定檔數
    position_fixed = (close > ma20).is_largest(30)

# 執行回測
report = sim(position, resample='M')
trades = report.get_trades()

if len(trades) == 0:
    print("❌ 無交易記錄,請放寬進場條件")
else:
    print(f"✅ 回測成功:{len(trades)} 筆交易")

參考回測錯誤處理

KeyError: Timestamp('2023-05-01') - 日期索引錯誤

現象:執行回測時拋出 KeyError,錯誤訊息顯示某個日期不存在

原因: - 使用多個資料源時,日期索引範圍不一致 - 使用 shift()rolling() 導致前期資料缺失

解決方法

import finlab

# 方法 1:使用 truncate_start 對齊起始日期
finlab.truncate_start = '2020-01-01'

# 方法 2:手動對齊日期索引
from finlab import data
close = data.get('price:收盤價')
volume = data.get('price:成交股數')

# 檢查日期範圍
print(f"收盤價:{close.index[0]} ~ {close.index[-1]}")
print(f"成交量:{volume.index[0]} ~ {volume.index[-1]}")

# 對齊
common_dates = close.index.intersection(volume.index)
close_aligned = close.loc[common_dates]
volume_aligned = volume.loc[common_dates]

# 方法 3:移除缺失值
position = (close > close.average(20)) & (volume > 1000)
position_cleaned = position.dropna(how='all', axis=0)  # 移除全為 NaN 的列
position_cleaned = position_cleaned.fillna(False)

參考回測錯誤處理

券商帳戶連線失敗

現象:執行 SinopacAccount()EsunAccount() 時拋出連線錯誤

原因: - 環境變數未正確設定(API KEY、憑證路徑) - 憑證檔案不存在或密碼錯誤 - 網路連線問題

解決方法

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} 未設定")
        print("請參考:https://doc.finlab.tw/details/order_api/")

# 檢查憑證檔案
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}")
    print("請檢查 API key、憑證路徑與密碼")

參考實盤下單錯誤處理

下單資金不足

現象:執行下單時提示資金不足

原因: - 可用資金不足以買入 position 指定的股票 - 未考慮手續費與證交稅 - 帳戶餘額與預期不符

解決方法

from finlab.online.order_executor import OrderExecutor

# 下單前檢查資金
available_cash = account.get_balance()
required_cash = position.total_value  # 預估所需資金(需根據實際 API)

print(f"可用資金:NT$ {available_cash:,.0f}")
print(f"預估成本:NT$ {required_cash:,.0f}")

if required_cash > available_cash * 0.9:  # 留 10% 緩衝
    print("❌ 資金不足")
    print("建議:")
    print("1. 降低總資金比例")
    print("2. 追加資金")
    print("3. 減少持股數量")
else:
    # 執行下單
    executor = OrderExecutor(position, account=account)
    executor.create_orders(view_only=True)  # 先預覽

參考實盤下單錯誤處理

訂單被拒絕(處置股未圈存)

現象:執行 create_orders() 後,部分訂單被券商拒絕

原因: - 處置股未圈存 - 股票暫停交易 - 漲跌停鎖死無法成交

解決方法

from finlab.online.order_executor import OrderExecutor

executor = OrderExecutor(position, account=account)

# 1. 檢查處置股
alerting_stocks = executor.show_alerting_stocks()

if alerting_stocks:
    print("⚠️ 發現處置股,請至券商平台圈存後再下單")
    print("參考圈存教學:")
    print("- 富果:https://support.fugle.tw/trading/trading-troubleshoot/5231/")
    print("- 永豐:https://www.sinotrade.com.tw/richclub/freshman/...")
    input("圈存完成後,按 Enter 繼續...")

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

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

參考實盤下單錯誤處理

檔案讀取失敗(自訂市場)

現象:執行自訂市場時拋出 FileNotFoundError

原因: - CSV 檔案不存在或路徑錯誤 - 使用相對路徑但工作目錄不正確

解決方法

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

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"請確認:\n"
                f"1. 檔案是否存在於 {self.data_dir} 目錄\n"
                f"2. 檔案名稱是否正確\n"
                f"3. 工作目錄:{os.getcwd()}"
            )

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

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

參考自訂市場錯誤處理

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

現象:DataFrame 格式不符合預期,導致回測失敗

原因: - Index 不是 DatetimeIndex - 資料型態錯誤(字串而非數值)

解決方法

import pandas as pd

# 讀取 CSV
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)
        print("✅ Index 已轉換為 DatetimeIndex")
    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()
if non_numeric_cols:
    print(f"⚠️ 以下欄位非數值型態:{non_numeric_cols}")
    for col in non_numeric_cols:
        df[col] = pd.to_numeric(df[col], errors='coerce')

參考自訂市場錯誤處理

模型訓練資料不足(機器學習)

現象:ML 模型訓練時提示資料量過少

原因: - 訓練集資料 < 1000 筆 - 特徵與標籤對齊後損失大量資料

解決方法

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

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

# 切分訓練/測試集
train_data = data_ml[data_ml.index.get_level_values(0) <= '2022-12-31']

print(f"訓練集:{len(train_data)} 筆")

if len(train_data) < 1000:
    print("⚠️ 訓練資料不足(< 1000 筆)")
    print("建議:")
    print("1. 增加歷史資料範圍")
    print("2. 降低 resample 頻率('1d' 改為 'W')")
    print("3. 減少特徵數量(降低缺失值)")

參考ML 錯誤處理

訓練/測試集日期重疊(機器學習)

現象:切分資料集時,訓練集與測試集日期重疊,導致資料洩露

原因: - 未正確使用時間序列切分 - 訓練集最後日期 >= 測試集第一日期

解決方法

# 定義切分日期
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"❌ 訓練集與測試集日期重疊!\n"
        f"訓練集最後日期:{train_last_date}\n"
        f"測試集第一日期:{test_first_date}\n"
        f"這會導致資料洩露(data leakage)"
    )

print(f"✅ 資料切分正確")

參考ML 錯誤處理

預測結果全為 NaN(機器學習)

現象:ML 模型預測後,結果全為 NaN

原因: - 模型訓練失敗但未拋出錯誤 - 預測資料包含 NaN 或 Inf

解決方法

import pandas as pd

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

# 檢查預測結果
if pd.Series(all_pred).isna().all():
    print("❌ 預測結果全為 NaN")
    print("請檢查:")
    print("1. 模型是否正確訓練")
    print("2. 預測資料是否包含 NaN 或 Inf")

    # 檢查預測資料
    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"✅ 預測完成:{len(all_pred)} 筆")

參考ML 錯誤處理