Разработка инструментария для анализа Price Action (Часть 67): Автоматизация мониторинга уровней поддержки и сопротивления в MQL5
Содержание
Введение
Горизонтальные уровни поддержки и сопротивления широко используются в торговле по Price Action для обозначения зон, где рынок, вероятно, отреагирует. Трейдеры наносят эти уровни вручную, чтобы принимать решения о входе, выходе и управлении рисками. После нанесения на график эти уровни остаются неизменными. Цена продолжает двигаться, но трейдеру приходится постоянно следить за графиком, чтобы замечать такие взаимодействия с уровнями, как касания, пробои и развороты. В условиях быстрого рынка или при работе с несколькими графиками такой непрерывный мониторинг становится затруднительным, и важные движения можно пропустить.
Проблема не в самих уровнях поддержки и сопротивления. Ограничение связано с отсутствием автоматического механизма, отслеживающего взаимодействие цены с вручную заданными уровнями в реальном времени. В этой статье представлен советник на языке MQL5, который в реальном времени отслеживает вручную нанесенные горизонтальные уровни. Он обнаруживает ключевые взаимодействия по мере развития рыночной ситуации. К концу статьи у вас будет готов советник, который в реальном времени отслеживает вручную построенные уровни поддержки и сопротивления. Он будет обнаруживать касания, пробои, развороты и ретесты, а также генерировать структурированные алерты и сигналы на графике.
Определение модели
Концептуальная основа – горизонтальные линии как уровни поддержки и сопротивления
Горизонтальные уровни поддержки и сопротивления представляют собой дискретные ценовые зоны, в которых многократно наблюдается реакция рынка. Эти уровни обычно строятся по максимумам и минимумам свинга, а также по зонам консолидации цены, где исторически происходил разворот потока ордеров. В результате они служат устойчивыми ориентирами для оценки будущего поведения цены.
Эта стабильность делает горизонтальные уровни особенно удобными для алгоритмического мониторинга. В отличие от динамических индикаторов, горизонтальная линия задает фиксированную ценовую границу, позволяя напрямую сравнивать текущую рыночную цену с заранее заданным уровнем. Это позволяет детерминированно оценивать такие характеристики, как близость, касание и пересечение уровня ценой, без пересчета самого уровня. Концептуально поддержка и сопротивление – это зоны. В этой системе они представлены как отдельные горизонтальные ценовые уровни, что позволяет точно и детерминированно выявлять взаимодействия.

Однако при всей структурной определенности этих уровней их практическое применение по-прежнему ограничено ручным наблюдением. После построения горизонтальные линии не дают никакой автоматической обратной связи. Такие взаимодействия, как касания, пробои и развороты, трейдеру приходится визуально отслеживать в реальном времени. В условиях быстрого рынка или при работе с несколькими графиками это создает пробел в мониторинге, из-за которого значимые взаимодействия могут остаться незамеченными.
Построение уровней поддержки и сопротивления
Уровни поддержки и сопротивления не произвольны; они строятся по четким правилам:
- максимумы и минимумы свинга – вершины и впадины на ценовом графике служат естественными опорными точками;
- зоны кластеризации – области, где цена многократно реагирует, формируют более сильные горизонтальные уровни;
- круглые числа и психологические уровни – целые числа или уровни, оканчивающиеся на 0, 50 или 100, часто выступают психологическими барьерами из-за коллективного поведения трейдеров;
- подтверждение со стороны индикаторов – хотя горизонтальные линии основаны исключительно на цене, их значимость возрастает, когда они совпадают с другими инструментами, такими как скользящие средние, уровни коррекции Фибоначчи или VWAP.

Надежный уровень поддержки или сопротивления формируется при совпадении нескольких критериев. Эти уровни становятся практически применимыми ориентирами для входов, выходов и управления рисками.
Интеграция горизонтальных линий в автоматизированную систему мониторинга
Концептуальный пробел возникает там, где трейдеры полагаются на ручную разметку, но не имеют автоматического мониторинга. Современный инструментарий Price Action может связать статичный и динамический аспекты анализа. Советник выполняет роль интеллектуального наблюдателя:
- он обнаруживает каждую горизонтальную линию на графике, распознавая ее ценовой уровень и атрибуты;
- он отслеживает взаимодействия цены с этими уровнями в реальном времени, включая касания, пробои и ретесты;
- он применяет ту или иную логику принятия решений в зависимости от типа взаимодействия, генерируя алерты или проводя дальнейший анализ при необходимости.
Эта модель сохраняет рабочий процесс трейдера: линии по-прежнему строятся вручную, сохраняя за трейдером возможность принимать решения по своему усмотрению, а советник добавляет непрерывный контроль и быструю реакцию.
Теоретическая архитектура – от взаимодействия к действию
Концептуально система работает как цикл мониторинга:
- ввод – трейдер рисует горизонтальные линии на любом таймфрейме;
- обнаружение – советник запрашивает все графические объекты, отбирает горизонтальные линии и сохраняет их ценовые уровни и идентификаторы;
- мониторинг – на каждом новом тике цена сравнивается с каждой линией, и при необходимости помечаются соответствующие условия, такие как касание, пробой или ретест;
- обработка – система оценивает заранее заданные условия, например, выявляет отбой, сигнализирует о потенциальном пробое или измеряет близость к уровню;
- вывод – генерируются алерты, метки и визуальные маркеры, превращающие статичные линии в инструменты мониторинга с обратной связью.

