English Deutsch 日本語
preview
Архитектура прибыльной торговли с усиленной многоуровневой защитой счёта

Архитектура прибыльной торговли с усиленной многоуровневой защитой счёта

MetaTrader 5Примеры |
38 1
Hlomohang John Borotho
Hlomohang John Borotho

Оглавление:

  1. Введение
  2. Обзор системы и принципы её работы
  3. Начало работы
  4. Результаты бэктеста
  5. Заключение


Введение

В современной алгоритмической торговле стремление к высокой доходности неизбежно сопровождается повышенной подверженностью рыночной волатильности, неопределённости исполнения ордеров и системным рискам, которые могут быстро разрушить капитал. Чтобы успешно работать в такой среде, советник должен не только выявлять прибыльные возможности, но и разумно регулировать объём риска, который он принимает в каждый момент времени. Именно здесь многоуровневая защита счёта становится необходимой: это архитектурный подход, объединяющий адаптивный расчёт лота, объём позиции, адаптируемый к волатильности и мониторинг эффективности в реальном времени в единую защитную структуру. Вместо опоры на один защитный механизм система использует несколько согласованных уровней, которые динамически меняются вместе с рыночными условиями.

Встраивая защиту непосредственно в торговую логику — структурно, поведенчески и системно, — мы даём советнику возможность применять стратегии, ориентированные на быстрый рост капитала, сохраняя при этом дисциплинированный подход к риску. Он определяет, когда увеличивать рыночную экспозицию, когда сокращать объём позиции и когда полностью останавливать торговлю ради сохранения капитала. Благодаря защитным порогам по средствам счёта, уровням просадки, сегментированной логике исполнения, аварийному останову и протоколам восстановления торговая система ведёт себя скорее как саморегулирующийся организм, чем как статический набор правил. В результате получается интеллектуальная и устойчивая торговая архитектура, способная извлекать выгоду из благоприятных движений рынка, одновременно активно управляя угрозами и нейтрализуя их в реальном времени.


Обзор системы и принципы её работы

Советник (EA) разработан как сложная автоматизированная торговая система для золота (XAUUSD), объединяющая стратегию восстановления по мартингейлу с надёжной многоуровневой защитой счёта. В основе он работает как цикл с управляемым риском: определяет новые тренды с помощью пересечений EMA и открывает начальную сделку. Если эта сделка убыточна, советник переходит в «фазу восстановления», увеличивая размер лота по выбранной формуле мартингейла, чтобы компенсировать потери, и продолжает этот процесс ограниченное число шагов. Когда сделка закрывается с прибылью, последовательность сбрасывается. Это базовое ядро окружено комплексной системой безопасности, предназначенной для предотвращения катастрофических потерь, обычно связанных с системами мартингейла. Ключевые защитные уровни включают динамические стопы по средствам счёта (equity stop), дневные лимиты убытка, ограничение торговой активности и аварийный останов, который останавливает всю торговлю после заданного числа последовательных убытков или при достижении определённого порога просадки.

Логика защиты советника работает как серия щитов и механизмов раннего срабатывания, охраняющих ваш капитал. Представьте её как иерархическую систему обороны: сначала отдельные сделки защищаются проверками проскальзывания и стоп-лоссами, скорректированными по волатильности. Затем счёт защищают модули мониторинга в реальном времени, отслеживающие текущее значение средств счёта. Если просадка от пикового значения средств счёта превышает заданный процент, система может полностью остановить торговлю. Дополнительные уровни включают ограничение количества сделок в заданном временном окне для предотвращения переторговки и протокол восстановления, который после тяжёлой серии убытков автоматически снижает риск и требует периода ожидания или восстановления средств счёта перед возобновлением работы. Такая структура гарантирует, что агрессивный механизм восстановления по мартингейлу всегда ограничен заранее заданными рациональными пределами, с приоритетом сохранения капитала.

Наконец, система использует механизм сохранения состояния, чтобы сохранять защитную логику между торговыми сессиями и перезапусками платформы. С помощью глобальных переменных она запоминает пиковое значение средств счёта, последовательные убытки и то, находится ли она в приостановленном состоянии «восстановления». Это позволяет советнику последовательно соблюдать правила, а не только в рамках одного запуска. Например, если в пятницу сработает аварийный останов и советник приостановится, в понедельник он останется на паузе, предотвращая немедленный повторный вход в рискованные условия. Такой дизайн превращает советника из простого автоматизированного скрипта в устойчивую торговую систему с учётом внутреннего состояния, которая методично стремится к прибыли и при этом изначально спроектирована так, чтобы переживать неблагоприятные рыночные условия и прежде всего защищать торговый счёт.



Начало работы

//+------------------------------------------------------------------+
//|                                                       GALEIT.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 Libraries
#include <Trade/Trade.mqh>

