runBacktest

Запустите backtest на одной или нескольких стратегиях

Описание

пример

backtester = runBacktest(backtester,pricesTT) запускает backtest по расписанию настроенных данных цен активов.

runBacktest инициализирует каждую стратегию ранее заданное использование backtestStrategy к InitialPortfolioValue и затем начинает обрабатывать расписание ценовых данных (pricesTT) можно следующим образом:

  1. На каждом временном шаге, runBacktest функция применяется, актив возвращается к положениям портфеля стратегии.

  2. runBacktest функция определяет который стратегии изменять баланс на основе RebalanceFrequency свойство backtestStrategy объекты.

  3. Для стратегий, для которых нужно изменение баланса, runBacktest вызовы функции их восстановление равновесия функционируют с прокручивающимся окном данных цен активов на основе LookbackWindow свойство каждого backtestStrategy.

  4. Операционные издержки вычислены и заряжены на основе изменений в положениях актива и TransactionCosts свойство каждого backtestStrategy объект.

  5. После того, как backtest завершен, результаты хранятся в нескольких свойствах backtestEngine объект.

пример

backtester = runBacktest(backtester,pricesTT,signalTT) запустите backtest использование настроенных данных цен активов и данных сигнала. Когда вы задаете расписание данных сигнала (signalTT), затем runBacktest функционируйте запускает backtest и дополнительно передает прокручивающееся окно данных сигнала к функции восстановления равновесия каждой стратегии во время шага восстановления равновесия.

пример

backtester = runBacktest(___,Name,Value) задает опции с помощью одного или нескольких дополнительных аргументов пары "имя-значение" в дополнение к входным параметрам в предыдущем синтаксисе. Например, backtester = runBacktest(backtester,assetPrices,'Start',50,'End',100).

Примеры

свернуть все

MATLAB® backtesting механизм запускает backtests стратегий портфельных инвестиций по timeseries данных цен активов. После создания набора backtest стратегий с помощью backtestStrategy и backtest механизм с помощью backtestEngine, runBacktest функция выполняет backtest. Этот пример иллюстрирует, как использовать runBacktest функционируйте, чтобы протестировать инвестиционные стратегии.

Загрузка данных

Загрузите один год данных о курсе акций. Для удобочитаемости этот пример только использует подмножество запасов DJIA.

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

% Prune the table on only hold 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)
ans=8×7 timetable
       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

Создайте стратегию

В этом вводном примере протестируйте равную взвешенную инвестиционную стратегию. Эта стратегия инвестирует равный фрагмент ликвидного капитала в каждый актив. Этот пример действительно описывает детали о том, как создают backtest стратегии. Для получения дополнительной информации о создании backtest стратегии, смотрите backtestStrategy.

Установите RebalanceFrequency восстанавливать равновесие портфеля каждые 60 дней. Этот пример не использует lookback окно, чтобы изменять баланс.

% Create the strategy
numAssets = size(pricesTT,2);
equalWeightsVector = ones(1,numAssets) / numAssets;
equalWeightsRebalanceFcn = @(~,~) equalWeightsVector;

ewStrategy = backtestStrategy("EqualWeighted",equalWeightsRebalanceFcn,...
    'RebalanceFrequency',60,...
    'LookbackWindow',0,...
    'TransactionCosts',0.005,...
    'InitialWeights',equalWeightsVector)
ewStrategy = 
  backtestStrategy with properties:

                  Name: "EqualWeighted"
          RebalanceFcn: @(~,~)equalWeightsVector
    RebalanceFrequency: 60
      TransactionCosts: 0.0050
        LookbackWindow: 0
        InitialWeights: [0.1429 0.1429 0.1429 0.1429 0.1429 0.1429 0.1429]

Запустите Backtest

Создайте backtesting механизм и запустите backtest более чем год данных о запасе. Для получения дополнительной информации о создании backtest механизмы, смотрите backtestEngine.

% Create the backtest engine. The backtest engine properties that hold the
% results are initialized to empty.
backtester = backtestEngine(ewStrategy)
backtester = 
  backtestEngine with properties:

               Strategies: [1x1 backtestStrategy]
             RiskFreeRate: 0
           CashBorrowRate: 0
          RatesConvention: "Annualized"
                    Basis: 0
    InitialPortfolioValue: 10000
                NumAssets: []
                  Returns: []
                Positions: []
                 Turnover: []
                  BuyCost: []
                 SellCost: []

