Skip to content

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:

  1. Retrieve the correct price data
  2. Calculate correct returns
  3. Display performance comparison against the benchmark
  4. 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

default_fee_ratio()

Returns the default fee ratio for this market.

RETURNS DESCRIPTION
float

The default fee ratio.

TYPE: float

default_tax_ratio staticmethod

default_tax_ratio()

Returns the default tax ratio for this market.

RETURNS DESCRIPTION
float

The default tax ratio.

TYPE: float

get_asset_id_to_name staticmethod

get_asset_id_to_name()

設定對標報酬率的時間序列 Returns: (dict): 股號與股名對照表,ex:{'2330':'台積電'}

get_benchmark staticmethod

get_benchmark()

設定對標報酬率的時間序列

這個函數用於設定對標報酬率的時間序列。

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

get_board_lot_size()

Returns the board lot size of the market.

RETURNS DESCRIPTION
int

The board lot size of the market.

TYPE: int

get_freq staticmethod

get_freq()

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

get_market_value()

取得回測用市值數據

RETURNS DESCRIPTION
DataFrame

市值數據,其中 index 為日期,而 columns 是股票代號。

get_name staticmethod

get_name()

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

get_odd_lot()

Returns the odd lot size of the market.

RETURNS DESCRIPTION
int

The odd lot size of the market.

TYPE: int

get_price

get_price(trade_at_price, adj=True)

取得回測用價格或成交量數據

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: Union[str, Series, DataFrame]

adj

是否使用還原股價計算。僅對價格資料有效,volume 會忽略此參數。

TYPE: bool DEFAULT: True

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

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

get_trading_price(name, adj=True)

取得回測用價格數據

PARAMETER DESCRIPTION
name

選擇回測之還原股價以收盤價或開盤價計算,預設為'close'。可選 'open'、'close'、'high'、'low'、'open_close_avg'、'high_low_avg'、或 'price_avg'。

TYPE: str

Returns: (pd.DataFrame): 價格數據

market_close_at_timestamp

market_close_at_timestamp(timestamp=None)

Returns the timestamp of the market close of the given timestamp.

PARAMETER DESCRIPTION
timestamp

The timestamp to find the market close to.

TYPE: datetime DEFAULT: None

RETURNS DESCRIPTION
datetime

The timestamp of the closest market close.

market_open_time staticmethod

market_open_time()

Returns (hour, minute) of market open in local timezone.

tzinfo staticmethod

tzinfo()

Returns the timezone of the market.

RETURNS DESCRIPTION
Union[timezone, None]

datetime.timezone: The timezone of the market.

Quick Development Tips

The simplest custom market only requires implementing two methods:

  1. get_name(): Return market name
  2. get_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:

@staticmethod
def get_benchmark():
    return pd.Series([])  # Do not display benchmark

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
)

Resources