Разработка инструментария для анализа движения цен (Часть 21): Поиск разворотов рыночной структуры
Содержание
- Введение
- Польза инструмента
- План действий (краткий обзор)
- Структура советника
- Исходный код
- Результаты
- Заключение
Введение
Представленный здесь советник решает одну из самых сложных задач в трейдинге — проблему ложных сигналов разворота. Рваное, флетовое движение цены сбивает с толку простые алгоритмы поиска разворотов, из-за чего трейдеры ловят в серию "пил". Детектор разворота рыночной структуры Market Structure Flip Detector решает эту задачу, преобразуя ATR в фильтр на основе количества баров. Он игнорирует незначительные колебания, фиксирует только валидные максимумы и минимумы, а затем сигнализирует о медвежьем развороте, когда более высокий максимум сменяется более низким максимумом, либо о бычьем развороте, когда более низкий минимум превращается в более высокий минимум. В статье мы увидим, как:
- преобразовывать ATR в измеритель глубины, который расширяется на волатильных рынках и сужается на спокойных;
- подтверждать развороты анализом точного количества баров по обе стороны от кандидата на максимум или минимум;
- следить за трендом, чтобы развороты фиксировались только после пробоя предыдущей структуры;
- отображать сигналы стрелками на графике, подписями разворотов и статистической панелью, отслеживающей количество и временные интервалы между разворотами.
В итоге должен получиться советник, который отсеивает рыночный шум и выдает только самые чистые, формализованные сигналы разворота, дополненные звуковыми и push-уведомлениями при возникновении реальных структурных разворотов.
Польза инструмента
На флетовых рынках ложные сигналы могут составлять более половины всех триггеров на основе разворотов. Результат — пилы и отрицательное математическое ожидание. Глубину разворота можно регулировать, если привязать фильтр свинг-точек к индикатору Average True Range (ATR), который был разработан в 1978 году Уайлдером для более точного измерения волатильности по сравнению с простым диапазоном high-low. Когда ATR растет в периоды всплесков волатильности, фильтр расширяется и игнорирует мелкие хаотичные колебания. Когда рынок успокаивается, он сужается, позволяя фиксировать реальные развороты.Истинный диапазон (True Range, TR) для каждого бара определяется как:
ATR — это n-периодное простое скользящее среднее TR (по умолчанию n = 14).
Преобразуем ATR в глубину по количеству баров d:
Медвежий разворот возникает тогда, когда мы сначала фиксируем более высокий максимум, а затем видим, что следующий свинг-максимум опускается ниже этого пика. Бычий — зеркально. Если предположить, что доходности подчиняются нормальному распределению с нулевым средним и дисперсией, назовем это "квадрат волатильности", то вероятность того, что один конкретный бар окажется максимальным в интервале длиной "два раза глубина плюс один" бар, равна просто единице, деленной на это же значение "дважды глубина + 1". Поскольку мы напрямую связываем глубину с рыночной волатильностью, оценивая волатильность как ATR, деленный на квадратный корень из половины π, мы напрямую управляем частотой ложных сигналов. На практике это позволяет подобрать такие параметры, при которых уровень шума удерживается примерно на уровне 5%.
Исследования TradeStation показали, что пивот-окна на основе ATR сокращают количество шумовых сделок примерно на 40% и увеличивают чистую прибыль примерно на 22% за 5 лет на данных S&P 500. По данным QuantifiedStrategies.com, развороты с фильтром по ATR повышают долю успешных сделок примерно с 35% до 58% и увеличивают среднее соотношение прибыль/риск с ~1,1 до ~1,8 на тестах по EURUSD и фьючерсам ES. Отзывы сообщества также подчеркивают, что инструменты для поиска разворотов с ATR-окном хорошо согласуются с институциональными пробоями ордер-флоу, особенно на таймфреймах 1H и 4H.
План действий (краткий обзор)
Данный советник отфильтровывает рыночный шум, преобразуя текущее значение ATR в гибкое "окно глубины": более широкое в условиях высокой волатильности и более узкое на спокойных рынках, после чего валидирует каждый закрывшийся бар (его максимум или минимум) относительно соседних баров в пределах этого окна. Он запоминает последние два подтвержденных свинг-максимума и минимума и отслеживает простой флаг направления, который переключается в состояние up, когда новый максимум превышает предыдущий, либо в состояние down, когда новый минимум опускается ниже предшествующего. Когда рыночная структура разворачивается — то есть в восходящем состоянии последний максимум оказывается ниже предыдущего (медвежий разворот), либо в нисходящем состоянии последний минимум оказывается выше предыдущего (бычий разворот), — советник размещает на графике цветную стрелку, подписывает оба разворота, обновляет информацию на панели и при необходимости запускает звуковые или push-уведомления. Это позволит видеть только реальные развороты типа из более высокого максимума в более низкий или из более низкого минимума в более высокий.
Медвежий разворот
Восходящий тренд определяется, когда цена формирует два последовательных более высоких максимума. После этого советник ищет свинг-максимум ниже предыдущего свинг-максимума. Свинг-максимум определяется как наивысшая точка в окне, образованном индикатором Average True Range (ATR). Когда советник находится в состоянии "up" и обнаруживает такой более низкий максимум, он помечает соответствующий бар красной стрелкой с подписью "LH". Помимо визуального обозначения, советник генерирует уведомление и записывает медвежий разворот, сигнализируя о том, что продавцы начинают перехватывать контроль.

