Разработка пробойной торговой системы на основе волатильности
Введение
Многие традиционные пробойные системы сталкиваются с такими проблемами, как частые ложные сигналы, когда цена на короткое время пробивает уровень поддержки или сопротивления, а затем разворачивается обратно в существующий диапазон. Эти ложные пробои часто происходят в условиях низкой волатильности или в периоды рыночного шума, что приводит к срабатыванию стоп-лоссов, снижению прибыльности и разочарованию трейдеров. Без учета меняющихся рыночных условий статичные пробойные стратегии могут испытывать трудности с адаптацией, что делает их ненадежными на разных этапах движения рынка.
Пробойная система, основанная на волатильности, решает эту проблему с помощью фильтров волатильности, таких как средний истинный диапазон (ATR, Average True Range), для измерения силы рынка и динамической корректировки условий пробоя. Требуя, чтобы цена вышла за пределы как диапазона колебаний, так и порога волатильности, система помогает отличать подлинные пробои от рыночного шума. Это обеспечивает заключение сделок только при наличии достаточного импульса, что повышает точность, улучшает управление рисками и укрепляет общую стабильность результатов.
Планирование и логика
Пробой на покупку при необъяснимой волатильности:

Пробой на покупку при объяснимой волатильности:

Пробой на продажу при необъяснимой волатильности:

Пробой на продажу при объяснимой волатильности:




Логика принятия решений при пробоях

