Main Content

Create `backtestStrategy`

object to define portfolio allocation
strategy

Create a `backtestStrategy`

object which defines a portfolio
allocation strategy.

Use this workflow to develop and run a backtest:

Define the strategy logic using a

`backtestStrategy`

object to specify how the strategy rebalances a portfolio of assets.Use

`backtestEngine`

to create a`backtestEngine`

object that specifies the parameters of the backtest.Use

`runBacktest`

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

`equityCurve`

to plot the equity curves of each strategy.Use

`summary`

to summarize the backtest results in a table format.

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

creates a `strategy`

= backtestStrategy(`name`

,`rebalanceFcn`

)`backtestStrategy`

object.

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,
`strategy`

= backtestStrategy(___,`Name,Value`

)```
strat =
backtestStrategy('MyStrategy',@myRebalFcn,'TransactionCost',0.005,'LookbackWindow',20)
```

.

`name`

— Strategy namestring

Strategy name, specified as a string.

**Data Types: **`string`

`rebalanceFcn`

— Rebalance functionfunction handle

Rebalance function, specified as a function handle. A function handle computes new portfolio weights during the backtest. The rebalanceFcn argument implements the core logic of the trading strategy and must have one of the following signatures:

`new_weights = rebalanceFcn(weights,assetPrices)`

`new_weights = rebalanceFcn(weights,assetPrices,signalData)`

The rebalance function is called by the `backtestEngine`

object each time the strategy must be
rebalanced. The `backtestEngine`

object calls the rebalance function with
the following arguments:

`weights`

— The current portfolio weights before rebalancing, specified as decimal percentages.`assetPrices`

— A`timetable`

containing a rolling window of adjusted asset prices.`signalData`

— A`timetable`

containing a rolling window of signal data. If you provide signal data is to the`backtestEngine`

object, then the engine object passes it to the strategy rebalance function using the three input argument syntax. If do not provide signal data the`backtestEngine`

object, then the engine object calls the rebalance function with the two input argument syntax.The rebalance function must return a single output argument for

`new_weights`

which is a vector of asset weights specified as decimal percentages.If the

`new_weights`

sum to`1`

, then the portfolio is fully invested.If the

`new_weights`

sum to less than`1`

, then the portfolio will has the remainder in cash, earning the`RiskFreeRate`

specified in the`backtestEngine`

object.If the

`new_weights`

sum to more than`1`

, then there is a negative cash position (margin) and the cash borrowed accrues interest at the cash borrowing rate specified in the`CashBorrowRate`

property of the`backtestEngine`

object.

For more information on developing a

`rebalanceFcn`

function handle, see Backtest Investment Strategies.

