backtestEngine

Create backtestEngine object to backtest strategies and analyze results

Description

Create a backtestEngine to run a backtest of portfolio investment strategies on historical data.

Use this workflow to develop and run a backtest:

  1. Define the strategy logic using a backtestStrategy object to specify how a strategy rebalances a portfolio of assets.

  2. Use backtestEngine to create a backtestEngine object that specifies parameters of the backtest.

  3. Use runBacktest to run the backtest against historical asset price data and, optionally, trading signal data.

  4. Use summary to summarize the backtest results in a table format.

For more detailed information on this workflow, see Backtest Investment Strategies.

Creation

Description

example

backtester = backtestEngine(strategies) creates a backtestEngine object. Use the backtestEngine object to backtest the portfolio trading strategies defined in the backtestStrategy objects.

example

backtester = backtestEngine(___,Name,Value) sets properties using name-value pair arguments and any of the arguments in the previous syntax. You can specify multiple name-value pair arguments. For example, backtester = backtestEngine(strategies,'RiskFreeRate',0.02/252,'InitialPortfolioValue',1000).

Input Arguments

expand all

Backtest strategies, specified as a vector of backtestStrategy objects. Each backtestStrategy object defines a portfolio trading strategy.

Data Types: object

Name-Value Pair Arguments

Specify optional comma-separated pairs of Name,Value arguments. Name is the argument name and Value is the corresponding value. Name must appear inside quotes. You can specify several name and value pair arguments in any order as Name1,Value1,...,NameN,ValueN.

Example: backtester = backtestEngine(strategies,'RiskFreeRate',0.02/252,'InitialPortfolioValue',1000)

Risk free rate, specified as the comma-separated pair consisting of 'RiskFreeRate' and a scalar numeric. The RiskFreeRate is a decimal percentage and represents the risk free rate for one time step in the backtest. For example, if the backtest uses daily asset price data, then the RiskFreeRate value must be the daily rate of return for cash.

Data Types: double

Cash borrowing rate, specified as the comma-separated pair consisting of 'CashBorrowRate' and a scalar numeric. The CashBorrowRate specifies the rate of interest accrual on negative cash balances (margin) during the backtest. The CashBorrowRate value is a decimal percentage and represents the interest accrual rate for one time step in the backtest. For example, if the backtest is using daily asset price data, then the CashBorrowRate value must be the daily interest rate for negative cash balances.

Data Types: double

Initial portfolio value, specified as the comma-separated pair consisting of 'InitialPortfolioValue' and a scalar numeric.

Data Types: double

Properties

expand all

Backtest strategies, specified as a vector of backtestStrategy objects.

Data Types: object

Risk free rate, specified as a scalar numeric.

Data Types: double

Cash borrowing rate, specified as a scalar numeric.

Data Types: double

Initial portfolio value, specified as a scalar numeric.

Data Types: double

This property is read-only.

Number of assets in the portfolio universe, a numeric. NumAssets is derived from the timetable of adjusted prices passed to runBacktest. NumAssets is empty until you run the backtest using the runBacktest function.

Data Types: double

This property is read-only.

Strategy returns, a NumTimeSteps-by-NumStrategies timetable of strategy returns. Returns are per time step. For example, if you use daily prices with runBacktest, then Returns is the daily strategy returns. Returns is empty until you run the backtest using the runBacktest function.

Data Types: timetable

This property is read-only.

Asset positions for each strategy, a structure containing a NumTimeSteps-by-NumAssets timetable of asset positions for each strategy. For example, if you use daily prices in the runBacktest, then the Positions structure holds timetables containing the daily asset positions. Positions is empty until you run the backtest using the runBacktest function.

Data Types: struct

This property is read-only.

Strategy turnover, a NumTimeSteps-by-NumStrategies timetable. Turnover is empty until you run the backtest using the runBacktest function.

Data Types: timetable

This property is read-only.

Transaction costs for the asset purchases of each strategy, a NumTimeSteps-by-NumStrategies timetable. BuyCost is empty until you run the backtest using the runBacktest function.

Data Types: timetable

This property is read-only.

Transaction costs for the asset sales of each strategy, a NumTimeSteps-by-NumStrategies timetable. SellCost is empty until you run the backtest using the runBacktest function.

Data Types: timetable

Object Functions

runBacktestRun backtest on one or more strategies
summaryGenerate summary table of backtest results

Examples

collapse all

Use a backtesting engine in MATLAB® to run a backtest on an investment strategy over a time series of market data. You can define a backtesting engine by using backtestEngine object. A backtestEngine object sets properties of the backtesting environment, such as the risk-free rate, and holds the results of the backtest. In this example, you can create a backtesting engine to run a simple backtest and examine the results.