На данном этапе эта архитектура полностью теоретическая, но она задает переход от ручного анализа к автоматизированному мониторингу на основе правил и подводит к следующему разделу – проектированию и реализации логики на MQL5.
Значение в анализе Price Action
Интеграция вручную построенных горизонтальных линий с автоматизированным мониторингом повышает надежность и эффективность:
- трейдеры больше не пропускают важные взаимодействия;
- алерты позволяют своевременно принимать решения даже в периоды высокой волатильности;
- исторический анализ упрощается, поскольку система может записывать взаимодействия в лог и отслеживать паттерны во времени;
- в сочетании с другими модулями инструментария эти взаимодействия могут входить в состав более крупных стратегий, включая уровни коррекции Фибоначчи, синхронизацию трендовых линий и разметку VWAP.
По сути, система сохраняет за трейдером свободу принятия решений, добавляя алгоритмическую точность и формируя целостную основу для адаптивного анализа Price Action.
Реализация на MQL5
В этом разделе описана реализация системы на MQL5 с акцентом на ее структуру, логику и процесс исполнения. Цель состоит в том, чтобы превратить ранее определенную модель в работоспособный советник, работающий в реальном времени.
Реализация разбирается пошагово. Разбор охватывает инициализацию, обработку действий пользователя, синхронизацию уровней, непрерывный мониторинг цены, обнаружение свечных паттернов и генерацию алертов и сигналов. Каждый компонент разработан для работы в составе единой системы мониторинга.
Инициализация советника – OnInit()
Функция OnInit() подготавливает окружение графика и включает интерактивный мониторинг. Через ChartSetInteger() включается отслеживание событий графика, связанных с созданием и удалением объектов. Это позволяет советнику в реальном времени обнаруживать вручную нанесенные линии поддержки и сопротивления. В ходе этой инициализации с помощью функции CreateButton() создаются три основные кнопки: Sync Supports, Sync Resistances и Clear All Lines. Эти кнопки позволяют пользователю синхронизировать вручную нанесенные линии с массивом мониторинга советника или очищать график, возвращая его в исходное состояние. Благодаря этому система мониторинга активируется сразу после прикрепления советника к графику.
//+------------------------------------------------------------------+ //| Expert initialization function | //| Sets up chart events and user interface | //+------------------------------------------------------------------+ int OnInit() { //--- Enable chart event tracking ChartSetInteger(0,CHART_EVENT_OBJECT_CREATE,true); ChartSetInteger(0,CHART_EVENT_OBJECT_DELETE,true); //--- Create control buttons CreateButton("SyncSupportBtn","Sync Supports",10,20,120,35,clrGreen); CreateButton("SyncResistanceBtn","Sync Resistances",140,20,120,35,clrRed); CreateButton("ClearAllBtn","Clear All Lines",270,20,120,35,clrOrange); return(INIT_SUCCEEDED); } //+------------------------------------------------------------------+
Деинициализация советника – OnDeinit()
Функция OnDeinit() дополняет OnInit(), обеспечивая корректную очистку при удалении советника или закрытии графика. Все кнопки, созданные во время инициализации, удаляются, чтобы не оставлять осиротевших объектов на графике. Правильная деинициализация особенно важна в MQL5, поскольку оставшиеся объекты или массивы могут мешать другим советникам или загромождать график. Благодаря автоматизации этого этапа советник сохраняет целостность графика и позволяет избежать непреднамеренных ошибок и визуальных несоответствий.
//+------------------------------------------------------------------+ //| Expert deinitialization function | //| Cleans up chart objects | //+------------------------------------------------------------------+ void OnDeinit(const int reason) { //--- Remove UI buttons ObjectDelete(0,PREFIX+"SyncSupportBtn"); ObjectDelete(0,PREFIX+"SyncResistanceBtn"); ObjectDelete(0,PREFIX+"ClearAllBtn"); } //+------------------------------------------------------------------+
Создание интерфейса – CreateButton()
CreateButton() – основа интерактивного интерфейса. Функция динамически создает кнопки на графике с настраиваемыми свойствами, такими как положение, размер, шрифт, цвет фона и тип рамки. Каждая кнопка получает уникальное имя с помощью макроса PREFIX, что предотвращает конфликты имен с другими графическими объектами. Свойства OBJPROP_COLOR, OBJPROP_BGCOLOR, OBJPROP_FONTSIZE и OBJPROP_BORDER_TYPE настраиваются так, чтобы обеспечить наглядность и удобство использования. Эти кнопки позволяют пользователям синхронизировать уровни поддержки и сопротивления и управлять ими без ручного изменения массивов или структур, делая работу с системой более интуитивной и отзывчивой.
//+------------------------------------------------------------------+ //| Create a styled button on the chart | //+------------------------------------------------------------------+ void CreateButton(string btnName,string text,int x,int y,int w,int h,color bgColor) { string objName=PREFIX+btnName; ObjectCreate(0,objName,OBJ_BUTTON,0,0,0); ObjectSetInteger(0,objName,OBJPROP_XDISTANCE,x); ObjectSetInteger(0,objName,OBJPROP_YDISTANCE,y); ObjectSetInteger(0,objName,OBJPROP_XSIZE,w); ObjectSetInteger(0,objName,OBJPROP_YSIZE,h); ObjectSetInteger(0,objName,OBJPROP_CORNER,CORNER_LEFT_UPPER); ObjectSetInteger(0,objName,OBJPROP_FONTSIZE,11); ObjectSetString(0,objName,OBJPROP_FONT,"Segoe UI"); ObjectSetInteger(0,objName,OBJPROP_COLOR,clrWhite); ObjectSetInteger(0,objName,OBJPROP_BGCOLOR,bgColor); ObjectSetInteger(0,objName,OBJPROP_BORDER_COLOR,clrDarkGray); ObjectSetInteger(0,objName,OBJPROP_BORDER_TYPE,BORDER_RAISED); ObjectSetInteger(0,objName,OBJPROP_STATE,false); ObjectSetString(0,objName,OBJPROP_TEXT,text); ObjectSetInteger(0,objName,OBJPROP_SELECTABLE,false); ObjectSetInteger(0,objName,OBJPROP_HIDDEN,false); } //+------------------------------------------------------------------+
Обработка событий графика – OnChartEvent()
Функция OnChartEvent() отслеживает все важные события графика, включая нажатия кнопок и удаление объектов. При нажатии кнопок Sync Supports или Sync Resistances функция OnChartEvent() вызывает SyncAllLines() с соответствующим типом линии. Это гарантирует, что все новые вручную нанесенные горизонтальные линии будут включены в массив мониторинга. Если горизонтальная линия удаляется, обработчик удаляет ее запись из g_lines[] и удаляет соответствующую метку. Функция UpdateClearButtonState() также вызывается, чтобы динамически показывать наличие активных линий и давать трейдеру визуальное подтверждение состояния системы.
//+------------------------------------------------------------------+ //| Handle chart events | //| Button clicks and object deletion | //+------------------------------------------------------------------+ void OnChartEvent(const int id,const long &lparam,const double &dparam,const string &sparam) { //--- Button clicks if(id==CHARTEVENT_OBJECT_CLICK) { if(sparam==PREFIX+"SyncSupportBtn") SyncAllLines(TYPE_SUPPORT); else if(sparam==PREFIX+"SyncResistanceBtn") SyncAllLines(TYPE_RESISTANCE); else if(sparam==PREFIX+"ClearAllBtn") ClearAllLines(); } //--- Object deletion else if(id==CHARTEVENT_OBJECT_DELETE) { for(int i=0;i<ArraySize(g_lines);i++) { if(g_lines[i].name==sparam) { g_lines[i].exists=false; string labelName=sparam+"_label"; if(ObjectFind(0,labelName)>=0) ObjectDelete(0,labelName); break; } } //--- Compact array int newSize=0; for(int i=0;i<ArraySize(g_lines);i++) { if(g_lines[i].exists) { if(i!=newSize) g_lines[newSize]=g_lines[i]; newSize++; } } ArrayResize(g_lines,newSize); UpdateClearButtonState(); } } //+------------------------------------------------------------------+
Синхронизация линий – SyncAllLines()
Функция SyncAllLines() перебирает все графические объекты, выявляет горизонтальные линии (OBJ_HLINE) и регистрирует их для мониторинга, если они еще не отслеживаются. Функция использует перечисление ELineType, чтобы классифицировать линии как поддержку или сопротивление, и вызывает AddLineToMonitor() для каждой новой линии. Функция также выводит количество синхронизированных линий для отладки и прозрачности работы. Эта функция обеспечивает полную интеграцию ручного технического анализа с автоматическим мониторингом, поддерживая согласованность между ручным анализом трейдера и обратной связью системы.
//+------------------------------------------------------------------+ //| Synchronize horizontal lines | //+------------------------------------------------------------------+ void SyncAllLines(ELineType type) { int total=ObjectsTotal(0); int added=0; for(int i=0;i<total;i++) { string objName=ObjectName(0,i); ENUM_OBJECT objType=(ENUM_OBJECT)ObjectGetInteger(0,objName,OBJPROP_TYPE); if(objType==OBJ_HLINE) { double price=ObjectGetDouble(0,objName,OBJPROP_PRICE,0); if(price>0 && !IsLineMonitored(objName)) { AddLineToMonitor(objName,price,type); added++; } } } Print("Synced ",added," lines."); UpdateClearButtonState(); } //+------------------------------------------------------------------+
Проверка отслеживания линии – IsLineMonitored()
Перед добавлением линии функция IsLineMonitored() проверяет, есть ли эта линия в массиве g_lines[]. Это предотвращает дублирование и гарантирует, что советник отслеживает только уникальные уровни. Поддержание точного внутреннего представления активных линий критически важно для корректной работы системы, особенно когда несколько линий построены близко друг к другу или пользователи часто изменяют элементы графика.
//+------------------------------------------------------------------+ //| Check if line already monitored | //+------------------------------------------------------------------+ bool IsLineMonitored(string lineName) { for(int i=0;i<ArraySize(g_lines);i++) { if(g_lines[i].name==lineName) return(true); } return(false); } //+------------------------------------------------------------------+
Добавление линий – AddLineToMonitor()
В функции AddLineToMonitor() каждая горизонтальная линия инициализируется для активного мониторинга. Функция присваивает каждому уровню уникальный идентификатор и формирует для уровня структурированную метку. Линии поддержки получают метки вида SupX @ Price, а линии сопротивления – ResX @ Price, где X – увеличивающийся числовой идентификатор. Эта система меток обеспечивает уникальную идентификацию каждого уровня на протяжении всего процесса мониторинга и генерации алертов. Функция также задает цвет и толщину линии в зависимости от того, является ли уровень поддержкой или сопротивлением.
Помимо базовой инициализации, система присваивает каждой линии полный набор переменных состояния. В их число входят флаги отслеживания lastSide, prevValidSide, touched, breakoutAlerted и reversalAlerted. Эти переменные необходимы для поддержания непрерывности логики взаимодействия с ценой: они позволяют системе обнаруживать такие переходы, как пробои, подтверждать развороты и предотвращать дублирование алертов. Такая структурированная схема управления состоянием позволяет каждому уровню работать как независимая единица мониторинга.
//+------------------------------------------------------------------+ //| Add horizontal line to monitoring | //+------------------------------------------------------------------+ void AddLineToMonitor(string lineName,double price,ELineType type) { int sz=ArraySize(g_lines); ArrayResize(g_lines,sz+1); //--- Initialize g_lines[sz].name=lineName; g_lines[sz].price=price; g_lines[sz].type=type; g_lines[sz].exists=true; //--- Apply styling color lineColor=(type==TYPE_SUPPORT)?SupportColor:ResistanceColor; ObjectSetInteger(0,lineName,OBJPROP_COLOR,lineColor); ObjectSetInteger(0,lineName,OBJPROP_WIDTH,LineWidth); ObjectSetInteger(0,lineName,OBJPROP_STYLE,STYLE_SOLID); ObjectSetInteger(0,lineName,OBJPROP_RAY_RIGHT,true); } //+------------------------------------------------------------------+
Очистка отслеживаемых линий – ClearAllLines()
ClearAllLines() сбрасывает систему мониторинга – удаляет все метки и очищает массив g_lines[]. Счетчики поддержек и сопротивлений также сбрасываются, поэтому каждая новая синхронизация начинается с чистого состояния. Эта функция важна для гибкого управления рабочим процессом, поскольку позволяет трейдеру начать новую сессию анализа без остаточных данных от предыдущих линий, которые могли бы мешать мониторингу и алертам. Эта функция связана с UpdateClearButtonState() и динамически меняет цвет кнопки, показывая, есть ли на графике активные отслеживаемые уровни.
//+------------------------------------------------------------------+ //| Remove all monitored lines and reset counters | //+------------------------------------------------------------------+ void ClearAllLines(){ for(int i=ArraySize(g_lines)-1;i>=0;i--){ string name=g_lines[i].name; string labelName=name+"_label"; if(ObjectFind(0,labelName)>=0) ObjectDelete(0,labelName); } ArrayResize(g_lines,0); g_nextSupportId=1; g_nextResistanceId=1; UpdateClearButtonState(); Print("All monitored lines cleared."); } //+------------------------------------------------------------------+
Динамическое состояние кнопки – UpdateClearButtonState()
Функция UpdateClearButtonState() обеспечивает мгновенную визуальную обратную связь, меняя цвет кнопки Clear All Lines в зависимости от того, отслеживаются ли сейчас какие-либо линии. Эта небольшая, но важная функция повышает удобство работы: трейдер сразу видит, отслеживает ли система уровни, без необходимости проверки внутренних массивов или сообщений консоли. Такая визуальная обратная связь делает взаимодействие более интуитивным и улучшает общее впечатление от работы с системой.
//+------------------------------------------------------------------+ //| Update Clear All button color based on whether lines exist | //+------------------------------------------------------------------+ void UpdateClearButtonState(){ bool hasLines=(ArraySize(g_lines)>0); color bgColor=hasLines?clrOrange:clrGray; ObjectSetInteger(0,PREFIX+"ClearAllBtn",OBJPROP_BGCOLOR,bgColor); } //+------------------------------------------------------------------+
Логика размещения стрелок – CanPlaceArrow() и PlaceArrow()
Для управления сигналами используется CanPlaceArrow(), чтобы определить, можно ли разместить новую стрелку. При этом учитываются периоды ожидания и правило "одна стрелка на бар". Когда условия выполнены, PlaceArrow() вызывает DrawArrow() и отрисовывает стрелку на графике. Система использует единое цветовое кодирование и направленные символы, чтобы различать сигналы на покупку и продажу и поддерживать ясную визуальную логику. Вместе эти функции делают каждый алерт практически полезным и не допускают избыточных сигналов, помогая принимать точные решения без перегрузки графика.
//+------------------------------------------------------------------+ //| Check arrow conditions | //+------------------------------------------------------------------+ bool CanPlaceArrow(int idx,datetime barTime) { if(!PlaceBuySellArrows) return(false); if(ArrowCooldownSeconds>0 && TimeCurrent()-g_lines[idx].lastArrowTime<ArrowCooldownSeconds) return(false); if(ArrowOnlyOnePerBar && g_lines[idx].lastArrowBarTime==barTime) return(false); return(true); } //+------------------------------------------------------------------+ //+------------------------------------------------------------------+ //| Place arrow | //+------------------------------------------------------------------+ void PlaceArrow(int idx,datetime barTime,double price,bool isBuy) { if(!CanPlaceArrow(idx,barTime)) return; DrawArrow(barTime,price,isBuy); g_lines[idx].lastArrowTime=TimeCurrent(); g_lines[idx].lastArrowBarTime=barTime; } //+------------------------------------------------------------------+
Мониторинг цены – OnTick()
Функция OnTick() служит ядром системы мониторинга. Она непрерывно сопоставляет все зарегистрированные линии с текущей ценой Bid, проверяя несколько типов взаимодействий: приближение, касание, пробой, ретест и разворот. Допуски рассчитываются динамически, а переменные направления, такие как lastSide и prevValidSide, обновляются для отслеживания текущего движения. Флаги touched, breakoutAlerted и patternAlerted предотвращают повторные уведомления. Они также позволяют фиксировать значимую рыночную активность. Работая на каждом тике, советник превращает статические уровни поддержки и сопротивления в инструмент непрерывного интеллектуального мониторинга. Обнаружение пробоя строится на изменении положения цены относительно уровня.
Система отслеживает, с какой стороны уровня цена находилась ранее, и сравнивает это с ее текущим положением. Когда цена переходит с одной стороны уровня на другую, пробой считается подтвержденным. Направление этого пробоя определяется по последнему корректному положению цены до перехода, что позволяет системе классифицировать движение как бычье или медвежье. Затем это направление используется для запуска соответствующих сигналов, например для размещения стрелок и отслеживания ретеста. Обнаружение ретеста включается только после подтвержденного пробоя. Внутренний флаг состояния пробоя предотвращает ложные алерты о ретесте во время обычных колебаний цены.
Все проверки взаимодействия выполняются по цене Bid. В реальной торговле при интерпретации касаний и пробоев следует учитывать спред, особенно для инструментов с переменным спредом.
//+------------------------------------------------------------------+ //| Main monitoring loop - detects all price interactions | //+------------------------------------------------------------------+ void OnTick(){ double point = SymbolInfoDouble(_Symbol,SYMBOL_POINT); double touchTol = TouchTolerancePips*PipMultiplier*point; double approachTol = ApproachZonePips*PipMultiplier*point; double patternZone = PatternZonePips*PipMultiplier*point; datetime currentBarTime = iTime(_Symbol,PERIOD_CURRENT,0); for(int i=0;i<ArraySize(g_lines);i++){ if(!g_lines[i].exists) continue; double levelPrice = g_lines[i].price; double bid = SymbolInfoDouble(_Symbol,SYMBOL_BID); double distance = MathAbs(bid-levelPrice); int currentSide = (bid<levelPrice)?1:((bid>levelPrice)?-1:0); //--- Update previous valid side if(currentSide!=0) g_lines[i].prevValidSide = currentSide; //--- 1. Approach if(distance<=approachTol && !g_lines[i].approached){ SendAlert("Approaching "+g_lines[i].label,levelPrice); g_lines[i].approached = true; } //--- 2. Touch (new bar) if(g_lines[i].lastTouchBarTime<currentBarTime){ double barHigh = iHigh(_Symbol,PERIOD_CURRENT,0); double barLow = iLow(_Symbol,PERIOD_CURRENT,0); if(levelPrice>=barLow-touchTol && levelPrice<=barHigh+touchTol){ if(!g_lines[i].touched){ g_lines[i].sideBeforeTouch = g_lines[i].lastSide; SendAlert("Touch at "+g_lines[i].label,levelPrice); g_lines[i].touched = true; g_lines[i].breakoutAlerted = false; } } g_lines[i].lastTouchBarTime = currentBarTime; } //--- 3. Breakout int lastSide = g_lines[i].lastSide; if(lastSide!=0 && currentSide!=0 && currentSide!=lastSide && !g_lines[i].breakoutAlerted){ SendAlert("Breakout on "+g_lines[i].label,levelPrice); g_lines[i].breakoutAlerted = true; g_lines[i].breakoutHappened = true; int sideBefore = (g_lines[i].prevValidSide!=0)?g_lines[i].prevValidSide:lastSide; g_lines[i].breakoutDirection = (sideBefore==1 && currentSide==-1)?1:-1; if(ArrowOnBreakout){ bool isBuy = (g_lines[i].breakoutDirection==1); PlaceArrow(i,currentBarTime,levelPrice,isBuy); } } //--- 4. Reversal if(g_lines[i].touched && g_lines[i].sideBeforeTouch!=0 && !g_lines[i].reversalAlerted && currentSide==g_lines[i].sideBeforeTouch && !g_lines[i].breakoutAlerted){ SendAlert("Reversal on "+g_lines[i].label,levelPrice); g_lines[i].reversalAlerted = true; if(ArrowOnReversal){ bool isBuy = (g_lines[i].sideBeforeTouch==1); PlaceArrow(i,currentBarTime,levelPrice,isBuy); } } //--- 5. Retest if(DetectRetest && g_lines[i].breakoutHappened && !g_lines[i].retestAlerted && distance<=touchTol){ SendAlert("Retest of "+g_lines[i].label+" after breakout",levelPrice); g_lines[i].retestAlerted = true; g_lines[i].breakoutHappened = false; if(ArrowOnRetest && g_lines[i].breakoutDirection!=0){ bool isBuy = (g_lines[i].breakoutDirection==1); PlaceArrow(i,currentBarTime,levelPrice,isBuy); } } //--- 6. Candlestick patterns if(DetectReversalPatterns && !g_lines[i].patternAlerted){ bool priceInZone = (MathAbs(bid-levelPrice)<=patternZone) || (MathAbs(iClose(_Symbol,PERIOD_CURRENT,0)-levelPrice)<=patternZone); if(priceInZone){ string patternName = CheckReversalPatterns(); if(patternName!="" && g_lines[i].lastPatternAlertTime!=currentBarTime){ SendAlert(StringFormat("Pattern [%s] at %s",patternName,g_lines[i].label),levelPrice); g_lines[i].patternAlerted = true; g_lines[i].lastPatternAlertTime = currentBarTime; if(ArrowOnPattern){ bool bullish = (StringFind(patternName,"Bullish")>=0); PlaceArrow(i,currentBarTime,levelPrice,bullish); } } } } //--- Reset flags when price moves away if(distance>approachTol){ g_lines[i].approached = false; g_lines[i].patternAlerted = false; } g_lines[i].lastSide = currentSide; } } //+------------------------------------------------------------------+
Отрисовка стрелок – DrawArrow()
DrawArrow() отвечает за создание объектов-стрелок с точным размещением относительно максимумов и минимумов свечей. Каждой стрелке присваивается уникальное имя на основе текущего счетчика тиков (GetTickCount64()), что предотвращает конфликты имен. Цвета и символы соответствуют сигналам на покупку и продажу, поддерживая ясную и единообразную визуальную логику. Благодаря этой функции каждый пробой, ретест или разворот, обнаруженный в OnTick(), мгновенно отображается на графике, превращая его в наглядный ориентир по текущему рыночному движению.
//+------------------------------------------------------------------+ //| Draw arrow object | //+------------------------------------------------------------------+ void DrawArrow(datetime time,double price,bool isBuy) { string name=PREFIX+"Arrow_"+IntegerToString(GetTickCount64()); int code=isBuy?ArrowBuyCode:ArrowSellCode; color clr=isBuy?ArrowBuyColor:ArrowSellColor; if(ObjectCreate(0,name,OBJ_ARROW,0,time,price)) { ObjectSetInteger(0,name,OBJPROP_ARROWCODE,code); ObjectSetInteger(0,name,OBJPROP_COLOR,clr); ObjectSetInteger(0,name,OBJPROP_WIDTH,2); } } //+------------------------------------------------------------------+
Система алертов – SendAlert()
Механизм алертов, реализованный в SendAlert(), централизует все уведомления, включая всплывающие окна, звуковые сигналы и push-уведомления. Алерты содержат тип события и точный ценовой уровень, предоставляя подробную и практически полезную информацию по каждому взаимодействию. Такая последовательная и мгновенная обратная связь подчеркивает роль системы как интеллектуального помощника, сокращающего разрыв между анализом и исполнением.
//+------------------------------------------------------------------+ //| Send alert | //+------------------------------------------------------------------+ void SendAlert(string msg,double price) { string text=StringFormat("%s at %G",msg,price); if(AlertPopup) Alert(text); if(AlertSound) PlaySound(SoundFile); if(PushNotifications) SendNotification(text); Print(text); } //+------------------------------------------------------------------+
Обнаружение свечных паттернов – CheckReversalPatterns()
CheckReversalPatterns() анализирует завершенные бары на наличие разворотных свечных паттернов – поглощения, молота и падающей звезды. Функция вычисляет размер тела свечи, тени и взаимное расположение элементов, чтобы подтвердить паттерн перед отправкой алерта или отрисовкой стрелки. Интегрируя эти сигналы с мониторингом уровней поддержки и сопротивления, советник обеспечивает многоуровневое подтверждение: статические уровни сочетаются с рыночным контекстом, благодаря чему сигналы становятся более надежными.
//+------------------------------------------------------------------+ //| Detect common candlestick reversal patterns on completed bars | //+------------------------------------------------------------------+ string CheckReversalPatterns() { //--- Use only completed bars for(int shift = 1; shift <= 3; shift++) { double open = iOpen(_Symbol, PERIOD_CURRENT, shift); double high = iHigh(_Symbol, PERIOD_CURRENT, shift); double low = iLow(_Symbol, PERIOD_CURRENT, shift); double close = iClose(_Symbol, PERIOD_CURRENT, shift); if(open == 0 || high == 0 || low == 0 || close == 0) continue; //--- Candle structure double bodySize = MathAbs(close - open); double lowerShadow = (open < close) ? open - low : close - low; double upperShadow = (open < close) ? high - close : high - open; //--- Hammer / Shooting Star if(PatternHammer && close > open && lowerShadow >= 2 * bodySize && upperShadow <= bodySize * 0.3) return "Hammer (Bullish)"; if(PatternHammer && close < open && upperShadow >= 2 * bodySize && lowerShadow <= bodySize * 0.3) return "Shooting Star (Bearish)"; //--- Engulfing Patterns if(PatternEngulfing && shift >= 2) { double prevOpen = iOpen(_Symbol, PERIOD_CURRENT, shift + 1); double prevClose = iClose(_Symbol, PERIOD_CURRENT, shift + 1); if(close > open && prevClose < prevOpen && open <= prevClose && close >= prevOpen) return "Bullish Engulfing"; if(close < open && prevClose > prevOpen && open >= prevClose && close <= prevOpen) return "Bearish Engulfing"; } //--- Morning / Evening Star if(PatternStar && shift >= 3) { double prevOpen2 = iOpen(_Symbol, PERIOD_CURRENT, shift + 2); double prevClose2 = iClose(_Symbol, PERIOD_CURRENT, shift + 2); double prevOpen1 = iOpen(_Symbol, PERIOD_CURRENT, shift + 1); double prevClose1 = iClose(_Symbol, PERIOD_CURRENT, shift + 1); bool smallBody = MathAbs(prevClose1 - prevOpen1) <= (iHigh(_Symbol, PERIOD_CURRENT, shift + 1) - iLow(_Symbol, PERIOD_CURRENT, shift + 1)) * 0.3; bool morningStar = (prevClose2 < prevOpen2) && smallBody && (close > open) && (close > (prevOpen2 + prevClose2) / 2); if(morningStar) return "Morning Star (Bullish)"; bool eveningStar = (prevClose2 > prevOpen2) && smallBody && (close < open) && (close < (prevOpen2 + prevClose2) / 2); if(eveningStar) return "Evening Star (Bearish)"; } //--- Piercing / Dark Cloud Cover if(PatternPiercing && shift >= 2) { double prevOpen = iOpen(_Symbol, PERIOD_CURRENT, shift + 1); double prevClose = iClose(_Symbol, PERIOD_CURRENT, shift + 1); bool piercing = (prevClose < prevOpen) && (close > open) && (open < prevClose) && (close > (prevOpen + prevClose) / 2) && (close < prevOpen); if(piercing) return "Piercing Line (Bullish)"; bool darkCloud = (prevClose > prevOpen) && (close < open) && (open > prevClose) && (close < (prevOpen + prevClose) / 2) && (close > prevOpen); if(darkCloud) return "Dark Cloud Cover (Bearish)"; } } return ""; } //+------------------------------------------------------------------+
Теперь система работает как непрерывный слой мониторинга, который в реальном времени оценивает взаимодействие цены с каждым синхронизированным уровнем. Каждый компонент встроен в определенную последовательность работы: от инициализации и взаимодействия с пользователем до синхронизации уровней, оценки условий и генерации сигналов.
Основные функции – OnInit(), CreateButton(), SyncAllLines(), AddLineToMonitor(), OnTick(), DrawArrow(), SendAlert() и CheckReversalPatterns() – координируют эти процессы и обеспечивают активный мониторинг и интерпретацию уровней поддержки и сопротивления в рамках структурированной системы.
Полный исходный код советника SupportResistanceMonitor приведен во вложении. Чтобы использовать его, поместите файл в каталог Experts в MetaTrader 5, перезапустите платформу или обновите окно "Навигатор", затем скомпилируйте код в MetaEditor. После успешной компиляции советник можно прикрепить к любому графику.
В следующем разделе показано, как система работает на графике в режиме реального времени: синхронизация уровней, обнаружение взаимодействий и генерация алертов.
Тестирование и результаты
В этом разделе работа системы оценивается на графике в режиме реального времени, чтобы проверить ее функциональность и взаимодействие с уровнями, заданными вручную. Оценка разбита на две части: сначала с помощью пошаговой процедуры настройки проверяются синхронизация уровней и процесс взаимодействия, а затем анализируются результаты непрерывного мониторинга цены.
Процедура тестирования и проверка рабочего процесса
Тестирование начинается с прикрепления советника к активному графику. После этого интерфейс позволяет напрямую работать с уровнями поддержки и сопротивления.

