Встраивание торговой дисциплины в код (Часть 5): Контроль соблюдения риск-ограничений на уровне счёта в MQL5
Содержание
- Введение
- Концепция и архитектура системы
- Основные компоненты системы дисциплины
- Реализация на MQL5
- Тестирование
- Заключение
Введение
В торговле правила управления рисками редко являются проблемой. Параметры обычно ясны: фиксированный процент эквити на сделку, определенный стоп-лосс, тейк-профит, рассчитанный на основе той же модели, а также постоянное соотношение риска и прибыли. Реальная проблема возникает после исполнения. После открытия позиций их можно изменять, частично закрывать или открывать из нескольких источников, включая ручное открытие сделок и различные советники, работающие с разными символами. Со временем это приводит к расхождению между заданным риском и фактической экспозицией.
Это нарушение обычно проявляется в узнаваемых формах: позиции без стоп-лосса или тейк-профита, размеры лотов, превышающие установленный порог риска, или уровни тейк-профита, которые больше не соответствуют предполагаемой структуре риска и прибыли. В MetaTrader 5 отсутствует встроенный модуль, который бы постоянно проверял эти условия на уровне счета после размещения сделки.
Для решения этой проблемы риск необходимо рассматривать как набор обязательных условий, а не как статические правила, определенные при входе в сделку. В этой системе позиция считается корректной только тогда, когда одновременно выполняются все следующие условия: присутствует стоп-лосс, определен тейк-профит, экспозиция соответствует фиксированному проценту от эквити, а результирующая структура соответствует настроенному соотношению риска и прибыли. Любое отклонение от этих условий рассматривается как нарушение.
В этой статье представлен модуль контроля на уровне счета, который постоянно отслеживает все открытые позиции, оценивает их на соответствие этим условиям и применяет корректирующие действия при необходимости. Основное внимание уделяется не логике входа или генерации сигналов, а поддержанию структурной согласованности риска на протяжении всего жизненного цикла позиции.
Концепция и архитектура системы

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

Сделка считается соответствующей требованиям только в том случае, если выполняются следующие условия:
- Наличие корректного стоп-лосса,
- определен тейк-профит,
- риск рассчитывается как заранее определенный процент от эквити счета,
- и тейк-профит поддерживает постоянное соотношение риска и прибыли.
Эти условия не только проверяются, но и динамически пересчитываются на основе эквити счета и свойств символа, тем самым соответствие обеспечивается программно, а не просто предполагается в момент исполнения сделки. Они составляют основу системы и определяют все последующие решения.
Непрерывная оценка в динамичной среде
После установления этих критериев задача переходит к непрерывной оценке. В MetaTrader 5 позиции могут открываться, изменяться или частично закрываться, и их происхождение может быть различным. Это означает, что соответствие требованиям нельзя проверить один раз; его необходимо оценивать многократно. Поэтому система разработана для работы в непрерывном цикле, где каждая активная позиция периодически проверяется на соответствие заданным правилам.
Операционный цикл системы
Это приводит к операционной модели:
Система начинает с обнаружения всех активных позиций на уровне счета. Она не делает различий между ручными и автоматизированными сделками, гарантируя, что каждая позиция подчиняется одним и тем же условиям. Каждая обнаруженная позиция оценивается на соответствие критериям. Это включает проверку наличия уровней стоп-лосс и тейк-профит, а также подтверждение того, что величина риск-экспозиции соответствует заданному проценту эквити счета. Если обнаружено нарушение, система применяет корректирующие меры. В зависимости от конфигурации это может включать в себя установку отсутствующих уровней стоп-лосса и тейк-профита, исправление несоответствующих параметров или корректировку размера позиции для восстановления приемлемого уровня риска. После принудительного применения изменений система обновляет как внутреннее состояние, так и визуальный вывод. Это включает в себя обновление информации на графике, ведение учета оцененных позиций и отслеживание частоты необходимых корректировок.
Поведение системы и режимы управления
Ключевым аспектом архитектуры является операционная гибкость. Не все торговые сценарии требуют немедленного вмешательства. По этой причине система поддерживает несколько режимов работы. В пассивном режиме позиции оцениваются, а нарушения регистрируются без внесения изменений. В режиме сопровождения система выделяет проблемы и подготавливает корректирующие действия с уведомлениями. В строгом режиме правила применяются автоматически, и все нарушения исправляются немедленно. Такой многоуровневый подход позволяет одной и той же системе функционировать как инструмент обучения и как строгий контроллер исполнения.
От поведения к структуре
Такая архитектура переносит ответственность с решений в моменте на структурированный процесс. Вместо того чтобы полагаться на ручное исполнение под давлением, система постоянно согласовывает позиции с заранее настроенными параметрами риска. Таким образом, дисциплина превращается из изменчивого поведения в постоянное свойство торговой среды.
Основные компоненты системы дисциплины
Для дисциплинированной торговой среды требуется нечто большее, чем просто заранее определенные правила. В этом разделе описываются основные компоненты системы и их роль в поддержании согласованности и контроля. Эти компоненты определяют, как дисциплина автоматически поддерживается на различных графиках, торговых инструментах и типах сделок.

