Выполнить обратный тест для одной или нескольких стратегий
выполняет обратный тест по графику скорректированных данных о цене основных средств.backtester = runBacktest(backtester,pricesTT)
runBacktest инициализирует каждую стратегию, ранее определенную с помощью backtestStrategy в InitialPortfolioValue а затем начинает обработку графика данных о ценах (pricesTT) следующим образом:
На каждом временном шаге runBacktest функция применяет возврат основных средств к позициям портфеля стратегии.
runBacktest определяет, какие стратегии для ребалансировки на основе RebalanceFrequency имущества backtestStrategy объекты.
Для стратегий, которые нуждаются в ребалансировке, runBacktest функция вызывает их функции ребалансировки с скользящим окном данных цены основного средства на основе LookbackWindow свойство каждого backtestStrategy.
Операционные затраты рассчитываются и начисляются на основе изменений в позициях основных средств и TransactionCosts свойство каждого backtestStrategy объект.
После завершения обратного тестирования результаты сохраняются в нескольких свойствах 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")
Обратите внимание, что стратегия обратного отклонения начинается в денежном выражении и остается в этом состоянии около 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")
Однако, если посмотреть на таблицу ребалансировки, можно увидеть, что стратегия все еще «пропустила» первую дату ребалансировки. При выполнении обратного теста по тестовому диапазону набора данных первая дата ребалансировки - 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")
Просмотр новой таблицы ребалансировки с новым '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
backtester - Двигатель обратного тестированияbacktestEngine объектМеханизм обратного тестирования, указанный как backtestEngine объект. Использовать backtestEngine для создания backtester объект.
Типы данных: object
pricesTT - Цены основных средствЦены активов, указанные как график цен активов, которые backtestEngine использует для обратного тестирования стратегий. Каждый столбец графика цен должен содержать временные ряды цен на актив. Цены активов за прошлые периоды должны быть скорректированы с учетом разделения и дивидендов.
Типы данных: timetable
signalTT - Сигнальные данные(Необязательно) Сигнальные данные, указанные как график торговых сигналов, которые стратегии используют для принятия торговых решений. signalTT является необязательным. Если предусмотрено, backtestEngine вызывает функции перебалансировки стратегии как с данными цены основного средства, так и с данными сигнала. signalTT расписание должно иметь то же измерение времени, что и pricesTT расписание.
Типы данных: timetable
Укажите дополнительные пары, разделенные запятыми Name,Value аргументы. Name является именем аргумента и Value - соответствующее значение. Name должен отображаться внутри кавычек. Можно указать несколько аргументов пары имен и значений в любом порядке как Name1,Value1,...,NameN,ValueN.
backtester = runBacktest(backtester,assetPrices,'Start',50,'End',100)'Start' - Временной шаг для начала обратного тестирования1
(по умолчанию) | integer | datetimeШаг времени для запуска обратного теста, указанного как пара, разделенная запятыми, состоящая из 'Start' и скалярное целое число или datetime.
Если целое число, Start время относится к строке в pricesTT расписание, где начинается обратный тест.
Если datetime объект, бэктест начнется в первый раз в графике цен, который происходит на или после 'Start' параметр. Бэктест закончится в последний раз в графике цен, который происходит на или до 'End' параметр. 'Start' и 'End' параметры задают границу данных, включенных в обратный тест.
Типы данных: double | datetime
'End' - Временной шаг для завершения бэктестаpricesTT расписание (по умолчанию) | целое | datetimebacktester - Двигатель обратного тестированияbacktestEngine объектМодуль обратного тестирования, возвращен как обновленный backtestEngine объект. После завершения тестирования, runBacktest заполняет несколько свойств в backtestEngine объект с результатами бэктеста. Результаты можно суммировать с помощью summary функция.
Имеется измененная версия этого примера. Открыть этот пример с помощью изменений?
1. Если смысл перевода понятен, то лучше оставьте как есть и не придирайтесь к словам, синонимам и тому подобному. О вкусах не спорим.
2. Не дополняйте перевод комментариями “от себя”. В исправлении не должно появляться дополнительных смыслов и комментариев, отсутствующих в оригинале. Такие правки не получится интегрировать в алгоритме автоматического перевода.
3. Сохраняйте структуру оригинального текста - например, не разбивайте одно предложение на два.
4. Не имеет смысла однотипное исправление перевода какого-то термина во всех предложениях. Исправляйте только в одном месте. Когда Вашу правку одобрят, это исправление будет алгоритмически распространено и на другие части документации.
5. По иным вопросам, например если надо исправить заблокированное для перевода слово, обратитесь к редакторам через форму технической поддержки.