Блог им. Voldemar227

Торговая стратегия по RSI с выходом через сеть плюс исходный код торгового робота

Эта стратегия сочетает классическую логику осциллятора RCI/RSI (период 14, уровни 30/70) с сеточной (grid) логикой усреднения и реализована в виде эксперта под MetaTrader 5 на MQL5 с использованием объектно‑ориентированного подхода и хэндла индикатора. Она рассчитана для тестера стратегий и требует доработки по рискам, проверкам и нормализациям перед запуском на реальном счёте.

Видео описание стратегии и разработки кода

Логика классической стратегии RSI

Используется осциллятор с периодом 14 и уровнями 30 (перепроданность) и 70 (перекупленность), что является классическим набором настроек для RSI‑подобных индикаторов.

Сигнал на покупку: линия индикатора опускается ниже уровня 30, а затем пересекает его снизу вверх (RCI2 < 30 и RCI1 > 30), то есть фиксируется выход из зоны перепроданности.

Сигнал на продажу: линия индикатора поднимается выше 70 и затем пересекает этот уровень сверху вниз (RCI2 > 70 и RCI1 < 70), что трактуется как выход из зоны перекупленности.

Стратегия ориентирована на старшие таймфреймы (например, H1), где сигналы редкие, но более «чистые», и особенно эффективно работает по тренду, а не против него.

Торговая идея и управление позицией

Базовая идея: открывать позиции только по чётким пересечениям 30/70, избегая «догадочных» входов, когда индикатор ещё не дал формальный сигнал.

В классическом варианте предлагается риск‑менеджмент с соотношением стоп‑лосс/тейк‑профит порядка 1:3 и риском на сделку не более 3% капитала, чтобы выдержать серию из 30–40 сделок.

Предпочтение отдаётся трейлинг‑стопу: после прохождения ценой 150–200 пунктов в прибыль стоп перетягивается в безубыток или зону профита, а сделка удерживается несколько дней, чтобы дать тренду максимум пространства.


Идея стратегии: классика RSI + грид

  • Основа — классическая логика RSI: период 14, уровни 70/30, работа по пробою зон перекупленности/перепроданности.
  • Вход на покупку: RSI выходит из перепроданности (ниже 30 → выше 30), вход на продажу — выход из перекупленности (выше 70 → ниже 70). В коде это уточнено допусловием «ломки» локального минимума/максимума (сравнение rsi и rsi).
  • Вместо жёсткого стоп‑лосса используется сеточное усреднение: первая сделка открывается базовым лотом, последующие — увеличенным объёмом через множитель iMultiplier, по мере движения цены против позиции.

Такая связка классического сигнального индикатора и грид‑логики делает стратегию наглядной для обучения: видно, где именно индикатор даёт вход и как математически вытаскивается серия убыточных ордеров.

 

Входы по RSI: формализация сигнала

Ключевой фрагмент:

 

if(::CopyBuffer(handle, 0, 0, 5, rsi) != -1)
  {
   ::ArraySetAsSeries(rsi, true);

   if(rsi[2] < rsi[1] && rsi[2] < iRSI_Dw_Lvl && rsi[1] > iRSI_Dw_Lvl)
     { ... Buy ... }

   if(rsi[2] > rsi[1] && rsi[2] > iRSI_Up_Lvl && rsi[1] < iRSI_Up_Lvl)
     { ... Sell ... }
  }
  • CopyBuffer(handle, 0, 0, 5, rsi) берёт 5 последних значений нулевого буфера RSI‑индикатора, начиная с текущего бара (shift 0).​

  • ArraySetAsSeries(rsi, true) разворачивает массив так, что rsi[0] — текущий бар, rsi[1] — предыдущий, rsi[2] — бар двумя свечами назад, что является стандартной практикой при работе с временными рядами в MQL5.​

