Непрерывная скользящая оптимизация (Часть 4): Программа для управления оптимизацией (автооптимизатор)

10 февраля 2020, 13:16
Andrey Azatskiy
11
1 030

Введение

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

  1. Непрерывная скользящая оптимизация (Часть 1): Механизм работы с отчетами оптимизации
  2. Непрерывная скользящая оптимизация (Часть 2): Механизм создания отчета оптимизации для любого робота
  3. Непрерывная скользящая оптимизация (Часть 3): Способ адаптации робота к автооптимизатору

Текущая же статья демонстрирует общую картину созданного продукта и служит некой инструкцией к применению. Также созданный автооптимизатор предусматривает возможность расширения своего функционала, и тот алгоритм оптимизации, что будет описан в данной статье, может быть с легкостью заменен на Ваш собственный алгоритм оптимизаций, что предоставляет простор для творчества. Сама же внутренняя структура созданной программы будет раскрыта в дальнейших описаниях. Основная цель данной статьи — именно описать механизм работы с получившимся приложением и его возможностей, так как мои коллеги, кто использовал данное приложение, не всегда сходу его осваивали. Хотя, как это будет продемонстрировано в статье, сам автооптимизатор довольно прост в применении и настройках. Соответственно, можно относиться к статье как к инструкции по использованию данного приложения, в которой говорится обо всех возможных подводных камнях и нюансах его настройки.


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

Для того чтобы перейти к рассмотрению работы самой созданной программы, сначала необходимо разобраться с целью данного проекта. Учитывая, что все мы решили для себя использовать научный подход к торговле и начали создавать четко запрограммированные торговые алгоритмы (не имеет значения — будь то индикаторные роботы или же основанные на применении модели нечеткой логики и нейронных сетей — все это запрограммированные алгоритмы, выполняющие конкретно поставленные задачи), то и подход к процессу отбора результатов их оптимизаций должен быть формализован. Иначе говоря, если в процессе торгов мы абстрагируемся от случайностей, то и процесс подготовки к торгам так же должен быть автоматизирован. В противном случае мы можем из той массы полученных параметров оптимизаций наугад выбирать понравившиеся результаты, что ближе к наитию, нежели к системной торговле. Озвученная идея является первым из мотивов, побудивших к созданию данного приложения, в следующая — это возможность тестирования алгоритмов через их оптимизацию — непрерывная скользящая оптимизация, которая визуально продемонстрирована на графике ниже.   


Непрерывная скользящая оптимизация чередует исторические (желтые) и форвардные (зеленого цвета) оптимизационные проходы на заданном временном интервале. К примеру, у нас доступна история на протяжении 10 лет. Мы определяем для себя, что оптимизационный промежуток должен состоять из интервала равного в 1 год, а форвардный — 1 квартал (или 3 месяца). В итоге мы получим на один проход оптимизации + форвард тест промежуток равный в 1,25 года (1 год + 1 квартал). На приложенной диаграмме каждая линия характеризует как раз озвученный временной интервал. 

Далее, мы производим однотипный процесс:

  1. Проводим оптимизацию на выбранном историческом интервале (1 год).
  2. Отбираем по фиксированной методике лучшие параметры из полученных на оптимизационном промежутке.
  3. Смещаемся на форвардный интервал времени и производим тест, возвращаемся на первый шаг, но уже на смещенном периоде. И так до тех пор, пока не дойдем до текущего временного периода.
  4. Собрав результаты тестового прохода каждого квартала, получаем итоговую оценку жизнеспособности алгоритма.
  5. Последний оптимизационный проход (у которого уже нет форвард теста) запускаем в бой.

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

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

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

В-третьих, мы получаем на довольно малом промежутке времени большое количество оптимизационных окон, что повышает анализ достоверности произведенных тестов. К примеру, на интервале в 2 года с упомянутой выше разлиновкой на форвардный (1 квартал) и оптимизационный (1 год) периоды мы получим 4 стресс-теста и 1 финальную оптимизацию.


Настройка запуска оптимизаций

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

  1. Управление оптимизацией (Часть I)
  2. Управление оптимизацией (Часть 2)

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

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