1. Монитор сделок
Монитор сделок в реальном времени отслеживает все позиции и отложенные ордера независимо от источника исполнения, проверяет соблюдение правил риска и отмечает нарушения. Без непрерывного мониторинга торговые лимиты могут не соблюдаться последовательно.
2. Оценщик рисков
После сбора данных оценщик рисков обрабатывает информацию. Этот компонент оценивает, соответствует ли сделка настройкам риска, основанным на эквити счета, проверяет корректные уровни стоп-лосса (SL) и тейк-профита (TP) и обеспечивает соответствие соотношения риска и прибыли стратегии. Он определяет, соответствует ли сделка определенным правилам. Если нет, сделка блокируется или корректируется.
3. Модуль принудительного применения правил
Модуль принудительного применения правил использует основанные на правилах действия. Он обеспечивает соблюдение лимитов, при необходимости удаляя лишние отложенные ордера и закрывая или корректируя позиции, нарушающие ограничения по риску. Важно отметить, что он делает это, не затрагивая сделки, уже находящиеся в пределах допустимого лимита. Это обеспечивает последовательное соблюдение лимитов без влияния на соответствующие требованиям позиции.
4. Дашборд и оповещения
Для эффективного мониторинга нужна прозрачность состояния системы. Дашборд предоставляет обзор состояния системы в реальном времени — сколько сделок уже использовано, к каким лимитам приближаются и какие правила активны. Оповещения, как на экране, так и в виде push-уведомлений, мгновенно сообщают о критических событиях. Эта обратная связь обеспечивает непрерывную прозрачность без необходимости постоянного мониторинга.
5. Настройка и контроль торговых сессий
Система включает в себя настраиваемые параметры для торгового дня, времени начала торговой сессии, дневных лимитов входа, процентных значений риска и разрешенных символов. Это позволяет адаптироваться к различным торговым стратегиям и рабочим сценариям, а не навязывать жесткие параметры. Она также точно определяет, что считается “сделкой” и когда начинается день. Это устраняет двусмысленность в обеспечении соблюдения правил.
Реализация на MQL5
После четкого определения структуры системы следующим шагом является преобразование этих концепций в работающую программу на MQL5. Реализация следует той же логике, введенной ранее: обнаружение сделок, оценка соответствия, обеспечение соблюдения правил и передача результатов. Каждая часть этого процесса сопоставляется с конкретными функциями MQL5 и обработчиками событий, обеспечивая непрерывную и надежную работу системы на уровне счета.
Инициализация (OnInit()): Настройка системы
Инициализация системы происходит в OnInit(), где устанавливаются все основные параметры. Это включает в себя режим работы по умолчанию, процент риска и соотношение риска и прибыли. Эти значения определяют базовый уровень для оценки сделок.
//+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+ int OnInit() { //--- Validate timer if(InpTimerSeconds < 1) { Print("ERROR: InpTimerSeconds must be >= 1. Using 1."); EventSetTimer(1); } else EventSetTimer(InpTimerSeconds); //--- Copy inputs to globals g_Mode = InpDefaultMode; g_RiskPercent = InpRiskPercent; g_RRatio = InpRRatio; //--- Create panel DeletePanel(); CreatePanel(); UpdatePanel(); Print("Discipline Engine started. Mode=",EnumToString(g_Mode), ", Risk=",DoubleToString(g_RiskPercent,2),"%, R:R=",DoubleToString(g_RRatio,2)); return INIT_SUCCEEDED; }
Система активирует таймер с помощью функции EventSetTimer(). Это гарантирует, что мониторинг происходит через фиксированные интервалы, независимо от тиков. Это позволяет избежать зависимости от функции OnTick(), которая зависит от движения цены. На рынках с низкой активностью сделки могут оставаться неоцененными. Использование таймера обеспечивает стабильное исполнение независимо от рыночных условий.
Кроме того, на этапе инициализации создается интерфейс на графике с помощью функции CreatePanel(). Панель выступает как слой оперативного управления и контроля параметров системы в реальном времени.
Постоянный мониторинг (OnTimer()): Цикл исполнения
Основной цикл исполнения выполняется в OnTimer(). Эта функция исполняется через фиксированные интервалы. Каждый цикл сканирует все активные позиции с помощью PositionsTotal() и извлекает тикеты с помощью PositionGetTicket(). После функции PositionSelectByTicket() система извлекает ключевые параметры: символ, размер лота, цена входа, SL, TP и тип позиции. Для извлечения используются функции PositionGetDouble() и PositionGetInteger(). На этом этапе система выполняет структурную проверку. Проверка рисков и соотношений осуществляется в ходе расчетов, связанных с обеспечением исполнения правил.
//+------------------------------------------------------------------+ //| Timer function | //+------------------------------------------------------------------+ void OnTimer() { ulong activeTickets[]; ArrayResize(activeTickets, 0); for(int i = PositionsTotal() - 1; i >= 0; i--) { ulong ticket = PositionGetTicket(i); if(!PositionSelectByTicket(ticket)) continue; string symbol = PositionGetString(POSITION_SYMBOL); double lot = PositionGetDouble(POSITION_VOLUME); double openPrice = PositionGetDouble(POSITION_PRICE_OPEN); double sl = PositionGetDouble(POSITION_SL); double tp = PositionGetDouble(POSITION_TP); ENUM_POSITION_TYPE type = (ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE); //--- store active ticket int idx = ArraySize(activeTickets); ArrayResize(activeTickets, idx + 1); activeTickets[idx] = ticket; double requiredSLDist = CalculateRequiredSLDistance(symbol, lot, g_RiskPercent); if(requiredSLDist <= 0.0) { Print("Warning: cannot calculate risk for ", symbol, " ticket ", ticket); continue; } if(g_Mode != MODE_PASSIVE) AdjustLotSize(ticket, symbol, type, lot, openPrice, requiredSLDist); //--- refresh position data if(PositionSelectByTicket(ticket)) { lot = PositionGetDouble(POSITION_VOLUME); sl = PositionGetDouble(POSITION_SL); tp = PositionGetDouble(POSITION_TP); } //--- compliance check bool isCompliant = true; string violationMsg = ""; if(sl == 0.0) { isCompliant = false; violationMsg = "Missing SL"; } else if(tp == 0.0) { isCompliant = false; violationMsg = "Missing TP"; } else { double currentSLDist = MathAbs(openPrice - sl); double point = SymbolInfoDouble(symbol, SYMBOL_POINT); if(MathAbs(currentSLDist - requiredSLDist) > point * 0.5) { isCompliant = false; violationMsg = StringFormat("Risk mismatch: current=%.5f required=%.5f", currentSLDist, requiredSLDist); } } g_TotalTradesChecked++; //--- violation handling if(!isCompliant) { Print("Violation on ticket ", ticket, ": ", violationMsg); if(g_Mode != MODE_PASSIVE) { bool corrected = EnforceSLTP(ticket, symbol, type, lot, openPrice, requiredSLDist, g_RRatio); if(corrected) { g_ViolationsCorrected++; if(g_Mode == MODE_ASSISTED) { Alert(StringFormat("Trade %I64u: %s. Corrected.", ticket, violationMsg)); LogAction(ticket, violationMsg + " corrected (assisted)"); } else if(g_Mode == MODE_STRICT) { LogAction(ticket, violationMsg + " corrected (strict)"); } } else { LogAction(ticket, violationMsg + " correction failed"); } } else { LogAction(ticket, violationMsg + " (passive, no action)"); } } //--- draw SL/TP lines if(symbol == _Symbol && PositionSelectByTicket(ticket)) { sl = PositionGetDouble(POSITION_SL); tp = PositionGetDouble(POSITION_TP); DrawSLTPLines(ticket, sl, tp); } } CleanupLines(activeTickets); UpdatePanel(); }
Такой подход оценивает все позиции независимо от их происхождения. Независимо от того, совершена ли сделка вручную или с помощью другого советника, к ней применяется единый подход. Это исключает несоответствия, возникающие при независимой работе различных стратегий на одном и том же счете. Мониторинг осуществляется непрерывно, а не однократно, при этом позиции переоцениваются на каждом цикле таймера. Это позволяет системе реагировать на изменения, такие как ручные корректировки, частичное закрытие или корректировки со стороны брокера, обеспечивая соблюдение требований на протяжении всего жизненного цикла позиции.
Оценка соответствия сделки правилам
После сбора данных о позиции система оценивает каждую сделку на соответствие заранее установленным правилам. Оценка начинается со структурных проверок: проверки наличия значений стоп-лосса и тейк-профита. Если какое-либо из этих значений отсутствует, позиция немедленно помечается как не соответствующая правилам. Если оба значения присутствуют, система переходит к расчету необходимого расстояния стоп-лосса с помощью функции CalculateRequiredSLDistance().
//+------------------------------------------------------------------+ //| Calculate required SL distance in price units | //+------------------------------------------------------------------+ double CalculateRequiredSLDistance(string symbol,double lot,double riskPercent) { double equity = AccountInfoDouble(ACCOUNT_EQUITY); if(equity<=0.0 || riskPercent<=0.0 || lot<=0.0) return 0.0; double riskAmount = equity*riskPercent/100.0; double tickValue = SymbolInfoDouble(symbol,SYMBOL_TRADE_TICK_VALUE); double tickSize = SymbolInfoDouble(symbol,SYMBOL_TRADE_TICK_SIZE); if(tickValue<=0.0 || tickSize<=0.0) return 0.0; double riskTicks = riskAmount/(tickValue*lot); if(riskTicks<=0.0) return 0.0; double slDistance = riskTicks*tickSize; long stopsLevel = SymbolInfoInteger(symbol,SYMBOL_TRADE_STOPS_LEVEL); if(stopsLevel>0) { double minStop = stopsLevel*tickSize; if(slDistance < minStop) slDistance = minStop; } return slDistance; }
Эта функция преобразует риск, выраженный в процентах от эквити счета, в ценовое расстояние. Она использует такие параметры, как эквити счета (AccountInfoDouble(ACCOUNT_EQUITY)), значение тика символа (SYMBOL_TRADE_TICK_VALUE) и размер тика (SYMBOL_TRADE_TICK_SIZE). Это обеспечивает согласованное измерение риска для разных инструментов, независимо от их ценовой структуры.
Далее рассчитанное расстояние до стоп-лосса сравнивается с фактическим расстоянием между ценой входа и существующим стоп-лоссом. Из-за точности вычислений с плавающей запятой и ограничений ценообразования брокера система не требует точного совпадения. Вместо этого она применяет пороговое значение допуска, основанное на стоимости пункта для символа. Это предотвращает ненужные изменения, вызванные незначительными различиями в округлении, и позволяет избежать избыточных торговых запросов.
Обработка ограничений брокера и точности
Реализация должна учитывать ограничения брокера. У каждого символа есть минимальная стоп-дистанция, определяемая SYMBOL_TRADE_STOPS_LEVEL. Если рассчитанный стоп-лосс или тейк-профит слишком близок к текущей рыночной цене, брокер отклонит изменение. Для обработки таких случаев система проверяет все рассчитанные уровни перед отправкой запроса. Если требуемое расстояние стоп-лосса меньше минимально допустимого, оно корректируется соответствующим образом. Это гарантирует, что все действия по обеспечению соблюдения правил остаются действительными и выполнимыми.
Для корректных сравнений необходимо учитывать точность котировок и вычислений. Цены нормализуются с использованием размера символа в пунктах, а сравнения проводятся в пределах заданного диапазона допустимых отклонений. Это позволяет избежать повторных изменений, вызванных незначительными расхождениями, которые в противном случае могли бы привести к ненужному количеству торговых запросов и потенциальным задержкам исполнения.
Логика регулировки размера лота
Перед применением корректировок стоп-лосса и тейк-профита система выполняет критическую проверку безопасности с помощью функции AdjustLotSize(). Этот шаг гарантирует, что сам размер позиции не нарушает ограничения риска. Функция рассчитывает максимально допустимый размер лота на основе настройки процента риска и минимального расстояния до стоп-лосса. Если текущий размер лота превышает это значение, система уменьшает уровень риска путем частичного закрытия позиции с помощью обратной сделки через OrderSend(). Результат торгового запроса должен проверяться для обеспечения успешного исполнения и обработки потенциальных ошибок.
//+------------------------------------------------------------------+ //| Adjust oversized lot (partial close) | //+------------------------------------------------------------------+ void AdjustLotSize(ulong ticket,string symbol,ENUM_POSITION_TYPE type, double currentLot,double openPrice,double requiredSLDist) { long stopsLevel = SymbolInfoInteger(symbol,SYMBOL_TRADE_STOPS_LEVEL); if(stopsLevel <= 0) return; double tickSize = SymbolInfoDouble(symbol,SYMBOL_TRADE_TICK_SIZE); double minStopPrice = stopsLevel*tickSize; if(requiredSLDist >= minStopPrice) return; double equity = AccountInfoDouble(ACCOUNT_EQUITY); double riskAmount = equity*g_RiskPercent/100.0; double tickValue = SymbolInfoDouble(symbol,SYMBOL_TRADE_TICK_VALUE); if(tickValue <= 0.0) return; double maxSafeLot = riskAmount/(tickValue*(minStopPrice/tickSize)); maxSafeLot = NormalizeVolumeByStep(symbol,maxSafeLot); if(currentLot > maxSafeLot + SymbolInfoDouble(symbol,SYMBOL_VOLUME_STEP)*0.5) { double reduceAmount = currentLot - maxSafeLot; reduceAmount = NormalizeVolumeByStep(symbol,reduceAmount); if(reduceAmount <= 0.0) return; Print("Lot size too large (",DoubleToString(currentLot,2), "). Reducing by ",DoubleToString(reduceAmount,2), " to ",DoubleToString(maxSafeLot,2)," for ticket ",ticket); MqlTradeRequest req = {}; MqlTradeResult res = {}; req.action = TRADE_ACTION_DEAL; req.symbol = symbol; req.volume = reduceAmount; req.deviation = 10; req.position = ticket; req.magic = (ulong)PositionGetInteger(POSITION_MAGIC); req.type_filling = ORDER_FILLING_IOC; if(type == POSITION_TYPE_BUY) req.type = ORDER_TYPE_SELL; else req.type = ORDER_TYPE_BUY; req.price = SymbolInfoDouble(symbol,(type==POSITION_TYPE_BUY)?SYMBOL_BID:SYMBOL_ASK); if(OrderSend(req,res)) { if(res.retcode == TRADE_RETCODE_DONE) { LogAction(ticket,StringFormat("Reduced lot from %.2f to %.2f",currentLot,maxSafeLot)); ObjectSetString(0,g_LblLastAct,OBJPROP_TEXT, StringFormat("Last: Ticket %I64u lot reduced",ticket)); } else LogAction(ticket,StringFormat("Lot reduction failed, retcode %d",res.retcode)); } else LogAction(ticket,"Lot reduction OrderSend failed"); } }
Этот шаг важен, поскольку риск представляет собой комбинацию расстояния стоп-лосса и размера лота. Даже при наличии корректного стоп-лосса, слишком большая позиция может привести к чрезмерному уровню риска. Сначала корректируется объем позиции, чтобы дальнейшая настройка стоп-лосса соответствовала целевому уровню риска.
Обеспечение соблюдения стоп-лосса и тейк-профита
После подтверждения размера позиции система приступает к обеспечению соблюдения надлежащих уровней стоп-лосса и тейк-профита с помощью функции EnforceSLTP().
//+------------------------------------------------------------------+ //| Enforce correct SL and TP for a position | //+------------------------------------------------------------------+ bool EnforceSLTP(ulong ticket,string symbol,ENUM_POSITION_TYPE type, double lot,double openPrice,double requiredSLDist,double rr) { if(requiredSLDist <= 0.0 || rr <= 0.0) return false; double newSL = 0.0; double newTP = 0.0; if(type == POSITION_TYPE_BUY) { newSL = openPrice - requiredSLDist; newTP = openPrice + requiredSLDist*rr; } else { newSL = openPrice + requiredSLDist; newTP = openPrice - requiredSLDist*rr; } newSL = NormalizePriceBySymbol(symbol,newSL); newTP = NormalizePriceBySymbol(symbol,newTP); long stopsLevel = SymbolInfoInteger(symbol,SYMBOL_TRADE_STOPS_LEVEL); double point = SymbolInfoDouble(symbol,SYMBOL_POINT); double minStopDist = stopsLevel*point; if(MathAbs(openPrice-newSL) < minStopDist || MathAbs(openPrice-newTP) < minStopDist) { Print("SL/TP too close to market (minStop=",DoubleToString(minStopDist,_Digits), "), correction skipped for ticket ",ticket); return false; } MqlTradeRequest req = {}; MqlTradeResult res = {}; req.action = TRADE_ACTION_SLTP; req.symbol = symbol; req.position = ticket; req.sl = newSL; req.tp = newTP; if(OrderSend(req,res) && res.retcode == TRADE_RETCODE_DONE) { LogAction(ticket,StringFormat("SL/TP set: SL=%.5f, TP=%.5f",newSL,newTP)); ObjectSetString(0,g_LblLastAct,OBJPROP_TEXT, StringFormat("Last: Ticket %I64u SL/TP corrected",ticket)); return true; } Print("Modify failed for ticket ",ticket,", retcode=",res.retcode); return false; }
Эта функция рассчитывает новые цены стоп-лосса и тейк-профита на основе цены входа, необходимого расстояния стоп-лосса и настроенного соотношения риска и прибыли. Затем она сравнивает эти значения с текущими уровнями, чтобы определить, необходима ли корректировка. Если требуется коррекция, формируется запрос на сделку с использованием функции TRADE_ACTION_SLTP и отправляется через OrderSend(). Перед исполнением система проверяет, соответствуют ли все значения требованиям брокера и не нарушают ли ограничения минимального расстояния. Этот процесс гарантирует, что каждая позиция имеет согласованную структуру: контролируемый риск, определенные точки выхода и предсказуемый профиль прибыли.
Управление поведением на основе режимов
Поведение системы контролируется посредством взаимодействия с пользователем, обрабатываемого в функции OnChartEvent(). Это позволяет трейдеру переключаться между режимами с помощью кнопок на графике без перезапуска советника.
//+------------------------------------------------------------------+ //| Chart event handler | //+------------------------------------------------------------------+ void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam) { if(id != CHARTEVENT_OBJECT_CLICK) return; //--- MODE CONTROL if(sparam == g_BtnPassive) { g_Mode = MODE_PASSIVE; UpdatePanel(); Alert("Mode set to PASSIVE"); Print("Mode changed to PASSIVE"); } else if(sparam == g_BtnAssisted) { g_Mode = MODE_ASSISTED; UpdatePanel(); Alert("Mode set to ASSISTED"); Print("Mode changed to ASSISTED"); } else if(sparam == g_BtnStrict) { g_Mode = MODE_STRICT; UpdatePanel(); Alert("Mode set to STRICT"); Print("Mode changed to STRICT"); } //--- RISK CONTROL else if(sparam == g_BtnRiskUp) { g_RiskPercent = MathMin(10.0, g_RiskPercent + 0.1); UpdatePanel(); Alert(StringFormat("Risk percent set to %.2f%%", g_RiskPercent)); PrintFormat("Risk changed to %.2f%%", g_RiskPercent); } else if(sparam == g_BtnRiskDown) { g_RiskPercent = MathMax(0.1, g_RiskPercent - 0.1); UpdatePanel(); Alert(StringFormat("Risk percent set to %.2f%%", g_RiskPercent)); PrintFormat("Risk changed to %.2f%%", g_RiskPercent); } //--- R:R CONTROL else if(sparam == g_BtnRRUp) { g_RRatio = MathMin(10.0, g_RRatio + 0.5); UpdatePanel(); Alert(StringFormat("R:R ratio set to %.1f", g_RRatio)); PrintFormat("R:R changed to %.1f", g_RRatio); } else if(sparam == g_BtnRRDown) { g_RRatio = MathMax(0.5, g_RRatio - 0.5); UpdatePanel(); Alert(StringFormat("R:R ratio set to %.1f", g_RRatio)); PrintFormat("R:R changed to %.1f", g_RRatio); } }
В пассивном режиме система оценивает сделки и логирует нарушения без внесения каких-либо изменений. В режиме сопровождения она применяет коррекции, одновременно выдавая оповещения для информирования трейдера. В строгом режиме все нарушения исправляются автоматически, и система работает как полностью автономный механизм обеспечения соблюдения правил. Такая гибкость позволяет системе адаптироваться к различным этапам развития торговли, от наблюдения и обучения до строгого соблюдения правил.
Визуальная обратная связь и отслеживание состояния
Система предоставляет визуальную обратную связь через панель на графике с помощью функции UpdatePanel(). Эта панель отображает ключевую информацию, такую как текущий режим, процент риска, соотношение риска и прибыли, эквити счета и рассчитанный показатель дисциплины.
//+------------------------------------------------------------------+ //| Update on-chart information panel | //+------------------------------------------------------------------+ void UpdatePanel() { string modeStr = EnumToString(g_Mode); ObjectSetString(0, g_LblMode, OBJPROP_TEXT, "Mode: " + modeStr); ObjectSetString(0, g_LblRisk, OBJPROP_TEXT, StringFormat("Risk: %.2f%%", g_RiskPercent)); ObjectSetString(0, g_LblRR, OBJPROP_TEXT, StringFormat("R:R: %.1f", g_RRatio)); double equity = AccountInfoDouble(ACCOUNT_EQUITY); ObjectSetString(0, g_LblEquity, OBJPROP_TEXT, StringFormat("Equity: %.2f", equity)); }
Показатель дисциплины — это измеримый индикатор соответствия. Он рассчитывается как доля сделок, требующих корректировки, от общего числа оцененных сделок. Это превращает дисциплину в количественный показатель, а не в субъективную оценку.
//+------------------------------------------------------------------+ //| Draw horizontal lines for SL and TP | //+------------------------------------------------------------------+ void DrawSLTPLines(ulong ticket,double sl,double tp) { string slName = g_Prefix + "SL_" + (string)ticket; string tpName = g_Prefix + "TP_" + (string)ticket; ObjectDelete(0,slName); ObjectDelete(0,tpName); if(sl != 0.0) { if(ObjectCreate(0,slName,OBJ_HLINE,0,0,sl)) { ObjectSetInteger(0,slName,OBJPROP_COLOR,clrRed); ObjectSetInteger(0,slName,OBJPROP_WIDTH,1); ObjectSetInteger(0,slName,OBJPROP_STYLE,STYLE_DASH); ObjectSetString(0,slName,OBJPROP_TOOLTIP,"SL Ticket " + (string)ticket); ObjectSetString(0,slName,OBJPROP_TEXT,"SL " + (string)ticket); } } if(tp != 0.0) { if(ObjectCreate(0,tpName,OBJ_HLINE,0,0,tp)) { ObjectSetInteger(0,tpName,OBJPROP_COLOR,clrLimeGreen); ObjectSetInteger(0,tpName,OBJPROP_WIDTH,1); ObjectSetInteger(0,tpName,OBJPROP_STYLE,STYLE_DASH); ObjectSetString(0,tpName,OBJPROP_TOOLTIP,"TP Ticket " + (string)ticket); ObjectSetString(0,tpName,OBJPROP_TEXT,"TP " + (string)ticket); } } }
В дополнение к панели, система визуально отображает уровни стоп-лосса и тейк-профита с помощью функции DrawSLTPLines(). Эти линии динамически обновляются и удаляются при закрытии позиций с помощью функции CleanupLines(), гарантируя, что график всегда точно отражает текущее состояние.
//+------------------------------------------------------------------+ //| Remove lines for positions that are no longer active | //+------------------------------------------------------------------+ void CleanupLines(ulong &activeTickets[]) { int total = ObjectsTotal(0,-1,-1); for(int i=total-1; i>=0; i--) { string name = ObjectName(0,i); if(StringFind(name,g_Prefix+"SL_")==0 || StringFind(name,g_Prefix+"TP_")==0) { int firstUnderscore = StringFind(name,"_"); int secondUnderscore = StringFind(name,"_",firstUnderscore+1); if(secondUnderscore > 0) { ulong ticket = (ulong)StringToInteger(StringSubstr(name,secondUnderscore+1)); bool found = false; for(int j=0; j<ArraySize(activeTickets); j++) { if(activeTickets[j] == ticket) { found = true; break; } } if(!found) ObjectDelete(0,name); } } } }
Логирование и отслеживаемость
Каждое действие, выполняемое системой, логируется с помощью функции LogAction(). Это включает в себя обнаруженные нарушения, примененные корректировки и любые неудачные операции.
//+------------------------------------------------------------------+ //| Log action to Experts tab | //+------------------------------------------------------------------+ void LogAction(ulong ticket,string msg) { Print(StringFormat("[%I64u] %s",ticket,msg)); }
Этот механизм логирования обеспечивает отслеживаемость. Это позволяет трейдеру анализировать поведение системы, проверять корректность применения правил и выявлять любые потенциальные проблемы во время тестирования или работы в реальных условиях.
Управление жизненным циклом и стабильность
Очистка системы осуществляется в OnDeinit(). Она включает в себя остановку таймера с помощью EventKillTimer(), удаление всех объектов панели и очистку любых графических элементов, созданных во время исполнения.
//+------------------------------------------------------------------+ //| Expert deinitialization function | //+------------------------------------------------------------------+ void OnDeinit(const int reason) { EventKillTimer(); DeletePanel(); DeleteAllLines(); }
Это предотвращает появление лишних объектов и обеспечивает чистоту среды, особенно при многократной установке и удалении советника.
Тестирование
Тестирование показало, что система работает так, как задумано в реальных рыночных условиях. Тестирование включало мониторинг позиций, выявление нарушений и применение мер обеспечения соблюдения без вмешательства в нормальную торговую деятельность. Оно проводилось на графиках в реальном времени по нескольким символам и таймфреймам. После подключения советника наблюдалось его поведение от инициализации до активного управления сделками в различных сценариях.

