Разрабатываем мультивалютный советник (Часть 7): Подбор группы с учётом форвард-периода

Yuriy Bykov | 5 апреля, 2024

Введение

Оптимизацию отдельных экземпляров торговых стратегий в предыдущих статьях мы проводили всегда на одном и том же временном периоде: с 2018 по 2022 годы. Выбор именно такого периода для оптимизации обусловлен тем, что с одной стороны, это достаточно длительный период за пять лет могли случиться разные события, влияющие на динамику цен торговых инструментов, а с другой стороны, он не очень большой, чтобы время одного прохода оставалось достаточно небольшим в пределах нескольких десятков секунд. Расположение выбранного периода так, чтобы до текущего момента времени оставалось ещё более года сделано как раз для того, чтобы можно было посмотреть, как будут вести себя экземпляры стратегий на том участке истории, который не использовался при оптимизации их параметров.

Штатный тестер MetaTrader 5 умеет проводить одиночные проходы и оптимизацию с учетом наличия так называемого форвард-периода. При его использовании тестер будет разбивать весь указанный период тестирования на две части основной период и форвард-период. При завершении основного периода все сделки будут закрываться и баланс торгового счёта будет приводиться в начальное состояние. Далее советник будет работать заново на форвард-периоде, и вся статистика, собираемая тестером, будет вычисляться по отдельности для основного периода и форвард-периода.

В области машинного обучения часто используют термины In-Sample и Out-Of-Sample (IS и OOS) для обозначения набора данных на которых происходит обучение моделей и проверка моделей. В нашей области основной период будет играть роль IS, а форвард-период OOS. Хотя стоит иметь в виду, что OOS это более широкое понятие, чем форвард-период: мы можем запустить тестирование советника без указания форвард-периода в тестере, но если период тестирования будет располагаться вне периода, на котором проводилась оптимизация, то это всё равно будет тестирование на OOS.

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


Форвард-период для готовых советников

Посмотрим на результаты, показываемые советниками из прошлой статьи для группы стратегий, отобранных вручную, и лучших групп стратегий, отобранных автоматизировано по максимальной прибыли. В качестве форвард-периода возьмём весь 2023 год. А три месяца 2024 года оставим пока про запас. Желательный для нас результат это прибыль на форвард-периоде примерно в пять раз меньшая, чем на основном периоде, так как форвард в пять раз короче основного периода. Просадка на форварде желательно должна быть примерно такой же или меньшей, чем на основном периоде.

Начнём с группы стратегий, отобранной вручную (советник BaselineExpert.mq5). На графике вертикальный участок синей линии чётко отделяет основной период от форвард-периода. На этой границе баланс счёта снова становится равным $10000. Часть графика, относящаяся к форвард-периоду, занимает только небольшую часть от всего графика. Если понадобится рассмотреть её подробнее, то можно запустить отдельный проход тестера только на том временном промежутке, который относится здесь к форвард-периоду.

Основной период

Форвард-период


Рис. 1. Результаты советника BaselineExpert.mq5 на основном и форвард-периоде


Но в данном случае и без отдельного прохода для форвард-периода хорошо видно, что результаты существенно хуже. Нельзя сказать, что это прямо означает слом стратегии. Просадка увеличилась с 10% до 12% и период восстановления затянулся более чем на полгода. Но это ещё не разворот тренда кривой баланса. Или уже разворот? В общем, как ни посмотри, результаты нуждаются в улучшении.

Посмотрим теперь на лучшую группу, отобранную без кластеризации наборов (советник OptGroupExpert.mq5).

Основной период

Форвард-период

Рис. 2. Результаты советника OptGroupExpert.mq5 на основном и форвард-периоде


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

Посмотрим ещё на результаты группы, отобранной с кластеризацией наборов параметров (советник OptGroupClusterExpert.mq5), хотя уже есть стойкое подозрение, что результаты будут примерно таким же, как и в двух предыдущих случаях.


Основной период


Форвард-период


