backtestStrategy
Create backtestStrategy object to define portfolio allocation
            strategy
Description
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 - backtestStrategyobject to specify how the strategy rebalances a portfolio of assets.
- Use - backtestEngineto create a- backtestEngineobject that specifies the parameters of the backtest.
- Use - runBacktestto run the backtest against historical asset price data and, optionally, trading signal data.
- Use - equityCurveto plot the equity curves of each strategy.
- Use - summaryto summarize the backtest results in a table format.
For more detailed information on this workflow, see Backtest Workflow and Backtest Investment Strategies Using Financial Toolbox.
Creation
Description
strategy = backtestStrategy(name,rebalanceFcn)backtestStrategy object.
strategy = backtestStrategy(___,Name,Value)strat =
                            backtestStrategy('MyStrategy',@myRebalFcn,'TransactionCost',0.005,'LookbackWindow',20). 
Input Arguments
Strategy name, specified as a string.
Note
The strategy name must be a unique and valid MATLAB® variable name. Any space character in the
                                        name is converted to an underscore
                                    character.
Data Types: string
Rebalance function, specified as a function handle which computes new
                            portfolio weights during the backtest. The
                                rebalanceFcn argument implements the core logic
                            of the trading strategy. 
The signature of the rebalanceFcn depends on
                            whether UserData and
                                EngineDataList name-value arguments are
                            specified for the backtestStrategy function. For an
                            example of rebalanceFcn where
                                UserData is specified, see Backtest with Brinson Attribution to Evaluate Portfolio Performance.
- If - UserDataand- EngineDataListare Empty- If the - UserDataand- EngineDataListname-value arguments are empty, then the- rebalanceFcnmust have one of the following signatures:- new_weights = rebalanceFcn(weights,assetPrices)
- new_weights = rebalanceFcn(weights,assetPrices,signalData)
 - If you do not specify - UserData, the rebalance function returns a single output argument,- new_weights, which is a vector of asset weights specified as decimal percentages.- If the - new_weightssum to- 1, then the portfolio is fully invested.
- If the - new_weightssum to less than- 1, then the portfolio has the remainder in cash, earning the- RiskFreeRatespecified in the- backtestEngineobject.
- If the - new_weightssum 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- CashBorrowRateproperty of the- backtestEngineobject.
 
- If - UserDatais Empty and- EngineDataListis Specified- If the - UserDataname-value argument is empty and the- EngineDataListname-value argument is specified, then the- rebalanceFcnmust have one of the following signatures:- new_weights = rebalanceFcn(engineData,assetPrices)
- new_weights = rebalanceFcn(engineData,assetPrices,signalData)
 
- If - UserDatais Specified and- EngineDataListis Empty- If you use the name-value argument for - UserDatato specify a strategy-specific- userDatastruct but do not specify the name-value argument for- EngineDataList, the required syntax for- rebalanceFcnmust have one of the following signatures:- [new_weights,userData] = rebalanceFcn(weights,assetPrices,userData)
- [new_weights,userData] = rebalanceFcn(weights,assetPrices,signalData,userData)
 - If - UserDatais specified, then in addition to an output for- new_weights, there is also an output for- userDatareturned as a struct.
- If Both - UserDataand- EngineDataListare Specified- If you use the name-value argument for - UserDatato specify a strategy-specific- userDatastruct and you use the name-value argument- EngineDataListto specify a backtest engine state, the required syntax for- rebalanceFcnmust have one of the following signatures:- [new_weights,userData] = rebalanceFcn(engineData,assetPrices,userData)
- [new_weights,userData] = rebalanceFcn(engineData,assetPrices,signalData,userData)
 - If - UserDatais specified, then in addition to an output for- new_weights, there is also an output for- userDatareturned as a struct.
The rebalanceFcn function is called by the backtestEngine object each time the strategy must be
                            rebalanced as specified in the RebalanceFrequency
                            name-value argument. The backtestEngine object calls the
                                rebalanceFcn function with the following arguments:
- weights— The current portfolio weights before rebalancing, specified as decimal percentages.
- assetPrices— A- timetablecontaining a rolling window of adjusted asset prices.
- signalData— (Optional) A- timetablecontaining a rolling window of signal data.- If you provide signal data to the - backtestEngineobject, then the engine object passes it to the strategy rebalance function using the three input argument syntax. If do not provide signal data the- backtestEngineobject, then the engine object calls the rebalance function with the two input argument syntax.
- userData— (Optional) A struct to contain strategy-specific user data.- If the - UserDataproperty is set, the- userDatastruct is passed into- rebalanceFcn.
- engineData— (Optional) A struct containing backtest state data.- If the - EngineDataListproperty is set, the- engineDatastruct is passed into- rebalanceFcn.
For more information on developing a rebalanceFcn
                            function handle, see Backtest Investment Strategies Using Financial Toolbox.
Data Types: function_handle
Name-Value Arguments
Specify optional pairs of arguments as
      Name1=Value1,...,NameN=ValueN, where Name is
      the argument name and Value is the corresponding value.
      Name-value arguments must appear after other arguments, but the order of the
      pairs does not matter.
    
      Before R2021a, use commas to separate each name and value, and enclose 
      Name in quotes.
    
Example: strat =
                        backtestStrategy('MyStrategy',@myRebalFcn,'TransactionCost',0.005,'LookbackWindow',20)
Rebalance frequency during the backtest, specified as the
                                comma-separated pair consisting of
                                    'RebalanceFrequency' and a scalar integer,
                                    duration or calendarDuration
                                object, or a vector of datetime objects.
The RebalanceFrequency specifies the schedule
                                of dates where the strategy will rebalance its portfolio. The
                                default is 1, meaning the strategy rebalances
                                with each time step.