Первый шаг рабочего процесса – вручную нанести на график линии поддержки и сопротивления. В этом подходе используются ценовые зоны, определяемые структурой рынка, а не автоматическими допущениями алгоритма. Это позволяет точно привязать уровни к видимым максимумам и минимумам свингов, сохраняя согласованность с принципами Price Action. После построения уровней процесс синхронизации запускается кнопками Sync Supports и Sync Resistances. Внутри системы эти функции считывают координаты построенных объектов и регистрируют их для мониторинга. На этом этапе система переходит из статической фазы настройки в активное состояние мониторинга.
После завершения синхронизации советник непрерывно отслеживает поведение цены относительно этих уровней. Дальнейшие ручные корректировки не требуются. Система оценивает каждый входящий тик и каждый завершенный бар, проверяя такие взаимодействия, как касания, отбои, свечные паттерны и пробои. GIF-анимация показывает весь этот цикл – от прикрепления советника и построения уровней до их синхронизации с системой и запуска непрерывного мониторинга. Это подтверждает, что рабочий процесс остается стабильным во время работы. Еще важнее то, что система сохраняет четкое разделение между структурой, заданной пользователем, и автоматическим анализом, а это критично для дисциплинированной торговли.
Наблюдаемое поведение системы и результаты
После проверки рабочего процесса следующий этап посвящен тому, как система ведет себя в условиях реального рынка. Изображение графика ниже ясно это показывает. Советник успешно регистрирует и отслеживает синхронизированные уровни в структурно значимых зонах, например сопротивление (Res1 @ 7956.7) и поддержку (Sup1 @ 7933.9). Эти уровни совпадают с видимыми реакциями цены, подтверждая, что логика синхронизации правильно интерпретирует уровни, заданные пользователем.