Сигнал на покупку:

  • rsi[2] < rsi[1]: на истории формируется локальный минимум (осциллятор «заворачивает» вверх).

  • rsi[2] < iRSI_Dw_Lvl && rsi[1] > iRSI_Dw_Lvl: классический выход из перепроданности (пересечение уровня снизу вверх).​

  • Если покупок по этому магику ещё нет (b == 0) — открывается стартовая позиция с iStartLots и фиксированным TakeProfit в пунктах. Если позиции уже есть, включается логика сетки (см. ниже).

Сигнал на продажу задаётся зеркально, через локальный максимум и выход из зоны перекупленности.​

Таким образом, стратегия не реагирует на простое нахождение RSI в зоне 30/70, а ждёт именно импульса выхода, что уменьшает число ложных входов в затяжном боковике

Архитектура эксперта: CTrade, PositionInfo и хэндл индикатора

Инициализация (OnInit)

trade.SetExpertMagicNumber(iMagicNumber);
trade.SetDeviationInPoints(iSlippage);
trade.SetTypeFillingBySymbol(_Symbol);
trade.SetMarginMode();

handle = ::iRSI(_Symbol, PERIOD_CURRENT, iRSI_Period, PRICE_CLOSE);
  • Используется стандартный класс CTrade из Trade.mqh для унифицированной отправки и модификации сделок (Buy, Sell, PositionModify и т.д.).​

  • CPositionInfo даёт удобный доступ к информации по каждой позиции (тип, объём, цена открытия, магик, символ).​

  • Через trade.SetExpertMagicNumber и SetDeviationInPoints задаются магик и допустимое проскальзывание, что избавляет от ручной передачи этих параметров при каждом вызове функций торговли.​

  • Хэндл RSI создаётся один раз через iRSI, а сам индикатор живёт в отдельной области памяти; дальнейший доступ к значениям осуществляется через CopyBuffer.​

Такой объектный подход — современный стандарт MQL5: логика торговли сосредоточена в классе, индикаторы — как отдельные «чёрные ящики», а код эксперта остаётся компактным.

Учёт позиций и подготовка данных для сетки

В начале OnTick робот обходится по всем позициям:

int total = ::PositionsTotal(), b = 0, s = 0;
double BuyPricSumm = 0, SelPricSumm = 0, BuyLotsSumm = 0, SelLotsSumm = 0;
double BuyLowPrice = 0, SelHigtPrice = 0;
double BuyLowLots  = 0, SelHigtLots  = 0;
  • Для позиций с нужным Symbol() и Magic() считается:

    • суммарный лот по покупкам и продажам;

    • сумма PriceOpen * Volume для каждой стороны;

    • самая «дальняя» от текущей цены точка входа: минимальная цена покупки (BuyLowPrice) и максимальная цена продажи (SelHigtPrice) с соответствующими объёмами (BuyLowLotsSelHigtLots).

Эти данные используются:

  • для решения, когда добавлять очередное колено сетки (через сравнение с iStep),

  • для расчёта средней цены корзины и общего тейк‑профита.​

Сеточная логика: добавление позиций и средний TP 

Добавление колен сетки

Фрагмент для покупок:

if(b == 0)
   if(trade.Buy(iStartLots, _Symbol, Ask, 0, Ask + iTakeProfit * _Point, ""))
      return;

if(b > 0)
   if((BuyLowPrice - Ask) >= iStep * _Point)
      if(trade.Buy(BuyLowLots * iMultiplier))
         return;

  • Если покупок ещё нет (b == 0), открывается первая сделка:

    • объём — iStartLots,

    • тейк‑профит — Ask + iTakeProfit * _Point.

  • Если покупки уже есть (b > 0) и цена ушла против позиции минимум на iStep пунктов от худшего входа (BuyLowPrice - Ask >= iStep * _Point), открывается новая позиция:

    • объём наращивается как BuyLowLots * iMultiplier, т.е. множится объём самого «крайнего» ордера.