% Run the backtest. The empty properties are now populated with
% timetables of detailed backtest results.
backtester = runBacktest(backtester,pricesTT)
backtester = 
  backtestEngine with properties:

               Strategies: [1x1 backtestStrategy]
             RiskFreeRate: 0
           CashBorrowRate: 0
          RatesConvention: "Annualized"
                    Basis: 0
    InitialPortfolioValue: 10000
                NumAssets: 7
                  Returns: [250x1 timetable]
                Positions: [1x1 struct]
                 Turnover: [250x1 timetable]
                  BuyCost: [250x1 timetable]
                 SellCost: [250x1 timetable]

Сводные данные Backtest

Используйте summary функция, чтобы сгенерировать сводную таблицу результатов backtest.

% Examing results. The summary table shows several performance metrics.
summary(backtester)
ans=9×1 table
                       EqualWeighted
                       _____________

    TotalReturn            0.22943  
    SharpeRatio            0.11415  
    Volatility           0.0075013  
    AverageTurnover     0.00054232  
    MaxTurnover           0.038694  
    AverageReturn       0.00085456  
    MaxDrawdown           0.098905  
    AverageBuyCost        0.030193  
    AverageSellCost       0.030193  

При выполнении backtest в MATLAB® необходимо изучить то, что - начальные условия, когда backtest начинается. Начальные веса для каждой стратегии, размера стратегии lookback окно и любое потенциальное разделение набора данных в разделы обучения и тестирования влияют на результаты backtest. В этом примере показано, как использовать runBacktest функция с 'Start' и 'End' аргументы пары "имя-значение", которые взаимодействуют с 'LookbackWindow' и 'RebalanceFrequency' свойства backtestStrategy возразите против "горячего запуска" backtest.

Загрузка данных

Загрузите один год данных о курсе акций. Для удобочитаемости этот пример использует только подмножество запасов DJIA.

% 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)
ans=8×7 timetable
       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

Создайте стратегию

Этот пример backtests "обратное отклонение" стратегия. Обратная функция восстановления равновесия отклонения реализована в разделе Local Functions. Для получения дополнительной информации о создании backtest стратегии, смотрите backtestStrategy. Обратное использование стратегии отклонения ковариация актива возвращается, чтобы принять решения относительно распределения активов. LookbackWindow поскольку эта стратегия должна содержать по крайней мере 30 дней запаздывающих данных (приблизительно 6 недель), и самое большее, 60 дней (приблизительно 12 недель).

Установите RebalanceFrequency для backtestStrategy восстанавливать равновесие портфеля каждые 25 дней.

% Create the strategy
minLookback = 30;
maxLookback = 60;
ivStrategy = backtestStrategy("InverseVariance",@inverseVarianceFcn,...
    'RebalanceFrequency',25,...
    'LookbackWindow',[minLookback maxLookback],...
    'TransactionCosts',[0.0025 0.005])
ivStrategy = 
  backtestStrategy with properties:

                  Name: "InverseVariance"
          RebalanceFcn: @inverseVarianceFcn
    RebalanceFrequency: 25
      TransactionCosts: [0.0025 0.0050]
        LookbackWindow: [30 60]
        InitialWeights: [1x0 double]

Запустите Backtest и исследуйте результаты

Создайте backtesting механизм и запустите backtest более чем год данных о запасе. Для получения дополнительной информации о создании backtest механизмы, смотрите backtestEngine.

% Create the backtest engine.
backtester = backtestEngine(ivStrategy);

% Run the backtest.
backtester = runBacktest(backtester,pricesTT);

Используйте assetAreaPlot функция помощника, заданная в разделе Local Functions этого примера, чтобы отобразить изменение в распределении активов в течение backtest.

assetAreaPlot(backtester,"InverseVariance")

Figure contains an axes object. The axes object with title InverseVariance Positions contains 8 objects of type area. These objects represent Cash, BA, CAT, DIS, GE, IBM, MCD, MSFT.

Заметьте, что обратная стратегия отклонения начинает все наличными и остается в том состоянии в течение приблизительно 2,5 месяцев. Это вызвано тем, что backtestStrategy объект не имеет заданного набора начальных весов, которые вы задаете использование InitialPortfolioValue аргумент пары "имя-значение". Обратная стратегия отклонения требует 30 дней запаздывающей истории цен активов перед изменением баланса. Можно использовать printRebalanceTable функция помощника, заданная в разделе Local Functions, чтобы отобразить расписание восстановления равновесия.