For more information, see Defining Schedules for Backtest Strategies.
Data Types: double | object | datetime
Transaction 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- TransactionCoststo- 0.001, then each transaction (buys and sells) would pay 0.1% in transaction fees.
- [buyRate, sellRate]— A- 1-by-- 2vector 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- backtestEngineobject calls the- TransactionCostsfunction to compute the fees for each rebalance. The user-defined- computeTransactionCostsFcnfunction handle has different signatures depending on whether the optional name-value arguments- UserDataand- EngineDataListare specified and whether the- sellCosts,- buyCosts, or- detailedCostsoutputs are used.- If - UserDatais Empty and- EngineDataListis Empty Using- buyCostsand- sellCostsOutputs- When the optional name-value arguments - UserDataand- EngineDataListare not specified and the sellCosts and buyCosts outputs are used, the- computeTransactionCostsFcnfunction handle has the following signature:- [buyCosts,sellCosts] = computeTransactionCostsFcn(deltaPositions) - The user-defined - computeTransactionCostsFcnfunction 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- deltaPositionsvector indicate purchases while negative entries represent sales. The user-defined function handle must return two output arguments- buyCostsand- sellCosts, which contain the total costs (in currency) for the entire rebalance for each type of transaction.
- If - UserDatais Empty and- EngineDataListis Empty and- detailedCostsOutput is specified- When the optional name-value arguments - UserDataand- EngineDataListare not specified and- detailedCostsoutput is specified, the- computeTransactionCostsFcnfunction handle has the following signature:- detailedCosts = computeTransactionCostsFcn(deltaPositions) - The user-defined - computeTransactionCostsFcnfunction 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- deltaPositionsvector indicate purchases while negative entries represent sales. The user-defined function handle returns one output argument for- detailedCostsfor per-asset transaction costs.
- If - UserDatais Empty and- EngineDataListis Specified Using- buyCostsand- sellCostsOutputs- When the optional name-value argument - UserDatais not specified, but the optional name-value argument- EngineDataListis specified and the- buyCostsand- sellCostsoutput are used, the- computeTransactionCostsFcnfunction handle has the following signatures:- [buyCosts,sellCosts] = computeTransactionCostsFcn(deltaPositions,engineData) 
- If - UserDatais Empty and- EngineDataListis Specified and- detailedCostsOutput is Specified- When the optional name-value argument - UserDatais not specified, but the optional name-value argument- EngineDataListis specified and- detailedCostsoutput is specified, the- computeTransactionCostsFcnfunction handle has the following signatures:- detailedCosts = computeTransactionCostsFcn(deltaPositions,engineData) 
- If - UserDatais Specified and- EngineDataListis Empty Using- buyCostsand- sellCostsOutputs- When the optional name-value argument - UserDatais specified, but the optional name-value argument- EngineDataListis not specified and- buyCostsand- sellCostsoutputs are used, the- computeTransactionCostsFcnfunction handle has the following signatures:- [buyCosts,sellCosts,userData] = computeTransactionCostsFcn(deltaPositions,userData) 
- If - UserDatais Specified and- EngineDataListis Empty and- detailedCostsOutput is specified- When the optional name-value argument - UserDatais specified, but the optional name-value argument- EngineDataListis not specified and- detailedCostsoutput is specified, the- computeTransactionCostsFcnfunction handle has the following signatures:- [detailedCosts,userData] = computeTransactionCostsFcn(deltaPositions,userData) 
- If Both - UserDataand- EngineDataListare Specified Using- buyCostsand- sellCostsOutputs- If you use the optional name-value argument for - UserDatato specify a strategy-specific- userDatastruct and the optional name-value argument- EngineDataListto specify required backtest engine state data and- buyCostsand- sellCostsarguments are used, the required syntax for the- computeTransactionCostsFcnfunction handle must have the following signature:If you specify the optional name-value argument- [buyCosts,sellCosts,userData] = computeTransactionCostsFcn(deltaPositions,engineData,userData) - UserData, then the strategy- userDatastruct is passed to the- computeTransactionCostsFcncost function as the final input argument. The- computeTransactionCostsFcnfunction handle can read or write any information to the- userDatastruct. The updated- userDatastruct is returned as the third output argument.
- If Both - UserDataand- EngineDataListare Specified and- detailedCostsOutput is Specified- If you use the optional name-value argument for - UserDatato specify a strategy-specific- userDatastruct and the optional name-value argument- EngineDataListto specify required backtest engine state data and the- detailedCostsoutput is specified, the required syntax for the- computeTransactionCostsFcnfunction handle must have the following signature:If you specify the optional name-value argument- [detailedCosts,userData] = computeTransactionCostsFcn(deltaPositions,engineData,userData) - UserData, then the strategy- userDatastruct is passed to the- computeTransactionCostsFcncost function as the final input argument. The- computeTransactionCostsFcnfunction handle can read or write any information to the- userDatastruct. The updated- userDatastruct is returned as the second output argument.
 
Data Types: double | function_handle
Lookback window, specified as the comma-separated pair consisting
                                of 'LookbackWindow' and a scalar or
                                    1-by-2 vector of integers,
                                a duration or calendarDuration
                                object.
A scalar LookbackWindow sets the minimum and
                                maximum lookback window to the same value. In other words, a scalar
                                    LookbackWindow has a fixed window
                                size.
A 1-by-2 vector defines the
                                minimum and maximum size of the rolling window of data (asset prices
                                and signal data) that is passed to the rebalance function
                                    (rebalanceFcn). For an example, see Set the Size of Rolling Windows for Backtest Strategies.
- When the inputs are integers, the lookback window refers to the number of rows of data from the asset ( - pricesTT) and signal (- signalTT) timetables passed to the rebalancing function. The lookback minimum sets the minimum number of rows that must be available, prior the row associated with the current rebalancing period, before a strategy rebalance can occur. The lookback maximum sets the maximum number of rows that is passed to the rebalance function.- For example: - [10 Inf]–— At least 10 rows of data must be available before the current rebalancing period.
- [0 50]–— At most 50 rows of data prior to the current rebalancing period are passed to the rebalancing function.
- [0 Inf]–— Use all available data up to the current rebalancing period; there is no minimum and no maximum. This is the default value.
- [20 20]–— Exactly 20 rows of data prior to the current rebalancing period are passed to the rebalancing function; this is equivalent to setting- LookbackWindowto the scalar value- 20.
 
- When the inputs are either - durationor- calendarDurationobjects, the lookback window minimum and maximum are defined in terms of timespans relative to the time of the current rebalance period. For example, if the lookback minimum is set to five days (that is,- days(5)), the rebalance only occurs if the backtest start time is at least five days prior to the rebalance time. Similarly, if the lookback maximum is set to six months (that is,- calmonths(6)), the lookback window only contains data from the six months prior to the rebalance period or later. For an example, see Backtest Investment Strategies Using datetime and calendarDuration.
Note
If the lookback minimum is non-zero, the software does not
                                    call the rebalance function (rebalanceFcn)
                                    until at least the lookback minimum time steps have passed
                                    before the current rebalancing period. This is regardless of the
                                    value of the rebalance frequency
                                        (RebalanceFrequency).
Data Types: double | object
Initial 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
Since R2022b
Management fee charged as an annualized percent of the total
                                portfolio value, specified as the comma-separated pair consisting of
                                    'ManagementFee' and a numeric scalar. The
                                management fee is the annualized fee rate paid to cover the costs of
                                managing a strategy's portfolio. The management fee is charged based
                                on the strategy portfolio balance at the start of each date
                                specified by the ManagementFeeSchedule. The
                                default is 0, meaning no management fee is paid. 
For more information, see Management Fees.
Data Types: double
Since R2022b
Management fee schedule, specified as the comma-separated pair
                                consisting of 'ManagementFeeSchedule' and a
                                numeric scalar, a duration or calendarDuration
                                object, or alternatively, as a vector of datetimes. 
The ManagementFeeSchedule specifies the
                                schedule of dates where the management fee is charged (if a
                                    ManagementFee is defined). The default is
                                    calyears(1), meaning the management fee is
                                charged annually.
For more information, see Defining Schedules for Backtest Strategies.
Data Types: double | datetime | object
Since R2022b
Performance fee charged as a percent of the total portfolio
                                growth, specified as the comma-separated pair consisting of
                                    'PerformanceFee' and a numeric scalar. The
                                performance fee, sometimes called an incentive fee, is a fee paid to
                                reward a fund manager based on performance of the fund. The fee is
                                paid periodically based on the schedule specified by the
                                    PerformanceFeeSchedule. The default is
                                    0, meaning no performance fee is paid. 