//--- Global Variables
CTrade trade;
ulong lastTicket = 0;
int martingaleStep = 0;
double dailyProfit = 0.0;
datetime lastTradeTime = 0;
string tradeSequenceId = "";

//--- Global Variable Names
#define GV_PEAK_EQUITY       "GV_PeakEquity"
#define GV_PAUSE_EA          "GV_EA_Paused"
#define GV_DD_LOCK_LEVEL     "GV_DrawdownLockLevel"
#define GV_CONSEC_LOSSES     "GV_ConsecLosses"
#define GV_TRADE_WINDOW_START "GV_TradeWindowStart"
#define GV_TRADES_IN_WINDOW  "GV_TradesInWindow"

//--- Input Parameters
input group  "Trading Strategy"
input int FastMAPeriod = 10;
input int SlowMAPeriod = 50;

input group "Martingale & Money Management"
input double InitialLotSize = 0.01;
input double LotMultiplier = 2.0;
input int MaxMartingaleSteps = 5;
input double RiskPercent = 2.0;

input group  "Volatility Management (ATR)"
input int ATR_Period = 14;
input double ATR_SL_Factor = 1.5;
input double ATR_TP_Factor = 1.0;

input group  "Account Protection"
input bool UseEquityStop = true;
input double EquityStopPercent = 8.0;
input bool UseDailyLossLimit = true;
input double DailyLossPercent = 5.0;
input bool UseMaxSpreadFilter = true;
input int MaxSpreadPoints = 5;

input group "Trailing Stop Parameters"
input bool UseTrailingStop = true;       
input int BreakEvenAtPips = 500;          
input int TrailStartAtPips = 600;        
input int TrailStepPips = 100;          

input group  "Circuit Breaker Settings"
input int MaxConsecutiveLosses = 3;
input double CircuitBreakerDD = 15.0;

input group  "Throttle Settings"
input int MaxTradesPerHour = 10;
input int ThrottleWindowSeconds = 3600;

input group  "Recovery Settings"
input double RecoveryRiskReduction = 0.5;
input double ResumeEquityPercent = 90.0;
input int ResumeAfterSeconds = 86400;

Мы начинаем с определения основных глобальных переменных, на которые советник будет опираться для отслеживания своего внутреннего состояния и торгового поведения. К ним относятся такие объекты, как экземпляр CTrade для исполнения ордеров, переменные для хранения тикета последней сделки, прогресса мартингейла и накопленного результата за день, а также переменные для отслеживания времени и идентификаторы торговых последовательностей. Сразу после этого мы объявляем набор имён глобальных переменных, которые сохраняются с помощью встроенной в MetaTrader системы GlobalVariable. Они позволяют советнику поддерживать важные состояния, связанные с риском, такие как пиковое значение средств счёта, условия паузы, уровни блокировки по просадке, последовательные убытки и активность в торговом окне, даже если MetaTrader перезапущен или график перезагружен.

Далее мы вводим сгруппированные входные параметры, которые определяют основную торговую стратегию и то, как система взаимодействует с рыночными условиями. Затем задаются параметры мартингейла и управления капиталом, такие как начальный размер лота, множитель лота, максимальное количество шагов восстановления и процент риска от средств счёта. Раздел управления волатильностью включает динамические расчёты стоп-лосса и тейк-профита на основе ATR, благодаря чему уровни выхода из сделки автоматически масштабируются в соответствии с рыночной волатильностью, а не используют фиксированные расстояния в пунктах.

Наконец, через дополнительные группы входных параметров мы определяем несколько уровней защитной логики. Раздел «Защита счёта» управляет стопом по средствам счёта, дневными лимитами убытка и фильтрацией спреда — инструментами, предназначенными для предотвращения торговли в опасных или дорогостоящих рыночных условиях. Параметры трейлинг-стопа позволяют советнику динамически фиксировать прибыль, когда сделка начинает приносить прибыль. Настройки «Аварийный останов» и «Ограничение частоты сделок» вводят системные защитные механизмы, останавливающие или ограничивающие торговлю, когда условия становятся слишком рискованными или частота сделок чрезмерно возрастает. Наконец, раздел «Восстановление» описывает, как советник должен вести себя после защитных остановок, включая степень снижения риска и условия, при которых торговля может безопасно возобновиться. В совокупности эти настройки создают многоуровневую, адаптивную и устойчивую торговую структуру.

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
{
   Print("GaleIT EA Initialized");
   trade.SetExpertMagicNumber(12345);
   tradeSequenceId = GenerateTradeSequenceId();
   
   // Initialize all protection systems
   InitAccountShields();
   InitFailSafes();
   
   return(INIT_SUCCEEDED);
}

//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
   Print("GaleIT EA Deinitialized - Reason: ", reason);
}

