Создание пользовательских индикаторов в MQL5 (Часть 4): Smart WaveTrend Crossover на основе двух осцилляторов
Введение
В нашей предыдущей статье (Часть 3) мы разработали индикатор с несколькими шкалами на MetaQuotes Language 5 (MQL5) с доработками для секторного и кругового стилей отображения, что позволило динамически визуализировать несколько показателей с помощью настраиваемых индикаторных шкал и секторов с цветовой кодировкой. В четвёртой части мы разработаем индикатор Smart WaveTrend Crossover, использующий два осциллятора: один — для сигналов, второй — для фильтрации тренда. Он формирует оповещения на покупку и продажу на основе пересечений с возможностью подтверждения по тренду. Этот индикатор окрашивает свечи по направлению тренда, отображает стрелки при пересечениях и поддерживает настраиваемые параметры длины и визуального стиля. Мы рассмотрим следующие темы:
К концу статьи у вас будет рабочий индикатор MQL5 для пересечений WaveTrend, готовый к дальнейшей настройке. Давайте начнем!
Понимание структуры Smart WaveTrend Crossover
Структура Smart WaveTrend Crossover основана на осцилляторе WaveTrend — инструменте оценки рыночного импульса, который измеряет состояния перекупленности и перепроданности с использованием сглаженных средних цен. Это помогает выявлять потенциальные развороты или продолжение импульсного движения рынка. Осциллятор вычисляет исходную цену на основе максимумов, минимумов и цен закрытия, а затем применяет экспоненциальные скользящие средние для построения двух линий: более быстрой осциллирующей линии и более медленной сглаженной линии. Пересечения этих линий сигнализируют о возможностях покупки или продажи. Используя две конфигурации WaveTrend — одну с более короткими периодами для более чувствительной генерации сигналов, а другую с более длинными периодами для определения общего тренда, — мы можем объединить быстрые сигналы для входа с более широким рыночным контекстом. Это помогает отфильтровывать ложные сигналы в условиях бокового и рваного рынка.
В бычьем сценарии мы ожидаем, что более быстрая линия пересечет более медленную линию снизу вверх на сигнальном осцилляторе. Это указывает на усиление восходящего импульса, особенно если сигнал совпадает с восходящим трендом по осциллятору с более длинными периодами. В медвежьем сценарии пересечение быстрой линией медленной сверху вниз указывает на нисходящий импульс. В идеале это подтверждается нисходящим трендом по осциллятору с более длинными периодами, что позволяет избегать сделок против тренда. Такой подход помогает использовать изменения импульса с учетом преобладающего тренда. Дополнительные визуальные элементы, например цветные свечи, выделяют направление тренда, а стрелки отмечают точные точки появления сигналов.
Мы планируем отдельно рассчитывать значения WaveTrend для сигналов и тренда, используя настраиваемые длины каналов, периодов усреднения и скользящих средних, обнаруживать пересечения в сигнальном осцилляторе и применять необязательный трендовый фильтр, чтобы сигналы соответствовали направлению тренда. Мы добавим визуальные элементы, такие как окрашивание свечей на основе состояния тренда и стрелки со смещением для сигналов покупки или продажи, создавая комплексную систему, которая предоставляет понятные и практически применимые сигналы для торговли по импульсу. Ниже приведено визуальное представление наших целей.