Рис. 3. Результаты советника OptGroupClusterExpert.mq5 на основном и форвард-периоде


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

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


Результаты оптимизации с форвард-периодом

Итак, мы получили результаты оптимизации с форвард-периодом для советника OptGroupClusterExpert.mq5. В качестве файла с наборами параметров использовался файл с одним набором для каждого из 64 кластеров. Для первичного анализа результатов оптимизации нам, наверное, вполне будет достаточно обычного Excel. Выполним экспорт результатов оптимизации для основного и форвард-периода в XML-файлы и сохраним их в формате Excel:


Рис. 4. Исходные файлы с результатами оптимизации на основном (IS) и форвард-периоде (OOS)


В файле для форвард-периода есть столбец Back Result, который содержит результат, полученный на основном периоде при таком же наборе оптимизируемых параметров. Это хорошо, но нам бы хотелось видеть рядом и все остальные характеристики из основного периода. Поэтому выполним объединение данных из этих двух таблиц в одну по ключевому столбцу Pass. Одинаковым значениям в этом столбце соответствуют одинаковые комбинации входных параметров в проходе.

После объединения раскрасим данные, относящиеся к основному периоду и форвард-периоду, в разные цвета, скроем временно некоторые столбцы и отсортируем данные по убыванию нормированной прибыли на основном периоде: 


Рис. 5. Объединённые результаты по основному и форвард-периоду


Хорошо видно, что для самых лучших результатов на основном периоде результаты на форвард-периоде в основном отрицательные. Нельзя сказать, что это очень плохие результаты  потеря 3% - 5% от начального баланса за год, но и хорошими их назвать точно нельзя. 

Напомним, как мы получаем значения в столбцах Forward Result и Back Result. Это результат функции OnTester(), возвращаемый советником после прохода. Эту величину мы называем нормированной прибылью и рассчитываем по такой формуле:

Result = Profit * (10% / EquityDD),

где Profit прибыль, полученная за период тестирования, EquityDD максимальная относительная просадка по средствам за период тестирования.

Смысл этой расчётной величины  это предполагаемая прибыль за период тестирования, которая могла бы быть получена, если изменить размер открываемых позиций так, чтобы максимальная относительная просадка достигала 10%.

Тут можно заметить, что результаты этого показателя корректно сравнивать, если для форвард-периода и для основного будет использован одинаковый коэффициент масштабирования позиций coeff = (10% / EquityDD). В процессе форвард-тестирования нам было бы проблематично получать значение этого коэффициента для основного периода, поэтому давайте проведем такую корректировку сейчас. Формула пересчёта будет выглядеть так:

ForwardResultCorrected = ForwardResult * (coeff_IS / coeff_OOS)

 = ForwardResult * (EquityDD_OOS / EquityDD_IS)

После применения корректировки получим такие результаты:

Рис. 6. Таблица результатов после пересчёта нормированной прибыли на форвард-периоде


Видим, что результаты на форвард-периоде по абсолютному значению увеличились. Это правильно по следующим соображениям. Представим, что мы взяли, например, второй набор параметров на основном периоде. Исходя из того, что просадка на неё составила 1.52%, мы увеличим размер позиций в 10 / 1.52 = 6.58 раз для достижения целевой просадки 10%. Тогда, если мы ничего пока не знаем про форвард-период, то и на нём надо было бы увеличить размер позиций в 6.58 раз. Но в этом случае, если на форвард-периоде была получена прибыль -98, то нормированная прибыль должна рассчитываться путём умножения прибыли на тот же коэффициент 6.58. Поэтому мы получим -635 вместо -240. Значение -240 получалось меньше из-за того, что на форвард-периоде просадка была почти в три раза больше (4.03% вместо 1.52%) и при расчёте нормированной прибыли коэффициент был 10 / 4.03 = 2.48, то есть почти в три раза меньше.

Ладно, мы увидели не особо приятные результаты. Попробуем теперь найти что-то более обнадеживающие. Во-первых, посмотрим, а есть ли у нас вообще положительные результаты на форвард-периоде. Отсортируем данные по столбцу Forward Result Corrected и увидим следующее:

Рис. 7. Таблица результатов с сортировкой по результату форвард-периода


Всё-таки есть группы наборов, на которых и на форвард-периоде есть положительные результаты. Они соответствуют таким группам, для которых на основном периоде достигается нормированная прибыль в районе 15000–18000. Видим, что тут и просадка не сильно отличается на основном и форвард-периоде, и нормированная прибыль на форвард-периоде составляет примерно пятую часть от нормированной прибыли на основном периоде.

Значит, можно выбрать хорошие группы?


Философский вопрос

На самом деле, это очень сложный вопрос. Его можно формулировать по-разному. Например:

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

Для того, чтобы попытаться как-то ответить на эти вопросы, вспомним байесовский подход. Сначала мы формулируем ряд гипотез (или предположений), затем оцениваем их вероятности до получения новых данных. После проведения эксперимента мы обновляем наши оценки, учитывая новые данные. То есть какие-то гипотезы становятся в наших глазах более вероятными, а другие  менее вероятными. Вероятность мы здесь рассматриваем как степень нашей уверенности в некотором исходе события.

Наша основная гипотеза будет такой: подбор группы с учётом форвард-периода улучшает результаты, получаемые на периоде после форварда. Альтернативная гипотеза: подбор группы с учётом форвард-периода не улучшает результаты.

Одним из возможных экспериментов будет такой: отберем некоторое количество групп с учётом форвард-периода и несколько групп без выделения форвард-периода. Затем протестируем все отобранные группы на периоде после форварда.

Если для групп, отобранных с учётом форвард-периода, результаты окажутся лучше, чем для групп, отобранных без форварда, то это будет слабым свидетельством в пользу основной гипотезы. Наша уверенность в истинности основной гипотезы повысится.

Если результаты для групп, подобранных без учёта форвард-периода, окажутся примерно такими же или лучше, то это будет слабым свидетельством в пользу альтернативной гипотезы. Но при этом мы не можем полностью отвергать ни одну из гипотез, так как результаты, показанные любой группой стратегий на периоде после форварда, зависят ещё от многих других факторов помимо способа отбора в группы. Это мог оказаться просто в целом неудачный период для используемых стратегий, и поэтому тот или иной способ отбора стратегий в группы мог не дать заметного эффекта.

Большего, наверное, мы себе позволить не можем. 

Стоит отметить, что фраза "с учётом форвард-периода" может быть понята не совсем правильно. Если мы применяем отбор с учётом форвард-периода, то это означает, что тот период, который был раньше форвардом (OOS), теперь, хотя и продолжает называться нами форвард-периодом, перестаёт быть периодом OOS и становится периодом IS. Это значит, что для оценки результатов торговли нам нужно использовать новый форвард-период (форвард за форвардом, простите за тавтологию).

Опишем более конкретно эксперимент, который мы хотим провести для получения дополнительной информации. Пусть у нас есть исторические данные за период с 2018 по 2023 годы включительно. Мы хотим на их основе отобрать группу стратегий, которая покажет хорошие результаты в 2024 году. Тогда мы можем поступить двумя способами:

  • Проводим оптимизацию на периоде 2018-2023 (IS), по её результатам отбираем лучшую группу.
  • Проводим оптимизацию на периоде 2018-2022 (IS) с одновременной проверкой на форвард-периоде 2023 (OOS). Отбираем лучшую группу, которая даёт хорошие и примерно одинаковые результаты на обоих периодах.

Во втором способе, скорее всего, мы выберем не ту же самую группу, как в первом. Её результаты будут несколько хуже, но про неё можно будет сказать, что она продержалась год на периоде OOS, не участвовавшем в оптимизации. А в первом способе сказать подобное про выбранную группу нельзя, так как мы её не проверяли за пределами периода IS. Зато в первом способе мы оптимизировали (обучали) группу на более длинном периоде, так как во втором способе мы должны были выделить 2022 год на форвард-период и не использовать его для оптимизации.

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