For more information, see Performance Fees.
Data Types: double
Since R2022b
Annualized hurdle rate or column in assetPrices
                                or signalData to act as hurdle asset, specified
                                as the comma-separated pair consisting of
                                    'PerformanceHurdle' and a numeric scalar or a
                                string. The PerformanceHurdle sets a minimum
                                level of performance that a strategy must achieve before the
                                performance fee is paid.
- If specified as a numeric scalar, the hurdle represents an annualized growth rate, for example 0.05 indicates a 5% growth rate. In this case, the performance fee is only paid if, on a performance fee date, the strategy portfolio value is greater than the high-water mark and the portfolio has produced a return greater than 5% annualized since the start of the backtest. The fee is paid only on the growth in excess of both the hurdle rate and the previous high-water mark. 
- If specified as a string, the hurdle must match a column in either the - assetPricesor- signalDatatimetables of the backtest and it indicates a hurdle asset. In this case, the performance fee is paid only if the portfolio value is greater than the high-water mark and the portfolio has produced a return greater than the hurdle asset return since the start of the backtest. The performance fee is charged only on the portfolio growth in excess of both the hurdle asset growth and the previous high-water mark.
For more information, see Performance Hurdle.
Data Types: double | string
Since R2022b
Performance fee schedule, specified as the comma-separated pair
                                consisting of 'PerformanceFeeSchedule' and a
                                numeric scalar, a duration or calendarDuration
                                object, or alternatively as a vector of datetimes. 
PerformanceFeeSchedule specifies the schedule
                                of dates where the performance fee is charged (if
                                    PerformanceFee is defined). The default is
                                    calyears(1), meaning the performance fee is
                                charged annually.
For more information, see Defining Schedules for Backtest Strategies.
Data Types: double | datetime | object
Since R2023a
Strategy-specific user data for use in
                                    rebalanceFcn, specified as a struct. When
                                    UserData is set to a struct, the backtestEngine provides all user-defined
                                    rebalanceFcn function handle functions with
                                the userData struct as an additional input
                                argument. The rebalanceFcn function handle
                                functions can read and write to the userData
                                struct. The rebalanceFcn function handle
                                functions return the updated userData struct as
                                an additional output argument.
For an example of using UserData, see Use UserData Property to Create Stop Loss Trading Strategy and Backtest with Brinson Attribution to Evaluate Portfolio Performance.
Data Types: struct
Since R2023a
Specify a set of optional backtest state data that a strategy
                                needs in the rebalanceFcn function handle,
                                specified as a scalar string or string array. By default, the
                                    EngineDataList property is empty, indicating
                                that no additional engine data is required.
If a strategy requires engine data, you can specify
                                    EngineDataList as a vector of strings
                                containing the names of the required engineData.
                                The backtestEngine creates an
                                    engineData struct with a field corresponding
                                to each element in the EngineDataList. Valid
                                    engineData values are:
- Weights— The current portfolio weights before rebalancing,specified as decimal percentages. This is the default first argument to the rebalance function (- RebalanceFcn).
- WeightsHistory— A timetable of- Weightsvectors for each past date in the backtest. This timetable holds the end-of-day asset weights for each asset for each day. Future dates in the timetable contain- NaNvalues.
- PortfolioValue— The current portfolio value at the time the rebalance function (- RebalanceFcn) is called, specified as a numeric scalar. This- PortfolioValueis potentially different from the "end of day" portfolio value reported by the- backtestEngineat the end of the backtest. Transaction costs and other fees are paid after the rebalance function is called, so the- PortfolioValuepassed into the rebalance function by the- engineDatastruct may be different from the final portfolio value reported for that day after the backtest completes.
- PortfolioValueHistory— A timetable of portfolio values for the backtest. The timetable contains two columns. The- BeforeExpensescolumn holds the portfolio value for each day before any expenses (transaction costs or fees). The- EndOfDaycolumn holds the portfolio value for each day, inclusive of all transaction costs and other fees. The values in the second column (- EndOfDay) match the final end-of-day portfolio values reported at the end of the backtest. Future dates in the timetable contain- NaNvalues.
- FeesHistory— A timetable of fees charged for each day in the backtest. The- FeesHistorytimetable has two columns:- Managementand- Performancefor the management and performance fees, respectively. The management and performance fees are defined using the- ManagementFeeand- PerformanceFeename-value arguments. Future dates in the timetable contain- NaNvalues.
- TransactionCostHistory— A timetable of per-asset transaction costs charged for each day in the backtest. Future dates in the timetable contain- NaNvalues. The- TransactionCostHistorycannot be set if the- TransactionCostsfunction generate aggregate costs. The- TransactionCosts- computeTransactionCostsFcnfunction handle must specify an output for- detailedCoststo set- TransactionCostHistory.
- CashAsset— A string array for items in the- pricesTTtimetable specified as cash assets.
- DebtAsset— A string array for items in the- pricesTTtimetable specified as debt assets.
Specifying an EngineDataList changes the
                                required syntax of the backtestStrategy function
                                handles that you define using rebalanceFcn. For
                                an example of using EngineDataList, see Use EngineDataList Property to Enforce Trading Strategy of Whole Shares.
Data Types: string
Output Arguments
Backtest strategy, returned as a backtestStrategy
                            object.
Properties
Strategy name, specified as a string.
Data Types: string
Rebalance function, specified as a function handle.
Data Types: function_handle
Rebalance frequency during the backtest, specified as a scalar numeric,
                            duration or calendarDuration
                        object, or vector of datetimes.
Data Types: double | object | datetime
Transaction costs, specified as a scalar numeric, vector, or function handle.
Data Types: double | function_handle
Lookback window, specified as a scalar numeric or vector.
Data Types: double
Initial weights, specified as a vector.
Data Types: double
Since R2022b
Management fee charged as an annualized percent of the total portfolio value, specified as a numeric scalar.
Data Types: double
Since R2022b
Management fee schedule, specified as scalar numeric, a vector of
                        datetimes, or a duration or
                            calendarDuration object. 
Data Types: double | datetime | object
Since R2022b
Performance fee charged as an absolute percent of the total portfolio growth, specified as a numeric scalar.
Data Types: double
Since R2022b
Annualized hurdle rate or column in assetPrices or
                            signalData to act as hurdle asset, specified as a
                        numeric scalar or a string in either assetPrices or
                            signalData as a hurdle asset. 
Data Types: double | string
Since R2022b
Performance fee schedule, specified as a scalar numeric, vector of
                        datetimes, or a duration or
                            calendarDuration object. 
Data Types: datetime | object
Since R2023a
Strategy-specific user data for use in rebalanceFcn,
                        specified as a struct. 
Data Types: struct
Since R2023a
Specify set of optional backtest state data a strategy needs in
                            rebalanceFcn, specified as a scalar string or
                        string array. 