По мере приближения цены к уровню сопротивления система наблюдает за ситуацией, не генерируя преждевременных сигналов. Такое сдержанное поведение важно, потому что оно отсекает шум и гарантирует, что сигналы будут появляться только при выполнении значимых условий. Отбой у сопротивления сопровождается движением вниз, что показывает: уровень отрабатывается до того, как включается логика пробоя. По мере развития движения цена движется к зоне поддержки и в итоге пробивает ее вниз с сильным импульсом. Советник определяет это событие и фиксирует его в логе как пробой, подтверждая, что логика обнаружения пробоя работает как ожидается.
Проверка свечных паттернов и алертов
Дополнительным подтверждением точности работы системы служит окно алертов. Ключевой пример – обнаружение паттерна медвежьего поглощения на уровне поддержки (Sup2 @ 4348.68). Это показывает, что логика обнаружения свечных паттернов работает не изолированно, а с учетом структурного контекста.

Помимо выявления паттернов, система генерирует несколько типов алертов о пробое, например:
- пробой уровней поддержки;
- пробой уровней сопротивления.
Эти алерты снабжаются отметками времени и записываются в лог, формируя надежную историю событий. Повторяющиеся уведомления о пробое на одном и том же уровне в разное время подтверждают, что система продолжает динамически отслеживать уровни, а не трактует их как одноразовые события.