Полученное приложение работает по следующей схеме:

  1. Во время запуска очередного процесса, будь то оптимизация или же тест, оно запускает терминал, делегируя терминалу весь процесс тестирования. По завершению тестирования или же оптимизации терминал сам закрывается.
  2. Робот по окончанию тестирования или оптимизации сам формирует отчет с выгруженными результатами оптимизации, подробнее об упомянутом процессе можно почитать в прошлых статей из данного цикла статей.
  3. Автооптимизатор, зная где располагается отчет, читает его и обрабатывает. По окончанию обработки либо запускается новый этап оптимизаций и тестирования, либо же оптимизация завершается и результаты выводятся во вкладке "Result". 

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


Если приглядеться к скриншоту, то первый ComboBox (выпадающий список), именуемый "Select Optimiser" и размещенный в области выделенной зеленым цветом, как раз и является выбором типа оптимизаций, реализованных в программе. 

Как видно, графический интерфейс полученного приложения разделен на 2 основные вкладки. Вкладка Settings является основной, именно с нее начинается работа с приложением когда нужно запустить или остановить оптимизацию, и её рассмотрению посвящена данная часть статьи.  Вторая вкладка "Result" является местом, где демонстрируются результаты проделанной оптимизации, о ней поговорим позднее.

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

Как видно на скриншоте, вкладка "Settings" разделена на 4 части. Границы каждой из частей экрана могут перетаскиваться, особенно это удобно, если требуется работать с алгоритмом, у которого большое количество входных параметров.

  • Первая часть экрана представляет список параметров из оптимизатора MetaTrader. Список экспертов "Avalible experts" обновляется всякий раз при смене выбранного терминала. В данном выпадающем списке находятся все эксперты, что размещены в соответствующей директории выбранного терминала. Эксперты указаны с учетом путей вложенных директорий. 
  • Вторая часть экрана указывает список параметров выбранного алгоритма. Список параметров подтягивается из соответствующих файлов, находящихся в директории "MQL5/Profiles/Tester/{Expertname}.set". Если же для выбранного эксперта файл не создан, то при выборе эксперта в списке из первой области экрана, сперва открывается терминал, где загружается тестер. Затем создается запрашиваемый файл с настройками по умолчанию, и уже после этого происходит закрытие терминала. Стоит упомянуть, что смена списка настроек происходит каждый раз после выбора нового эксперта из списка  "Avalible experts". Если же по каким либо причинам вы решите обновить список параметров, что был загружен, то для этого достаточно кликнуть на кнопку "Update (*.set) file", после чего будет произведено обновление параметров путем открытия терминала с последующим завершением его работы. Перед данным действом существующий файл будет удален, посему новый файл создастся на его старом месте в указанной ранее директории.
  • Третья часть экрана  содержит в себе крайне важные параметры, а именно — список критериев для фильтрации и сортировки массива выгруженных данных. Сама процедура сортировки подробно рассматривается в первой статье из данного цикла статей. Стоит упомянуть, что критерии фильтрации могут быть заданы двояко, к примеру ">=", "<=", "< || >", о чем также говорилось в первой статье. 
  • Завершающая четверть экрана указывает череду форвардных и исторических временных проходов. Логический смысл коих был упомянут выше, а реализация сортировки и соотношения между собой раскрывалась в первой статье из данного цикла. Стоит упомянуть, что если мы запускаем тест, а не оптимизацию (параметр Optimisation model = Disabled), то в данном поле должен находиться либо один исторический временной диапазон, либо 2 (исторический и форвардный). Так как ввод данных при каждом запуске автооптимизатора может быть утомителен (проверено на собственном опыте), то был создан механизм сохранения данных в файл. При нажатии на кнопку Save/Load проверяется наличие введенных ранее данных в список параметров. Если список полон, то мы производим сохранение полученных данных в файл, если же список пуст, то выбирается файл, из которого будет загружен список с датами оптимизационных проходов. Внутренняя структура файла будет приведена в дальнейших повествованиях, в данном же ограничимся тем, что упомянем, что все файлы формируемые программой имеют xml-формат, и данный файлик не является исключением. Также необходимо иметь ввиду, что формат ввода дат — "ДД.ММ.ГГГГ", а формат отображения дат — "ММ.ДД.ГГГГ". Это связано с тем, что перевод даты в строку осуществляется автоматически, так как для меня это не было сильно критично, то я оставил как было, так как программа в любом случае поймет введенную дату.      