//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
{
   // Update protection systems
   UpdatePeakEquity();
   TryResumeFromRecovery();
   
   // Check if trading is allowed
   if(!IsTradingAllowed()) return;
   
   // Check all safety limits
   if(!CheckSafetyLimits()) return;
   
   // Manage existing positions
   ManageExistingPositions();
   
   // Check for new trading opportunities
   if(IsNewBar())
   {
      CheckForNewTrade();
   }
   
   ManageOpenTrades();
}

Инициализация начинается в функции OnInit(), где советник выводит сообщение о запуске, задаёт уникальный идентификатор сделок (Magic Number) и генерирует tradeSequenceId для отслеживания текущей последовательности сделок. Это гарантирует, что все сделки, открытые советником, получают уникальные метки, что упрощает их дальнейшее сопровождение. Затем советник вызывает две важные функции инициализации: InitAccountShields() и InitFailSafes(). Эти функции подготавливают все уровни управления риском — такие как блокировки по средствам счёта, трекеры дневного финансового результата, аварийный останов и состояния восстановления, — чтобы советник начинал работу с активированными и синхронизированными системами защиты.

Когда советник удаляется или терминал завершает работу, функция OnDeinit() записывает сообщение о деинициализации советника вместе с кодом причины, предоставленным MetaTrader. Хотя эта функция не выполняет сложной логики, она обеспечивает прозрачность и отслеживаемость, позволяя трейдеру или разработчику понять, был ли советник удалён вручную, перезагружен или остановлен системным событием. Корректная деинициализация помогает сохранять согласованное состояние глобальных переменных и снижает риск влияния устаревших данных на будущие сессии.

Основной торговый движок работает в OnTick(), который выполняется каждый раз при изменении рыночной цены. Первые действия связаны с безопасностью и восстановлением: советник обновляет пиковое значение средств счёта для отслеживания условий просадки и пытается возобновить торговлю, если ранее вошёл в состояние восстановления или паузы. Перед принятием любых торговых решений советник проверяет, разрешена ли торговля через IsTradingAllowed(), и не были ли нарушены какие-либо лимиты безопасности через CheckSafetyLimits(). Если всё безопасно, советник переходит к управлению открытыми позициями, корректировке стопов и оценке торговой логики. На каждом новом баре он вызывает CheckForNewTrade() для поиска возможностей на основе правил стратегии. Наконец, ManageExistingPositions() обеспечивает корректное выполнение трейлинг-стопов, переводов в безубыток и частичных закрытий позиции, завершая полный цикл оценки и исполнения на каждом тике.

//+------------------------------------------------------------------+
//| Check if trading is allowed                                      |
//+------------------------------------------------------------------+
bool IsTradingAllowed()
{
   // Check if EA is paused by protection systems
   if(IsEAProtectedPaused()) return false;
   
   // Check circuit breaker
   if(CheckCircuitBreaker(MaxConsecutiveLosses, CircuitBreakerDD)) return false;
   
   // Check trade throttle
   if(!ThrottleAllowNewTrade(MaxTradesPerHour, ThrottleWindowSeconds)) return false;
   
   // Check spread filter
   if(UseMaxSpreadFilter)
   {
      long spread = SymbolInfoInteger(_Symbol, SYMBOL_SPREAD);
      if(spread > MaxSpreadPoints * 10) return false;
   }
   
   return true;
}

//+------------------------------------------------------------------+
//| Check safety limits                                              |
//+------------------------------------------------------------------+
bool CheckSafetyLimits()
{
   // Equity Stop protection
   if(UseEquityStop)
   {
      double currentEquity = AccountInfoDouble(ACCOUNT_EQUITY);
      double currentBalance = AccountInfoDouble(ACCOUNT_BALANCE);
      double equityDropPercent = (1 - (currentEquity / currentBalance)) * 100;
      
      if(equityDropPercent >= EquityStopPercent)
      {
         Print("Equity stop triggered: ", equityDropPercent, "%");
         CloseAllPositions();
         ExpertRemove();
         return false;
      }
   }
   
   // Daily loss limit
   if(UseDailyLossLimit)
   {
      double dailyLossLimit = (DailyLossPercent / 100) * AccountInfoDouble(ACCOUNT_BALANCE);
      if(dailyProfit <= -dailyLossLimit)
      {
         Print("Daily loss limit reached: ", dailyProfit);
         CloseAllPositions();
         return false;
      }
   }
   
   return true;
}

Функция IsTradingAllowed() выступает первым фильтром допуска советника, определяя, безопасно ли открывать новые сделки. Она начинает с проверки, не был ли советник поставлен на паузу какими-либо защитными механизмами, такими как режим восстановления или блокировки по просадке. Затем она проверяет аварийный останов, который останавливает торговлю после слишком большого числа последовательных убытков или чрезмерной просадки. Функция также применяет правила ограничения торговой активности, ограничивая количество сделок, которые могут быть открыты в заданном временном окне. Наконец, если включён фильтр спреда, она проверяет, что текущий рыночный спред не превышает максимально допустимый уровень. Только если каждая из этих проверок пройдена, советник получает разрешение перейти к оценке или открытию сделок.

