exponenta event banner

runBacktest

Выполнить обратный тест для одной или нескольких стратегий

Описание

пример

backtester = runBacktest(backtester,pricesTT) выполняет обратный тест по графику скорректированных данных о цене основных средств.

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

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

  2. runBacktest определяет, какие стратегии для ребалансировки на основе RebalanceFrequency имущества backtestStrategy объекты.

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

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

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

пример

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

пример

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

Примеры

свернуть все

Механизм обратного тестирования MATLAB ® выполняет обратные тесты стратегий портфельных инвестиций по временным интервалам данных о ценах на активы. После создания набора стратегий обратного тестирования с использованиемbacktestStrategy и механизм обратного тестирования с использованием backtestEngine, runBacktest выполняет обратный тест. В этом примере показано, как использовать 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

Создание стратегии

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

Установите RebalanceFrequency для ребалансировки портфеля каждые 60 дней. В этом примере для перебалансировки не используется окно обратного просмотра.

% 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]

Выполнить обратное тестирование

Создайте механизм тестирования и выполните тестирование в течение года данных запаса. Дополнительные сведения о создании механизмов обратного тестирования см. в разделе 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]

Сводка по обратному тестированию

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

% 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  

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

Загрузить данные

Загрузите данные цены запаса за один год. Для удобочитаемости в этом примере используется только подмножество запасов 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

Создание стратегии

В этом примере выполняется обратное тестирование стратегии «обратной дисперсии». Функция ребалансировки обратной дисперсии реализована в разделе Локальные функции. Дополнительные сведения о создании стратегий обратного тестирования см. в разделе 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]

Выполнить обратное тестирование и проверить результаты

Создайте механизм тестирования и выполните тестирование в течение года данных запаса. Дополнительные сведения о создании механизмов обратного тестирования см. в разделе backtestEngine.

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

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

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

assetAreaPlot(backtester,"InverseVariance")

Figure contains an axes. The axes 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 вспомогательная функция, определенная в разделе Локальные функции, для отображения расписания перебалансировки.

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 февраля, но в стратегии недостаточно истории цен для заполнения действительного окна обратного просмотра (минимум 30 дней), поэтому перебалансировки не происходит. Следующая дата ребаланса - 16 марта, полные 50 дней в заднем тестировании.

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

Обратный тест теплого пуска

Возможно «теплое начало» самого заднего. Теплый старт означает, что результаты обратного тестирования отражают эффективность стратегии в рыночных условиях, отраженных в графике цен. Чтобы начать, установите начальные веса стратегии, чтобы не начинать все наличными.

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

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);

Используйте раздел разогрева для установки начальных весов стратегии обратной дисперсии. Таким образом, можно начать бэктест с уже «запущенной» стратегии и избежать начальных недель, проведенных в кассовой позиции.

% 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,:));

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

% 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,:));

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

assetAreaPlot(backtester,"InverseVariance")

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

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

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

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 марта, чтобы заполнить действительное окно обратного просмотра. Тем не менее, более ранние данные недоступны для обработчика backtest, так как backtest был запущен с использованием только тестового раздела.

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

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

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

Повторно запустите обратный тест с помощью '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. The axes 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 марта), и обратный тест «начался тепло». Используя исходный набор данных, первый день данных остается 3 января, и 'Start' позволяет переместить дату начала обратного теста вперед, чтобы избежать диапазона прогрева.

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

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

Функция перебалансировки стратегии реализуется следующим образом. Дополнительные сведения о создании стратегий и записи функций ребалансировки см. в разделе 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

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

свернуть все

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

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

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

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

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

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

Аргументы пары «имя-значение»

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

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

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

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

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

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

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

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

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

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

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

свернуть все

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

Представлен в R2020b