Разработка инструментария для анализа движения цен (Часть 14): Parabolic Stop and Reverse
Содержание
Введение
Технические индикаторы — это сигналы, генерируемые путем анализа исторических данных, таких как цена, объем и открытый интерес. Эти эвристические инструменты позволяют трейдерам оценивать поведение рынка и прогнозировать будущие движения на основе проверенных тенденций и статистических моделей.
В этой статье мы сосредоточимся на разработке советника с использованием MQL5, предназначенного для выявления потенциальных разворотов рынка. Советник использует Parabolic SAR для обнаружения сигналов, генерируя торговые сигналы путем мониторинга технических индикаторов, оценивая их достоверность в реальном времени и определяя оптимальные точки выхода при достижении заранее определенных уровней.
Наше обсуждение начинается с обзора базовой стратегии и обоснования использования технических индикаторов в торговле. После этого мы подробно рассмотрим процесс внедрения в MQL5, всесторонне проанализируем результаты нашего тестирования и дадим заключение и рекомендации для трейдеров, стремящихся интегрировать эти методы в свои системы.
Стратегия
Parabolic SAR
В индикаторе Parabolic SAR термин parabolic относится к форме построенной кривой. По мере развития тренда точки индикатора ускоряются, принимая кривую параболическую форму, что отражает растущий импульс по мере того, как цены отдаляются от недавних экстремумов.
С другой стороны, SAR означает Stop and Reverse (стоп и разворот). Этот компонент индикатора сигнализирует о возможном развороте тренда. Когда точки переходят с одной стороны цены на другую, это говорит о том, что текущий тренд, возможно, заканчивается, побуждая трейдеров задуматься о закрытии текущей позиции и подготовке к возможному развороту направления рынка.
Прежде чем углубляться в нашу концепцию, важно добавить Parabolic SAR на график для удобства использования. Есть два способа сделать это, но я объясню один подход. В MetaTrader 5 откройте меню "Вставка", выберите "Индикаторы" > "Трендовые". Найдите и добавьте Parabolic SAR. После добавления настройте параметры по своему усмотрению, убедившись, что они соответствуют параметрам, используемым в вашем советнике (EA).
Более подробную информацию о том, как добавить индикатор, см. на рисунке 1 ниже.

Рис 1. Добавление индикатора
Логика генерации сигналов
Работа с индикатором Parabolic SAR подразумевает внимательное наблюдение за взаимодействием параболы SAR и ценового движения. В этой стратегии сигналы генерируются на основе следующей логики:
Сигнал на покупку
Сигнал на покупку срабатывает, когда:
- Текущий бар является бычьим (цена закрытия выше цены открытия), а его значение PSAR ниже цены закрытия.
- Предыдущие два бара подтверждают медвежий тренд, поскольку их точки PSAR находятся выше цен закрытия.
- Разрыв между значениями PSAR предыдущих баров находится в приемлемом диапазоне, что обеспечивает согласованность.

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