Отбираем первым способом

Для отбора первым способом нам необходимо сначала провести оптимизацию одиночного экземпляра стратегии на периоде 2018 - 2023 годов. До этого у нас такая оптимизация проводилась на периоде до 2022 года без включения 2023. После оптимизации мы получим наборы параметров, которые разделим на кластеры, как было описано в прошлой статье. Затем запустим оптимизацию на подбор хороших групп из восьми наборов параметров. Посмотрим на результаты самых лучших найденных групп наборов на периоде 2018-2023 и 2024 годов:

Рис. 8. Результаты оптимизации OptGroupClusterExpert.mq5 для подбора группы на основном периоде 2018 - 2023 годов



Рис. 9. Результаты оптимизации OptGroupClusterExpert.mq5 для подбора группы на периоде трёх месяцев 2024 года

Видим, что у лучших групп, найденных на основном периоде за 2018-2023 годы, на форвард-периоде в 2024 году результаты в целом положительные, но достаточно сильно отличающиеся друг от друга. Для более тщательной проверки выберем самую верхнюю группу, поставим ей значение масштабирующего множителя scale_ = 10 / 2.04 = 5 и запустим в тестере на основном периоде 2023 года и форвард-периоде 2024 года.

2023 год

2024 год (3 месяца)


Рис. 10.  Результаты советника OptGroupClusterExpert.mq5 для лучшей группы за 2023 и 2024 годы

По этим результатам не особо можно оценить перспективы дальнейшего поведения советника с такой группой наборов параметров стратегий, но, по крайней мере, мы не увидели наступления явно выраженного тренда на снижение кривой баланса в 2024 году. Запомним эти результаты и вернёмся к ним позднее, чтобы сравнить с результатами, полученными при втором способе отбора.


Отбираем вторым способом

Воспользуемся готовыми результатами оптимизации на периоде 2018-2022, выберем лучшую по полученной нормированной прибыли группу и посмотрим более внимательно на её результаты. Мы их уже видели на рис. 3., но теперь посмотрим на график работы не с 2018, а только с 2023 года. Установим в тестере в качестве основного периода весь 2023 год, и весь доступный 2024 год в качестве форвард-периода. Вот что у нас получится:

2023 год

2024 год (3 месяца)


Рис. 11.  Результаты советника OptGroupClusterExpert.mq5 за 2023 и 2024 годы

Обратим внимание на то, что просадка за 2023 год превысила расчётную по основному периоду тестирования почти в два раза: $1820 вместо $1000.

Для отбора в группы с учётом 2023 года как форвард-периода применим такой алгоритм:

Для тестирования совместной работы выбранных групп на основе советника OptGroupClusterExpert.mq5 создадим новый и внесём в него небольшие изменения. Так как этот советник не будет использоваться для оптимизации, то из него можно убрать функции OnTesterInit() и OnTesterDeinit(). Также можно убрать входные параметры, задающие индексы наборов параметров для включения в группу, поскольку их мы жестко зададим в коде на основе выполненной процедуры отбора.

