Запуск backtest на одной или нескольких стратегиях
выполнение бэктеста по расписанию скорректированных данных о ценах активов.backtester
= runBacktest(backtester
,pricesTT
)
runBacktest
инициализирует каждую стратегию, ранее определенную с помощью backtestStrategy
на InitialPortfolioValue
а затем начинает обработку расписания данных о ценах (pricesTT
) следующим образом:
На каждом временном шаге runBacktest
функция применяет возвраты активов к позициям портфеля стратегии.
The runBacktest
функция определяет, какие стратегии для ребаланса основаны на RebalanceFrequency
свойство backtestStrategy
объекты.
Для стратегий, которые нуждаются в ребалансировке, runBacktest
функция вызывает их функции ребаланса с помощью скользящего окна данных о ценах основных средств на основе LookbackWindow
свойство каждого backtestStrategy
.
Транзакционные затраты рассчитываются и начисляются на основе изменений в позициях основных средств и TransactionCosts
свойство каждого backtestStrategy
объект.
После завершения бэктеста результаты сохраняются в нескольких свойствах backtestEngine
объект.
запуск бэктеста с использованием скорректированных данных о цене активов и сигнальных данных. Когда вы задаете расписание данных сигнала (backtester
= runBacktest(backtester
,pricesTT
,signalTT
)signalTT
), затем runBacktest
функция запускает backtest и дополнительно передает скользящее окно данных сигнала в функцию ребаланса каждой стратегии во время шага ребаланса.
задает опции с использованием одного или нескольких необязательных аргументов пары "имя-значение" в дополнение к входным параметрам в предыдущем синтаксисе. Для примера, backtester
= runBacktest(___,Name,Value
)backtester = runBacktest(backtester,assetPrices,'Start',50,'End',100)
.
Механизм обратного тестирования MATLAB ® запускает бэктесты стратегий портфельных инвестиций во timeseries данных о ценах активов. После создания набора стратегий backtest с использованием backtestStrategy
и backtest engine, использующий 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
Создайте стратегию
В этом вступительном примере протестируйте равную взвешенную инвестиционную стратегию. Эта стратегия инвестирует равный фрагмент доступного капитала в каждый актив. В этом примере описываются подробные сведения о том, как создать стратегии backtest. Для получения дополнительной информации о создании стратегий backtest смотрите 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]
Запуск бэктеста
Создайте механизм обратного тестирования и запустите бэктест за год хранения данных. Для получения дополнительной информации о создании узлов 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]
Бэктест- Сводные данные
Используйте 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 начинается. Начальные веса для каждой стратегии, размер окна поиска стратегии и любое потенциальное разделение набора данных на разделы обучения и проверки влияют на результаты бэктеста. В этом примере показано, как использовать 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
Создайте стратегию
В этом примере описывается стратегия «обратного отклонения». Функция обратного отклонения баланса реализована в разделе Local Functions. Для получения дополнительной информации о создании стратегий backtest смотрите backtestStrategy
. Стратегия обратного отклонения использует ковариацию возвратов основных средств для принятия решений о распределении основных средств. The 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 и исследуйте результаты
Создайте механизм обратного тестирования и запустите бэктест за год хранения данных. Для получения дополнительной информации о создании узлов backtest смотрите backtestEngine
.
% Create the backtest engine. backtester = backtestEngine(ivStrategy); % Run the backtest. backtester = runBacktest(backtester,pricesTT);
Используйте assetAreaPlot
helper function, заданная в разделе Локальные функции этого примера, чтобы отобразить изменение в распределении активов в течение бэктеста.
assetAreaPlot(backtester,"InverseVariance")
Заметьте, что стратегия обратного отклонения начинается все наличными и остается в этом состоянии около 2,5 месяцев. Это потому, что backtestStrategy
объект не имеет заданного набора начальных весов, которые вы задаете используя InitialPortfolioValue
аргумент пары "имя-значение". Стратегия обратного отклонения требует 30 дней конечной ценовой истории актива перед ребалансировкой. Можно использовать printRebalanceTable
helper, заданная в разделе Локальные функции, для отображения расписания ребаланса.
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% в backtest.
Бэктест теплого запуска
Возможно «теплое начало» бэктеста. Теплый старт означает, что самые задние результаты отражают эффективность стратегии в рыночных условиях, отраженные в расписании цен. Чтобы начать, установите начальные веса стратегии, чтобы избежать запуска всех наличными.
Стратегия обратного отклонения требует 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 марта, чтобы заполнить допустимое окно поиска. Однако более ранние данные недоступны для бэктеста, поскольку бэктест выполнялся с использованием только тестового раздела.
Использование Start
и End
Параметры для runBacktest
Идеальный рабочий процесс в этой ситуации состоит в том, чтобы опустить область значений данных разогрева из backtest, чтобы избежать интерполяционного смещения, но включить данные разогрева в историю цен, чтобы иметь возможность заполнить интерполяционное окно стратегии со всеми доступными данными истории цен. Вы можете сделать это при помощи 'Start'
параметр для runBacktest
функция.
The 'Start'
и 'End'
аргументы пары "имя-значение" для runBacktest
позволяет вам начать и завершить бэктест в определенные даты. Можно задать 'Start'
и 'End'
как строки расписания цен или как значения datetime (см. документацию для runBacktest
функция для получения дополнительной информации). The 'Start'
аргумент позволяет 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")
Просмотр новой таблицы ребаланса с новой '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
при оценке эффективности стратегии в 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
backtester
- Механизм обратного тестированияbacktestEngine
объектМеханизм обратного тестирования, заданный как backtestEngine
объект. Использование backtestEngine
чтобы создать backtester
объект.
Типы данных: object
pricesTT
- Цены активовЦены активов, указанные как расписание цен активов, которые backtestEngine
использует для обратной проверки стратегий. Каждый столбец расписания цен должен содержать timeseries цен для актива. Исторические цены активов должны быть скорректированы с учетом разделений и дивидендов.
Типы данных: timetable
signalTT
- Сигнальные данные(Необязательно) Данные сигнала, заданные как расписание торговых сигналов, которые используются стратегиями для принятия торговых решений. signalTT
опционально. Если предусмотрено, backtestEngine
вызывает функции восстановления баланса стратегии как с данными о цене основного средства, так и с данными о сигнале. The signalTT
расписание должно иметь ту же временную размерность, что и pricesTT
timetable.
Типы данных: timetable
Задайте необязательные разделенные разделенными запятой парами Name,Value
аргументы. Name
- имя аргумента и Value
- соответствующее значение. Name
должны находиться внутри кавычек. Можно задать несколько аргументов в виде пар имен и значений в любом порядке Name1,Value1,...,NameN,ValueN
.
backtester = runBacktest(backtester,assetPrices,'Start',50,'End',100)
'Start'
- Временной шаг для запуска обратного теста1
(по умолчанию) | целое число | datetimeВременной шаг для запуска бэктеста, заданный как разделенная разделенными запятой парами, состоящая из 'Start'
и скаляр целое число или datetime.
Если целое число, Start
время относится к строке в pricesTT
timetable, где начинается бэктест.
Если a datetime
объект, backtest начнется в первый раз в расписании цен, которое происходит на или после 'Start'
параметр. Бэктест закончится в последний раз в расписании цен, которое происходит на или перед 'End'
параметр. The 'Start'
и 'End'
параметры устанавливают контур данных, включенных в бэктест.
Типы данных: double
| datetime
'End'
- Временной шаг к завершению обратного тестаpricesTT
timetable (по умолчанию) | целое число | datetimeВременной шаг для завершения обратного теста, заданный как разделенная разделенными запятой парами, состоящая из 'End'
и скаляр целое число или datetime.
Если целое число, End
время относится к строке в pricesTT
расписание, где заканчивается бэктест.
Если a datetime
объект, TBD
Типы данных: double
| datetime
backtester
- Механизм обратного тестированияbacktestEngine
объектМеханизм обратного тестирования, возвращенный как обновленный backtestEngine
объект. После завершения обратного тестирования runBacktest
заполняет несколько свойств в backtestEngine
объект с результатами бэктеста. Результирующие результаты можно подвести при помощи summary
функция.
У вас есть измененная версия этого примера. Вы хотите открыть этот пример с вашими правками?
1. Если смысл перевода понятен, то лучше оставьте как есть и не придирайтесь к словам, синонимам и тому подобному. О вкусах не спорим.
2. Не дополняйте перевод комментариями “от себя”. В исправлении не должно появляться дополнительных смыслов и комментариев, отсутствующих в оригинале. Такие правки не получится интегрировать в алгоритме автоматического перевода.
3. Сохраняйте структуру оригинального текста - например, не разбивайте одно предложение на два.
4. Не имеет смысла однотипное исправление перевода какого-то термина во всех предложениях. Исправляйте только в одном месте. Когда Вашу правку одобрят, это исправление будет алгоритмически распространено и на другие части документации.
5. По иным вопросам, например если надо исправить заблокированное для перевода слово, обратитесь к редакторам через форму технической поддержки.