Использование Экономического календаря MQL5 для фильтрации новостей (Часть 2): Временное удаление и восстановление стоп-уровней во время выхода новостей
Введение
В части 1 был представлен новостной фильтр, блокирующий открытие новых сделок во время событий высокой важности. Этот механизм снижает риск входа в рынок в условиях аномальной волатильности, но не решает оставшуюся проблему: сделки, открытые до начала новостного окна, по-прежнему подвержены расширению спредов, кратковременным всплескам и искажениям, что может преждевременно активировать SL и TP. Закрытие всех позиций перед каждым новостным событием часто неприемлемо: оно нарушает структуру сделки, искажает статистику и противоречит долгосрочной логике.
В статье рассматривается конкретная техническая задача: как добавить слой управления стоп-уровнями с возможностью обратного восстановления, который временно снимает SL и TP для уже открытых позиций на время ограниченного новостного окна, а затем детерминированно восстанавливает их. Критерии успеха четко заданы: действия должны выполняться один раз за каждое новостное окно без повторных модификаций; исходные значения SL и TP должны сохраняться и восстанавливаться, когда это технически возможно; должны соблюдаться правила брокера о минимальной дистанции до стоп-уровня; механизм должен позволять отбирать сделки по магическому числу советника и иметь четко определенную область действия, включая документированное поведение для одного символа. Цель — снижение риска преждевременного срабатывания стопов, а не прогнозирование направления рынка.
Решение, цель и принципы
Цель части 2 четко определена: убрать уровни стоп-лосса и тейк-профита на время ограниченного новостного окна и безопасно восстановить их после его окончания.
Этот подход не пытается интерпретировать направление реакции рынка на новости. Он лишь снижает риск преждевременного срабатывания стопов, вызванного аномальными рыночными условиями.
Принципы восстановления стоп-уровней
Логика восстановления детерминирована:
- если цена не пересекла сохраненный исходный уровень стоп-лосса или тейк-профита, соответствующий уровень восстанавливается точно на исходном значении;
- если цена уже пересекла исходный уровень стоп-лосса, стоп-лосс восстанавливается на ближайшем допустимом уровне непосредственно перед текущей ценой.
Та же логика применяется к уровням тейк-профита. Это обеспечивает:
- отсутствие некорректной установки стоп-уровней;
- отсутствие отказов брокера из-за того, что цена уже прошла соответствующие уровни;
- отсутствие восстановления логически невозможных значений;
- сохранение исходной структуры сделки во всех технически возможных случаях.
Механизм по-прежнему работает по четко заданным правилам.
Инженерные требования
Поскольку этот подход модифицирует открытые позиции, реализация должна прямо или косвенно обрабатывать несколько нетривиальных случаев:
- несколько открытых сделок, потенциально по разным символам;
- ограничения брокера по минимальной дистанции до стоп-уровня;
- цена, уже пересекшая исходный стоп-уровень или целевой уровень на момент восстановления.
Для стабильной работы система должна:
- безопасно сохранять исходные значения SL и TP перед их удалением;
- отслеживать сделки, находящиеся в состоянии приостановки (когда стопы временно удалены);
- избегать повторных или ненужных попыток модификации;
- восстанавливать стоп-уровни только один раз за цикл события.
Поэтому сохранение состояния стратегии и логика контролируемой модификации являются ключевыми компонентами этой части.
Обзор решения и рамки статьи
Решение, представленное в статье, добавляет слой управления стоп-уровнями, активируемый только во время ограниченного новостного окна.
Рабочий процесс включает следующие этапы:
- обнаружить активное новостное окно, как описано в части 1;
- сохранить исходные значения SL и TP;
- временно удалить уровни SL и TP;
- отслеживать состояние приостановки;
- определить окончание новостного окна;
- детерминированно восстановить SL и TP;
- безопасно сбросить состояние.
Такая структура позволяет торговой стратегии немедленно восстановить полный контроль после окончания новостного окна, не изменяя на постоянной основе исходную структуру сделки.
Ограничения подхода с временным удалением стоп-уровней
Несмотря на свою эффективность, этот подход имеет ограничения.
- Риск открытой позиции без жесткой защиты: во время новостного окна с ограничениями сделки временно не имеют защиты стоп-лоссом. Резкие или продолжительные направленные движения могут увеличить плавающую просадку. Рекомендуется устанавливать короткое новостное окно с ограничениями, например продолжительностью 3-10 минут.
- Риск гэпа: если в период приостановки возникает значительный ценовой разрыв, восстановление может произойти на менее выгодном уровне.
- Это не система прогнозирования волатильности: данный механизм не прогнозирует реакцию рынка или направление движения. Он лишь снижает риск преждевременного срабатывания стопов, вызванного аномальными спредами или кратковременными ценовыми всплесками.
- Ограничения исполнения у брокера: восстановление стоп-уровней должно соответствовать правилам брокера о минимальной дистанции и ограничениям конкретного символа.
- Совместимость со стратегией: этот подход может не подходить для ультракраткосрочных скальпинговых систем, в которых обязательно постоянное наличие стоп-лосса.
Цель этого слоя — не устранение риска, а устранение конкретной структурной слабости автоматической торговли во время событий высокой важности.
Реализация
Прежде чем приступить к реализации, необходимо определить структуру рабочего процесса.
Структура рабочего процесса
Этап обнаружения:
- выявить предстоящие новостные события — этот вопрос уже рассмотрен в части 1 и используется в части 2;
- проверить, попадает ли текущее время в новостное окно.
Этап приостановки:
- пройти по открытым позициям;
- сохранить исходные значения SL и TP в массиве в памяти;
- удалить SL и TP;
- пометить сделку как приостановленную.
Этап мониторинга:
- предотвращать повторные модификации;
- поддерживать состояние приостановки до момента восстановления.
Этап восстановления:
- для каждой приостановленной сделки — если цена не пересекла исходные SL и TP, восстановить точные значения; если цена пересекла исходные уровни, восстановить их на ближайшем допустимом уровне перед текущей ценой;
- очистить состояние приостановки.