В функции OnInit() создадим два массива  strGroups для отобранных групп и scales для множителей этих групп. Элементами массива strGroups будут строки, содержащие индексы наборов параметров, разделённые запятыми.

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit() {
   // Загружаем наборы параметров стратегий
   int totalParams = LoadParams(fileName_, params);

   // Если ничего не загрузили, то сообщим об ошибке
   if(totalParams == 0) {
      PrintFormat(__FUNCTION__" | ERROR: Can't load data from file %s.\n"
                  "Check that it exists in data folder or in common data folder.",
                  fileName_);
      return(INIT_PARAMETERS_INCORRECT);
   }

   // Отобранные группы наборов
   string strGroups[] = {"55,12,3,35,48,54,16,40",
                         "11,54,33,30,62,6,10,23",
                         "50,15,8,34,2,36,4,9",
                         "26,42,25,22,36,51,53,0"
                        };

   // Масштабирующие множители для отобранных групп наборов
   double scales[] = {4.16,
                      3.40,
                      3.33,
                      2.76
                     };
                     
   // Устанавливаем параметры в классе управления капиталом
   CMoney::DepoPart(expectedDrawdown_ / 10.0);
   CMoney::FixedBalance(fixedBalance_);

   // Создаем эксперта, работающего с виртуальными позициями
   expert = new CVirtualAdvisor(magic_, "SimpleVolumes_OptGroupForwardCluster");

   CVirtualStrategyGroup *groups[ArraySize(strGroups)];

   FOREACH(strGroups, {
      // Формируем строку из индексов наборов параметров, разделённых запятыми
      string strIndexes = strGroups[i];

      // Превращаем эту строку в массив
      string indexes[];
      StringSplit(strIndexes, ',', indexes);

      // Создаем и наполняем массив из всех экземпляров стратегий
      CVirtualStrategy *strategies[];

      FOREACH(indexes, {
         // Убираем номер кластера из строки набора параметров
         string param = CSVStringGet(params[StringToInteger(indexes[i])], 0, 11);
         // Добавляем стратегию с набором параметров с заданным индексом
         APPEND(strategies, new CSimpleVolumesStrategy(param))
      });

      // Добавляем стратегию к очередной группе стратегий
      groups[i] = new CVirtualStrategyGroup(strategies, scales[i]);
   });

   // Формируем и добавляем к эксперту группу групп стратегий
   expert.Add(CVirtualStrategyGroup(groups, scale_));

   return(INIT_SUCCEEDED);
}

Сохраним этот код в файле OptGroupForwardClusterExpert.mq5 в текущей папке.

Посмотрим на результаты тестирования этого советника. Также, как и в прошлый раз, мы объединим в одном проходе два периода: 2023 год и первые три месяца 2024 года.

2023 год

2024 год (3 месяца)


Рис. 12. Результаты советника OptGroupClusterForwardExpert.mq5 за 2023 и 2024 годы

Здесь уже результаты за 2023 год явно получше: тенденция роста наблюдается на всем периоде, хотя есть и период с марта по июль, когда кривая баланса не имела выраженного роста. Просадка на этом периоде тоже улучшилась и находится в пределах максимальной ожидаемой.

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

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


Заключение

Как видим, проведённый эксперимент не повысил нашу уверенность в том, что лучше выделять дополнительный период в качестве форварда и отбирать группы с учётом лучшей работы на обоих периодах. Но это не значит, что такой подход не стоит использовать. Нам еще не очень повезло с тем, что для сравнения мы использовали только три месяца от 2024 года. Это слишком короткий период, поскольку мы видели, что используемая торговая стратегия может иметь периоды колебания прироста баланса около нуля протяжённостью до нескольких месяцев. Поэтому не понятно, то ли первые три месяца 2024 это начало подобного периода, который потом сменится ростом, то ли роста может уже и не быть.

Можно попробовать провести подобный эксперимент, сдвинув периоды на один год назад. В этом случае основной период будет начинаться в 2017 году, отбор вторым способом будет проводиться на периоде 2022 года, а для сравнения двух способов у нас останется весь 2023 и начало 2024 года.

Но мы лучше пойдём дальше. Никто не мешает нам часть групп отобрать первым способом, а другую часть вторым, и соединить их в одном советнике. Но какое максимальное количество экземпляров торговых стратегий мы сможем объединить в одном советнике? Объединить так, чтобы его работа не занимала слишком много ресурсов на сервере и не требовался какой-то повышенный по сравнению с типичными значениями объем оперативной памяти. Постараемся прояснить этот вопрос в следующей или одной из следующих статей.

Спасибо за внимание, продолжение следует.



P.S. Так получилось, что при подготовке этой статьи мы не вносили никаких изменений в ранее созданные файлы. Мы добавили лишь один новый файл, поэтому только он приложен к этой статье. Все остальные упоминаемые файлы с кодом можно взять из предыдущей статьи.