Skip to content

FinLab FAQ: Login, Data Download, Backtesting, and Live Trading

If you are new to FinLab, start with Getting Started. If you are blocked by dataset keys or download errors, read Data Download. This page collects the most common login, data, backtesting, and order-execution questions.

Question type Fastest path Related docs
Cannot log in or fetch data Run finlab.login() again, then test data.get('price:adj_close') after data.set_market('us') Data Download
Do not know the dataset key Use data.search('keyword') to find fields finlab.data
Stop-loss or take-profit behaves unexpectedly Check whether position is sparse or use hold_until() Backtesting
Broker integration and live orders Configure the account first, then preview orders with OrderExecutor Order Tutorial

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 FinLab Studio?
  • FinLab Studio: 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 cannot find the login window or fetch data. What should I do?

If you are using VSCode, Terminal, or another IDE, run the browser-based login flow at the beginning of your script, then retry the data download:

import finlab

finlab.login()

from finlab import data

data.set_market('us')
close = data.get('price:adj_close')

For automated environments, run python -m finlab token --env locally first, then set the printed environment variables on the server or scheduler. Legacy finlab.login('token') remains compatible, but do not write tokens directly in notebooks or scripts.

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 a universe filter with set_universe(). Run data.reset_universe() to remove it, or restart the 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

data.set_market('us')
close = data.get('price:adj_close')
net_income = data.get('us_income_statement:net_income')
total_assets = data.get('us_balance_sheet:total_assets')
roa = net_income / total_assets  # quarterly
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!

Why can't I write df.index = pd.to_datetime(df.index) directly? Can this be only a warning?

This should not be downgraded to a warning. A FinlabDataFrame returned by data.get() preserves the timing semantics of financial statement data, such as the U.S. quarterly label 2024-US-Q1 (Taiwan data also uses 2024-Q1 and, for monthly revenue, 2024-M01). Assigning to index directly can accidentally treat the reporting period as the date when the data was publicly available, which may introduce lookahead bias in backtests.

For this reason, assigning directly to FinlabDataFrame.index raises an AttributeError by design. It is a safety guard, not a generic date-format conversion error.

For factors such as AssetGrowth that need to align data from different sources, you usually do not need to manually reindex the source data first:

from finlab import data

data.set_market('us')
close = data.get('price:adj_close')
asset = data.get('us_balance_sheet:total_assets')

# FinlabDataFrame automatically aligns different frequencies during operations
asset_growth = asset / asset.shift(4) - 1

# If the final position needs daily trade dates, reindex the position at the end
position = (asset_growth > 0).index_str_to_date()
position = position.reindex(close.index, method='ffill')

If you explicitly need to convert quarterly string indices into dates, use index_str_to_date(), which places each value on its company's actual filing date (Taiwan data also offers deadline(), the statutory filing deadline):

asset_by_filing = asset.index_str_to_date()

If you are certain that you want plain pandas semantics and will handle the timing yourself, convert to pandas.DataFrame first. This leaves FinlabDataFrame auto-alignment and lookahead protection, so you are responsible for verifying the time semantics:

plain = pd.DataFrame(asset_by_filing)
plain.index = pd.to_datetime(plain.index)

If the data comes from an external CSV or a custom market source, parse and clean dates as a regular pandas.DataFrame first, then wrap it as a FinlabDataFrame:

import pandas as pd

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

from finlab.dataframe import FinlabDataFrame

factor = FinlabDataFrame(raw)
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., the U.S. company profile) into the same format as other backtest data (index=date, columns=symbol)?

Taking the beta column of us_company_profile as an example (the profile holds one current value per symbol):

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

data.set_market('us')
profile = data.get('us_company_profile').set_index('symbol')
revenue = data.get('us_income_statement:revenue')
beta = profile['beta']
beta = FinlabDataFrame(
    pd.DataFrame([list(beta)], columns=profile.index, index=revenue.index)
)

For shares outstanding, use data.get('us_income_statement:weighted_average_shs_out'), which already has dates as the index and symbols as columns. The shares_outstanding column of us_company_profile is empty because the data source does not provide it.

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:
    buy = close > close.average(20)
    sell = close < close.average(60)
    
    # 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?

Follow the Research to Production workflow and tune parameters with Strategy Optimization, keeping an out-of-sample period for validation.

Can I disable the web report when running backtests locally or on Colab?

This is the default: sim()'s upload defaults to None and only uploads in cloud-scheduled runs (when the FINLAB_FORCED_STRATEGY_NAME environment variable is set); since 2.0.24 FinLab Studio also needs upload=True to upload. You can still pass upload=False explicitly:

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, contact FinLab support.

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 finlab.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?
  • Windows, macOS, Linux: Run $ pip install ta-lib; PyPI provides prebuilt wheels
  • On Google Colab: Use !pip install ta-lib to install
  • Via conda: In other kernels, run $ conda install -c conda-forge ta-lib to install
  • For other options, see the official installation guide
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?