Для продаж применяется зеркальная формула, используя SelHigtPrice и SelHigtLots.​

Это классический пример фиксированного шага сетки с геометрическим ростом объёма, типичной для сеточных и мартингейл‑подобных систем.​

Расчёт средней цены и целевого тейк‑профита
double BuyAwerage = 0;
double SelAwerage = 0;

if(b >= 2)
   BuyAwerage = NormalizeDouble(BuyPricSumm / BuyLotsSumm + iProfitPlus * _Point, _Digits);
if(s >= 2)
   SelAwerage = NormalizeDouble(SelPricSumm / SelLotsSumm - iProfitPlus * _Point, _Digits);

  • Средняя цена покупки:

    • PriceavgBuy=∑ (BuyPriceOpen_i * BuyLot_i) / ∑BuyLot

    • К ней добавляется iProfitPlus пунктов, чтобы общий TP находился выше нулевой точки и корзина закрывалась с небольшим плюсом.

  • Для продаж средняя цена считается аналогично, а iProfitPlus вычитается, так как прибыль по sell возникает при падении цены ниже средней.​

  • NormalizeDouble(..., _Digits) приводит результат к нужному количеству знаков после запятой, что является обязательной практикой в MQL5 перед установкой цен SL/TP.


Модификация TP по всем позициям корзины
for(int i = 0; i < total; i++)
   if(posit.SelectByIndex(i))
      if(posit.Symbol() == _Symbol)
         if(posit.Magic() == iMagicNumber)
           {
            if(BuyAwerage > 0)
               if(posit.PositionType() == POSITION_TYPE_BUY)
                  if(NormalizeDouble(posit.TakeProfit(), _Digits) != BuyAwerage)
                     trade.PositionModify(posit.Ticket(), 0, BuyAwerage);

            if(SelAwerage > 0)
               if(posit.PositionType() == POSITION_TYPE_SELL)
                  if(NormalizeDouble(posit.TakeProfit(), _Digits) != SelAwerage)
                     trade.PositionModify(posit.Ticket(), 0, SelAwerage);
           }
  • Для каждой позиции нужного символа и magic робот:

    • если есть корректно посчитанный BuyAwerage/SelAwerage, сравнивает текущий TP позиции с целевым;

    • при расхождении вызывает trade.PositionModify(ticket, sl, tp) с новым TP (SL = 0, т.е. без стоп‑лосса).​

  • Таким образом, вся сетка закрывается по единой цене: как только рынок доходит до уровня средней цены +/− iProfitPlus, закрываются все колена, и корзина фиксирует общий результат.​

Такой подход характерен для грид‑роботов: главное — не отдельный ордер, а итог всей серии, поэтому TP привязывается к средневзвешенной позиции корзины.​

Риски и направления доработки для реала

Для тестера стратегия уже рабочая, но для реального счёта есть необходимость серьёзной доработки. На что стоит обратить внимание:

  • Проверки торговых условий брокера:

    • минимальный и максимальный лот, шаг лота, минимальная дистанция до TP/SL, стоп‑уровни и freeze‑уровни.​

    • при нарушении этих ограничений PositionModify и торговые операции могут возвращать ошибки (invalid stops, volume limit и т.п.).​

  • Обработка ошибок торгового сервера: логирование GetLastError(), повторные попытки при временных ошибках, фильтр торговых сессий и новостных пауз.​

  • Управление риском на уровне счёта:

    • ограничение максимального количества колен сетки и суммарного лота;

    • контроль допустимой просадки по equity и соотношения маржи/свободных средств.​

  • Оптимизация параметров:

    • подбор iRSI_Period, уровней iRSI_Up_Lvl/iRSI_Dw_LvliStepiMultiplieriTakeProfit и iProfitPlus по отдельным инструментам и таймфреймам через тестер и оптимизацию.​