Стоит так же упомянуть, что из-за текстового формата set-файлов, мы не можем различить форматы исходных параметров алгоритмов. К примеру все enum параметры отражаются как int. И посему было принято решение отражать во второй части экрана список параметров в виде строк. Если Вам будет неудобно настраивать шаги оптимизации и прочие параметры робота перед тестом напрямую в автооптимизаторе, то вы вольны произвести все настройки в терминале, далее — после смены вкладок в тестере или же по закрытию терминала — настройки запишутся в искомый файл и всё, что потребуется  сделать — это выбрать требуемый алгоритм в автооптимизаторе, он будет загружен уже в произведенными Вами настройками.

Непосредственно для запуска оптимизации или же теста из автооптимизатора, требуется в настроить следующие поля:

  1. Выбор эксперта.
  2. Отметить оптимизируемые параметры галочкой и выбрать диапазон (тут все как в терминале).
  3. Выбрать хоть один критерий сортировки — именно исходя из выбранных параметров сортировки будут сортироваться выгруженные роботом данные, и наилучший результат из получившихся будет запущен на форвардном промежутке (в случае запуска теста можно не указывать).
  4. Выбрать даты скользящей оптимизации (или же даты тестов в случае запуска теста, а не оптимизации).   
  5. Должен быть выбран требуемый оптимизатор, если производится оптимизация (выпадающий список "Select Optimiser:"). 
  6. Должен быть указан Asset name — это имя актива, на котором будет запускаться запрашиваемая операция. В случае ошибки терминал не сможет запустить процесс теста или же оптимизации.
  7. Для дополнительной спецификации имени сохраняемого оптимизационного прохода задается "Directory prefix". Дело в том, что по окончанию оптимизации в специальной внутренней директории программы создается папка с результатами оптимизации. Данная папка именуется следующим образом: "{Directory prefix} {Selected optimiser} {Expert name} {Asset name}". Именно эти имена мы видим в выпадающем списке "Optimisation:" из которого в последствии сможем подгружать их для дальнейшего просмотра и анализа.
  8. Выпадающий список с параметрами "Rewrite" или "Append" так же является опциональным. Он указывает действие, которое должен произвести автооптимизатор, если уже найдет среди сохраненных  результаты с подобным именем (создание имени описано в пункте 7 настоящего перечисления). Если будет выбран пункт "Rewrite", то все файлы будут перезаписаны на новые, если же "Append", то будут перезаписаны лишь те даты оптимизаций, что будут находить друг на друга. Если в текущем списке оптимизационных диапазонов и в списке ранее сохраненных найдется один и тот же диапазон, то его ранее сохраненные результаты будут перезаписаны на вновь полученные, те же временные диапазоны, что не были ранее сохранены, будут просто добавлены к уже записанным.

По завершении упомянутых настроек остается лишь нажать на кнопку "Start/Stop" и процесс будет запущен. При повторном клике на данную кнопку и запущенном процессе оптимизации она будет прервана. Во время всего процесса оптимизации ее статус отражается в ProgressBar и текстовой метке, кои расположены в основании окна автооптимизатора. По завершению процесса оптимизации будут загружены полученные результаты на вкладку "Result" и ProgressBar будет сброшен в исходное положение. Однако, если запускать не оптимизацию, а тест через нажатие на кнопку "Start / Stop", то терминал не будет закрываться автоматически. Это сделано для удобства, чтобы пользователь мог в достаточной мере изучить интересующие его данные. По окончанию изучения искомой информации для продолжения работы с автооптимизатором требуется закрыть терминал вручную как обычно. Также не лишним будет повторить, что для корректной работы описываемого приложения требуется, чтобы терминал был всегда закрыт, ведь приложение по сути является менеджером оптимизаций и должно иметь возможность управлять терминалом самостоятельно.