Data Types: string
Examples
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 backtest engine calls a strategy rebalance function, a rolling 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 that data. The LookbackWindow property sets the size of these rolling windows in terms of time steps. The window determines the amount 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 of the rolling window. This way of defining the LookbackWindows allows for an expanding window of data. For example: 
- [10 Inf]— At least 10 rows of data must be available before the current rebalancing period.
- [0 50]— At most 50 rows of data prior to the current rebalancing period are passed to the rebalancing function.
- [0 Inf]— Use all available data up to the current rebalancing period; there is no minimum and no maximum. This the default value.
- [20 20]— Exactly 20 rows of data prior to the current rebalancing period are passed to the rebalancing function; this is equivalent to setting- LookbackWindowto 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 0.0333 0.0333 0.0333 0.0333 0.0333 0.0333 0.0333 0.0333 0.0333 0.0333 0.0333 0.0333 0.0333 0.0333 0.0333 0.0333 0.0333 0.0333 0.0333 0.0333 0.0333 0.0333 0.0333 0.0333]
             ManagementFee: 0
     ManagementFeeSchedule: 1y
            PerformanceFee: 0
    PerformanceFeeSchedule: 1y
         PerformanceHurdle: 0
                  UserData: [0×0 struct]
            EngineDataList: [0×0 string]
% 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 0.0333 0.0333 0.0333 0.0333 0.0333 0.0333 0.0333 0.0333 0.0333 0.0333 0.0333 0.0333 0.0333 0.0333 0.0333 0.0333 0.0333 0.0333 0.0333 0.0333 0.0333 0.0333 0.0333 0.0333]
             ManagementFee: 0
     ManagementFeeSchedule: 1y
            PerformanceFee: 0
    PerformanceFeeSchedule: 1y
         PerformanceHurdle: 0
                  UserData: [0×0 struct]
            EngineDataList: [0×0 string]
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
Since R2023a
This example shows how to use the UserData property of backtestStrategy to create a stop loss trading strategy. A stop loss strategy sets a threshold to define how much money that you are willing to lose before closing a position. For example, if you bought a stock at $100, you could set a stop loss at $95, and if the stock price falls below that point, then you sell your position. You can then set a new price that is your buy-in price to buy back into the stock. 
Load Data
% Load equity adjusted price data and convert to timetable T = readtable('dowPortfolio.xlsx'); pricesTT = table2timetable(T(:,[1 3:end]),'RowTimes','Dates')
pricesTT=251×30 timetable
       Dates        AA       AIG      AXP      BA        C       CAT      DD       DIS      GE       GM       HD       HON      HPQ      IBM     INTC      JNJ      JPM      KO       MCD      MMM      MO       MRK     MSFT      PFE      PG        T       UTX      VZ       WMT      XOM 
    ___________    _____    _____    _____    _____    _____    _____    _____    _____    _____    _____    _____    _____    _____    _____    _____    _____    _____    _____    _____    _____    _____    _____    _____    _____    _____    _____    _____    _____    _____    _____
    03-Jan-2006    28.72    68.41    51.53    68.63    45.26    55.86    40.68    24.18     33.6    17.82    39.79    36.14    28.35    80.13    24.57    59.08    37.78    38.98    32.72    75.93    52.27    30.73    26.19    22.16    56.38     22.7    54.94    26.79     44.9    56.64
    04-Jan-2006    28.89    68.51    51.03    69.34    44.42    57.29    40.46    23.77    33.56     18.3    39.05    35.99    29.18    80.03     24.9    59.99    37.56    38.91    33.01    75.54    52.65    31.08    26.32    22.88    56.48    22.87    54.61    27.58    44.99    56.74
    05-Jan-2006    29.12     68.6    51.57    68.53    44.65    57.29    40.38    24.19    33.47    19.34    38.67    35.97    28.97    80.56    25.25    59.74    37.67     39.1    33.05    74.85    52.52    31.13    26.34     22.9     56.3    22.92    54.41     27.9    44.38    56.45
    06-Jan-2006    29.02    68.89    51.75    67.57    44.65    58.43    40.55    24.52     33.7    19.61    38.96    36.53     29.8    82.96    25.28    60.01    37.94    39.47    33.25    75.47    52.95    31.08    26.26    23.16    56.24    23.21    54.58    28.01    44.56    57.57
    09-Jan-2006    29.37    68.57    53.04    67.01    44.43    59.49    40.32    24.78    33.61    21.12    39.38    36.23    30.17    81.76    25.44    60.38    38.55    39.66    33.88    75.84    53.11    31.58    26.21    23.16    56.67     23.3     55.2    28.12     44.4    57.54
    10-Jan-2006    28.44    69.18    52.88    67.33    44.57    59.25     40.2    25.09    33.43    20.79    40.33    36.17    30.33     82.1     25.1    60.49    38.61     39.7    33.91    75.37    53.04    31.27    26.35    22.77    56.45    23.16    55.24    28.24    44.54    57.99
    11-Jan-2006    28.05     69.6    52.59     68.3    44.98    59.28    38.87    25.33    33.66    20.61    41.44    36.19    30.88    82.19    25.12    59.91    38.58    39.72     34.5    75.22    53.31    31.39    26.63    23.06    56.65    23.34    54.41    28.58    45.23    58.38
    12-Jan-2006    27.68    69.04     52.6     67.9    45.02    60.13    38.02    25.41    33.25    19.76    41.05    35.77    30.57    81.61    24.96    59.63    37.87     39.5    33.96    74.57    53.23    31.41    26.48     22.9    56.02    23.24     53.9    28.69    44.43    57.77
    13-Jan-2006    27.81    68.84     52.5     67.7    44.92    60.24    37.86    25.47    33.35     19.2    40.43    35.85    31.43    81.22    24.78    59.26    37.84    39.37    33.65    74.38    53.29     31.4    26.53    22.99    56.49    23.27     54.1    28.75     44.1    59.06
    17-Jan-2006    27.97    67.84    52.03    66.93    44.47    60.85    37.75    25.15     33.2    18.68    40.11    35.56     31.2    81.05    24.52    58.74    37.64    39.11    33.77    73.99    52.85    31.16    26.34    22.63    56.25    23.13    54.41    28.12    43.66    59.61
    18-Jan-2006    27.81    67.42    51.84    66.58    44.41    60.04    37.54    24.97    33.08    18.98    40.42    35.71    31.21    81.83    21.72    59.61    37.24    38.91    34.16    74.07    52.89    30.99    26.18    22.36    56.54    23.11    54.08    27.83    43.88    58.78
    19-Jan-2006    28.33    66.92    51.66    66.42    44.02    60.66    37.69       26    32.95     19.1    39.83    35.88    31.77    81.14    21.53     59.6    37.03    39.01    34.36    73.81    52.68    31.26    26.36    23.27    56.39    23.18    54.41    28.07    44.49    59.57
    20-Jan-2006    27.67    65.55    50.49    64.79    41.95    58.98    37.34    25.49     31.7     18.9    38.76    34.57    31.28    79.45    20.91    58.28    36.07    38.21    35.01    72.21    52.18     31.2    25.77    23.03    55.74    23.01    53.46    27.64    43.71    58.63
    23-Jan-2006    28.03    65.46    50.53     65.3    42.24    59.23    37.37    25.29    31.63     20.6    38.29    34.78    30.88     79.5    20.52    58.66    36.28     38.6    34.86    72.65    52.09     30.8    25.71    23.19    55.55    22.77    52.94    27.69    43.95    59.28
    24-Jan-2006    28.16    65.39    51.89    65.92    42.25    59.53    37.09    25.76    31.32    21.73    39.03    35.25    30.91    78.95    20.45     56.9    36.13    38.98       35    71.21    51.74    30.76    25.64    22.91    55.77    22.96    54.86     27.6    44.41    59.05
    25-Jan-2006    28.57    64.67    51.97    65.19    42.45    60.23    37.05    25.21    31.13    22.48    38.57    34.79    31.64    79.01    20.38    56.08    36.48    39.21    34.32    70.06    51.49    31.14    25.76    23.14    56.35    23.47     55.4    28.03    44.65    58.32
      ⋮
