【TQuant : From 0 to 1 - Day 4】 Core Architecture of Backtesting: What are the key settings for Initialize?

TQuant
Photo by Austin Distel on Unsplash

Introduction

The Zipline engine, integrated within TQuant Lab, offers a high-quality and realistic backtesting framework. It leverages four core functions—initialize、handle_data、analyze、run_algorithm——to construct a comprehensive simulation environment. These components enable trading strategies to dynamically adjust to market conditions, incorporating elements such as slippage and transaction costs to ensure that backtest results closely reflect real-world performance.

This article begins with a brief overview of the four key components and their applications. It then focuses in detail on the initialize function, exploring its specific configurations and role within the backtesting process.

Overview of the Four Core Components

initialize

The initialize function serves as the starting point of any trading strategy within Zipline and plays a critical role in configuring the backtesting environment. Common settings defined here include slippage models, commission fees, the target security, and the benchmark index. This function is executed once at the beginning of the backtest.

The initialize function takes a single parameter, context, which acts as a persistent storage object for custom variables. These variables can be accessed and updated on each trading day during the simulation. For example, one might define context.day = 0 to count the number of trading days elapsed, and context.has_ordered = False to track whether a position in TSMC stock has been initiated.

In addition, the functions set_slippage() and set_commission() can be used within initialize to define slippage behavior and commission cost models, respectively. These configurations will be briefly discussed later in this article.

from zipline.api import set_slippage, set_commission
from zipline.finance import slippage, commission
def initialize(context):
    context.day = 0
    context.has_ordered = False
    set_slippage(slippage.FixedSlippage())
    set_commission(commission.PerShare(cost=0.00285))

handle_data

The handle_data function is responsible for defining the trading logic, executing orders, and recording relevant trading information. It is called once per day throughout the backtesting period after the simulation begins.

This function takes two parameters: context and data. The context parameter serves the same purpose as in initialize—it stores custom variables that persist across trading days and can be updated or referenced during the backtest. The data parameter provides access to daily price and volume information for each asset. For example, data.current() can be used to retrieve the current day’s open, high, low, and close prices, while data.history() allows access to historical price and volume data.

In addition, the record function can be used within handle_data to track specific values across trading days. These recorded values will appear as columns in the final output DataFrame generated by run_algorithm.

Below is an example of how this function can be implemented:

record( ‘column_name’ = value)

As an example, consider a simple strategy in which we place an order to buy 1,000 shares of TSMC (ticker: 2330) and hold the position until the end of the backtest. In this case, we record three key elements during each trading day: the number of trading days elapsed (context.day), whether the position has already been initiated (context.has_ordered), and the current closing price retrieved via data.current(symbol(“2330”), “close”).

Within the handle_data function, Zipline supports six different order functions to place trades. A detailed explanation of the first four methods will be provided in the next section of this series.

from zipline.api import order, record, symbol
def handle_data(context, data):
    context.day += 1
    if not context.has_ordered:
        order(symbol("2330"), 1000)
        context.has_ordered = True    
    record(
        trade_days = context.day,
        has_ordered = context.has_ordered,
        TSMC = data.current(symbol("2330"), "close")
    )

analyze

The analyze function is primarily used for visualizing backtest results, helping users evaluate strategy performance and risk management outcomes. It is called once at the end of the backtest process.

This function takes two parameters: context and perf. The context object serves the same role as described in the initialize section, while perf refers to the output DataFrame generated by run_algorithm. This DataFrame contains a variety of recorded performance metrics and can be used to extract specific columns for plotting and analysis.

As an example, we can use Matplotlib to visualize the portfolio value over time and compare it with the price trend of TSMC (2330). A more detailed discussion of how to use the analyze function will be covered in a later section of this series.

import matplotlib.pyplot as plt
def analyze(context, perf):
    ax1 = plt.subplot(211)
    perf.portfolio_value.plot(ax=ax1,title='portfolio values')
    ax2 = plt.subplot(212, sharex=ax1)
    perf['TSMC'].plot(ax=ax2,title='TSMC close')
    plt.gcf().set_size_inches(18, 8)
    plt.show()

run_algorithm

The run_algorithm function is the core entry point for executing a backtest and serves as the primary trigger for running a trading strategy.

This function accepts multiple parameters and includes several built-in fields that allow users to configure the backtest environment in detail. A comprehensive explanation of these parameters and their applications will be provided in a later section of this series.

Example Code:

from zipline import run_algorithm
import pandas as pd 

start_date = pd.Timestamp('2018-12-30',tz='utc')
end_date = pd.Timestamp('2023-05-26',tz='utc')

results = run_algorithm(start= start_date,  
                       end=end_date,
                       initialize=initialize,                       
                       capital_base=1e6,                       
                       analyze=analyze,
                       handle_data=handle_data,
                       data_frequency='daily',
                       bundle='tquant'
                       )

How to Use the initialize() Function

After introducing the four core components of the backtesting framework, we now turn to a detailed explanation of the initialize() function—its parameters and practical usage.

One of its most common applications is to set slippage and commission models, which help ensure that backtest results better reflect real-world market conditions. Additionally, custom variables and parameters can be defined within initialize() to store and manage trading-related information throughout the simulation.

Setting Slippage

The set_slippage() function is used to define the slippage model. Zipline supports the following four types of slippage models:

Slippage ModelsExplanation
FixedSlippageSimulates transaction costs by applying a constant bid-ask spread. This model does not account for volume constraints.
VolumeShareSlippageCalculates slippage based on the proportion of the trade volume relative to the asset’s total daily volume, incorporating market impact.
FixedBasisPointsSlippageApplies a fixed number of basis points to each trade, with the option to set volume limits to reflect liquidity constraints.
NoSlippageAssumes trades are executed at market prices without any slippage applied.