Рис. 3. Условия продажи
Реализация средствами MQL5
Заголовок и свойства файла
мы начинаем с заголовка, который включает в себя важную информацию о файле, такую как имя файла, сведения об авторских правах и ссылку на веб-страницу автора, в данном случае - на мою. Далее следуют несколько директив #property, которые задают критические свойства компиляции, такие как номер версии и строгий режим.
//+--------------------------------------------------------------------+ //| Parabolic SAR EA.mql5 | //| Copyright 2025, Christian Benjamin | //| https://www.mql5.com | //+--------------------------------------------------------------------+ #property copyright "2025, Christian Benjamin" #property link "https://www.mql5.com/en/users/lynnchris" #property version "1.0" #property strict
Использование #property strict является основополагающим для строгой проверки типов и валидации параметров советника. Эта мера предосторожности помогает выявить потенциальные ошибки кодирования до того, как они перерастут в ошибки времени выполнения. Независимо от того, планируете ли вы опубликовать советник или использовать его в реальной торговой среде, эта настройка действует как защита, обеспечивая бесперебойную работу и совместимость с будущими обновлениями MetaTrader 5. Это важная мера контроля качества вашего кода.
Входные параметры
Далее код определяет ряд входных параметров, которые можно настроить непосредственно из интерфейса MetaTrader 5. Эти входные данные включают параметры для индикатора Parabolic SAR, такие как SARStep и SARMaximum, которые контролируют коэффициент ускорения и максимальное ускорение соответственно. Также имеются настройки для уточнения обнаружения сигнала, например MinConsecutiveDots и MaxDotGapPercentage, которые гарантируют, что сигналы генерируются только при сильных трендах. Вы также можете включить оповещения, звуковые уведомления и отображение стрелок на графике.
// Input parameters for the Parabolic SAR indicator input double SARStep = 0.02; // Acceleration factor for PSAR input double SARMaximum = 0.2; // Maximum acceleration for PSAR // Input parameters for refining the signal based on PSAR dots input int MinConsecutiveDots = 2; // Require at least 2 consecutive bars in one trend before reversal input double MaxDotGapPercentage = 1.0; // Maximum allowed gap between consecutive PSAR dots (% of current close) // Input parameters for alerts and arrow drawing input bool EnableAlerts = true; // Enable popup alerts input bool EnableSound = true; // Enable sound alerts input bool EnableArrows = true; // Draw arrows on chart input string BuyArrowSymbol = "233"; // Wingdings up arrow (as string) input string SellArrowSymbol = "234"; // Wingdings down arrow (as string) input int ArrowWidth = 2; // Arrow thickness input double ArrowOffsetMultiplier = 5; // Multiplier for arrow placement offset
Эти входные данные позволяют вам настраивать советник в соответствии с различными рыночными условиями и вашими торговыми предпочтениями. Важным аспектом является универсальность символов стрелок в MetaTrader 5. Эти символы, взятые из шрифта Wingdings, легко настраиваются. Например, вы можете выбрать галочку для сигналов на покупку или X для сигналов на продажу, в зависимости от вашего стиля торговли. Регулировка таких параметров, как ширина стрелки, еще больше улучшает видимость сигнала, позволяя точно настроить визуальные элементы графика для ясности и мгновенного распознавания.
Глобальные переменные и перечисления
Для управления внутренним состоянием советника объявлено несколько глобальных переменных и перечисление. Например, мы сохраняем хэндл индикатора для Parabolic SAR в переменной sarHandle, которая позволяет нам ссылаться на индикатор во всем нашем коде. Мы также отслеживаем время последнего обработанного бара с помощью lastBarTime таким образом, чтобы советник обрабатывал каждый бар только один раз. Перечисление SignalType определяет возможные состояния сигнала: нет сигнала, сигнал на покупку или сигнал на продажу.
// Global indicator handle for PSAR int sarHandle = INVALID_HANDLE; // Global variable to track last processed bar time datetime lastBarTime = 0; // Enumeration for signal types enum SignalType { NO_SIGNAL, BUY_SIGNAL, SELL_SIGNAL }; // Global variables for pending signal mechanism SignalType pendingSignal = NO_SIGNAL; int waitCount = 0; // Counts new closed bars since signal detection double pendingReversalLevel = 0.0; // Stores the PSAR value at signal detection
Кроме того, такие переменные, как pendingSignal, waitCount, и pendingReversalLevel используются для управления отложенными сигналами, которые ожидают подтверждения в течение нескольких баров, прежде чем будет выполнено окончательное действие. Важной переменной является pendingReversalLevel, который фиксирует значение PSAR в момент генерации сигнала. Выступая в качестве точки отсчета, этот уровень помогает советнику отслеживать последующие движения цен, чтобы определить, является ли разворот подлинным или это просто ложная тревога. Этот механизм контрольных точек имеет жизненно важное значение для сокращения ненужных сделок и повышения общей точности советника.
Рисование стрелок на графике
Для более наглядного визуального представления в советнике предусмотрена функция DrawSignalArrow. Эта функция отвечает за отрисовку стрелки на графике при обнаружении сигнала. Она работает, генерируя уникальное имя объекта на основе времени бара, проверяя наличие любого существующего объекта с таким именем и удаляя его, чтобы избежать дублирования.
void DrawSignalArrow(string prefix, datetime barTime, double price, color arrowColor, long arrowCode) { string arrowName = prefix + "_" + TimeToString(barTime, TIME_SECONDS); // Remove existing object with the same name to prevent duplicates if(ObjectFind(0, arrowName) != -1) ObjectDelete(0, arrowName); if(ObjectCreate(0, arrowName, OBJ_ARROW, 0, barTime, price)) { ObjectSetInteger(0, arrowName, OBJPROP_COLOR, arrowColor); ObjectSetInteger(0, arrowName, OBJPROP_ARROWCODE, arrowCode); ObjectSetInteger(0, arrowName, OBJPROP_WIDTH, ArrowWidth); } else { Print("Failed to create arrow object: ", arrowName); } }
Советник использует объект OBJ_ARROW для визуального представления сигналов на графике. Чтобы избежать беспорядка из-за дублирующихся маркеров, код сначала проверяет и удаляет все существующие стрелки, прежде чем рисовать новую. Такой подход позволяет сохранить чистый и понятный график, что имеет решающее значение для принятия торговых решений в реальном времени. Вы также можете настраивать различные аспекты, такие как символ стрелки, цвет и смещение, в соответствии с вашими личными предпочтениями в отношении визуальных подсказок.
Функция обнаружения сигнала
Функция CheckForSignal - это, безусловно, сердце советника. Здесь код проверяет последние три бара цен и данные PSAR. Для распознавания сигнала на покупку функция проверяет, является ли текущий бар бычьим (то есть его цена закрытия выше цены открытия) и находится ли точка PSAR под свечой, в то время как два предыдущих бара были медвежьими, а их точки PSAR — над свечами. Обратная логика применима для сигнала на продажу.
SignalType CheckForSignal(const double &sarArray[], const double &openArray[], const double &closeArray[]) { // Mapping indices: // Index 0: Last closed bar (current candidate) // Index 1: Previous bar // Index 2: Bar before previous double sar0 = sarArray[0], sar1 = sarArray[1], sar2 = sarArray[2]; double open0 = openArray[0], close0 = closeArray[0]; double open1 = openArray[1], close1 = closeArray[1]; double open2 = openArray[2], close2 = closeArray[2]; // Check for BUY signal: if((close0 > open0) && (sar0 < close0) && (sar1 > close1) && (sar2 > close2)) { int countBearish = 0; if(sar1 > close1) countBearish++; if(sar2 > close2) countBearish++; double dotGap = MathAbs(sar1 - sar2); double gapThreshold = (MaxDotGapPercentage / 100.0) * close0; if(countBearish >= MinConsecutiveDots && dotGap <= gapThreshold) return BUY_SIGNAL; } // Check for SELL signal: if((close0 < open0) && (sar0 > close0) && (sar1 < close1) && (sar2 < close2)) { int countBullish = 0; if(sar1 < close1) countBullish++; if(sar2 < close2) countBullish++; double dotGap = MathAbs(sar1 - sar2); double gapThreshold = (MaxDotGapPercentage / 100.0) * close0; if(countBullish >= MinConsecutiveDots && dotGap <= gapThreshold) return SELL_SIGNAL; } return NO_SIGNAL; }
Функция также использует пороговое значение разрыва, чтобы гарантировать, что расстояние между последовательными точками PSAR остается в приемлемых пределах. Если условия выполнены, возвращается соответствующий тип сигнала (покупка или продажа); в противном случае сигнал не возвращается. Функция обнаружения сигналов эффективно определяет развороты, используя позиционирование PSAR и свечные паттерны. Однако его конструкция допускает дальнейшее усовершенствование путем включения дополнительных фильтров, таких как скользящие средние, RSI или даже индекс волатильности. Добавление этих слоев может помочь устранить ложные сигналы во время боковых колебаний рынка или в периоды, на которые влияют важные новости. По сути, советник создан для адаптации, предоставляя вам свободу подстраивать его логику под вашу уникальную торговую стратегию и рыночные условия.
Обнаружение нового бара
Перед обработкой любых данных советнику необходимо определить, закрылся ли новый бар. Функция IsNewBar делает это, извлекая время последнего закрытого бара и сравнивая его с сохраненным lastBarTime. Если время отличается, это означает, что сформировался новый бар, поэтому функция lastBarTime обновляется и возвращает true.
bool IsNewBar() { datetime times[]; if(CopyTime(_Symbol, _Period, 1, 1, times) <= 0) { Print("Failed to retrieve bar time in IsNewBar()."); return false; } if(times[0] != lastBarTime) { lastBarTime = times[0]; return true; } return false; }
Эта проверка имеет решающее значение, поскольку она гарантирует, что советник обрабатывает данные только один раз для каждого нового бара, предотвращая тем самым повторную или ошибочную обработку сигналов. Функция IsNewBar усиливает эффективность советника. Гарантируя, что сигналы обрабатываются только один раз для каждого нового бара, он предотвращает избыточные вычисления и повторные оповещения. Эта простая проверка обеспечивает эффективную работу советника и снижает риск неверной интерпретации ценовых движений в пределах одного бара. В целом, это помогает поддерживать постоянную и предсказуемую производительность платформы MetaTrader.
Инициализация и деинициализация
При загрузке советника функция OnInit запускается первой. Его основная задача — создать хэндл для встроенного индикатора Parabolic SAR, используя параметры, предоставленные пользователем. Если индикатор успешно инициализирован, выводится подтверждающее сообщение. Если нет, формируется сообщение об ошибке и инициализация завершается неудачей. Функция OnDeinit вызывается при удалении советника с графика или при закрытии терминала.
int OnInit() { // Create the built-in Parabolic SAR indicator handle sarHandle = iSAR(_Symbol, _Period, SARStep, SARMaximum); if(sarHandle == INVALID_HANDLE) { Print("Error creating PSAR handle"); return INIT_FAILED; } Print("SAR EA initialized successfully."); return INIT_SUCCEEDED; }
Эта функция отвечает за освобождение хэндла индикатора и очистку любых графических объектов (например, стрелок), созданных во время работы советника, гарантируя отсутствие помех. Правильно спроектированный советник уделяет внимание очистке во время деинициализации. Удаление всех стрелок и других визуальных элементов, созданных в ходе работы, предотвращает загромождение графика устаревшими метками. Процесс очистки сродни хорошей уборке: он гарантирует, что каждый раз при запуске советника вы работаете с новым, незагроможденным графиком и оптимальной производительностью системы.
Основная логика (функция OnTick)
Наконец, функция OnTick - это основной движок, который работает на каждом тике. Сначала он проверяет, закрылся ли новый бар, используя функцию IsNewBar. Если новый бар не обнаружен, функция завершает работу преждевременно, чтобы избежать избыточной обработки. После подтверждения нового бара советник извлекает последние значения PSAR вместе с соответствующими ценами открытия и закрытия для последних баров. На этом этапе советник оценивает, есть ли ожидающий подтверждения сигнал. Если сигнал ожидается, счетчик (waitCount) увеличивается на единицу для отслеживания количества пройденных баров.
void OnTick() { // Process only once per new closed bar if(!IsNewBar()) return; // Retrieve PSAR and price data double sarArray[4]; if(CopyBuffer(sarHandle, 0, 1, 4, sarArray) < 3) { /* error handling */ } double openArray[4], closeArray[4]; if(CopyOpen(_Symbol, _Period, 1, 4, openArray) < 3 || CopyClose(_Symbol, _Period, 1, 4, closeArray) < 3) { /* error handling */ } // Pending Signal Logic... if(pendingSignal != NO_SIGNAL) { waitCount++; // Increment waiting counter if(pendingSignal == BUY_SIGNAL) { if(closeArray[0] <= pendingReversalLevel) { // Confirm reversal: Close alert for BUY signal } else if(waitCount >= 3) { // Warn about a possible fake BUY signal } } else if(pendingSignal == SELL_SIGNAL) { if(closeArray[0] >= pendingReversalLevel) { // Confirm reversal: Close alert for SELL signal } else if(waitCount >= 3) { // Warn about a possible fake SELL signal } } return; // Wait until pending signal is resolved } // Check for a new reversal signal if no pending signal exists SignalType newSignal = CheckForSignal(sarArray, openArray, closeArray); if(newSignal != NO_SIGNAL) { pendingSignal = newSignal; waitCount = 0; // Reset counter pendingReversalLevel = sarArray[0]; // Store current PSAR value // Alert and optionally draw an arrow based on the new signal if(newSignal == BUY_SIGNAL) { /* process BUY signal */ } else if(newSignal == SELL_SIGNAL) { /* process SELL signal */ } } }
Затем советник проверяет, достигла ли текущая цена сохраненного уровня разворота: если это так, выдается оповещение о необходимости "закрыть здесь"; если три бара проходят без подтверждения, советник предупреждает о возможном ложном сигнале. Если активного ожидающего сигнала нет, советник вызывает функцию CheckForSignal, позволяющую увидеть, появился ли новый сигнал. Если сигнал появился, она сохраняет сигнал, сбрасывает счетчик ожидания, устанавливает уровень ожидаемого разворота на основе текущего значения PSAR и активирует соответствующие оповещения и визуальные маркеры. Инновационная особенность функции OnTick - это метод сортировки потенциальных ложных сигналов.
Используя счетчик (waitCount) для отслеживания, сколько баров прошло после генерации сигнала, советник дает время на подтверждение, прежде чем предпринять какие-либо действия. Такая задержка особенно полезна на нестабильных рынках, где резкие колебания цен в противном случае могли бы спровоцировать преждевременные сигналы. Регулировка количества баров подтверждения позволяет вам балансировать между отзывчивостью и осторожностью, делая советник достаточно гибким для поддержки как краткосрочного скальпинга, так и долгосрочных трендовых стратегий.
Полный MQL5-код советника
//+--------------------------------------------------------------------+ //| Parabolic SAR EA.mql5 | //| Copyright 2025, Christian Benjamin | //| https://www.mql5.com | //+--------------------------------------------------------------------+ #property copyright "2025, Christian Benjamin" #property link "https://www.mql5.com/en/users/lynnchris" #property version "1.2" #property strict // Input parameters for the Parabolic SAR indicator input double SARStep = 0.1; // Acceleration factor for PSAR input double SARMaximum = 1; // Maximum acceleration for PSAR // Input parameters for refining the signal based on PSAR dots input int MinConsecutiveDots = 2; // Require at least 2 consecutive bars in one trend before reversal input double MaxDotGapPercentage = 1.0; // Maximum allowed gap between consecutive PSAR dots (% of current close) // Input parameters for alerts and arrow drawing input bool EnableAlerts = true; // Enable popup alerts input bool EnableSound = true; // Enable sound alerts input bool EnableArrows = true; // Draw arrows on chart input string BuyArrowSymbol = "233"; // Wingdings up arrow (as string) input string SellArrowSymbol = "234"; // Wingdings down arrow (as string) input int ArrowWidth = 2; // Arrow thickness input double ArrowOffsetMultiplier = 5; // Multiplier for arrow placement offset // Global indicator handle for PSAR int sarHandle = INVALID_HANDLE; // Global variable to track last processed bar time datetime lastBarTime = 0; // Enumeration for signal types enum SignalType { NO_SIGNAL, BUY_SIGNAL, SELL_SIGNAL }; // Global variables for pending signal mechanism SignalType pendingSignal = NO_SIGNAL; int waitCount = 0; // Counts new closed bars since signal detection double pendingReversalLevel = 0.0; // Stores the PSAR value at signal detection //+------------------------------------------------------------------+ //| DrawSignalArrow - Draws an arrow object on the chart | //+------------------------------------------------------------------+ void DrawSignalArrow(string prefix, datetime barTime, double price, color arrowColor, long arrowCode) { string arrowName = prefix + "_" + TimeToString(barTime, TIME_SECONDS); // Remove existing object with the same name to prevent duplicates if(ObjectFind(0, arrowName) != -1) ObjectDelete(0, arrowName); if(ObjectCreate(0, arrowName, OBJ_ARROW, 0, barTime, price)) { ObjectSetInteger(0, arrowName, OBJPROP_COLOR, arrowColor); ObjectSetInteger(0, arrowName, OBJPROP_ARROWCODE, arrowCode); ObjectSetInteger(0, arrowName, OBJPROP_WIDTH, ArrowWidth); } else { Print("Failed to create arrow object: ", arrowName); } } //+-------------------------------------------------------------------+ //| CheckForSignal - Evaluates PSAR and price data to determine signal| //+-------------------------------------------------------------------+ SignalType CheckForSignal(const double &sarArray[], const double &openArray[], const double &closeArray[]) { // Mapping indices: // Index 0: Last closed bar (current candidate) // Index 1: Previous bar // Index 2: Bar before previous double sar0 = sarArray[0], sar1 = sarArray[1], sar2 = sarArray[2]; double open0 = openArray[0], close0 = closeArray[0]; double open1 = openArray[1], close1 = closeArray[1]; double open2 = openArray[2], close2 = closeArray[2]; // Check for BUY signal: if((close0 > open0) && (sar0 < close0) && (sar1 > close1) && (sar2 > close2)) { int countBearish = 0; if(sar1 > close1) countBearish++; if(sar2 > close2) countBearish++; double dotGap = MathAbs(sar1 - sar2); double gapThreshold = (MaxDotGapPercentage / 100.0) * close0; if(countBearish >= MinConsecutiveDots && dotGap <= gapThreshold) return BUY_SIGNAL; } // Check for SELL signal: if((close0 < open0) && (sar0 > close0) && (sar1 < close1) && (sar2 < close2)) { int countBullish = 0; if(sar1 < close1) countBullish++; if(sar2 < close2) countBullish++; double dotGap = MathAbs(sar1 - sar2); double gapThreshold = (MaxDotGapPercentage / 100.0) * close0; if(countBullish >= MinConsecutiveDots && dotGap <= gapThreshold) return SELL_SIGNAL; } return NO_SIGNAL; } //+------------------------------------------------------------------+ //| IsNewBar - Determines if a new closed bar is available | //+------------------------------------------------------------------+ bool IsNewBar() { datetime times[]; if(CopyTime(_Symbol, _Period, 1, 1, times) <= 0) { Print("Failed to retrieve bar time in IsNewBar()."); return false; } if(times[0] != lastBarTime) { lastBarTime = times[0]; return true; } return false; } //+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+ int OnInit() { // Create the built-in Parabolic SAR indicator handle sarHandle = iSAR(_Symbol, _Period, SARStep, SARMaximum); if(sarHandle == INVALID_HANDLE) { Print("Error creating PSAR handle"); return INIT_FAILED; } Print("SAR EA initialized successfully."); return INIT_SUCCEEDED; } //+------------------------------------------------------------------+ //| Expert deinitialization function | //+------------------------------------------------------------------+ void OnDeinit(const int reason) { if(sarHandle != INVALID_HANDLE) IndicatorRelease(sarHandle); // Remove all arrow objects from the current chart window ObjectsDeleteAll(0, (int)OBJ_ARROW, 0); } //+------------------------------------------------------------------+ //| Expert tick function | //+------------------------------------------------------------------+ void OnTick() { // Process only once per new closed bar if(!IsNewBar()) return; // Retrieve the last 4 PSAR values (we require at least 3 values) double sarArray[4]; if(CopyBuffer(sarHandle, 0, 1, 4, sarArray) < 3) { Print("Failed to retrieve PSAR data."); return; } // Retrieve the last 4 bars' price data (Open and Close) double openArray[4], closeArray[4]; if(CopyOpen(_Symbol, _Period, 1, 4, openArray) < 3 || CopyClose(_Symbol, _Period, 1, 4, closeArray) < 3) { Print("Failed to retrieve price data."); return; } // Process pending signal logic if a signal is waiting confirmation if(pendingSignal != NO_SIGNAL) { waitCount++; // Increment the waiting counter if(pendingSignal == BUY_SIGNAL) { if(closeArray[0] <= pendingReversalLevel) { Print("Reversal level reached for BUY signal. Close here."); if(EnableAlerts) Alert("Close here for BUY signal on ", _Symbol, " at price ", DoubleToString(closeArray[0], _Digits)); if(EnableSound) PlaySound("alert.wav"); pendingSignal = NO_SIGNAL; waitCount = 0; } else if(waitCount >= 3) { Print("Warning: Possible fake BUY signal - reversal not confirmed in 3 candles."); if(EnableAlerts) Alert("Warning: Possible fake BUY signal on ", _Symbol); pendingSignal = NO_SIGNAL; waitCount = 0; } } else if(pendingSignal == SELL_SIGNAL) { if(closeArray[0] >= pendingReversalLevel) { Print("Reversal level reached for SELL signal. Close here."); if(EnableAlerts) Alert("Close here for SELL signal on ", _Symbol, " at price ", DoubleToString(closeArray[0], _Digits)); if(EnableSound) PlaySound("alert.wav"); pendingSignal = NO_SIGNAL; waitCount = 0; } else if(waitCount >= 3) { Print("Warning: Possible fake SELL signal - reversal not confirmed in 3 candles."); if(EnableAlerts) Alert("Warning: Possible fake SELL signal on ", _Symbol); pendingSignal = NO_SIGNAL; waitCount = 0; } } return; } // Check for a new reversal signal SignalType newSignal = CheckForSignal(sarArray, openArray, closeArray); if(newSignal != NO_SIGNAL) { pendingSignal = newSignal; waitCount = 0; // Reset waiting counter pendingReversalLevel = sarArray[0]; // Set reversal level from current PSAR value if(newSignal == BUY_SIGNAL) { Print("Buy signal detected on ", TimeToString(lastBarTime, TIME_DATE|TIME_SECONDS), ". Waiting for reversal confirmation (up to 3 candles)."); if(EnableAlerts) Alert("Buy signal detected on ", _Symbol, " at price ", DoubleToString(closeArray[0], _Digits)); if(EnableArrows) { double lowVal[]; if(CopyLow(_Symbol, _Period, 1, 1, lowVal) > 0) { double offset = _Point * ArrowOffsetMultiplier; DrawSignalArrow("BuyArrow", lastBarTime, lowVal[0] - offset, clrGreen, StringToInteger(BuyArrowSymbol)); } else Print("Failed to retrieve low price for arrow placement."); } } else if(newSignal == SELL_SIGNAL) { Print("Sell signal detected on ", TimeToString(lastBarTime, TIME_DATE|TIME_SECONDS), ". Waiting for reversal confirmation (up to 3 candles)."); if(EnableAlerts) Alert("Sell signal detected on ", _Symbol, " at price ", DoubleToString(closeArray[0], _Digits)); if(EnableArrows) { double highVal[]; if(CopyHigh(_Symbol, _Period, 1, 1, highVal) > 0) { double offset = _Point * ArrowOffsetMultiplier; DrawSignalArrow("SellArrow", lastBarTime, highVal[0] + offset, clrRed, StringToInteger(SellArrowSymbol)); } else Print("Failed to retrieve high price for arrow placement."); } } } } //+------------------------------------------------------------------+
Тестирование и результаты
Прежде чем переходить к реальной торговле, я рекомендую протестировать инструмент на истории на демо-счете. Вот как вы можете настроить свой советник на основе результатов тестирования:- После того, как вы успешно скомпилировали свой советник в MetaEditor, откройте MetaTrader.
- В MetaTrader перейдите в тестер стратегий. Выберите советник, который вы хотите протестировать, затем выберите желаемый таймфрейм, период тестирования и любые другие соответствующие параметры.
- После настройки параметров нажмите "Старт", чтобы начать тестирование.

Рис 4. Инициализация тестера
Ниже приведены изображения и несколько GIF-анимаций тестирования на истории. Первое изображение представляет собой диаграмму для индекса Volatility 25, на которой отмечены подтверждения сигналов на продажу и покупку в ходе тестирования. Далее следуют несколько GIF-анимаций для дополнительной визуализации, которые помогут вам лучше понять работу советника.

Рис. 5. Тестирование на истории на индексе Volatility 25
Тестирование на истории на индексе Volatility 25.

Рис. 6. Тестирование на индексе Volatility 25
Тестирование на истории на индексе Step.

Рис. 7. Тестирование на истории на индексе Step
Реальная торговля на индексе Step.

Рис. 8. Торговля в реальном времени
Заключение
Мы разработали и протестировали советник, добившись положительных результатов. Однако некоторые сигналы могут потребовать дополнительной сортировки. Помните, что этот советник призван дополнять ваши торговые стратегии, а не заменять их. Всегда проверяйте все соответствующие условия, прежде чем совершать какие-либо сделки на основе его сигналов. Работа с более крупными таймфреймами может иметь решающее значение для минимизации ложных сигналов.
| Дата | Название инструмента | Описание | Версия | Обновления | Примечания |
|---|---|---|---|---|---|
| 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 | Панель с кнопками для анализа | 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/2025 | Parabolic Stop and Reverse (PSAR) | Автоматизация стратегии PSAR | 1.0 | Первоначальная версия | Инструмент номер 14 |
Перевод с английского произведен MetaQuotes Ltd.
Оригинальная статья: https://www.mql5.com/en/articles/17234
Предупреждение: все права на данные материалы принадлежат MetaQuotes Ltd. Полная или частичная перепечатка запрещена.
Данная статья написана пользователем сайта и отражает его личную точку зрения. Компания MetaQuotes Ltd не несет ответственности за достоверность представленной информации, а также за возможные последствия использования описанных решений, стратегий или рекомендаций.
Разработка динамического советника на нескольких парах (Часть 3): Стратегии возврата к среднему и моментума
Разработка инструментария для анализа движения цен (Часть 13): RSI Sentinel
Создание самооптимизирующихся советников на MQL5 (Часть 6): Предотвращение стоп-аутов
От новичка до эксперта: Индикатор Market Periods Synchronizer
- Бесплатные приложения для трейдинга
- 8 000+ сигналов для копирования
- Экономические новости для анализа финансовых рынков
Вы принимаете политику сайта и условия использования