Также перед запуском оптимизаций следует произвести настройки самого оптимизатора. Это не обязательное требование, но структура получившегося менеджера оптимизаций такова, что позволяя создавать пользовательские оптимизаторы, она так же позволяет задавать каждому из них настройки, вызываемые через нажатие на кнопку "GUI", расположенную прямо рядом с комбобоксом выбора оптимизатора. Настройки реализованного оптимизатора следующие:

  1. Test on ticks  указывает способ тестирования данных на исторических и форвард тестах. Способ оптимизации указывается в первой четверти экрана "Settings", а вот способ тестирования — в настройках оптимизатора. Если стоит галочка, то тесты будут проводиться на тиках, однако если галочка отсутствует, то тесты будут проводиться с использованием метода OHLC 1 minute. 
  2. Replace real dates to setted указывает, нужно ли замещать реальные даты начала и завершения оптимизаций на переданные. Дело в том, что как указано в третьей статье из данного цикла статей, время начала и завершения оптимизаций сохраняется с использованием минутного графика, и иногда, если не было торгов или же если были выходные, то даты начала или же окончания оптимизации могут разниться с заданными. Ставя эту галочку мы просто добавляем более привычные даты, дабы не гадать в последствии, к какому интервалу времени относился исследуемый проход оптимизации. Однако если есть желание видеть реальные даты торгов, то можно не ставить галочку на описываемом параметре.
  3. Use different shift for tick test — как было упомянуто во второй статье из данного цикла, мы можем добавлять проскальзывание и сдвиг к выгружаемым данным. Если мы тестируем на тиках, то порой разумно будет отключить проскальзывание или же сделать его меньше. Посему был введен данный пункт, который активируется лишь когда настройка "Test on ticks" помечена как активная. Для его задействования требуется указать те параметры алгоритма, которые отвечают за задание комиссии и проскальзывания, и задать им новое значение. К примеру, если указать параметр робота, отвечающий за проскальзывание и поставить ему значение равное 0, то мы уберем из выгрузки проскальзывание в режиме тикового теста. После указания параметра и его значения для сохранения данного параметра нужно добавить его в таблицу, нажав кнопку "Add".

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


Работа с полученными результатами оптимизаций

После запуска оптимизаций единственное, что требуется — так это невмешательство в данный процесс. Также эксперт не должен удаляться с графика ранее, чем оптимизатор его остановит, в противном случае автооптимизатор из-за несоответствия дат заявленным посчитает, что произошла какая-либо ошибка во время процесса оптимизации. По завершению процесса и закрытии терминала оптимизатор загрузит отчет оптимизации во вкладку "Results", где будет возможность оценить проделанную работу. Структура  данной вкладки изображена на скриншоте:

 

Как видно, вкладка с результатами оптимизации также поделена на фракции, помеченные номерами для простаты повествования. В первой части данной вкладки содержатся оптимизационные проходы, разделенные между вкладками (Selected pass и Optimisations). Первая вкладка данного контейнера, именуемая "Selected pass", содержит отобранные проходы оптимизаций. Данные проходы поделены между двумя вкладками ("Forward" и "History"). Для большей конкретики покажем на примере, как производится распределение параметров оптимизаций между этими вкладками. К примеру, первым из окон оптимизации следуют следующие даты:

  1. 01.01.2012 - 07.12.2012 - History
  2. 10.12.2012 - 07.03.2012 - Forward

Соответственно, оптимизация будет выполняться на историческом интервале времени. Далее будут отобраны лучшие параметры с применением фильтрации и сортировки, как это описывалось в прошлой главе. По завершению процесса отбора производится 2 теста: на историческом и на форвардном окне. Далее, результаты тестов данных лучших параметров заносятся во вкладку "Selected pass", делясь между вкладками "History" и "Forwars" соответственно окнам исторического и форвардного временных промежутков. Сами проходы оптимизаций же заносятся во вкладку "Optimisations",  где можно посмотреть весь список оптимизаций как в терминале за любой из выбранных исторических интервалов. Рассмотрим вкладку с оптимизациями поподробнее.