For U.S. stocks, FinLab connects to Charles Schwab (SchwabAccount). Taiwan brokers (SinoPac, E.SUN/Fugle, Fubon, Masterlink) are also supported for the Taiwan market. After opening the account, follow the Order Tutorial.

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 (report.display_mae_mfe_analysis() with mae_mfe_window set in sim()) to determine whether it's appropriate to enter mid-cycle.

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. Open your account settings in FinLab Studio and change the card in the payment settings.

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?

Use the FinLab support page.

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.

Where can I submit feature requests?

Send feature requests through the FinLab support page.

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. Support requests are answered within two business days.

Where can I find new research and tutorial updates?

Research articles and strategy studies are published on the FinLab blog. Package changes are listed in the Changelog.

Common Errors & Solutions

DatasetNotFoundError - Data table not found

Symptom: finlab.exceptions.DatasetNotFoundError (a RuntimeError subclass) is raised when executing data.get()

Causes: - Data table name is misspelled - Login has not been completed or the login state is invalid - Non-VIP member trying to access latest data

Solution:

import finlab

# 1. Confirm login
finlab.login()

# 2. Use data.search() to find the correct field name
from finlab import data

matching_fields = data.search('close', market='us')
print(matching_fields)  # Verify the correct name

# 3. Test data download
data.set_market('us')
close = data.get('price:adj_close')
print(f"Data range: {close.index[0]} ~ {close.index[-1]}")

For servers or scheduled jobs, first run python -m finlab token --env locally, then set the printed environment variables in the runtime environment.

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.us_universe() or data.universe() - Sector or industry name doesn't exist - Data source temporarily has no data

Solution:

from finlab import data

# Check data integrity
data.set_market('us')
close = data.get('price:adj_close')

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
    # Rank by a score, not a condition: always the 30 strongest relative to their 20-day average
    ma20 = close.average(20)
    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:

from finlab import data

# Method 1: Use truncate_start to align start dates
data.truncate_start = '2020-01-01'

# Method 2: Manually align date indexes
data.set_market('us')
close = data.get('price:adj_close')
volume = data.get('price:volume')

# 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 > 1_000_000)
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 SchwabAccount() (or a Taiwan broker account such as SinopacAccount())

Causes: - Environment variables not set correctly (API key, app secret, token path) - Token file doesn't exist or has expired - Network connection issues

Solution:

import os
from finlab.online.schwab_account import SchwabAccount

# Check environment variables
required_vars = [
    'SCHWAB_API_KEY',
    'SCHWAB_SECRET',
    'SCHWAB_TOKEN_PATH',
]

for var in required_vars:
    if not os.environ.get(var):
        print(f"{var} is not set")

# Check token file
token_path = os.environ.get('SCHWAB_TOKEN_PATH')
if token_path and not os.path.exists(token_path):
    print(f"Token file not found: {token_path}")

# Attempt connection
try:
    acc = SchwabAccount()
    print("Account connection successful")
except Exception as e:
    print(f"Connection failed: {e}")
    print("Please check your API key, app secret, and token file")

Taiwan broker accounts read their own environment variables (API keys and certificate path and password); the Order Tutorial lists them per broker.

Reference: Live Trading Error Handling

Insufficient funds for order placement

Symptom: Insufficient funds error when placing orders

Causes: - Available funds are insufficient to buy the stocks in the target position - Commission fees not accounted for - Account balance doesn't match expectations

Solution:

from finlab.online.order_executor import OrderExecutor, Position

fund = 100_000  # Capital (USD) to allocate to this strategy

# Check funds before placing orders
available_cash = account.get_cash()

print(f"Available funds: ${available_cash:,.0f}")
print(f"Planned allocation: ${fund:,.0f}")

if fund > 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
    position = Position.from_report(report, fund)
    executor = OrderExecutor(position, account=account)
    executor.create_orders(view_only=True)  # Preview first

Reference: Live Trading Error Handling

Order rejected by the broker (Taiwan market: disposition stocks)

Symptom: Some orders are rejected by the broker after executing create_orders()

Causes (Taiwan market only): - Disposition stocks require cash or shares to be deposited at the broker before they can be traded - Stock is suspended from trading - A limit-up or limit-down lock prevents execution

Solution:

from finlab.online.order_executor import OrderExecutor

executor = OrderExecutor(position, account=account)

# 1. List alert and disposition stocks among the new orders
executor.show_alerting_stocks()

# 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()

If disposition stocks are listed, deposit the required cash or shares on your broker's platform before placing the 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 Guide

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 Guide

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 import data
from finlab.ml import feature as mlf, label as mll

data.set_market('us')
with data.us_universe(index='SP500'):
    close = data.get('price:adj_close')
features = mlf.combine({'momentum': close / close.shift(20)}, resample='W')
labels = mll.excess_over_mean(features.index, resample='W')

# Feature-label alignment
data_ml = features.join(labels.rename('label'), 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 finlab.ml.qlib as q

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

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

# Check prediction results
if all_pred.isna().all().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: {all_pred.notna().sum().sum()} predictions")

Reference: ML Error Handling