Функция CheckSafetyLimits() обеспечивает второй уровень защиты, применяя жёсткие правила защиты счёта. Сначала она оценивает систему стопа по средствам счёта — механизм, который рассчитывает процентное снижение средств счёта относительно баланса. Если это падение достигает или превышает настроенный порог, советник немедленно закрывает все открытые позиции и удаляет себя с графика, чтобы предотвратить дальнейший ущерб. Затем функция проверяет дневной лимит убытка, гарантируя, что торговля остановится, когда счёт достигнет заранее заданной максимальной дневной просадки. Если срабатывает любое из этих условий, торговля прекращается, а советник возвращает false, сигнализируя о нарушении лимитов безопасности. Только когда обе защиты остаются в безопасных границах, советник разрешает торговому циклу продолжаться.

//+------------------------------------------------------------------+
//| Check for new trade opportunity                                  |
//+------------------------------------------------------------------+
void CheckForNewTrade()
{
   if(PositionsTotal() > 0) return;
   
   int signal = GetTradingSignal();
   if(signal != 0)
   {
      double lotSize = CalculateLotSize();
      double sl, tp;
      CalculateSLTP(signal, sl, tp);
      
      if(OpenPosition(signal, lotSize, sl, tp))
      {
         lastTradeTime = TimeCurrent();
         martingaleStep = 0;
         tradeSequenceId = GenerateTradeSequenceId();
      }
   }
}

//+------------------------------------------------------------------+
//| Get trading signal                                               |
//+------------------------------------------------------------------+
int GetTradingSignal()
{
   int fastMA = iMA(_Symbol, _Period, FastMAPeriod, 0, MODE_EMA, PRICE_CLOSE);
   int slowMA = iMA(_Symbol, _Period, SlowMAPeriod, 0, MODE_EMA, PRICE_CLOSE);
   
   if(fastMA == INVALID_HANDLE || slowMA == INVALID_HANDLE) return 0;
   
   double fastMAValues[], slowMAValues[];
   ArraySetAsSeries(fastMAValues, true);
   ArraySetAsSeries(slowMAValues, true);
   
   if(CopyBuffer(fastMA, 0, 0, 3, fastMAValues) < 3) 
   {
      IndicatorRelease(fastMA);
      IndicatorRelease(slowMA);
      return 0;
   }
   if(CopyBuffer(slowMA, 0, 0, 3, slowMAValues) < 3) 
   {
      IndicatorRelease(fastMA);
      IndicatorRelease(slowMA);
      return 0;
   }
   
   double currentFast = fastMAValues[0];
   double currentSlow = slowMAValues[0];
   double prevFast = fastMAValues[1];
   double prevSlow = slowMAValues[1];
   
   int signal = 0;
   if(prevFast <= prevSlow && currentFast > currentSlow)
      signal = 1;
   else if(prevFast >= prevSlow && currentFast < currentSlow)
      signal = -1;
   
   IndicatorRelease(fastMA);
   IndicatorRelease(slowMA);
   
   return signal;
}

//+------------------------------------------------------------------+
//| Calculate lot size                                               |
//+------------------------------------------------------------------+
double CalculateLotSize()
{
   if(martingaleStep == 0)
   {
      // Use risk-based lot sizing for first trade
      double atr = GetATR(_Symbol, _Period, ATR_Period);
      double currentPrice = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
      double slPrice = currentPrice - (atr * ATR_SL_Factor);
      
      return CalculateLotFromRisk(_Symbol, currentPrice, slPrice, RiskPercent);
   }
   else
   {
      // Martingale recovery trade
      return NormalizeDouble(InitialLotSize * MathPow(LotMultiplier, martingaleStep), 2);
   }
}

//+------------------------------------------------------------------+
//| Calculate Stop Loss and Take Profit                              |
//+------------------------------------------------------------------+
void CalculateSLTP(int signal, double &sl, double &tp)
{
   double atr = GetATR(_Symbol, _Period, ATR_Period);
   double currentPrice = signal > 0 ? SymbolInfoDouble(_Symbol, SYMBOL_ASK) : SymbolInfoDouble(_Symbol, SYMBOL_BID);
   
   if(signal > 0)
   {
      sl = currentPrice - (atr * ATR_SL_Factor);
      tp = currentPrice + (atr * ATR_TP_Factor);
   }
   else
   {
      sl = currentPrice + (atr * ATR_SL_Factor);
      tp = currentPrice - (atr * ATR_TP_Factor);
   }
   
   // Validate distances
   double point = SymbolInfoDouble(_Symbol, SYMBOL_POINT);
   double minDist = 100 * point;
   
   if(signal > 0)
   {
      if(currentPrice - sl < minDist) sl = currentPrice - minDist;
      if(tp - currentPrice < minDist) tp = currentPrice + minDist;
   }
   else
   {
      if(sl - currentPrice < minDist) sl = currentPrice + minDist;
      if(currentPrice - tp < minDist) tp = currentPrice - minDist;
   }
   
   int digits = (int)SymbolInfoInteger(_Symbol, SYMBOL_DIGITS);
   sl = NormalizeDouble(sl, digits);
   tp = NormalizeDouble(tp, digits);
}

