MQL5: Руководство по тестированию и оптимизации советников

12 октября 2010, 17:13
Samuel Olowoyo
4
14 322

Введение

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


1. Поиск и исправление ошибок в коде

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

Представьте, вы только закончили написание кода (или, к примеру, модифицируете код, написанный кем-то). Все должно работать как полагается, поскольку вы почти уверены в отсутствии ошибок. Нажимаете кнопку "Компилировать" (F7) и ... во вкладке "Ошибки" окна "Инструменты" получаете сообщения о наличии множества ошибок в коде:

Рисунок 1. Ошибки при компиляции кода советника

Рисунок 1. Ошибки при компиляции кода советника

Вот это да! 38 ошибок и 1 предупреждение. Возможно, в вашем коде не так много ошибок, как здесь.

Рассмотрим типы ошибок, которые могут появляться при компиляции нашего кода и способы их решения. Для этого посмотрим структуру вкладки "Ошибки" (рис. 1):

  • В столбце "Описание" (отмечен цифрой 1) выводится описание ошибки в коде. Это дает нам информацию о характере ошибки.
  • В столбце "Файл" (отмечен цифрой 2) показано имя файла, в котором обнаружена ошибка. Эта информация очень полезна при работе с включаемыми файлами, которые содержат ошибки. Таким образом, мы сможем узнать, какой файл следует проверять на наличие указанной ошибки.
  • В столбцах "Строка" и "Столбец" (отмечены цифрой 3) выводится информация о номере строки и позиции в строке, где находится ошибка. Это позволяет нам узнать, где именно следует искать указанную ошибку.
  • В последней строке (отмечена цифрой 4) выводится сводная информация об ошибках компиляции и предупреждениях.

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

Для того чтобы начать с самого начала, перейдем на первые строки вкладки "Ошибки".

Рисунок 2. Выявление и исправление ошибок в коде

Рисунок 2. Выявление и исправление ошибок в коде

Первая ошибка обнаружена в позиции 20 строки 16, ее описание выглядит следующим образом: "truncation of constant value".

Переход на строку, содержащую ошибку, можно осуществить несколькими способами.  Для перехода к заданной строке нужно выбрать меню "Перейти к строке..." в разделе "Правка" главного меню или нажать клавиши Ctrl-G на клавиатуре.

Рисунок 3. Переход к указанной строке кода

Рисунок 3. Переход к указанной строке кода

В результате появится диалоговое окно с запросом номера строки:

Рисунок 4.  Диалоговое окно перехода к строке

Рисунок 4.  Диалоговое окно перехода к строке

Диапазон возможных значений, показанный в диалоговом окне - общее количество строк в коде. В этом случае (1-354) наш код содержит 354 строк.

В строке ввода укажите номер строки, которую вы хотите проверить, и нажмите кнопку "OK". В редакторе вы попадете прямо на строку с указанным номером, это видно по мигающему курсору:

Рисунок 5. Курсор установлен на строке с ошибкой

Рисунок 5. Курсор установлен на строке с ошибкой

Проблема здесь в том, что мы объявили переменную Lot типа int (как целочисленную), однако инициализировали ее значением типа double (0.1).

Для исправления этой ошибки мы изменим тип переменной int на double, сохраняем файл и снова нажимаем кнопку "Компилировать", чтобы убедиться в том, что ошибка была исправлена.

Рисунок 6. Компилируйте и сохраняйте код после исправления ошибок

Рисунок 6. Компилируйте и сохраняйте код после исправления ошибок

Компилируя снова, мы обнаружим, что первая ошибка исправлена, но в коде присутствуют другие ошибки:

Рисунок 7. Ошибки все еще присутствуют

Рисунок 7. Ошибки все еще присутствуют

Аналогично переходим к строке 31.

Однако в этот раз мы сделаем это при помощи правого щелчка мыши на вкладке "Ошибки" и выбора пункта "Перейти к строке".

Рисунок 8. Другой способ перехода к строке с ошибкой

Рисунок 8. Другой способ перехода к строке с ошибкой

Можно просто отметить ошибку в списке и нажать клавишу Enter.

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

Рисунок 9. Переход на строку с ошибкой

Рисунок 9a. Переход на строку с ошибкой

