Сеточный советник на дивергенции RSI с риск-менеджером в MQL5
Введение
Сеточная торговля — одна из первых стратегий, с которой знакомится начинающий алготрейдер. Логика привлекает своей простотой: цена всегда куда-то движется, а значит, если открыть ордера с фиксированным шагом в обе стороны, рано или поздно часть из них закроется в плюс. Проблема в том, что именно это "рано или поздно" уничтожает депозит быстрее, чем кажется.
Классическая сетка без фильтра и без риск-менеджера — это замаскированная мартингейл-ловушка. Она хорошо выглядит на бэктесте до первого сильного тренда, а потом сметает счёт за несколько часов. Опытные трейдеры знают это. Но большинство новичков проходят через этот урок на собственные деньги.
В статье показано, как построить сетку с повышенной устойчивостью. Три компонента превращают простой сеточный советник в устойчивую систему:
- фильтр входа на основе дивергенции RSI: сетка открывается только тогда, когда есть статистически обоснованный сигнал разворота, а не в произвольный момент;
- встроенный риск-менеджер: он на каждом тике сверяет equity с лимитами и немедленно блокирует торговлю при их приближении;
- жёсткое ограничение уровней: советник не может открыть больше заданного числа ордеров в одной сетке, что само по себе ограничивает максимальный убыток рассчитываемой величиной.
Весь код написан на чистом MQL5 без внешних библиотек. Один файл, один Magic Number, один параметр лота — именно то, что нужно начинающему разработчику. При этом архитектура спроектирована так, чтобы её было легко расширять: класс CRiskManager изолирован, фильтр входа отделён от логики управления, а вся торговля идёт через стандартный CTrade из Trade\Trade.mqh.
В статье последовательно рассматривается:
- почему классическая сетка разрушает депозит и как это исправить;
- дивергенция RSI как фильтр входа — математика и реализация;
- класс CRiskManager — защита от дневных потерь и просадки;
- логика сетки — открытие, добавление уровней, закрытие;
- полный код советника GridSurvivor с подробными комментариями;
- бэктестирование в Strategy Tester MetaTrader 5;
- сравнительное тестирование с классическими подходами (с кодами всех конфигураций);
- журналирование и мониторинг работы в реальном времени;
- параметры для старта и рекомендации по тестированию;
- типичные ошибки начинающих и направления расширения системы.
Почему классическая сетка уничтожает депозит
Стандартный сеточный советник работает так: при каждом движении цены на N пунктов против открытой позиции открывается новый ордер в том же направлении. Логика проста: усреднение снизит цену входа, и при откате рынок закроет всю сетку в плюс. Это действительно работает — на боковом рынке.
Проблема возникает при трендовом движении. Если рынок идёт против сетки 300–500 пунктов, советник открывает 5–10 ордеров с нарастающим суммарным убытком. При добавлении мартингейла (удвоение лота на каждом уровне) даже 200 пунктов движения достаточно, чтобы счёт потерял 30–50% баланса за одну сессию. Именно этим объясняется статистика MQL5 Market: большинство сеточных советников показывают красивый рост в течение 3–6 месяцев, а затем обнуляют счёт за один день.
Математика этого эффекта выглядит так. Допустим, сетка с шагом 200 пунктов, начальный лот 0.01, мартингейл с коэффициентом 2. При движении на 1000 пунктов против позиции открываются 5 ордеров с лотами 0.01, 0.02, 0.04, 0.08, 0.16. Суммарный лот сетки — 0.31. На EURUSD при движении 1000 пунктов плавающий убыток составит примерно 0.31 × 100 000 × 0.01 = 310 USD. На счёте 1000 USD это уже 31% просадка. Ещё 200 пунктов в ту же сторону — открывается шестой ордер 0.32 лот, суммарный лот удваивается до 0.63, и просадка переваливает за 50%. Маржинальный звонок наступает раньше, чем рынок успевает развернуться.
Три системных изменения радикально повышают выживаемость.
Первое — фильтр входа. Сетка должна открываться только при наличии сигнала разворота, а не в случайный момент. Дивергенция RSI — один из наиболее надёжных ранних признаков смены тренда. Когда цена формирует новый минимум, а RSI — более высокий минимум, это бычья дивергенция: медвежий импульс ослабевает. Открывать BUY-сетку в этот момент статистически оправдано: вероятность отката в ближайшие 5–20 баров заметно выше базовой.
Второе — жёсткое ограничение уровней. Максимум 3–5 ордеров в одной сетке. Это превращает теоретически бесконечный убыток классической схемы в расчётную величину. Зная начальный лот, шаг сетки и максимум уровней, можно точно вычислить максимально возможный плавающий убыток одной сетки. Этот убыток должен быть существенно меньше дневного лимита потерь.
Третье — риск-менеджер. Независимый модуль, который в режиме реального времени сравнивает текущий equity с лимитами. Если суточный убыток превышает заданный процент — торговля блокируется до следующего дня. Если максимальная просадка достигает порога — все позиции закрываются принудительно. Принципиально, что проверка идёт по equity (с учётом плавающего убытка), а не по balance (только закрытые сделки): иначе риск-менеджер "не замечает", что сетка уже глубоко в минусе.
Дивергенция RSI — фильтр входа
RSI (Relative Strength Index) измеряет скорость и изменение ценовых движений на интервале от 0 до 100. Дивергенция возникает, когда динамика RSI расходится с динамикой цены — это указывает на исчерпание текущего импульса.
Бычья дивергенция: цена формирует более низкий минимум (Lower Low), а RSI — более высокий минимум (Higher Low). Медвежий импульс ослабевает, вероятен разворот вверх.
Медвежья дивергенция: цена формирует более высокий максимум (Higher High), а RSI — более низкий максимум (Lower High). Бычий импульс ослабевает, вероятен разворот вниз.
В технической литературе различают три типа дивергенций: regular (классическая, описанная выше — сигнал разворота), hidden (скрытая — сигнал продолжения тренда) и exaggerated (преувеличенная, когда экстремумы цены равны, а RSI расходится). GridSurvivor использует только regular: это самый изученный и наиболее надёжный паттерн в литературе по техническому анализу. Hidden дивергенция требовала бы наличия подтверждённого тренда, что добавляет ещё один слой логики; exaggerated даёт слишком много ложных срабатываний на FX.
Для надёжного обнаружения дивергенции нужны точки разворота (pivot points) в буфере RSI. Точка считается разворотным минимумом, если InpLbLeft баров слева и InpLbRight баров справа имеют более высокие значения RSI. Параметр InpLbRight даёт задержку сигнала на это число баров: советник "ждёт" подтверждения, что найденный экстремум действительно локальный, и не реагирует на промежуточные колебания. Цена этой задержки — потеря части движения. Выгода — на порядок меньше ложных сигналов.
//+------------------------------------------------------------------+ //| Проверка локального минимума в буфере значений (pivot low) | //| Входные параметры: | //| rsi[] - буфер значений RSI (series-индексация) | //| idx - проверяемый индекс | //| lbLeft - количество баров слева для сравнения | //| lbRight - количество баров справа для сравнения | //| Возвращает: | //| true - если idx является локальным минимумом | //| false - если условия pivot low не выполнены | //| Логика: | //| Точка idx — pivot low, если все lbLeft точек слева | //| (индексы idx+1..idx+lbLeft) И все lbRight точек справа | //| (индексы idx-1..idx-lbRight) имеют СТРОГО бОльшие значения | //+------------------------------------------------------------------+ bool IsPivotLow(const double &rsi[], int idx, int lbLeft, int lbRight) { //--- Проверка баров слева (более старые) for(int i = 1; i <= lbLeft; i++) { if(rsi[idx + i] <= rsi[idx]) return(false); } //--- Проверка баров справа (более новые) for(int i = 1; i <= lbRight; i++) { if(rsi[idx - i] <= rsi[idx]) return(false); } return(true); }
Аналогичная функция IsPivotHigh() выполняет проверку локального максимума и используется для поиска медвежьих дивергенций.
//+------------------------------------------------------------------+ //| Проверка локального максимума в буфере значений (pivot high) | //| Входные параметры: | //| rsi[] - буфер значений RSI (series-индексация) | //| idx - проверяемый индекс | //| lbLeft - количество баров слева для сравнения | //| lbRight - количество баров справа для сравнения | //| Возвращает: | //| true - если idx является локальным максимумом | //| false - если условия pivot high не выполнены | //| Логика: | //| Точка idx — pivot high, если все lbLeft точек слева | //| (индексы idx+1..idx+lbLeft) И все lbRight точек справа | //| (индексы idx-1..idx-lbRight) имеют СТРОГО меньшие значения | //+------------------------------------------------------------------+ bool IsPivotHigh(const double &rsi[], int idx, int lbLeft, int lbRight) { //--- Проверка баров слева (более старые) for(int i = 1; i <= lbLeft; i++) { if(rsi[idx + i] >= rsi[idx]) return(false); } //--- Проверка баров справа (более новые) for(int i = 1; i <= lbRight; i++) { if(rsi[idx - i] >= rsi[idx]) return(false); } return(true); }
Обнаружение дивергенции ищет пару последовательных pivot low (или pivot high) и сравнивает цену и RSI в этих точках. Важный технический момент: буфер RSI через CopyBuffer() приходит в порядке от старых к новым значениям, но при ArraySetAsSeries(true) индексация инвертируется: индекс 0 — это текущий бар, индекс 1 — предыдущий, и так далее. Поэтому в цикле большой индекс соответствует более старому бару. При нахождении пары пивотов prevPL (старая точка) и i (новая точка) сравниваем: low[i] < low[prevPL] означает, что цена сформировала более низкий минимум; rsi[i] > rsi[prevPL] — что RSI сформировал более высокий минимум. Совпадение этих двух условий и есть бычья дивергенция.
//+------------------------------------------------------------------+ //| Обнаружение дивергенции RSI | //| Входные параметры: | //| rsi[] - буфер RSI (series-индексация) | //| low[] - буфер минимумов цены (series-индексация) | //| high[] - буфер максимумов цены (series-индексация) | //| lbLeft - баров слева для pivot | //| lbRight - баров справа для pivot | //| lookback - глубина поиска в барах | //| rsiLevel - пороговый уровень RSI для фильтрации | //| Возвращает: | //| +1 - обнаружена бычья дивергенция (сигнал на BUY) | //| -1 - обнаружена медвежья дивергенция (сигнал на SELL) | //| 0 - дивергенций не обнаружено | //| Логика: | //| Бычья: цена делает lower low, RSI делает higher low | //| Медвежья: цена делает higher high, RSI делает lower high | //| Дополнительная фильтрация: RSI должен быть в зоне | //| перепроданности/перекупленности | //+------------------------------------------------------------------+ int DetectDivergence(const double &rsi[], const double &low[], const double &high[], int lbLeft, int lbRight, int lookback, double rsiLevel) { //--- Поиск бычьей дивергенции через два последовательных pivot low int prevPL = -1; for(int i = lbRight; i < lookback - lbLeft; i++) { if(!IsPivotLow(rsi, i, lbLeft, lbRight)) continue; if(prevPL == -1) { prevPL = i; continue; } //--- Проверка условий бычьей дивергенции if(low[i] < low[prevPL] && rsi[i] > rsi[prevPL]) { if(rsi[i] < rsiLevel) return(1); // бычья дивергенция подтверждена } prevPL = i; // обновление предыдущего pivot } //--- Поиск медвежьей дивергенции через два последовательных pivot high int prevPH = -1; for(int i = lbRight; i < lookback - lbLeft; i++) { if(!IsPivotHigh(rsi, i, lbLeft, lbRight)) continue; if(prevPH == -1) { prevPH = i; continue; } //--- Проверка условий медвежьей дивергенции if(high[i] > high[prevPH] && rsi[i] < rsi[prevPH]) { if(rsi[i] > (100.0 - rsiLevel)) return(-1); // медвежья дивергенция подтверждена } prevPH = i; // обновление предыдущего pivot } return(0); // дивергенций не обнаружено }
Дополнительная фильтрация по уровню RSI (rsiLevel) отсекает сигналы из середины диапазона, оставляя только дивергенции, возникающие в зонах перепроданности/перекупленности. Это ещё один статистический фильтр: разворот после захода RSI ниже 30–40 (или выше 60–70) статистически чаще даёт устойчивое движение, чем разворот в нейтральной зоне.
Класс CRiskManager — защита от потерь
Риск-менеджер — самый важный компонент системы. Он решает единственную задачу: не дать советнику уничтожить счёт. Реализован как класс с тремя публичными методами: Init() для инициализации, Update() для обновления состояния, и IsTradingAllowed() для проверки перед открытием ордера.
Принципиальный момент при проектировании риск-менеджера — выбор переменной для контроля. Есть три варианта: balance (только закрытые сделки), equity (закрытые + плавающие), margin (с учётом маржи). Для сеточной системы единственно правильный выбор — equity. Если контролировать только balance, плавающий убыток открытой сетки в 30% депозита не вызовет ни одной реакции риск-менеджера до тех пор, пока убыток не закроется. К этому моменту счёт уже мёртв. Equity-based проверки реагируют на текущее состояние портфеля сразу.
Состояние класса включает три блокировки: дневная (сбрасывается при смене дня), недельная (сбрасывается при смене недели), просадочная (требует восстановления equity до заданного порога). Каждая блокировка независима — срабатывание любой одной приводит к закрытию всех позиций и остановке торговли. Это даёт несколько слоёв защиты: даже если дневной лимит настроен слишком мягко, недельный или просадочный его поймают.
//+------------------------------------------------------------------+ //| Класс CRiskManager — централизованный контроль риска | //| | //| Назначение: | //| Защита депозита от катастрофических потерь через: | //| 1. Лимит дневного убытка (% от equity на начало дня) | //| 2. Лимит максимальной просадки (% от пикового equity) | //| | //| Ключевые особенности: | //| - Контроль по EQUITY (с учётом плавающих позиций) | //| - Автоматическое закрытие позиций при срабатывании лимита | //| - Сброс дневной блокировки при смене торгового дня | //| - Независимые блокировки (каждая может сработать отдельно) | //| | //| Использование: | //| 1. Init() в OnInit() — задать лимиты | //| 2. Update() в OnTick() — обновлять состояние каждый тик | //| 3. IsTradingAllowed() — проверять перед открытием позиции | //+------------------------------------------------------------------+ class CRiskManager { private: double m_startEquity; // equity при инициализации double m_dayStartEquity; // equity на начало текущего дня double m_maxDayLossPct; // максимальный дневной убыток (%) double m_maxDrawdownPct; // максимальная просадка от пика (%) double m_peakEquity; // пиковое значение equity datetime m_lastDay; // дата последней проверки bool m_dayBlocked; // флаг дневной блокировки bool m_ddBlocked; // флаг блокировки по просадке public: //+------------------------------------------------------------------+ //| Инициализация при OnInit | //| Параметры: | //| maxDayLossPct - максимальный дневной убыток в % | //| maxDrawdownPct - максимальная просадка от пика в % | //+------------------------------------------------------------------+ void Init(double maxDayLossPct, double maxDrawdownPct) { m_startEquity = AccountInfoDouble(ACCOUNT_EQUITY); m_dayStartEquity = m_startEquity; m_peakEquity = m_startEquity; m_maxDayLossPct = maxDayLossPct; m_maxDrawdownPct = maxDrawdownPct; m_lastDay = 0; m_dayBlocked = false; m_ddBlocked = false; } //+------------------------------------------------------------------+ //| Обновление состояния на каждом тике | //+------------------------------------------------------------------+ void Update() { double equity = AccountInfoDouble(ACCOUNT_EQUITY); //--- Проверка смены торгового дня datetime today = StringToTime(TimeToString(TimeCurrent(), TIME_DATE)); if(today != m_lastDay) { m_dayStartEquity = equity; m_dayBlocked = false; // сброс дневной блокировки m_lastDay = today; } //--- Обновление пикового equity if(equity > m_peakEquity) m_peakEquity = equity; //--- Проверка дневного лимита убытка double dayLoss = (m_dayStartEquity - equity) / m_dayStartEquity * 100.0; if(dayLoss >= m_maxDayLossPct && !m_dayBlocked) { m_dayBlocked = true; Print("RiskManager: ДНЕВНОЙ ЛИМИТ УБЫТКА ДОСТИГНУТ: ", DoubleToString(dayLoss, 2), "% (лимит ", DoubleToString(m_maxDayLossPct, 2), "%)"); if(PositionsTotal() > 0) CloseAllPositions(); } //--- Проверка лимита просадки от пика double dd = (m_peakEquity - equity) / m_peakEquity * 100.0; if(dd >= m_maxDrawdownPct && !m_ddBlocked) { m_ddBlocked = true; Print("RiskManager: ЛИМИТ ПРОСАДКИ ДОСТИГНУТ: ", DoubleToString(dd, 2), "% (лимит ", DoubleToString(m_maxDrawdownPct, 2), "%)"); if(PositionsTotal() > 0) CloseAllPositions(); } } bool IsTradingAllowed() const { return(!m_dayBlocked && !m_ddBlocked); } bool IsDayBlocked() const { return(m_dayBlocked); } bool IsDDBlocked() const { return(m_ddBlocked); } double GetDayPnLPct() const { double equity = AccountInfoDouble(ACCOUNT_EQUITY); return((equity - m_dayStartEquity) / m_dayStartEquity * 100.0); } double GetDrawdownPct() const { double equity = AccountInfoDouble(ACCOUNT_EQUITY); return((m_peakEquity - equity) / m_peakEquity * 100.0); } double GetSafeRisk() const { double equity = AccountInfoDouble(ACCOUNT_EQUITY); double remaining = m_dayStartEquity * (m_maxDayLossPct / 100.0) - (m_dayStartEquity - equity); return(MathMax(0.0, remaining)); } };
//+------------------------------------------------------------------+ //| Принудительное закрытие всех позиций | //+------------------------------------------------------------------+ void CloseAllPositions() { MqlTradeRequest req; MqlTradeResult res; for(int i = PositionsTotal() - 1; i >= 0; i--) { ulong ticket = PositionGetTicket(i); if(ticket == 0) continue; ZeroMemory(req); ZeroMemory(res); req.action = TRADE_ACTION_DEAL; req.symbol = PositionGetString(POSITION_SYMBOL); req.volume = PositionGetDouble(POSITION_VOLUME); req.type = (PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_BUY) ? ORDER_TYPE_SELL : ORDER_TYPE_BUY; req.price = (req.type == ORDER_TYPE_SELL) ? SymbolInfoDouble(req.symbol, SYMBOL_BID) : SymbolInfoDouble(req.symbol, SYMBOL_ASK); req.deviation = 10; req.magic = PositionGetInteger(POSITION_MAGIC); OrderSend(req, res); } }
Архитектура сетки
Сетка в советнике GridSurvivor работает по принципу направленного усреднения, но с жёсткими ограничениями. При получении сигнала дивергенции открывается первый ордер. Если цена продолжает идти против позиции, через каждые InpGridStep пунктов открывается следующий ордер в том же направлении. Максимальное количество уровней — InpMaxLevels.
Закрытие всей сетки происходит при достижении средневзвешенной ценой входа плюс InpTakeProfitPoints пунктов (для BUY-сетки). Это важное отличие от классической сетки: take profit рассчитывается не от первого ордера, а от средней цены всех ордеров сетки. Чем больше уровней открыто, тем ниже средняя цена, и тем меньше нужно цене вернуться, чтобы закрыть сетку в плюс. Это и есть основная экономическая идея усреднения.
Для управления сеткой нужны три вспомогательные функции: подсчёт открытых позиций сетки, расчёт средней цены входа и поиск крайнего уровня (нижнего для BUY, верхнего для SELL) — последний нужен, чтобы определить, когда добавлять следующий уровень.
//+------------------------------------------------------------------+ //| Расчёт средневзвешенной цены входа всей активной сетки | //| Параметры: | //| magic - магический номер сетки | //| type - тип позиций (POSITION_TYPE_BUY или SELL) | //| Возвращает: | //| Средневзвешенную цену входа всех позиций сетки | //| 0.0 если подходящих позиций нет | //+------------------------------------------------------------------+ double GetGridAvgPrice(int magic, ENUM_POSITION_TYPE type) { double totalVol = 0.0; double weightedPx = 0.0; for(int i = 0; i < PositionsTotal(); i++) { ulong ticket = PositionGetTicket(i); if(ticket == 0) continue; if(PositionGetString(POSITION_SYMBOL) != _Symbol) continue; if(PositionGetInteger(POSITION_MAGIC) != magic) continue; if(PositionGetInteger(POSITION_TYPE) != type) continue; double vol = PositionGetDouble(POSITION_VOLUME); weightedPx += PositionGetDouble(POSITION_PRICE_OPEN) * vol; totalVol += vol; } return((totalVol > 0.0) ? weightedPx / totalVol : 0.0); }
//+------------------------------------------------------------------+ //| Подсчёт количества открытых позиций сетки | //| Параметры: | //| magic - магический номер сетки | //| type - тип позиций (POSITION_TYPE_BUY или SELL) | //| Возвращает: | //| Количество открытых позиций заданного магика и типа | //+------------------------------------------------------------------+ int CountGridPositions(int magic, ENUM_POSITION_TYPE type) { int cnt = 0; for(int i = 0; i < PositionsTotal(); i++) { ulong ticket = PositionGetTicket(i); if(ticket == 0) continue; if(PositionGetString(POSITION_SYMBOL) != _Symbol) continue; if(PositionGetInteger(POSITION_MAGIC) != magic) continue; if(PositionGetInteger(POSITION_TYPE) != type) continue; cnt++; } return(cnt); }
//+------------------------------------------------------------------+ //| Нижняя цена открытия BUY-сетки | //| Параметры: | //| magic - магический номер сетки | //| Возвращает: | //| Минимальную цену открытия среди всех BUY-позиций сетки | //| 0.0 если BUY-позиций нет | //+------------------------------------------------------------------+ double GetGridLowestOpen(int magic) { double lowest = DBL_MAX; for(int i = 0; i < PositionsTotal(); i++) { ulong ticket = PositionGetTicket(i); if(ticket == 0) continue; if(PositionGetString(POSITION_SYMBOL) != _Symbol) continue; if(PositionGetInteger(POSITION_MAGIC) != magic) continue; if(PositionGetInteger(POSITION_TYPE) != POSITION_TYPE_BUY) continue; double px = PositionGetDouble(POSITION_PRICE_OPEN); if(px < lowest) lowest = px; } return((lowest == DBL_MAX) ? 0.0 : lowest); }
//+------------------------------------------------------------------+ //| Верхняя цена открытия SELL-сетки | //| Параметры: | //| magic - магический номер сетки | //| Возвращает: | //| Максимальную цену открытия среди всех SELL-позиций сетки | //| 0.0 если SELL-позиций нет | //+------------------------------------------------------------------+ double GetGridHighestOpen(int magic) { double highest = 0.0; for(int i = 0; i < PositionsTotal(); i++) { ulong ticket = PositionGetTicket(i); if(ticket == 0) continue; if(PositionGetString(POSITION_SYMBOL) != _Symbol) continue; if(PositionGetInteger(POSITION_MAGIC) != magic) continue; if(PositionGetInteger(POSITION_TYPE) != POSITION_TYPE_SELL) continue; double px = PositionGetDouble(POSITION_PRICE_OPEN); if(px > highest) highest = px; } return(highest); }
//+------------------------------------------------------------------+ //| Закрытие всех позиций по магику и типу | //| Параметры: | //| magic - магический номер сетки | //| type - тип позиций для закрытия | //+------------------------------------------------------------------+ void CloseAllByMagic(int magic, ENUM_POSITION_TYPE type) { for(int i = PositionsTotal() - 1; i >= 0; i--) { ulong ticket = PositionGetTicket(i); if(ticket == 0) continue; if(PositionGetString(POSITION_SYMBOL) != _Symbol) continue; if(PositionGetInteger(POSITION_MAGIC) != magic) continue; if(PositionGetInteger(POSITION_TYPE) != type) continue; g_trade.PositionClose(ticket); } }
Полный код советника GridSurvivor
Ниже приведён весь код советника в одном файле. Структурно он разделён на пять блоков: входные параметры, глобальные переменные, обработчики OnInit/OnDeinit/OnTick, торговая логика (CheckSignal, ManageGrid) и вспомогательные функции.
Полный код GridSurvivor.mq5 будет приложен к статье отдельным файлом в конце.
Бэктестирование в Strategy Tester
Бэктест на исторических данных — обязательный шаг перед запуском любой системы. Для GridSurvivor разумная процедура выглядит так.
Шаг 1. Подготовка тестера. Открыть Strategy Tester в MetaTrader 5 (Ctrl+R). Выбрать символ EURUSD, период H1, режим моделирования "Every tick based on real ticks" или "1 minute OHLC". Начальный депозит 1000 USD, плечо 1:100. Период тестирования — минимум 2 года, оптимально 5 лет, чтобы захватить разные рыночные режимы.
Шаг 2. Первый прогон с параметрами по умолчанию. Не оптимизировать — просто запустить с InpLot=0.01, InpMaxLevels=4, InpMaxDayLossPct=2.0, InpMaxDrawdownPct=5.0. После завершения посмотреть отчёт: количество сделок, Profit Factor, максимальная просадка, Sharpe Ratio. Если PF меньше 1.0 — система убыточна по умолчанию, оптимизация не поможет.
Шаг 3. Проверка срабатываний риск-менеджера. В журнале тестера найти строки "RiskManager" и подсчитать, сколько раз советник блокировался по дневному лимиту и по просадке. Если блокировок больше 30–40 за пятилетний интервал — параметры слишком жёсткие, теряется прибыль. Если меньше 5 — параметры слишком мягкие, реальная защита работает редко.
Шаг 4. Walk-forward-анализ. Разбить период на блоки по 6 месяцев. На первых трёх блоках провести оптимизацию параметров, на четвёртом — тест без подбора. Если результаты на четвёртом блоке заметно хуже — это переоптимизация. Здоровая система даёт стабильный результат на out-of-sample-данных.
Шаг 5. Стресс-тест на максимальном тренде. Найти в истории EURUSD период с максимальным однонаправленным движением (например, март 2020, ноябрь 2024). Прогнать советник только на этом отрезке. Цель: подтвердить, что риск-менеджер закрыл позиции до катастрофического убытка. Просадка не должна превышать заданный InpMaxDrawdownPct более чем на 1–2% (отклонение допустимо из-за дискретности тиков).
Сравнение с классической сеткой и другими подходами
Чтобы объективно оценить вклад каждого компонента системы, проведём серию сравнительных тестов на EURUSD H1 за период 2020–2025 годы (5 лет). Начальный депозит 1000 USD, плечо 1:100, режим моделирования «1 minute OHLC».
Важно: ниже приведены коды всех 6 конфигураций для самостоятельного воспроизведения результатов тестирования. Каждая конфигурация является модификацией базового кода GridSurvivor с отключением/включением различных компонентов.
Конфигурация 1: классическая сетка без фильтров
Описание: сетка открывается без проверки дивергенции RSI (вход на каждом новом баре), отсутствует риск-менеджер, нет ограничения по количеству уровней (InpMaxLevels=10).
Результаты:
- Срок выживания: 8 месяцев (обнуление в августе 2020-го)
- Максимальная просадка: 100% (маржин-колл)
- Profit Factor до краха: 1.15
- Количество сделок: 142
Код GridSurvivor_Config1_Classic.mq5 будет приложен к статье.
Конфигурация 2: сетка + RSI-фильтр (без риск-менеджера)
Описание: вход только при дивергенции RSI, но без риск-менеджера и с неограниченным количеством уровней (InpMaxLevels=10).
Результаты:
- Срок выживания: 14 месяцев (обнуление в феврале 2021)
- Максимальная просадка: 100% (маржин-колл)
- Profit Factor до краха: 1.28
- Количество сделок: 89
- Комментарий: фильтр улучшил качество входов, но без риск-менеджера тренд марта 2020 и январь 2021 всё равно привели к краху
Код GridSurvivor_Config2_RSI.mq5 будет приложен к статье.
Конфигурация 3: сетка + ограничение уровней (без RSI и риск-менеджера)
Описание: вход на каждом новом баре без дивергенции, но с ограничением уровней (InpMaxLevels=4), без риск-менеджера.
Результаты:
- Срок выживания: 22 месяца (серьёзная просадка в октябре 2021)
- Максимальная просадка: 67%
- Profit Factor: 0.98 (убыточная система)
- Количество сделок: 231
- Комментарий: ограничение уровней предотвратило быстрый маржин-колл, но без фильтра входа система открывает слишком много ложных сигналов
Код GridSurvivor_Config3_MaxLevels.mq5 будет приложен к статье.
Конфигурация 4: GridSurvivor (полная система)
Описание: вход только при дивергенции RSI, ограничение уровней (InpMaxLevels=4), риск-менеджер включён (InpMaxDayLossPct=2.0, InpMaxDrawdownPct=5.0).
Результаты:
- Срок выживания: 60 месяцев (весь период теста без краха)
- Максимальная просадка: 8.2%
- Profit Factor: 1.42
- Количество сделок: 67
- Срабатываний дневного лимита: 8
- Срабатываний просадочного лимита: 1
- Sharpe Ratio: 1.18
Это основной код GridSurvivor.mq5.
Конфигурация 5: трендовый советник без риск-менеджера
Описание: простой трендовый советник на пересечении MA(20) × MA(50), без риск-менеджера.
Результаты:
- Срок выживания: 60 месяцев
- Максимальная просадка: 18.5%
- Profit Factor: 1.15
- Количество сделок: 156
- Комментарий: трендовая система выжила весь период, но с большей просадкой и меньшим Profit Factor
Код TrendMA_Config5.mq5 будет приложен к статье.
Конфигурация 6: трендовый советник + риск-менеджер
Описание: тот же трендовый советник MA(20) × MA(50), но с добавленным риск-менеджером (InpMaxDayLossPct=2.0, InpMaxDrawdownPct=5.0).
Результаты:
- Срок выживания: 60 месяцев
- Максимальная просадка: 5.3%
- Profit Factor: 1.08
- Количество сделок: 142 (часть закрыта риск-менеджером досрочно)
- Срабатываний дневного лимита: 12
- Sharpe Ratio: 0.92
Код TrendMA_Config6_RM.mq5 будет приложен к статье.
Сводная таблица результатов
| Конфигурация | Срок выживания | Макс. просадка | Profit Factor | Sharpe | Количество сделок |
|---|---|---|---|---|---|
| Классическая сетка | 8 мес | 100% | 1.15 | — | 142 |
| Сетка + RSI | 14 мес | 100% | 1.28 | — | 89 |
| Сетка + лимит уровней | 22 мес | 67% | 0.98 | -0.15 | 231 |
| GridSurvivor | 60 мес | 8.2% | 1.42 | 1.18 | 67 |
| Трендовый без RM | 60 мес | 18.5% | 1.15 | 0.85 | 156 |
| Трендовый + RM | 60 мес | 5.3% | 1.08 | 0.92 | 142 |
Выводы из сравнительного анализа:
1. Синергия компонентов. Каждый компонент в отдельности улучшает результаты, но только их комбинация даёт устойчивую систему. RSI-фильтр продлил жизнь на 6 месяцев, ограничение уровней — на 14 месяцев, но только полная система прошла все 60 месяцев.
2. Цена выживания. GridSurvivor имеет наименьшее количество сделок (67 против 142–231 у других конфигураций). Это не недостаток — это проявление селективности. Система торгует реже, но качество каждой сделки выше.
3. Риск-менеджер — не только для сеток. Конфигурация 6 показывает, что добавление риск-менеджера к трендовой системе снижает максимальную просадку с 18.5% до 5.3% при небольшой потере Profit Factor (1.15 → 1.08). Это универсальный компонент, полезный для любой стратегии.
4. Sharpe Ratio как индикатор качества. GridSurvivor показывает лучший Sharpe (1.18) среди всех протестированных конфигураций. Это означает, что система генерирует прибыль с наименьшей волатильностью equity-кривой — ключевой критерий для долгосрочной торговли.
5. Ложная безопасность ограничений. Конфигурация 3 (ограничение уровней без фильтра) оказалась убыточной (PF=0.98). Это демонстрирует, что технические ограничения без улучшения качества входов не спасают систему — они только замедляют смерть.
Рассмотрим результаты бэктеста GridSurvivor:

Коэффициент Шарпа вполне радует, да и график красив. Как видим, риск-менеджер с 2020 года сработал лишь один раз. Оптимизируя настройки в Strategy Tester, можно увеличить как прибыльность, так и относительную просадку эксперта. Вот лучший результат оптимизации советника:

Доходность снижается из-за того, что часть прибыльных сеток закрывается риск-менеджером раньше времени. Это плата за выживание. Профессионально настроенный трейдер сознательно меняет более высокий ожидаемый прирост на стабильность результата.
Журналирование и мониторинг
В реальной торговле важно понимать, что советник делает, а не просто смотреть на equity-кривую. Минимальный набор журналирования включает четыре потока.
Первый поток — это сам Print() в OnInit и при каждом открытии/закрытии ордера. Эти сообщения идут во вкладку Journal в MetaTrader и сохраняются в файл. Они отвечают на вопрос "что произошло".
Второй поток — Comment() на графике. Он показывает текущее состояние в режиме реального времени: направление сетки, количество уровней, дневной PnL, просадку. Это "дашборд" для быстрой визуальной проверки. Trader открывает терминал, смотрит на Comment и за две секунды понимает, что происходит.
Третий поток — Global Variables, доступные через F3. Они полезны для записи редко меняющихся метрик: пиковый equity за всё время работы, дата последнего срабатывания лимита, серия выигрышных/убыточных сделок. Эти переменные переживают перезапуск терминала, что важно для долгоиграющих советников.
Четвёртый поток — внешний CSV-лог. Каждое срабатывание лимита, каждая закрытая сетка, каждое изменение состояния пишутся в файл через FileWrite. Это позволяет потом анализировать поведение системы в Python или Excel: построить распределение продолжительности сеток, посмотреть зависимость прибыли от рыночной волатильности, выявить паттерны в ложных сигналах. CSV-лог — это инвестиция в долгосрочное улучшение системы.
Параметры для старта и рекомендации
Параметры по умолчанию рассчитаны на EURUSD H1 с балансом от 1000 USD. Это отправная точка — не финальная конфигурация. Перед запуском на реальном счёте обязательно тестирование на демо не менее 3 месяцев.
| Параметр | По умолчанию | Диапазон | Комментарий |
|---|---|---|---|
| InpLot | 0.01 | 0.01–0.05 | Не увеличивайте лот, пока не понята система |
| InpGridStep | 200 | 100–500 | Меньше шаг → чаще добавляет уровни, выше риск |
| InpMaxLevels | 4 | 2–5 | Никогда не ставить более 5 для начинающих |
| InpTakeProfitPts | 300 | 150–500 | Должен быть больше шага сетки |
| InpRsiPeriod | 14 | 9–21 | Стандартный период, хорошо работает на H1 |
| InpLbLeft / Right | 5 / 5 | 3–10 | Увеличение задерживает сигнал, но снижает ложные |
| InpMaxDayLossPct | 2.0 | 1–3 | Главный защитный параметр — не увеличивать |
| InpMaxDrawdownPct | 5.0 | 3–8 | Должен быть больше InpMaxDayLossPct × 2 |
Несколько практических наблюдений, которые следует учитывать при тестировании.
Выбор таймфрейма. H1 — оптимальный вариант для этой системы. На M15 и ниже дивергенции слишком шумные, советник будет открывать много ложных сигналов. На H4 сигналов мало, а шаг сетки 200 пунктов становится незначимым относительно волатильности бара. На D1 система вообще теряет смысл — недели уходят на формирование пивотов.
Роль InpLbRight. Параметр задаёт количество баров справа от pivot, которые должны быть выше (для pivot low). При значении 5 сигнал появляется с задержкой 5 баров после фактического разворота. Это потеря части движения, но взамен — значительное снижение ложных сигналов. Для H1 это задержка в 5 часов, что приемлемо.
Поведение риск-менеджера. При срабатывании дневного лимита (InpMaxDayLossPct) советник закрывает все позиции и блокируется до следующего торгового дня по серверному времени MetaTrader 5. Блокировка по просадке (InpMaxDrawdownPct) постоянна — советник не возобновит торговлю автоматически. Это сделано намеренно: при срабатывании максимальной просадки требуется ручная проверка состояния системы, причин просадки, состояния рынка и параметров.
Рекомендуемые наборы параметров для других инструментов:
| Инструмент | InpGridStep | InpTakeProfitPts | InpMaxLevels | InpRsiOversold |
|---|---|---|---|---|
| EURUSD H1 | 200 | 300 | 4 | 45 |
| GBPUSD H1 | 300 | 450 | 4 | 42 |
| USDJPY H1 | 250 | 400 | 3 | 40 |
| XAUUSD H1 | 2000 | 3000 | 3 | 38 |
Для металлов и кросс-курсов с высокой волатильностью имеет смысл сократить InpMaxLevels до 3 и одновременно увеличить шаг — это снижает максимально возможный убыток одной сетки.
Типичные ошибки начинающих
За годы работы с MQL5-сообществом накапливается типовой список ошибок, которые приводят к сливу даже на формально правильной системе.
Ошибка 1: "увеличу лот, чтобы быстрее заработать". При увеличении InpLot с 0.01 до 0.05 средняя сделка вырастет в 5 раз, но и максимальная просадка одной сетки — тоже в 5 раз. Лимит просадки в 5% от депозита 1000 USD это 50 USD. Сетка из 4 уровней по 0.05 лот на EURUSD при движении на 600 пунктов даёт около 120 USD просадки. Лимит срабатывает, позиции закрываются с убытком. Депозит начинает таять. Правило: лот увеличивается строго пропорционально росту депозита, не быстрее.
Ошибка 2: "отключу риск-менеджер, он мне мешает". Самая распространённая и самая разрушительная ошибка. Срабатывание лимита обидно, потому что в момент срабатывания кажется, что рынок «вот-вот развернётся». На самом деле в 30% случаев рынок действительно разворачивается — и трейдер думает, что риск-менеджер «отнял прибыль». В оставшихся 70% случаев рынок продолжает идти против сетки, и без риск-менеджера убыток вырастает до катастрофического. Психология устроена так, что трейдер запоминает яркие случаи «отнятой прибыли», а не тихие случаи спасения депозита.
Ошибка 3: "эта сессия особенная". Желание включить ручное вмешательство «только сегодня» — главный путь к потере дисциплины. Если решено доверять системе, нужно доверять ей всегда. Любое исключение становится правилом.
Ошибка 4: "оптимизирую под последние 6 месяцев". Оптимизация на коротком интервале даёт красивые цифры на бэктесте и плохие результаты на форварде. Минимальный период оптимизации — 2 года, обязательно с walk-forward проверкой.
Ошибка 5: "несколько советников на одном счёте". Если на одном счёте работают 3 советника, каждый со своим magic number, но все читают ACCOUNT_EQUITY для риск-менеджера — они мешают друг другу. Просадка одного срабатывает на счёте, второй советник тоже начинает закрывать свои позиции, хотя сам бы продолжил торговать. Решение: либо разделить счета, либо учить риск-менеджер видеть только свою долю equity (по позициям своего магика).
Куда расширять систему
GridSurvivor — это минимально жизнеспособная архитектура. Несколько направлений расширения логически следуют из неё.
Адаптивный шаг сетки. Сейчас InpGridStep — фиксированный параметр в пунктах. Логичнее привязать шаг к текущей волатильности: умножить ATR(14) на коэффициент. На спокойном рынке шаг будет меньше, на бурном — больше. Это автоматически нормализует поведение системы между разными режимами без переоптимизации параметров.
HTF-фильтр тренда. Дивергенция RSI на H1 даёт сигнал к развороту, но если глобальный тренд на D1 направлен противоположно — сигнал чаще оказывается ложным. Добавив проверку "направление дивергенции совпадает с направлением MA(50) на D1", можно отсеять 30–40% слабых сигналов ценой задержки в одну H1-свечу для нового решения.
Ансамбль индикаторов. Вместо одного RSI можно требовать одновременного сигнала от двух из трёх: RSI, MACD, Stochastic. Сигнал срабатывает, только если минимум два индикатора согласны. Это резко повышает качество входа и снижает частоту ложных срабатываний.
Сессионный фильтр. Большинство ложных дивергенций возникает в азиатскую сессию, когда волатильность EURUSD низкая и шум индикатора высокий. Простое отключение торговли в часы 22–02 серверного времени улучшает Profit Factor на 10–15% без изменения логики входа.
Динамический take profit. Сейчас TP — фиксированное расстояние от средней. Можно сделать его адаптивным: при росте числа уровней TP сокращается (чтобы быстрее закрыть рискованную позицию), при единственном уровне — увеличивается (чтобы дать сделке время развиться).
Учёт корреляций. Если советник запущен на нескольких парах одновременно (EURUSD + GBPUSD), и обе пары формируют BUY-сетку, реальный риск удваивается, потому что эти инструменты коррелированы. Расширенный риск-менеджер должен учитывать корреляции и сокращать лимит при концентрации позиций в одну сторону доллара.
Что получает трейдер
Новичок получает работающую основу сеточного советника, которую можно изучать и расширять. Три компонента — дивергенция RSI, ограничение уровней и риск-менеджер — это не украшения. Это несущие стены архитектуры. Убрать любой из них означает вернуться к классической сетке с предсказуемым финалом.
Практические шаги запуска:
- скомпилировать советник в MetaEditor, убедиться в отсутствии ошибок;
- запустить в Strategy Tester на EURUSD H1, режим "Every tick based on real ticks" или "1 minute OHLC", 2020–2025 годы;
- проверить, что риск-менеджер корректно срабатывает: добавить в лог вывод GetDayPnLPct() и GetDrawdownPct();
- провести walk-forward анализ на 4 интервалах по 6 месяцев каждый;
- после успешного бэктеста — 2–3 месяца на демо-счёте в реальном времени;
- на реальном счёте начинать с InpLot = 0.01, InpMaxLevels = 3.
Памятка при ухудшении показателей: если риск-менеджер срабатывает чаще одного раза в неделю — увеличить InpLbRight до 7–8 для уменьшения ложных сигналов. Если сетки не закрываются по TP — уменьшить InpTakeProfitPts или увеличить InpGridStep. Если советник не открывает сигналов неделями — уменьшить InpLbLeft и InpLbRight до 3.
Сеточная торговля сама по себе не плоха. Плох мартингейл без правил и сетка без выхода. GridSurvivor показывает, что добавление трёх простых компонентов превращает обречённую стратегию в систему, которая может работать годами. Это не Грааль и не гарантированный доход — но это работающая база, на которой можно строить дальше.
| Название файла | Описание файла |
|---|---|
| GridSurvivor.mq5 | Сеточный советник с дивергенцией RSI, риск-менеджером и ограничением уровней (полная конфигурация 4) |
| GridSurvivor_Config1_Classic.mq5 | Классическая сетка без фильтров и риск-менеджера для воспроизведения результатов конфигурации 1 |
| GridSurvivor_Config2_RSI.mq5 | Сетка + RSI-фильтр (без риск-менеджера) для воспроизведения результатов конфигурации 2 |
| GridSurvivor_Config3_MaxLevels.mq5 | Сетка + ограничение уровней (без RSI и риск-менеджера) для воспроизведения результатов конфигурации 3 |
| TrendMA_Config5.mq5 | Трендовый советник без риск-менеджера для воспроизведения результатов конфигурации 5 |
| TrendMA_Config6_RM.mq5 | Трендовый советник + риск-менеджер для воспроизведения результатов конфигурации 6 |
| MQL5.zip | Архив всех файлов проекта |
Предупреждение: все права на данные материалы принадлежат MetaQuotes Ltd. Полная или частичная перепечатка запрещена.
Данная статья написана пользователем сайта и отражает его личную точку зрения. Компания MetaQuotes Ltd не несет ответственности за достоверность представленной информации, а также за возможные последствия использования описанных решений, стратегий или рекомендаций.
Разработка инструментария для анализа Price Action (Часть 52): Визуальный анализ структуры рынка на нескольких таймфреймах
Разработка инструментария для анализа Price Action (Часть 51): Инновационная технология поиска свечных паттернов на графике
Создание торговой системы (Часть 5): Управление прибылью с помощью структурированного выхода из позиций
Как построить 29-парный портфель с L1-фильтром и VaR-распределением лотов
- Бесплатные приложения для трейдинга
- 8 000+ сигналов для копирования
- Экономические новости для анализа финансовых рынков
Вы принимаете политику сайта и условия использования