Начало работы
//+------------------------------------------------------------------+ //| Volatility Breakout.mq5 | //| GIT under Copyright 2025, MetaQuotes Ltd. | //| https://www.mql5.com/en/users/johnhlomohang/ | //+------------------------------------------------------------------+ #property copyright "GIT under Copyright 2025, MetaQuotes Ltd." #property link "https://www.mql5.com/en/users/johnhlomohang/" #property version "1.00" #include <Trade\Trade.mqh> #include <Trade\PositionInfo.mqh>
Как обычно, начнём с включения необходимых для торговли библиотек на языке MQL5: #include <Trade\Trade.mqh> и #include <Trade\PositionInfo.mqh>. Первая из них предоставляет доступ к классу CTrade, который позволяет советнику исполнять, изменять и закрывать торговые ордера, а вторая — к классу CPositionInfo, позволяющему советнику получать подробную информацию об активных позициях, такую как данные о типе ордера, размере лота, уровнях стоп-лосса и тейк-профита.
//+------------------------------------------------------------------+ //| Input Parameters | //+------------------------------------------------------------------+ input group "--------------- Range Settings ---------------" input int RangeStartTime = 600; // Range start time (minutes from midnight) input int RangeDuration = 120; // Range duration in minutes input int RangeCloseTime = 1200; // Range close time (minutes from midnight) input group "--------------- Trading Settings ---------------" input double RiskPerTrade = 1.0; // Risk percentage per trade input double StopLossMultiplier = 1.5; // Stop loss multiplier (range-based) input double TakeProfitMultiplier = 2.0; // Take profit multiplier (range-based) input bool UseTrailingStop = true; // Enable trailing stops input int BreakEvenAtPips = 250; // Move to breakeven at this profit (pips) input int TrailStartAtPips = 500; // Start trailing at this profit (pips) input int TrailStepPips = 100; // Trail by this many pips input group "--------------- Volatility Settings ---------------" input int ATRPeriod = 14; // ATR period for volatility calculation input double ATRMultiplier = 2.0; // ATR multiplier for volatility stops
Здесь мы определяем входные параметры, которые трейдер может настроить при использовании советника. Первая группа параметров, «Настройки диапазона» (Range Settings), задает временной интервал для формирования ценового диапазона: RangeStartTime указывает, когда начинается диапазон (в минутах от полуночи), RangeDuration определяет продолжительность диапазона, а RangeCloseTime определяет, когда сессия будет закрыта. Эти настройки позволяют советнику выстраивать логику пробоя в зависимости от конкретных торговых часов, что особенно полезно для установления целей активных торговых сессий.
Вторая и третья группы настраивают правила торговли и волатильности системы. Настройки торговли позволяют контролировать управление рисками и торговлей, например, параметром RiskPerTrade для определения размера позиции, множителями для стоп-лосса и тейк-профита в зависимости от размера диапазона, а также логикой трейлинг-стопа (BreakEvenAtPips, TrailStartAtPips и TrailStepPips). Между тем, в настройках волатильности используется индикатор ATR с заданными пользователем значениями ATRPeriod и ATRMultiplier для расчета стоп-уровней на основе волатильности. Это обеспечивает динамическую адаптацию пробойных сделок к меняющимся рыночным условиям, снижая вероятность ложных сигналов.
//+------------------------------------------------------------------+ //| Global Variables | //+------------------------------------------------------------------+ long ExpertMagicNumber = 20250908; CTrade TradeManager; CPositionInfo PositionInfo; MqlTick CurrentTick; // Range structure struct TradingSession { datetime SessionStart; datetime SessionEnd; datetime SessionClose; double SessionHigh; double SessionLow; bool IsActive; bool HighBreakoutTriggered; bool LowBreakoutTriggered; datetime LastCalculationDate; TradingSession() : SessionStart(0), SessionEnd(0), SessionClose(0), SessionHigh(0), SessionLow(DBL_MAX), IsActive(false), HighBreakoutTriggered(false), LowBreakoutTriggered(false), LastCalculationDate(0) {}; }; TradingSession CurrentSession; // Volatility stops double UpperVolatilityStop = 0.0; double LowerVolatilityStop = 0.0; int ATRHandle = INVALID_HANDLE;
В этом разделе кода объявляются глобальные переменные, которые будут использоваться везде в советнике. Уникальное магическое число советника (ExpertMagicNumber) позволяет идентифицировать сделки, открытые этим советником, чтобы их можно было отличить от сделок, совершенных вручную, или сделок, открытых другими советниками. Объект CTrade TradeManager отвечает за исполнение сделок и управление ими, а объект CPositionInfo PositionInfo получает подробную информацию об открытых позициях. Переменная MqlTick CurrentTick содержит самую последнюю котировку цены (цена покупки, цена продажи и время), гарантируя, что торговая логика всегда будет основана на данных рынка в режиме реального времени.
Структура торговой сессии систематизирует такую ключевую информацию, относящуюся к сессии, как время ее начала и окончания, максимальные и минимальные цены за сессию, а также отметки, позволяющие отслеживать, произошел ли уже пробой. Экземпляр этой структуры, CurrentSession, объявляется для хранения данных о торговой сессии за текущий торговый день. Кроме того, настроены переменные для управления волатильностью: UpperVolatilityStop и LowerVolatilityStop устанавливают динамические пороговые значения на основе ATR, а ATRHandle хранит дескриптор индикатора, который необходим для доступа к значениям ATR. В совокупности эти переменные обеспечивают основу для обнаружения пробоев и исполнения сделок.
//+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+ int OnInit() { // Validate input parameters if(RiskPerTrade <= 0 || RiskPerTrade > 5) { Alert("Risk per trade must be between 0.1 and 5.0"); return INIT_PARAMETERS_INCORRECT; } // Initialize ATR indicator ATRHandle = iATR(_Symbol, _Period, ATRPeriod); if(ATRHandle == INVALID_HANDLE) { Alert("Failed to create ATR indicator"); return INIT_FAILED; } TradeManager.SetExpertMagicNumber(ExpertMagicNumber); // Calculate initial session CalculateTradingSession(); return INIT_SUCCEEDED; }
Функция OnInit() исполняется при запуске советника и подготавливает систему к торговле. Во-первых, она проверяет входные параметры, чтобы убедиться, что уровень риска находится в допустимом диапазоне; в противном случае советник остановится и выдаст ошибку. Затем она инициализирует индикатор ATR, создавая хэндл для расчетов волатильности, и если это не удается, советник завершает работу. Далее, TradeManager получает уникальный ExpertMagicNumber, чтобы сделки можно было корректно отслеживать, и, наконец, советник вызывает CalculateTradingSession() для настройки исходной торговой сессии, после чего возвращает статус успешного завершения.
//+------------------------------------------------------------------+ //| Expert deinitialization function | //+------------------------------------------------------------------+ void OnDeinit(const int reason) { // Clean up indicators if(ATRHandle != INVALID_HANDLE) { IndicatorRelease(ATRHandle); } // Delete all graphical objects ObjectsDeleteAll(0, -1, -1); }
Функция OnDeinit() запускается при удалении или остановке советника. Эта функция выпускает индикатор ATR, чтобы освободить ресурсы, и удаляет с графика все графические объекты, чтобы исключить появление лишних отрисовок.
//+------------------------------------------------------------------+ //| Check if we need to calculate a new session for the day | //+------------------------------------------------------------------+ void CheckForNewSession() { MqlDateTime currentTime; TimeToStruct(CurrentTick.time, currentTime); MqlDateTime lastCalcTime; TimeToStruct(CurrentSession.LastCalculationDate, lastCalcTime); // Check if we're in a new day or if session hasn't been calculated yet if (currentTime.day != lastCalcTime.day || currentTime.mon != lastCalcTime.mon || currentTime.year != lastCalcTime.year || CurrentSession.LastCalculationDate == 0) { CalculateTradingSession(); } }
Функция CheckForNewSession() гарантирует, что новая торговая сессия будет рассчитываться в начале каждого нового дня. Она преобразует текущее время тика и время последнего расчета сессии в структуры дат, а затем сравнивает день, месяц и год. Если текущая дата отличается от даты последней зарегистрированной торговой сессии или если торговая сессия еще не была начата, вызывается функция CalculateTradingSession() для сброса и подготовки новой торговой сессии.
//+------------------------------------------------------------------+ //| Calculate trading session for the day | //+------------------------------------------------------------------+ void CalculateTradingSession() { // Reset session variables CurrentSession.SessionStart = 0; CurrentSession.SessionEnd = 0; CurrentSession.SessionClose = 0; CurrentSession.SessionHigh = 0; CurrentSession.SessionLow = DBL_MAX; CurrentSession.IsActive = false; CurrentSession.HighBreakoutTriggered = false; CurrentSession.LowBreakoutTriggered = false; // Calculate session times int timeCycle = 86400; // Seconds in a day CurrentSession.SessionStart = (CurrentTick.time - (CurrentTick.time % timeCycle)) + RangeStartTime * 60; // Adjust for weekends for(int i = 0; i < 8; i++) { MqlDateTime tmp; TimeToStruct(CurrentSession.SessionStart, tmp); int dayOfWeek = tmp.day_of_week; if(CurrentTick.time >= CurrentSession.SessionStart || dayOfWeek == 0 || dayOfWeek == 6) { CurrentSession.SessionStart += timeCycle; } } // Calculate session end time CurrentSession.SessionEnd = CurrentSession.SessionStart + (RangeDuration * 60); for(int i = 0; i < 2; i++) { MqlDateTime tmp; TimeToStruct(CurrentSession.SessionEnd, tmp); int dayOfWeek = tmp.day_of_week; if(dayOfWeek == 0 || dayOfWeek == 6) { CurrentSession.SessionEnd += timeCycle; } } // Calculate session close time CurrentSession.SessionClose = (CurrentSession.SessionEnd - (CurrentSession.SessionEnd % timeCycle)) + RangeCloseTime * 60; for(int i = 0; i < 3; i++) { MqlDateTime tmp; TimeToStruct(CurrentSession.SessionClose, tmp); int dayOfWeek = tmp.day_of_week; if(CurrentSession.SessionClose <= CurrentSession.SessionEnd || dayOfWeek == 0 || dayOfWeek == 6) { CurrentSession.SessionClose += timeCycle; } } // Set last calculation date CurrentSession.LastCalculationDate = CurrentTick.time; // Draw session objects DrawSessionObjects(); }
Функция CalculateTradingSession() настраивает дневную торговую сессию, сбрасывая все связанные с сессией переменные и рассчитывая правильное время начала, окончания и закрытия на основе введенных пользователем данных. Она обеспечивает соответствие торговых сессий суточным циклам, пропуская выходные дни, поэтому торговля будет осуществляться только в рабочие дни рынка. Эта функция также сбрасывает флаги пробоя, обновляет дату расчета сессии и вызывает функцию DrawSessionObjects() для визуального отображения границ и уровней сессии на графике.
//+------------------------------------------------------------------+ //| Update trading session with current price data | //+------------------------------------------------------------------+ void UpdateTradingSession() { if(CurrentTick.time >= CurrentSession.SessionStart && CurrentTick.time < CurrentSession.SessionEnd) { CurrentSession.IsActive = true; // Update session high if(CurrentTick.ask > CurrentSession.SessionHigh) { CurrentSession.SessionHigh = CurrentTick.ask; } // Update session low if(CurrentTick.bid < CurrentSession.SessionLow) { CurrentSession.SessionLow = CurrentTick.bid; } // Draw session on chart DrawSessionObjects(); } }
Функция UpdateTradingSession() непрерывно обновляет данные текущей сессии по мере поступления новых ценовых тиков. Она активирует сессию, когда текущее время находится в пределах периода сессии, а затем отслеживает максимальную цену предложения и минимальную цену покупки для обновления максимальной и минимальной цен сессии. Наконец, она вызывает метод DrawSessionObjects(), чтобы визуально отобразить эти обновленные уровни на графике.
//+------------------------------------------------------------------+ //| Calculate volatility stops using ATR | //+------------------------------------------------------------------+ void CalculateVolatilityStops() { double atrValue[1]; if(CopyBuffer(ATRHandle, 0, 0, 1, atrValue) <= 0) { Print("Failed to get ATR value"); return; } // Validate ATR value to prevent "inf" errors if(atrValue[0] <= 0 || !MathIsValidNumber(atrValue[0]) || atrValue[0] > 1000) { Print("Invalid ATR value: ", atrValue[0], ", using default"); atrValue[0] = 10 * SymbolInfoDouble(_Symbol, SYMBOL_POINT); } // Calculate volatility stops UpperVolatilityStop = CurrentSession.SessionHigh + (atrValue[0] * ATRMultiplier); LowerVolatilityStop = CurrentSession.SessionLow - (atrValue[0] * ATRMultiplier); // Draw volatility stops on chart DrawVolatilityStops(); }
Функция CalculateVolatilityStops() вычисляет динамические верхний и нижний стоп-уровни на основе волатильности рынка с использованием индикатора ATR. Сначала она получает последнее значение ATR, проверяет его во избежание ошибок и при необходимости устанавливает безопасное значение по умолчанию. Затем функция вычисляет верхний и нижний уровни волатильности, прибавляя или вычитая значения ATR (масштабированные с помощью ATRMultiplier) из максимальной и минимальной цен торговой сессии, и вызывает функцию DrawVolatilityStops() для отображения этих уровней на графике.
//+------------------------------------------------------------------+ //| Check for breakouts and execute trades | //+------------------------------------------------------------------+ void CheckForBreakouts() { // Only check after session formation period if(CurrentTick.time < CurrentSession.SessionEnd || !CurrentSession.IsActive) { return; } // Check for high breakout if(!CurrentSession.HighBreakoutTriggered && CurrentTick.ask >= CurrentSession.SessionHigh) { CurrentSession.HighBreakoutTriggered = true; // Check if price has cleared volatility stop if(CurrentTick.ask >= UpperVolatilityStop) { // Regular breakout - execute buy ExecuteTrade(ORDER_TYPE_BUY, "Session High Breakout"); } else { // False breakout - execute sell (fade) ExecuteTrade(ORDER_TYPE_SELL, "False Breakout - Fade High"); } } // Check for low breakout if(!CurrentSession.LowBreakoutTriggered && CurrentTick.bid <= CurrentSession.SessionLow) { CurrentSession.LowBreakoutTriggered = true; // Check if price has cleared volatility stop if(CurrentTick.bid <= LowerVolatilityStop) { // Regular breakout - execute sell ExecuteTrade(ORDER_TYPE_SELL, "Session Low Breakout"); } else { // False breakout - execute buy (fade) ExecuteTrade(ORDER_TYPE_BUY, "False Breakout - Fade Low"); } } }
Здесь функция CheckForBreakouts() отслеживает движение цены, чтобы выявить потенциальные возможности для пробоя после того, как торговая сессия сформировалась и стала активной. Он проверяет, пересекает ли цена максимум или минимум сессии, и гарантирует, что каждый пробой срабатывает только один раз за сессию. Сделки исполняются в зависимости от того, превышает ли цена соответствующий стоп-уровень по волатильности: подтвержденный пробой запускает сделку в направлении этого пробоя, а ложный пробой, когда цена не преодолевает порог волатильности, запускает сделку на снижение в противоположном направлении.
//+------------------------------------------------------------------+ //| Execute trade with proper risk management | //+------------------------------------------------------------------+ void ExecuteTrade(ENUM_ORDER_TYPE orderType, string comment) { // Calculate position size based on risk double lotSize = CalculatePositionSize(); if(lotSize <= 0) { Print("Failed to calculate position size"); return; } // Calculate stop loss and take profit double stopLoss = 0.0; double takeProfit = 0.0; CalculateStopLevels(orderType, stopLoss, takeProfit); // Validate stop levels if(!ValidateStopLevels(orderType, stopLoss, takeProfit)) { Print("Invalid stop levels - trade not executed"); return; } // Execute trade if(orderType == ORDER_TYPE_BUY) { TradeManager.Buy(lotSize, _Symbol, CurrentTick.ask, stopLoss, takeProfit, comment); } else { TradeManager.Sell(lotSize, _Symbol, CurrentTick.bid, stopLoss, takeProfit, comment); } // Check result if(TradeManager.ResultRetcode() != TRADE_RETCODE_DONE) { Print("Trade execution failed: ", TradeManager.ResultRetcodeDescription()); } }
Функция ExecuteTrade() отвечает за открытие сделки с надлежащим управлением рисками. Сначала она рассчитывает размер позиции с помощью функции CalculatePositionSize(), затем определяет соответствующие уровни стоп-лосса и тейк-профита с помощью функции CalculateStopLevels(). После проверки этих уровней с помощью функции ValidateStopLevels(), функция исполняет ордер на покупку или продажу, используя объект TradeManager, и регистрирует сообщение об ошибке, если сделка не удалась.
//+------------------------------------------------------------------+ //| Calculate position size based on risk percentage | //+------------------------------------------------------------------+ double CalculatePositionSize() { double accountBalance = AccountInfoDouble(ACCOUNT_BALANCE); double tickSize = SymbolInfoDouble(_Symbol, SYMBOL_TRADE_TICK_SIZE); double tickValue = SymbolInfoDouble(_Symbol, SYMBOL_TRADE_TICK_VALUE); double point = SymbolInfoDouble(_Symbol, SYMBOL_POINT); if(accountBalance <= 0 || tickSize <= 0 || tickValue <= 0 || point <= 0) { return 0.0; } // Get ATR value for stop loss distance double atrValue[1]; if(CopyBuffer(ATRHandle, 0, 0, 1, atrValue) <= 0) { return 0.0; } // Validate ATR value if(atrValue[0] <= 0 || !MathIsValidNumber(atrValue[0])) { return 0.0; } // Calculate stop distance in points double stopDistance = atrValue[0] * StopLossMultiplier / point; // Calculate risk amount double riskAmount = accountBalance * (RiskPerTrade / 100.0); // Calculate position size double lotStep = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_STEP); double lotSize = (riskAmount / (stopDistance * tickValue)) * tickSize; // Normalize lot size lotSize = MathFloor(lotSize / lotStep) * lotStep; // Apply min/max limits double minLot = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MIN); double maxLot = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MAX); lotSize = MathMax(minLot, MathMin(maxLot, lotSize)); return lotSize; }
Эта функция определяет объем сделок в соответствии с остатками средств на счете, заданным пользователем процентом риска и текущими рыночными условиями. Она извлекает относящиеся к конкретному символу данные, такие как размер тика, значение тика и размер точки, а затем вычисляет расстояние до стоп-уровня, используя индикатор ATR, масштабированный с помощью StopLossMultiplier. Используя рассчитанный уровень риска, функция вычисляет размер лота, нормализует его в соответствии с разрешенным брокером шагом и проверяет, соответствует ли он минимальным и максимальным ограничениям по объему, прежде чем вернуть окончательный размер позиции.
//+------------------------------------------------------------------+ //| Calculate stop loss and take profit levels | //+------------------------------------------------------------------+ void CalculateStopLevels(ENUM_ORDER_TYPE orderType, double &stopLoss, double &takeProfit) { // Use range-based stops double rangeSize = CurrentSession.SessionHigh - CurrentSession.SessionLow; if(orderType == ORDER_TYPE_BUY) { stopLoss = CurrentTick.bid - (rangeSize * StopLossMultiplier / 100.0); takeProfit = CurrentTick.ask + (rangeSize * TakeProfitMultiplier / 100.0); } else { stopLoss = CurrentTick.ask + (rangeSize * StopLossMultiplier / 100.0); takeProfit = CurrentTick.bid - (rangeSize * TakeProfitMultiplier / 100.0); } // Normalize prices stopLoss = NormalizeDouble(stopLoss, _Digits); takeProfit = NormalizeDouble(takeProfit, _Digits); } //+------------------------------------------------------------------+ //| Validate stop loss and take profit levels | //+------------------------------------------------------------------+ bool ValidateStopLevels(ENUM_ORDER_TYPE orderType, double stopLoss, double takeProfit) { // Check if stop levels are valid double point = SymbolInfoDouble(_Symbol, SYMBOL_POINT); double minStopDistance = SymbolInfoInteger(_Symbol, SYMBOL_TRADE_STOPS_LEVEL) * point; if(orderType == ORDER_TYPE_BUY) { if(CurrentTick.ask - stopLoss < minStopDistance) { Print("Stop loss too close for buy order"); return false; } if(takeProfit - CurrentTick.ask < minStopDistance) { Print("Take profit too close for buy order"); return false; } } else { if(stopLoss - CurrentTick.bid < minStopDistance) { Print("Stop loss too close for sell order"); return false; } if(CurrentTick.bid - takeProfit < minStopDistance) { Print("Take profit too close for sell order"); return false; } } return true; }
Функция CalculateStopLevels() определяет уровни стоп-лосса и тейк-профита для сделки на основе диапазона текущей торговой сессии. Для ордеров на покупку стоп-лосс устанавливается ниже текущей цены bid, а тейк-профит — выше текущей цены ask, масштабированных заданными пользователем множителями; для ордеров на продажу логика обратная. Оба уровня нормализованы с учетом десятичной точности инструмента для обеспечения точности исполнения.
Функция ValidateStopLevels() обеспечивает соответствие рассчитанных уровней стоп-лосса и тейк-профита минимальным требованиям брокера к минимальному расстоянию до стоп-уровней. Она проверяет, превышает ли расстояние между ценой входа и стоп-уровнями значение SYMBOL_TRADE_STOPS_LEVEL, умноженное на размер точки, что предотвращает недействительные ордера или ордера со слишком узким диапазоном. Если стоп-уровни расположены слишком близко, функция возвращает false и выводит сообщение об ошибке, гарантируя исполнение только действительных сделок.
//+------------------------------------------------------------------+ //| Manage trailing stops for open positions | //+------------------------------------------------------------------+ void ManageTrailingStops() { for(int i = PositionsTotal() - 1; i >= 0; i--) { ulong ticket = PositionGetTicket(i); if(PositionSelectByTicket(ticket) && PositionGetString(POSITION_SYMBOL) == _Symbol && PositionGetInteger(POSITION_MAGIC) == ExpertMagicNumber) { ENUM_POSITION_TYPE positionType = (ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE); double openPrice = PositionGetDouble(POSITION_PRICE_OPEN); double currentStop = PositionGetDouble(POSITION_SL); double currentProfit = PositionGetDouble(POSITION_PROFIT); double point = SymbolInfoDouble(_Symbol, SYMBOL_POINT); // Calculate profit in pips double profitInPips = 0; if(positionType == POSITION_TYPE_BUY) { profitInPips = (CurrentTick.bid - openPrice) / point; } else { profitInPips = (openPrice - CurrentTick.ask) / point; } // Check if we should move to breakeven if(profitInPips >= BreakEvenAtPips && currentStop != openPrice) { if(positionType == POSITION_TYPE_BUY) { TradeManager.PositionModify(ticket, openPrice, PositionGetDouble(POSITION_TP)); } else { TradeManager.PositionModify(ticket, openPrice, PositionGetDouble(POSITION_TP)); } } // Check if we should start trailing if(profitInPips >= TrailStartAtPips) { double newStop = 0; if(positionType == POSITION_TYPE_BUY) { newStop = CurrentTick.bid - (TrailStepPips * point); // Only move stop if it's higher than current if(newStop > currentStop || currentStop == 0) { TradeManager.PositionModify(ticket, newStop, PositionGetDouble(POSITION_TP)); } } else { newStop = CurrentTick.ask + (TrailStepPips * point); // Only move stop if it's lower than current if(newStop < currentStop || currentStop == 0) { TradeManager.PositionModify(ticket, newStop, PositionGetDouble(POSITION_TP)); } } } } } } //+------------------------------------------------------------------+ //| Draw session objects on chart | //+------------------------------------------------------------------+ void DrawSessionObjects() { // Draw session high ObjectDelete(0, "SessionHigh"); ObjectCreate(0, "SessionHigh", OBJ_HLINE, 0, 0, CurrentSession.SessionHigh); ObjectSetInteger(0, "SessionHigh", OBJPROP_COLOR, clrBlue); ObjectSetInteger(0, "SessionHigh", OBJPROP_STYLE, STYLE_DASH); ObjectSetInteger(0, "SessionHigh", OBJPROP_WIDTH, 2); // Draw session low ObjectDelete(0, "SessionLow"); ObjectCreate(0, "SessionLow", OBJ_HLINE, 0, 0, CurrentSession.SessionLow); ObjectSetInteger(0, "SessionLow", OBJPROP_COLOR, clrBlue); ObjectSetInteger(0, "SessionLow", OBJPROP_STYLE, STYLE_DASH); ObjectSetInteger(0, "SessionLow", OBJPROP_WIDTH, 2); // Draw session start time ObjectDelete(0, "SessionStart"); ObjectCreate(0, "SessionStart", OBJ_VLINE, 0, CurrentSession.SessionStart, 0); ObjectSetInteger(0, "SessionStart", OBJPROP_COLOR, clrBlue); ObjectSetInteger(0, "SessionStart", OBJPROP_WIDTH, 2); // Draw session end time ObjectDelete(0, "SessionEnd"); ObjectCreate(0, "SessionEnd", OBJ_VLINE, 0, CurrentSession.SessionEnd, 0); ObjectSetInteger(0, "SessionEnd", OBJPROP_COLOR, clrRed); ObjectSetInteger(0, "SessionEnd", OBJPROP_WIDTH, 2); // Draw session close time ObjectDelete(0, "SessionClose"); ObjectCreate(0, "SessionClose", OBJ_VLINE, 0, CurrentSession.SessionClose, 0); ObjectSetInteger(0, "SessionClose", OBJPROP_COLOR, clrDarkRed); ObjectSetInteger(0, "SessionClose", OBJPROP_WIDTH, 2); } //+------------------------------------------------------------------+ //| Draw volatility stops on chart | //+------------------------------------------------------------------+ void DrawVolatilityStops() { // Draw upper volatility stop ObjectDelete(0, "VolatilityStopUpper"); ObjectCreate(0, "VolatilityStopUpper", OBJ_HLINE, 0, 0, UpperVolatilityStop); ObjectSetInteger(0, "VolatilityStopUpper", OBJPROP_COLOR, clrGreen); ObjectSetInteger(0, "VolatilityStopUpper", OBJPROP_STYLE, STYLE_DOT); ObjectSetInteger(0, "VolatilityStopUpper", OBJPROP_WIDTH, 1); // Draw lower volatility stop ObjectDelete(0, "VolatilityStopLower"); ObjectCreate(0, "VolatilityStopLower", OBJ_HLINE, 0, 0, LowerVolatilityStop); ObjectSetInteger(0, "VolatilityStopLower", OBJPROP_COLOR, clrRed); ObjectSetInteger(0, "VolatilityStopLower", OBJPROP_STYLE, STYLE_DOT); ObjectSetInteger(0, "VolatilityStopLower", OBJPROP_WIDTH, 1); }
Функция ManageTrailingStops() обеспечивает динамическое управление активными сделками по мере их перехода в стадию получения прибыли. Она перебирает все позиции, фильтрует их по символу и магическому числу, отбирая позиции, принадлежащие советнику, а затем рассчитывает прибыль в пипсах. В зависимости от условий, она может перемещать стоп-лосс в точку безубыточности после достижения определенного порога прибыли, или применить трейлинг-стоп для фиксации прибыли по мере дальнейшего развития сделки в пользу трейдера. Это помогает снизить риски и защитить прибыль без необходимости вмешиваться, действуя вручную.
Функция DrawSessionObjects() визуально отображает на графике текущую торговую сессию. Она создает горизонтальные линии для максимальной и минимальной цен сессии, а также вертикальные линии для начала, конца и завершения сессии, причем каждая из этих линий имеет свой собственный цвет и стиль. Это позволяет трейдерам легко визуально подтверждать непосредственно на графике границы торговых сессий и ключевые уровни.
Наконец, функция DrawVolatilityStops() отображает на графике рассчитанные стоп-уровни, основанные на волатильности. Зеленая пунктирная линия обозначает верхний стоп-уровень волатильности, а красная пунктирная линия — нижний. Эти линии обеспечивают четкие визуальные ориентиры для подтверждения пробоя и обнаружения ложных пробоев, улучшая процесс принятия решений за счет согласования ценового движения с динамикой волатильности.
Результаты тестирования на истории
Тестирование на истории оценивалось на таймфрейме 1H на протяжении примерно 2-месячного периода тестирования (с 7 июля 2025 года по 8 сентября 2025 года) со следующими настройками:



Заключение
Подытоживая все вышеописанное: мы разработали пробойную систему на основе волатильности, объединив определение диапазона торговой сессии, анализ волатильности с использованием ATR и структурированное управление сделками. Система начинает работу с определения ежедневных торговых сессий, отслеживания максимумов и минимумов сессий, а также расчета стоп-уровней волатильности для различения подлинных и ложных пробоев. Затем система включает в себя логику обнаружения пробоев, совершает сделки с динамическим размещением стоп-лосса и тейк-профита, а также применяет надежное управление рисками посредством определения размера позиции, корректировки точки безубыточности и использования трейлинг-стопов. Кроме того, для обеспечения наглядности торговых условий непосредственно на графике были интегрированы такие визуальные элементы, как маркеры сессий и стоп-линии волатильности.
В заключение надо отметить, что эта система помогает трейдерам избежать распространенных ошибок традиционных пробойных стратегий, таких как открытие сделок на ложных движениях без учета волатильности рынка. Используя стоп-уровни волатильности на основе ATR, она обеспечивает соответствие сделок в большей степени реальной рыночной динамике, чем произвольным уровням. Добавление автоматизированных средств контроля рисков и визуализации графиков еще больше совершенствуют процесс принятия решений, предоставляя трейдерам дисциплинированную, прозрачную и более надежную систему пробойной торговли, которая может адаптироваться к различным условиям рынка.
Перевод с английского произведен MetaQuotes Ltd.
Оригинальная статья: https://www.mql5.com/en/articles/19459
Предупреждение: все права на данные материалы принадлежат MetaQuotes Ltd. Полная или частичная перепечатка запрещена.
Данная статья написана пользователем сайта и отражает его личную точку зрения. Компания MetaQuotes Ltd не несет ответственности за достоверность представленной информации, а также за возможные последствия использования описанных решений, стратегий или рекомендаций.
Алгоритм извлечения торговых правил из паттернов в MQL5
Возможности Мастера MQL5, которые вам нужно знать (Часть 65): Использование паттернов FrAMA и индекса силы
Моделирование рынка (Часть 18): Первые шаги на SQL (I)
- Бесплатные приложения для трейдинга
- 8 000+ сигналов для копирования
- Экономические новости для анализа финансовых рынков
Вы принимаете политику сайта и условия использования