Многие трейдеры при поиске или разработке торговой системы слышали о схеме "Три экрана", которую предложил когда-то Александр Элдер. Найдётся множество людей высказывающих своё мнение в интернете, что эта система не работает. И найдётся также много мнений, что из этого можно извлечь прибыль. Но необязательно верить ни тем ни другим. Всё всегда нужно проверять самому. А если Вы изучаете программирование, то всё тем более в Ваших руках, так как можно проверить работоспособность схемы на истории.
В этой статье разработаем схему для торговой системы типа "Три экрана Элдера" на MQL5. Разрабатывать эксперта будем начинать не с нуля, а просто модифицируем уже практически готовую под эту схему программу из предыдущей статьи "Использование индикаторов для формирования условий в эксперте". То есть, цель статьи также и показать, как можно легко модифицировать схемы уже готовых программ.
В эксперте из предыдущей статьи уже есть возможность включить/отключить уровни Stop Loss/Take Profit, Trailing Stop, наращивание объёма позиции, переворот позиции по противоположному сигналу. А все необходимы функции уже расставлены на своих местах. В итоге вся работа сводится к тому, что нужно всего лишь изменить список внешних параметров, добавив в него дополнительные опции и модифицировать некоторые уже имеющиеся функции.
В этой схеме в качестве примера сделаем так, чтобы сигналы на трёх таймфреймах формировались по индикатору Moving Average. После, для продолжения эксперимента с этой схемой, Вы можете вызывать любые другие индикаторы внеся небольшие изменения в код. Также сделаем так, чтобы таймфреймы для каждого "экрана" можно было устанавливать. А если в параметр, который отвечает за период индикатора, установлено нулевое значение, то это будет означать, что этот "экран" не используется. То есть, можно настроить систему на один или два таймфрейма.
Перед тем, как начать, сделайте копию папки с экспертом из предыдущей статьи и переименуйте её.
Начнём с внешних параметров. Ниже представлен код обновлённого списка. Выделены новые строки. Таймфреймы объявлены с типом перечисления ENUM_TIMEFRAMES. Из выпадающего списка можно будет выбрать любой таймфрейм.
Параметр AmountMove, переменную gAmountMove и функцию CorrectingParameters() я убрал для упрощения примера схемы. Те, кто заинтересован этим условием могут попробовать реализовать его самостоятельно. Также в файле ENUMS.mqh нужно убрать перечисление индикаторов, так как в этом эксперте будет использоваться только один индикатор.
В начале файла нужно добавить константу AVAL, значение которой будет использоваться вместо gAmountMove:
Так как на каждом таймфрейме будет использоваться отдельный индикатор, то для получения хэндла каждого из них понадобится своя переменная:
Проверка нового бара будет производиться по минимальному таймфрейму. Минимальный таймфрейм во внешних параметрах необязательно устанавливать в последовательности: максимальный, средний, минимальный. Можно и в обратной последовательности, и вообще в любой последовательности. Поэтому нужна функция, которая будет определять минимальный ТФ из всех указанных.
Так как идентификаторы (PERIOD_MN1, PERIOD_W1, PERIOD_D1 и т.д.), которые относятся к перечислению таймфреймов имеют целочисленный тип (int) и перечислены от минимального к максимальному, то их можно проверять с помощью функций fmax() и fmin(). Обратитесь к Справке, чтобы узнать более подробную информацию об этих функциях.
Для сохранения минимального значения таймфрейма создадим ещё одну переменную на глобальном уровне:
Так как эксперт может быть настроен на работу не только по трём таймфреймам, но и на одном или двух, то нужно учесть все варианты при определении минимального таймфрейма. Ниже представлен код функции MinTF():
Функцию MinTF() нужно будет вызывать при инициализации эксперта в функции OnInit(). Значение переменной gTFmin затем используется в функциях NewBar() и GetDataBars().
Функция GetHandlesIndicators() теперь выглядит так, как показано ниже. Для каждого индикатора указываются свой период и таймфрейм.
В файле ARRAYS.mqh нужно добавить массивы для значений индикаторов (для каждого таймфрейма отдельно):
Функция GetDataIndicators() для получения значений индикаторов теперь выглядит так, как показано ниже. Осуществляется проверка валидности хэндлов и, если всё в порядке, то массивы заполняются значениями индикаторов.
В функции GetSignal() также, как и при определении минимального таймфрейма учитываются все возможные варианты состояний внешних параметров, которые относятся к формированию условий на открытие позиций. Ниже представлен её код:
В принципе это всё. Схема для торговых систем типа "Три экрана Элдера" готова. В любой момент её можно изменить, заменив индикаторы или, при необходимости, добавив какие-то дополнительные условия.
Далее оптимизируем параметры и посмотрим на результаты. Настройки тестера установим, как на картинке ниже:
Параметры эксперта для оптимизации были установлены так, как показано ниже. Таймфреймы можно установить на оптимизацию, но я предпочитаю их устанавливать вручную.
Оптимизация будет длиться приблизительно один час на двухъядерном компьютере. Ниже представлен График оптимизации:
Для теста выберем результат по максимальному значению фактора восстановления:
Как видно из статьи, можно довольно быстро изменить эксперта при наличии основных функций. Буквально изменив блок сигналов и хэндлы индикаторов можно получить новую торговую систему.
В разделе Торговые системы также можно посмотреть/скачать другие мультитаймфреймовые эксперты и результаты тестов. Ниже можно скачать архив с файлами и сет с настройками эксперта описанного в статье. Если возникли вопросы, спрашивайте в комментариях ниже.
Скачать архив с файлами эксперта exp3tf.zip
Скачать сет с настройками эксперта
В этой статье разработаем схему для торговой системы типа "Три экрана Элдера" на MQL5. Разрабатывать эксперта будем начинать не с нуля, а просто модифицируем уже практически готовую под эту схему программу из предыдущей статьи "Использование индикаторов для формирования условий в эксперте". То есть, цель статьи также и показать, как можно легко модифицировать схемы уже готовых программ.
В эксперте из предыдущей статьи уже есть возможность включить/отключить уровни Stop Loss/Take Profit, Trailing Stop, наращивание объёма позиции, переворот позиции по противоположному сигналу. А все необходимы функции уже расставлены на своих местах. В итоге вся работа сводится к тому, что нужно всего лишь изменить список внешних параметров, добавив в него дополнительные опции и модифицировать некоторые уже имеющиеся функции.
В этой схеме в качестве примера сделаем так, чтобы сигналы на трёх таймфреймах формировались по индикатору Moving Average. После, для продолжения эксперимента с этой схемой, Вы можете вызывать любые другие индикаторы внеся небольшие изменения в код. Также сделаем так, чтобы таймфреймы для каждого "экрана" можно было устанавливать. А если в параметр, который отвечает за период индикатора, установлено нулевое значение, то это будет означать, что этот "экран" не используется. То есть, можно настроить систему на один или два таймфрейма.
Перед тем, как начать, сделайте копию папки с экспертом из предыдущей статьи и переименуйте её.
Начнём с внешних параметров. Ниже представлен код обновлённого списка. Выделены новые строки. Таймфреймы объявлены с типом перечисления ENUM_TIMEFRAMES. Из выпадающего списка можно будет выбрать любой таймфрейм.
//+------------------------------------------------------------------+ //| ВНЕШНИЕ ПАРАМЕТРЫ ЭКСПЕРТА | //+------------------------------------------------------------------+ sinput long MagicNumber = 777; // Magic Number sinput int Deviation = 10; // Deviation (p) //--- input ENUM_TIMEFRAMES TF01 = PERIOD_W1; // TF01 input int PeriodIndTF01 = 5; // Period Indicator TF01 //--- input ENUM_TIMEFRAMES TF02 = PERIOD_D1; // TF02 input int PeriodIndTF02 = 5; // Period Indicator TF02 //--- input ENUM_TIMEFRAMES TF03 = PERIOD_H4; // TF03 input int PeriodIndTF03 = 5; // Period Indicator TF03 //--- input double TakeProfit = 100; // Take Profit (p) input double StopLoss = 50; // Stop Loss (p) input double TrailingSL = 10; // Step Trailing Stop (p) input bool Reverse = true; // On/Off Reverse input double Lot = 0.1; // Lot input double Increase = 0.1; // Increase Volume input double StepIncrease = 10; // Step Increase (p) sinput bool InfoPanel = true; // Info Panel //---
Параметр AmountMove, переменную gAmountMove и функцию CorrectingParameters() я убрал для упрощения примера схемы. Те, кто заинтересован этим условием могут попробовать реализовать его самостоятельно. Также в файле ENUMS.mqh нужно убрать перечисление индикаторов, так как в этом эксперте будет использоваться только один индикатор.
В начале файла нужно добавить константу AVAL, значение которой будет использоваться вместо gAmountMove:
//--- #define AVAL 3 // Кол-во значений, которые нужно получить у индикаторов и баров //---
Так как на каждом таймфрейме будет использоваться отдельный индикатор, то для получения хэндла каждого из них понадобится своя переменная:
//+------------------------------------------------------------------+ //| ГЛОБАЛЬНЫЕ ПЕРЕМЕННЫЕ | //+------------------------------------------------------------------+ int hdlTF01=INVALID_HANDLE, // Хэндл индикатора на первом ТФ hdlTF02=INVALID_HANDLE, // Хэндл индикатора на втором ТФ hdlTF03=INVALID_HANDLE; // Хэндл индикатора на третьем ТФ //---
Проверка нового бара будет производиться по минимальному таймфрейму. Минимальный таймфрейм во внешних параметрах необязательно устанавливать в последовательности: максимальный, средний, минимальный. Можно и в обратной последовательности, и вообще в любой последовательности. Поэтому нужна функция, которая будет определять минимальный ТФ из всех указанных.
Так как идентификаторы (PERIOD_MN1, PERIOD_W1, PERIOD_D1 и т.д.), которые относятся к перечислению таймфреймов имеют целочисленный тип (int) и перечислены от минимального к максимальному, то их можно проверять с помощью функций fmax() и fmin(). Обратитесь к Справке, чтобы узнать более подробную информацию об этих функциях.
Для сохранения минимального значения таймфрейма создадим ещё одну переменную на глобальном уровне:
//--- // Переменная для определения минимального ТФ ENUM_TIMEFRAMES gTFmin=NULL; //---
Так как эксперт может быть настроен на работу не только по трём таймфреймам, но и на одном или двух, то нужно учесть все варианты при определении минимального таймфрейма. Ниже представлен код функции MinTF():
//+------------------------------------------------------------------+ //| ОПРЕДЕЛЕНИЕ МИНИМАЛЬНОГО ТФ ДЛЯ ПРОВЕРКИ НОВОГО БАРА | //+------------------------------------------------------------------+ void MinTF() { // Условия для одного таймфрейма if(PeriodIndTF01>0 && PeriodIndTF02==0 && PeriodIndTF03==0) { gTFmin=TF01; return; } //--- if(PeriodIndTF01==0 && PeriodIndTF02>0 && PeriodIndTF03==0) { gTFmin=TF02; return; } //--- if(PeriodIndTF01==0 && PeriodIndTF02==0 && PeriodIndTF03>0) { gTFmin=TF03; return; } //--- // Условия для двух таймфреймов if(PeriodIndTF01>0 && PeriodIndTF02>0 && PeriodIndTF03==0) { gTFmin=fmin(TF01,TF02); return; } //--- if(PeriodIndTF01==0 && PeriodIndTF02>0 && PeriodIndTF03>0) { gTFmin=fmin(TF02,TF03); return; } //--- if(PeriodIndTF01>0 && PeriodIndTF02==0 && PeriodIndTF03>0) { gTFmin=fmin(TF01,TF03); return; } //--- // Условия для трёх таймфреймов // Определение минимального ТФ в два этапа if(PeriodIndTF01>0 && PeriodIndTF02>0 && PeriodIndTF03>0) { gTFmin=fmin(TF01,TF02); gTFmin=fmin(gTFmin,TF03); return; } } //---
Функцию MinTF() нужно будет вызывать при инициализации эксперта в функции OnInit(). Значение переменной gTFmin затем используется в функциях NewBar() и GetDataBars().
Функция GetHandlesIndicators() теперь выглядит так, как показано ниже. Для каждого индикатора указываются свой период и таймфрейм.
//+------------------------------------------------------------------+ //| ПОЛУЧАЕТ ХЭНДЛЫ ИНДИКАТОРОВ | //+------------------------------------------------------------------+ void GetHandlesIndicators() { // Получим хэндлы индикаторов, которые указаны в параметрах if(PeriodIndTF01>0 && hdlTF01==INVALID_HANDLE) { hdlTF01=iMA(_Symbol,TF01,PeriodIndTF01,0,MODE_SMA,PRICE_CLOSE); } //--- if(PeriodIndTF02>0 && hdlTF02==INVALID_HANDLE) { hdlTF02=iMA(_Symbol,TF02,PeriodIndTF02,0,MODE_SMA,PRICE_CLOSE); } //--- if(PeriodIndTF03>0 && hdlTF03==INVALID_HANDLE) { hdlTF03=iMA(_Symbol,TF03,PeriodIndTF03,0,MODE_SMA,PRICE_CLOSE); } //--- // Если неудалось получить хендл индикатора для первого таймфрейма if(PeriodIndTF01>0 && hdlTF01==INVALID_HANDLE) { Print("Не удалось получить хэндл индикатора для TF01!"); } //--- // Если неудалось получить хендл индикатора для второго таймфрейма if(PeriodIndTF02>0 && hdlTF02==INVALID_HANDLE) { Print("Не удалось получить хэндл индикатора для TF02!"); } //--- // Если неудалось получить хендл индикатора для третьего таймфрейма if(PeriodIndTF01>0 && hdlTF03==INVALID_HANDLE) { Print("Не удалось получить хэндл индикатора для TF03!"); } } //---
В файле ARRAYS.mqh нужно добавить массивы для значений индикаторов (для каждого таймфрейма отдельно):
//--- // Массив для значений индикатора double bfr_ind_TF01[],bfr_ind_TF02[],bfr_ind_TF03[]; //---
Функция GetDataIndicators() для получения значений индикаторов теперь выглядит так, как показано ниже. Осуществляется проверка валидности хэндлов и, если всё в порядке, то массивы заполняются значениями индикаторов.
//+------------------------------------------------------------------+ //| ПОЛУЧАЕТ ЗНАЧЕНИЯ ИНДИКАТОРОВ | //+------------------------------------------------------------------+ bool GetDataIndicators() { // Если хэндлы индикаторов не получены, то... if((PeriodIndTF01>0 && hdlTF01==INVALID_HANDLE) || (PeriodIndTF02>0 && hdlTF02==INVALID_HANDLE) || (PeriodIndTF03>0 && hdlTF03==INVALID_HANDLE) ) { // ...попробуем получить их ещё раз GetHandlesIndicators(); } //--- // Если используется первый ТФ и хэндл индикатора был получен if(PeriodIndTF01>0 && hdlTF01!=INVALID_HANDLE) { // Установим обратный порядок индексации (... 3 2 1 0) ArraySetAsSeries(bfr_ind_TF01,true); //--- // Получим значения индикатора if(CopyBuffer(hdlTF01,0,0,AVAL,bfr_ind_TF01)<AVAL) { Print("Не удалось скопировать значения ("+ _Symbol+"; "+TFtoS(_Period)+") в массив bfr_ind_TF01! Ошибка ("+ IS(GetLastError())+"): "+ErrorDesc(GetLastError())+""); //--- return(false); } } //--- // Если используется второй ТФ и хэндл индикатора был получен if(PeriodIndTF02>0 && hdlTF02!=INVALID_HANDLE) { // Установим обратный порядок индексации (... 3 2 1 0) ArraySetAsSeries(bfr_ind_TF02,true); //--- // Получим значения индикатора if(CopyBuffer(hdlTF02,0,0,AVAL,bfr_ind_TF02)<AVAL) { Print("Не удалось скопировать значения ("+ _Symbol+"; "+TFtoS(_Period)+") в массив bfr_ind_TF02! Ошибка ("+ IS(GetLastError())+"): "+ErrorDesc(GetLastError())+""); //--- return(false); } } //--- // Если используется третий ТФ и хэндл индикатора был получен if(PeriodIndTF03>0 && hdlTF03!=INVALID_HANDLE) { // Установим обратный порядок индексации (... 3 2 1 0) ArraySetAsSeries(bfr_ind_TF03,true); //--- // Получим значения индикатора if(CopyBuffer(hdlTF03,0,0,AVAL,bfr_ind_TF03)<AVAL) { Print("Не удалось скопировать значения ("+ _Symbol+"; "+TFtoS(_Period)+") в массив bfr_ind_TF03! Ошибка ("+ IS(GetLastError())+"): "+ErrorDesc(GetLastError())+""); //--- return(false); } } //--- return(true); } //---
В функции GetSignal() также, как и при определении минимального таймфрейма учитываются все возможные варианты состояний внешних параметров, которые относятся к формированию условий на открытие позиций. Ниже представлен её код:
//+------------------------------------------------------------------+ //| ВОЗВРАЩАЕТ СИГНАЛ | //+------------------------------------------------------------------+ int GetSignal() { // Сигнал на продажу (1): // Если текущее значение индикаторов на сформировавшихся барах // ниже, чем предыдущие, то это сигнал на продажу //--- // Условия для одного таймфрейма if(PeriodIndTF01>0 && PeriodIndTF02==0 && PeriodIndTF03==0) { if(bfr_ind_TF01[1]<bfr_ind_TF01[2]) { return(1); } } //--- if(PeriodIndTF01==0 && PeriodIndTF02>0 && PeriodIndTF03==0) { if(bfr_ind_TF02[1]<bfr_ind_TF02[2]) { return(1); } } //--- if(PeriodIndTF01==0 && PeriodIndTF02==0 && PeriodIndTF03>0) { if(bfr_ind_TF03[1]<bfr_ind_TF03[2]) { return(1); } } //--- // Условия для двух таймфреймов if(PeriodIndTF01>0 && PeriodIndTF02>0 && PeriodIndTF03==0) { if(bfr_ind_TF01[1]<bfr_ind_TF01[2] && bfr_ind_TF02[1]<bfr_ind_TF02[2]) { return(1); } } //--- if(PeriodIndTF02>0 && PeriodIndTF03>0 && PeriodIndTF01==0) { if(bfr_ind_TF02[1]<bfr_ind_TF02[2] && bfr_ind_TF03[1]<bfr_ind_TF03[2]) { return(1); } } //--- if(PeriodIndTF01>0 && PeriodIndTF03>0 && PeriodIndTF02==0) { if(bfr_ind_TF01[1]<bfr_ind_TF01[2] && bfr_ind_TF03[1]<bfr_ind_TF03[2]) { return(1); } } //--- // Условия для трёх таймфреймов if(PeriodIndTF01>0 && PeriodIndTF02>0 && PeriodIndTF03>0) { if(bfr_ind_TF01[1]<bfr_ind_TF01[2] && bfr_ind_TF02[1]<bfr_ind_TF02[2] && bfr_ind_TF03[1]<bfr_ind_TF03[2] ) { return(1); } } //--- // Сигнал на покупку (0): // Если текущее значение индикаторов на сформировавшихся барах // выше, чем предыдущие, то это сигнал на покупку //--- // Условия для одного таймфрейма if(PeriodIndTF01>0 && PeriodIndTF02==0 && PeriodIndTF03==0) { if(bfr_ind_TF01[1]>bfr_ind_TF01[2]) { return(0); } } //--- if(PeriodIndTF01==0 && PeriodIndTF02>0 && PeriodIndTF03==0) { if(bfr_ind_TF02[1]>bfr_ind_TF02[2]) { return(0); } } //--- if(PeriodIndTF01==0 && PeriodIndTF02==0 && PeriodIndTF03>0) { if(bfr_ind_TF03[1]>bfr_ind_TF03[2]) { return(0); } } //--- // Условия для двух таймфреймов if(PeriodIndTF01>0 && PeriodIndTF02>0 && PeriodIndTF03==0) { if(bfr_ind_TF01[1]>bfr_ind_TF01[2] && bfr_ind_TF02[1]>bfr_ind_TF02[2]) { return(0); } } //--- if(PeriodIndTF02>0 && PeriodIndTF03>0 && PeriodIndTF01==0) { if(bfr_ind_TF02[1]>bfr_ind_TF02[2] && bfr_ind_TF03[1]>bfr_ind_TF03[2]) { return(0); } } //--- if(PeriodIndTF01>0 && PeriodIndTF03>0 && PeriodIndTF02==0) { if(bfr_ind_TF01[1]>bfr_ind_TF01[2] && bfr_ind_TF03[1]>bfr_ind_TF03[2]) { return(0); } } //--- // Условия для трёх таймфреймов if(PeriodIndTF01>0 && PeriodIndTF02>0 && PeriodIndTF03>0) { if(bfr_ind_TF01[1]>bfr_ind_TF01[2] && bfr_ind_TF02[1]>bfr_ind_TF02[2] && bfr_ind_TF03[1]>bfr_ind_TF03[2] ) { return(0); } } //--- return(-1); } //---
В принципе это всё. Схема для торговых систем типа "Три экрана Элдера" готова. В любой момент её можно изменить, заменив индикаторы или, при необходимости, добавив какие-то дополнительные условия.
Далее оптимизируем параметры и посмотрим на результаты. Настройки тестера установим, как на картинке ниже:
Параметры эксперта для оптимизации были установлены так, как показано ниже. Таймфреймы можно установить на оптимизацию, но я предпочитаю их устанавливать вручную.
Оптимизация будет длиться приблизительно один час на двухъядерном компьютере. Ниже представлен График оптимизации:
Для теста выберем результат по максимальному значению фактора восстановления:
Как видно из статьи, можно довольно быстро изменить эксперта при наличии основных функций. Буквально изменив блок сигналов и хэндлы индикаторов можно получить новую торговую систему.
В разделе Торговые системы также можно посмотреть/скачать другие мультитаймфреймовые эксперты и результаты тестов. Ниже можно скачать архив с файлами и сет с настройками эксперта описанного в статье. Если возникли вопросы, спрашивайте в комментариях ниже.
Скачать архив с файлами эксперта exp3tf.zip
Скачать сет с настройками эксперта
Комментариев нет :
Отправить комментарий