printRebalanceTable(ivStrategy,pricesTT,minLookback);
    First Day of Data    Backtest Start Date    Minimum Days to Rebalance
    _________________    ___________________    _________________________

       03-Jan-2006           03-Jan-2006                   30            



    Rebalance Dates    Days of Available Price History    Enough Data to Rebalance
    _______________    _______________________________    ________________________

      08-Feb-2006                     26                           "No"           
      16-Mar-2006                     51                           "Yes"          
      21-Apr-2006                     76                           "Yes"          
      26-May-2006                    101                           "Yes"          
      03-Jul-2006                    126                           "Yes"          
      08-Aug-2006                    151                           "Yes"          
      13-Sep-2006                    176                           "Yes"          
      18-Oct-2006                    201                           "Yes"          
      22-Nov-2006                    226                           "Yes"          
      29-Dec-2006                    251                           "Yes"          

Первая дата восстановления равновесия происходит 8 февраля, но стратегия не имеет достаточного количества ценовой истории, чтобы заполнить допустимое lookback окно (минимум составляет 30 дней), таким образом, никакое восстановление равновесия не происходит. Следующая дата восстановления равновесия 16 марта, полные 50 дни в backtest.

Эта ситуация не идеальна, когда эти 50 дней, находясь во все-денежной позиции представляют приблизительно 20% общего количества backtest. Следовательно, когда backtesting отчеты механизма относительно эффективности стратегии (то есть, совокупный доход, отношение Шарпа, энергозависимость, и так далее), результаты не отражают "истинную" эффективность стратегии, потому что стратегия только начала принимать решения распределения активов только приблизительно 20% в backtest.

Горячий запуск Backtest

Это возможно к "горячему запуску" backtest. Горячий запуск означает, что результаты backtest отражают эффективность стратегии в состоянии рынка, отраженном в ценовом расписании. Чтобы запуститься, установите начальные веса стратегии постараться не запускать все наличными.

Обратная стратегия отклонения требует, чтобы 30 дней ценовой истории заполнили допустимое lookback окно, таким образом, можно разделить ценовой набор данных в два раздела, набор "прогрева" и "тестовый" набор.

warmupRange = 1:30;
% The 30th row is included in both ranges since the day 30 price is used
% to compute the day 31 returns.
testRange = 30:height(pricesTT);

Используйте раздел прогрева, чтобы установить начальные веса обратной стратегии отклонения. Путем выполнения так, можно начать backtest со стратегии, уже "запускающейся", и избежать начальных недель, проведенных в денежной позиции.

% Use the rebalance function to set the initial weights. This might
% or might not be possible for other strategies depending on the details of
% the strategy logic.
initWeights = inverseVarianceFcn([],pricesTT(warmupRange,:));

Обновите стратегию и повторно выполните backtest. Поскольку область значений прогрева используется, чтобы инициализировать обратную стратегию отклонения, необходимо не использовать эти данные из backtest, чтобы избежать предварительного смещения, или "наблюдения будущего", и к backtest только в "тестовой области значений".

% Set the initial weights on the strategy in the backtester. You can do this when you 
% create the strategy as well, using the 'InitialWeights' parameter.
backtester.Strategies(1).InitialWeights = initWeights;

% Rerun the backtest over the "test" range.
backtester = runBacktest(backtester,pricesTT(testRange,:));

Когда вы генерируете график области, вы видите, что проблемы, где стратегия находится наличными для первого фрагмента backtest, избегают.

assetAreaPlot(backtester,"InverseVariance")

Figure contains an axes object. The axes object with title InverseVariance Positions contains 8 objects of type area. These objects represent Cash, BA, CAT, DIS, GE, IBM, MCD, MSFT.

Однако, если вы смотрите на таблицу восстановления равновесия, вы видите, что стратегия все еще "пропустила" первую дату восстановления равновесия. Когда вы запускаете backtest в тестовой области значений набора данных, первая дата восстановления равновесия 22 марта. Это вызвано тем, что область значений прогрева не использована из ценовой истории, и стратегия имела только 26 дней в наличии истории в ту дату (меньше, чем минимальные 30 дней, требуемых для lookback окна). Поэтому восстановление равновесия 22 марта пропущено.