Наглядная схема
Шаг 1. Проектирование структуры состояния стоп-уровней
Прежде чем удалять или сохранять что-либо, необходимо ответить на простой вопрос: где будут храниться исходные значения SL и TP после их удаления?
Если удалить стоп-уровни без надежного сохранения, их восстановление станет невозможным.
Почему необходима структура отслеживания
К началу новостного окна уже может быть открыто несколько сделок с разными тикетами, уровнями стоп-лосса и уровнями тейк-профита. Для каждой сделки необходимо сохранить исходную конфигурацию. Поэтому вводится структура состояния, которая будет:
- хранить номер тикета;
- хранить исходный стоп-лосс;
- хранить исходный тейк-профит.
Это обеспечивает точное, а не приблизительное восстановление для каждой сделки.
Структура состояния стоп-уровней
Определяется структура, служащая контейнером для каждой приостановленной сделки.
Необходимый код:
//+------------------------------------------------------------------+ //| Global variables | //+------------------------------------------------------------------+ //--- Structure to store removed stop information struct SavedStops { ulong ticket; // Trade ticket number double sl; // Original stop loss double tp; // Original take profit };
Массив хранения
Поскольку может быть открыто несколько сделок, используется массив следующей структуры:
SavedStops savedStops[]; // Each element of this array represents one suspended trade
Флаг состояния приостановки
Помимо хранения значений стоп-уровней, необходимо отслеживать, находится ли система в состоянии приостановки. Это предотвращает:
- повторное удаление стоп-уровней,
- повторные попытки восстановления;
- ненужную модификацию позиции.
Вводится логический флаг состояния:
bool newsSuspended = false; // When true stops are currently removed, when false stops are active
Почему это решение важно
Эта основа обеспечивает детерминированное восстановление, отслеживание по каждому тикету, отсутствие путаницы между сделками и разделение логики обнаружения и модификации.
На данном этапе пока ничего не снимается. Пока лишь создается система памяти, на которую будет опираться советник.
Все глобальные переменные следует разместить перед определением функции OnInit(), как показано ниже:
//+------------------------------------------------------------------+ //| News integration part2.mq5 | //| soloharbinger | //| https://www.mql5.com/ru/users/soloharbinger | //+------------------------------------------------------------------+ #property copyright "soloharbinger" #property link "https://www.mql5.com/ru/users/soloharbinger" #property version "1.00" #include <Trade/Trade.mqh> //+------------------------------------------------------------------+ //| GLOBAL VARIABLES | //+------------------------------------------------------------------+ CTrade trade; //--- Structure to store removed stop information struct SavedStops { ulong ticket; // Trade ticket number double sl; // Original stop loss double tp; // Original take profit }; SavedStops savedStops[]; bool newsSuspended = false; // when true stops are currently removed, and when false stops are active //+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+ int OnInit() { return(INIT_SUCCEEDED); }
Шаг 2. Фаза приостановки — удаление стоп-уровней во время новостей
Теперь, когда создана структура для хранения данных о стоп-уровнях, можно реализовать фазу их удаления.
Когда новостное окно становится активным, нужно:
- пройти по открытым позициям;
- сохранить исходные значения SL и TP;
- удалить уровни SL и TP на стороне брокера;
- пометить систему как приостановленную.
Защита от повторной приостановки
Если удалять стоп-уровни на каждом тике, пока новостное окно активно, будут постоянно перезаписываться сохраненные значения, выполняться повторные попытки модификации, возникать риск отказа брокера и искажаться сохраненные исходные уровни. Поэтому логика приостановки должна выполняться только один раз за новостной цикл.
Используется следующее условие:
//--- Already in the desired state, prevent duplicate execution if (newsSuspended) { return; }
Функция SuspendStops()
//+------------------------------------------------------------------+ //| Suspend Stops During News | //+------------------------------------------------------------------+ void SuspendStops(bool suspend) { if (suspend == newsSuspended) return; newsSuspended = suspend; if(suspend) { ArrayResize(savedStops, 0); // Clear previous list for(int i = PositionsTotal() - 1; i >= 0; i--) { ulong ticket = PositionGetTicket(i); //--- If needed, filter by magic number here if(PositionSelectByTicket(ticket)) { // Save current SL/TP int idx = ArraySize(savedStops); ArrayResize(savedStops, idx + 1); savedStops[idx].ticket = ticket; savedStops[idx].sl = PositionGetDouble(POSITION_SL); savedStops[idx].tp = PositionGetDouble(POSITION_TP); // Remove stops from broker side if(trade.PositionModify(ticket, 0.0, 0.0)) { PrintFormat("Frozen trade #%I64u: SL/TP removed for news.", ticket); } } } } else // Unsuspended using the RestoreStops() function then reset array { for(int k = 0; k < ArraySize(savedStops); k++) { RestoreStops(savedStops[k].ticket, savedStops[k].sl, savedStops[k].tp); } ArrayResize(savedStops, 0); } }
На данном этапе сделки остаются открытыми, а стоп-уровни не активны. Исходные уровни хранятся в памяти.
Шаг 3. Фаза восстановления — детерминированное восстановление стоп-уровней
После завершения новостного окна необходимо безопасно восстановить стоп-уровни. Однако восстановление — это не просто
trade.PositionModify(ticket, originalSL, originalTP);
поскольку цена могла измениться.
Если цена пересекла исходный стоп-уровень, брокер отклонит его. Еще хуже — может быть восстановлено логически невозможное состояние. Поэтому при восстановлении необходимо учитывать текущую цену и соблюдать правила брокера.
Принципы восстановления
Для каждой сделки с приостановленными стоп-уровнями нужно:
- выбрать позицию по тикету;
- получить текущую рыночную цену;
- сравнить цену с сохраненными уровнями SL и TP;
- если исходный уровень по-прежнему допустим, точно восстановить его;
- если уровень уже пересечен, восстановить его на ближайшем допустимом уровне с соответствующей стороны от текущей цены;
- соблюдать правила брокера о минимальной дистанции до стоп-уровня.
Функция восстановления RestoreStops()
//+------------------------------------------------------------------+ //| Restore Stops After News | //+------------------------------------------------------------------+ bool RestoreStops(ulong ticket, double sl, double tp) { if (!PositionSelectByTicket(ticket)) return(false); ENUM_POSITION_TYPE type = (ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE); double price = (type == POSITION_TYPE_BUY) ? SymbolInfoDouble(_Symbol, SYMBOL_BID) : SymbolInfoDouble(_Symbol, SYMBOL_ASK); double point = SymbolInfoDouble(_Symbol, SYMBOL_POINT); double minStopDist = SymbolInfoInteger(_Symbol, SYMBOL_TRADE_STOPS_LEVEL) * point; // Safety check: If price has moved past the saved SL/TP, adjust to a valid level if(type == POSITION_TYPE_BUY) { if(sl != 0 && price <= sl) sl = price - minStopDist; if(tp != 0 && price >= tp) tp = price + minStopDist; } else // SELL { if(sl != 0 && price >= sl) sl = price + minStopDist; if(tp != 0 && price <= tp) tp = price - minStopDist; } // Full restore of stops if(trade.PositionModify(ticket, sl, tp)) { PrintFormat("Restored SL/TP for #%I64u | SL=%.5f, TP=%.5f", ticket, sl, tp); return true; } else { PrintFormat("Failed to restore SL/TP for #%I64u | Error=%d", ticket, GetLastError()); return false; } }
Что делает эта функция
Для каждой сделки с приостановленными стоп-уровнями:
- восстанавливает исходные SL и TP, если они по-прежнему допустимы;
- корректирует до ближайшего уровня, если цена уже пересекла исходный уровень;
- соблюдает установленную брокером минимальную дистанцию до стоп-уровня.
Раздел интеграции — добавление части 2 к части 1
Мы выполним следующее:
- добавим входные параметры приостановки;
- добавим переменные состояния;
- добавим функции SuspendStops() и RestoreStops();
- интегрируем их в OnTick() с использованием логики переходов состояния.
В качестве ориентира будем использовать код из части 1.
Добавим новый входной параметр приостановки к существующим параметрам:
input bool SuspendStopsDuringNews = false; // Temporarily remove and later restore SL/TP during the news window
//EXISTING INPUTS input bool EnableNewsFilter = false; // Enable Economic News Filter input int NewsMinutesBefore = 5; // Minutes before news to restrict input int NewsMinutesAfter = 5; // Minutes after news to restrict input bool RestrictNewTradesDuringNews = true; // Block new trades during news window input bool CloseOpenTradesBeforeHighImpactNews = false; // Close all trades before news input string SymbolCurrencyOverride = ""; // Manual currency override e.g. "USD,JPY" enum ENUM_NEWS_IMPORTANCE_MODE { NEWS_HIGH_ONLY = 0, NEWS_MODERATE_ONLY, NEWS_HIGH_AND_MODERATE }; input ENUM_NEWS_IMPORTANCE_MODE NewsImportanceMode = NEWS_HIGH_ONLY; // Which importance levels to consider // Cache reload interval input int CacheReloadHours = 6; // How often to reload calendar cache (hours)
Добавим ранее приведенную структуру и глобальные переменные:
// EXISTING GLOBALS MqlCalendarValue TodayEvents[]; // Calendar cache datetime lastCalendarLoad = 0; CTrade trade; // CTrade instance for order management // NEWLY ADDED [part 2] // Structure to store removed stop information struct SavedStops { ulong ticket; // Trade ticket number double sl; // Original stop loss double tp; // Original take profit }; SavedStops savedStops[]; bool newsSuspended = false;
Добавим ранее описанные функции приостановки и восстановления.
Разместим ранее описанные функции приостановки и восстановления ниже функций части 1.
4. Интеграция в OnTick
Почему необходимо реструктурировать OnTick
В части 1 сейчас используются флаг bool isNews, переменная-защелка закрытия сделок и сброс по окончании новостей. Для сценария с закрытием сделок этого достаточно, но во второй части вводится более сложный механизм: необходимо точно определять начало и окончание новостного окна. Это необходимо, поскольку удаление стоп-уровней должно выполняться один раз при начале новостного окна, а восстановление — один раз после его окончания.
Вводятся две переменные:
currentNewsState — состояние, которое система видит в данный момент;
lastNewsState — состояние, которое система видела на предыдущем тике.
Если эти два значения различаются, произошел переход состояния. Переход означает, что новостное окно либо только что началось, либо только что завершилось.
Итоговая функция OnTick
Ниже приведен интегрированный код, заменяющий новостной блок из части 1. Благодаря этому части 1 и 2 остаются объединенными.
//+------------------------------------------------------------------+ //| OnTick | //+------------------------------------------------------------------+ void OnTick() { // Main News Logic if(EnableNewsFilter) { // Track previous state of the news window static bool lastNewsState = false; bool currentNewsState = (EnableNewsFilter && IsNewsTime(_Symbol)); // Detect state transition if(currentNewsState != lastNewsState) { if(currentNewsState) // News window has just started { Print("--- NEWS WINDOW STARTED ---"); // Part 1 Feature: Close trades if enabled if(CloseOpenTradesBeforeHighImpactNews) { Print("Closing all trades due to high impact news setting."); CloseAllTradesForSymbol(_Symbol); } // Part 2 Feature: Suspend SL/TP if enabled if(SuspendStopsDuringNews) { Print("Freezing trade management (SL/TP)."); SuspendStops(true); } } else // News window has just ended { Print("--- NEWS WINDOW ENDED ---"); // Part 2 Feature: Restore SL/TP if(SuspendStopsDuringNews) { Print("Restoring normal trade management."); SuspendStops(false); } } // Update last state after handling transition lastNewsState = currentNewsState; } } }
Переменная-защелка tradesClosedForThisNewsWindow удалена, проверка закрытия на каждом тике исключена, а вместо нее используется модель переходов состояния: одно выполнение при начале или окончании каждого окна.
Новое поведение системы
При начале новостного окна система при необходимости закрывает сделки и удаляет SL и TP. После окончания новостного окна SL и TP восстанавливаются.
Ниже приведено изображение с настройками новостного фильтра.