//+------------------------------------------------------------------+
//| Open position                                                    |
//+------------------------------------------------------------------+
bool OpenPosition(int signal, double lotSize, double sl, double tp)
{
   double price = (signal > 0) ? SymbolInfoDouble(_Symbol, SYMBOL_ASK) : SymbolInfoDouble(_Symbol, SYMBOL_BID);
   ENUM_ORDER_TYPE orderType = (signal > 0) ? ORDER_TYPE_BUY : ORDER_TYPE_SELL;
   
   // Use protective execution
   return ExecuteProtectedOrder(_Symbol, orderType, lotSize, price, sl);
}

//+------------------------------------------------------------------+
//| Manage existing positions                                        |
//+------------------------------------------------------------------+
void ManageExistingPositions()
{
   int totalPositions = PositionsTotal();
   
   for(int i = totalPositions - 1; i >= 0; i--)
   {
      ulong ticket = PositionGetTicket(i);
      if(ticket > 0 && PositionGetString(POSITION_COMMENT) == tradeSequenceId)
      {
         double currentProfit = PositionGetDouble(POSITION_PROFIT);
         
         if(PositionGetInteger(POSITION_TIME_UPDATE) > lastTradeTime)
         {
            bool wasProfit = (currentProfit > 0);
            OnTradeClosed(wasProfit);
            
            if(!wasProfit)
            {
               martingaleStep++;
               if(martingaleStep > MaxMartingaleSteps)
               {
                  Print("Max martingale steps reached. Activating recovery protocol.");
                  double reducedRisk = RiskPercent;
                  RecoveryProtocol(reducedRisk, RecoveryRiskReduction, ResumeEquityPercent, ResumeAfterSeconds);
                  martingaleStep = 0;
               }
            }
            else
            {
               martingaleStep = 0;
            }
            
            dailyProfit += currentProfit;
            lastTradeTime = TimeCurrent();
         }
      }
   }
}

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

Сам сигнал основан на классической логике пересечения скользящих средних, но сделан более надёжным за счёт извлечения нескольких элементов буфера для подтверждения реального пересечения, а не случайного однотикового колебания. ATR играет ключевую роль в расчёте размера позиции и SL/TP: для новых сделок ATR определяет риск-ориентированный размер через расстояние до стоп-лосса, а для выходов из позиции ATR задаёт динамические цели прибыли и пороги убытка. Значения SL/TP нормализуются по точности символа, ограничиваются минимальными торговыми расстояниями и различаются в зависимости от того, получает ли система возможность на покупку (бычье пересечение) или продажу (медвежье пересечение).

Функция ManageExistingPositions() отвечает за интеллектуальное управление существующими позициями и анализ их результатов. Каждый раз, когда сделка, связанная с текущей последовательностью, обновляется, система оценивает, закрылась ли она с прибылью или убытком. Прибыльные сделки сбрасывают счётчик шага мартингейла, а убыточные увеличивают шаг и могут запускать дополнительные входы по мартингейлу — до заранее заданного лимита. Если достигнут максимальный шаг, советник активирует протокол восстановления, который снижает риск, останавливает торговлю до стабилизации средств счёта и возобновляет её только после улучшения рыночных условий или истечения периода принудительного ожидания. Это формирует безопасный цикл восстановления, направленный на аккуратный выход из серии убытков без неконтролируемого роста риска.

double CalculateLotFromRisk(string symbol, double entry_price, double sl_price, double risk_percent)
{
   if(risk_percent <= 0) return(InitialLotSize);
   
   double equity = AccountInfoDouble(ACCOUNT_EQUITY);
   double risk_amount = equity * (risk_percent / 100.0);
   double point = SymbolInfoDouble(symbol, SYMBOL_POINT);
   double sl_points = MathMax(1.0, MathAbs(entry_price - sl_price) / point);
   double tick_value = SymbolInfoDouble(symbol, SYMBOL_TRADE_TICK_VALUE);
   double tick_size  = SymbolInfoDouble(symbol, SYMBOL_TRADE_TICK_SIZE);
   
   if(tick_value <= 0 || tick_size <= 0) return(InitialLotSize);
   
   double value_per_point_per_lot = tick_value / (tick_size / point);
   double risk_per_lot = sl_points * value_per_point_per_lot;
   if(risk_per_lot <= 0.0) return(InitialLotSize);
   
   double volume = risk_amount / risk_per_lot;
   double step = SymbolInfoDouble(symbol, SYMBOL_VOLUME_STEP);
   double minLot = SymbolInfoDouble(symbol, SYMBOL_VOLUME_MIN);
   double maxLot = SymbolInfoDouble(symbol, SYMBOL_VOLUME_MAX);
   if(step <= 0) step = 0.01;
   
   double normalized = MathFloor(volume / step) * step;
   if(normalized < minLot) normalized = minLot;
   if(normalized > maxLot) normalized = maxLot;
   
   return(NormalizeDouble(normalized, (int)MathMax(0, (int)(-MathLog10(step)))));
}