Бычий разворот
Нисходящий тренд устанавливается, когда цена фиксирует два последовательных более низких минимума. Затем советник определяет свинг-минимум, который превышает предыдущий свинг-минимум. Свинг-минимум определяется как наинизшая точка в окне, основанном на показателях ATR. Когда советник находится в состоянии "down" и обнаруживает такой более высокий минимум, он отображает на соответствующем баре зеленую стрелку с подписью "HL". Кроме того, он создает уведомление и записывает бычий разворот, указывая на потенциальное возвращение интереса со стороны покупателей.

Структура советника
При начале работы с любым файлом MQL5 мы добавляем директивы #property, чтобы задать базовые параметры. Здесь мы включаем строгую компиляцию, чтобы компилятор отлавливал небезопасные приведения типов или устаревшие вызовы. Также указываем авторские права, ссылку и версию — кто написал код, где найти дополнительную информацию и какая это версия. Эти строки не влияют на логику — они служат для идентификации файла.
#property copyright "Copyright 2025, MetaQuotes Ltd." #property link "https://www.mql5.com/en/users/lynnchris" #property version "1.0" #property strict
Далее добавляем параметры, которые пользователи смогут настраивать. Первые три входных параметра определяют, как именно мы будем находить развороты с помощью ATR.
- InpAtrPeriod — задает количество баров, используемых для расчета ATR. Короткий период (например, 7) реагирует быстро, но может показывать больше шума. Более длинный период (скажем, 21) сглаживает всплески, но может быть немного запаздывающим.
- InpAtrMultiplier — преобразует диапазон ATR в минимальную ширину сдвига. При значении 1.0 требуется движение величиной в один ATR, чтобы зафиксировать разворот. Увеличение до 1.5 или 2.0 делает фильтр более избирательным.
- InpAtrLoosenFactor — масштабирует эту ширину в диапазоне от 0 до 1. Коэффициент 0.5 уменьшает требование вдвое, благодаря чему развороты появляются раньше, что может быть полезно при низкой волатильности.
Затем мы настраиваем компоновку графика.
- InpAutoShift — резервирует пустые бары справа при появлении новых баров.
- InpShiftBars — задает количество оставляемых пустых баров (по умолчанию пять).
Такое смещение предотвращает наложение стрелок, подписей и панели статистики на ценовой график.
Далее у нас два способа оповещений:
- InpEnableSound — советник будет воспроизводить WAV-файл при каждом развороте.
- InpSoundFile — название звукового файла в папке Sounds терминала MetaTrader 5.
- InpEnablePush — отправляет push-уведомление в мобильное приложение MetaTrader 5.
Это позволяет настроить звуковое оповещение на компьютере, push-уведомление на телефоне или использовать оба варианта.
input int InpAtrPeriod = 14; // How many bars for ATR input double InpAtrMultiplier = 1.0; // Scale ATR into bar‑depth input double InpAtrLoosenFactor = 0.5; // Optional: loosen the swing filter input bool InpAutoShift = true; // Push bars left for visibility input int InpShiftBars = 5; // Number of bars for right margin input bool InpEnableSound = true; // Play a sound on flip input string InpSoundFile = "alert.wav"; input bool InpEnablePush = false; // Send push notifications
При запуске советника функция OnInit сначала проверяет InpAutoShift и, если сдвиг включен, вызывает ChartSetInteger с параметрами CHART_SHIFT и InpShiftBars, чтобы сдвинуть новые бары влево и зарезервировать чистое пространство для аннотаций. Затем она запрашивает хэндл встроенного индикатора ATR через функцию iATR, сохраняет его в atrHandle и немедленно завершает инициализацию с INIT_FAILED, если хэндл некорректен. После этого создается угловая метка (OBJ_LABEL с именем panelName), которая закрепляется в левом верхнем углу графика, смещается на 10 пикселей по горизонтали и вертикали, получает размер шрифта 10 и желтый цвет, после чего OnInit возвращает INIT_SUCCEEDED. Это подтверждает что доступ к данным ATR и панель статистики готовы к работе в OnTick.
int OnInit() { if(InpAutoShift) ChartSetInteger(0, CHART_SHIFT, InpShiftBars); atrHandle = iATR(_Symbol, _Period, InpAtrPeriod); if(atrHandle == INVALID_HANDLE) return INIT_FAILED; ObjectCreate(0, panelName, OBJ_LABEL, 0, 0, 0); ObjectSetInteger(0, panelName, OBJPROP_CORNER, CORNER_LEFT_UPPER); ObjectSetInteger(0, panelName, OBJPROP_XDISTANCE, 10); ObjectSetInteger(0, panelName, OBJPROP_YDISTANCE, 10); ObjectSetInteger(0, panelName, OBJPROP_FONTSIZE, 10); ObjectSetInteger(0, panelName, OBJPROP_COLOR, clrYellow); return INIT_SUCCEEDED; }
При удалении советника или закрытии MetaTrader 5 функция OnDeinit удаляет все созданные стрелки и текстовые объекты, удаляет статистическую метку и освобождает хэндл ATR. Это предотвращает мусор графика и освобождает ресурсы.
void OnDeinit(const int reason) { ObjectsDeleteAll(0, -1, OBJ_ARROW); ObjectsDeleteAll(0, -1, OBJ_TEXT); ObjectDelete(0, panelName); if(atrHandle != INVALID_HANDLE) IndicatorRelease(atrHandle); }
В обработчике OnTick? чтобы не выполнять логику поиска пивотов на каждом новом тике, вводим переменную типа datetime lastBar. Мы получаем thisBar через iTime(...,1), что возвращает время открытия последнего закрытого бара, и сравниваем его с lastBar. Если время не изменилось, значит мы все еще внутри той же свечи. Если же thisBar изменился, обновляем lastBar и продолжаем выполнение.
Затем мы вызываем CopyBuffer для хэндла индикатора ATR, извлекая только последнее значение ATR. Если произошла ошибка, выходим, чтобы не работать с некорректными данными. Когда у нас есть валидный ATR, вычисляем "глубину" свинга. Для этого переводим единицы ATR в количество баров. Делим ATR на минимальный ценовой шаг (SYMBOL_POINT) и умножаем результат на InpAtrMultiplier и InpAtrLoosenFactor, после чего принудительно задаем минимум в один бар с помощью MathMax. В результате получается динамическая глубина, которая увеличивается при всплесках волатильности (требуя более крупных движений для фиксации разворотов) и уменьшается на спокойном рынке (разрешая более узкие свинги), прежде чем передать управление функциям поиска разворотов.
void OnTick() { static datetime lastBar=0; datetime thisBar = iTime(_Symbol,_Period,1); if(thisBar == lastBar) return; lastBar = thisBar; double atrBuf[]; if(CopyBuffer(atrHandle, 0, 1, 1, atrBuf) <= 0) return; double atr = atrBuf[0]; int depth = MathMax(1, int(atr / SymbolInfoDouble(_Symbol, SYMBOL_POINT) * InpAtrMultiplier * InpAtrLoosenFactor)); // … pivot checks follow … }
После определения глубины запускаем два простых цикла для проверки разворотов. IsSwingHigh(1, depth) проверяет, что ни один бар в пределах depth баров слева и справа не превышает кандидатный максимум. IsSwingLow выполняет аналогичную проверку для минимумов. Когда мы находим новый максимум или минимум, сдвигаем lastHigh в prevHigh (аналогично для минимумов) и сохраняем временную метку. Отслеживание двух разворотов (предыдущего и текущего) позволяет затем сравнить их между собой.
bool newHigh = IsSwingHigh(1, depth); bool newLow = IsSwingLow (1, depth); double h = iHigh(_Symbol,_Period,1), l = iLow(_Symbol,_Period,1); if(newHigh) { prevHigh = lastHigh; prevHighTime = lastHighTime; lastHigh = h; lastHighTime = thisBar; } if(newLow) { prevLow = lastLow; prevLowTime = lastLowTime; lastLow = l; lastLowTime = thisBar; }
После фиксации разворотов обновляем structState, отражая тренд: более высокий максимум устанавливает состояние 1 (возможен будущий медвежий разворот), более низкий минимум — состояние 2 (возможен будущий бычий разворот). Затем проверяем сам факт разворота: в состоянии 1, если новый максимум ниже предыдущего, это медвежий разворот; в состоянии 2, если новый минимум выше предыдущего, это бычий разворот. При срабатывании разворота вызываются функции отрисовки и уведомлений, а счетчики обновляются.
// Update bias if(newHigh && prevHigh>0 && lastHigh > prevHigh) structState = 1; if(newLow && prevLow>0 && lastLow < prevLow) structState = 2; // Bearish flip if(newHigh && structState==1 && lastHigh < prevHigh) { PlotArrow(...); PlotLabel(...); Notify(...); if(countBear>0) sumBearInterval += (lastHighTime - prevLowTime)/60.0; countBear++; } // Bullish flip if(newLow && structState==2 && lastLow > prevLow) { PlotArrow(...); PlotLabel(...); Notify(...); if(countBull>0) sumBullInterval += (lastLowTime - prevHighTime)/60.0; countBull++; }
Вся отрисовка на графике реализована в двух вспомогательных функциях — PlotArrow и PlotLabel. Внутри каждой функции мы сначала вызываем ObjectFind(0, name), который ищет существующий объект по его уникальному имени. Эта операция выполняется за время O(n) относительно количества объектов, но на современных компьютерах это достаточно быстро для периодических проверок на каждом баре. Если объект не найден (ObjectFind возвращает –1), создаем его ровно один раз с помощью ObjectCreate. При этом выбираем соответствующий тип объекта (стрелка для PlotArrow, текстовая метка для PlotLabel).
Затем настраиваем свойства: для стрелок задаем OBJPROP_ARROWCODE для выбора нужного символа (например, код Wingdings 234 для красной стрелки вниз) и OBJPROP_COLOR для определения цвета. Для меток устанавливаем OBJPROP_TEXT с подписью (например, LH или HL), а также смещения и размер шрифта. Таким образом мы не вызываем лишний раз функцию ObjectCreate, благодаря чему не страдает производительность и предотвращаются утечки памяти, которые возникли бы при накоплении сотен или тысяч одинаковых объектов. Также каждая метка разворота имеет собственный идентификатор, благодаря чему можно будет изменять, например, OBJPROP_ZORDER (приоритет отрисовки) или удалять объект по имени, не затрагивая другие элементы графика.
void PlotArrow(string name, datetime t, double price, int code, color c) { if(ObjectFind(0, name) < 0) { ObjectCreate(0, name, OBJ_ARROW, 0, t, price); ObjectSetInteger(0, name, OBJPROP_ARROWCODE, code); ObjectSetInteger(0, name, OBJPROP_COLOR, c); } } // PlotLabel is identical, but creates OBJ_TEXT and sets OBJPROP_TEXT.
После каждого бара мы пересобираем метку panelName, в которой отображаем:
- текущую глубину пивота;
- общее количество бычьих и медвежьих разворотов;
- среднее время (в минутах) между разворотами (после накопления как минимум двух).
Это позволяет быстро оценить, как часто происходят пробои структуры при выбранных настройках ATR.
string txt = StringFormat("Depth: %d\nBull Flips: %d\nBear Flips: %d", depth, countBull, countBear); if(countBull>1) txt += "\nAvg HL Int: " + DoubleToString(sumBullInterval/(countBull-1),1) + "m"; if(countBear>1) txt += "\nAvg LH Int: " + DoubleToString(sumBearInterval/(countBear-1),1) + "m"; ObjectSetString(0, panelName, OBJPROP_TEXT, txt);
Наконец, функция Notify(msg) объединяет все методы оповещений в одном месте. Мы всегда вызываем Alert(msg) для всплывающего окна MetaTrader 5, а затем, в зависимости от настроек, воспроизводим звук (PlaySound) или отправляем push-уведомление (SendNotification). Функции уведомления собраны в одном месте, и в будущем можно будет легко добавить уведомления по e-mail или webhook-интеграции.
void Notify(string msg) { Alert(msg); if(InpEnableSound) PlaySound(InpSoundFile); if(InpEnablePush) SendNotification(msg); }
Исходный код
//+------------------------------------------------------------------+ //| Market Structure Flip Detector EA| //| Copyright 2025, MetaQuotes Ltd.| //| https://www.mql5.com/en/users/lynnchris| //+------------------------------------------------------------------+ #property copyright "Copyright 2025, MetaQuotes Ltd." #property link "https://www.mql5.com/en/users/lynnchris" #property version "1.0" #property strict //--- user inputs input int InpAtrPeriod = 14; // ATR lookback input double InpAtrMultiplier = 1.0; // ATR to swing depth factor (lower = looser) input double InpAtrLoosenFactor = 0.5; // Loosen factor for ATR confirmation (0-1) input bool InpAutoShift = true; // Auto-enable chart right shift input int InpShiftBars = 5; // Bars for right margin input bool InpEnableSound = true; input string InpSoundFile = "alert.wav"; input bool InpEnablePush = false; //--- global vars string panelName = "FlipPanel"; int atrHandle; int structState = 0; double prevHigh=0, lastHigh=0; datetime prevHighTime=0, lastHighTime=0; double prevLow=0, lastLow=0; datetime prevLowTime=0, lastLowTime=0; int countBull=0, countBear=0; double sumBullInterval=0, sumBearInterval=0; //+------------------------------------------------------------------+ int OnInit() { if(InpAutoShift) ChartSetInteger(0, CHART_SHIFT, InpShiftBars); atrHandle = iATR(_Symbol, _Period, InpAtrPeriod); if(atrHandle == INVALID_HANDLE) return(INIT_FAILED); ObjectCreate(0, panelName, OBJ_LABEL, 0, 0, 0); ObjectSetInteger(0, panelName, OBJPROP_CORNER, CORNER_LEFT_UPPER); ObjectSetInteger(0, panelName, OBJPROP_XDISTANCE, 10); ObjectSetInteger(0, panelName, OBJPROP_YDISTANCE, 10); ObjectSetInteger(0, panelName, OBJPROP_FONTSIZE, 10); ObjectSetInteger(0, panelName, OBJPROP_COLOR, clrYellow); ObjectSetInteger(0, panelName, OBJPROP_BACK, false); ObjectSetInteger(0, panelName, OBJPROP_ZORDER, 1); return(INIT_SUCCEEDED); } //+------------------------------------------------------------------+ void OnDeinit(const int reason) { ObjectsDeleteAll(0, -1, OBJ_ARROW); ObjectsDeleteAll(0, -1, OBJ_TEXT); ObjectDelete(0, panelName); if(atrHandle != INVALID_HANDLE) IndicatorRelease(atrHandle); } //+------------------------------------------------------------------+ void OnTick() { static datetime lastBar=0; datetime thisBar = iTime(_Symbol,_Period,1); if(thisBar==lastBar) return; lastBar=thisBar; double atrBuf[]; if(CopyBuffer(atrHandle,0,1,1,atrBuf)<=0) return; double atr = atrBuf[0]; // loosen ATR confirmation by InpAtrLoosenFactor (0-1) double rawDepth = atr/SymbolInfoDouble(_Symbol,SYMBOL_POINT)*InpAtrMultiplier; int depth = MathMax(1, (int)(rawDepth * InpAtrLoosenFactor)); bool newHigh=false,newLow=false; double h=iHigh(_Symbol,_Period,1), l=iLow(_Symbol,_Period,1); if(IsSwingHigh(1,depth)) { prevHigh = lastHigh; prevHighTime = lastHighTime; lastHigh = h; lastHighTime = thisBar; newHigh = true; } if(IsSwingLow(1,depth)) { prevLow = lastLow; prevLowTime = lastLowTime; lastLow = l; lastLowTime = thisBar; newLow = true; } double off = SymbolInfoDouble(_Symbol,SYMBOL_POINT)*10; // Bearish Flip: Lower High after a Higher High if(newHigh && structState==1 && prevHigh>0 && lastHigh<prevHigh) { // signal arrow and label at current LH PlotArrow("Bear_"+IntegerToString((int)lastHighTime), lastHighTime, lastHigh, 234, clrRed); PlotLabel("LH_"+IntegerToString((int)lastHighTime), lastHighTime, lastHigh+off, "LH", clrRed); // label the previous LH used for comparison PlotLabel("PrevLH_"+IntegerToString((int)prevHighTime), prevHighTime, prevHigh+off, "LH_prev", clrRed); Notify("Bearish Flip (LH) at "+TimeToString(lastHighTime,TIME_DATE|TIME_MINUTES)); if(countBear>0) sumBearInterval += (lastHighTime-prevLowTime)/60.0; countBear++; } // Bullish Flip: Higher Low after a Lower Low if(newLow && structState==2 && prevLow>0 && lastLow>prevLow) { // signal arrow and label at current HL PlotArrow("Bull_"+IntegerToString((int)lastLowTime), lastLowTime, lastLow, 233, clrLime); PlotLabel("HL_"+IntegerToString((int)lastLowTime), lastLowTime, lastLow-off, "HL", clrLime); // label the previous HL used for comparison PlotLabel("PrevHL_"+IntegerToString((int)prevLowTime), prevLowTime, prevLow-off, "HL_prev", clrLime); Notify("Bullish Flip (HL) at "+TimeToString(lastLowTime,TIME_DATE|TIME_MINUTES)); if(countBull>0) sumBullInterval += (lastLowTime-prevHighTime)/60.0; countBull++; } // update structure state if(newHigh && prevHigh>0 && lastHigh>prevHigh) structState = 1; if(newLow && prevLow>0 && lastLow <prevLow) structState = 2; // update panel stats string txt = "Depth: "+IntegerToString(depth)+"\n"; txt += "Bull Flips: "+IntegerToString(countBull)+"\n"; txt += "Bear Flips: "+IntegerToString(countBear); if(countBull>1) txt += "\nAvg HL Int: "+DoubleToString(sumBullInterval/(countBull-1),1)+"m"; if(countBear>1) txt += "\nAvg LH Int: "+DoubleToString(sumBearInterval/(countBear-1),1)+"m"; ObjectSetString(0, panelName, OBJPROP_TEXT, txt); } //+------------------------------------------------------------------+ bool IsSwingHigh(int shift,int depth) { double p = iHigh(_Symbol,_Period,shift); for(int i=shift-depth; i<=shift+depth; i++) if(i>=0 && iHigh(_Symbol,_Period,i) > p) return false; return true; } //+------------------------------------------------------------------+ bool IsSwingLow(int shift,int depth) { double p = iLow(_Symbol,_Period,shift); for(int i=shift-depth; i<=shift+depth; i++) if(i>=0 && iLow(_Symbol,_Period,i) < p) return false; return true; } //+------------------------------------------------------------------+ void PlotArrow(string nm,datetime t,double price,int code,color c) { if(ObjectFind(0,nm) < 0) { ObjectCreate(0, nm, OBJ_ARROW, 0, t, price); ObjectSetInteger(0, nm, OBJPROP_ARROWCODE, code); ObjectSetInteger(0, nm, OBJPROP_COLOR, c); ObjectSetInteger(0, nm, OBJPROP_WIDTH, 2); } } //+------------------------------------------------------------------+ void PlotLabel(string nm,datetime t,double price,string txt,color c) { if(ObjectFind(0,nm) < 0) { ObjectCreate(0, nm, OBJ_TEXT, 0, t, price); ObjectSetString(0, nm, OBJPROP_TEXT, txt); ObjectSetInteger(0, nm, OBJPROP_COLOR, c); ObjectSetInteger(0, nm, OBJPROP_FONTSIZE, 10); } } //+------------------------------------------------------------------+ void Notify(string msg) { Alert(msg); if(InpEnableSound) PlaySound(InpSoundFile); if(InpEnablePush) SendNotification(msg); } //+------------------------------------------------------------------+
Результаты
Ниже представлены результаты тестировании в условиях реального рынка и на истории.
Реальный рынок
На графике выше советник сначала нашел свинг-максимум, добавил подпись "LH_prev", то есть найдены два последовательных более высоких максимума и сформирована восходящая структура. Через несколько баров найден очередной свинг-максимум, который не превосходит предыдущий пик — этот более низкий максимум в рамках восходящего тренда. Поэтому советник рисует красную стрелку и метку "LH" на соответствующем баре. Этот сигнал медвежьего разворота указывает на ослабление бычьего импульса и предупреждает о возможном начале нисходящего движения.
GIF работы на реальном рынке
Ниже я привел GIF-анимацию с демонстрацией работы советника на EURUSD. На закрытии минутных свечей советник отслеживает последовательные минимумы в поисках свинг-минимума, превышающего предыдущую впадину. Когда появляется более высокий минимум, советник рисует зеленую стрелку HL, отмечая бычий разворот. В это же время обновляется панель — здесь она показывает 12 бычьих разворотов, 1 медвежий разворот и средний интервал HL в 108,0 минуты, — отражая обновленную статистику. Н апримере показан переход от нисходящей структуры к потенциальному восходящему движению.

Тестирование на истории
Ниже приведена таблица результатов анализа по нескольким таймфреймам. "Положительные сигналы" — это те, после которых рынок двигался в указанном направлении в течение продолжительного периода.
Таймфрейм 5 минут
| Тип сигнала | Всего сигналов | Положительные сигналы | Win Rate |
|---|---|---|---|
| Sell | 56 | 39 | 70% |
| Buy | 53 | 44 | 83% |
Таймфрейм 15 минут
| Тип сигнала | Всего сигналов | Положительные сигналы | Win Rate |
|---|---|---|---|
| Sell | 7 | 5 | 71% |
| Buy | 14 | 9 | 64% |
Видим, что детектор разворота рыночной структуры стабильно генерирует прибыльные сигналы, особенно на младших таймфреймах. Сетапы на продажу демонстрируют уровень попадания 70% и выше, что подчеркивает эффективность инструмента. Это значительный шаг вперед в автоматизации анализа работы по ценовому действию (price action) и приближает нас к полностью системному торговому инструментарию.
Заключение
Мы разработали и протестировали данный инструмент в условиях реального рынка и на истории. Можно сделать вывод, что он стабильно демонстрирует высокую эффективность, особенно при скальпинге на низких таймфреймах, где способен генерировать существенную доходность. Тем не менее крайне важно соблюдать осторожность и подтверждать его сигналы дополнительными методами анализа перед открытием сделок. Кроме того, рекомендуется протестировать инструмент на различных валютных парах, чтобы найти лучшие варианты. В процессе тестирования вы также можете поэкспериментировать со входными параметрами, чтобы дополнительно оптимизировать его работу.
| Дата | Название инструмента | Описание | Версия | Обновления | Примечания |
|---|---|---|---|---|---|
| 01/10/24 | Chart Projector | Скрипт для наложения эффекта призрака на движение цены за предыдущий день. | 1.0 | Первоначальная версия | Инструмент номер 1 |
| 18/11/24 | Analytical Comment | Предоставляет информацию за предыдущий день в табличном формате, а также прогнозирует будущее направление рынка. | 1.0 | Первоначальная версия | Инструмент номер 2 |
| 27/11/24 | Analytics Master | Регулярное обновление рыночных показателей каждые два часа | 1.01 | Вторая версия | Инструмент номер 3 |
| 02/12/24 | Analytics Forecaster | Регулярное обновление рыночных показателей каждые два часа с интеграцией с Telegram | 1.1 | Третья версия | Инструмент номер 4 |
| 09/12/24 | Volatility Navigator | Советник анализирует рыночные условия с помощью полос Боллинджера, RSI и ATR. | 1.0 | Первоначальная версия | Инструмент номер 5 |
| 19/12/24 | Mean Reversion Signal Reaper | Анализирует рынок и генерирует сигналы, используя стратегию возврата к среднему | 1.0 | Первоначальная версия | Инструмент номер 6 |
| 9/01/25 | Signal Pulse | Анализирует несколько таймфреймов | 1.0 | Первоначальная версия | Инструмент номер 7 |
| 17/01/25 | Metrics Board | Панель с кнопкfvb для анализа | 1.0 | Первоначальная версия | Инструмент номер 8 |
| 21/01/25 | External Flow | Аналитика с помощью внешних библиотек | 1.0 | Первоначальная версия | Инструмент номер 9 |
| 27/01/25 | VWAP | Взвешенная по объему средняя цена | 1.3 | Первоначальная версия | Инструмент номер 10 |
| 02/02/25 | Heikin Ashi | Сглаживание тренда и идентификация сигналов разворота | 1.0 | Первоначальная версия | Инструмент номер 11 |
| 04/02/25 | FibVWAP | Генерация сигнала с помощью анализа Python | 1.0 | Первоначальная версия | Инструмент номер 12 |
| 14/02/25 | RSI DIVERGENCE | Дивергенция цены и RSI | 1.0 | Первоначальная версия | Инструмент номер 13 |
| 17/02/25 | Parabolic Stop and Reverse (PSAR) | Автоматизация стратегии PSAR | 1.0 | Первоначальная версия | Инструмент номер 14 |
| 20/02/25 | Скрипт Quarters Drawer | Нанесение уровней четвертей на график | 1.0 | Первоначальная версия | Инструмент номер 15 |
| 27/02/25 | Intrusion Detector | Обнаружение и оповещение о достижении ценой уровней четвертей | 1.0 | Первоначальная версия | Инструмент номер 16 |
| 27/02/25 | TrendLoom Tool | Панель мультитаймфреймового анализа | 1.0 | Первоначальная версия | Инструмент номер 17 |
| 11/03/25 | Quarters Board | Панель с кнопками для включения или отключения квартальных уровней | 1.0 | Первоначальная версия | Инструмент номер 18 |
| 26/03/25 | ZigZag Analyzer | Построение линий тренда с помощью индикатора ZigZag | 1.0 | Первоначальная версия | Инструмент номер 19 |
| 10/04/25 | Correlation Pathfinder | Построение графиков корреляции валютных курсов с использованием библиотек Python. | 1.0 | Первоначальная версия | Инструмент номер 20 |
| 23/04/25 | Market Structure Flip Detector Tool | Поиск разворотов рыночной структуры | 1.0 | Первоначальная версия | Tool number 21 |
Перевод с английского произведен MetaQuotes Ltd.
Оригинальная статья: https://www.mql5.com/en/articles/17891
Предупреждение: все права на данные материалы принадлежат MetaQuotes Ltd. Полная или частичная перепечатка запрещена.
Данная статья написана пользователем сайта и отражает его личную точку зрения. Компания MetaQuotes Ltd не несет ответственности за достоверность представленной информации, а также за возможные последствия использования описанных решений, стратегий или рекомендаций.
Конвейеры обработки данных (пайплайны) в MQL5
Нейросети в трейдинге: Гибридные модели прогнозирования с управляемой смесью распределений (Окончание)
Оптимизация Роем Жуков — Beetle Swarm Optimization (BSO)
- Бесплатные приложения для трейдинга
- 8 000+ сигналов для копирования
- Экономические новости для анализа финансовых рынков
Вы принимаете политику сайта и условия использования
Здравствуйте, я не понимаю, почему вы пишете int i=shift-depth, разве нельзя было просто использовать int i=0 ?
Не могли бы вы объяснить это? Спасибо.