На изображении ниже показана система сразу после инициализации. На этом этапе панель активна и отображает настроенные параметры, включая режим, процент риска и соотношение риска и прибыли. Для этого теста система была установлена в СТРОГИЙ режим, в котором все нарушения исправляются автоматически. Эквити счета отображается в реальном времени вместе с показателем дисциплины, который отражает текущее состояние соответствия системы требованиям. Затем в контролируемых условиях были открыты сделки для проверки логики системы.
Первый тест был сосредоточен на отсутствующих уровнях стоп-лосса и тейк-профита. Позиции открывались вручную без защитных уровней. В течение следующего цикла таймера система обнаружила эти позиции и немедленно применила стоп-лосс и тейк-профит на основе настроенных параметров риска. Изменения отразились на графике в виде горизонтальных уровней, проведенных на уровне установленных цен.
Второй тест был направлен на выявление несоответствия рисков. Позиции открывались намеренно с размерами лотов, превышающими допустимый уровень риска относительно эквити счета. В этом случае система сначала уменьшала размер позиции, а затем корректировала стоп-лосс. После того, как уровень риска приводился в соответствие с заданным процентом риска, система пересчитывала и применяла корректные уровни стоп-лосса и тейк-профита. Это подтвердило, что система рассматривает риск как комбинацию размера позиции и ценового расстояния, а не обрабатывает их по отдельности.
Третий тест подтвердил возможность непрерывного мониторинга. После корректировки позиций системой уровни стоп-лосса были удалены вручную, а значения тейк-профита были изменены. В каждом случае система обнаруживала изменения в последующих циклах таймера и автоматически восстанавливала соответствие требованиям. Это показывает, что контроль осуществляется непрерывно на протяжении всего жизненного цикла позиции.

