finlab.market
The Market object defines how the backtesting system retrieves price data, benchmark indices, market characteristics, and other information. FinLab has built-in support for Taiwan stocks, US stocks, and the Emerging Stock Board. You can also create custom markets by inheriting the Market class.
Market Object Concept
Each market has different characteristics:
- Price data source: Where to get open, close, volume, and other price data
- Benchmark index: Benchmark for performance comparison (e.g., TAIEX for Taiwan, S&P 500 for US)
- Trading hours: Open/close times and timezone
- Market rules: Price limits, transaction costs, special stock categories
Through market objects, the backtesting engine can:
- Retrieve the correct price data
- Calculate correct returns
- Display performance comparison against the benchmark
- Support time-based decisions for live trading
Built-in Market Comparison
| Feature | TWMarket | USMarket | ROTCMarket |
|---|---|---|---|
| Market name | 'tw_stock' |
'us_stock' |
'rotc_stock' |
| Data frequency | Daily ('1d') |
Daily ('1d') |
Daily ('1d') |
| Benchmark | TAIEX | S&P 500 | (None) |
| Timezone | Asia/Taipei | US/Eastern | Asia/Taipei |
| Market close | 15:00 | 16:00 | 14:00 |
| Price limit | 10% | None | None |
| Special stock categories | Disposal/full-delivery | None | None |
| Data source | finlab.data |
finlab.data |
finlab.data |
Use Cases
1. Using Built-in Markets
from finlab import data, backtest
from finlab.markets.tw import TWMarket
from finlab.markets.us import USMarket
# Taiwan stock backtest (default)
tw_close = data.get('price:收盤價')
tw_position = tw_close > tw_close.average(20)
report = backtest.sim(tw_position, resample='M')
# Equivalent to
report = backtest.sim(tw_position, resample='M', market=TWMarket())
# US stock backtest
us_close = data.get('etl:us_adj_close')
us_position = us_close > us_close.average(50)
report = backtest.sim(us_position, resample='W', market=USMarket())
2. Custom Market - Cryptocurrency
from finlab.market import Market
import pandas as pd
class CryptoMarket(Market):
@staticmethod
def get_name():
return 'crypto'
def get_price(self, trade_at_price='close', adj=True):
df = pd.read_csv('crypto_close.csv', index_col=0, parse_dates=True)
return df
@staticmethod
def get_benchmark():
df = pd.read_csv('crypto_close.csv', index_col=0, parse_dates=True)
return df['BTC'] # Use BTC as benchmark
# Usage
report = backtest.sim(position, market=CryptoMarket())
3. Custom Market - Futures (with leverage)
class FuturesMarket(Market):
def __init__(self, leverage=10):
self.leverage = leverage
self.prices = pd.read_csv('futures_price.csv', index_col=0, parse_dates=True)
@staticmethod
def get_name():
return 'futures'
def get_price(self, trade_at_price='close', adj=True):
price = self.prices[trade_at_price]
daily_return = price.pct_change() * self.leverage
leveraged_price = (1 + daily_return).fillna(1).cumprod() * 100
return leveraged_price
# Use 10x leverage
report = backtest.sim(position, market=FuturesMarket(leverage=10))
Common Custom Market Scenarios
| Scenario | Description | Examples |
|---|---|---|
| Cryptocurrency | 24-hour trading, no price limits | BTC, ETH, BNB |
| Futures | Leverage effect, need to simulate leveraged returns | TAIEX futures, US stock futures |
| Foreign stocks | Markets not in FinLab database | Japanese, Hong Kong, Chinese stocks |
| Custom data | Load prices from CSV, API | Private data, simulated data |
| Mixed markets | Backtest combinations of multiple markets | Taiwan + US + Crypto |
Detailed Guide
See Custom Market Object Guide for:
- Complete Market class API documentation
- Required vs optional methods
- How to handle multiple timezones
- How to implement market holiday logic
- Loading data from CSV, API, FinLab database (mixed)
- 3 practical examples (Gold ETF, Global Market, Crypto 4H)
API Reference
finlab.market.Market
Bases: ABC
市場類別
假如希望開發新的交易市場套用到回測系統,可以繼承 finlab.market.Market 來實做新類別。
default_fee_ratio
staticmethod
Returns the default fee ratio for this market.
| RETURNS | DESCRIPTION |
|---|---|
float
|
The default fee ratio.
TYPE:
|
default_tax_ratio
staticmethod
Returns the default tax ratio for this market.
| RETURNS | DESCRIPTION |
|---|---|
float
|
The default tax ratio.
TYPE:
|
get_asset_id_to_name
staticmethod
設定對標報酬率的時間序列
Returns:
(dict): 股號與股名對照表,ex:{'2330':'台積電'}
get_benchmark
staticmethod
設定對標報酬率的時間序列
這個函數用於設定對標報酬率的時間序列。
| RETURNS | DESCRIPTION |
|---|---|
Series
|
pd.Series: 時間序列的報酬率。 |
| RAISES | DESCRIPTION |
|---|---|
ExceptionType
|
Description of conditions under which the exception is raised. |
Examples:
| date | 0050 |
|---|---|
| 2007-04-23 | 100 |
| 2007-04-24 | 100.1 |
| 2007-04-25 | 99 |
| 2007-04-26 | 98.3 |
| 2007-04-27 | 99.55 |
get_board_lot_size
staticmethod
Returns the board lot size of the market.
| RETURNS | DESCRIPTION |
|---|---|
int
|
The board lot size of the market.
TYPE:
|
get_freq
staticmethod
Returns the frequency of the data.
Used to determine how to resample the data when the data is not daily.
The freq will be saved in finlab.core.report.
Returns: str: The frequency of the data.
get_market_value
staticmethod
取得回測用市值數據
| RETURNS | DESCRIPTION |
|---|---|
DataFrame
|
市值數據,其中 index 為日期,而 columns 是股票代號。 |
get_name
staticmethod
Returns the name of the market data source.
This function is used to get the name of the market data source.
get_odd_lot
staticmethod
Returns the odd lot size of the market.
| RETURNS | DESCRIPTION |
|---|---|
int
|
The odd lot size of the market.
TYPE:
|
get_price
取得回測用價格或成交量數據
Subclasses that define _price_table and _adj_prefix class
variables inherit this default implementation automatically.
Markets with different schemas (e.g. TWMarket) should override.
| PARAMETER | DESCRIPTION |
|---|---|
trade_at_price
|
價格類型或自訂資料 - str: 可選 'open', 'close', 'high', 'low', 'volume' - pd.Series: 自訂價格序列(單一股票或時間序列) - pd.DataFrame: 自訂價格資料(多檔股票)
TYPE:
|
adj
|
是否使用還原股價計算。僅對價格資料有效,volume 會忽略此參數。
TYPE:
|
| RETURNS | DESCRIPTION |
|---|---|
DataFrame
|
價格或成交量數據,index 為日期,columns 為股票代號。 |
Note
必須實作的 trade_at_price 值:
- 'open', 'close', 'high', 'low': 基本價格資料(必須實作)
- 用於
get_trading_price()計算組合價格 -
用於回測的交易價格
-
'volume': 成交量資料(強烈建議實作)
LiquidityAnalysis: 流動性分析需要計算成交金額ml/qlib.py: 機器學習特徵生成portfolio_sync_manager.py: 實時 portfolio 管理-
若不實作,相關分析功能會失效
-
pd.Series, pd.DataFrame: 自訂資料(建議支援)
- 用於彈性回測,允許傳入自訂價格資料
- Series 會自動轉換為單欄 DataFrame
Examples:
回傳格式範例:
| date | 0015 | 0050 | 0051 | 0052 |
|---|---|---|---|---|
| 2007-04-23 | 9.54 | 57.85 | 32.83 | 38.4 |
| 2007-04-24 | 9.54 | 58.1 | 32.99 | 38.65 |
| 2007-04-25 | 9.52 | 57.6 | 32.8 | 38.59 |
| 2007-04-26 | 9.59 | 57.7 | 32.8 | 38.6 |
| 2007-04-27 | 9.55 | 57.5 | 32.72 | 38.4 |
詳細實作可參考 finlab.markets.tw.TWMarket.get_price()
get_reference_price
Returns the most recent reference price of the market.
| RETURNS | DESCRIPTION |
|---|---|
Dict[str, float]
|
pandas.Series: The most recent reference price of the market. |
get_trading_price
取得回測用價格數據
| PARAMETER | DESCRIPTION |
|---|---|
name
|
選擇回測之還原股價以收盤價或開盤價計算,預設為'close'。可選 'open'、'close'、'high'、'low'、'open_close_avg'、'high_low_avg'、或 'price_avg'。
TYPE:
|
Returns: (pd.DataFrame): 價格數據
market_close_at_timestamp
Returns the timestamp of the market close of the given timestamp.
| PARAMETER | DESCRIPTION |
|---|---|
timestamp
|
The timestamp to find the market close to.
TYPE:
|
| RETURNS | DESCRIPTION |
|---|---|
datetime
|
The timestamp of the closest market close. |
market_open_time
staticmethod
Returns (hour, minute) of market open in local timezone.
Quick Development Tips
The simplest custom market only requires implementing two methods:
get_name(): Return market nameget_price(): Return price DataFrame
All other methods are optional and can be implemented as needed.
Common Errors
get_price()must return a DataFrame with DatetimeIndex as index- columns should be stock tickers (str)
- Missing values should be handled with forward fill
FAQ
Q: How do I handle adjusted prices (adj=True)?
def get_price(self, trade_at_price='close', adj=True):
if adj:
# Return adjusted prices accounting for dividends and splits
return pd.read_csv('adj_close.csv', index_col=0, parse_dates=True)
else:
# Return raw closing prices
return pd.read_csv('raw_close.csv', index_col=0, parse_dates=True)
Q: Why is get_benchmark() needed?
The benchmark index is used for performance comparison. When report.display() is called, it shows the strategy vs benchmark return curve. If no benchmark is needed, return an empty Series:
Q: Can I use finlab.data.get() inside get_price()?
Yes! This is the standard approach for mixing FinLab data:
from finlab import data
class MixedMarket(Market):
def get_price(self, trade_at_price='close', adj=True):
tw_close = data.get('price:收盤價')[['2330', '2317']]
us_close = data.get('etl:us_adj_close')[['AAPL', 'TSLA']]
return pd.concat([tw_close, us_close], axis=1)
Q: How do I simulate transaction costs?
Transaction costs are set in the backtest.sim() function, not in the Market object:
# Cryptocurrency typically has lower fees, zero tax
report = backtest.sim(
position,
market=CryptoMarket(),
fee_ratio=0.001, # 0.1% commission
tax_ratio=0 # No transaction tax
)