Чтобы избежать backtesting в области значений прогрева, область значений была удалена это из набора данных. Это означает новую backtest дату начала, и все последующие даты восстановления равновесия 30 дней спустя. Ценовые данные об истории, содержавшиеся в области значений прогрева, были полностью удалены, поэтому когда backtest механизм совершил нападки, первое восстановление равновесия датируются, ценовая история была недостаточна, чтобы изменять баланс.

printRebalanceTable(ivStrategy,pricesTT(testRange,:),minLookback);
    First Day of Data    Backtest Start Date    Minimum Days to Rebalance
    _________________    ___________________    _________________________

       14-Feb-2006           14-Feb-2006                   30            



    Rebalance Dates    Days of Available Price History    Enough Data to Rebalance
    _______________    _______________________________    ________________________

      22-Mar-2006                     26                           "No"           
      27-Apr-2006                     51                           "Yes"          
      02-Jun-2006                     76                           "Yes"          
      10-Jul-2006                    101                           "Yes"          
      14-Aug-2006                    126                           "Yes"          
      19-Sep-2006                    151                           "Yes"          
      24-Oct-2006                    176                           "Yes"          
      29-Nov-2006                    201                           "Yes"          

Этот сценарий также не правилен, поскольку исходное ценовое расписание (прогрев и тестовые разделы вместе) действительно имеет достаточно ценовой истории к 22 марта, чтобы заполнить допустимое lookback окно. Однако более ранние данные не доступны для backtest механизма, потому что backtest был запущен с помощью только тестовый раздел.

Используйте Start и End Параметры для runBacktest

Идеальный рабочий процесс в этой ситуации должен оба не использовать диапазон данных прогрева от backtest, чтобы избежать предварительного смещения, но включать данные прогрева в ценовую историю, чтобы смочь заполнить lookback окно стратегии со всеми доступными ценовыми данными об истории. Можно сделать так при помощи 'Start' параметр для runBacktest функция.

'Start' и 'End' аргументы пары "имя-значение" для runBacktest включите вам к началу и концу backtest в определенные даты. Можно задать 'Start' и 'End' как строки ценового расписания или как значения datetime (см. документацию для runBacktest функция для деталей). 'Start' аргумент позволяет backtest начаться в конкретную дату при предоставлении backtest доступа к механизму к полному набору данных.

Повторно выполните backtest использование 'Start' аргумент пары "имя-значение", а не только работа раздела исходного набора данных.

% Rerun the backtest starting on the last day of the warmup range.
startRow = warmupRange(end);
backtester = runBacktest(backtester,pricesTT,'Start',startRow);

Постройте новый график области актива.

assetAreaPlot(backtester,"InverseVariance")

Figure contains an axes object. The axes object with title InverseVariance Positions contains 8 objects of type area. These objects represent Cash, BA, CAT, DIS, GE, IBM, MCD, MSFT.

Просмотрите новую таблицу восстановления равновесия с новым 'Start' параметр.

printRebalanceTable(ivStrategy,pricesTT,minLookback,startRow);
    First Day of Data    Backtest Start Date    Minimum Days to Rebalance
    _________________    ___________________    _________________________

       03-Jan-2006           14-Feb-2006                   30            



    Rebalance Dates    Days of Available Price History    Enough Data to Rebalance
    _______________    _______________________________    ________________________

      22-Mar-2006                     55                           "Yes"          
      27-Apr-2006                     80                           "Yes"          
      02-Jun-2006                    105                           "Yes"          
      10-Jul-2006                    130                           "Yes"          
      14-Aug-2006                    155                           "Yes"          
      19-Sep-2006                    180                           "Yes"          
      24-Oct-2006                    205                           "Yes"          
      29-Nov-2006                    230                           "Yes"          

Обратная стратегия отклонения теперь имеет достаточно данных, чтобы изменять баланс в первую дату восстановления равновесия (22 марта), и backtest является "теплым, запустился". При помощи исходного набора данных первый день данных остается 3 января, и 'Start' параметр позволяет вам перемещать backtest дату начала вперед, чтобы избежать области значений прогрева.

Даже при том, что результаты не существенно отличаются, этот пример иллюстрирует взаимодействие между LookbackWindow and RebalanceFrequency аргументы пары "имя-значение" для backtestStrategy возразите и область значений данных, используемых в runBacktest когда вы оцениваете эффективность стратегии в backtest.

Локальные функции

Функция восстановления равновесия стратегии реализована можно следующим образом. Для получения дополнительной информации о создании стратегий и записи функций восстановления равновесия, смотрите backtestStrategy.