На втором изображении показана работа системы в динамических условиях. Движение рынка продолжается, в то время как позиции изменяются в реальном времени. Система поддерживает согласованную структуру по всем сделкам. Панель постоянно обновляется, и принудительно применяемые уровни стоп-лосса и тейк-профита остаются видимыми. Важное наблюдение из тестирования заключается в том, что система работает независимо от происхождения сделки. Независимо от того, были ли сделки открыты вручную или сгенерированы с помощью другой логики, они оценивались и корректировались по одним и тем же правилам. Это демонстрирует, что соблюдение дисциплины обеспечивается на уровне счета. Все изменения в сделках соответствовали ограничениям брокера, включая минимальные расстояния стоп-лосса и точность котировок. Исполнение на основе таймера оказалось надежным, обеспечивая непрерывность мониторинга даже в периоды низкой активности рынка.
В целом, тестирование показывает, что система работает в соответствии с замыслом. Она последовательно обнаруживает нарушения, применяет корректировки и поддерживает соответствие требованиям по всем активным позициям без необходимости ручного вмешательства.
Заключение
В данной статье представлена система, которая преобразует управление рисками из однократной настройки в непрерывный процесс обеспечения соблюдения правил. Вместо того чтобы полагаться на дисциплину в момент исполнения сделки, этот подход вводит структурированный цикл — выявление позиций, оценка соответствия, принудительное внесение корректировок и обновление состояния системы — который последовательно применяется ко всему счету.
Реализация на MQL5 напрямую следует этой структуре. Цикл, управляемый таймером, гарантирует, что все позиции проверяются через фиксированные интервалы, независимо от активности рынка. Риск преобразуется из процента от эквити в точные ценовые уровни, что обеспечивает последовательную проверку для разных символов. При выявлении нарушений применяются корректирующие действия с помощью двух механизмов: обеспечение соблюдения уровней стоп-лосса и тейк-профита, а также уменьшение размера позиции, когда риск превышает допустимые пределы. Вспомогательные компоненты, такие как логирование, визуализация графиков и панель управления, обеспечивают прозрачность и интерактивность.
В результате получается готовый к использованию модуль контроля дисциплины на уровне счета, который работает независимо от способа открытия сделок. После установки на графике он непрерывно оценивает каждую активную позицию и обеспечивает соответствие определенным параметрам риска. Его поведение можно наблюдать в реальном времени: применяются недостающие уровни защиты, снижается чрезмерный уровень риска, действия логируются, а обновленные уровни отображаются на интерфейсе графика.
Такой подход исключает зависимость от дисциплины, действующей во время исполнения, за счет внедрения непрерывной проверки на уровне счета. Вместо того чтобы предполагать корректный риск при входе в сделку, система многократно проверяет и восстанавливает соответствие требованиям на протяжении всего жизненного цикла позиции. В результате каждая позиция остается в соответствии с определенными параметрами риска, обеспечивая согласованную структуру риска, соблюдение которой можно гарантировать на уровне всего счета.
Перевод с английского произведен MetaQuotes Ltd.
Оригинальная статья: https://www.mql5.com/en/articles/21995
Предупреждение: все права на данные материалы принадлежат MetaQuotes Ltd. Полная или частичная перепечатка запрещена.
Данная статья написана пользователем сайта и отражает его личную точку зрения. Компания MetaQuotes Ltd не несет ответственности за достоверность представленной информации, а также за возможные последствия использования описанных решений, стратегий или рекомендаций.
Особенности написания Пользовательских Индикаторов
Создание пользовательских индикаторов в MQL5 (Часть 7): Гибридные рыночные профили Time Price Opportunity (TPO) для анализа сессий
Встраивание торговой дисциплины в код (Часть 4): Принудительный контроль торговых сессий и блокировка торговли на новостях в MQL5
- Бесплатные приложения для трейдинга
- 8 000+ сигналов для копирования
- Экономические новости для анализа финансовых рынков
Вы принимаете политику сайта и условия использования