Реализация в MQL5
Чтобы создать индикатор в MQL5, просто откройте MetaEditor, перейдите в Навигатор, найдите папку Индикаторы, нажмите вкладку «Создать» и следуйте подсказкам для создания файла. После его создания в среде разработки мы определим свойства и настройки индикатора, такие как количество буферов, графических построений, а также отдельные свойства линий, например цвет, ширину и метку.
//+------------------------------------------------------------------+ //| 1. Smart WaveTrend Crossover PART1.mq5 | //| Copyright 2026, Allan Munene Mutiiria. | //| https://t.me/Forex_Algo_Trader | //+------------------------------------------------------------------+ #property copyright "Copyright 2026, Allan Munene Mutiiria." #property link "https://t.me/Forex_Algo_Trader" #property version "1.00" #property indicator_chart_window #property indicator_buffers 23 #property indicator_plots 3 #property indicator_label1 "Colored Candles" #property indicator_type1 DRAW_COLOR_CANDLES #property indicator_color1 clrTeal, clrRed #property indicator_style1 STYLE_SOLID #property indicator_width1 1 #property indicator_label2 "Buy Signals" #property indicator_type2 DRAW_ARROW #property indicator_color2 clrForestGreen #property indicator_style2 STYLE_SOLID #property indicator_width2 1 #property indicator_label3 "Sell Signals" #property indicator_type3 DRAW_ARROW #property indicator_color3 clrOrangeRed #property indicator_style3 STYLE_SOLID #property indicator_width3 1
Начнем с настройки индикатора для отображения непосредственно в основном окне графика с помощью "#property indicator_chart_window", благодаря чему он накладывается на ценовые данные без открытия отдельного подокна. Затем мы выделяем в общей сложности 23 буфера с помощью "#property indicator_buffers 23" для хранения всех необходимых массивов данных для расчетов и визуализации, а также задаем 3 графических построения через "#property indicator_plots 3", чтобы определить видимые элементы на графике.
Для первого отображаемого элемента мы задаем метку "Colored Candles" и устанавливаем тип DRAW_COLOR_CANDLES для отображения ценовых баров с динамическими цветами: "clrTeal" для бычьего состояния и "clrRed" для медвежьего, со сплошным стилем и шириной 1. Второй отображаемый элемент имеет метку "Buy Signals", тип "DRAW_ARROW" и цвет clrForestGreen, сплошной стиль и ширину 1, чтобы визуально отмечать потенциальные точки входа. Аналогично третий отображаемый элемент с меткой "Sell Signals" использует DRAW_ARROW тип, цвет "clrOrangeRed", сплошной стиль и ширину 1 для обозначения возможностей продажи. Теперь определим несколько входных параметров для управления индикатором.
//+------------------------------------------------------------------+ //| Inputs | //+------------------------------------------------------------------+ input group "Colors" input color col_up = clrTeal; // Bull Color input color col_dn = clrRed; // Bear Color input group "WaveTrend Settings for Signals" input int wt_channel_len = 5; // Signal Channel Length input int wt_average_len = 10; // Signal Average Length input int wt_ma_len = 4; // Signal MA Length input group "WaveTrend Settings for Trend" input int wt_trend_channel_len = 10; // Trend Channel Length input int wt_trend_average_len = 100; // Trend Average Length input int wt_trend_ma_len = 10; // Trend MA Length input group "Signal Settings" input bool use_trend_filter = true; // Use Trend Filter? input color signal_buy_col = clrForestGreen; // Buy Signal Color input color signal_sell_col = clrOrangeRed; // Sell Signal Color input int base_offset = 10; // Base Signal Offset from Candle input group "Visual Settings" input bool color_candles = true; // Color Candles by Trend?
Здесь мы организуем пользовательские входные параметры в логические группы с помощью "input group", чтобы сделать настройку более понятной в диалоговом окне параметров индикатора. Сначала в группе "Colors" мы даем возможность выбрать цвета бычьего и медвежьего состояния — по умолчанию бирюзовый и красный соответственно; эти цвета будут применяться при окрашивании свечей. Далее в группе "WaveTrend Settings for Signals" задаются целочисленные входные параметры для сигнального осциллятора: длина канала равна 5, период усреднения — 10, а длина скользящей средней — 4, что позволяет настраивать чувствительность формирования сигналов пересечения.
Затем группа "WaveTrend Settings for Trend" включает аналогичные целочисленные входные параметры, но с более длинными значениями по умолчанию: длина канала — 10, период усреднения — 100, длина скользящей средней — 10. Они предназначены для определения более широкого рыночного тренда. В группе "Signal Settings" мы добавляем логический параметр, по умолчанию включающий трендовую фильтрацию, выбор цветов для сигналов покупки — clrForestGreen по умолчанию — и сигналов продажи — оранжево-красный, а также целочисленный параметр базового смещения от свечей, равный 10 пунктам, который управляет размещением сигнальных стрелок. Наконец, группа "Visual Settings" содержит логический входной параметр, по умолчанию включенный, который окрашивает свечи на основе обнаруженного тренда и дает пользователю возможность управлять применением динамической визуализации свечей. После компиляции мы получаем следующий раздел входных параметров.