function new_weights = inverseVarianceFcn(current_weights, pricesTT) 
% Inverse-variance portfolio allocation.

assetReturns = tick2ret(pricesTT);
assetCov = cov(assetReturns{:,:});
new_weights = 1 ./ diag(assetCov);
new_weights = new_weights / sum(new_weights);

end

Этот помощник графики функций распределение активов как график области.

function assetAreaPlot(backtester,strategyName)

t = backtester.Positions.(strategyName).Time;
positions = backtester.Positions.(strategyName).Variables;
h = area(t,positions);
title(sprintf('%s Positions',strategyName));
xlabel('Date');
ylabel('Asset Positions');
datetick('x','mm/dd','keepticks');
xlim([t(1) t(end)])
oldylim = ylim;
ylim([0 oldylim(2)]);
cm = parula(numel(h));
for i = 1:numel(h)
    set(h(i),'FaceColor',cm(i,:));
end
legend(backtester.Positions.(strategyName).Properties.VariableNames)

end

Эта функция помощника генерирует таблицу дат восстановления равновесия наряду с доступной ценовой историей в каждую дату.

function printRebalanceTable(strategy,pricesTT,minLookback,startRow)

if nargin < 4
    startRow = 1;
end

allDates = pricesTT.(pricesTT.Properties.DimensionNames{1});
rebalanceDates = allDates(startRow:strategy.RebalanceFrequency:end);
[~,rebalanceIndices] = ismember(rebalanceDates,pricesTT.Dates);

disp(table(allDates(1),rebalanceDates(1),minLookback,'VariableNames',{'First Day of Data','Backtest Start Date','Minimum Days to Rebalance'}));
fprintf('\n\n');
numHistory = rebalanceIndices(2:end);
sufficient = repmat("No",size(numHistory));
sufficient(numHistory > minLookback) = "Yes";
disp(table(rebalanceDates(2:end),rebalanceIndices(2:end),sufficient,'VariableNames',{'Rebalance Dates','Days of Available Price History','Enough Data to Rebalance'}));

end

Входные параметры

свернуть все

Механизм Backtesting в виде backtestEngine объект. Используйте backtestEngine создать backtester объект.

Типы данных: object

Цены активов в виде расписания цен активов, что backtestEngine использование к backtest стратегии. Каждый столбец ценового расписания должен содержать timeseries цен на актив. Исторические цены активов должны быть настроены для разделений и дивидендов.

Типы данных: timetable

(Необязательно) Данные сигнала в виде расписания торговых сигналов, что использование стратегий, чтобы принять торговые решения. signalTT является дополнительным. Если обеспечено, backtestEngine вызывает функции восстановления равновесия стратегии и с данными цен активов и с данными сигнала. signalTT расписание должно иметь то же измерение времени как pricesTT расписание.

Типы данных: timetable

Аргументы name-value

Задайте дополнительные разделенные запятой пары Name,Value аргументы. Name имя аргумента и Value соответствующее значение. Name должен появиться в кавычках. Вы можете задать несколько аргументов в виде пар имен и значений в любом порядке, например: Name1, Value1, ..., NameN, ValueN.

Пример: backtester = runBacktest(backtester,assetPrices,'Start',50,'End',100)

Временной шаг, чтобы запустить backtest в виде разделенной запятой пары, состоящей из 'Start' и скалярное целое число или datetime.

Если целое число, Start время относится к строке в pricesTT расписание, где backtest начинается.

Если datetime объект, backtest начнется в первый раз в ценовом расписании, которое происходит на или после 'Start' параметр. backtest закончится на прошлом разе в ценовом расписании, которое происходит на или перед 'End' параметр. 'Start' и 'End' параметры устанавливают контур данных, которые включены в backtest.

Типы данных: double | datetime

Временной шаг, чтобы закончить backtest в виде разделенной запятой пары, состоящей из 'End' и скалярное целое число или datetime.

Если целое число, End время относится к строке в pricesTT расписание, где backtest заканчивается.

Если datetime объект, TBD

Типы данных: double | datetime

Выходные аргументы

свернуть все

Механизм Backtesting, возвращенный как обновленный backtestEngine объект. После того, как backtesting завершен, runBacktest заполняет несколько свойств в backtestEngine объект результатами backtest. Можно обобщить результаты при помощи summary функция.

Введенный в R2020b