Настройки новостного фильтра

Пример сообщений Print в период действия новостного окна
Важное замечание
- В начале логики открытия сделок или логики обработки сделок вызовите:
if(!CanOpenNewTrade(_Symbol)) return;
- Создайте магическое число, чтобы все модификации затрагивали только сделки, совершенные этим советником.
input int magicNumber = 12345; //--- in SuspendStops and CloseAllTradesForSymbol, filter by magic: if (PositionSelectByTicket(ticket) && PositionGetInteger(POSITION_MAGIC) == magicNumber)
- Эта реализация предназначена для односимвольных советников. Она не будет корректно работать для мультисимвольных советников, торгующих несколькими символами одновременно, поскольку восстановление стоп-уровней зависит от текущего символа графика.
Заключение
Мы устранили технический разрыв между блокировкой открытия новых сделок и безопасным управлением существующими позициями во время новостей. Предложенное решение детерминировано и готово к интеграции: структура SavedStops для каждого тикета сохраняет исходные SL и TP в памяти; флаг приостановки и модель переходов состояния обеспечивают однократное выполнение при начале и окончании новостного окна; SuspendStops() однократно удаляет SL и TP, а RestoreStops() восстанавливает стоп-уровни с учетом текущей цены и минимальной дистанции, заданной брокером. Этот механизм сохраняет исходную структуру сделки, когда это возможно. Если цена пересекла сохраненный уровень, используется предсказуемый запасной сценарий — устанавливается ближайший допустимый уровень.
Практические ограничения сформулированы явно: используйте магическое число для ограничения области действия, учитывайте односимвольную работу текущей реализации и временную незащищенность позиций во время окна приостановки. Этот слой не является предиктором волатильности. Это узконаправленный механизм снижения риска, основанный на правилах, который предотвращает ложные срабатывания стоп-уровней из-за расширения спредов и ценовых всплесков, сохраняя статистическую целостность советника. В следующей части будет добавлено постоянное хранилище, чтобы удаленные на время новости стоп-уровни сохранялись после перезапуска терминала или советника.
Ниже приведено изображение с выводом операторов Print() во время работы новостного окна.
Перевод с английского произведен MetaQuotes Ltd.
Оригинальная статья: https://www.mql5.com/en/articles/21446
Предупреждение: все права на данные материалы принадлежат MetaQuotes Ltd. Полная или частичная перепечатка запрещена.
Данная статья написана пользователем сайта и отражает его личную точку зрения. Компания MetaQuotes Ltd не несет ответственности за достоверность представленной информации, а также за возможные последствия использования описанных решений, стратегий или рекомендаций.
Освоение массивов премии и дисконта: Оптимизация торговли по дисбалансам в PD-массивах
Свинговые экстремумы и откаты в MQL5 (Часть 2): Автоматизация стратегии с помощью советника
Единый пайплайн валидации для защиты от переобучения бэктеста
Вероятностный метод главных компонент на MQL5
- Бесплатные приложения для трейдинга
- 8 000+ сигналов для копирования
- Экономические новости для анализа финансовых рынков
Вы принимаете политику сайта и условия использования
Как я планирую использовать эти функции:
Я планирую использовать часть 1 статьи — «Предотвращение открытия новых сделок или закрытия открытых позиций в период ограничения торговли из-за новостей» — во всех своих советниках, запущенных на счетах проп-фирм.
Однако части 2 и 3 — удаление SL/TP, а затем их восстановление до и после периода ограничений на торговлю из-за новостей — я планирую использовать в отдельном советнике. Поскольку этот код проходит по всем открытым позициям на счетах, у меня может быть открыто более одного графика, и мне не нужно запускать его больше раз, чем необходимо. Я вручную перенастрою выбор символов на «USD, GBP, EUR», так как это единственные новости из «красной папки», которые, как я знаю по опыту, мне уже приходится фильтровать (у вас это будет по-другому).
Поэтому внес однострочное изменение в функцию RestoreStops().
Функция SuspendStops() не требовала изменений.
Судя по всему, мне не придется ничего менять в Части 3, так как мне нужны только: номер тикета, SL и TP.
Большое спасибо @Solomon Anietie Sunday
Я протестирую это в реальном режиме на демо-счете!
// I won't need this if statement, in the 2nd EA, as the 2nd EA only modifies trades, // only need this is statement in the first EA (that opens trades) if(!CanOpenNewTrade(_Symbol)) return;