После этого можно определить несколько глобальных переменных для буферов индикатора и вспомогательных функций, которые будут использоваться во всем индикаторе.
//+------------------------------------------------------------------+ //| Buffers | //+------------------------------------------------------------------+ double esa_signal[]; //--- Signal ESA buffer double d_signal[]; //--- Signal D buffer double ci_signal[]; //--- Signal CI buffer double wt1_signal[]; //--- Signal WT1 buffer double wt2_signal[]; //--- Signal WT2 buffer double signal_hist[]; //--- Signal histogram buffer double signal_bull_cross[]; //--- Signal bull cross buffer double signal_bear_cross[]; //--- Signal bear cross buffer double esa_trend[]; //--- Trend ESA buffer double d_trend[]; //--- Trend D buffer double ci_trend[]; //--- Trend CI buffer double wt1_trend[]; //--- Trend WT1 buffer double wt2_trend[]; //--- Trend WT2 buffer double trend_hist[]; //--- Trend histogram buffer double trend_is_bull[]; //--- Trend bull indicator buffer double trend_is_bear[]; //--- Trend bear indicator buffer double openBuf[]; //--- Open price buffer double highBuf[]; //--- High price buffer double lowBuf[]; //--- Low price buffer double closeBuf[]; //--- Close price buffer double candleColorBuf[]; //--- Candle color buffer double buyArrowBuf[]; //--- Buy arrow buffer double sellArrowBuf[]; //--- Sell arrow buffer //+------------------------------------------------------------------+ //| Calculate EMA manually | //+------------------------------------------------------------------+ double CalcEMA(double prev, double val, int period) { if (period < 1) return val; //--- Handle invalid period double alpha = 2.0 / (period + 1); //--- Compute alpha factor return alpha * val + (1 - alpha) * prev; //--- Return EMA value }
Мы объявляем серию массивов double, которые будут служить буферами для хранения промежуточных и итоговых данных во время расчетов индикатора. Сюда входят буферы компонентов сигнального WaveTrend: экспоненциальные сглаженные средние, разности, индексы канала и две основные линии, используемые для обнаружения пересечений, а также гистограмма и флаги бычьих и медвежьих пересечений. Аналогично мы создаем параллельные буферы для трендового WaveTrend, включая его собственное сглаживание, разности, индексы, линии, гистограмму и логические флаги для определения бычьего или медвежьего тренда. Кроме того, мы выделяем буферы для ценовых данных, чтобы перерисовывать свечи с цветами, включая open, high, low, close и буфер цветового индекса, а также отдельные буферы для отображения стрелок покупки и продажи в точках сигналов.
Для поддержки расчетов WaveTrend мы определяем функцию "CalcEMA", которая вручную рассчитывает экспоненциальную скользящую среднюю: сначала проверяет некорректные периоды и в таком случае возвращает само значение, затем вычисляет коэффициент сглаживания alpha как 2.0, деленное на период плюс один, и затем вычисляет новое значение EMA как взвешенную комбинацию текущего значения и предыдущей EMA. Мы будем использовать эту функцию при выполнении расчетов на каждом тике, а пока инициализируем индикатор следующим образом.
//+------------------------------------------------------------------+ //| Initialize indicator | //+------------------------------------------------------------------+ int OnInit() { IndicatorSetInteger(INDICATOR_DIGITS, _Digits); //--- Set indicator digits IndicatorSetString(INDICATOR_SHORTNAME, "Smart WaveTrend Crossover PART1"); //--- Set short name SetIndexBuffer(0, openBuf, INDICATOR_DATA); //--- Bind open buffer SetIndexBuffer(1, highBuf, INDICATOR_DATA); //--- Bind high buffer SetIndexBuffer(2, lowBuf, INDICATOR_DATA); //--- Bind low buffer SetIndexBuffer(3, closeBuf, INDICATOR_DATA); //--- Bind close buffer SetIndexBuffer(4, candleColorBuf, INDICATOR_COLOR_INDEX); //--- Bind color buffer PlotIndexSetInteger(0, PLOT_SHOW_DATA, color_candles); //--- Set candle visibility SetIndexBuffer(5, buyArrowBuf, INDICATOR_DATA); //--- Bind buy arrow buffer SetIndexBuffer(6, sellArrowBuf, INDICATOR_DATA); //--- Bind sell arrow buffer PlotIndexSetInteger(1, PLOT_ARROW, 233); //--- Set buy arrow symbol PlotIndexSetInteger(1, PLOT_SHOW_DATA, true); //--- Enable buy arrow display PlotIndexSetInteger(1, PLOT_DRAW_BEGIN, 0); //--- Set buy draw begin PlotIndexSetInteger(1, PLOT_LINE_COLOR, 0, signal_buy_col); //--- Set buy color PlotIndexSetInteger(2, PLOT_ARROW, 234); //--- Set sell arrow symbol PlotIndexSetInteger(2, PLOT_SHOW_DATA, true); //--- Enable sell arrow display PlotIndexSetInteger(2, PLOT_DRAW_BEGIN, 0); //--- Set sell draw begin PlotIndexSetInteger(2, PLOT_LINE_COLOR, 0, signal_sell_col); //--- Set sell color SetIndexBuffer(7, esa_signal, INDICATOR_CALCULATIONS); //--- Bind signal ESA SetIndexBuffer(8, d_signal, INDICATOR_CALCULATIONS); //--- Bind signal D SetIndexBuffer(9, ci_signal, INDICATOR_CALCULATIONS); //--- Bind signal CI SetIndexBuffer(10, wt1_signal, INDICATOR_CALCULATIONS); //--- Bind signal WT1 SetIndexBuffer(11, wt2_signal, INDICATOR_CALCULATIONS); //--- Bind signal WT2 SetIndexBuffer(12, signal_hist, INDICATOR_CALCULATIONS); //--- Bind signal hist SetIndexBuffer(13, signal_bull_cross, INDICATOR_CALCULATIONS); //--- Bind bull cross SetIndexBuffer(14, signal_bear_cross, INDICATOR_CALCULATIONS); //--- Bind bear cross SetIndexBuffer(15, esa_trend, INDICATOR_CALCULATIONS); //--- Bind trend ESA SetIndexBuffer(16, d_trend, INDICATOR_CALCULATIONS); //--- Bind trend D SetIndexBuffer(17, ci_trend, INDICATOR_CALCULATIONS); //--- Bind trend CI SetIndexBuffer(18, wt1_trend, INDICATOR_CALCULATIONS); //--- Bind trend WT1 SetIndexBuffer(19, wt2_trend, INDICATOR_CALCULATIONS); //--- Bind trend WT2 SetIndexBuffer(20, trend_hist, INDICATOR_CALCULATIONS); //--- Bind trend hist SetIndexBuffer(21, trend_is_bull, INDICATOR_CALCULATIONS); //--- Bind trend bull SetIndexBuffer(22, trend_is_bear, INDICATOR_CALCULATIONS); //--- Bind trend bear return(INIT_SUCCEEDED); //--- Return success }
В обработчике OnInit мы настраиваем свойства индикатора, задавая количество отображаемых знаков в соответствии с точностью символа с помощью IndicatorSetInteger и назначая короткое имя с помощью IndicatorSetString для удобной идентификации в платформе. Мы привязываем ценовые буферы open, high, low и close к начальным индексам построения как типы данных, а цветовой буфер подключаем как цветовой индекс для поддержки динамического отображения свечей, одновременно управляя видимостью построения через PlotIndexSetInteger на основе пользовательских настроек. Для стрелочных построений мы связываем буферы стрелок покупки и продажи с соответствующими индексами, задаем символы стрелок с конкретными кодами, например 233 для покупок и 234 для продаж, включаем отображение и задаем начало отрисовки с нулевого бара и применяем пользовательские цвета с помощью функции "PlotIndexSetInteger". Вы можете использовать любые коды по своему выбору из MQL5 Wingdings, как показано ниже.