//+------------------------------------------------------------------+
//| STRUCTURAL ACCOUNT SHIELDS                                      |
//+------------------------------------------------------------------+
void InitAccountShields()
{
   if(!GlobalVariableCheck(GV_PEAK_EQUITY))
      GlobalVariableSet(GV_PEAK_EQUITY, AccountInfoDouble(ACCOUNT_EQUITY));
   if(!GlobalVariableCheck(GV_PAUSE_EA))
      GlobalVariableSet(GV_PAUSE_EA, 0.0);
   if(!GlobalVariableCheck(GV_DD_LOCK_LEVEL))
      GlobalVariableSet(GV_DD_LOCK_LEVEL, 0.0);
}

void UpdatePeakEquity()
{
   double current = AccountInfoDouble(ACCOUNT_EQUITY);
   double peak = GlobalVariableGet(GV_PEAK_EQUITY);
   if(current > peak) GlobalVariableSet(GV_PEAK_EQUITY, current);
}

double GetCurrentDrawdownPercent()
{
   double peak = GlobalVariableGet(GV_PEAK_EQUITY);
   double equity = AccountInfoDouble(ACCOUNT_EQUITY);
   if(peak <= 0) return(0.0);
   return ((peak - equity) / peak) * 100.0;
}

bool IsEAProtectedPaused()
{
   return (GlobalVariableGet(GV_PAUSE_EA) != 0.0);
}

//+------------------------------------------------------------------+
//| TRADE-LEVEL REINFORCEMENT                                       |
//+------------------------------------------------------------------+
bool ExecuteProtectedOrder(string symbol, ENUM_ORDER_TYPE type, double volume, double price, double sl_price)
{
   if(IsEAProtectedPaused()) return false;
   
   double point = SymbolInfoDouble(symbol, SYMBOL_POINT);
   double allowableDeviationPoints = 10;
   
   if(type == ORDER_TYPE_BUY)
   {
      double ask = SymbolInfoDouble(symbol, SYMBOL_ASK);
      if(MathAbs(ask - price) / point > allowableDeviationPoints) return false;
   }
   else
   {
      double bid = SymbolInfoDouble(symbol, SYMBOL_BID);
      if(MathAbs(bid - price) / point > allowableDeviationPoints) return false;
   }
   
   double minLot = SymbolInfoDouble(symbol, SYMBOL_VOLUME_MIN);
   double maxLot = SymbolInfoDouble(symbol, SYMBOL_VOLUME_MAX);
   if(volume < minLot || volume > maxLot) return false;
   
   bool ok = false;
   if(type == ORDER_TYPE_BUY) 
      ok = trade.Buy(volume, symbol, price, sl_price, 0, NULL);
   else if(type == ORDER_TYPE_SELL) 
      ok = trade.Sell(volume, symbol, price, sl_price, 0, NULL);
   
   if(!ok) PrintFormat("Order failed: %s (err %d)", symbol, GetLastError());
   return ok;
}

//+------------------------------------------------------------------+
//| SYSTEMIC FAIL-SAFES                                             |
//+------------------------------------------------------------------+
void InitFailSafes()
{
   if(!GlobalVariableCheck(GV_CONSEC_LOSSES)) GlobalVariableSet(GV_CONSEC_LOSSES, 0);
   if(!GlobalVariableCheck(GV_TRADE_WINDOW_START)) GlobalVariableSet(GV_TRADE_WINDOW_START, TimeCurrent());
   if(!GlobalVariableCheck(GV_TRADES_IN_WINDOW)) GlobalVariableSet(GV_TRADES_IN_WINDOW, 0);
}

void OnTradeClosed(bool wasProfit)
{
   if(wasProfit) 
      GlobalVariableSet(GV_CONSEC_LOSSES, 0);
   else 
      GlobalVariableSet(GV_CONSEC_LOSSES, GlobalVariableGet(GV_CONSEC_LOSSES) + 1);
   
   GlobalVariableSet(GV_TRADES_IN_WINDOW, GlobalVariableGet(GV_TRADES_IN_WINDOW) + 1);
}

bool CheckCircuitBreaker(int maxConsecLosses, double drawdownThresholdPercent)
{
   int consec = (int)GlobalVariableGet(GV_CONSEC_LOSSES);
   double dd = GetCurrentDrawdownPercent();
   if(consec >= maxConsecLosses || dd >= drawdownThresholdPercent)
   {
      GlobalVariableSet(GV_PAUSE_EA, 1.0);
      Print("Circuit breaker engaged: consec=", consec, " dd=", dd);
      return true;
   }
   return false;
}