Как видно, структура самой таблицы (первая часть вкладки "Optimisations") аналогична структуре вкладок "Forward" и "History". Однако, в данной таблице отображаются все проходы оптимизации за запрашиваемый интервал времени. Интересующий интервал времени можно выбрать из выпадающего списка "Optimisation dtes". При выборе нового временного интервала обновляется вся таблица с оптимизационными проходами. Вторая часть описываемой вкладки аналогична третьей части вкладки "Settings", упомянутой в прошлой главе настоящей статьи, и все изменения в данной области синхронизируются с подобной областью на вкладке "Settings".

Также, как видно на представленном скриншоте, вкладка "Optimisations",  как и вкладка "Selected pass", содержит кнопку "Save to (*.csv)". По нажатию на данную кнопку при выбранной вкладке "Selected pass" создается (*.csv) файл со списком исторических и форвардных оптимизационных проходов. При выбранной вкладке "Optimisations" по нажатии данной кнопки выгружается информация обо всех осуществленных оптимизациях в соответствующий (*csv) файл. Другие же кнопки "Sort" и "Filter" доступны лишь на вкладке "Optimisations"— и их задача состоит в предоставлении возможности фильтрации и сортировки полученных оптимизационных проходов в соответствии с настройками, указанными во второй части вкладки "Optimisations". На деле эта опция так таковая не требуется, ведь сам автооптимизатор использует как раз тот же механизм. Однако, если вы не согласны с полученными результатами и считаете что сможете подобрать более адекватные чем указанные ранее путем смены фильтров - данный функционал вам будет полезен. 

Обе таблицы интерактивны. При одиночном клике на строку в таблице вторая и третья части вкладки "Result" обновляют свои значения для выбранного оптимизационного прохода. Двойной клик по какой-либо строке запускает тест в терминале. Причем, терминал не будет закрыт автоматически по завершению теста, соответственно, после изучения искомых данных в отчете оптимизации терминала и просмотра графика теста, его необходимо будет закрыть вручную для того, чтобы была возможность дальнейшей работы автооптимизатора с терминалом. Перед запуском теста можно также настроить некоторые параметры тестера — 2 часть вкладки "Results":

  

Дата начала и завершения теста автоматически обновляются согласно выбранному временному интервалу. Однако, по желанию их можно изменить двойным кликом на выбранную строку. Также можно выбрать задержку исполнения и тип тестирования (тики, OHLC и прочие). По умолчанию стоят варианты "Без задержки" и "Каждый тик". 

Третья часть данной вкладки отображает как статистические показатели торгов на выбранном оптимизационном проходе (вкладки Daily PL и Max PL/DD), так и параметры робота для выбранного прохода (вкладка Bot params). Причем вкладка "Max PL/DD" не показывает конечное значение прибыли/убытка, она показывает суммарную прибыль (сумма лишь прибыльных сделок) и суммарный убыток (сумма лишь убыточных сделок). Прибыль и максимальная просадка, что была на момент завершения торгов, отображается в таблице в результатами оптимизаций и тестов. Вкладка Daily PL отображает среднюю прибыль за день и средний убыток за день — все подобно отчету, что находится в терминале.

Еще один простейший пример алгоритма для работы с автооптимизатором

Рассмотрев, как пользоваться автооптимизатором, приведем завершенный пример робота, который приспособлен для работы автооптимизатора. В статье №3 из данного цикла статей мы уже рассматривали шаблон написания алгоритма для поставленной задачи, в данной статье мы адаптируем наш шаблон для алгоритма, написанного в C стиле. Сперва рассмотрим сам алгоритм — по традиции возьмем простейший алгоритм 2х скользящих средних, за тем лишь исключением, что закрытие позиций будет производиться либо по фиксированному стопу, либо по фиксированному тейку. Для пущего акцентирования внимания из приложенного кода уберем реализацию функций, описывающих логику полученного эксперта — это не является целью данного примера.

//+------------------------------------------------------------------+
//|                                                     SimpleMA.mq5 |
//|                        Copyright 2019, MetaQuotes Software Corp. |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2019, MetaQuotes Software Corp."
#property link      "https://www.mql5.com"
#property version   "1.00"