Create Backtest Stop Loss Strategy
The stop loss strategy will either be 100% invested in the stock or 100% in cash. You can use two fields in the UserData struct for backtestStrategy to set the stop loss limits:
- StopLossPercent— How much you are willing to lose before selling, as a decimal percent
- BuyInPercent— How much further a stock must fall after you sell it before we buy back in, as a percent
In addition, the UserData struct has the following fields:
- Asset— Ticker symbol AIG for the asset you want to trade
- StopLossPrice— Threshold to sell the asset. If the price falls below this stop loss price, sell the asset and go to cash.
- BuyInPrice— Target price to buy the asset. If the asset is at this price or lower, buy the asset.
stopLossStrat = backtestStrategy("StopLoss",@stopLossSingleAsset,RebalanceFrequency=1); % Setup the initial UserData stopLossStrat.UserData = struct(StopLossPercent=0.05,BuyInPercent=0.02,Asset="AIG",StopLossPrice=nan,BuyInPrice=Inf);
The backtest strategy starts 100% in cash. Normally the BuyInPrice is set by the strategy after you sell a stock, but in this case, you can initialize the BuyInPrice to be Inf. This triggers a stock buy on the first rebalance (since any price is less than Inf). You don't need to set the StopLossPrice at this point (it is set to nan) because the strategy sets the stop loss price once a stock purchase is made.
The strategy specifies that you are only willing to lose 5% of your capital before the stop loss is triggered and you sell.  Then you will buy back into the stock if it falls by an additional 2%. You can set the rebalance frequency to 1 because this type of strategy has to "rebalance" every day in order to check the price to determine if any action is needed.
Run Backtest
Create a backtestEngine object and run the backtest using runBacktest.
backtester = backtestEngine(stopLossStrat); backtester = runBacktest(backtester,pricesTT);
Use equityCurve to generate a plot the stop loss trading strategy.
equityCurve(backtester)

The flat parts of the equity curve are where the stop loss strategy has sold AIG and then later buys back into AIG.
Local Functions
function [new_weights,user_data] = incrementCounter(current_weights,prices,user_data) % Don't change the weights new_weights = current_weights; % Increment the user data counter user_data.Counter = user_data.Counter + 1; end
function [new_weights,user_data] = stopLossSingleAsset(current_weights,prices,user_data) % Start with the existing weights new_weights = current_weights; % Find column of the asset asset = char(user_data.Asset); assetIdx = find(strncmpi(asset,prices.Properties.VariableNames,numel(asset))); assetPrice = prices{end,assetIdx}; % Determine if currently invested or in cash inCash = sum(current_weights) < 1e-5; if inCash % You are in cash, see if you should buy back in if assetPrice <= user_data.BuyInPrice % Buy back in new_weights(assetIdx) = 1; % Set new stop loss price user_data.StopLossPrice = assetPrice * (1 - user_data.StopLossPercent); end else % You are in the stock, see if you should sell if assetPrice <= user_data.StopLossPrice % Sell the stock new_weights(assetIdx) = 0; % Set new buy-in price user_data.BuyInPrice = assetPrice * (1 - user_data.BuyInPercent); end end end
Since R2023a
This example shows how to use the EngineDataList property of backtestStrategy to enforce a trading strategy with a whole shares constraint. 
Load Data
% Load equity adjusted price data and convert the data to timetable T = readtable('dowPortfolio.xlsx'); pricesTT = table2timetable(T(:,[1 3:end]),'RowTimes','Dates')
pricesTT=251×30 timetable
       Dates        AA       AIG      AXP      BA        C       CAT      DD       DIS      GE       GM       HD       HON      HPQ      IBM     INTC      JNJ      JPM      KO       MCD      MMM      MO       MRK     MSFT      PFE      PG        T       UTX      VZ       WMT      XOM 
    ___________    _____    _____    _____    _____    _____    _____    _____    _____    _____    _____    _____    _____    _____    _____    _____    _____    _____    _____    _____    _____    _____    _____    _____    _____    _____    _____    _____    _____    _____    _____
    03-Jan-2006    28.72    68.41    51.53    68.63    45.26    55.86    40.68    24.18     33.6    17.82    39.79    36.14    28.35    80.13    24.57    59.08    37.78    38.98    32.72    75.93    52.27    30.73    26.19    22.16    56.38     22.7    54.94    26.79     44.9    56.64
    04-Jan-2006    28.89    68.51    51.03    69.34    44.42    57.29    40.46    23.77    33.56     18.3    39.05    35.99    29.18    80.03     24.9    59.99    37.56    38.91    33.01    75.54    52.65    31.08    26.32    22.88    56.48    22.87    54.61    27.58    44.99    56.74
    05-Jan-2006    29.12     68.6    51.57    68.53    44.65    57.29    40.38    24.19    33.47    19.34    38.67    35.97    28.97    80.56    25.25    59.74    37.67     39.1    33.05    74.85    52.52    31.13    26.34     22.9     56.3    22.92    54.41     27.9    44.38    56.45
    06-Jan-2006    29.02    68.89    51.75    67.57    44.65    58.43    40.55    24.52     33.7    19.61    38.96    36.53     29.8    82.96    25.28    60.01    37.94    39.47    33.25    75.47    52.95    31.08    26.26    23.16    56.24    23.21    54.58    28.01    44.56    57.57
    09-Jan-2006    29.37    68.57    53.04    67.01    44.43    59.49    40.32    24.78    33.61    21.12    39.38    36.23    30.17    81.76    25.44    60.38    38.55    39.66    33.88    75.84    53.11    31.58    26.21    23.16    56.67     23.3     55.2    28.12     44.4    57.54
    10-Jan-2006    28.44    69.18    52.88    67.33    44.57    59.25     40.2    25.09    33.43    20.79    40.33    36.17    30.33     82.1     25.1    60.49    38.61     39.7    33.91    75.37    53.04    31.27    26.35    22.77    56.45    23.16    55.24    28.24    44.54    57.99
    11-Jan-2006    28.05     69.6    52.59     68.3    44.98    59.28    38.87    25.33    33.66    20.61    41.44    36.19    30.88    82.19    25.12    59.91    38.58    39.72     34.5    75.22    53.31    31.39    26.63    23.06    56.65    23.34    54.41    28.58    45.23    58.38
    12-Jan-2006    27.68    69.04     52.6     67.9    45.02    60.13    38.02    25.41    33.25    19.76    41.05    35.77    30.57    81.61    24.96    59.63    37.87     39.5    33.96    74.57    53.23    31.41    26.48     22.9    56.02    23.24     53.9    28.69    44.43    57.77
    13-Jan-2006    27.81    68.84     52.5     67.7    44.92    60.24    37.86    25.47    33.35     19.2    40.43    35.85    31.43    81.22    24.78    59.26    37.84    39.37    33.65    74.38    53.29     31.4    26.53    22.99    56.49    23.27     54.1    28.75     44.1    59.06
    17-Jan-2006    27.97    67.84    52.03    66.93    44.47    60.85    37.75    25.15     33.2    18.68    40.11    35.56     31.2    81.05    24.52    58.74    37.64    39.11    33.77    73.99    52.85    31.16    26.34    22.63    56.25    23.13    54.41    28.12    43.66    59.61
    18-Jan-2006    27.81    67.42    51.84    66.58    44.41    60.04    37.54    24.97    33.08    18.98    40.42    35.71    31.21    81.83    21.72    59.61    37.24    38.91    34.16    74.07    52.89    30.99    26.18    22.36    56.54    23.11    54.08    27.83    43.88    58.78
    19-Jan-2006    28.33    66.92    51.66    66.42    44.02    60.66    37.69       26    32.95     19.1    39.83    35.88    31.77    81.14    21.53     59.6    37.03    39.01    34.36    73.81    52.68    31.26    26.36    23.27    56.39    23.18    54.41    28.07    44.49    59.57
    20-Jan-2006    27.67    65.55    50.49    64.79    41.95    58.98    37.34    25.49     31.7     18.9    38.76    34.57    31.28    79.45    20.91    58.28    36.07    38.21    35.01    72.21    52.18     31.2    25.77    23.03    55.74    23.01    53.46    27.64    43.71    58.63
    23-Jan-2006    28.03    65.46    50.53     65.3    42.24    59.23    37.37    25.29    31.63     20.6    38.29    34.78    30.88     79.5    20.52    58.66    36.28     38.6    34.86    72.65    52.09     30.8    25.71    23.19    55.55    22.77    52.94    27.69    43.95    59.28
    24-Jan-2006    28.16    65.39    51.89    65.92    42.25    59.53    37.09    25.76    31.32    21.73    39.03    35.25    30.91    78.95    20.45     56.9    36.13    38.98       35    71.21    51.74    30.76    25.64    22.91    55.77    22.96    54.86     27.6    44.41    59.05
    25-Jan-2006    28.57    64.67    51.97    65.19    42.45    60.23    37.05    25.21    31.13    22.48    38.57    34.79    31.64    79.01    20.38    56.08    36.48    39.21    34.32    70.06    51.49    31.14    25.76    23.14    56.35    23.47     55.4    28.03    44.65    58.32
      ⋮