Визуальная обратная связь и интерпретация графика
На графике отображаются направленные стрелки и помеченные уровни поддержки и сопротивления, что позволяет интерпретировать взаимодействия цены непосредственно по графику. Стрелки указывают на обнаруженные события, а помеченные уровни дают четкие ориентиры для оценки положения цены. Это снижает зависимость от логов и сообщений алертов, поскольку ключевые взаимодействия визуально представлены прямо на графике. В результате мониторинг становится частью самого графика и поддерживает структурированный непрерывный анализ без необходимости постоянного ручного наблюдения.
Заключение
В этой статье мы разобрали ограничение, связанное с вручную построенными уровнями поддержки и сопротивления: для выявления таких взаимодействий, как приближение, касание, пробой и ретест, требуется постоянное наблюдение. Предложенное решение – советник с четкой структурой, который автоматизирует этот мониторинг, сохраняя ручной процесс определения уровней.
Эта реализация превращает модель в рабочую систему. OnChartEvent() фиксирует действия пользователя, SyncAllLines() и AddLineToMonitor() отвечают за синхронизацию уровней и отслеживание состояния, а OnTick() непрерывно оценивает взаимодействия цены с уровнями по заданным допускам. Обнаруженные события классифицируются и обрабатываются, а SendAlert() и DrawArrow() обеспечивают обратную связь в реальном времени через алерты и сигналы на графике.
В результате вручную заданные уровни превращаются в активно отслеживаемые опорные точки. Система оценивает только синхронизированные уровни и применяет единые правила обнаружения взаимодействий, что позволяет проводить структурированный и повторяемый анализ без постоянного наблюдения за графиком.
Итогом становится полноценный советник SupportResistanceMonitor, способный в реальном времени обнаруживать взаимодействия цены, генерировать алерты и отображать визуальные сигналы на основе вручную заданных уровней поддержки и сопротивления.
Перевод с английского произведен MetaQuotes Ltd.
Оригинальная статья: https://www.mql5.com/en/articles/21961
Предупреждение: все права на данные материалы принадлежат MetaQuotes Ltd. Полная или частичная перепечатка запрещена.
Данная статья написана пользователем сайта и отражает его личную точку зрения. Компания MetaQuotes Ltd не несет ответственности за достоверность представленной информации, а также за возможные последствия использования описанных решений, стратегий или рекомендаций.
Автоматизация торговых стратегий с помощью MQL5 (Часть 48): Блоки ордеров, провокация ликвидности, пробой структуры
Рыночные секреты Ларри Уильямса (Часть 13): Автоматизация разворотных паттернов Hidden Smash Day
Рыночные секреты Ларри Уильямса (Часть 14): Обнаружение разворотов Hidden Smash Day с помощью пользовательского индикатора
Торговые инструменты на MQL5 (Часть 30): Боковая панель палитры инструментов с архитектурой на основе классов
- Бесплатные приложения для трейдинга
- 8 000+ сигналов для копирования
- Экономические новости для анализа финансовых рынков
Вы принимаете политику сайта и условия использования