#include <Trade/Trade.mqh>
#define TESTER_ONLY

input int ma_fast = 10; // MA fast
input int ma_slow = 50; // MA slow
input int _sl_ = 20; // SL
input int _tp_ = 60; // TP
input double _lot_ = 1; // Lot size

int ma_fast_handle,ma_slow_handle;
const double tick_size = SymbolInfoDouble(_Symbol,SYMBOL_TRADE_TICK_SIZE);
CTrade trade;

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
//---

#ifdef TESTER_ONLY
   if(MQLInfoInteger(MQL_TESTER)==0 &&
      MQLInfoInteger(MQL_OPTIMIZATION)==0)
     {
      Print("This expert was created for demonstration! It is not anabled for real trading !");
      ExpertRemove();
      return(INIT_FAILED);
     }
#endif

   ma_fast_handle = iMA(_Symbol,PERIOD_CURRENT,ma_fast,0,MODE_EMA,PRICE_CLOSE);
   ma_slow_handle = iMA(_Symbol,PERIOD_CURRENT,ma_slow,0,MODE_EMA,PRICE_CLOSE);

   if(ma_fast_handle == INVALID_HANDLE ||
      ma_slow_handle == INVALID_HANDLE)
     {
      ExpertRemove();
      return(INIT_FAILED);
     }
//---
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
   if(ma_fast_handle != INVALID_HANDLE)
      IndicatorRelease(ma_fast_handle);
   if(ma_slow_handle != INVALID_HANDLE)
      IndicatorRelease(ma_slow_handle);
  }

enum Direction
  {
   Direction_Long,
   Direction_Short,
   Direction_None
  };

//+------------------------------------------------------------------+
//| Calculate stop                                                   |
//+------------------------------------------------------------------+
double get_sl(const double price, const Direction direction)
  {
   ...
  }

//+------------------------------------------------------------------+
//| Calculate take                                                   |
//+------------------------------------------------------------------+
double get_tp(const double price, const Direction direction)
  {
   ...
  }

//+------------------------------------------------------------------+
//| Open position according to direction                             |
//+------------------------------------------------------------------+
void open_position(const double price,const Direction direction)
  {
   ...
  }

//+------------------------------------------------------------------+
//| Get direction                                                    |
//+------------------------------------------------------------------+
Direction get_direction()
  {
   ...
  }

//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
  {
   if(!PositionSelect(_Symbol))
     {
      Direction direction = get_direction();
      if(direction != Direction_None)
         open_position(iClose(_Symbol,PERIOD_CURRENT,0),direction);
     }
  }
//+------------------------------------------------------------------+

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

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

В методе OnOnit все что мы делаем, так это инстанцирование индикаторов скользящих средних. В методе OnDeinit мы, соответственно, удаляем индикаторы. Для определения направления используется перечисление Direction, объявленное в коде. Открытие позиций происходит через класс CTrade, замаскированное в функции open_position. Сама логика описана в четырех строках кода в коллбеке OnTick. Теперь добавим подключение требуемого функционала в эксперт.

//+------------------------------------------------------------------+
//|                                                     SimpleMA.mq5 |
//|                        Copyright 2019, MetaQuotes Software Corp. |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2019, MetaQuotes Software Corp."
#property link      "https://www.mql5.com"
#property version   "1.00"

#include <Trade/Trade.mqh>
#include <History manager/AutoLoader.mqh> // Include CAutoUploader
#define TESTER_ONLY

input int ma_fast = 10; // MA fast
input int ma_slow = 50; // MA slow
input int _sl_ = 20; // SL
input int _tp_ = 60; // TP
input double _lot_ = 1; // Lot size

// Comission and price shift (Article 2) 
input double _comission_ = 0; // Comission
input int _shift_ = 0; // Shift

int ma_fast_handle,ma_slow_handle;
const double tick_size = SymbolInfoDouble(_Symbol,SYMBOL_TRADE_TICK_SIZE);
CTrade trade;
CAutoUploader * auto_optimiser; // Pointer to CAutoUploader class (Article 3)
CCCM _comission_manager_; // Comission manager (Article 2)

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
//---