Create Backtest Strategies
This example runs a backtest on two equal-weighted strategies and then compares the results. The first backtest strategy uses an exact equal-weight rebalance function that allows fractional shares. The second backtest strategy uses a EngineDataList property for PortfolioValue to enforce a whole shares constraint.  
Use backtestStrategy to create the first strategy for partial shares.
% Partial shares and equal weight for assets partialRebal = @(~,prices) ones(1,width(prices)) / width(prices); ewPartial = backtestStrategy("PartialShares",partialRebal,RebalanceFrequency=1);
Use backtestStrategy to create the second strategy for whole shares.
% Whole shares and equal weight for assets ewWhole = backtestStrategy("WholeShares",@equalWeightShares,RebalanceFrequency=1,EngineDataList="PortfolioValue");
Run Backtest
Aggregate the two strategy objects, create a backtestEngine object, and then run the backtest using runBacktest.
strats = [ewPartial, ewWhole]; backtester = backtestEngine(strats,RiskFreeRate=0.02); backtester = runBacktest(backtester,pricesTT);
Use equityCurve to generate a plot for both of the trading strategies.
equityCurve(backtester);

You can verify that the second strategy traded in whole shares.
% Plot the number of shares of each asset plot(backtester.Positions.PartialShares{:,2:end} ./ pricesTT{:,:}); title('Number of Shares per Asset (Partial Shares)')

plot(backtester.Positions.WholeShares{:,2:end} ./ pricesTT{:,:})
title('Number of Shares per Asset (Whole Shares)')
Local Functions
function new_weights = equalWeightShares(engine_data,prices) numAssets = width(prices); % Dollars per asset for equal weight with partial shares dollars_per_asset = engine_data.PortfolioValue / numAssets; % Compute shares (partial shares first, then round down) asset_shares = floor(dollars_per_asset ./ prices{end,:}); % Convert number of shares into dollars asset_dollars = asset_shares .* prices{end,:}; % Convert dollars into weights new_weights = asset_dollars / engine_data.PortfolioValue; end
Since R2024a
This example shows how to use the LookbackWindow name-value argument to set the rolling windows of the backtesting strategies. Each time the backtest engine calls a strategy rebalance function, a rolling window of asset price data (and possibly signal data) is passed to the rebalance function (rebalanceFcn). The rebalance function can then make trading and allocation decisions based on that data. The LookbackWindow property sets the size of these rolling windows in terms of time steps. The window determines the amount of data from the asset price timetable that is passed to the rebalance function.
The LookbackWindow property can be set in two ways:
- Expanding rolling window — For an expanding rolling window of data, set the - LookbackWindowproperty to a- 1-by-- 2vector [- min max]. The software then calls the rebalance function with a price timetable containing at least- minrows and at most- maxrows of rolling price data.
- Fixed-size rolling window — For a fixed-sized rolling window of data, set the - LookbackWindowproperty to a single scalar value (N). The software then calls the rebalance function with a price timetable containing exactly N rows of rolling price data.
Load Data
Load 10 days of adjusted price data for a selected set of stocks.
% Read a table of daily adjusted close prices for 2006 DJIA stocks. T = readtable('dowPortfolio.xlsx'); % For readability, use only a few stocks. assetSymbols = ["AA","GM","MMM","MSFT","PG","T","XOM"]; % Prune the table to hold only 10 days of data and selected stocks. timeColumn = "Dates"; T = T(1:10,[timeColumn assetSymbols]); % Convert the table to a timetable. pricesTT = table2timetable(T,'RowTimes','Dates');
Set Expanding Rolling Window
Set the backtesting strategy to rebalance every two days.
% Define the rebalance frequency.
rebalFreq = 2;Set the lookback window to a minimum of four days and a maximum of eight days.
% Define the lookback window. lookback = [4 8]; % Set backtest strategy. strat = backtestStrategy('MaxSharpeRatio',@maxSharpeRatioFcn, ... RebalanceFrequency=rebalFreq,LookbackWindow=lookback); % Define a backtest engine. backtester = backtestEngine(strat); % Run the backtest. runBacktest(backtester,pricesTT,Start=1);
#################################################################################
Rebalancing period: 09-Jan-2006
Number of time periods in data: 5
#################################################################################
       Dates        AA       GM       MMM     MSFT      PG        T       XOM 
    ___________    _____    _____    _____    _____    _____    _____    _____
    03-Jan-2006    28.72    17.82    75.93    26.19    56.38     22.7    56.64
    04-Jan-2006    28.89     18.3    75.54    26.32    56.48    22.87    56.74
    05-Jan-2006    29.12    19.34    74.85    26.34     56.3    22.92    56.45
    06-Jan-2006    29.02    19.61    75.47    26.26    56.24    23.21    57.57
    09-Jan-2006    29.37    21.12    75.84    26.21    56.67     23.3    57.54