Для предупреждений (подобно тем, с которыми мы сталкивались в строке 16) выводится иконка в виде желтого треугольника слева от строки:

Рисунок 9b. Переход на строку с ошибкой

Рисунок 9b. Переход на строку с ошибкой

Очевидно, в строке 31 нет никаких проблем, однако описание ошибки имеет вид: "'STP' - unexpected token".

Теперь мы должны проверить предыдущую строку кода (номер 30) на наличие возможной ошибки. При внимательном рассмотрении мы обнаруживаем отсутствие точки с запятой после объявления "double ccminb = -95.0000" в строке 30, что и явилось причиной появления ошибки в строке 31.

Добавим точку с запятой после -95.0000 и скомпилируем код снова.

Сообщения об ошибке в строке 31 больше нет. Следующей является строка 100:

Рисунок 10. В коде все еще много ошибок

Рисунок 10. В коде все еще много ошибок

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

У вас возник этот вопрос?

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

Вернемся к нашей проблеме, проверим следующую ошибку в строке 100. В описании ошибки указано 'if' - expressions are not allowed on a global scope" , но я уверен в том, что в строке 100 нет этой ошибки, почему же возникла эта ошибка? Перейдем к строке 100.

Рисунок 11. Переходим к ошибке в коде

Рисунок 11. Переходим к ошибке в коде

В строке 100 никаких ошибок нет, и поскольку перед этим мы закончили исправление ошибки в строке 31, теперь мы можем быть уверены в том, что проблема в коде между 32 и 99 строками. Посмотрим выше, на строку 99 (там ошибки быть не может, поскольку это комментарий). Также если посмотреть выше, структуры типов MqlTick, MqlTradeRequest и MqlTradeResult объявлены правильно.

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

Рисунок 12. Смотрим на строку выше для выявления ошибки

Рисунок 12. Смотрим на строку выше для выявления ошибки

Добавляем открывающую фигурную скобку и компилируем код снова.

//--- Имеем ли мы достаточное количество баров для работы
   int Mybars=Bars(_Symbol,_Period);
   if(Mybars<60) // если количество баров меньше 60
    {
      Alert("Количество баров меньше 60, эксперт торговать не будет!!");
      return;
     }

После компиляции кода ошибки в строках 100, 107, 121, 126 и 129 полностью исчезли. Видите, как здорово двигаться шаг за шагом?

Рисунок 13. В коде еще присутствуют ошибки

Рисунок 13. В коде еще присутствуют ошибки

Далее в строке 56 мы видим две ошибки: "'cciVal1' - parameter conversion is not allowed" и "'cciVal1' - array is required".

Рассмотрев детально строку 56, приходим к выводу, что cciVal1 должен был быть объявлен как массив. Могло ли так получиться, что мы не объявили его как массив, а теперь пытаемся использовать его как массив? Давайте проверим раздел объявлений для подтверждения наших предположений.

//--- Другие переменные
int maHandle;               // хэндл индикатора Moving Average
int cciHandle1,cciHandle2;  // хэндлы индикаторов CCI
double maVal[];             // динамический массив для значений индикатора Moving Average
double cciVal1,cciVal2[];   // динамические массивы для значений индикаторов CCI
double p1_close,p2_close;   // переменные для цен закрытия 2-х предыдущих баров

Здесь мы видим, что мы ошибочно объявили переменную cciVal1 как число типа double (пропустив в объявлении квадратные скобки []), вместо того, чтобы объявить ее динамическим массивом.

Давайте добавим квадратные скобки (так же, как у cciVal2[]) и затем скомпилируем код:

//--- Другие переменные
int maHandle;               // хэндл индикатора Moving Average
int cciHandle1,cciHandle2;  // хэндлы индикаторов CCI
double maVal[];             // динамический массив для значений индикатора Moving Average
double cciVal1[],cciVal2[]; // динамические массивы для значений индикаторов CCI
double p1_close,p2_close;   // переменные для цен закрытия 2-х предыдущих баров

Рисунок 14. Количество ошибок в коде существенно сократилось

Рисунок 14. Количество ошибок в коде существенно сократилось

Смотрите! Сразу исчезло множество ошибок. Мы лишь скорректировали ошибку в строке 56, и при этом автоматически исправили некоторые другие ошибки. Произошло это по причине того, что ошибка в строке 56 являлась причиной появления других ошибок. Вот почему лучше придерживаться пошагового процесса при устранении ошибок в вашем коде.