**Data Types: **`function_handle`

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`

.

```
strat =
backtestStrategy('MyStrategy',@myRebalFcn,'TransactionCost',0.005,'LookbackWindow',20)
```

`RebalanceFrequency`

— Rebalance frequency during backtest`1`

(default) | integer | `duration`

object | `calendarDuration`

object | vector of `datetime`

objectsRebalance frequency during the backtest, specified as the
comma-separated pair consisting of
`'RebalanceFrequency'`

and a scalar integer,
`duration`

object,
`calendarDuration`

object, or a vector of
`datetime`

objects.

If using an integer, the integer represents the number of time
steps between rebalancing. For example, if you provide the `backtestEngine`

object with daily price data, then the
`RebalanceFrequency`

specifies the number of
days between rebalancing. The default is `1`

,
meaning the strategy rebalances with each time step.

If using a `duration`

object or
`calendarDuration`

, the
backtest engine creates a rebalance schedule of times, starting at
the backtest start time, with rebalance times occurring after each
step of the specified duration.

If using a vector of `datetime`

objects, the
`RebalanceFrequency`

defines an explicit
schedule of rebalance times. The backtest engine will rebalance at
each datetime in the provided schedule.

**Note**

For both the duration and datetime syntaxes, if a rebalance time is not found in the backtest dataset, the engine will rebalance at the nearest time prior to the scheduled time. For example, if the rebalance schedule contains a weekend, the rebalance will occur on the Friday before.

**Data Types: **`double`

| `object`

`TransactionCosts`

— Transaction costs for trades`0`

(not computed) (default) | numeric | vector | function handleTransaction costs for trades, specified as the comma-separated
pair consisting of `'TransactionCosts'`

and a
scalar numeric, vector, or function handle. You can specify
transaction costs in three ways:

`rate`

— A scalar decimal percentage charge to both purchases and sales of assets. For example ,if you set`TransactionCosts`

to`0.001`

, then each transaction (buys and sells) would pay 0.1% in transaction fees.`[buyRate, sellRate]`

— A`1`

-by-`2`

vector of decimal percentage rates that specifies separate rates for buying and selling of assets.`computeTransactionCostsFcn`

— A function handle to compute customized transaction fees. If you specify a function handle, the`backtestEngine`

object calls the`TransactionCosts`

function to compute the fees for each rebalance. The user-defined function handle must have the following signature:[buyCosts,sellCosts] = computeCostsFcn(deltaPositions)

The user-defined function handle takes a single input argument

`deltaPositions`

, which is a vector of changes in asset positions for all assets (in currency units) as a result of a rebalance. Positive elements in the`deltaPositions`

vector indicate purchases while negative entries represent sales. The user-defined function handle must return two output arguments`buyCosts`

and`sellCosts`

, which contain the total costs (in currency) for the entire rebalance for each type of transaction.

**Data Types: **`double`

| `function_handle`

`LookbackWindow`

— Lookback window`[0 Inf]`

(default) | `1`

-by-`2`

vector using
integers | `duration`

object | `calendarDuration`

objectLookback window, specified as the comma-separated pair consisting
of `'LookbackWindow'`

and a
`1`

-by-`2`

vector of integers,
a `duration`

object, or
`calendarDuration`

object.

When using a `1`

-by-`2`

vector
with integers that defines the minimum and maximum size of the
rolling window of data (asset prices and signal data) that you
provide to the `rebalanceFcn`

argument. You
specify these limits in terms of the number of time steps. When
specified as integers, the lookback window is defined in terms of
rows of data from the asset (`pricesTT`

) and signal
(`signalTT`

) timetables used in the backtest.
The lookback minimum sets the minimum number of rows of asset price
data that must be available to the rebalance function before a
strategy rebalance can occur. The lookback maximum sets the maximum
size for the rolling window of price data that is passed to the
rebalance function.

For example, if the `backtestEngine`

object is provided with daily price
data, then `LookbackWindow`

specifies the size
bounds of the rolling window in days. The default is ```
[0
Inf]
```

, meaning that all available past data is given to
the rebalance function. If you specify a non-zero minimum, then the
software does not call `rebalanceFcn`

until
enough time steps process to meet the minimum size.

If you specify `LookbackWindow`

as a single
scalar value, then the value is both the minimum and maximum of the
`LookbackWindow`

(that is, a fixed-sized
window).

If using a `duration`

object or
`calendarDuration`

, the
lookback window minimum and maximum are defined in terms of
timespans relative to the time at a rebalance. For example if the
lookback minimum was set to five days (that is,
`days(5)`

), the rebalance will only occur if
the backtest start time is at least five days prior to the rebalance
time. Similarly, if the lookback maximum was set to six months (that
is, `calmonths(6)`

), the lookback window would
contain only data that occurred at six months prior to the rebalance
time or later.

**Note**

Alternatively, the `LookbackWindow`

can be
set to a single scalar value indicating that the rolling window
should be exactly that size (either in terms of rows or a time
duration). The minimum and maximum size will both be set to the
provided value.

**Data Types: **`double`

| `object`

`InitialWeights`

— Initial portfolio weights`[ ]`

(default) | vectorInitial portfolio weights, specified as the comma-separated pair
consisting of `'InitialWeights'`

and a vector. The
`InitialWeights`

vector sets the portfolio
weights before the `backtestEngine`

object begins the backtest. The size of
the initial weights vector must match the number of assets used in
the backtest.

Alternatively, you can set the `InitialWeights`

name-value pair argument to empty (`[ ]`

) to
indicate the strategy will begin with no investments and in a 100%
cash position. The default for `InitialWeights`

is empty (`[ ]`

).

**Data Types: **`double`

`Name`

— Strategy namestring

Strategy name, specified as a string.

**Data Types: **`string`

`RebalanceFcn`

— Rebalance functionfunction handle

Rebalance function, specified as a function handle.

**Data Types: **`function_handle`

`RebalanceFrequency`

— Rebalance frequency during backtest`1`

(default) | numericRebalance frequency during the backtest, specified as a scalar numeric.

**Data Types: **`double`

`TransactionCosts`

— Transaction costs`0`

(default) | numeric | vector | function handleTransaction costs, specified as a scalar numeric, vector, or function handle.

**Data Types: **`double`

| `function_handle`

`LookbackWindow`

— Lookback window`[0 Inf]`

(default) | numeric | vectorLookback window, specified as a scalar numeric or vector.

**Data Types: **`double`

`InitialWeights`

— Initial weights`[ ]`

(default) | vectorInitial weights, specified as a vector.

**Data Types: **`double`

Define a backtest strategy by using a `backtestStrategy`

object. `backtestStrategy`

objects contain properties specific to a trading strategy, such as the rebalance frequency, transaction costs, and a rebalance function. The rebalance function implements the core logic of the strategy and is used by the backtesting engine during the backtest to allow the strategy to change its asset allocation and to make trades. In this example, to illustrate how to create and use backtest strategies in MATLAB®, you prepare two simple strategies for backtesting:

An equal weighted strategy

A strategy that attempts to "chase returns"

The strategy logic for these two strategies is defined in the rebalance functions.

**Set Strategy Properties**

A `backtestStrategy`

object has several properties that you set using parameters for the `backtestStrategy`

function.

**Initial Weights**

The `InitialWeights`

property contains the asset allocation weights at the start of the backtest. The default value for `InitialWeights`

** **is empty (`[]`

), which indicates that the strategy begins the backtest uninvested, meaning that 100% of the capital is in cash earning the risk-free rate.

Set the `InitialWeights`

** **to a specific asset allocation. The size of the initial weights vector must match the number of assets in the backtest.

% Initialize the strategies with 30 weights, since the backtest % data comes from a year of the 30 DJIA stocks. numAssets = 30; % Give the initial weights for both strategies equal weighting. Weights % must sum to 1 to be fully invested. initialWeights = ones(1,numAssets); initialWeights = initialWeights / sum(initialWeights);

**Transaction Costs**

The `TransactionCosts`

property allows you to set the fees that the strategy pays for trading assets. Transaction costs are paid as a percentage of the total change in position for each asset. Specify costs in decimal percentages. For example, if `TransactionCosts`

is set to 1% (`0.01`

) and the strategy buys $100 worth of a stock, then the transaction costs incurred are $1.

Transaction costs are set using a `1`

-by-`2`

vector that sets separate fee rates for purchases and sales of assets. In this example, both strategies pay the same transaction costs — 25 basis points for asset purchases and 50 basis points for sales.

% Define the Transaction costs as [buyCosts sellCost] and specify the costs % as decimal percentages. tradingCosts = [0.0025 0.005];

You can also set the TransactionCosts property to a function handle if you need to implement arbitrarily complex transaction cost structures. For more information on creating transaction cost functions, see `backtestStrategy`

.

**Rebalance Frequency**

The `RebalanceFrequency`

property determines how often the backtesting engine rebalances and reallocates the portfolio of a strategy using the rebalance function. Set the `RebalanceFrequency`

in terms of time steps in the backtest. For example, if the backtesting engine is testing a strategy with a set of daily price data, then set the rebalance function in days. Essentially, `RebalanceFrequency`

represents the number of rows of price data to process between each call to the strategy rebalance function.

```
% Both strategies rebalance every 4 weeks (20 days).
rebalFreq = 20;
```

**Lookback Window**

Each time the backtesting engine calls a strategy rebalance function, a window of asset price data (and possibly signal data) is passed to the rebalance function. The rebalance function can then make trading and allocation decisions based on a rolling window of market data. The `LookbackWindow`

property sets the size of these rolling windows. Set the window in terms of time steps. The window determines the number of rows of data from the asset price timetable that are passed to the rebalance function.

The `LookbackWindow`

property can be set in two ways. For a fixed-sized rolling window of data (for example, "50 days of price history"), the `LookbackWindow `

property is set to a single scalar value (`N`

= `50`

). The software then calls the rebalance function with a price timetable containing exactly `N`

rows of rolling price data.

Alternatively, you can define the LookbackWindow property by using a `1`

-by-`2`

vector `[min max]`

that specifies the minimum and maximum size for an expanding window of data. In this way, you can set flexible window sizes. For example:

`[10 Inf]`

— At least 10 rows of data`[0 50]`

— No more than 50 rows of data`[0 Inf]`

— All available data (that is, no minimum, no maximum); this is the default value`[20 20]`

— Exactly 20 rows of data; this is equivalent to setting`LookbackWindow`

to the scalar value`20`

The software does not call the rebalance function if the data is insufficient to create a valid rolling window, regardless of the value of the `RebalanceFrequency`

property.

If the strategy does not require any price or signal data history, then you can indicate that the rebalance function requires no data by setting the `LookbackWindow`

property to `0`

.

% The equal weight strategy does not require any price history data. ewLookback = 0; % The "chase returns" strategy bases its decisions on the trailing % 10-day asset returns. The lookback window is set to 11 since computing 10 days % of returns requires the close price from day 0. chaseLookback = 11;

**Rebalance Function**

The rebalance function (`rebalanceFcn`

) is the user-authored function that contains the logic of the strategy. The backtesting engine calls the strategy rebalance function with a fixed set of parameters and expects it to return a vector of asset weights representing the new, desired portfolio allocation after a rebalance. For more information, see the rebalance functions.

**Create Strategies**

Using the prepared strategy properties, you can create the two strategy objects.

% Create the equal weighted strategy. The rebalance function @equalWeights % is defined in the Rebalance Functions section at the end of this example. equalWeightStrategy = backtestStrategy("EqualWeight",@equalWeight,... 'RebalanceFrequency',rebalFreq,... 'TransactionCosts',tradingCosts,... 'LookbackWindow',ewLookback,... 'InitialWeights',initialWeights)

equalWeightStrategy = backtestStrategy with properties: Name: "EqualWeight" RebalanceFcn: @equalWeight RebalanceFrequency: 20 TransactionCosts: [0.0025 0.0050] LookbackWindow: 0 InitialWeights: [0.0333 0.0333 0.0333 0.0333 0.0333 0.0333 ... ]

% Create the "chase returns" strategy. The rebalance function % @chaseReturns is defined in the Rebalance Functions section at the end of this example. chaseReturnsStrategy = backtestStrategy("ChaseReturns",@chaseReturns,... 'RebalanceFrequency',rebalFreq,... 'TransactionCosts',tradingCosts,... 'LookbackWindow',chaseLookback,... 'InitialWeights',initialWeights)

chaseReturnsStrategy = backtestStrategy with properties: Name: "ChaseReturns" RebalanceFcn: @chaseReturns RebalanceFrequency: 20 TransactionCosts: [0.0025 0.0050] LookbackWindow: 11 InitialWeights: [0.0333 0.0333 0.0333 0.0333 0.0333 0.0333 ... ]

**Set Up Backtesting Engine**

To backtest the two strategies, use the `backtestEngine`

object. The backtesting engine sets parameters of the backtest that apply to all strategies, such as the risk-free rate and initial portfolio value. For more information, see `backtestEngine`

.

% Create an array of strategies for the backtestEngine. strategies = [equalWeightStrategy chaseReturnsStrategy]; % Create backtesting engine to test both strategies. backtester = backtestEngine(strategies);

**Rebalance Functions**

Strategy rebalance functions defined using the `rebalanceFcn`

argument for `backtestStrategy`

must adhere to a fixed API that the backtest engine expects when interacting with each strategy. Rebalance functions must implement one of the following two syntaxes:

function new_weights = exampleRebalanceFcn(current_weights,assetPriceTimeTable) function new_weights = exampleRebalanceFcn(current_weights,assetPriceTimeTable,signalDataTimeTable)

All rebalance functions take as their first input argument the current allocation weights of the portfolio. `current_weights`

represents the asset allocation just before the rebalance occurs. During a rebalance, you can use `current_weights`

in a variety of ways. For example, you can use `current_weights`

to determine how far the portfolio allocation has drifted from the target allocation or to size trades during the rebalance to limit turnover.

The second and third arguments of the rebalance function syntax are the rolling windows of asset prices and optional signal data. The two tables contain the trailing `N`

rows of the asset and signal timetables that are passed to the `runBacktest`

function, where `N`

is set using the `LookbackWindow`

property of each strategy.

If optional signal data is provided to the `runBacktest`

function, then the backtest engine passes the rolling window of signal data to each strategy that supports it.

The `equalWeight`

strategy simply invests equally across all assets.

function new_weights = equalWeight(current_weights,assetPrices) %#ok<INUSD> % Invest equally across all assets. num_assets = numel(current_weights); new_weights = ones(1,num_assets) / num_assets; end

The `chaseReturns`

strategy invests only in the top *X* stocks based on their rolling returns in the lookback window. This naive strategy is used simply as an illustrative example.

function new_weights = chaseReturns(current_weights,assetPrices) % Set number of stocks to invest in. numStocks = 15; % Compute rolling returns from lookback window. rollingReturns = assetPrices{end,:} ./ assetPrices{1,:}; % Select the X best performing stocks over the lookback window [~,idx] = sort(rollingReturns,'descend'); bestStocksIndex = idx(1:numStocks); % Initialize new weights to all zeros. new_weights = zeros(size(current_weights)); % Invest equally across the top performing stocks. new_weights(bestStocksIndex) = 1; new_weights = new_weights / sum(new_weights); end

`runBacktest`

| `summary`

| `backtestEngine`

| `equityCurve`

| `timetable`

You have a modified version of this example. Do you want to open this example with your edits?

You clicked a link that corresponds to this MATLAB command:

Run the command by entering it in the MATLAB Command Window. Web browsers do not support MATLAB commands.

Choose a web site to get translated content where available and see local events and offers. Based on your location, we recommend that you select: .

Select web siteYou can also select a web site from the following list:

Select the China site (in Chinese or English) for best site performance. Other MathWorks country sites are not optimized for visits from your location.

- América Latina (Español)
- Canada (English)
- United States (English)

- Belgium (English)
- Denmark (English)
- Deutschland (Deutsch)
- España (Español)
- Finland (English)
- France (Français)
- Ireland (English)
- Italia (Italiano)
- Luxembourg (English)

- Netherlands (English)
- Norway (English)
- Österreich (Deutsch)
- Portugal (English)
- Sweden (English)
- Switzerland
- United Kingdom (English)