summary
Description
generates a table of metrics to summarize the backtest. Each row of the table is a
calculated metric and each column represents a strategy. You must run the summary function
only after running the summaryTable
= summary(backtester
)runBacktest
function.
Examples
Examining Backtest Results
The MATLAB® backtesting engine runs backtests of portfolio investment strategies over time series of asset price data. You can use summary
to compare multiple strategies over the same market scenario. This example shows how to examine the results of a backtest with two strategies.
Load Data
Load one year of stock price data. For readability, this example uses a subset of the DJIA stocks.
% Read table of daily adjusted close prices for 2006 DJIA stocks T = readtable('dowPortfolio.xlsx'); % Prune the table to include only the dates and selected stocks timeColumn = "Dates"; assetSymbols = ["BA", "CAT", "DIS", "GE", "IBM", "MCD", "MSFT"]; T = T(:,[timeColumn assetSymbols]); % Convert to timetable pricesTT = table2timetable(T,'RowTimes','Dates'); % View the final asset price timetable head(pricesTT)
Dates BA CAT DIS GE IBM MCD MSFT ___________ _____ _____ _____ _____ _____ _____ _____ 03-Jan-2006 68.63 55.86 24.18 33.6 80.13 32.72 26.19 04-Jan-2006 69.34 57.29 23.77 33.56 80.03 33.01 26.32 05-Jan-2006 68.53 57.29 24.19 33.47 80.56 33.05 26.34 06-Jan-2006 67.57 58.43 24.52 33.7 82.96 33.25 26.26 09-Jan-2006 67.01 59.49 24.78 33.61 81.76 33.88 26.21 10-Jan-2006 67.33 59.25 25.09 33.43 82.1 33.91 26.35 11-Jan-2006 68.3 59.28 25.33 33.66 82.19 34.5 26.63 12-Jan-2006 67.9 60.13 25.41 33.25 81.61 33.96 26.48
The inverse variance strategy requires some price history to initialize, so you can allocate a portion of the data to use for setting initial weights. By doing this, you can "warm start" the backtest.
warmupRange = 1:20; testRange = 21:height(pricesTT);
Create Strategies
Define an investment strategy by using the backtestStrategy
function. This example builds two strategies:
Equal weighted
Inverse variance
This example does not provide details on how to build the strategies. For more information on creating strategies, see backtestStrategy
. The strategy rebalance functions are implemented in the Rebalance Functions section.
% Create the strategies ewInitialWeights = equalWeightFcn([],pricesTT(warmupRange,:)); ewStrategy = backtestStrategy("EqualWeighted",@equalWeightFcn, ... 'RebalanceFrequency',20, ... 'TransactionCosts',[0.0025 0.005], ... 'LookbackWindow',0, ... 'InitialWeights',ewInitialWeights)
ewStrategy = backtestStrategy with properties: Name: "EqualWeighted" RebalanceFcn: @equalWeightFcn RebalanceFrequency: 20 TransactionCosts: [0.0025 0.0050] LookbackWindow: 0 InitialWeights: [0.1429 0.1429 0.1429 0.1429 0.1429 0.1429 0.1429] ManagementFee: 0 ManagementFeeSchedule: 1y PerformanceFee: 0 PerformanceFeeSchedule: 1y PerformanceHurdle: 0 UserData: [0x0 struct] EngineDataList: [0x0 string]
ivInitialWeights = inverseVarianceFcn([],pricesTT(warmupRange,:)); ivStrategy = backtestStrategy("InverseVariance",@inverseVarianceFcn, ... 'RebalanceFrequency',20, ... 'TransactionCosts',[0.0025 0.005], ... 'InitialWeights',ivInitialWeights)
ivStrategy = backtestStrategy with properties: Name: "InverseVariance" RebalanceFcn: @inverseVarianceFcn RebalanceFrequency: 20 TransactionCosts: [0.0025 0.0050] LookbackWindow: [0 Inf] InitialWeights: [0.1401 0.0682 0.0795 0.2187 0.1900 0.1875 0.1160] ManagementFee: 0 ManagementFeeSchedule: 1y PerformanceFee: 0 PerformanceFeeSchedule: 1y PerformanceHurdle: 0 UserData: [0x0 struct] EngineDataList: [0x0 string]
% Aggregate the strategies into an array
strategies = [ewStrategy ivStrategy];
Run Backtest
Create a backtesting engine and run a backtest over a year of stock data. For more information on creating backtesting engines, see backtestEngine
. The software initializes several properties of the backtestEngine
object to empty. These read-only properties are populated by the engine after you run the backtest.
% Create the backtesting engine using the default settings
backtester = backtestEngine(strategies)
backtester = backtestEngine with properties: Strategies: [1x2 backtestStrategy] RiskFreeRate: 0 CashBorrowRate: 0 RatesConvention: "Annualized" Basis: 0 InitialPortfolioValue: 10000 DateAdjustment: "Previous" PayExpensesFromCash: 0 NumAssets: [] Returns: [] Positions: [] Turnover: [] BuyCost: [] SellCost: [] TransactionCosts: [] Fees: []
Run the backtest using runBacktest
.
% Run the backtest
backtester = runBacktest(backtester,pricesTT(testRange,:));
Examine Summary Results
The summary
function uses the results of the backtest and returns a table of high-level results from the backtest.
s1 = summary(backtester)
s1=9×2 table
EqualWeighted InverseVariance
_____________ _______________
TotalReturn 0.17567 0.17155
SharpeRatio 0.097946 0.10213
Volatility 0.0074876 0.0069961
AverageTurnover 0.0007014 0.0024246
MaxTurnover 0.021107 0.097472
AverageReturn 0.00073178 0.00071296
MaxDrawdown 0.097647 0.096299
AverageBuyCost 0.018532 0.061913
AverageSellCost 0.037064 0.12383
Each row of the table output is a measurement of the performance of a strategy. Each strategy occupies a column. The summary
function reports on the following metrics:
TotalReturn
— The nonannulaized total return of the strategy, inclusive of fees, over the full backtest period.SharpeRatio
— The nonannualized Sharpe ratio of each strategy over the backtest. For more information, seesharpe
.Volatility
— The nonannualized standard deviation of per-time-step strategy returns.AverageTurnover
— The average per-time-step portfolio turnover, expressed as a decimal percentage.MaxTurnover
— The maximum portfolio turnover in a single rebalance, expressed as a decimal percentage.AverageReturn
—The arithmetic mean of the per-time step portfolio returns.MaxDrawdown
— The maximum drawdown of the portfolio, expressed as a decimal percentage. For more information, seemaxdrawdown
.AverageBuyCost
— The average per-time-step transaction costs the portfolio incurred for asset purchases.AverageSellCost
— The average per-time-step transaction costs the portfolio incurred for asset sales.
Sometimes it is useful to transpose the summary
table when plotting the metrics of different strategies.
s2 = rows2vars(s1);
s2.Properties.VariableNames{1} = 'StrategyName'
s2=2×10 table
StrategyName TotalReturn SharpeRatio Volatility AverageTurnover MaxTurnover AverageReturn MaxDrawdown AverageBuyCost AverageSellCost
___________________ ___________ ___________ __________ _______________ ___________ _____________ ___________ ______________ _______________
{'EqualWeighted' } 0.17567 0.097946 0.0074876 0.0007014 0.021107 0.00073178 0.097647 0.018532 0.037064
{'InverseVariance'} 0.17155 0.10213 0.0069961 0.0024246 0.097472 0.00071296 0.096299 0.061913 0.12383
bar(s2.AverageTurnover) title('Average Turnover') ylabel('Average Turnover (%)') set(gca,'xticklabel',s2.StrategyName)
Examine Detailed Results
After you run the backtest, the backtestEngine
object updates the read-only fields with the detailed results of the backtest. The Returns
, Positions
, Turnover
, BuyCost
, SellCost
, and Fees
properties each contain a timetable of results. Since this example uses daily price data in the backtest, these timetables hold daily results.
backtester
backtester = backtestEngine with properties: Strategies: [1x2 backtestStrategy] RiskFreeRate: 0 CashBorrowRate: 0 RatesConvention: "Annualized" Basis: 0 InitialPortfolioValue: 10000 DateAdjustment: "Previous" PayExpensesFromCash: 0 NumAssets: 7 Returns: [230x2 timetable] Positions: [1x1 struct] Turnover: [230x2 timetable] BuyCost: [230x2 timetable] SellCost: [230x2 timetable] TransactionCosts: [1x1 struct] Fees: [1x1 struct]
Returns
The Returns
property holds a timetable of strategy (simple) returns for each time step. These returns are inclusive of all transaction fees.
head(backtester.Returns)
Time EqualWeighted InverseVariance ___________ _____________ _______________ 02-Feb-2006 -0.007553 -0.0070957 03-Feb-2006 -0.0037771 -0.003327 06-Feb-2006 -0.0010094 -0.0014312 07-Feb-2006 0.0053284 0.0020578 08-Feb-2006 0.0099755 0.0095781 09-Feb-2006 -0.0026871 -0.0014999 10-Feb-2006 0.0048374 0.0059589 13-Feb-2006 -0.0056868 -0.0051232
binedges = -0.025:0.0025:0.025; h1 = histogram(backtester.Returns.EqualWeighted,'BinEdges',binedges); hold on histogram(backtester.Returns.InverseVariance,'BinEdges',binedges); hold off title('Distribution of Daily Returns') legend([strategies.Name]);
Positions
The Positions
property holds a structure of timetables, one per strategy.
backtester.Positions
ans = struct with fields:
EqualWeighted: [231x8 timetable]
InverseVariance: [231x8 timetable]
The Positions
timetable of each strategy holds the per-time-step positions for each asset as well as the Cash
asset (which earns the risk-free rate). The Positions
timetables contain one more row than the other results timetables because the Positions
timetables include initial positions of the strategy as their first row. You can consider the initial positions as the Time
= 0
portfolio positions. In this example, the Positions
timetables start with February 1, while the others start on February 2.
head(backtester.Positions.InverseVariance)
Time Cash BA CAT DIS GE IBM MCD MSFT ___________ ___________ ______ ______ ______ ______ ______ ______ ______ 01-Feb-2006 0 1401.2 682.17 795.14 2186.8 1900.1 1874.9 1159.8 02-Feb-2006 0 1402.8 673.74 789.74 2170.8 1883.5 1863.6 1145 03-Feb-2006 0 1386.5 671.2 787.2 2167.3 1854.3 1890.5 1139 06-Feb-2006 1.0971e-12 1391.9 676.78 785.62 2161.1 1843.6 1899.1 1123.8 07-Feb-2006 0 1400 661.66 840.23 2131.9 1851.6 1902.3 1114.5 08-Feb-2006 -2.2198e-12 1409.8 677.9 846.58 2160.4 1878.2 1911 1113.2 09-Feb-2006 -2.2165e-12 1414.8 674.35 840.87 2172.2 1869 1908.3 1102.6 10-Feb-2006 0 1425.1 677.29 839.6 2195.8 1890.6 1909.3 1103.9
% Plot the change of asset allocation over time t = backtester.Positions.InverseVariance.Time; positions = backtester.Positions.InverseVariance.Variables; h = area(t,positions); title('Inverse Variance Positions'); xlabel('Date'); ylabel('Asset Positions'); datetick('x','mm/dd','keepticks'); ylim([0 12000]) xlim([t(1) t(end)]) cm = parula(numel(h)); for i = 1:numel(h) set(h(i),'FaceColor',cm(i,:)); end
Turnover
The Turnover
timetable holds the per-time-step portfolio turnover.
head(backtester.Turnover)
Time EqualWeighted InverseVariance ___________ _____________ _______________ 02-Feb-2006 0 0 03-Feb-2006 0 0 06-Feb-2006 0 0 07-Feb-2006 0 0 08-Feb-2006 0 0 09-Feb-2006 0 0 10-Feb-2006 0 0 13-Feb-2006 0 0
Depending on your rebalance frequency, the Turnover
table can contain mostly zeros. Removing these zeros when you visualize the portfolio turnover is useful.
nonZeroIdx = sum(backtester.Turnover.Variables,2) > 0; to = backtester.Turnover(nonZeroIdx,:); plot(to.Time,to.EqualWeighted,'-o',to.Time,to.InverseVariance,'-x',... 'LineWidth',2,'MarkerSize',5); legend([strategies.Name]); title('Portfolio Turnover'); ylabel('Turnover (%)');
BuyCost and SellCost
The BuyCost
and SellCost
timetables hold the per-time-step transaction fees for each type of transaction, purchases, and sales.
totalCost = sum(backtester.BuyCost{:,:}) + sum(backtester.SellCost{:,:}); bar(totalCost); title('Total Transaction Costs'); ylabel('$') set(gca,'xticklabel',[strategies.Name])
Generate Equity Curve
Use equityCurve
to plot the equity curve for the equal weighted and inverse variance strategies.
equityCurve(backtester)
Rebalance Functions
This section contains the implementation of the strategy rebalance functions. For more information on creating strategies and writing rebalance functions, see backtestStrategy
.
function new_weights = equalWeightFcn(current_weights, pricesTT) %#ok<INUSL> % Equal weighted portfolio allocation nAssets = size(pricesTT, 2); new_weights = ones(1,nAssets); new_weights = new_weights / sum(new_weights); end
function new_weights = inverseVarianceFcn(current_weights, pricesTT) %#ok<INUSL> % Inverse-variance portfolio allocation assetReturns = tick2ret(pricesTT); assetCov = cov(assetReturns{:,:}); new_weights = 1 ./ diag(assetCov); new_weights = new_weights / sum(new_weights); end
Input Arguments
backtester
— Backtesting engine
backtestEngine
object
Backtesting engine, specified as a backtestEngine
object. Use
backtestEngine
to create the
backtesting engine and then use runBacktest
to run a
backtest.
Data Types: object
Output Arguments
summaryTable
— Metrics summarizing backtest
table
Metrics summarizing the backtest, returned as a table where each row of the table is a calculated metric and each column represents a strategy. The reported metrics are as follows:
TotalReturn
— The total return of the strategy over the entire backtestSharpeRatio
— The Sharpe ratio for each strategyVolatility
— The volatility of each strategy over the backtestAverageTurnover
— Average turnover per-time-step as a decimal percentMaxTurnover
— Maximum turnover in a single time stepAverageReturn
— Average return per-time-stepMaxDrawdown
— Maximum portfolio drawdown as a decimal percentAverageBuyCost
— Average per-time-step transaction costs for asset purchasesAverageSellCost
— Average per-time-step transaction costs for asset sales
Version History
Introduced in R2020b
See Also
backtestStrategy
| backtestEngine
| runBacktest
| equityCurve
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)
Asia Pacific
- Australia (English)
- India (English)
- New Zealand (English)
- 中国
- 日本Japanese (日本語)
- 한국Korean (한국어)