Тогда эта реализация станет не только учебным примером работы с RSI и грид‑логикой в MQL5, но и основой для более продвинутого промышленного советника с контролируемым риском и гибкой фильтрацией входов.


Исходный код прикрепить не получается, по этому просто вставлю его в текст
//+------------------------------------------------------------------+
//|                                                  VR RSI Grid.mq5 |
//|                                      Copyright 2025, Trading-Go. |
//|                                           https://trading-go.ru/ |
//+------------------------------------------------------------------+
//| 🤖 Торговые роботы, индикаторы, скрипты                          |
//| 📲 Телеграмм группа: https://t.me/tradinggoea                    |
//|                                                                  |
//| 💥 Видео уроки, примеры, коды, логика, разбор                    |
//| 📲 Телеграмм группа: https://t.me/mql_master_group               |
//+------------------------------------------------------------------+

#property copyright "Copyright 2025, Trading-Go."
#property link      "https://trading-go.ru/"
#property version   "25.120"

#include <Trade\Trade.mqh>        CTrade        trade; // --- Подключение глобального класса для торговли
#include <Trade\PositionInfo.mqh> CPositionInfo posit; // --- Подключаем библиотеку класс PositionInfo

input double  iStartLots   = 0.01; // Start Lots
input double  iMultiplier  = 2.0;  // Multiplier
input double  iProfitPlus  = 30;   // Profit Plus
input int     iStep        = 200;  // Step
input int     iTakeProfit  = 200;  // Take Profit

input int     iRSI_Period  = 14;   // RSI Period
input double  iRSI_Up_Lvl  = 70;   // RSI Up lvl
input double  iRSI_Dw_Lvl  = 30;   // RSI Dw lvl

input int     iMagicNumber = 227;  // Magic Number
input int     iSlippage    = 30;   // Magic Number

int handle = -1;
double rsi[];
//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
// --- Отправляем MagicNumber в CTrade
   trade.SetExpertMagicNumber(iMagicNumber);
// --- Отправляем Slippage в CTrade
   trade.SetDeviationInPoints(iSlippage);
// --- Отправляем Symbol() в CTrade
   trade.SetTypeFillingBySymbol(_Symbol);
// --- Указываем CTrade что тип счета Хедж
   trade.SetMarginMode();
// ---
   handle = ::iRSI(_Symbol, PERIOD_CURRENT, iRSI_Period, PRICE_CLOSE);