Setting Commission

The set_commission() function is used to define the commission model. Zipline provides several methods for calculating transaction costs, including:

Commission ModelsExplanation
NoCommissionNo transaction fees are charged. This setting is typically used in simulation environments or promotional programs.
PerDollarCommission is calculated as a fixed rate applied to the total trade value. For example, a fee of 0.0015 is charged for every NT$1 traded. This is Zipline’s default setting.
PerTradeA fixed fee is charged per transaction, regardless of the trade size or value. The default is NT$0.
Custom_TW_CommissionTaiwan Equity Market Commission Model (Tailored for local regulations): This model includes two direct costs: brokerage commission (0.1425%) and securities transaction tax (0.3%) and allows for setting a minimum fee (default: NT$20).

1. transaction fee
• Applies to both buy and sell orders.
• Formula: Trade Price × Number of Shares × 0.1425% × Discount (Discount defaults to 1, meaning no discount applied)
• A minimum commission (default NT$20) is enforced.

2. Securities Transaction Tax
• This only applies to sell transactions.
• Formula: Trade Price × Number of Shares × Tax Rate (Default tax rate: 0.3%)

 Example Code:

from zipline.api import set_commission
from zipline.finance import commission
def initialize(context):
    set_slippage(slippage.<slippage models>)
    set_commission(commission.<commission models>)

The context Parameter

context is a namespace object used to store user-defined variables and parameters. These variables persist throughout the backtest and can be accessed or updated on each trading day.

For example, we may define a variable to track the number of trading days elapsed and another to record whether the strategy has already placed an order for the target stock:

  • context.day = 0
    • Used to count the number of trading days. Initialized at 0 and incremented by one with each new trading day.
  • context.has_ordered = False
    • Used to determine whether the target stock has been purchased. Initialized as False (indicating no position). Once the order is executed, the flag can be updated to True.

Example Code:

from zipline.api import set_slippage, set_commission
from zipline.finance import slippage, commission

def initialize(context):
    context.day = 0
    context.has_ordered = False
    set_slippage(slippage.FixedSlippage())
    set_commission(commission.PerShare(cost=0.00285))

You can also define a list of stock tickers to be traded and convert them into Zipline-recognizable Asset objects, which simplifies subsequent order placement. In addition, you can specify a custom benchmark for performance comparison.

  • context.tickers = [‘1101’, ‘2330’]
    • Defines the list of stock tickers to be traded.
  • context.asset = [symbol(ticker) for ticker in context.tickers]
    • Converts the ticker symbols into Asset objects recognized by Zipline, and stores them in the context for later use in trading logic.
  • set_benchmark(symbol(‘IR0001’))
    • Sets the benchmark to the asset with ticker ‘IR0001’, which represents the Capitalization-Weighted Price Return Index of the Taiwan stock market.

Example Code:

from zipline.api import set_slippage, set_commission
from zipline.finance import slippage, commission

def initialize(context):
    context.tickers = ['1101', '2330']
    context.asset = [symbol(ticker) for ticker in context.tickers]
    set_slippage(slippage.FixedSlippage(spread=0.00))
    set_commission(commission.PerDollar(cost=commission_cost))
    set_benchmark(symbol('IR0001'))

Important Reminder: This analysis is for reference only and does not constitute any product or investment advice.

We welcome readers interested in various trading strategies to consider purchasing relevant solutions from Quantitative Finance Solution. With our high-quality databases, you can construct a trading strategy that suits your needs.

“Taiwan stock market data, TEJ collect it all.”

The characteristics of the Taiwan stock market differ from those of other European and American markets. Especially in the first quarter of 2024, with the Taiwan Stock Exchange reaching a new high of 20,000 points due to the rise in TSMC’s stock price, global institutional investors are paying more attention to the performance of the Taiwan stock market. 

Taiwan Economical Journal (TEJ), a financial database established in Taiwan for over 30 years, serves local financial institutions and academic institutions, and has long-term cooperation with internationally renowned data providers, providing high-quality financial data for five financial markets in Asia. 

  • Complete Coverage: Includes all listed companies on stock markets in Taiwan, China, Hong Kong, Japan, Korea, etc. 
  • Comprehensive Analysis of Enterprises: Operational aspects, financial aspects, securities market performance, ESG sustainability, etc. 
  • High-Quality Database: TEJ data is cleaned, checked, enhanced, and integrated to ensure it meets the information needs of financial and market analysis. 

With TEJ’s assistance, you can access relevant information about major stock markets in Asia, such as securities market, financials data, enterprise operations, board of directors, sustainability data, etc., providing investors with timely and high-quality content. Additionally, TEJ offers advisory services to help solve problems in theoretical practice and financial management!

Summary

Within the initialize() function, we can configure key components such as slippage, commission model, tradable assets, and the benchmark index. Additionally, the context object serves as a central storage for tracking the trading state, ensuring that the backtest executes smoothly and systematically.

【TQuant: From 0 to 1 – Day 5】(Link to be added)

Extended Reading

【TQuant : From 0 to 1 – Day 1】 Your Gateway to Quantitative Trading: Become a Quant Expert with TQuant Lab, No Experience Needed!

【TQuant : From 0 to 1 – Day 2】 Avoid the Invisible Killers of Quantitative Trading: Use TQuant Lab to Precisely Manage Commission and Slippage, Giving Your Strategies the Edge!

[TQuant from 0 to 1 – Day 3] Building a Comprehensive Investment Data Perspective: Stock Pool Screening and Data Retrieval with TejToolAPI

Useful Links

Back
Procesing