Frequently Asked Questions
AI Coding Assistant
How do I use the AI assistant to write FinLab strategies?
FinLab provides an official AI Skill that can be installed into AI coding tools such as Claude Code, Cursor, Windsurf, etc., allowing the AI to become familiar with the entire API.
Installation: Go to finlab.finance and follow the instructions to install.
What you can do after installation:
- Ask "Write me a stock selection strategy based on revenue growth and technical breakout" and the AI will generate executable code
- Ask "What financial statement data tables are available in
data.get?" and the AI will list the correct key names - Paste an error message and the AI will help diagnose and fix it
- Ask "Add a 10% stop loss to this strategy and run a backtest" and the AI will modify the code directly
For detailed instructions, see Getting Started.
What is the difference between AI Skill and studio.finlab.tw?
- studio.finlab.tw: An online AI platform that requires no installation. Write strategies by chatting directly in the browser.
- FinLab Skill: Installed into local AI coding tools (Claude Code, Cursor, etc.). The AI can read/write files, execute code, and debug directly in your development environment. Suitable for advanced users who already have a local development setup.
Authentication
I can't find the api_token input box and am unable to fetch data. What should I do?
If you are using VSCode or another IDE, you can add the following code at the beginning of your script to log in directly:
Data Download
Can't get complete data? Why does the data range only go up to 2020?
This is because you have not subscribed to VIP. For non-VIP members, we only provide data up to the end of 2020. Consider subscribing to access historical data up to the latest trading day.
Why does the downloaded data only have index values, with all values being empty?
You may have set set_universe. Execute data.universe_stocks = {} or restart the entire kernel and re-run your code.
Common Strategy Syntax
Do I need to use reindex to align different financial data indexes for calculations?
No, this would waste a lot of time on data alignment. In the finlab package, all data retrieved via data.get will automatically align during binary operations. You don't need to spend any time aligning data. Here's an example:
For instance, if we retrieve this data:
from finlab import data
close = data.get('price:收盤價')
roa = data.get('fundamental_features:ROA稅後息前')
close and roa are FinlabDataFrame, so instead of creating conditions like this:
You can simplify it to:
When performing arithmetic or comparison operations, the system automatically computes roa_daily behind the scenes, so you don't need to do it manually!
How can I exclude specific months from backtesting (e.g., stop trading during certain months)?
Taking July and August as an example (no strategy signals during these months), simply intersect month_condition with your other conditions:
How do I reshape a specific column from a database table (e.g., 'Company Basic Info') into the same format as other backtest data (index=date, columns=stock_id)?
Taking the '已發行普通股數或TDR原發行股數' (shares outstanding) column as an example:
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))
Backtesting
Why isn't stop loss/take profit working correctly?
When using:
Even withstop_loss enabled, if the system detects that position has a holding on a given day, it prioritizes position. Only when your position index is sparse (e.g., index only has monthly entries) and the system cannot find the position to set for that day, will it use stop_loss to monitor whether to sell.
This might seem counterintuitive, but consider: if after a stop loss is triggered, the position is still True, should the system re-enter the position the next day, or has it already been stopped out? This becomes ambiguous. So if position is True, it takes priority over stop loss — this is the simplest approach.
If your position index covers every trading day, stop_loss will indeed have no effect. Here are some solutions:
- If you are using
hold_untilto create your position, you can use: - If you are not using
hold_until, you can set stop loss/take profit as follows:
How should I optimize and validate a strategy?
For detailed concepts, refer to: https://www.finlab.tw/phcebus-thinking-report-part2-backtest-sop/ and implement using the FinLab backtesting system.
Can I disable the web report when running backtests locally or on Colab?
Set the upload=False parameter in the sim backtest module:
Why are my backtest returns different from what I calculated manually?
First check whether you are using adjusted prices, confirm the exact entry/exit timing of your strategy, and whether you are using open or close prices. If you still have questions, discuss them in the Discord forum.
Why doesn't Edge ratio appear in report's display_mae_mfe_analysis?
You need to set the mae_mfe_window parameter in the sim backtest module. See https://doc.finlab.tw/reference/backtest/ .
How can I further customize rebalancing frequency (advanced resample usage)?
Refer to the pandas documentation at https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.resample.html and apply it directly to the position before passing it to the backtest module.
How do I resolve a MemoryError when running on the platform?
This exceeds the web memory limit. We recommend moving to Google Colab.
What does the green dot next to a strategy name mean?
It indicates that the strategy has a signal change or rebalancing event on that day.
Environment
How do I install TA-Lib?
- On Google Colab: Use
!pip install ta-lib-binto install - Via conda: In other kernels, run
$ conda install -c conda-forge ta-libto install - macOS: First install Homebrew, then use
brew install ta-libto install - Windows: Download the appropriate version wheel from https://www.lfd.uci.edu/~gohlke/pythonlibs/#ta-lib and run
$ pip install filename.whlfrom the download directory
How do I export data from Colab? How do I import data and set the correct path?
You need to mount Google Drive. You can click "Mount Drive" in the "Files" menu on the left panel, or directly enter and execute:
Where "/drive/MyDrive" is the Google Drive root directory.Backtest results on my local machine or Colab differ from those on the platform?
The results from running the latest version of the package on your local machine or Colab should be considered authoritative.
Is the FinLab platform suitable for use on mobile phones?
For viewing strategy performance, individual stock data, and industry information, mobile is fine. However, for writing and executing strategies, due to memory limitations, we recommend using a computer.
Order Execution
Can I execute orders directly on the FinLab platform?
No. To connect to a broker's order API, you need to use another environment.
How do I connect to a broker API for order execution?
FinLab has partnerships with SinoPac Securities and Yushan Fugle. After opening the relevant account, refer to the documentation to start integration. The documentation includes tutorial videos — just follow the steps: https://doc.finlab.tw/details/order_api/
How do I avoid closing positions that don't belong to the current strategy when placing orders?
First create a Position object and define manual positions (positions unrelated to the current strategy). In the OrderExecutor's position parameter, pass the sum of your strategy position and manual positions, then place orders directly.
What should I do if I missed a strategy's entry/exit date?
You can use Edge Ratio analysis to determine whether it's appropriate to enter mid-cycle. For detailed explanation, see Ben's article: https://www.finlab.tw/edge-ratio-follow-application/
Platform Usage
Where can I set up automatic strategy updates so I don't have to click every day?
On the strategy page of the platform, click "Auto Update" at the top. You can then select which strategies to update and set the time for daily automatic updates.
Will I be notified when strategy signals change?
Strategies with auto-update enabled on the platform will trigger an email notification when signal changes occur (notifications are sent between 06:00-07:00 on business days).
What's the difference between 'Current Statistics' and 'Recent Changes List' in the strategy list?
Current Statistics includes the strategy's actual current holdings. The Recent Changes List includes both current holdings and recent changes for individual stocks.
Why does the 'Recent Changes List' change every day?
As the data used in the strategy changes daily, stocks that haven't been entered yet will change accordingly. In practice, you should execute rebalancing at your originally set rebalancing cycle.
Subscription & Other Questions
Can I change the credit card linked to my subscription?
Yes. Click on the member page in the upper right corner of the platform, then click the "Payment Settings & Records" tab, and click "Change Invoice and Credit Card".
I registered with the wrong account. How do I switch the FinLab account I registered with?
Simply log in with a new Google account, then send an email from the old account's email address to finlab.company@finlab.tw to request the change.
I have questions about using the platform. Where is the support system? How do I ask questions?
FinLab has a dedicated Discord chat room. Search for "Python 選股實驗室" on Discord, or go to the FinLab website and navigate to "Community > Discord Chat Room" on the left sidebar.
Questions should primarily be about platform/package usage issues. For general programming syntax or non-FinLab-related problems, please try using Google or ChatGPT first.
When asking questions, provide a complete description of your situation, including your environment, package version, when the issue occurred, and attach complete code and error messages. The more information you provide, the faster the support team can help you. For strategy-related questions, describe the concept logically for more efficient solutions.
To prevent messages from getting lost in high-traffic channels and to enable easy tracking of extended discussions, please use the forum section for topic-based discussions.
Where can I submit feature requests?
You can leave your wishes in the VIP-exclusive wish pool on Discord. VIP only!
How long will it take for support to reply? What are the customer service hours?
Customer service hours are Monday to Friday, 9:00-17:00. Questions posted on Discord will be answered within two business days.
Where can I find new research and tutorial updates?
Research and tutorial information is posted on Discord's tutorial resources channel and the Facebook fan page. Research results are primarily in the form of blog articles and YouTube videos — follow and bookmark them!
FinLab YouTube Channel: https://www.youtube.com/@FinlabPython FinLab Blog: https://www.finlab.tw/
Common Errors & Solutions
KeyError: 'price:收盤價' - Data table not found
Symptom: KeyError is thrown when executing data.get()
Causes: - Data table name is misspelled - API Token is not set or is invalid - Non-VIP member trying to access latest data
Solution:
import finlab
# 1. Confirm login
finlab.login('YOUR_API_TOKEN') # Get it from https://ai.finlab.tw/member_info
# 2. Use data.search() to find the correct field name
from finlab import data
matching_fields = data.search('收盤')
print(matching_fields) # Verify the correct name
# 3. Test data download
close = data.get('price:收盤價')
print(f"Data range: {close.index[0]} ~ {close.index[-1]}")
Reference: Data Download Error Handling
Empty DataFrame
Symptom: Download succeeds but the DataFrame contains no data
Causes:
- Filter criteria are too strict when using data.universe()
- Industry category name doesn't exist
- Data source temporarily has no data
Solution:
from finlab import data
# Check data integrity
close = data.get('price:收盤價')
if close.empty:
print("Data is empty")
print("Possible causes:")
print("1. Universe filter criteria too strict")
print("2. Data table itself has no data")
else:
print(f"Data OK: {close.shape[0]} trading days, {close.shape[1]} stocks")
Reference: Data Download Error Handling
Strategy has no trade records
Symptom: Backtest completes but there are no trades; get_trades() returns an empty DataFrame
Causes:
- Entry conditions are too strict; position is almost entirely False
- Data date range is too short
- Stock pool after filtering is too small
Solution:
from finlab.backtest import sim
# Check entry signal statistics
entry_stats = position.sum(axis=1)
print(f"Average daily entry stocks: {entry_stats.mean():.2f}")
print(f"Maximum entry stocks: {entry_stats.max()}")
if entry_stats.mean() < 1:
print("Warning: Entry conditions too strict")
print("Suggestions:")
print("1. Relax conditions (e.g., price range)")
print("2. Use .is_largest(N) to ensure a fixed number of stocks")
# Fix example: ensure fixed number of stocks
position_fixed = (close > ma20).is_largest(30)
# Run backtest
report = sim(position, resample='M')
trades = report.get_trades()
if len(trades) == 0:
print("No trade records. Please relax entry conditions.")
else:
print(f"Backtest successful: {len(trades)} trades")
Reference: Backtest Error Handling
KeyError: Timestamp('2023-05-01') - Date index error
Symptom: KeyError is thrown during backtest with an error message showing a date doesn't exist
Causes:
- Date index ranges are inconsistent when using multiple data sources
- Using shift() or rolling() causes missing data at the beginning
Solution:
import finlab
# Method 1: Use truncate_start to align start dates
finlab.truncate_start = '2020-01-01'
# Method 2: Manually align date indexes
from finlab import data
close = data.get('price:收盤價')
volume = data.get('price:成交股數')
# Check date ranges
print(f"Close prices: {close.index[0]} ~ {close.index[-1]}")
print(f"Volume: {volume.index[0]} ~ {volume.index[-1]}")
# Align
common_dates = close.index.intersection(volume.index)
close_aligned = close.loc[common_dates]
volume_aligned = volume.loc[common_dates]
# Method 3: Remove missing values
position = (close > close.average(20)) & (volume > 1000)
position_cleaned = position.dropna(how='all', axis=0) # Remove rows that are all NaN
position_cleaned = position_cleaned.fillna(False)
Reference: Backtest Error Handling
Broker account connection failure
Symptom: Connection error when executing SinopacAccount() or EsunAccount()
Causes: - Environment variables not set correctly (API KEY, certificate path) - Certificate file doesn't exist or password is wrong - Network connection issues
Solution:
import os
from finlab.online.sinopac_account import SinopacAccount
# Check environment variables
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} is not set")
print("Reference: https://doc.finlab.tw/details/order_api/")
# Check certificate file
cert_path = os.environ.get('SHIOAJI_CERT_PATH')
if cert_path and not os.path.exists(cert_path):
print(f"Certificate file not found: {cert_path}")
# Attempt connection
try:
acc = SinopacAccount()
print("Account connection successful")
except Exception as e:
print(f"Connection failed: {e}")
print("Please check your API key, certificate path, and password")
Reference: Live Trading Error Handling
Insufficient funds for order placement
Symptom: Insufficient funds error when placing orders
Causes: - Available funds are insufficient to buy stocks specified in position - Commission fees and transaction tax not accounted for - Account balance doesn't match expectations
Solution:
from finlab.online.order_executor import OrderExecutor
# Check funds before placing orders
available_cash = account.get_balance()
required_cash = position.total_value # Estimated required funds (depends on actual API)
print(f"Available funds: NT$ {available_cash:,.0f}")
print(f"Estimated cost: NT$ {required_cash:,.0f}")
if required_cash > available_cash * 0.9: # Leave 10% buffer
print("Insufficient funds")
print("Suggestions:")
print("1. Reduce total capital allocation")
print("2. Add more funds")
print("3. Reduce number of holdings")
else:
# Execute orders
executor = OrderExecutor(position, account=account)
executor.create_orders(view_only=True) # Preview first
Reference: Live Trading Error Handling
Order rejected (disposition stocks not deposited)
Symptom: Some orders are rejected by the broker after executing create_orders()
Causes: - Disposition stocks not deposited (圈存) - Stock is suspended from trading - Limit up/down lock prevents execution
Solution:
from finlab.online.order_executor import OrderExecutor
executor = OrderExecutor(position, account=account)
# 1. Check for disposition stocks
alerting_stocks = executor.show_alerting_stocks()
if alerting_stocks:
print("Warning: Disposition stocks found. Please deposit them at your broker platform before placing orders.")
print("Deposit tutorials:")
print("- Fugle: https://support.fugle.tw/trading/trading-troubleshoot/5231/")
print("- SinoPac: https://www.sinotrade.com.tw/richclub/freshman/...")
input("Press Enter after deposit is complete...")
# 2. Preview first
executor.create_orders(view_only=True)
# 3. Execute after confirmation
confirmation = input("Enter 'YES' to confirm and execute orders: ")
if confirmation == 'YES':
executor.create_orders()
Reference: Live Trading Error Handling
File read failure (custom market)
Symptom: FileNotFoundError when running a custom market
Causes: - CSV file doesn't exist or path is incorrect - Using a relative path but working directory is wrong
Solution:
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')
# Check if file exists
if not os.path.exists(file_path):
raise FileNotFoundError(
f"Data file not found: {file_path}\n"
f"Please verify:\n"
f"1. File exists in {self.data_dir} directory\n"
f"2. File name is correct\n"
f"3. Working directory: {os.getcwd()}"
)
df = pd.read_csv(file_path, index_col=0, parse_dates=True)
return df
# Usage
try:
market = CryptoMarket(data_dir='/path/to/data')
close = market.get_price('close')
except FileNotFoundError as e:
print(e)
Reference: Custom Market Error Handling
Data format error (Index must be DatetimeIndex)
Symptom: DataFrame format doesn't meet expectations, causing backtest failure
Causes: - Index is not a DatetimeIndex - Data types are wrong (strings instead of numbers)
Solution:
import pandas as pd
# Read CSV
df = pd.read_csv('data.csv', index_col=0)
# Convert index to DatetimeIndex
if not isinstance(df.index, pd.DatetimeIndex):
try:
df.index = pd.to_datetime(df.index)
print("Index successfully converted to DatetimeIndex")
except Exception as e:
print(f"Unable to convert Index: {e}")
print(f"Index samples: {df.index[:3].tolist()}")
print("Please verify the first column format is a date (e.g., 2020-01-01)")
# Check data types
non_numeric_cols = df.select_dtypes(exclude=['number']).columns.tolist()
if non_numeric_cols:
print(f"Warning: The following columns are non-numeric: {non_numeric_cols}")
for col in non_numeric_cols:
df[col] = pd.to_numeric(df[col], errors='coerce')
Reference: Custom Market Error Handling
Insufficient training data (Machine Learning)
Symptom: ML model training reports insufficient data
Causes: - Training set has < 1000 samples - Significant data loss after feature-label alignment
Solution:
from finlab.ml import feature as mlf, label as mll
# Feature-label alignment
data_ml = features.join(labels, how='inner')
# Train/test split
train_data = data_ml[data_ml.index.get_level_values(0) <= '2022-12-31']
print(f"Training set: {len(train_data)} samples")
if len(train_data) < 1000:
print("Warning: Insufficient training data (< 1000 samples)")
print("Suggestions:")
print("1. Increase historical data range")
print("2. Lower resample frequency (change '1d' to 'W')")
print("3. Reduce number of features (reduce missing values)")
Reference: ML Error Handling
Train/test set date overlap (Machine Learning)
Symptom: Training and test set dates overlap during data splitting, causing data leakage
Causes: - Time series splitting not done correctly - Last date in training set >= first date in test set
Solution:
# Define split dates
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]
# Verify date ordering
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"Training and test set dates overlap!\n"
f"Training set last date: {train_last_date}\n"
f"Test set first date: {test_first_date}\n"
f"This will cause data leakage"
)
print(f"Data split is correct")
Reference: ML Error Handling
All predictions are NaN (Machine Learning)
Symptom: All prediction results are NaN after ML model inference
Causes: - Model training failed silently without raising an error - Prediction data contains NaN or Inf values
Solution:
import pandas as pd
# Predict
all_pred = model.predict(data_ml.drop(columns=['label']))
# Check prediction results
if pd.Series(all_pred).isna().all():
print("All predictions are NaN")
print("Please check:")
print("1. Whether the model was trained correctly")
print("2. Whether prediction data contains NaN or Inf")
# Check prediction data
X_pred = data_ml.drop(columns=['label'])
print(f"NaN count: {X_pred.isna().sum().sum()}")
print(f"Inf count: {(X_pred == float('inf')).sum().sum()}")
# Fill missing values
X_pred = X_pred.fillna(0).replace([float('inf'), float('-inf')], 0)
all_pred = model.predict(X_pred)
print(f"Prediction complete: {len(all_pred)} samples")
Reference: ML Error Handling