// ---
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
  {

   int total = ::PositionsTotal(), b = 0, s = 0;
   double   BuyPricSumm = 0,   SelPricSumm = 0, BuyLotsSumm = 0, SelLotsSumm = 0;

   double   BuyLowPrice = 0,   SelHigtPrice = 0;
   double   BuyLowLots  = 0,   SelHigtLots  = 0;

   double   Bid = ::SymbolInfoDouble(_Symbol, SYMBOL_BID);
   double   Ask = ::SymbolInfoDouble(_Symbol, SYMBOL_ASK);

   for(int i = 0; i < total; i++)
      if(posit.SelectByIndex(i))
         if(posit.Symbol() == _Symbol)
            if(posit.Magic() == iMagicNumber)
               if(posit.PositionType() == POSITION_TYPE_BUY)
                 {
                  b++;
                  BuyPricSumm += posit.PriceOpen() * posit.Volume();
                  BuyLotsSumm += posit.Volume();

                  if(posit.PriceOpen() < BuyLowPrice || BuyLowPrice == 0)
                    {
                     BuyLowPrice = posit.PriceOpen();
                     BuyLowLots = posit.Volume();
                    }

                 }
               else
                  if(posit.PositionType() == POSITION_TYPE_SELL)
                    {
                     s++;
                     SelPricSumm += posit.PriceOpen() * posit.Volume();
                     SelLotsSumm += posit.Volume();

                     if(posit.PriceOpen() > SelHigtPrice || SelHigtPrice == 0)
                       {
                        SelHigtPrice = posit.PriceOpen();
                        SelHigtLots = posit.Volume();
                       }
                    }


   if(::CopyBuffer(handle, 0, 0, 5, rsi) != -1)
     {
     
      ::ArraySetAsSeries(rsi, true);

      if(rsi[2] < rsi[1] && rsi[2] < iRSI_Dw_Lvl && rsi[1] > iRSI_Dw_Lvl)
        {
         if(b == 0)
            if(trade.Buy(iStartLots, _Symbol, Ask, 0, Ask + iTakeProfit * _Point, ""))
               return;

         if(b > 0)
            if((BuyLowPrice - Ask) >= iStep * _Point)
               if(trade.Buy(BuyLowLots * iMultiplier))
                  return;
        }

      if(rsi[2] > rsi[1] && rsi[2] > iRSI_Up_Lvl && rsi[1] < iRSI_Up_Lvl)
        {
         if(s == 0)
            if(trade.Sell(iStartLots, _Symbol, Bid, 0, Bid - iTakeProfit * _Point, ""))
               return;

         if(s > 0)
            if((Bid - SelHigtPrice) >= iStep * _Point)
               if(trade.Sell(SelHigtLots * iMultiplier))
                  return;
        }
     }
// ===
// Расчет средних цен
// ===
   double BuyAwerage = 0;
   double SelAwerage = 0;
   
   if(b >= 2)
      BuyAwerage = NormalizeDouble(BuyPricSumm / BuyLotsSumm + iProfitPlus * _Point, _Digits);
   if(s >= 2)
      SelAwerage = NormalizeDouble(SelPricSumm / SelLotsSumm - iProfitPlus * _Point, _Digits);
// ===
// Модификация позиций
// ===
   for(int i = 0; i < total; i++)
      if(posit.SelectByIndex(i))
         if(posit.Symbol() == _Symbol)
            if(posit.Magic() == iMagicNumber)
              {
               if(BuyAwerage > 0)
                  if(posit.PositionType() == POSITION_TYPE_BUY)
                     if(NormalizeDouble(posit.TakeProfit(), _Digits) != BuyAwerage)
                        trade.PositionModify(posit.Ticket(), 0, BuyAwerage);

               if(SelAwerage > 0)
                  if(posit.PositionType() == POSITION_TYPE_SELL)
                     if(NormalizeDouble(posit.TakeProfit(), _Digits) != SelAwerage)
                        trade.PositionModify(posit.Ticket(), 0, SelAwerage);
              }
  }
//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {

  }
//+------------------------------------------------------------------+

💥 Видео уроки, примеры, коды, логика, разбор
📲 Телеграмм группа: t.me/mql_master_group



494 | ★1
1 комментарий
Не взлетит.
Подгоните под 14, будут хорошими впоследствии 4 или 40, например. А при 14 жёсткий минус.
Ну, и подгонять вверх и вниз нужно независимо, со своими параметрами каждое направление.
avatar

Читайте на SMART-LAB:
Фото
Топ-7 дивидендных акций. Что купить перед летним сезоном
Российский фондовый рынок приближается к самому масштабному в году дивидендному сезону — летнему. Разбираем топ интересных дивидендных...
X5 разыгрывает один миллион рублей в честь своего 20-летия
🔛 В честь своего 20-летнего юбилея запускаем акцию «Отличные дни Х5»: весь май торговые сети и бизнесы Х5 будут предлагать клиентам выгодные...
Фото
«Газпром» ― чего ждать от отчета за 2025 год?
До конца апреля «Газпром» планирует представить отчетность по МСФО по итогам 2025 года. По оценкам аналитиков «Финама», по итогам года...
Фото
Какой убыток мог быть у Магнита в 2025 году?
На этой неделе, вероятно, под занавес сезона годовых отчетов, свои результаты должен опубликовать Магнит. Что ждать и насколько все плохо?

теги блога Voldemar227

....все тэги



UPDONW
Новый дизайн