
Автоматизация торговых стратегий с помощью MQL5 (Часть 1): Система Profitunity (Торговый хаос Билла Вильямса)
Введение
В данной статье мы исследуем систему Profitunity, торговую систему, разработанную Биллом Вильямсом, которая ставит своей целью извлечение прибыли из рыночного "хаоса", используя несколько ключевых индикаторов, а также покажем, как автоматизировать ее на языке программирования MetaQuotes Language 5 (MQL5). Мы начнем с обзора стратегии и ее основных принципов. Затем пройдемся по процессу ее реализации на языке MQL5, уделяя основное внимание написанию кода для важнейших индикаторов и автоматизации сигналов входа и выхода. Далее мы протестируем и оптимизируем систему, чтобы обеспечить ее работу в различных рыночных условиях. И наконец, мы подведем итоги, обсудив потенциал и эффективность системы Profitunity для автоматизированного трейдинга. Данная статья состоит из следующих разделов:
- Обзор системы Profitunity
- Реализация стратегии на языке MQL5
- Тестирование и оптимизация стратегии
- Заключение
Дойдя до конца данной статьи, вы получите ясное понимание того, как автоматизировать систему Profitunity с помощью языка MQL5: от реализации ее ключевых индикаторов до оптимизации ее работы. Благодаря этому вы получить инструменты, которые позволят вам усилить вашу торговую стратегию и воспользоваться рыночным "хаосом" для улучшения потенциальной результативности торговли. Приступим.
Обзор системы Profitunity
Система Profitunity, разработанная Биллом Вильямсом, использует несколько специальных индикаторов, которые позволяют нам понимать хаотичные движения на рынке и действовать в соответствии с ними. Стратегия сочетает в себе возможности трендовых индикаторов и индикаторов моментума для создания динамичной торговой методики с высокой скоростью реагирования. Система выявляет развороты тренда и ускорение рынка, помогая нам находить торговые сетапы с высокой вероятностью реализации. Ключевые индикаторы, используемые в стратегии:
- Фракталы (Fractals)
- Аллигатор (Alligator)
- Awesome Oscillator (AO)
- Accelerator Oscillator (AC)
Все эти индикаторы работают вместе, предоставляя ключевые данные о рыночных условиях и предлагая сигналы входа и выхода. Давайте детальнее ознакомимся с отдельными настройками индикаторов, которые применяются в стратегии.
Настройки индикаторов
Индикатор Fractals. Выявляет точки разворота на рынке. Фракталы формируются, когда имеется пять подряд идущих баров, из которых бар, находящийся посередине, является самым высоким или самым низким. Они сигнализируют о потенциальном начале нового тренда или развороте цены, что помогает помечать локальные максимумы или минимумы и дает возможность заранее выявить возможную смену тренда. Что касается настроек, период для фракталов по умолчанию равен 2 или 5. Это означает, что индикатор ищет паттерны, в которых один бар окружен с каждой из сторон двумя барами, которые выше (для фракталов вниз) или ниже (для фракталов вверх) него. Вот как это выглядит на графике.
Индикатор Alligator. Данный индикатор представляет собой сочетание трех сглаженных скользящих средних, известных как "челюсть" (Jaw), "зубы" (Teeth) и "губы" (Lips): вместе они позволяют определить тренд на рынке. Взаимодействие между этими линиями помогает распознать, находится рынок в тренде или в консолидации. Когда линии начинают расходиться, это сигнализирует о тренде, а когда они сходятся, это говорит о том, что рынок находится в фазе консолидации.
Настройки:
- Jaw (синяя линия): Период 13, сглаживание на 8 баров
- Teeth (красная линия): Период 8, сглаживание на 5 баров
- Lips (зеленая линия): Период 5, сглаживание на 3 бара
Индикатор помогает нам выявить направление и продолжительность тренда, а также точки входа в рынок и выхода из него. Вот его настройки на графике.
Индикатор Awesome Oscillator (AO). Данный индикатор – это индикатор моментума, который рассчитывает разницу между простыми скользящими средними по медианной цене с периодом 34 и периодом 5. Это помогает измерить силу и направление тренда, отобразив разницу между двумя такими скользящими средними в виде гистограммы. Данный индикатор использует настройки по умолчанию.
Обычно AO используется для выявления бычьего или медвежьего моментума на рынке и обнаружения смены тренда. Гистограмма выше нулевой линии указывает на восходящий моментум, а гистограмма ниже нулевой линии сигнализирует о нисходящем моментуме.
Индикатор Accelerator Oscillator (AC). Данный индикатор является производным от индикатора AO и показывает ускорение рыночного моментума. Он дает представление о том, ускоряется или замедляется рыночный моментум, что крайне важно для выявления изменений в тренде до того, как они полностью разовьются. Индикатор AC осциллирует вокруг нулевой линии, перемещаясь между зеленой (положительной) и красной (отрицательной) зонами. Его настройки также заданы по умолчанию.
Индикатор AC используется совместно с индикатором AO для поиска подтверждения силы рынка и изменений моментума, давая уверенность перед тем как входить в сделку в том, что рынок с достаточной силой движется в одном направлении. Теперь давайте взглянем на условия входа и выхода, используемые в системе. Индикаторы AO и AC покажут следующую картину.
Условия входа и выхода
Система использует для сделок входа и выхода несколько особых условий, которые основаны на последовательных сигналах индикаторов Fractal, Alligator, Accelerator Oscillator (AC) и Awesome Oscillator (AO). Сигналы, работая вместе, обеспечивают, чтобы сделки инициировались только тогда, когда рынок продемонстрирует уверенное подтверждение направления, тем самым снижается риск ложных сигналов.
Условия входа в покупку:
- Сигнал индикатора Fractal: Возникает сигнал фрактала вниз (Fractal Down), когда движение цены формирует последовательность возрастающих минимумов, что предполагает потенциальный разворот цены вверх.
- Пробой линии индикатора Alligator: Синяя линия аллигатора ("челюсть") пересекается снизу вверх, указывая на начало восходящего тренда.
- Подтверждение на индикаторе Accelerator Oscillator (AC): Индикатор AC находится в зеленой зоне, что указывает на бычий моментум и говорит о силе тренда.
- Подтверждение на индикаторе Awesome Oscillator (AO): Гистограмма AO пересекает нулевую линию снизу вверх, также подтверждая восходящий моментум.
Условие покупки:
Вход в покупку срабатывает после того, как гистограмма индикатора AO пересекает нулевую линию снизу, подтверждая тем самым рост в рамках бычьего моментума. Это признак того, что развивается сильный восходящий тренд, и здесь будет точка нашего открытия длинной позиции по рынку. Ниже представлен пример сигнала на покупку на графике MetaTrader 5 .
Условия входа в продажу:
- Сигнал индикатора Fractal: Возникает сигнал фрактала вверх (Fractal Up), когда движение цены формирует последовательность убывающих максимумов, что предполагает потенциальный разворот цены вверх.
- Пробой линии индикатора Alligator: Синяя линия аллигатора ("челюсть") пересекается сверху вниз, указывая на начало нисходящего тренда.
- Подтверждение на индикаторе Accelerator Oscillator (AC): Индикатор AC находится в красной зоне, подтверждая сильный медвежий моментум и указывая на высокую вероятность продолжительного нисходящего движения.
- Подтверждение на индикаторе Awesome Oscillator (AO): Гистограмма индикатора AO пересекает нулевую линию сверху вниз, сигнализируя о медвежьем тренде.
Условие продажи:
Вход в продажу срабатывает после того, как гистограмма индикатора AO пересекает нулевую линию сверху, подтверждая нисходящий моментум. Это сигнализирует о том, что рынок вероятнее всего продолжит движение вниз, и здесь будет точка нашего открытия короткой позиции по рынку.
Условия выхода или разворота:
- Разворот линии индикатора Alligator: Происходит разворот зеленой линии аллигатора ("губы"), что создает предположение об окончании текущего тренда. Направление "губ" в сторону разворота сообщает, что цена в настоящий момент может разворачиваться либо консолидироваться.
- Разворот индикатора Accelerator Oscillator (AC): Индикатор AC переходит из зеленой зоны в красную (либо наоборот), сигнализируя тем самым о потенциальном изменении моментума. Это ранний признак того, что рыночный моментум изменяется, и текущий тренд может завершаться.
- Разворот Awesome Oscillator (AO): Гистограмма индикатора AO пересекает нулевую линию в противоположном направлении, что дает дополнительное подтверждение того, что вероятен разворот тренда.
Условие выхода:
Можно использовать любые или все из вышеперечисленных условий выхода, однако в нашем случае мы будем принимать решение о выходе из позиции, когда гистограммы индикатора AO переходят в противоположную зону, указывая на изменение рыночного моментума.
Комбинируя вышеупомянутые индикаторы, система Profitunity предлагает отличный метод для выявления разворотов рынка и возможного возникновения сильных трендов. В следующем разделе мы обсудим, как реализовать эти условия входа и выхода на языке MQL5, обеспечив тем самым полную автоматизацию данной стратегии.
Реализация стратегии на языке MQL5
После изучения всех теоретических материалов о торговой стратегии Билла Уильямса под названием Profitunity мы можем наконец перейти к автоматизации теории и разработке эксперта (советника) на языке MetaQuotes Language 5 (MQL5) для MetaTrader 5.
Для создания эксперта (советника) перейдите в ваш терминал MetaTrader 5, нажмите на вкладку "Сервис" и откройте "Редактор MetaQuotes Language", либо просто нажмите F4 на вашей клавиатуре. Как вариант, вы можете нажать на иконку IDE (интегрированной среды разработки) на панели инструментов. Это откроет среду Редактора MetaQuotes Language , позволяющую писать торговых роботов, технические индикаторы, скрипты и библиотеки функций.
Как только MetaEditor откроется, перейдите на вкладку "Файл" на панели инструментов и кликните по полю "Новый файл" либо просто нажмите CTRL + N, чтобы создать новый документ. Также, как вариант, вы можете нажать на иконку "Создать" на панели инструментов. В результате откроется окно Мастера MQL.
В открывшемся окне мастера выберите "Советник (шаблон)" и нажмите "Далее".
В общих параметрах советника укажите имя вашего советника в поле "Имя". Учтите, что для указания или создания папки, которая не существует, необходимо использовать обратную косую черту перед именем советника. Например, в нашем случае у нас по умолчанию указано "Experts\". Это означает, что наш советник будет создан в папке "Experts", и мы сможем найти его там. Другие разделы достаточно прямолинейны, но вы можете перейти по ссылке внизу окна мастера, чтобы узнать подробную процедуру для них.
После указания желаемого имени файла советника, нажмите "Далее" и затем "Готово". Теперь, после выполнения всех вышеуказанных действий мы готовы писать код и программировать нашу стратегию.
В первую очередь, мы начнем с того, что определим некоторые метаданные советника (эксперта). Сюда входят имя советника, сведения об авторских правах, а также ссылка на сайт MetaQuotes. Мы также указываем версию советника, у которое задано значение "1.00".
//+------------------------------------------------------------------+ //| 1. PROFITUNITY (TRADING CHAOS BY BILL WILLIAMS).mq5 | //| Copyright 2024, ALLAN MUNENE MUTIIRIA. #@Forex Algo-Trader. | //| https://forexalgo-trader.com | //+------------------------------------------------------------------+ #property copyright "Copyright 2024, ALLAN MUNENE MUTIIRIA. #@Forex Algo-Trader" #property link "https://forexalgo-trader.com" #property description "1. PROFITUNITY (TRADING CHAOS BY BILL WILLIAMS)" #property version "1.00"
Такой код отобразит метаданные системы при загрузке программы. Теперь мы можем перейти к следующему шагу и добавить несколько глобальных переменных, которыми мы будем пользоваться внутри программы. Во-первых, мы включаем экземпляр сделки, используя #include в начале исходного кода. В результате мы получим доступ к классу CTrade, который мы будем использовать для создания объекта сделки. Это важнейший шаг, поскольку нам это понадобится для открытия сделок.
#include <Trade/Trade.mqh>
CTrade obj_Trade;
Предпроцессор заменит строку #include <Trade/Trade.mqh> на содержимое файла Trade.mqh. Угловые скобки указывают на то, что файл Trade.mqh будет взят из стандартной директории (обычно terminal_installation_directory\MQL5\Include). Текущая директория не включена в поиск. Данную строку можно разместить в любом месте программы, но обычно все включения размещаются в начале исходного кода (для улучшения структуры кода и упрощения поиска). Объявление объекта obj_Trade класса CTrade предоставит нам простой доступ к методам, содержащимся в этом классе, за что стоит поблагодарить разработчиков MQL5.
После этого нам нужно объявить несколько важных обработчиков индикаторов, которые мы будем использовать в торговой системе.
int handle_Fractals = INVALID_HANDLE; //--- Initialize fractals indicator handle with an invalid handle value int handle_Alligator = INVALID_HANDLE; //--- Initialize alligator indicator handle with an invalid handle value int handle_AO = INVALID_HANDLE; //--- Initialize Awesome Oscillator (AO) handle with an invalid handle value int handle_AC = INVALID_HANDLE; //--- Initialize Accelerator Oscillator (AC) handle with an invalid handle value
Здесь мы задаем первоначальные переменные, которые будут содержать хэндлы для каждого технического индикатора в программе. В частности, мы инициализируем четыре переменные типа integer: "handle_Fractals", "handle_Alligator", "handle_AO" и "handle_AC» со значением INVALID_HANDLE.
Каждый из этих хэндлов будет выступать в качестве ссылки для доступа к соответствующему индикатору во всем коде. Присваивая исходное значение "INVALID_HANDLE", мы обеспечиваем, чтобы каждая переменная хэндла явно показывала недействительное состояние до момента, пока позднее в коде не произойдет ее надлежащая инициализация. Такая конфигурация предотвращает ошибки, связанные с использованием неинициализированного хэндла, и помогает обнаруживать сбои в загрузке какого-либо индикатора в процессе инициализации.
Коротко говоря, вот что будет делать каждый из индикаторов в отдельности:
- "handle_Fractals" будет хранить хэндл для индикатора Fractals;
- "handle_Alligator" будет хранить хэндл для индикатора Alligator;
- "handle_AO" будет хранить хэндл для индикатора Awesome Oscillator;
- "handle_AC" будет хранить хэндл для индикатора Accelerator Oscillator.
Далее нам необходимо определить и инициализировать массивы и константы, требуемые для хранения и обработки данных от индикаторов, используемых в данном советнике, которые мы специально получаем из инициализированных хэндлов индикаторов. Мы будем делать это последовательно, чтобы все было просто и понятно для последующих обращений.
double fractals_up[]; //--- Array to store values for upward fractals double fractals_down[]; //--- Array to store values for downward fractals double alligator_jaws[]; //--- Array to store values for Alligator's Jaw line double alligator_teeth[]; //--- Array to store values for Alligator's Teeth line double alligator_lips[]; //--- Array to store values for Alligator's Lips line double ao_values[]; //--- Array to store values of the Awesome Oscillator (AO) double ac_color[]; //--- Array to store color status of the Accelerator Oscillator (AC) #define AC_COLOR_UP 0 //--- Define constant for upward AC color state #define AC_COLOR_DOWN 1 //--- Define constant for downward AC color state
Мы начнем с создания двух массивов, "fractals_up" и "fractals_down", которые будут хранить значения фракталов вверх и фракталов вниз, соответственно. Эти массивы позволят нам отслеживать определенные фрактальные точки, помогая выявлять значимые ценовые развороты или паттерны.
Затем мы создаем три массива, а именно "alligator_jaws", "alligator_teeth" и "alligator_lips", для хранения значений различных линий индикатора Alligator. Благодаря хранению этих значений в отдельных массивах мы можем эффективно отслеживать состояние каждой из линий индикатора Alligator и обращаться к ним за торговыми сигналами.
Также мы определяем массив "ao_values", в котором будут храниться значения индикатора Awesome Oscillator (AO). Индикатор AO будет помогать нам выявлять рыночный моментум и тренды, а хранение этих значений позволит нам анализировать изменения во времени и применять их к нашим торговым условиям.
Наконец, мы объявляем массив "ac_color" для хранения цветового статуса индикатора Accelerator Oscillator (AC). Данный массив будет содержать информацию о восходящем или нисходящем движении индикатора AC в виде цветового состояния. Чтобы упростить этот процесс, объявим две константы: "AC_COLOR_UP" (со значением 0) и "AC_COLOR_DOWN" (со значением 1). Эти константы будут представлять цветовые состояния индикатора AC, при этом зеленый цвет (вверх) указывает на рост моментума, а красный (вниз) – на замедление тренда. Такая конфигурация упростит нашу логику, когда позднее мы будем проверять статус индикатора AC для торговых сигналов.
Вы могли заметить, что мы можем легко сохранять значения наших индикаторов напрямую за исключением фракталов. Это связано с тем, что их значения доступны сразу для каждого бара. Однако что касается фракталов, они формируют определенные точки свинга, которые находятся на расстоянии не менее 3 баров от текущего. Таким образом, мы не можем получить напрямую получать бары с фракталами, поскольку они формируются при соблюдении определенных условий. Поэтому нам необходима некая логика для отслеживания предыдущего значения фрактала, а также его направления. Мы применяем следующую логику.
double lastFractal_value = 0.0; //--- Variable to store the value of the last detected fractal enum fractal_direction {FRACTAL_UP, FRACTAL_DOWN, FRACTAL_NEUTRAL}; //--- Enum for fractal direction states fractal_direction lastFractal_direction = FRACTAL_NEUTRAL; //--- Variable to store the direction of the last fractal
Здесь мы определяем переменные и перечисление для хранения и управления данными о фрактале, обнаруженном последним в рамках нашего торгового анализа. Мы начинаем с объявления переменной "lastFractal_value" и инициализируем ее значением "0.0". Данная переменная будет хранить числовое значение последнего фрактала, который мы обнаруживаем на графике. Отслеживая данное значение, мы можем использовать его для сравнения с текущей ценой и анализа фрактальных формаций на предмет потенциальных торговых сигналов.
Далее мы определяем перечисление "fractal_direction" с тремя возможными состояниями: "FRACTAL_UP", "FRACTAL_DOWN" и "FRACTAL_NEUTRAL". Эти состояния представляют направление последнего фрактала:
- "FRACTAL_UP" будет указывать на фрактал вверх, сигнализируя о потенциально медвежьих условиях.
- "FRACTAL_DOWN" будет указывать на фрактал вниз, сигнализируя о потенциально бычьих условиях.
- "FRACTAL_NEUTRAL" представляет состояние, когда не было подтверждено какое-либо определенное направление фрактала.
Наконец, мы объявляем переменную "lastFractal_direction" типа "fractal_direction" и инициализируем ее значением "FRACTAL_NEUTRAL". Эта переменная будет хранить направление последнего обнаруженного фрактала, позволяя нам оценивать направление рынка в рамках торговой логики на основании данных о последнем выявленном фрактале.
Теперь мы наконец можем перейти к самой логике обработки кода. Нам нужно будет инициализировать наши индикаторы, и поэтому мы сразу перейдем к обработчику событий OnInit , который вызывается и исполняется каждый раз при каждой инициализации программы.//+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+ int OnInit(){ //--- //--- return(INIT_SUCCEEDED); //--- Return successful initialization status }
Попросту говоря, это обработчик событий инициализации по умолчанию, который мы будем использовать для инициализации логики управления нашей программы. Далее нам необходимо инициализировать индикаторы так, чтобы они были прикреплены к графику, и мы могли использовать их для извлечения данных и принятия торговых решений. Сперва мы инициализируем индикатор фракталов, как указано ниже.
handle_Fractals = iFractals(_Symbol,_Period); //--- Initialize the fractals indicator handle if (handle_Fractals == INVALID_HANDLE){ //--- Check if the fractals indicator failed to initialize Print("ERROR: UNABLE TO INITIALIZE THE FRACTALS INDICATOR. REVERTING NOW!"); //--- Print error if fractals initialization failed return (INIT_FAILED); //--- Exit initialization with failed status }
Здесь мы инициализируем переменную "handle_Fractals" путем вызова функции iFractals. Данная функция создает хэндл для индикатора Fractals, которые применяется к указанному символу (представленному аргументом _Symbol) и текущему периоду графика (аргумент _Period). Присваивая переменной "handle_Fractals" значение, возвращаемое функцией iFractals, мы предоставляем доступ к данным индикатора, которые мы впоследствии сможем использовать для анализа фрактальных формаций в рамках нашей стратегии.
После попытки инициализации индикатора фракталов, мы убеждаемся в ее успешности, проверяя, имеет ли переменная "handle_Fractals" значение "INVALID_HANDLE". Значение "INVALID_HANDLE" указывает, что индикатор не удалось инициализировать, что могло произойти по разным причинам, таким как недостаток системных ресурсов или некорректные параметры.
Если инициализация не удалась, мы используем функцию Print для вывода сообщения об ошибке "ERROR: UNABLE TO INITIALIZE THE FRACTALS INDICATOR. REVERTING NOW!" в журнал. Данное сообщение будет явно обозначать проблему, что облегчит устранение неисправностей. Затем мы возвращаем INIT_FAILED на выходе функции OnInit, что сигнализирует о том, что процесс инициализации не удалось завершить успешно. Эта проверка помогает убедиться в том, что советник не продолжит работать с неполными настройками, что может привести к ошибкам в исполнении. Аналогичную проверку делаем для инициализации индикатора Alligator.
handle_Alligator = iAlligator(_Symbol,_Period,13,8,8,5,5,3,MODE_SMMA,PRICE_MEDIAN); //--- Initialize the alligator indicator with specific settings if (handle_Alligator == INVALID_HANDLE){ //--- Check if the alligator indicator failed to initialize Print("ERROR: UNABLE TO INITIALIZE THE ALLIGATOR INDICATOR. REVERTING NOW!"); //--- Print error if alligator initialization failed return (INIT_FAILED); //--- Exit initialization with failed status }
Мы инициализируем переменную "handle_Alligator" путем вызова функции iAlligator. Индикатор Alligator требует несколько параметров для определения его трех линий ("челюсти", "зубы" и "губы"), каждая из которых реагирует на рыночные тренды. Мы задаем эти настройки следующим образом: "13" в качестве периода "челюстей", "8" в качестве периода "зубов" и "5" в качестве периода "губ". Также мы задаем значения сдвига "8", "5" и "3" для каждой линии, ставим метод расчета MODE_SMMA (сглаженная скользящая средняя) и тип цены PRICE_MEDIAN.
После попытки инициализировать индикатор Alligator мы проверяем, имеет ли переменная "handle_Alligator" действительное значение. Если она равняется INVALID_HANDLE, это указывает на то, что процесс инициализации не удался. Это могло произойти в связи с недостаточностью ресурсов или некорректностью параметров, что не позволяет индикатору Alligator функционировать корректно.
Если инициализация не удалась, мы вызываем функцию Print для вывода сообщения об ошибке: "ERROR: UNABLE TO INITIALIZE THE ALLIGATOR INDICATOR. REVERTING NOW!" Это сообщение предупредит нас о типе проблемы, что упрощает процесс ее диагностики и решения. После вывода сообщения мы возвращаем INIT_FAILED на выходе функции OnInit, которое указывает на то, что инициализация не завершилась успешно.
Мы используем аналогичный подход при инициализации индикаторов AO и AC, как описано ниже.
handle_AO = iAO(_Symbol,_Period); //--- Initialize the Awesome Oscillator (AO) indicator handle if (handle_AO == INVALID_HANDLE){ //--- Check if AO indicator failed to initialize Print("ERROR: UNABLE TO INITIALIZE THE AO INDICATOR. REVERTING NOW!"); //--- Print error if AO initialization failed return (INIT_FAILED); //--- Exit initialization with failed status } handle_AC = iAC(_Symbol,_Period); //--- Initialize the Accelerator Oscillator (AC) indicator handle if (handle_AC == INVALID_HANDLE){ //--- Check if AC indicator failed to initialize Print("ERROR: UNABLE TO INITIALIZE THE AC INDICATOR. REVERTING NOW!"); //--- Print error if AC initialization failed return (INIT_FAILED); //--- Exit initialization with failed status }
Как только все индикаторы будут успешно инициализированы, мы сможем добавить их на график автоматически, когда программа загрузится. Для этого мы используем следующую логику.
if (!ChartIndicatorAdd(0,0,handle_Fractals)){ //--- Add the fractals indicator to the main chart window and check for success Print("ERROR: UNABLE TO ADD THE FRACTALS INDICATOR TO CHART. REVERTING NOW!"); //--- Print error if fractals addition failed return (INIT_FAILED); //--- Exit initialization with failed status }
Здесь мы пытаемся добавить индикатор Fractals в главное окно графика с помощью функции ChartIndicatorAdd. Мы передаем "0" в качестве идентификатора графика (указываем на текущий график) и указываем "0" в качестве идентификатора окна, указывая на главное окно графика. Переменная "handle_Fractals", которую мы ранее инициализировали для хранения хэндла индикатора Fractals, передается для добавления данного конкретного индикатора.
После вызова функции ChartIndicatorAdd мы проверяем, успешно ли он прошел. Если функция возвращает "false" в виде "!", это указывает на то, что индикатор Fractals не удалось добавить на график. Здесь сбой мог возникнуть из-за ограничений графика или недостаточности ресурсов. В этом случае мы отображаем сообщение об ошибке с помощью Print, чтобы получить уведомление: "ERROR: UNABLE TO ADD THE FRACTALS INDICATOR TO CHART. REVERTING NOW!" Данное сообщение поможет нам быстро выявить источник проблемы в ходе отладки.
Если добавить индикатор не удалось, мы возвращаем INIT_FAILED на выходе функции OnInit со статусом сбоя, обеспечивая тем самым, что советник не будет работать, если на графике отсутствует индикатор Fractals, что помогает впоследствии предотвратить ошибки при исполнении путем подтверждения визуальной доступности индикатора. Схожая логика используется для добавления индикатора Alligator, поскольку он также в главном окне, как указано ниже.
if (!ChartIndicatorAdd(0,0,handle_Alligator)){ //--- Add the alligator indicator to the main chart window and check for success Print("ERROR: UNABLE TO ADD THE ALLIGATOR INDICATOR TO CHART. REVERTING NOW!"); //--- Print error if alligator addition failed return (INIT_FAILED); //--- Exit initialization with failed status }
Для добавления других индикаторов применяется аналогичный подход, но с тем отличием, что теперь изменяются подокна, поскольку мы создаем новое подокно для каждого соответствующего индикатора, как показано ниже.
if (!ChartIndicatorAdd(0,1,handle_AO)){ //--- Add the AO indicator to a separate subwindow and check for success Print("ERROR: UNABLE TO ADD THE AO INDICATOR TO CHART. REVERTING NOW!"); //--- Print error if AO addition failed return (INIT_FAILED); //--- Exit initialization with failed status } if (!ChartIndicatorAdd(0,2,handle_AC)){ //--- Add the AC indicator to a separate subwindow and check for success Print("ERROR: UNABLE TO ADD THE AC INDICATOR TO CHART. REVERTING NOW!"); //--- Print error if AC addition failed return (INIT_FAILED); //--- Exit initialization with failed status }
Мы добавляем индикаторы Awesome Oscillator (AO) и Accelerator Oscillator (AC) в отдельные подокна на графике, обеспечивая, чтобы каждый из них имел свое выделенное представление. Чтобы добиться этого, мы используем функцию ChartIndicatorAdd для каждого индикатора. Мы указываем "0" в качестве идентификатора графика (указываем на текущий график) и используем отдельные идентификаторы окон: "1" для индикатора AO и "2" для индикатора AC, каждый из которых появляется в уникальном подокне. Нумерация здесь крайне важна, так как каждый индикатор должен быть в своем отдельном окне, поэтому вам необходимо следить за надлежащей индексацией подокон.
Затем мы проверим успешность каждого добавления в отдельности. Если ChartIndicatorAdd возвращает "false" для индикатора AO либо для индикатора AC, это сигнализирует о том, что процесс добавления прошел неуспешно. В случае сбоя мы выводим сообщение об ошибке через "Print", чтобы уточнить, какой конкретно индикатор не удалось загрузить. К примеру, если не удалось добавить индикатор AO, мы выводим "ERROR: UNABLE TO ADD THE AO INDICATOR TO CHART. REVERTING NOW!" Аналогичным образом, мы выводим сообщение об ошибке в случае неудачного добавления индикатора AC.
Если не удалось добавить любой из индикаторов, мы немедленно возвращаем INIT_FAILED, выходим из функции OnInit и запрещаем дальнейшее исполнение. Чтобы убедиться, что все в порядке, мы можем вывести хэндлы индикаторов в журнал.
Print("HANDLE ID FRACTALS = ",handle_Fractals); //--- Print the handle ID for fractals Print("HANDLE ID ALLIGATOR = ",handle_Alligator); //--- Print the handle ID for alligator Print("HANDLE ID AO = ",handle_AO); //--- Print the handle ID for AO Print("HANDLE ID AC = ",handle_AC); //--- Print the handle ID for AC
При запуске программы мы получаем следующие данные инициализации.
На изображении мы можем увидеть, что идентификаторы хэндлов начинаются с 10 и меняются до 13. Идентификаторы хэндлов критически важны в MQL5, поскольку они позволяют нам обращаться на каждый из индикаторов из любого места в жизненном цикле советника. Когда функция, такая как CopyBuffer, извлекает значения из индикатора, она полагается на эти хэндлы для доступа к корректным данным. В этом примере целые числа представляют собой идентификаторы для каждого из инициализированных индикаторов. Каждый из идентификаторов действует в качестве уникального "указателя" в среде MQL5, связывая каждый хэндл к соответствующему ему индикатору. Это помогает советнику узнавать, какие данные индикаторов извлекать из памяти, поддерживая эффективное исполнение и четкую организацию задач на основе индикаторов.
Теперь все, что нам нужно, это настроить массивы для хранения данных в виде временных рядов.
ArraySetAsSeries(fractals_up,true); //--- Set the fractals_up array as a time series ArraySetAsSeries(fractals_down,true); //--- Set the fractals_down array as a time series ArraySetAsSeries(alligator_jaws,true); //--- Set the alligator_jaws array as a time series ArraySetAsSeries(alligator_teeth,true); //--- Set the alligator_teeth array as a time series ArraySetAsSeries(alligator_lips,true); //--- Set the alligator_lips array as a time series ArraySetAsSeries(ao_values,true); //--- Set the ao_values array as a time series ArraySetAsSeries(ac_color,true); //--- Set the ac_color array as a time series
Здесь мы настраиваем каждый массив как временной ряд, т.е. данные внутри каждого массива будут упорядочены от самых новых к самым старым значениям. Делаем мы это, применяя функцию ArraySetAsSeries к каждому массиву и передавая "true" в качестве второго аргумента. Эта настройка обеспечивает, чтобы последняя точка данных всегда содержалась на индексе 0, что особенно полезно в приложениях для трейдинга, где получение доступа к последнему значению крайне важно для принятия решений в реальном времени.
Начнем с того, что настроим массивы "fractals_up" и "fractals_down" в виде временных рядов, что позволит нам эффективно отслеживать последние значения фракталов вверх и фракталов вниз. Аналогичным образом, мы применяем данный принцип организации к массивам "alligator_jaws", "alligator_teeth" и "alligator_lips", которые представляют три линии индикатора Alligator. Это позволит нам получать доступ к последним значениям каждой из линий в реальном времени, упрощая тем самым обнаружение любых изменений в рыночных трендах.
Также мы аналогичным образом настраиваем массив "ao_values", хранящий данные индикатора Awesome Oscillator. Настраивая его в виде временного ряда, мы обеспечиваем, чтобы последнее значение осциллятора всегда было мгновенно доступно для наших расчетов. Наконец, мы применяем данную структуру к массиву "ac_color", которая отслеживает цветовой статус индикатора Accelerator Oscillator, так, чтобы к последнему цветовому статусу можно было получить мгновенный доступ. Ниже представлен полный фрагмент кода, отвечающего за беспрепятственную и чистую инициализацию.
//+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+ int OnInit(){ //--- handle_Fractals = iFractals(_Symbol,_Period); //--- Initialize the fractals indicator handle if (handle_Fractals == INVALID_HANDLE){ //--- Check if the fractals indicator failed to initialize Print("ERROR: UNABLE TO INITIALIZE THE FRACTALS INDICATOR. REVERTING NOW!"); //--- Print error if fractals initialization failed return (INIT_FAILED); //--- Exit initialization with failed status } handle_Alligator = iAlligator(_Symbol,_Period,13,8,8,5,5,3,MODE_SMMA,PRICE_MEDIAN); //--- Initialize the alligator indicator with specific settings if (handle_Alligator == INVALID_HANDLE){ //--- Check if the alligator indicator failed to initialize Print("ERROR: UNABLE TO INITIALIZE THE ALLIGATOR INDICATOR. REVERTING NOW!"); //--- Print error if alligator initialization failed return (INIT_FAILED); //--- Exit initialization with failed status } handle_AO = iAO(_Symbol,_Period); //--- Initialize the Awesome Oscillator (AO) indicator handle if (handle_AO == INVALID_HANDLE){ //--- Check if AO indicator failed to initialize Print("ERROR: UNABLE TO INITIALIZE THE AO INDICATOR. REVERTING NOW!"); //--- Print error if AO initialization failed return (INIT_FAILED); //--- Exit initialization with failed status } handle_AC = iAC(_Symbol,_Period); //--- Initialize the Accelerator Oscillator (AC) indicator handle if (handle_AC == INVALID_HANDLE){ //--- Check if AC indicator failed to initialize Print("ERROR: UNABLE TO INITIALIZE THE AC INDICATOR. REVERTING NOW!"); //--- Print error if AC initialization failed return (INIT_FAILED); //--- Exit initialization with failed status } if (!ChartIndicatorAdd(0,0,handle_Fractals)){ //--- Add the fractals indicator to the main chart window and check for success Print("ERROR: UNABLE TO ADD THE FRACTALS INDICATOR TO CHART. REVERTING NOW!"); //--- Print error if fractals addition failed return (INIT_FAILED); //--- Exit initialization with failed status } if (!ChartIndicatorAdd(0,0,handle_Alligator)){ //--- Add the alligator indicator to the main chart window and check for success Print("ERROR: UNABLE TO ADD THE ALLIGATOR INDICATOR TO CHART. REVERTING NOW!"); //--- Print error if alligator addition failed return (INIT_FAILED); //--- Exit initialization with failed status } if (!ChartIndicatorAdd(0,1,handle_AO)){ //--- Add the AO indicator to a separate subwindow and check for success Print("ERROR: UNABLE TO ADD THE AO INDICATOR TO CHART. REVERTING NOW!"); //--- Print error if AO addition failed return (INIT_FAILED); //--- Exit initialization with failed status } if (!ChartIndicatorAdd(0,2,handle_AC)){ //--- Add the AC indicator to a separate subwindow and check for success Print("ERROR: UNABLE TO ADD THE AC INDICATOR TO CHART. REVERTING NOW!"); //--- Print error if AC addition failed return (INIT_FAILED); //--- Exit initialization with failed status } Print("HANDLE ID FRACTALS = ",handle_Fractals); //--- Print the handle ID for fractals Print("HANDLE ID ALLIGATOR = ",handle_Alligator); //--- Print the handle ID for alligator Print("HANDLE ID AO = ",handle_AO); //--- Print the handle ID for AO Print("HANDLE ID AC = ",handle_AC); //--- Print the handle ID for AC ArraySetAsSeries(fractals_up,true); //--- Set the fractals_up array as a time series ArraySetAsSeries(fractals_down,true); //--- Set the fractals_down array as a time series ArraySetAsSeries(alligator_jaws,true); //--- Set the alligator_jaws array as a time series ArraySetAsSeries(alligator_teeth,true); //--- Set the alligator_teeth array as a time series ArraySetAsSeries(alligator_lips,true); //--- Set the alligator_lips array as a time series ArraySetAsSeries(ao_values,true); //--- Set the ao_values array as a time series ArraySetAsSeries(ac_color,true); //--- Set the ac_color array as a time series //--- return(INIT_SUCCEEDED); //--- Return successful initialization status }
Далее мы можем перейти к обработчику событий OnTick, в котором будет содержаться логика управления.
//+------------------------------------------------------------------+ //| Expert tick function | //+------------------------------------------------------------------+ void OnTick(){ //--- }Попросту говоря, это обработчик событий тиков по умолчанию, который мы будем использовать в качестве основы для нашей логики управления. Далее мы извлекаем значения данных из хэндлов индикаторов для дальнейшего анализа.
if (CopyBuffer(handle_Fractals,0,2,3,fractals_up) < 3){ //--- Copy upward fractals data; check if copying is successful Print("ERROR: UNABLE TO COPY THE FRACTALS UP DATA. REVERTING!"); //--- Print error message if failed return; } if (CopyBuffer(handle_Fractals,1,2,3,fractals_down) < 3){ //--- Copy downward fractals data; check if copying is successful Print("ERROR: UNABLE TO COPY THE FRACTALS DOWN DATA. REVERTING!"); //--- Print error message if failed return; }
Здесь мы копируем данные из буферов "fractals_up" и "fractals_down" индикатора "handle_Fractals" в соответствующие им массивы. Мы используем функцию CopyBuffer для извлечения данных из хэндла индикатора. В частности, мы предпринимаем попытки скопировать 3 точки данных, начиная с третьего (считая с последнего) бара (с индексом 2), как для верхнего, так и для нижнего фракталов.
В первую очередь, проверим, возвращает ли функция значение меньше чем 3, что означало бы, что в массив "fractals_up" было скопировано менее трех значений. Если это произойдет, мы выведем сообщение об ошибке ("ERROR: UNABLE TO COPY THE FRACTALS UP DATA. REVERTING!") и выйдем из функции во избежание любой дальнейшей работы программы с неполными данными.
Аналогичным образом, мы попытаемся скопировать данные о фракталах вниз в массив "fractals_down" с помощью той же функции CopyBuffer. И снова, если копирование не удастся (возвращается менее трех значений), мы выведем соответствующее сообщение об ошибке ("ERROR: UNABLE TO COPY THE FRACTALS DOWN DATA. REVERTING!") и выйдем из функции во избежание любой дальнейших проблем. Данный подход обеспечивает, что наша программа не будет работать с недействительными или неполными данными, поддерживая таким образом надежность нашей торговой логики. Проверяя, что скопировано верное количество значений, мы можем предотвратить потенциальные ошибки при анализе фракталов, который крайне важен для обнаружения точек разворота рынка.
Однако вы могли заметить, что номера буферов индикаторов отличаются – 0 и 1. Это крайне важные индексы, с которые вам придется часто иметь дело, ведь они представляют актуальные буферы разметки для значений индикаторов. На иллюстрации ниже продемонстрировано, почему мы используем специальные индексы.
На иллюстрации мы можем видеть, что первым идет фрактал вверх, то есть его индекс – 0, а фрактал вниз идет вторым с индексом 1. Согласно той же логике мы размечаем линии аллигатора.
if (CopyBuffer(handle_Alligator,0,0,3,alligator_jaws) < 3){ //--- Copy Alligator's Jaw data Print("ERROR: UNABLE TO COPY THE ALLIGATOR JAWS DATA. REVERTING!"); return; } if (CopyBuffer(handle_Alligator,1,0,3,alligator_teeth) < 3){ //--- Copy Alligator's Teeth data Print("ERROR: UNABLE TO COPY THE ALLIGATOR TEETH DATA. REVERTING!"); return; } if (CopyBuffer(handle_Alligator,2,0,3,alligator_lips) < 3){ //--- Copy Alligator's Lips data Print("ERROR: UNABLE TO COPY THE ALLIGATOR LIPS DATA. REVERTING!"); return; }
Здесь, поскольку буферы относятся к трем линиям, вы можете заметить, что индексы буферов начинаются с 0, затем идут 1 и 2. В случае когда имеется только один буфер, как в случае с индикатором AO, у нас будет только индекс буфера 0 для цен, как представлено ниже.
if (CopyBuffer(handle_AO,0,0,3,ao_values) < 3){ //--- Copy AO data Print("ERROR: UNABLE TO COPY THE AO DATA. REVERTING!"); return; }
Аналогичная ситуация была бы с индикатором AC, если бы нам были интересны его значения. Нам же нужно лишь узнать цвет сформировавшейся гистограммы. Его можно получить, пользуясь той же логикой нумерации буферов, однако в этом случае цветовые буферы в основном размечаются на следующем недоступном индексе буфера в окне данных. Таким образом, в нашем случае это 0+1=1, как показано ниже.
if (CopyBuffer(handle_AC,1,0,3,ac_color) < 3){ //--- Copy AC color data Print("ERROR: UNABLE TO COPY THE AC COLOR DATA. REVERTING!"); return; }
На самом деле, чтобы получить буферы цвета, вам достаточно открыть окно свойств индикатора, и цветовые индексы появятся на вкладке параметров.
После извлечения и хранения данных мы сможем использовать их для принятия торговых решений. Чтобы сберечь ресурсы, мы будем проводить проверки каждого бара, но не каждого сгенерированного тика. Таким образом, нам необходима логика для обнаружения вновь сформированных баров.
if (isNewBar()){ //--- Check if a new bar has formed //--- }
Здесь мы используем пользовательскую функцию, код которой представлен ниже.
//+------------------------------------------------------------------+ //| IS NEW BAR FUNCTION | //+------------------------------------------------------------------+ bool isNewBar(){ static int prevBars = 0; //--- Store previous bar count int currBars = iBars(_Symbol,_Period); //--- Get current bar count for the symbol and period if (prevBars == currBars) return (false); //--- If bars haven't changed, return false prevBars = currBars; //--- Update previous bar count return (true); //--- Return true if new bar is detected }
Здесь мы определяем булеву функцию "isNewBar", которая проверяет, появился ли новый бар на графике, что помогает нам определять, когда формируется новая свеча или бар, что крайне важно для обновления и перерасчета торговых условий. Мы начинаем с объявления статической переменной "prevBars" типа integer и инициализируем ее значением "0". Ключевое слово "static" обеспечивает, что значение "prevBars" сохраняется между разными вызовами функции вместо того, чтобы сбрасываться при каждом вызове. Это позволяет нам сохранять количество баров после предыдущего выполнения функции.
Далее мы определяем локальную целочисленную переменную "currBars" и используем встроенную функцию iBars для извлечения текущего количества баров для выбранного символа (_Symbol) и периода (_Period). Данная функция подсчитывает общее количество баров, доступных для заданного таймфрейма, и сохраняет его в переменной "currBars".
Затем мы сравниваем переменные "prevBars" и "currBars". Если два значения равны, это означает, что с момента последнего вызова функции новых баров не сформировалось, поэтому мы возвращаем "false", чтобы обозначить, что новые бары не появились. Если количество баров изменилось (т.е. сформировался новый бар), условие не срабатывает, и мы присваиваем переменной "prevBars" актуальное количество баров (значение "currBars"), чтобы отслеживать новое значение. Наконец, мы возвращаем "true", чтобы сообщить о том, что был обнаружен новый бар.
Теперь в рамках этой функции мы можем определять и сохранять данные о фракталах путем проверки и обновления значения и направления последнего обнаруженного фрактала на основе данных, сохраненных в массивах "fractals_up" и "fractals_down".
const int index_fractal = 0; if (fractals_up[index_fractal] != EMPTY_VALUE){ //--- Detect upward fractal presence lastFractal_value = fractals_up[index_fractal]; //--- Store fractal value lastFractal_direction = FRACTAL_UP; //--- Set last fractal direction as up } if (fractals_down[index_fractal] != EMPTY_VALUE){ //--- Detect downward fractal presence lastFractal_value = fractals_down[index_fractal]; lastFractal_direction = FRACTAL_DOWN; }
Начнем с того, что объявим целочисленную константу "index_fractal" и присваиваем ей значение 0. Данная константа будет представлять индекс текущих данных о фракталах, которые мы хотим проверить. В этом случае мы проверяем первый фрактал в массивах.
Затем мы проверяем условие, что элемент "fractals_up[index_fractal]" не равен EMPTY_VALUE, и если это так, значит имеется действительный фрактал вверх. Если наше условие истинно, мы сохраняем значение фрактала вверх в переменную "lastFractal_value". Мы также присвоили переменной "lastFractal_direction" значение "FRACTAL_UP", чтобы обозначить, что последним обнаруженным фракталом является фрактал вверх.
Аналогичным образом, мы проверяем, что значение элемента "fractals_down[index_fractal]" не равняется EMPTY_VALUE, что указывает на наличие фрактала вниз. Если данное условие истинно, мы сохраняем значение фрактала вниз в переменную "lastFractal_value" и присваиваем переменной "lastFractal_direction" значение "FRACTAL_DOWN", чтобы обозначить, что последним обнаруженным фракталом является фрактал вверх. Затем мы можем записать полученные данные в лог и проверить их действительность.
if (lastFractal_value != 0.0 && lastFractal_direction != FRACTAL_NEUTRAL){ //--- Ensure fractal is valid Print("FRACTAL VALUE = ",lastFractal_value); Print("FRACTAL DIRECTION = ",getLastFractalDirection()); }
В результате данные индикатора Fractals будут записаны в лог. Мы написали пользовательскую функцию для получения направления фрактала, код которой представлен ниже.
//+------------------------------------------------------------------+ //| FUNCTION TO GET FRACTAL DIRECTION | //+------------------------------------------------------------------+ string getLastFractalDirection(){ string direction_fractal = "NEUTRAL"; //--- Default direction set to NEUTRAL if (lastFractal_direction == FRACTAL_UP) return ("UP"); //--- Return UP if last fractal was up else if (lastFractal_direction == FRACTAL_DOWN) return ("DOWN"); //--- Return DOWN if last fractal was down return (direction_fractal); //--- Return NEUTRAL if no specific direction }
Здесь мы определяем функцию "getLastFractalDirection" для выяснения направления последнего обнаруженного фрактала и его возвращения. Функция работает путем проверки значения переменной "lastFractal_direction", которая отслеживает направление последнего фрактала (либо вверх, либо вниз). Мы начинаем с инициализации переменной "direction_fractal" типа string и задаем ей значение по умолчанию "NEUTRAL". Это означает, что если не будет обнаружено действительного направления, либо если направление фрактала не меняется, функция возвращает результат "NEUTRAL".
Далее мы проверяем значение переменной "lastFractal_direction". Если оно равняется "FRACTAL_UP" (что говорит о том, что последний обнаруженный фрактал является фракталом вверх), функция возвращает строку "UP". Если переменная "lastFractal_direction" имеет значение "FRACTAL_DOWN" (что говорит о том, что последний обнаруженный фрактал является фракталом вниз), функция возвращает строку "DOWN". Если ни одно из условий не выполнено (т.е. не был обнаружен ни фрактал вверх, ни фрактал вниз, и направление по-прежнему нейтральное), функция возвращает значение по умолчанию "NEUTRAL", сообщая о том, что на данный момент направление не определено.
Мы также можем записать в лог данные остальных индикаторов, как показано ниже.
Print("ALLIGATOR JAWS = ",NormalizeDouble(alligator_jaws[1],_Digits)); Print("ALLIGATOR TEETH = ",NormalizeDouble(alligator_teeth[1],_Digits)); Print("ALLIGATOR LIPS = ",NormalizeDouble(alligator_lips[1],_Digits)); Print("AO VALUE = ",NormalizeDouble(ao_values[1],_Digits+1)); if (ac_color[1] == AC_COLOR_UP){ Print("AC COLOR UP GREEN = ",AC_COLOR_UP); } else if (ac_color[1] == AC_COLOR_DOWN){ Print("AC COLOR DOWN RED = ",AC_COLOR_DOWN); }
После запуска мы получим следующий вывод:
Подтверждение индикатора Fractals:
Подтверждение остальных индикаторов:
Из визуализации мы можем заметить, что извлеченные данные соответствуют фактическим данным, отображаемым в окне данных, что означает успех. В дальнейшем мы сможем использовать эти данные в целях торговли. Сначала, как показано ниже, мы определяем некоторые необходимые функции, которые мы будем использовать для анализа.
//+------------------------------------------------------------------+ //| FUNCTION TO GET CLOSE PRICES | //+------------------------------------------------------------------+ double getClosePrice(int bar_index){ return (iClose(_Symbol, _Period, bar_index)); //--- Retrieve the close price of the specified bar } //+------------------------------------------------------------------+ //| FUNCTION TO GET ASK PRICES | //+------------------------------------------------------------------+ double getAsk(){ return (NormalizeDouble(SymbolInfoDouble(_Symbol, SYMBOL_ASK), _Digits)); //--- Get and normalize the Ask price } //+------------------------------------------------------------------+ //| FUNCTION TO GET BID PRICES | //+------------------------------------------------------------------+ double getBid(){ return (NormalizeDouble(SymbolInfoDouble(_Symbol, SYMBOL_BID), _Digits)); //--- Get and normalize the Bid price }
Здесь мы определяем три функции для получения цены закрытия, цены Ask и цены Bid, соответственно. Далее нам необходимо определить булевы переменные для проверки потенциальных торговых сигналов, основанных на линии челюсти индикатора Alligator, как показано ниже.
bool isBreakdown_jaws_buy = alligator_jaws[1] < getClosePrice(1) //--- Check if breakdown for buy && alligator_jaws[2] > getClosePrice(2); bool isBreakdown_jaws_sell = alligator_jaws[1] > getClosePrice(1) //--- Check if breakdown for sell && alligator_jaws[2] < getClosePrice(2);
Сначала мы определяем "isBreakdown_jaws_buy" для выявления условия пробоя для потенциального сигнала на покупку. Условие заключается в том, что значение элемента массива "alligator_jaws" с индексом 1 (представляющего предыдущий бар) должно быть меньше цены закрытия предыдущего бара, которая получена путем вызова функции "getClosePrice(1)". Кроме того, значение элемента массива "alligator_jaws" с индексом 2 (представляющего бар, стоящий перед вышеупомянутым предыдущим) должно быть выше цены закрытия бара, стоящего перед предыдущим, которая получена путем вызова функции "getClosePrice(2)". Такая комбинация предполагает, что линия челюсти индикатора Alligator пересекла уровень цены закрытия предыдущего бара вниз, но осталась выше цены закрытия бара, стоящего перед предыдущим, что можно интерпретировать как потенциальный сетап для сделки на покупку.
Далее мы определяем "isBreakdown_jaws_sell" для выявления условия пробоя для потенциального сигнала на продажу. В этом случае значение элемента массива "alligator_jaws" с индексом 1 должно быть выше цены закрытия предыдущего бара, а значение элемента "alligator_jaws" с индексом 2 должно быть меньше цены закрытия бара, стоящего перед предыдущим. Данный сценарий предполагает, что линия челюсти индикатора Alligator пересекла уровень цены закрытия предыдущего бара вверх, но осталась ниже цены закрытия бара, стоящего перед предыдущим, что предполагает потенциальный сетап для сделки на продажу. Отсюда мы можем определить остальные условия для открытия позиций.
if (lastFractal_direction == FRACTAL_DOWN //--- Conditions for Buy signal && isBreakdown_jaws_buy && ac_color[1] == AC_COLOR_UP && (ao_values[1] > 0 && ao_values[2] < 0)){ Print("BUY SIGNAL GENERATED"); obj_Trade.Buy(0.01,_Symbol,getAsk()); //--- Execute Buy order } else if (lastFractal_direction == FRACTAL_UP //--- Conditions for Sell signal && isBreakdown_jaws_sell && ac_color[1] == AC_COLOR_DOWN && (ao_values[1] < 0 && ao_values[2] > 0)){ Print("SELL SIGNAL GENERATED"); obj_Trade.Sell(0.01,_Symbol,getBid()); //--- Execute Sell order }
Здесь мы реализуем логику исполнения сигналов на покупку и продажу на основании комбинации индикаторов, в частности направления фрактала, пробоя челюсти Alligator, цветового статуса Accelerator Oscillator (AC) и значений Awesome Oscillator (AO).
Во-первых, мы проверяем, выполняются ли условия для сигнала на покупку. Мы проверяем, что "lastFractal_direction" имеет значение "FRACTAL_DOWN", т.е. последний обнаруженный фрактал является фракталом вниз. Затем мы проверяем, истинно ли условие "isBreakdown_jaws_buy", и если это так, это говорит о том, что линия челюсти индикатора Alligator пересекла ценовой уровень вниз и образовала сетап для потенциальной покупки.
Кроме того, мы проверяем, что "ac_color[1]" равняется "AC_COLOR_UP", и это означает, что индикатор Accelerator Oscillator находится в восходящем цветовом статусе, сигнализируя о бычьем сентименте на рынке. Наконец, мы проверяем значения индикатора Awesome Oscillator: элемент "ao_values[1]" должен быть больше нуля (указывая на положительный моментум), а элемент "ao_values[2]" должен быть меньше нуля (указывая на предшествующий отрицательный моментум). Данная комбинация предполагает, что произошел разворот моментума, при котором рынок перешел из отрицательной зоны в положительную. Если все эти условия выполнены, генерируется сигнал на покупку, и мы исполняем ордер на покупку с указанным размером лота (0,01) по цене Ask.
С другой стороны мы проверяем, выполняются ли условия для сигнала на продажу. Мы проверяем, что "lastFractal_direction" имеет значение "FRACTAL_UP", т.е. последний обнаруженный фрактал является фракталом вверх. Затем мы проверяем, истинно ли условие "isBreakdown_jaws_sell", и если это так, это говорит о том, что линия челюсти индикатора Alligator пересекла ценовой уровень вверх и образовала сетап для потенциального ордера на продажу.
Кроме того, мы проверяем, что "ac_color[1]" равняется "AC_COLOR_DOWN", и это означает, что индикатор Accelerator Oscillator находится в нисходящем цветовом статусе, сигнализируя о медвежьем сентименте на рынке. Наконец, мы проверяем значения индикатора Awesome Oscillator: элемент "ao_values[1]" должен быть меньше нуля (указывая на нисходящий моментум), а элемент "ao_values[2]" должен быть больше нуля (указывая на предшествующий восходящий моментум). Данная комбинация предполагает, что рынок разворачивается от положительного моментума к отрицательному. Если все эти условия выполнены, генерируется сигнал на продажу, и мы исполняем ордер на продажу с указанным размером лота (0,01) по цене Bid.
В результате откроются позиции. При этом вы увидите, что ордеры на выход не размещаются, и соответственно позиции "зависнут". Поэтому нам необходима будет логика для закрытия позиций, основанная на разворотах индикатора AO.
if (ao_values[1] < 0 && ao_values[2] > 0){ //--- Condition to close all Buy positions if (PositionsTotal() > 0){ Print("CLOSE ALL BUY POSITIONS"); for (int i=0; i<PositionsTotal(); i++){ ulong pos_ticket = PositionGetTicket(i); //--- Get position ticket if (pos_ticket > 0 && PositionSelectByTicket(pos_ticket)){ //--- Check if ticket is valid ENUM_POSITION_TYPE pos_type = (ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE); if (pos_type == POSITION_TYPE_BUY){ //--- Close Buy positions obj_Trade.PositionClose(pos_ticket); } } } } } else if (ao_values[1] > 0 && ao_values[2] < 0){ //--- Condition to close all Sell positions if (PositionsTotal() > 0){ Print("CLOSE ALL SELL POSITIONS"); for (int i=0; i<PositionsTotal(); i++){ ulong pos_ticket = PositionGetTicket(i); //--- Get position ticket if (pos_ticket > 0 && PositionSelectByTicket(pos_ticket)){ //--- Check if ticket is valid ENUM_POSITION_TYPE pos_type = (ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE); if (pos_type == POSITION_TYPE_SELL){ //--- Close Sell positions obj_Trade.PositionClose(pos_ticket); } } } } }
Здесь мы реализуем логику для закрытия всех активных позиций (как длинных, так и коротких) на основе значений индикатора Awesome Oscillator (AO). Во-первых, мы проверяем условие закрытия всех позиций на покупку. Если элемент "ao_values[1]" меньше нуля, а элемент "ao_values[2]" больше нуля, это говорит о потенциальной смене моментума с положительного на отрицательный. Это условие для закрытия всех длинных позиций. Чтобы это реализовать, мы сначала проверяем, имеются ли какие-либо открытые позиции, с помощью функции PositionsTotal.
Если позиции имеются, мы проходимся циклом по каждой позиции, извлекая номер тикета каждой из них с помощью функции PositionGetTicket. Для каждой позиции мы проверяем тикет с помощью функции PositionSelectByTicket, чтобы убедиться, что эта позиция действительная. Затем мы извлекаем тип позиции с помощью функции PositionGetInteger и вносим его в перечисление ENUM_POSITION_TYPE. Если позиция имеет тип POSITION_TYPE_BUY, это означает, что это длинная позиция, и мы закрываем ее с помощью "obj_Trade.PositionClose(pos_ticket)" и выводим "CLOSE ALL BUY POSITIONS" для подтверждения.
Далее мы проверяем условие закрытия всех позиций на продажу. Если элемент "ao_values[1]" больше нуля, а элемент "ao_values[2]" меньше нуля 0, это говорит о потенциальной смене моментума с положительного на отрицательный, что будет сигнализировать о необходимости закрыть все короткие позиции. Аналогичным образом, мы сперва проверяем, имеются ли какие-либо открытые позиции. Если таковые имеются, мы проходимся по ним циклом, извлекаем тикеты позиций, проверяем тикеты и проверяем тип позиции. Если позиция имеет тип POSITION_TYPE_SELL, это означает, что это короткая позиция, и мы закрываем ее с помощью "obj_Trade.PositionClose(pos_ticket)" и выводим "CLOSE ALL SELL POSITIONS" для подтверждения.
Как только мы запустим программу, мы получим следующий вывод.
Все прошло успешно. Мы можем увидеть, что мы подтвердили и открыли длинную позиций, когда были выполнены все условия для входа. На этом реализация стратегии завершена. Теперь нам нужно протестировать программу в тестере стратегий и оптимизировать ее, если необходимо адаптировать ее к текущим рыночным условиям. Займемся этим в нижеследующем разделе.
Тестирование и оптимизация стратегии
После реализации основных аспектов следующим этапом будет тестирование нашего советника (эксперта) с помощью тестера стратегий MetaTrader 5 для точной оценки его эффективности в различных рыночных сценариях. Целью данного этапа тестирования является проверка того, что стратегии ведет себя в соответствии с нашими ожиданиями, а также выявление необходимых корректировок для оптимизации результатов. На данном этапе мы уже выполнили первоначальную оптимизацию, акцентируя особое внимание на неотъемлемых параметрах нашей стратегии.
Мы уделили особое внимание пороговым значениям фракталов и линий аллигатора, чтобы оценить реагирование советника в различных торговых сессиях и при различных условиях. Такое тщательное тестирование позволило нам убедиться в том, что программа реагирует на ожидаемые сигналы на покупку и продажу, эффективно управляет сделками, максимизируя надежность и эффективность и минимизируя потенциальные ошибки. Ниже представлены результаты, которые мы получили в ходе тестирования.
Результаты ретроспективного тестирования:
График ретроспективного тестирования:
Заключение
В данной статье мы рассматриваем процесс создания советника (эксперта) на языке MQL5 на основе торговой стратегии Profitunity, в которой используются фракталы, индикатор Alligator и осцилляторы, такие как Awesome Oscillator и Accelerator Oscillator, для выявления стратегических сигналов на покупку и продажу. Начиная с ключевых индикаторов и пороговых условий, мы автоматизируем торговые сигналы, использующие рыночный моментум и ценовые пробои. Каждый этап включает в себя вдумчивое написание кода, конфигурацию хэндлов индикаторов и реализацию логики открытия сделок на покупку и продажу в соответствии с критериями стратегии. После завершения реализации мы тщательно проверили советник с помощью тестера стратегий MetaTrader 5, чтобы убедиться в ее надлежащем реагировании и надежности в различных рыночных условиях, делая акцент на точности в исполнении сделок за счет оптимизированных параметров.
Данная статья предлагает структурный подход к автоматизации торговых сигналов на языке MQL5 посредством стратегии Profitunity. Мы надеемся, что это побудит вас к дальнейшему изучению разработки на языке MQL5, вдохновит на создание более сложных и прибыльных торговых систем. Приятного кодинга и успешного трейдинга!
Перевод с английского произведен MetaQuotes Ltd.
Оригинальная статья: https://www.mql5.com/en/articles/16365
Предупреждение: все права на данные материалы принадлежат MetaQuotes Ltd. Полная или частичная перепечатка запрещена.
Данная статья написана пользователем сайта и отражает его личную точку зрения. Компания MetaQuotes Ltd не несет ответственности за достоверность представленной информации, а также за возможные последствия использования описанных решений, стратегий или рекомендаций.





- Бесплатные приложения для трейдинга
- 8 000+ сигналов для копирования
- Экономические новости для анализа финансовых рынков
Вы принимаете политику сайта и условия использования
С уважением, Владимир.
Спасибо за добрый отзыв. Инициализация хэндлов не является обязательной, но это хорошая практика программирования, позволяющая проверить, были ли они инициализированы после того, как вы их определили, чтобы избежать возможных ошибок. Это просто для проверки безопасности. Например, вы можете сделать вот так:
Теперь все понятно? Спасибо.
Цитата: В этой статье мы рассмотрим систему Profitunity Билла Вильямса, разберем ее основные компоненты и уникальный подход к торговле в условиях рыночного хаоса.
Ответ: Колонки прибыли и убытков будут существовать только в том случае, если ваш протестированныйпродукт или флэтовый рынок так же хорош, как и форвардный рынок, который вы используете против последующего портфеля или корзины индексов, которые будут следовать за этой линией ордеров.
Есть некоторые индексы и недавно основанные ETF, которые выходят или которые производятся на растущей основе, как для этого предполагаемого использования, и будут давать эти результаты, прибыли, такие как индекс dowjones 30, а также многие другие индексы, которые были созданы для этого предполагаемого использования. Питер Мэтти
Статья не о "колонках" прибыли/убытков или рыночных индексах/ETF. Она посвящена системе Profitunity System Билла Вильямса и тому, как реализовать ее индикаторы (Fractals, Alligator, AO, AC) на MQL5.
Обсуждение здесь ведется вокруг практики кодирования и автоматизации стратегий, поэтому соблюдение этих моментов будет наиболее полезным для читателей.
Статья не о "колонках" прибыли/убытков или рыночных индексах/ETF. Она посвящена системе Profitunity System Билла Вильямса и тому, как реализовать ее индикаторы (Fractals, Alligator, AO, AC) на MQL5.
Обсуждение здесь ведется вокруг практики кодирования и автоматизации стратегий, поэтому соблюдение этих моментов будет наиболее полезным для читателей.
Конечно