#ifdef TESTER_ONLY
   if(MQLInfoInteger(MQL_TESTER)==0 &&
      MQLInfoInteger(MQL_OPTIMIZATION)==0)
     {
      Print("This expert was created for demonstration! It is not anabled for real trading !");
      ExpertRemove();
      return(INIT_FAILED);
     }
#endif

   ma_fast_handle = iMA(_Symbol,PERIOD_CURRENT,ma_fast,0,MODE_EMA,PRICE_CLOSE);
   ma_slow_handle = iMA(_Symbol,PERIOD_CURRENT,ma_slow,0,MODE_EMA,PRICE_CLOSE);

   if(ma_fast_handle == INVALID_HANDLE ||
      ma_slow_handle == INVALID_HANDLE)
     {
      ExpertRemove();
      return(INIT_FAILED);
     }

   // Set Comission adn shift
   _comission_manager_.add(_Symbol,_comission_,_shift_);

   // Add robot params
   BotParams params[];
   APPEND_BOT_PARAM(ma_fast,params);
   APPEND_BOT_PARAM(ma_slow,params);
   APPEND_BOT_PARAM(_sl_,params);
   APPEND_BOT_PARAM(_tp_,params);
   APPEND_BOT_PARAM(_lot_,params);
   APPEND_BOT_PARAM(_comission_,params);
   APPEND_BOT_PARAM(_shift_,params);

   // Add Instance CAutoUploader class (Article3)
   auto_optimiser = new CAutoUploader(&_comission_manager_,"SimpleMAMutex",params);
//---
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
   if(ma_fast_handle != INVALID_HANDLE)
      IndicatorRelease(ma_fast_handle);
   if(ma_slow_handle != INVALID_HANDLE)
      IndicatorRelease(ma_slow_handle);

   // Delete CAutoUploaderclass (Article 3)
   delete auto_optimiser; 
  }

enum Direction
  {
   Direction_Long,
   Direction_Short,
   Direction_None
  };

//+------------------------------------------------------------------+
//| Calculate stop                                                   |
//+------------------------------------------------------------------+
double get_sl(const double price, const Direction direction)
  {
   ...
  }

//+------------------------------------------------------------------+
//| Calculate take                                                   |
//+------------------------------------------------------------------+
double get_tp(const double price, const Direction direction)
  {
   ...
  }

//+------------------------------------------------------------------+
//| Open position according to direction                             |
//+------------------------------------------------------------------+
void open_position(const double price,const Direction direction)
  {
   ...   
  }

//+------------------------------------------------------------------+
//| Get direction                                                    |
//+------------------------------------------------------------------+
Direction get_direction()
  {
   ...
  }

//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
  {
   auto_optimiser.OnTick(); // Save current date (Article 3)

   if(!PositionSelect(_Symbol))
     {
      Direction direction = get_direction();
      if(direction != Direction_None)
         open_position(iClose(_Symbol,PERIOD_CURRENT,0),direction);
     }
  }
//+------------------------------------------------------------------+

Все нововведения проделанные с кодом подсвечены зеленым. Рассмотрим их, двигаясь сверху вниз по представленному коду. Сперва мы подключаем заголовочный файл AutoLoader, который описан в статье №3. В данном файле содержится класс CAutoUploader, задача которого в момент разрушения выгрузить накопившийся историю торгов. В коллбеке OnInit мы добавляем комиссию в соответствующий класс CCCM, описанный в статье №2, и инстанцируем класс CAutoUploader , предварительно добавив в него параметры эксперта. А в коллбеке OnDeinit удаляем экземпляр класса CAutoUploader, инициализируя тем самым вызов деструктора, где происходит выгрузка отчета торгов в xml-файл (статья №1). 

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


Заключение

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

  • Проект созданного автооптимизатора
  • Код описанного эксперта

Для запуска программы автооптимизатора его требуется скомпилировать с помощью Ide Visual Studio. Так же не стоит забывать про то, что в директории MQL5/Include/Libraries должна находиться библиотека "ReportManager.dll", описанная в первой статье из данного цикла статей. Также ее можно найти в приложенном проекте с Автооптимизатором (также требуется компиляция).    