Теперь перейдем к следующей ошибке в строке 103: "'GetLastError' - undeclared identifier". Минутку, GetLastError - это ведь функция. Чтобы разобраться в чем проблема, перейдем к строке 103:

//--- Скопируем текущую котировку в MQL5-структуру типа MqlTick
   if(!SymbolInfoTick(_Symbol,latest_price))
     {
      Alert("Ошибка получения последних котировок - Ошибка:",GetLastError,"!!"); // строка 103
      return;
     }

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

//--- Скопируем текущую котировку в MQL5-структуру типа MqlTick
   if(!SymbolInfoTick(_Symbol,latest_price))
     {
      Alert("Ошибка получения последних котировок - Ошибка:",GetLastError(),"!!"); // строка 103
      return;
     }

Теперь перейдем к строке 159, содержащей сообщение об ошибке:"'=' - l-value requiredи предупреждение: "expression is not Boolean". Посмотрим, что означают эти ошибки.

      else if(PositionGetInteger(POSITION_TYPE)=POSITION_TYPE_SELL)
        {
         Sell_opened=true; // открыта короткая позиция

Как видно, здесь мы присвоили значение POSITION_TYPE_SELL для  PositionGetInteger(POSITION_TYPE) в выражении условного оператора if, и ясно, что это не то, что мы намеревались сделать.

Мы хотели сделать сравнение. Теперь изменим операцию присваивания на операцию сравнения, записав "==" вместо  "=". Сделаем исправление и компилируем код.

      else if(PositionGetInteger(POSITION_TYPE)==POSITION_TYPE_SELL)
        {
         Sell_opened=true; // открыта короткая позиция

Отлично! Теперь осталась одна ошибка.

Перейдем на строку 292 для того чтобы понять, что является причиной ошибки "'PositionsTotal' - undeclared identifier". Минутку, вы помните, что ошибку такого типа мы уже видели прежде?

Это было с "GetlastError" на строке 103.  Возможно, здесь мы также забыли добавить пару скобок для PositionsTotal, поскольку это функция. Чтобы убедиться в этом, перейдем к строке 292.

bool CheckTrade(string otyp)
{
   bool marker = false;
   for (int i=1; i<=PositionsTotal;i++)  // строка 292
   {

Как мы и подозревали, это произошло потому, что мы забыли добавить пару скобок для функции PositionsTotal. Теперь добавим пару скобок в PositionsTotal() и скомпилируем код.

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

Рисунок 15. Все ошибки компиляции были успешно исправлены

Рисунок 15. Все ошибки компиляции были успешно исправлены

Замечательно! Теперь мы умеем исправлять любые ошибки, возникающие в процессе компиляции. Пришло время для отладки кода и его проверки на наличие ошибок времени выполнения.

Здесь мы не будем углубляться в детали отладки нашего кода, это описано в статье "Пошаговое руководство по написанию MQL5-советников для начинающих".

При начале отладки мы замечаем другую ошибку:

Рисунок 16. Ошибка времени выполнения, возникающая при отладке кода

Рисунок 16. Ошибка времени выполнения, возникающая при отладке кода

Нажмите кнопку OK и вы попадете прямо на строку кода, который сгенерировал ошибку.

Рисунок 17. Выявление строки кода, приведшей к возникновению ошибки времения выполнения

Рисунок 17. Выявление строки кода, приведшей к возникновению ошибки времения выполнения

Как можно видеть из рис.17, ошибка произошла в коде на строке 172.  Поскольку тип ошибки "Array out of range", это означает, что значение, которое мы намереваемся получить из массива находится за пределами допустимых значений. Для того чтобы разобраться в причине появления проблемы, перейдем к строке, в которой производится копирование индикаторных буферов в массивы.

//--- копируем новые значения наших индикаторов в массивы, используя индикаторные хэндлы
   if(CopyBuffer(maHandle,0,0,3,maVal)<0)
     {
      Alert("Ошибка копирования значений индикатора MA - Ошибка:",GetLastError(),"!");
      return;
     }
   if(CopyBuffer(cciHandle1,0,0,3,cciVal1)<0 || CopyBuffer(cciHandle2,0,0,3,cciVal2)<0)
     {
      Alert("Ошибка копирования значений индикатора CCI - Ошибка:",GetLastError(),"!");
      return;
     }

Как видно из вызова функций CopyBuffer, мы копируем только три значения (для баров 0, 1 и 2). Это означает, что мы можем обращаться к значениям maVal[0], maVal[1], maVal[2] и cciVal1[0], cciVal1[1], и cciVal1[2]. Но в нашем коде в строке 172 мы пытались получить значение элемента cciVal1[3], что и явилось причиной ошибки. Теперь остановим отладчик для исправления ошибки:


Рисунок 18. Останавливаем отладчик для исправления ошибки

Рисунок 18. Останавливаем отладчик для исправления ошибки

Для исправления ошибки нам нужно увеличить до 5  количество записей, копируемых из индикаторных буферов, таким образом, в случае необходимости, мы получим возможность получения значений элементов cciVal1[0], cciVal1[1], cciVal1[2], cciVal1[3] и cciVal1[4].

//--- копируем новые значения наших индикаторов в массивы, используя индикаторные хэндлы
   if(CopyBuffer(maHandle,0,0,5,maVal)<0)
     {
      Alert("Ошибка копирования значений индикатора MA - Ошибка:",GetLastError(),"!");
      return;
     }
   if(CopyBuffer(cciHandle1,0,0,5,cciVal1)<0 || CopyBuffer(cciHandle2,0,0,5,cciVal2)<0)
     {
      Alert("Ошибка копирования значений индикатора CCI - Ошибка:",GetLastError(),"!");
      return;
     }

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


Рисунок 19. Все ошибки исправлены, советник производит торговые операции в процессе отладки

Рисунок 19. Все ошибки исправлены, советник производит торговые операции в процессе отладки


2. Тестирование советника

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

Для запуска Тестера стратегий выберите пункт "Тестер стратегий" в разделе "Вид" главного меню:

Рисунок 20. Запускаем Тестер стратегий

Рисунок 20. Запускаем Тестер стратегий

2.1. Предварительное тестирование нашего советника

Итак, мы хотим протестировать нашего советника на истории по всем символам, заданным в окне "Обзор рынка". Это позволит нам выбрать валютные пары для оптимизации нашего советника. Убедитесь в том, что окно "Обзор рынка" содержит большинство из тех символов, для работы с которыми проектировался ваш советник.

Во вкладке "Настройки" выберите вашего советника, выберите нужный период тестирования и таймфрейм (можно также протестировать его на различных таймфреймах) и затем в поле "Оптимизация" выберите "Все символы, выбранные в окне "Обзор рынка".  В качестве параметров оптимизации выберите "Balance + max Profit Factor".

Рисунок 34. Предварительное тестирование эксперта на всех символах, заданных в окне "Обзор рынка"

Рисунок 21. Предварительное тестирование эксперта на всех символах, заданных в окне "Обзор рынка"

Настройки предварительной оптимизации приведены на рис. 21.

1. Выберите режим генерации тиков –(Каждый тик)

2. Выберите режим оптимизации –(Все символы, выбранные в окне "Обзор рынка)

3. Критерий оптимизации (Balance + max Profit Factor)

Подробное описание различных типов оптимизации можно посмотреть в справке клиентского терминала. Мы не собираемся производить форвардное тестирование, поэтому оставим значение "No" для параметра Форвард-период.

Для этого теста мы будем использовать следующие параметры советника (выделены зеленым цветом) во вкладке "Входные параметры".


Рисунок 35. Входные параметры для предварительного тестирования

Рисунок 22. Входные параметры для предварительного тестирования

После того, как значения установлены, переключитесь во вкладку "Настройки" и нажмите кнопку "Старт". После завершения тестирования во вкладке "Журнал" вы увидите сообщения, подобные приведенным на рис.23:

Рисунок 36. Предварительный тест завершен

Рисунок 23. Предварительный тест завершен

После того, как тест завершен, для просмотра результатов перейдем во вкладку "Результаты оптимизации":

Рисунок 37. Результаты оптимизации предварительного теста

Рисунок 24. Результаты оптимизации предварительного теста

Нас интересует символ, по которому получены наилучшие результаты целевых параметров оптимизации (Balance + max Profit Factor).

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

Рисунок 38. Анализ результатов предварительной оптимизации

Рисунок 25. Анализ результатов предварительной оптимизации

Из этих результатов следует, что наш советник может быть прибыльным на следующих символах (EURUSD, EURJPY, AUDUSD) для выбранного нами таймфрейма (H1). В дальнейшем, вы можете провести этот тест на другом таймфрейме, например получасовом, и посмотреть, что же получится. Можете рассматривать это призыв к действию, сделайте это и поделитесь результатами - нам всем это будет полезно.

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

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

  • Прибыльность (Profit Factor):

Прибыльность - это отношение всей полученной прибыли к общему убытку. Чем больше коэффициент прибыльности, тем более прибыльной является стратегия.

  • Просадка (Drawdown %):

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

  • Коэффициент восстановления (Recovery Factor):

Коэффициент восстановления представляет собой отношение прибыли к максимальной просадке. Этот параметр отражает степень риска торговой стратегии.

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

2.2. Оптимизируем эксперта

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

Сначала мы идем на вкладку "Настройки" для того чтобы включить оптимизацию, а затем выбираем критерий оптимизации.

Рисунок 39. Настройки оптимизации для советника

Рисунок 26. Настройки оптимизации для советника

1. Выберите режим генерации тиков (Каждый тик)

2. Выберите режим оптимизации (Быстрая (генетический алгоритм))

3. Выберите критерий оптимизации (Balance + max Profit Factor)

Узнать побольше о различных типах оптимизации можно в справке по терминалу. Поскольку мы не будем производить форвард-тестирование, оставим значение "No" для параметра Форвард-период. После установки параметров оптимизации, давайте настроим входные параметры советника, которые будут варьироваться в процессе оптимизации:

Рисунок 40. Оптимизируемые входные параметры

Рисунок 27. Оптимизируемые входные параметры

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

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

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

Установка значений параметров

Старт:

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

Шаг:

Шаг является значение инкремента для параметра Stop Loss. Если мы установим инкремент в 2, это означает, что в первом проходе он будет использовать значение 30, а в последующих проходах 32, 36, 34 и т.д. Однако это не означает то, что в процессе оптимизации будет производится последовательное изменение параметра. В процессе оптимизации тестер будет выбирать значения случайно, однако они всегда будут принадлежать множеству значений, полученному последовательным увеличением значений на 2, начиная со значений, указанный в столбцах "Старт" и заканчивая значениями, указанными в столбце "Стоп".

Стоп:

Это максимальное или наибольшее значение параметра, которое будет использоваться в процессе оптимизации. Здесь мы указали значение, равное 38. Это означает, что тестер стратегий будет использовать значения между 30 и 38 с шагом 2. Он никогда не будет использовать значения, равные или более 40.

Общее число тестов, которые будут проводится, зависит от настроек этих трех столбцов. В нашем примере тестер станет комбинировать 5 возможных значений для Stop Loss, в соответствии с шагом Steps вкладки "Входные параметры". С каждым из этих значений тестер стратегий будет комбинировать каждое из 8 возможных значений параметра Take Profit и т.д.

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

Убедитесь также в том, что установленное значение шага не приведет к слишком большому количеству вариантов. Например, если в качестве шага использовать значение, равное 1, то число возможных значений параметра Stop Loss станет равным 10. Как я уже говорил ранее, время, которое требуется для завершения серии оптимизации зависит от общего числа доступных агентов тестера, установленных в вашей системе.

Я надеюсь, что объяснил понятно.

После того, как мы закончили с настройкой входных параметров, вернемся снова во вкладку "Настройки" и нажмем на кнопку "Старт".

После завершения оптимизации мы можем видеть ее подробности во вкладке "Журнал":

Рисунок 43. Сообщения о завершении оптимизации во вкладке "Журнал"

Рисунок 28. Сообщения о завершении оптимизации во вкладке "Журнал"

Для просмотра результатов каждого теста, производящегося в настоящей момент или завершенного, мы идем во вкладку "Результаты оптимизации".

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

Рисунок 44. Отчет об оптимизации

Рисунок 29. Отчет об оптимизации

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

Рисунок 45. График оптимизации

Рисунок 30. График оптимизации

Ну и как вам этот график? Не волнуйтесь, точками здесь изображены проходы тестирования вашего советника и результаты, полученные при данном проходе. В нашем случае в качестве оптимизируемой величины была выбрана пара "Balance + max Profit factor".

2.3. Интерпретация результатов

Для успешной интерпретации отчета оптимизации, перейдем во вкладку "Результаты оптимизации". Вы обнаружите, что там не показаны некоторые параметры, например Profit factor, Expected Payoff, Drawdown % и т.д. Для того, чтобы отобразить их, кликните правой кнопкой мыши в любом месте вкладки "Результаты оптимизации" и выберите дополнительную информацию, которую вы хотите отображать, например:

Рисунок 46. Выбираем отображение параметра Drawdown% в результатах оптимизации

Рисунок 31. Выбираем отображение параметра Drawdown% в результатах оптимизации

  Рисунок 47. Выбираем отображение параметра Profit Factor в результатах оптимизации

Рисунок 32. Выбираем отображение параметра Profit Factor в результатах оптимизации

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

Optimization report Analysis

Рисунок 33. Анализ результатов оптимизации

Как видно из рисунка 48, наилучшие результаты для нашего советника были получены с параметрами, выделенными цветом в два набора (A и B). Теперь выбор полностью за вами. Все зависит от того, что вам требуется. Тем не менее, нас интересуют не только настройки, при которых была получена наибольшая прибыль, но параметры, при которых процент просадки (drawdown%) был минимальным.

Как вы видите, набор A (выделенный желтым цветом) обеспечивает наилучший результат  (Balance + max Profit Factor), равный 22381.71, с общей прибылью 924.10 , в то время как набор B (выделенный зеленым цветом) имеет второй лучший результат, равный 22159.25, но с большей прибылью, равной 936.55.

Набора параметров A имел меньшее значение параметра просадки (Drawdown%), равное of 1.78 , в то время как при тестировании с параметрами набора B просадка получилась больше и составила 1.95.

После окончания оптимизации вы можете заглянуть в каталог "<Папка данных терминала>\Tester\cache", где вы обнаружите файл cci_ma_ea.EURUSD.H1.0.xml, в котором содержатся численные значения всех параметров и характеристик, полученных в результате оптимизации.

Результаты оптимизации сохраняются в файле: ExpertName.Symbol.Period.GenerationMode.xml, где:

  • ExpertName - наименование оптимизируемого эксперта;
  • Symbol - символ;
  • Period - таймфрейм (M1,H1,...);
  • GenerationMode - режим генерации тиков (0 - "Каждый тик", 1 - "OHLC на M1", 2 - "Только цены открытия").

Этот файл можно открыть в Excel для последующего анализа.

2.4. Выбираем наилучшие результаты

Для получения наилучших результатов нам нужно снова посмотреть на график оптимизации. Вернемся во вкладку "График оптимизации".

Кликните правой кнопкой мыши в любом месте и в появившемся меню выберите "Линейный график (1D)".

Рисунок 49. Выбираем линейный (1D) график для анализа результатов

Рисунок 34. Выбираем линейный (1D) график для анализа результатов

Это позволит нам легко увидеть значения каждого параметра, при которых получаются наилучшие результаты.  Теперь вы можете начать анализировать каждый из параметров для поиска наилучших значений. Кликните правой кнопкой мыши на графике, выберите пункт "Ось X" и наименование параметра, который вас интересует. В результате получим график примерно следующего вида (параметр Stop Loss):

Рисунок 50. Используем результаты оптимизации для получения наилучших значений параметра StopLoss

Рисунок 35. Используем результаты оптимизации для получения наилучших значений параметра StopLoss

Благодаря результатам оптимизации мы видим, что что наилучшее значение Stoploss=34, TakeProfit=78, а лучший период CCI_Period1=62. Для получения наилучших значений параметров MAPeriod  и CCI_Period2, найдем их аналогичным образом.

Рисунок 51. Получаем наилучшие значения параметра периода Moving Average при помощи результатов оптимизации

Рисунок 36. Получаем наилучшие значения параметра периода Moving Average при помощи результатов оптимизации

По рисунку 51 видно, что наилучшие результаты получаются при MA_Period=26.

Рисунок 52. Получение наилучших значений параметра CCI_Period1 при помощи результатов оптимизации

Рисунок 37. Получение наилучших значений параметра CCI_Period1 при помощи результатов оптимизации

На этом графике видно, что наилучший результат получается при значении параметра CCI_Period1=62.

Рисунок 53. Получение наилучших значений параметра CCI_Period2 при помощи результатов оптимизации

Рисунок 38. Получение наилучших значений параметра CCI_Period2 при помощи результатов оптимизации

Из рисунка 53 следует, что самые лучшие результаты получаются при значениях параметра CCI_Period2, равных 28 и 30.

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

2.5. Финальный тест

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

Рисунок 54. Входные параметры финального теста

Рисунок 39. Входные параметры финального теста

Во вкладке "Настройки" Тестера стратегий мы отключим оптимизацию:

Рисунок 55. Настройки финального теста

Рисунок 40. Настройки финального теста

Теперь нажмем кнопку Start для начала теста. Результаты тестирования на истории станут доступны после завершения теста во вкладке "Результаты".

Рисунок 56. Результаты финального теста

Рисунок 41. Результаты финального теста

График результатов тестирования советника на истории находится во вкладке "График".

Рисунок 57. График результатов финального тестирования

Рисунок 42. График результатов финального тестирования


Выводы

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

Я надеюсь, что статья поможет вам в отладке, тестировании и оптимизации советников.

Перевод с английского произведен MetaQuotes Software Corp.
Оригинальная статья: https://www.mql5.com/en/articles/156

Прикрепленные файлы |
cci_ma_ea_rus.mq5 (14.67 KB)
MetaQuotes
Renat Fatkhullin | 12 окт 2010 в 17:51

Категорически не рекомендуется добавлять удаленных агентов, стоящих в том же каталоге.

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


Рекомендую полностью убрать из статьи блок про удаленных агентов.
gisip
gisip | 18 янв 2011 в 02:27

Здравствуйте!

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


fenix74
fenix74 | 15 янв 2014 в 15:45
Подскажите пожалуйста, есть подобная статья о по тестированию и оптимизации советников для MQL 4. Не могу найти хотя бы справочников кодов ошибок, примеров частых ошибок, приходится искать описание каждой ошибки через поиск в инете.
Rashid Umarov
Rashid Umarov | 9 окт 2015 в 08:15
fenix74:
Подскажите пожалуйста, есть подобная статья о по тестированию и оптимизации советников для MQL 4.
Статьи смотрели ? https://www.mql5.com/ru/articles/mt4/strategy_tester
Интервью с Берроном Паркером (ATC 2010) Интервью с Берроном Паркером (ATC 2010)

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

Простой пример построения индикатора с использованием нечеткой логики (Fuzzy Logic) Простой пример построения индикатора с использованием нечеткой логики (Fuzzy Logic)

Статья посвящена вопросам практического применения концепции нечеткой логики (fuzzy logic) для анализа финансовых рынков. Предложен пример индикатора, выдающего сигналы на основе двух нечетких правил, основанных на индикаторе Envelopes. Разработанный индикатор использует несколько индикаторных буферов: 7 буферов для расчетов, 5 буферов для вывода графиков и 2 буфера цвета.

Интервью с Валерием Мазуренко (ATC 2010) Интервью с Валерием Мазуренко (ATC 2010)

К концу первой торговой недели на первом месте оказался Валерий Мазуренко (notused) с мультивалютным экспертом ch2010. Ранее воспринимавший трейдинг как хобби, Валерий уже полгода пытается монетизировать свое «увлечение» и написать устойчивый советник для реальной торговли. В этом интервью экспертописатель делится своими взглядами на роль математики в трейдинге и рассказывает, почему объектно-ориентированный подход отлично подходит для написания мультивалютников.

Интервью с Борисом Одинцовым (ATC 2010) Интервью с Борисом Одинцовым (ATC 2010)

Борис Одинцов - один из самых ярких участников Чемпионата, преодолевший на третьей торговой неделе планку в $100 000. Стремительный взлет своего эксперта Борис объясняет благоприятным стечением обстоятельств. В этом интервью он рассказывает, каким вещам стоит уделять внимание в трейдинге и о том, какой рынок неблагоприятен для его эксперта.