| name | finlab |
| description | Comprehensive guide for FinLab quantitative trading package for Taiwan stock market (台股). Use when working with trading strategies, backtesting, Taiwan stock data, FinLabDataFrame, factor analysis, stock selection, or when the user mentions FinLab, trading, 回測, 策略, 台股, quant trading, or stock market analysis. Includes data access, strategy development, backtesting workflows, and best practices. |
| allowed-tools | Read, Grep, Glob, Bash |
FinLab Quantitative Trading Package
Overview
FinLab is a comprehensive Python package for quantitative trading strategy development, backtesting, and financial data analysis, specifically designed for the Taiwan stock market (TSE/OTC, 台股). It provides:
- Extensive Data Access: Price data, financial statements, monthly revenue, valuation metrics, institutional trading, technical indicators
- FinLabDataFrame: Enhanced pandas DataFrame with trading-specific methods (
is_largest,is_smallest,rise,fall,sustain,hold_until) - Backtesting Engine: Robust
sim()function with rebalancing, transaction costs, stop-loss/take-profit, risk management - Factor Analysis: IC calculation, Shapley values, centrality analysis, regression trends
- Machine Learning: Feature engineering for TA-Lib indicators, label generation for returns
Prerequisites
Before running any FinLab code, verify:
API Token is set (required - finlab will fail without it):
echo $FINLAB_API_TOKEN # If empty, set it: export FINLAB_API_TOKEN="your_token" # Get token from: https://ai.finlab.tw/api_token/FinLab is installed:
python3 -c "import finlab" || python3 -m pip install finlab
Quick Start Example
from finlab import data
from finlab.backtest import sim
# 1. Fetch data
close = data.get("price:收盤價")
vol = data.get("price:成交股數")
pb = data.get("price_earning_ratio:股價淨值比")
# 2. Create conditions
cond1 = close.rise(10) # Rising last 10 days
cond2 = vol.average(20) > 1000*1000 # High liquidity
cond3 = pb.rank(axis=1, pct=True) < 0.3 # Low P/B ratio
# 3. Combine conditions and select stocks
position = cond1 & cond2 & cond3
position = pb[position].is_smallest(10) # Top 10 lowest P/B
# 4. Backtest
report = sim(position, resample="M", upload=False)
# 5. Print metrics - Two equivalent ways:
# Option A: Using metrics object
print(report.metrics.annual_return())
print(report.metrics.sharpe_ratio())
print(report.metrics.max_drawdown())
# Option B: Using get_stats() dictionary (different key names!)
stats = report.get_stats()
print(f"CAGR: {stats['cagr']:.2%}")
print(f"Sharpe: {stats['monthly_sharpe']:.2f}")
print(f"MDD: {stats['max_drawdown']:.2%}")
report
Core Workflow: 5-Step Strategy Development
Step 1: Fetch Data
Use data.get("<TABLE>:<COLUMN>") to retrieve data:
from finlab import data
# Price data
close = data.get("price:收盤價")
volume = data.get("price:成交股數")
# Financial statements
roe = data.get("fundamental_features:ROE稅後")
revenue = data.get("monthly_revenue:當月營收")
# Valuation
pe = data.get("price_earning_ratio:本益比")
pb = data.get("price_earning_ratio:股價淨值比")
# Institutional trading
foreign_buy = data.get("institutional_investors_trading_summary:外陸資買賣超股數(不含外資自營商)")
# Technical indicators
rsi = data.indicator("RSI", timeperiod=14)
macd, macd_signal, macd_hist = data.indicator("MACD", fastperiod=12, slowperiod=26, signalperiod=9)
Filter by market/category using data.universe():
# Limit to specific industry
with data.universe(market='TSE_OTC', category=['水泥工業']):
price = data.get('price:收盤價')
# Set globally
data.set_universe(market='TSE_OTC', category='半導體')
See data-reference.md for complete data catalog.
Step 2: Create Factors & Conditions
Use FinLabDataFrame methods to create boolean conditions:
# Trend
rising = close.rise(10) # Rising vs 10 days ago
sustained_rise = rising.sustain(3) # Rising for 3 consecutive days
# Moving averages
sma60 = close.average(60)
above_sma = close > sma60
# Ranking
top_market_value = data.get('etl:market_value').is_largest(50)
low_pe = pe.rank(axis=1, pct=True) < 0.2 # Bottom 20% by P/E
# Industry ranking
industry_top = roe.industry_rank() > 0.8 # Top 20% within industry
See dataframe-reference.md for all FinLabDataFrame methods.
Step 3: Construct Position DataFrame
Combine conditions with & (AND), | (OR), ~ (NOT):
# Simple position: hold stocks meeting all conditions
position = cond1 & cond2 & cond3
# Limit number of stocks
position = factor[condition].is_smallest(10) # Hold top 10
# Entry/exit signals with hold_until
entries = close > close.average(20)
exits = close < close.average(60)
position = entries.hold_until(exits, nstocks_limit=10, rank=-pb)
Important: Position DataFrame should have:
- Index: DatetimeIndex (dates)
- Columns: Stock IDs (e.g., '2330', '1101')
- Values: Boolean (True = hold) or numeric (position size)
Step 4: Backtest
from finlab.backtest import sim
# Basic backtest
report = sim(position, resample="M")
# With risk management
report = sim(
position,
resample="M",
stop_loss=0.08,
take_profit=0.15,
trail_stop=0.05,
position_limit=1/3,
fee_ratio=1.425/1000/3,
tax_ratio=3/1000,
trade_at_price='open',
upload=False
)
# Extract metrics - Two ways:
# Option A: Using metrics object
print(f"Annual Return: {report.metrics.annual_return():.2%}")
print(f"Sharpe Ratio: {report.metrics.sharpe_ratio():.2f}")
print(f"Max Drawdown: {report.metrics.max_drawdown():.2%}")
# Option B: Using get_stats() dictionary (note: different key names!)
stats = report.get_stats()
print(f"CAGR: {stats['cagr']:.2%}") # 'cagr' not 'annual_return'
print(f"Sharpe: {stats['monthly_sharpe']:.2f}") # 'monthly_sharpe' not 'sharpe_ratio'
print(f"MDD: {stats['max_drawdown']:.2%}") # same name
See backtesting-reference.md for complete sim() API.
Step 5: Execute Orders (Optional)
Convert backtest results to live trading:
from finlab.online.order_executor import Position, OrderExecutor
from finlab.online.sinopac_account import SinopacAccount
# 1. Convert report to position
position = Position.from_report(report, fund=1000000)
# 2. Connect broker account
acc = SinopacAccount()
# 3. Create executor and preview orders
executor = OrderExecutor(position, account=acc)
executor.create_orders(view_only=True) # Preview first
# 4. Execute orders (when ready)
executor.create_orders()
See trading-reference.md for complete broker setup and OrderExecutor API.
Documentation Structure
This skill includes comprehensive reference documentation:
- data-reference.md: Complete data catalog (900+ columns across 80+ tables),
data.get()usage,data.universe()filtering - backtesting-reference.md:
sim()function API, all parameters, resampling strategies, metric extraction - trading-reference.md: Order execution, Position class, broker account setup (Esun/Sinopac/Masterlink/Fubon), OrderExecutor API
- factor-examples.md: 60+ complete factor examples (momentum, value, quality, growth, technical)
- dataframe-reference.md: All FinLabDataFrame methods with signatures and examples
- factor-analysis-reference.md: Factor analysis tools (IC, Shapley values, centrality)
- best-practices.md: Coding patterns, anti-patterns, future data pollution prevention
- machine-learning-reference.md: ML feature engineering and label generation
When to Use Each Reference
| Task | Reference File |
|---|---|
| Find available data sources | data-reference.md |
| Fetch price, revenue, financial statement data | data-reference.md |
| Filter stocks by industry/market | data-reference.md |
| Configure backtest parameters | backtesting-reference.md |
| Set stop-loss, take-profit, rebalancing | backtesting-reference.md |
| Execute orders to broker | trading-reference.md |
| Setup broker account (Esun/Sinopac/Masterlink/Fubon) | trading-reference.md |
| Calculate position from backtest | trading-reference.md |
| Find strategy examples | factor-examples.md |
| Calculate moving averages, trends | dataframe-reference.md |
| Select top N stocks | dataframe-reference.md |
| Combine entry/exit signals | dataframe-reference.md |
| Analyze factor performance | factor-analysis-reference.md |
| Avoid common mistakes | best-practices.md |
| Prevent lookahead bias | best-practices.md |
| Build ML models for trading | machine-learning-reference.md |
Common Use Cases
Use Case 1: Value + Momentum Strategy
from finlab import data
from finlab.backtest import sim
# Value: Low P/B ratio
pb = data.get("price_earning_ratio:股價淨值比")
low_pb = pb.rank(axis=1, pct=True) < 0.3
# Momentum: Rising price
close = data.get("price:收盤價")
momentum = close.rise(20)
# Liquidity filter
vol = data.get("price:成交股數")
liquid = vol.average(20) > 500*1000
# Combine
position = low_pb & momentum & liquid
position = pb[position].is_smallest(15)
report = sim(position, resample="M", stop_loss=0.1)
Use Case 2: Monthly Revenue Growth Strategy
from finlab import data
from finlab.backtest import sim
rev = data.get("monthly_revenue:當月營收")
rev_growth = data.get("monthly_revenue:去年同月增減(%)")
# Revenue at new high
rev_ma3 = rev.average(3)
rev_high = (rev_ma3 / rev_ma3.rolling(12).max()) == 1
# Strong growth
strong_growth = (rev_growth > 20).sustain(3)
position = rev_high & strong_growth
position = rev_growth[position].is_largest(10)
# Use monthly revenue index for rebalancing
position_resampled = position.reindex(rev.index_str_to_date().index, method="ffill")
report = sim(position_resampled)
Use Case 3: Technical Indicator Strategy
from finlab import data
from finlab.backtest import sim
close = data.get("price:收盤價")
rsi = data.indicator("RSI", timeperiod=14)
# RSI golden cross
rsi_short = data.indicator("RSI", timeperiod=7)
rsi_long = data.indicator("RSI", timeperiod=21)
golden_cross = (rsi_short > rsi_long) & (rsi_short.shift() < rsi_long.shift())
# Above moving average
sma60 = close.average(60)
uptrend = close > sma60
position = golden_cross & uptrend & (rsi < 70)
position = position[position].is_smallest(20)
report = sim(position, resample="W")
Key Concepts
FinLabDataFrame Automatic Alignment
FinLabDataFrame automatically aligns indices and columns during operations:
close = data.get("price:收盤價") # Daily data
revenue = data.get("monthly_revenue:當月營收") # Monthly data
# Automatically aligns - no manual reindexing needed
position = close > close.average(60) & (revenue > revenue.shift(1))
Prevent Future Data Pollution
Critical: Avoid lookahead bias (using future data to make past decisions):
# ✅ GOOD: Use shift(1) to get previous value
prev_close = close.shift(1)
# ❌ BAD: Don't use iloc[-2] (can cause lookahead)
# prev_close = close.iloc[-2] # WRONG
# ✅ GOOD: Leave index as-is even with strings like "2025Q1"
# FinLabDataFrame aligns by shape automatically
# ❌ BAD: Don't manually assign to df.index
# df.index = new_index # FORBIDDEN
See best-practices.md for comprehensive anti-patterns.
Installation & Setup
See Prerequisites section for API token and installation verification.
# Common imports
from finlab import data
from finlab.backtest import sim
from finlab.dataframe import FinLabDataFrame
Getting Help
- For complete data catalog: see data-reference.md
- For factor examples: see factor-examples.md
- For best practices: see best-practices.md
- For backtesting parameters: see backtesting-reference.md
Notes
- All strategy code examples use Traditional Chinese (繁體中文) variable names where appropriate
- This package is specifically designed for Taiwan stock market (TSE/OTC)
- Data frequency varies: daily (price), monthly (revenue), quarterly (financial statements)
- Always use
sim(..., upload=False)for experiments,upload=Trueonly for final production strategies