Skip to content

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:

import finlab
finlab.login('YOUR_api_token')

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稅後息前')
At this point, both close and roa are FinlabDataFrame, so instead of creating conditions like this:

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

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

You can simplify it to:

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

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:

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

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:

report = sim(position, stop_loss=0.1)
Even with stop_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:

  1. If you are using hold_until to create your position, you can use:
    # Original
    position = buy.hold_until(sell)
    # With stop loss
    position = buy.hold_until(sell, stop_loss=0.1)
    
  2. If you are not using hold_until, you can set stop loss/take profit as follows:
    position.is_entry().hold_until(position.is_exit(), stop_loss=0.1)
    
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:

report = sim(position, upload=False)

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-bin to install
  • Via conda: In other kernels, run $ conda install -c conda-forge ta-lib to install
  • macOS: First install Homebrew, then use brew install ta-lib to install
  • Windows: Download the appropriate version wheel from https://www.lfd.uci.edu/~gohlke/pythonlibs/#ta-lib and run $ pip install filename.whl from 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:

from google.colab import drive
drive.mount('/content/drive')
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