常見問題
登入驗證相關
找不到api_token的輸入框,導致無法抓取資料怎麼辦呢?
可能您是使用VSCode、或其他IDE,在程式最前面加上以下語法,也能直接登入摟
資料下載
抓不到完整資料?為什麼資料範圍只到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,所以您不需要親自動手寫喔!
想要在特定月份停止交易,要怎麼在回測中排除特定月份呢?
以7,8月不要有策略訊號為例,將month_condition與其他條件取交集即可。
要如何將資料庫中例如「企業基本資訊」的特定欄位整理成像其他回測資料同樣的格式呢?(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))
策略回測
為何沒有正確的停損停利?
當使用
的時候,就算是 stop_loss 有開啟,當偵測到 position 當天有部位時,還是優先以 position 為準。除非您的 position index 是稀疏的狀況下(例如 index 只有每個月),系統查找不到當天需設定的部位時,才會以 stop_loss 來監測是否賣出。你可能會覺得這樣不太直覺,但是設想假如停損後,還是維持 position 是 True,那明天究竟是已經停損還是應該重新買入?就會變成大哉問。所以假如 position 是 True,就以 position 為準,而不額外停損,是最單純的方式。
假如您的 position 的 index 是每個交易日,確實會導致 stop_loss 沒有作用。以下是一些解決方法:
- 假如您剛好有使用 hold_until 來製作 position 時,可以使用
- 假如您原本就沒有使用 hold_until,則可以用以下方式來設定 來設定停損停利
該如何優化與驗證策略?
詳細概念可參考: https://www.finlab.tw/phcebus-thinking-report-part2-backtest-sop/ 並透過Finlab回測系統進行實作。
為什麼回測出來的報酬跟自己算出來的都不一樣呢?
可以先檢查是否已用還原股價計算、確認策略進出場確切時間點、是使用抓開盤或收盤價。若依然有疑慮可至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 雲端硬碟,可在頁面左側「檔案」選單中點選「掛接雲端硬碟」,或直接輸入並執行:
其中「/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 錯誤處理