Затем мы связываем все расчетные буферы для сигнальных и трендовых элементов WaveTrend — включая сглаженные средние, разности, индексы, линии, гистограммы, пересечения и признаки тренда — с более высокими индексами как невидимые расчетные данные. В завершение возвращаем INIT_SUCCEEDED чтобы сообщить, что инициализация завершилась без ошибок. Теперь нам остается выполнять расчеты на каждом тике при необходимости. Сначала мы инициализируем буферы для всех баров, если индикатор впервые загружается для всех свечей, а затем выполним необходимые вычисления.
//+------------------------------------------------------------------+ //| Calculate indicator values | //+------------------------------------------------------------------+ int OnCalculate(const int rates_total, const int prev_calculated, const datetime &time[], const double &open[], const double &high[], const double &low[], const double &close[], const long &tick_volume[], const long &volume[], const int &spread[]) { int start = prev_calculated - 1; //--- Set start index if (start < 0) start = 0; //--- Adjust invalid start if (prev_calculated == 0) { //--- Handle initial calculation ArrayInitialize(esa_signal, EMPTY_VALUE); //--- Init signal ESA ArrayInitialize(d_signal, EMPTY_VALUE); //--- Init signal D ArrayInitialize(ci_signal, EMPTY_VALUE); //--- Init signal CI ArrayInitialize(wt1_signal, EMPTY_VALUE); //--- Init signal WT1 ArrayInitialize(wt2_signal, EMPTY_VALUE); //--- Init signal WT2 ArrayInitialize(signal_hist, EMPTY_VALUE); //--- Init signal hist ArrayInitialize(signal_bull_cross, 0); //--- Init bull cross ArrayInitialize(signal_bear_cross, 0); //--- Init bear cross ArrayInitialize(esa_trend, EMPTY_VALUE); //--- Init trend ESA ArrayInitialize(d_trend, EMPTY_VALUE); //--- Init trend D ArrayInitialize(ci_trend, EMPTY_VALUE); //--- Init trend CI ArrayInitialize(wt1_trend, EMPTY_VALUE); //--- Init trend WT1 ArrayInitialize(wt2_trend, EMPTY_VALUE); //--- Init trend WT2 ArrayInitialize(trend_hist, EMPTY_VALUE); //--- Init trend hist ArrayInitialize(trend_is_bull, 0); //--- Init trend bull ArrayInitialize(trend_is_bear, 0); //--- Init trend bear ArrayInitialize(buyArrowBuf, EMPTY_VALUE); //--- Init buy arrows ArrayInitialize(sellArrowBuf, EMPTY_VALUE); //--- Init sell arrows } return(rates_total); //--- Return total rates }
В обработчике OnCalculate мы получаем параметры, включая общее количество баров, количество ранее рассчитанных баров, а также массивы времени, цен, объемов и спредов для эффективной обработки новых данных. Мы определяем начальный индекс для расчетов, устанавливая его на единицу меньше ранее рассчитанного значения, и корректируем его до нуля, если он оказывается ниже нуля, чтобы избежать некорректной индексации.
Когда индикатор впервые загружается или пересчитывается с нуля, на что указывает значение prev_calculated, равное нулю, мы инициализируем все буферы с помощью ArrayInitialize: устанавливаем буферы сигнальных и трендовых компонентов, таких как экспоненциальное сглаживание, разности, индексы, линии и гистограммы, в EMPTY_VALUE, буферы флагов пересечений и тренда — в ноль, а буферы стрелок — в "EMPTY_VALUE" для чистого старта. Наконец, мы возвращаем общее количество рассчитанных баров, чтобы обозначить завершение этого цикла расчета. Теперь можно пройти по барам в цикле и выполнить вычисления. Ниже показан использованный подход.
for (int i = start; i < rates_total; i++) { //--- Loop through bars double src_wt = (high[i] + low[i] + close[i]) / 3.0; //--- Compute source if (i == 0) { //--- Handle first bar signal esa_signal[i] = src_wt; //--- Set initial ESA d_signal[i] = MathAbs(src_wt - esa_signal[i]); //--- Set initial D double denom_signal = 0.015 * d_signal[i]; //--- Compute denominator ci_signal[i] = (denom_signal != 0.0) ? (src_wt - esa_signal[i]) / denom_signal : 0.0; //--- Set CI wt1_signal[i] = ci_signal[i]; //--- Set initial WT1 wt2_signal[i] = wt1_signal[i]; //--- Set initial WT2 } else { //--- Handle subsequent bars esa_signal[i] = CalcEMA(esa_signal[i-1], src_wt, wt_channel_len); //--- Update ESA d_signal[i] = CalcEMA(d_signal[i-1], MathAbs(src_wt - esa_signal[i]), wt_channel_len); //--- Update D double denom_signal = 0.015 * d_signal[i]; //--- Compute denominator ci_signal[i] = (denom_signal != 0.0) ? (src_wt - esa_signal[i]) / denom_signal : 0.0; //--- Update CI wt1_signal[i] = CalcEMA(wt1_signal[i-1], ci_signal[i], wt_average_len); //--- Update WT1 wt2_signal[i] = 0; //--- Reset WT2 int cnt_ma = 0; //--- Init MA count for (int k = 0; k < wt_ma_len; k++) { //--- Loop for MA if (i - k < 0) break; //--- Skip invalid index wt2_signal[i] += wt1_signal[i - k]; //--- Accumulate WT1 cnt_ma++; //--- Increment count } if (cnt_ma > 0) wt2_signal[i] /= cnt_ma; //--- Average WT2 } signal_hist[i] = wt1_signal[i] - wt2_signal[i]; //--- Compute signal hist signal_bull_cross[i] = (wt1_signal[i] > wt2_signal[i] && (i == 0 || wt1_signal[i-1] <= wt2_signal[i-1])) ? 1 : 0; //--- Detect bull cross signal_bear_cross[i] = (wt1_signal[i] < wt2_signal[i] && (i == 0 || wt1_signal[i-1] >= wt2_signal[i-1])) ? 1 : 0; //--- Detect bear cross double src_wt_trend = src_wt; //--- Set trend source if (i == 0) { //--- Handle first bar trend esa_trend[i] = src_wt_trend; //--- Set initial ESA d_trend[i] = MathAbs(src_wt_trend - esa_trend[i]); //--- Set initial D double denom_trend = 0.015 * d_trend[i]; //--- Compute denominator ci_trend[i] = (denom_trend != 0.0) ? (src_wt_trend - esa_trend[i]) / denom_trend : 0.0; //--- Set CI wt1_trend[i] = ci_trend[i]; //--- Set initial WT1 wt2_trend[i] = wt1_trend[i]; //--- Set initial WT2 } else { //--- Handle subsequent bars esa_trend[i] = CalcEMA(esa_trend[i-1], src_wt_trend, wt_trend_channel_len); //--- Update ESA d_trend[i] = CalcEMA(d_trend[i-1], MathAbs(src_wt_trend - esa_trend[i]), wt_trend_channel_len); //--- Update D double denom_trend = 0.015 * d_trend[i]; //--- Compute denominator ci_trend[i] = (denom_trend != 0.0) ? (src_wt_trend - esa_trend[i]) / denom_trend : 0.0; //--- Update CI wt1_trend[i] = CalcEMA(wt1_trend[i-1], ci_trend[i], wt_trend_average_len); //--- Update WT1 wt2_trend[i] = 0; //--- Reset WT2 int cnt_ma_trend = 0; //--- Init MA count for (int k = 0; k < wt_trend_ma_len; k++) { //--- Loop for MA if (i - k < 0) break; //--- Skip invalid index wt2_trend[i] += wt1_trend[i - k]; //--- Accumulate WT1 cnt_ma_trend++; //--- Increment count } if (cnt_ma_trend > 0) wt2_trend[i] /= cnt_ma_trend; //--- Average WT2 } trend_hist[i] = wt1_trend[i] - wt2_trend[i]; //--- Compute trend hist trend_is_bull[i] = (wt1_trend[i] > wt2_trend[i]) ? 1 : 0; //--- Set bull trend trend_is_bear[i] = (wt1_trend[i] < wt2_trend[i]) ? 1 : 0; //--- Set bear trend if (color_candles) { //--- Check candle coloring openBuf[i] = open[i]; //--- Set open highBuf[i] = high[i]; //--- Set high lowBuf[i] = low[i]; //--- Set low closeBuf[i] = close[i]; //--- Set close candleColorBuf[i] = trend_is_bull[i] == 1 ? 0 : 1; //--- Set color index } buyArrowBuf[i] = EMPTY_VALUE; //--- Reset buy arrow sellArrowBuf[i] = EMPTY_VALUE; //--- Reset sell arrow if (signal_bull_cross[i] == 1 && (!use_trend_filter || trend_is_bull[i] == 1)) { //--- Check buy condition buyArrowBuf[i] = low[i] - _Point * base_offset; //--- Place buy arrow } if (signal_bear_cross[i] == 1 && (!use_trend_filter || trend_is_bear[i] == 1)) { //--- Check sell condition sellArrowBuf[i] = high[i] + _Point * base_offset; //--- Place sell arrow } }
Здесь мы проходим в цикле по каждому бару от начального индекса до общего количества доступных баров, рассчитывая исходную цену как среднее значение high, low и close для этого бара. Это значение используется как входной параметр для обоих осцилляторов WaveTrend.
Для сигнального WaveTrend на первом баре мы инициализируем экспоненциальную сглаженную среднюю непосредственно из исходной цены, рассчитываем абсолютную разность для буфера отклонения с помощью функции MathAbs , вычисляем знаменатель, умножая отклонение на 0.015, и задаем индекс канала, деля разность между исходной ценой и сглаженной средней на знаменатель, если он не равен нулю; иначе используем ноль. Затем мы устанавливаем обе основные линии равными значению индекса канала. Для последующих баров мы обновляем экспоненциальную сглаженную среднюю с помощью "CalcEMA", используя предыдущее значение, исходную цену и длину канала, пересчитываем отклонение еще одним вызовом "CalcEMA" по абсолютной разности от "MathAbs" с той же длиной, заново рассчитываем знаменатель, аналогично обновляем индекс канала, сглаживаем первую основную линию с помощью "CalcEMA" с использованием периода усреднения и вычисляем вторую основную линию, усредняя последние значения первой линии за период скользящей средней через вложенный цикл, который накапливает значения, выполняет деление и пропускает некорректные индексы.
Затем мы получаем сигнальную гистограмму как разность между двумя основными сигнальными линиями, отмечаем бычье пересечение, если первая линия находится выше второй и не находилась выше на предыдущем баре (или если это первый бар), и отмечаем медвежье пересечение, если первая линия ниже второй и ранее не была ниже. Повторяя аналогичный процесс для трендового WaveTrend с использованием той же исходной цены, мы обрабатываем первый бар, одинаково инициализируя экспоненциальное сглаживание, отклонение через "MathAbs", знаменатель, индекс канала и обе основные линии, но для последующих баров применяем параметры тренда: обновление через "CalcEMA" с длинами трендового канала и периода усреднения, а также усреднение второй линии по длине трендовой скользящей средней во вложенном цикле. Мы рассчитываем трендовую гистограмму как разность между ее основными линиями, устанавливаем флаг бычьего тренда в 1, если первая линия выше второй, и флаг медвежьего тренда в 1, если она ниже.
Если окрашивание свечей включено, мы копируем open, high, low и close бара в соответствующие буферы и назначаем цветовой индекс 0 для бычьих трендов или 1 для медвежьих. Наконец, мы сбрасываем буферы стрелок покупки и продажи в EMPTY_VALUE, затем размещаем стрелку покупки ниже low на величину базового смещения, умноженного на _Point , если произошло бычье пересечение сигнала и при этом трендовый фильтр не используется либо тренд является бычьим. Аналогично мы размещаем стрелку продажи выше high, если произошло медвежье пересечение при совпадающих условиях. После компиляции получаем следующий результат.

По визуализации видно, что мы рассчитываем индикатор, отрисовываем цветные свечи и задаем буферы индикатора, тем самым достигая поставленных целей. Остается выполнить бэктестинг программы, и этим мы займемся в следующем разделе.
Бэктестинг
Мы провели тестирование, и ниже приведена итоговая визуализация в виде одного растрового изображения формата Graphics Interchange Format (GIF).

Заключение
В заключение отметим, что мы разработали индикатор Smart WaveTrend Crossover на MQL5, который использует два осциллятора WaveTrend: один — для сигналов пересечения, другой — для фильтрации тренда, с настраиваемыми параметрами длины канала, периода усреднения и скользящей средней. Индикатор применяет окрашивание свечей на основе тренда, отображает стрелки покупки и продажи при пересечениях, а также включает параметры подтверждения по тренду вместе с визуальными настройками, такими как цвета и смещения. Такая конфигурация предоставляет визуальные индикаторы изменений импульса, согласованных с более широкими трендами.
В следующей части мы улучшим индикатор расширенными визуальными элементами, включая сигнальные блоки, полупрозрачные области для визуализации тренда, настраиваемые метки покупки/продажи и встроенное отображение take-profit и stop-loss для более эффективного управления рисками, как показано ниже. Следите за продолжением.

Перевод с английского произведен MetaQuotes Ltd.
Оригинальная статья: https://www.mql5.com/en/articles/20811
Предупреждение: все права на данные материалы принадлежат MetaQuotes Ltd. Полная или частичная перепечатка запрещена.
Данная статья написана пользователем сайта и отражает его личную точку зрения. Компания MetaQuotes Ltd не несет ответственности за достоверность представленной информации, а также за возможные последствия использования описанных решений, стратегий или рекомендаций.
MQL5 Bootstrap (I): Набор переиспользуемых функций для работы с позициями и ордерами
Моделирование рынка: Position View (VIII)
Алгоритм бактериальной эволюции — Bacterial Evolutionary Algorithm (BEA)
Оптимизация трендовой торговли: Торговля по направлению тренда и с учётом его силы
- Бесплатные приложения для трейдинга
- 8 000+ сигналов для копирования
- Экономические новости для анализа финансовых рынков
Вы принимаете политику сайта и условия использования