bool ThrottleAllowNewTrade(int maxTrades, int windowSeconds)
{
   datetime start = (datetime)GlobalVariableGet(GV_TRADE_WINDOW_START);
   int count = (int)GlobalVariableGet(GV_TRADES_IN_WINDOW);
   datetime now = TimeCurrent();
   
   if((now - start) > windowSeconds)
   {
      GlobalVariableSet(GV_TRADE_WINDOW_START, now);
      GlobalVariableSet(GV_TRADES_IN_WINDOW, 0);
      count = 0;
   }
   return (count < maxTrades);
}

void RecoveryProtocol(double &riskPercent, double reductionFactor, double resumeEquityPercentOfPeak, int resumeAfterSeconds)
{
   riskPercent *= reductionFactor;
   GlobalVariableSet(GV_PAUSE_EA, 1.0);
   GlobalVariableSet("GV_RecoveryResumeTime", TimeCurrent() + resumeAfterSeconds);
   GlobalVariableSet("GV_ResumeEquityPercent", resumeEquityPercentOfPeak);
}

bool TryResumeFromRecovery()
{
   double resumeTime = GlobalVariableGet("GV_RecoveryResumeTime");
   if(resumeTime == 0) return false;
   if(TimeCurrent() < (datetime)resumeTime) return false;
   
   double resumePercent = GlobalVariableGet("GV_ResumeEquityPercent");
   if(resumePercent <= 0)
   {
      GlobalVariableSet(GV_PAUSE_EA, 0.0);
      GlobalVariableSet("GV_RecoveryResumeTime", 0);
      return true;
   }
   
   double peak = GlobalVariableGet(GV_PEAK_EQUITY);
   double needed = peak * (resumePercent / 100.0);
   if(AccountInfoDouble(ACCOUNT_EQUITY) >= needed)
   {
      GlobalVariableSet(GV_PAUSE_EA, 0.0);
      GlobalVariableSet("GV_RecoveryResumeTime", 0);
      return true;
   }
   return false;
}

Функция CalculateLotFromRisk() обеспечивает базовый механизм динамического управления уровнем риска, преобразуя заданный пользователем процент риска в точный размер лота с учётом волатильности. Она рассчитывает денежный риск, допустимый на сделку, на основе средств счёта, расстояния от входа до стоп-лосса в пунктах и денежной стоимости пункта для символа. Это позволяет советнику рассчитывать размер позиций пропорционально как рыночной волатильности, так и допустимому для трейдера риску. Ограничения брокера, такие как минимальный и максимальный лот, а также шаг изменения лота, применяются для того, чтобы все рассчитанные объёмы оставались доступными для исполнения. Нормализованный результат обеспечивает точность, соответствие требованиям и последовательное распределение риска, особенно на быстро движущихся рынках.

Далее блок «Структурные защитные механизмы счёта» создаёт защитную структуру для мониторинга состояния счёта и защиты от глубоких просадок. При запуске советник записывает пиковое значение средств счёта, инициализирует защитные переменные, а затем динамически обновляет пик каждый раз, когда средства счёта достигают нового максимума. Используя эти значения, система измеряет текущую просадку в процентах от пика, позволяя советнику принимать логичные решения на основе текущего уровня просадки. Также включён флаг паузы, чтобы внешние модули — такие как аварийный останов или протоколы восстановления — могли временно заморозить все торговые операции при нарушении защитного порога.

Система «Защита исполнения сделок» сосредоточена на проверке и защите каждого ордера во время исполнения. Перед отправкой любой сделки на сервер советник убеждается, что защитный режим не активен, цена исполнения достаточно близка к текущему bid/ask (что предотвращает риски, связанные с проскальзыванием), а размер лота находится в пределах правил брокера. Затем ордера открываются с помощью торгового объекта MQL5 с возможной установкой SL для дополнительной безопасности. Если сделка не удалась, советник записывает конкретный символ и код ошибки, что упрощает отладку и помогает строить адаптивные процедуры. Эта модель защищённого исполнения гарантирует, что на рынок попадают только структурно корректные сделки — правильно оценённые, рассчитанные по объёму и разрешённые.

Наконец, модуль «Системные аварийные защиты» (Systemic Fail-Safes) работает как аварийный контур стабилизации советника, отслеживая торговое поведение во времени для выявления опасных паттернов. Он отслеживает последовательные убытки, количество сделок в скользящем окне по времени и текущую просадку. Аварийный останов приостанавливает торговлю, когда серии убытков или пороги просадки превышены, предотвращая разрушительные спирали. Механизм ограничения торговой активности ограничивает количество сделок, которые можно совершить в заданном временном окне, предотвращая переторговку в периоды волатильности. Когда условия ухудшаются ещё сильнее, Протокол восстановления снижает риск, приостанавливает торговлю на период принудительного ожидания и при необходимости ждёт восстановления средств счёта до определённого процента от пика перед возобновлением. Это создаёт многоуровневую систему аварийной защиты, которая проактивно защищает капитал и позволяет советнику возобновлять работу только при благоприятных условиях.