#################################################################################
#################################################################################
Rebalancing period: 11-Jan-2006
Number of time periods in data: 7
#################################################################################
       Dates        AA       GM       MMM     MSFT      PG        T       XOM 
    ___________    _____    _____    _____    _____    _____    _____    _____
    03-Jan-2006    28.72    17.82    75.93    26.19    56.38     22.7    56.64
    04-Jan-2006    28.89     18.3    75.54    26.32    56.48    22.87    56.74
    05-Jan-2006    29.12    19.34    74.85    26.34     56.3    22.92    56.45
    06-Jan-2006    29.02    19.61    75.47    26.26    56.24    23.21    57.57
    09-Jan-2006    29.37    21.12    75.84    26.21    56.67     23.3    57.54
    10-Jan-2006    28.44    20.79    75.37    26.35    56.45    23.16    57.99
    11-Jan-2006    28.05    20.61    75.22    26.63    56.65    23.34    58.38
#################################################################################
#################################################################################
Rebalancing period: 13-Jan-2006
Number of time periods in data: 8
#################################################################################
       Dates        AA       GM       MMM     MSFT      PG        T       XOM 
    ___________    _____    _____    _____    _____    _____    _____    _____
    04-Jan-2006    28.89     18.3    75.54    26.32    56.48    22.87    56.74
    05-Jan-2006    29.12    19.34    74.85    26.34     56.3    22.92    56.45
    06-Jan-2006    29.02    19.61    75.47    26.26    56.24    23.21    57.57
    09-Jan-2006    29.37    21.12    75.84    26.21    56.67     23.3    57.54
    10-Jan-2006    28.44    20.79    75.37    26.35    56.45    23.16    57.99
    11-Jan-2006    28.05    20.61    75.22    26.63    56.65    23.34    58.38
    12-Jan-2006    27.68    19.76    74.57    26.48    56.02    23.24    57.77
    13-Jan-2006    27.81     19.2    74.38    26.53    56.49    23.27    59.06
#################################################################################
Start=1 in runBacktest sets the starting time period to "03-Jan-2006", the first date available in pricesTT. Given that RebalanceFrequency is set to 2, you might expect the next rebablancing period to be "05-Jan-2006". However, the fist time the strategy rebalances is on "09-Jan-2006". This happens because the minimum value of the lookback window is set to 4 and on "05-Jan-2006" there are only three time periods available, so the backtest engine does not call the rebalancing function on this time period. On the next rebalancing period, "11-Jan-2006", the data includes seven observations which correspond to all the rows up to "11-Jan-2006". On the last rebalancing period, "13-Jan-2006", the first input of pricesTT is not included in the data. This happens because the maximum value of the lookback window is set to 8, which means that only the last eight time periods are passed to the rebalancing function. Also, note that the rolling window increases in size from rebalancing period to rebalancing period until it reaches the maximum size.
Set Fixed-Sized Rolling Window
Set the backtesting strategies to rebalance every 2 days.
% Define the rebalance frequency.
rebalFreq = 2;Set the lookback window to a fixed-size of 5 days.
% Define the lookback window. lookback = 5; % Set backtest strategy. strat = backtestStrategy('MaxSharpeRatio',@maxSharpeRatioFcn, ... RebalanceFrequency=rebalFreq,LookbackWindow=lookback); % Define a backtest engine. backtester = backtestEngine(strat); % Run the backtest. runBacktest(backtester,pricesTT,Start=1);
#################################################################################
Rebalancing period: 09-Jan-2006
Number of time periods in data: 5
#################################################################################
       Dates        AA       GM       MMM     MSFT      PG        T       XOM 
    ___________    _____    _____    _____    _____    _____    _____    _____
    03-Jan-2006    28.72    17.82    75.93    26.19    56.38     22.7    56.64
    04-Jan-2006    28.89     18.3    75.54    26.32    56.48    22.87    56.74
    05-Jan-2006    29.12    19.34    74.85    26.34     56.3    22.92    56.45
    06-Jan-2006    29.02    19.61    75.47    26.26    56.24    23.21    57.57
    09-Jan-2006    29.37    21.12    75.84    26.21    56.67     23.3    57.54
#################################################################################
#################################################################################
Rebalancing period: 11-Jan-2006
Number of time periods in data: 5
#################################################################################
       Dates        AA       GM       MMM     MSFT      PG        T       XOM 
    ___________    _____    _____    _____    _____    _____    _____    _____
    05-Jan-2006    29.12    19.34    74.85    26.34     56.3    22.92    56.45
    06-Jan-2006    29.02    19.61    75.47    26.26    56.24    23.21    57.57
    09-Jan-2006    29.37    21.12    75.84    26.21    56.67     23.3    57.54
    10-Jan-2006    28.44    20.79    75.37    26.35    56.45    23.16    57.99
    11-Jan-2006    28.05    20.61    75.22    26.63    56.65    23.34    58.38
#################################################################################
#################################################################################
Rebalancing period: 13-Jan-2006
Number of time periods in data: 5
#################################################################################
       Dates        AA       GM       MMM     MSFT      PG        T       XOM 
    ___________    _____    _____    _____    _____    _____    _____    _____
    09-Jan-2006    29.37    21.12    75.84    26.21    56.67     23.3    57.54
    10-Jan-2006    28.44    20.79    75.37    26.35    56.45    23.16    57.99
    11-Jan-2006    28.05    20.61    75.22    26.63    56.65    23.34    58.38
    12-Jan-2006    27.68    19.76    74.57    26.48    56.02    23.24    57.77
    13-Jan-2006    27.81     19.2    74.38    26.53    56.49    23.27    59.06
#################################################################################
Start=1 in runBacktest sets the starting time period to "03-Jan-2006", the first date available in pricesTT. Given that RebalanceFrequency is set to 2, you might expect the next rebalancing period to be "05-Jan-2006". However, the fist time the strategy rebalances is on "09-Jan-2006". This happens because LookbackWindow is set to 5, which automatically sets the minimum lookback window to 5 and at "05-Jan-2006" there are only three time periods available. On the second rebalancing period, "11-Jan-2006", the first two inputs of pricesTT are not included in the data passed to the rebalancing function. This happens because the maximum value of the lookback window is implicitly set to 5, which means that only the last five time periods are passed to the rebalancing function. The same behavior happens in the last rebalancing period. Also, note that the size of the rolling window remains constant throughout all rebalancing periods.
Local Functions
function new_weights = maxSharpeRatioFcn(~, pricesTT) % Maximum Sharpe Ratio allocation % Display prices timetable and rebalancing period. fprintf('#################################################################################\n') fprintf('Rebalancing period: %s\n',string(pricesTT.Dates(end))) fprintf('Number of time periods in data: %i\n',size(pricesTT,1)) fprintf('#################################################################################\n') disp(pricesTT) fprintf('#################################################################################\n\n') % Compute asset returns. assetReturns = tick2ret(pricesTT); % Define porfolio problem. p = Portfolio; p = estimateAssetMoments(p, assetReturns{:,:}); p = setDefaultConstraints(p); % Estimate the Max Sharpe Ratio. new_weights = estimateMaxSharpeRatio(p); end
More About
A backtesting schedule is a set of dates defining when specific events will occur.
The backtestStrategy object uses schedules in several of ways,
                including setting the rebalance frequency (RebalanceFrequency),
                the performance fee (PerformanceFeeSchedule), and the
                management fee (ManagementFeeSchedule) schedules. Schedules are
                specified with a syntax that supports a variety of data types, which include numeric
                scalars, a vector of datetimes, and  duration or calendarDuration objects. The
                resulting schedules for the different data types are:
- Numeric — If a schedule is defined using a numeric scalar, it refers to the number of rows (of the - assetPricestimetable) between each event in the schedule, starting from the backtest start date. For example, if a schedule is set to 10, then the schedule event (a rebalance date or fee payment date) occurs on every 10th row of the prices timetable while the backtest runs.
- durationor- calendarDuration— If specified as a- durationor- calendarDurationobject, the schedule specifies a duration of time between each event (rebalance date or fee payment date). The schedule is calculated starting at the backtest start date and then schedule dates are added after each step of the specified duration. For an example, see Backtest Investment Strategies Using datetime and calendarDuration.
- Vector of datetimes — If the numeric or duration syntaxes are not appropriate, then you can explicitly specify schedules using a vector of datetimes. 
Note
For both the duration and datetime syntaxes, if a resulting schedule date
                        is not found in the backtest data set (the assetPrices
                        timetable), the backtestEngine
                        either adjusts the date to the nearest valid date or else issues an error.
                        This behavior is controlled by the DateAdjustment
                        property of the backtestEngine
                        object. By default, dates are adjusted to the nearest previous date. For
                        example, if a schedule date falls on a Saturday, and is not found in the
                        backtest data set, then the date is adjusted to the previous Friday.
                        Alternatively, the backtestEngine
                        DateAdjustment property allows you to adjust dates to the
                        next valid date, or to issue an error if a date is not found by requiring
                        exact dates.
The management fee is the annualized rate charged on the assets under management of the strategy to pay for the fund's management costs.
The fee is charged on each date specified by the
                    ManagementFeeSchedule name-value argument. For more
                information, see Defining Schedules for Backtest Strategies.
The management fee is forward looking, meaning that a fee paid on a given date
                covers the management of the strategy from that date to the next management fee
                date. For some syntaxes of theManagementFeeSchedule, the
                resulting schedule does not begin and end precisely on the backtest start and end
                dates. In these cases, the backtest engine adds default dates to the start and end
                of the schedule to ensure that the entire backtest is covered by management fees.
                For example:
- If - ManagementFeeScheduleis specified as a vector of datetimes, and the backtest start date is not included in the vector, then the start date is added to the fee schedule. 
- If - ManagementFeeScheduleis specified as a numeric scalar or a vector of datetimes, and the final specified fee date occurs before the backtest end date, then the backtest end date is added to the fee schedule. 
- If - ManagementFeeScheduleis specified as a- durationor- calendarDurationobject, and the backtest end date is not included in the generated schedule, then a final fee date is added to the end by adding the duration object to the (previous) final fee date. This adjustment can produce a final fee date that is beyond the end of the backtest, but it results in more realistic fees paid at the final payment date. 
For more information on where the resulting management fees are reported, see the
                    backtestEngine object
                read-only property Fees.
A performance fee, or incentive fee, is a periodic payment that is paid by fund investors based on the fund's performance.
The performance fee can have a variety of structures, but it often is calculated as a percentage of a fund's increase in NAV over some tracked high-water mark. This calculation ensures the fee is paid only when the investment manager produces positive results. Performance fees in hedge funds are sometimes set at 20%, meaning if the fund NAV goes from $1M to $1.2M, a fee of $40k would be charged (20% of the $200k growth).
During the backtest, the backtest engine tracks a high-water mark. The high-water mark starts at the portfolio initial value. At each performance fee date, if the portfolio value is greater than the previous high-water mark, then the high-water mark is updated to the new portfolio value and the performance fee is paid on the growth of the portfolio. The portfolio growth is the difference between the new high-water mark and the previous one. If, on a performance fee date, the portfolio value is less than the previous high-water mark, then no fee is paid and the high-water mark is not updated.
The high-water mark is typically updated at each performance fee date and does not track maximum portfolio values between dates, as illustrated:

For more information on where the resulting performance fees are reported, see the
                    backtestEngine object
                read-only property Fees.
A hurdle is an optional feature of a performance fee.
A hurdle sets a minimum performance threshold that a fund must beat before the performance fee is charged. Hurdles can be fixed rates of return, a hurdle rate, such as 5%. For example, if the fund NAV goes from $1M to $1.2M, the performance fee is charged only on the performance beyond 5%. A 5% hurdle rate on $1M sets the target hurdle value at $1.05M. The $1.2M portfolio is $150k over the hurdle value. The performance fee is charged only on that $150k, so 20% of $150k is a $30k performance fee.
The following figure illustrates a 5% hurdle rate.

You can also specify a performance hurdle as a benchmark asset or index. In this case, the performance fee is paid only on the fund performance in excess of the hurdle instrument. For example, assume that the fund NAV goes from $1M to $1.2M and you have specified the S&P500 as your hurdle. If the S&P500 index returns 25% over the same period, then no performance fee is paid because the fund did not produce returns in excess of the hurdle instrument. If the fund value is greater than the hurdle, but still less than the previous high-water mark, again, no performance fee is paid.
At each performance fee date, the fund charges the performance fee on the difference between the current fund value and the maximum of the high-water mark and the hurdle value. The high-water mark is updated only if a performance fee is paid.

For more information on where the resulting performance fees are reported, see the
                    backtestEngine object
                read-only property Fees.
Version History
Introduced in R2020bWhen using a function handle with the TransactionCosts
                name-value argument, you can define a detailedCosts output
                argument to generate per-asset transaction costs when the backtest runs.
The EngineDataList name-value argument supports a string
                    "TransactionCostHistory" for reporting per-asset transaction
                costs charged for each day in the backtest when you use runBacktest.
The EngineDataList name-value argument supports information
                for CashAssets and DebtAssets. The
                    CashAssets and DebtAssets are defined as
                name-value arguments when using runBacktest.
The backtestStrategy name-value argument for
                    UserData supports strategy-specific user data for use in
                the user-defined function handles for the rebalance function. Also the
                    backtestStrategy name-value argument for
                    EngineDataList enables you to specify the set of optional
                backtest state data that a strategy needs in the user-defined function
                handles.
The backtestStrategy object supports name-value arguments for
                    ManagementFee, ManagementFeeSchedule,
                    PerformanceFee, PerformanceHurdle, and
                    PerformanceFeeSchedule.
The backtestStrategy object supports NaNs in
                the assetPrices timetable and NaNs and
                    <missing> in the signalData
                timetable.
See Also
runBacktest | summary | backtestEngine | equityCurve | timetable | duration | calendarDuration
Topics
- Backtest Investment Strategies Using Financial Toolbox
- Backtest Investment Strategies with Trading Signals
- Backtest Investment Strategies Using datetime and calendarDuration
- Backtest Using Risk-Based Equity Indexation
- Backtest with Brinson Attribution to Evaluate Portfolio Performance
- runBacktest Processing Steps
MATLAB Command
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.
Select a Web Site
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: .
You can also select a web site from the following list
How to Get Best Site Performance
Select the China site (in Chinese or English) for best site performance. Other MathWorks country sites are not optimized for visits from your location.
Americas
- América Latina (Español)
- Canada (English)
- United States (English)
Europe
- 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)