Create Strategy

Define an investment strategy by using the backtestStrategy function. This example builds a simple equal-weighted investment strategy that invests equally across all assets. For more information on creating backtest strategies, see backtestStrategy.

% The rebalance function is simple enough that you can use an anonymous function
equalWeightRebalanceFcn = @(current_weights,~) ones(size(current_weights)) / numel(current_weights);

% Create the strategy
strategy = backtestStrategy("EqualWeighted",equalWeightRebalanceFcn,...
    'RebalanceFrequency',20,...
    'TransactionCosts',[0.0025 0.005],...
    'LookbackWindow',0)
strategy = 
  backtestStrategy with properties:

                  Name: "EqualWeighted"
          RebalanceFcn: [function_handle]
    RebalanceFrequency: 20
      TransactionCosts: [0.0025 0.0050]
        LookbackWindow: 0
        InitialWeights: [1x0 double]

Set Backtesting Engine Properties

The backtesting engine has several properties that you set by using parameters to the backtestEngine function.

Risk-Free Rate

The RiskFreeRate property holds the interest rate earned for uninvested capital (that is, cash). When the sum of portfolio weights is below 1, the remaining capital is invested in cash and earns the risk-free rate. The risk-free rate is defined as the "per-time-step" rate, meaning that it is the interest rate that cash earns for each step of the backtest, not the annualized rate. For this example, you run the backtest using daily data and the risk-free interest rate is 2% annualized, so set the RiskFreeRate property to 0.02 / 252 to approximate the daily interest rate.

% Approximate a 2% annualized risk-free rate for daily backtest data
riskFreeRate = 0.02 / 252;

Cash Borrow Rate

The CashBorrowRate property sets the interest accrual rate applied to negative cash balances. If at any time the portfolio weights sum to a value greater than 1, then the cash position is negative by the amount in excess of 1. This behavior of portfolio weights is analogous to borrowing capital on margin to invest with leverage. Like the RiskFreeRate property, the CashBorrowRate property is defined as the per-time-step rate, you must provide the annualized rate divided by the number of time steps per year.

% Approximate a 6% annualized margin interest rate for daily backtest data
cashBorrowRate = 0.06 / 252;

Initial Portfolio Value

The InitialPortfolioValue property sets the value of the portfolio at the start of the backtest for all strategies. The default is $10,000.

% Start backtest with $1M
initPortfolioValue = 1000000;

Create Backtest Engine

Using the prepared properties, create the backtesting engine using the backtestEngine function.

% The backtesting engine takes an array of backtestStrategy objects as the first argument
backtester = backtestEngine(strategy,...
    'RiskFreeRate',riskFreeRate,...
    'CashBorrowRate',cashBorrowRate,...
    'InitialPortfolioValue',initPortfolioValue)
backtester = 
  backtestEngine with properties:

               Strategies: [1x1 backtestStrategy]
             RiskFreeRate: 7.9365e-05
           CashBorrowRate: 2.3810e-04
    InitialPortfolioValue: 1000000
                NumAssets: []
                  Returns: []
                Positions: []
                 Turnover: []
                  BuyCost: []
                 SellCost: []

Several additional properties of the backtesting engine are initialized to empty. The backtesting engine populates these properties, which contain the results of the backtest, upon completion of the backtest.

Load Data and Run Backtest

Run the backtest over daily price data from the 30 component stocks of the DJIA.

% Read table of daily adjusted close prices for 2006 DJIA stocks
T = readtable('dowPortfolio.xlsx');

% Remove the DJI index column and convert to timetable
pricesTT = table2timetable(T(:,[1 3:end]),'RowTimes','Dates');

Run the backtest using the runBacktest function.

backtester = runBacktest(backtester,pricesTT)
backtester = 
  backtestEngine with properties:

               Strategies: [1x1 backtestStrategy]
             RiskFreeRate: 7.9365e-05
           CashBorrowRate: 2.3810e-04
    InitialPortfolioValue: 1000000
                NumAssets: 30
                  Returns: [250x1 timetable]
                Positions: [1x1 struct]
                 Turnover: [250x1 timetable]
                  BuyCost: [250x1 timetable]
                 SellCost: [250x1 timetable]

Examine Results

The backtesting engine populates the read-only properties of the backtestEngine object with the backtest results. Daily values for portfolio returns, asset positions, turnover, and transaction costs are available to examine.

% Generate a histogram of daily portfolio returns
histogram(backtester.Returns{:,1})
title('Daily Portfolio Returns')

Introduced in R2020b