Результаты бэктеста

Бэктест проводился на таймфрейме M15 в течение примерно двух месяцев тестирования (с 1 октября 2025 года по 30 ноября 2025 года) со следующими настройками:

Переменная Входное значение
FastMAPeriod 20
SlowMAPeriod 160
InitialLotSize 1
LotMultiplier 5.2
MaxMartingaleSteps 13
RiskPercent 7.8
ATR_Period 69
ATR_SL_Factor 3.3
ATR_TP_Factor 9.8
UseEquityStop false
EquityStopPercentage 27.2
UseDailyLossLimit true
DailyLossPercent 12.7
UseMaxSpreadFilter true
MaxSpreadPoints 25
UseTrailingStop true
BreakEvenAtPips 500
TrailStartAtPips 600
TrailStepPips 100
MaxConsecutiveLosses 22
CircuitBreakerDD 115.5
MaxTradesPerHour 33
ThrottleWindowSeconds 21025
RecoveryRiskReduction 3.6
ResumeEquityPercent 135
ResumeAfterSeconds 845307


Заключение

Мы разработали комплексную многоуровневую структуру защиты счёта, которая объединяет динамическое управление уровнем риска, структурные механизмы защиты счёта, защиту исполнения сделок и системные аварийные предохранители. Эта архитектура сочетает расчёт лота на основе риска, отслеживание пикового значения средств счёта, контролируемые блокировки по просадке, проверку ордеров с учётом проскальзывания, аварийный останов и механизмы ограничения торговой активности, чтобы каждая сделка была одновременно разумно рассчитана по объёму и ответственно управлялась. Объединяя эти компоненты в единый защитный движок, система постоянно адаптируется к волатильности, отслеживает состояние средств счёта и автоматически вмешивается, когда торговые условия становятся неблагоприятными.

Эта многоуровневая модель защиты предоставляет трейдерам высокоустойчивую саморегулирующуюся среду, которая ставит сохранение капитала в приоритет, не отказываясь от стремления к высокой доходности. Вместо опоры на одну защитную функцию советник работает в согласованной сети предохранителей, которые уменьшают влияние эмоциональных решений, предотвращают катастрофические спирали убытков и обеспечивают долгосрочную устойчивость торговли. Это даёт трейдерам больше уверенности при работе на более высокие цели по прибыли, поскольку система спроектирована так, чтобы активно управлять риском, стабилизировать результаты и сохранять счёт на всех этапах рыночного поведения. 

Перевод с английского произведен MetaQuotes Ltd.
Оригинальная статья: https://www.mql5.com/en/articles/20449

Прикрепленные файлы |
GALEIT.mq5 (28.1 KB)
Последние комментарии | Перейти к обсуждению на форуме трейдеров (1)
Hemant Yadav
Hemant Yadav | 26 дек. 2025 в 06:49
Здравствуйте, сэр, как запустить торговлю по паре XAU/USD? Я проверил в тестере MT5, но сделки не открываются — может, программа работает некорректно? Пожалуйста, пришлите инструкции.
Особенности написания Пользовательских Индикаторов Особенности написания Пользовательских Индикаторов
Написание пользовательских индикаторов в торговой системе MetaTrader 4
Рыночные секреты Ларри Уильямса (Часть 4): Автоматизация краткосрочных свинговых максимумов и минимумов в MQL5 Рыночные секреты Ларри Уильямса (Часть 4): Автоматизация краткосрочных свинговых максимумов и минимумов в MQL5
Освойте автоматизацию краткосрочных свинговых паттернов Ларри Уильямса с помощью MQL5. В этом руководстве мы разработаем полностью настраиваемого советника (Expert Advisor, EA), использующего неслучайные рыночные структуры. Мы рассмотрим, как интегрировать надежное управление рисками и гибкую логику выхода, создав прочную основу для системной разработки и бэктестирования торговых стратегий.
Особенности написания экспертов Особенности написания экспертов
Написание и тестирование экспертов в торговой системе MetaTrader 4.
Рыночные секреты Ларри Уильямса (Часть 3): Проверка неслучайного поведения рынка средствами MQL5 Рыночные секреты Ларри Уильямса (Часть 3): Проверка неслучайного поведения рынка средствами MQL5
Исследуйте, действительно ли финансовые рынки случайны, воспроизводя эксперименты Ларри Уильямса по поведению рынка с помощью MQL5. В этой статье показано, как простые тесты на основе поведения цены могут выявлять статистические перекосы в поведении рынка с помощью советника (Expert Advisor, EA).