Прикрепленные файлы |
Auto_Optimiser.zip (126.38 KB)
Последние комментарии | Перейти к обсуждению на форуме трейдеров (11)
Andrey Azatskiy
Andrey Azatskiy | 15 фев 2020 в 18:16
Good Beer:

2. Как обеспечивается непрерывное скольжение оптимизации? ...

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

Andrey Azatskiy
Andrey Azatskiy | 15 фев 2020 в 18:48

Good Beer:

У меня пока не получилось скомпилировать проект ...

Самый простой и быстрый способ скомпилировать - открыть проект и нажать сочетание клавиш CTRL+SHIFT+B.

Более визуальный - нажать на стрелочку зеленую в редакторе, произойдет запуск приложения в режиме отладки кода, но компиляция пройдет тоже. 

Еще один вариант - из выпадающего меню пункт Build

Позже по пути  MetaTrader Auto Optimiser/bin/Debug (или  MetaTrader Auto Optimiser/bin/Release, зависит от выбранного типа сборки) - появится скомпилированная программа.

Good Beer
Good Beer | 15 фев 2020 в 18:55

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

Список периодов можно составить средствами MQL.

Про #define TESTER_ONLY я имел ввиду, что выражение в скобках сработает и без TESTER_ONLY. Я не понял зачем нужно применять подстановку?  Много раз такое видел.

Andrey Azatskiy
Andrey Azatskiy | 15 фев 2020 в 19:12
Good Beer:

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

Список периодов можно составить средствами MQL.

Про #define TESTER_ONLY я имел ввиду, что выражение в скобках сработает и без TESTER_ONLY. Я не понял зачем нужно применять подстановку?  Много раз такое видел.

Хорошо, как доделаю первый вариант статьи, запланирую себе продолжение, все равно есть ряд идей которые хотел бы реализовать.

Список периодов - именно в авто оптимизаторе нужен, он сам переключает тесты и должен видеть эти периоды (согласно логике получившейся программы). 

Касательно дефайна:

#ifdef TESTER_ONLY
   if(MQLInfoInteger(MQL_TESTER)==0 &&
      MQLInfoInteger(MQL_OPTIMIZATION)==0)
     {
      Print("This expert was created for demonstration! It is not anabled for real trading !");
      ExpertRemove();
      return(INIT_FAILED);
     }
#endif

Выражение обернуто в конструкцию #ifdef #endif - это условие выполняемое на момент компиляции. Если скомпилировать проект определив #define TESTER_ONLY - то в сборку попадает то выражение что обернуто в данное условие, если же не объявить - то не попадает. Я так сделал что бы если вдруг кому то захочется поэксперементировать и запустить робота на свой страх и риск на реале, то все что потребуется - это лишь закоментировать #define TESTER_ONLY - не меняя исходный код.



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

Good Beer
Good Beer | 15 фев 2020 в 21:30
Спасибоза внимание! Всё понятно. С нетерпением жду продолжения.
Библиотека для простого и быстрого создания программ для MetaTrader (Часть XXXIV): Отложенные торговые запросы - удаление ордеров, модификация ордеров и позиций по условиям Библиотека для простого и быстрого создания программ для MetaTrader (Часть XXXIV): Отложенные торговые запросы - удаление ордеров, модификация ордеров и позиций по условиям

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

Библиотека для простого и быстрого создания программ для MetaTrader (Часть XXXIII): Отложенные торговые запросы - закрытие позиций по условиям Библиотека для простого и быстрого создания программ для MetaTrader (Часть XXXIII): Отложенные торговые запросы - закрытие позиций по условиям

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

Прогнозирование временных рядов (Часть 1): метод эмпирической модовой декомпозиции (EMD) Прогнозирование временных рядов (Часть 1): метод эмпирической модовой декомпозиции (EMD)

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

Работа с сетевыми функциями, или MySQL без DLL: Часть II - программа для мониторинга изменения свойств сигналов Работа с сетевыми функциями, или MySQL без DLL: Часть II - программа для мониторинга изменения свойств сигналов

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