English 中文 Español Deutsch 日本語 Português 한국어 Français Italiano Türkçe
Как создать свой Trailing Stop

Как создать свой Trailing Stop

MetaTrader 5Индикаторы | 5 августа 2010, 14:42
28 871 38
Dmitry Fedoseev
Dmitry Fedoseev

Введение

Перед тем как приступить к разговору на тему статьи, предлагаю расставить точки над i. Лишний раз не помешает определиться с понятиями "позиция" и "ордер":

  • Позиция - это рыночное обязательство, количество купленных или проданных контрактов по финансовому инструменту. Позиция по одному инструменту может быть только одна.
  • Ордер - это распоряжение брокерской компании купить или продать финансовый инструмент. Различают несколько типов ордеров: рыночные и отложенные, а также стоп ордера (Стоп Лосс и Тейк Профит).

Позиции и ордера

Рис. 1. Позиции и ордера.

В данной статье речь пойдет о трейлинге уровня Стоп Лосс для позиций. Для отложенных ордеров данная операция не имеет смысла, так как двигать можно непосредственно цену ордера. А уж когда он превратится в позицию (или ее часть), тогда вам и пригодится нижеизложенный материал.

Торговую позицию можно закрыть не только при помощи кнопки "Закрыть" окна управления позицией (рис. 2).

 
Рис. 2. Закрытие позиции кнопкой "Закрыть" окна управления позицией. 1 - открыть контекстное меню позиции, 2 - выбрать команду "Закрыть позицию",
3 - нажать кнопку "Закрыть".

Кроме этого, позиция может быть закрыта автоматически при достижении ценой заранее установленного уровня прибыли (Тейк Профит) или уровня убытка (Стоп Лосс). В отличие от закрытия позиции кнопкой "Закрыть", закрытие по Стоп Лосс и Тейк Профит выполняется не из терминала (трейдером или экспертом), а брокером. Таким образом, обеспечивается абсолютно гарантированное закрытие позиции, независимо от наличия связи и электропитания. Это делает применение Стоп Лосс практически обязательным элементом в работе трейдера.

Единственное действие, которое должен сделать трейдер, - отдать брокеру приказ на установку уровня защитной остановки. Другими словами, необходимо установить Стоп Лосс на позицию (или сразу открыть позиции с установленным уровнем). Установка Стоп Лосс выполняется при помощи команды "Изменить" контекстного меню терминала. В списке позиций терминала необходимо навести указатель мыши на позицию, нажать правую кнопку и выбрать команду "Изменить или удалить". После этого в открывшемся окне управления позицией нужно ввести необходимый уровень Стоп Лосс и нажать кнопку "Изменить" (рис. 3).

 
Рис. 3. Установка уровня Стоп Лосс позиции. 1 - открыть контекстное меню позиции, 2 - выбрать команду "Изменить или удалить", 3 - установить значение, 4 - нажать кнопку "Изменить".

Уровень Стоп Лосс позиции отображается на ценовом графике вместе с уровнем ее открытия (рис.4).


Рис. 4. Позиция со Стоп Лосс. Уровень обозначен красной пунктирной линией с надписью sl с левого края линии.

Стоп Лосс для позиции можно не только устанавливать, но и периодически менять его значение. Например, его можно подтягивать вслед за изменениями цены в сторону прибыльности позиции, тем самым сокращая возможный убыток. Такое подтягивание защитного стоп-уровня собственно и называется скользящим стопом или трейлинг стопом.

Существует огромное количество вариантов трейлинг стопа: Стоп Лосс можно просто подтягивать за ценой на заданной дистанции. Можно начинать перемещать Стоп Лосс не сразу, а после того, как позиция достигла определенной прибыли; при этом сначала перенести его сразу на уровень безубыточности. Такой вариант считается стандартным и даже встроен в терминал MetaTrader 5. Для использования стандартного трейлинг стопа необходимо открыть контекстное меню позиции и выбрать команду "Трейлинг стоп" (рис. 5).

 
Рис. 5. Включение стандартного трейлинг стопа в терминале. 1 - открыть контекстное меню позиции, 2 - выбрать команду "Трейлинг стоп",  3 - выбрать значение (или установить значение).
Команда  "Установить значение"
находится в самом низу контекстного меню, на изображении не показана.

Кроме непосредственного отслеживания цены, трейлинг стоп может работать на основе какого-нибудь технического индикатора. Например, на основе скользящих средних, что позволяет не реагировать на кратковременные изменения цены; на индикаторе Ichimoku или на более подходящем; и даже на предназначенном для этих целей индикаторе Parabolic SAR (Stop And Reverse - стоп и разворот), рис. 6.


Рис. 6. Индикатор Parabolic SAR. 

При процедурном программировании на MQL4 трейлинг стоп обычно выполнялся в виде отдельной функции или был интегрирован в другие функции. Например, в эксперте MACD Sample, входящем в комплект терминала MetaTrader 4, функция трейлинга интегрирована с функцией рыночного закрытия ордеров:

for(cnt=0;cnt<total;cnt++)
  {
   OrderSelect(cnt,SELECT_BY_POS,MODE_TRADES);
   if(OrderType()<=OP_SELL &&         // check for opened position 
      OrderSymbol()==Symbol())        // check for symbol
     {
      if(OrderType()==OP_BUY)         // long position is opened
        {
         // should it be closed?
         if(MacdCurrent>0 && MacdCurrent<SignalCurrent && MacdPrevious>SignalPrevious && 
            MacdCurrent>(MACDCloseLevel*Point))
           {
            OrderClose(OrderTicket(),OrderLots(),Bid,3,Violet); // close position
            return(0); // exit
           }
         // check for trailing stop
         if(TrailingStop>0) 
           {
             
            if(Bid-OrderOpenPrice()>Point*TrailingStop)
              {
               if(OrderStopLoss()<Bid-Point*TrailingStop)
                 {
                  OrderModify(OrderTicket(),OrderOpenPrice(),Bid-Point*TrailingStop,OrderTakeProfit(),0,Green);
                  return(0);
                 }
              }
           }
        }
      else // go to short position
        {
         // should it be closed?
         if(MacdCurrent<0 && MacdCurrent>SignalCurrent && 
            MacdPrevious<SignalPrevious && MathAbs(MacdCurrent)>(MACDCloseLevel*Point))
           {
            OrderClose(OrderTicket(),OrderLots(),Ask,3,Violet); // close position
            return(0); // exit
           }
         // check for trailing stop
         if(TrailingStop>0) 
           {
             
            if((OrderOpenPrice()-Ask)>(Point*TrailingStop))
              {
               if((OrderStopLoss()>(Ask+Point*TrailingStop)) || (OrderStopLoss()==0))
                 {
                  OrderModify(OrderTicket(),OrderOpenPrice(),Ask+Point*TrailingStop,OrderTakeProfit(),0,Red);
                  return(0);
                 }
              }
           }
        }
     }
  }

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


1. Создание базового класса трейлинга

Как уже говорилось выше, разновидностей трейлинг стопа существует огромное количество, но у всех у них есть общие функциональные моменты:

  • определение типа (направления) позиции;
  • определение текущего уровня Стоп Лосс позиции;
  • расчет нового уровня Стоп Лосс;
  • проверка на необходимость изменения текущего уровня Стоп Лосс;
  • модификация уровня Стоп Лосс для позиции.

От типа трейлинг стопа будет зависеть только значение расчетного уровня Стоп Лосс. Таким образом, основной функционал трейлинга будет входить в базовый класс. Для функционала, зависящего от типа трейлинга, будут созданы подклассы. Обращение к методам этих подклассов будет выполняться через виртуальные методы базового класса.

Поскольку планируется использовать технические индикаторы, для обеспечения их устойчивой работы требуется обеспечить к ним периодическое обращение. Для этой цели будет использоваться таймер. Запланирована возможность программного включения/выключения трейлинга (при использовании класса в составе механической торговой системы), и включение/выключение при помощи графического объекта кнопки (при использовании класса в составе вспомогательных экспертов). В соответствии с этими функциональными требованиями базовый класс будет иметь следующий набор методов:

class CTrailingStop
  {
protected:
public:
   void CTrailingStop(){};
   void ~CTrailingStop(){};
   void Init(){};                   // Инициализация класса
   bool StartTimer(){};             // Запуск таймера
   void StopTimer(){};              // Остановка таймера
   void On(){};                     // Включение трейлинг стопа
   void Off(){};                    // Выключение трейлинг стопа
   bool DoStoploss(){};             // Основной метод управления уровнем Стоп Лосс позиции
   void EventHandle(){};            // Метод обработки событий графика (нажатия кнопки включения трейлинг стопа)
   void Deinit(){};                 // Деинициализация
   virtual bool Refresh(){};        // Обновление показаний индикатора
   virtual void Setparameters(){};  // Установка параметров индикатора и загрузка индикатора
   virtual int Trend(){};           // Тренд, показываемый индикатором
   virtual double BuyStoploss(){};  // Значение Стоп Лосс для позиции buy
   virtual double SellStoploss(){}; // Значение Стоп лосс для позиции sell
  };

При вызове метода Init() ему будут передаваться общие параметры, не зависящие от типа используемого трейлинг стопа. При выполнении метода будет производиться установка режима работы трейлинг стопа и подготовка переменных с некоторыми рыночными параметрами.

  • Метод StartTimer() - будет использоваться для запуска таймера, необходимого для периодического обращения к индикаторам и принудительного удержания их в кэше терминала.
  • Метод Stoptimer() - будет использоваться для остановки таймера при завершении работы эксперта.
  • Метод On() - программное включение трейлинг стопа и установка кнопки в нажатое положение (если кнопка используется).
  • Метод Off() - программное выключение трейлинг стопа и установка кнопки в отжатое положение (если кнопа используется).
  • Метод DoStoploss() - основной метод для управления уровнем Стоп Лосс позиции.
  • Метод EventHandle() - используется для обработки событий графика, в частности, для реагирования на нажатие кнопки и включение/выключение трейлинг стопа в зависимости от положения кнопки.
  • Метод Deinit() - выполняется при завершении работы эксперта, обеспечивает освобождение хэндла используемого индикатора.
  • Метод Refresh() - обеспечивает обновление значений индикатора. Метод необходим для определения текущих значений индикатора перед вычислением значений стоплосс. Кроме того, метод используется самостоятельно - периодически вызывается по таймеру для поддержания индикаторов в работоспособном состоянии.
  • Метод SetParameters() - при вызове метода ему передаются параметры индикатора, загружается индикатор с указанными параметрами.
  • Метод Trend() - метод определения тренда показываемого индикатором. Если индикатор показывает направление вверх, метод возвращает значение 1, если вниз - значение -1.
  • Методы BuyStoploss() и SellStoploss() - будут возвращать рассчитанные по индикатору новые значения Стоп Лосс для позиций buy и sell соответственно. 

1.1. Метод Init()

Метод Init() является первым методом, вызываемым после создания экземпляра класса. В него передаются общие, не зависящие от типа трейлинг стопа, параметры: символ, таймфрейм; устанавливается режим работы трейлинг стопа: по тикам или по барам; присоединять или не присоединять индикатор на график, создавать или нет кнопку. Затем свойства кнопки: координата X кнопки, координата Y кнопки, цвет кнопки, цвет надписи на кнопке.

Необходимые в дальнейшей работе параметры сохраняются в переменных класса. Дополнительно, при выполнении метода Init(), определяются основные неизменные рыночные параметры, необходимые для работы трейлинг стопа: количество знаков после запятой и величина пункта. Наконец, в зависимости от выбранного типа трейлинг стопа, формируется имя кнопки и надпись на ней. Если установлено использование кнопки, то выполняется ее создание. 

В разделе "protected" объявим все необходимые переменные:

protected:
string m_symbol;             // символ
ENUM_TIMEFRAMES m_timeframe; // таймфрейм
bool m_eachtick;             // работать на каждом тике
bool m_indicator;            // показывать индикатор на графике
bool m_button;               // показывать кнопку включения/выключения
int m_button_x;              // координата х кнопки
int m_button_y;              // координата у кнопки
color m_bgcolor;             // цвет кнопки
color m_txtcolor;            // цвет надписи кнопки
int m_shift;                 // смещение бара
bool m_onoff;                // включено/выключено
int m_handle;                // хэндл индикатора
datetime m_lasttime;         // время последнего выполнения трейлинг стопа
MqlTradeRequest m_request;   // структура торгового запроса
MqlTradeResult m_result;     // структура результата торгового запроса
int m_digits;                // количество знаков после запятой у цены
double m_point;              // значение пункта
string m_objname;            // имя кнопки
string m_typename;           // имя типа трейлинг стопа
string m_caption;            // надпись на кнопке

Теперь напишем сам метод Init():

//--- Метод инициализации трейлинг стопа
void Init(string             symbol,
          ENUM_TIMEFRAMES timeframe,
          bool   eachtick  =   true,
          bool   indicator =  false,
          bool   button    =  false,
          int    button_x  =      5,
          int    button_y  =     15,
          color  bgcolor   = Silver,
          color  txtcolor  =   Blue)
  {
//--- установка параметров
   m_symbol    = symbol;    // символ
   m_timeframe = timeframe; // таймфрейм
   m_eachtick  = eachtick;  // true - работать на каждом тике, false - работать раз на бар 
//--- установка бара, с которого используется значение индикатора
   if(eachtick)
     {
      m_shift=0; // формирующийся бар, при потиковом режиме
     }
   else
     {
      m_shift=1; // сформированный бар при побарном режиме
     }
   m_indicator = indicator; // true - присоединять индикатор на график
   m_button    = button;    // true - создавать кнопку для включения/выключения трейлинг стопа
   m_button_x  = button_x;  // координата Х кнопки
   m_button_y  = button_y;  // координата Y кнопки
   m_bgcolor   = bgcolor;   // цвет кнопки
   m_txtcolor  = txtcolor;  // цвет надписи на кнопке 
//--- получение неизменной рыночной информации 
   m_digits=(int)SymbolInfoInteger(m_symbol,SYMBOL_DIGITS); // количество знаков после запятой у цены
   m_point=SymbolInfoDouble(m_symbol,SYMBOL_POINT);         // значение пункта 
//--- формирование имени кнопки и надписи на ней
   m_objname="CTrailingStop_"+m_typename+"_"+symbol;        // имя кнопки
   m_caption=symbol+" "+m_typename+" Trailing";             // надпись на кнопке 
//--- заполнение структуры торгового запроса
   m_request.symbol=m_symbol;                               // подготовка структуры торгового запроса, установка символа
   m_request.action=TRADE_ACTION_SLTP;                      // подготовка структуры торгового запроса, установка типа торгового действия
//--- создание кнопки
   if(m_button)
     {
      ObjectCreate(0,m_objname,OBJ_BUTTON,0,0,0);                 // создание
      ObjectSetInteger(0,m_objname,OBJPROP_XDISTANCE,m_button_x); // установка координаты Х
      ObjectSetInteger(0,m_objname,OBJPROP_YDISTANCE,m_button_y); // установка координаты Y
      ObjectSetInteger(0,m_objname,OBJPROP_BGCOLOR,m_bgcolor);    // установка цвета фона
      ObjectSetInteger(0,m_objname,OBJPROP_COLOR,m_txtcolor);     // установка цвета надписи
      ObjectSetInteger(0,m_objname,OBJPROP_XSIZE,120);            // установка ширины
      ObjectSetInteger(0,m_objname,OBJPROP_YSIZE,15);             // установка высоты
      ObjectSetInteger(0,m_objname,OBJPROP_FONTSIZE,7);           // установка размера шрифта
      ObjectSetString(0,m_objname,OBJPROP_TEXT,m_caption);        // установка надписи 
      ObjectSetInteger(0,m_objname,OBJPROP_STATE,false);          // установка состояния кнопки, по умолчанию выключена, отжата
      ObjectSetInteger(0,m_objname,OBJPROP_SELECTABLE,false);     // пользователь не сможет выделять и перемещать кнопку, только нажимать
      ChartRedraw();                                              // обновление отображения графика 
     }
//--- установка состояния трейлинг стопа
   m_onoff=false;                                                 // состояние трейлинг стопа - включен/выключен, по умолчанию выключен 
  };

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

1.2. Метод StartTimer()

Метод StartTimer() выполняет запуск общего таймера эксперта.  

//--- Запуск таймера
bool StartTimer()
  {
   return(EventSetTimer(1));
  };

При использовании таймера, в функцию эксперта OnTimer() будет необходимо добавлять вызов метода Refresh(), чтобы периодически происходило обращение к индикатору.

1.3. Метод StopTimer()

Метод StartTimer() выполняет остановку таймера эксперта.  

//--- Остановка таймера
void StopTimer()
  {
   EventKillTimer();
  };

По завершении работы эксперта, если использовался таймер, его необходимо остановить, что и делает этот метод. Метод будет вызываться при выполнении метода Deninit() класса.

1.4. Метод On()

Метод On() позволяет производить программное включение трейлинг стопа. Включение выполняется установкой для переменной m_onoff значения true. Если при инициализации класса установлено использование кнопки, то она переводится в нажатое положение. 

//--- Включение трейлинг стопа
void On()
  {
   m_onoff=true; 
   if(m_button)
     { // если используется кнопка, она переводится в нажатое положение
      if(!ObjectGetInteger(0,m_objname,OBJPROP_STATE))
        {
         ObjectSetInteger(0,m_objname,OBJPROP_STATE,true);
        }
     }
  }

1.5. Метод Off()

Метод On() позволяет производить программное выключение трейлинг стопа. Выключение выполняется установкой для переменной m_onoff значения false. Если при инициализации класса установлено использование кнопки, то она переводится в отжатое положение. 

//--- Выключение трейлинг стопа
void Off()
  {
   m_onoff=false;
   if(m_button)
     { // Если используется кнопка, она переводится в отжатое положение
      if(ObjectGetInteger(0,m_objname,OBJPROP_STATE))
        {
         ObjectSetInteger(0,m_objname,OBJPROP_STATE,false);
        }
     }
  }

1.6. Метод EventHandle()

Метод EventHandle() будет вызываться из функции OnChartEvent() эксперта и соответственно ему будут передаваться все параметры, передаваемые в функцию OnChartEvent().

//--- Метод отслеживания нажатия/отжатия кнопки
void EventHandle(const int id,const long  &lparam,const double &dparam,const string &sparam)
  {
   if(id==CHARTEVENT_OBJECT_CLICK && sparam==m_objname)
     { // есть событие с кнопкой
      if(ObjectGetInteger(0,m_objname,OBJPROP_STATE))
        { // проверка состояния кнопки
         On(); // включение
        }
      else
        {
         Off(); // выключение
        }
     }
  }

Если происходит событие CHARTEVENT_OBJECT_CLICK, и это событие происходит с кнопкой, имеющей имя m_objname (имя объекта, с которым произошло событие, передается в переменной sparam), то в зависимости от состояния кнопки выполняется метод On() или Off().

1.7. Метод Deinit()

Метод Deinit() должен вызываться по завершении работы эксперта. При выполнении метода происходит остановка таймера, освобождение хэндла индикатора и удаление кнопки, если она использовалась. 

//--- Метод деинициализации
void Deinit()
  {
   StopTimer();                  // остановка таймера
   IndicatorRelease(m_handle);   // освобождение хэндла индикатора
   if(m_button)
     {
      ObjectDelete(0,m_objname); // удаление кнопки
      ChartRedraw();             // обновление отображения графика 
     }
  }

Виртуальные методы Refresh(), SetParameters(), Trend(), BuyStoploss(), SellStoploss() будут рассмотрены позже - при создании подклассов трейлинг стопа, а пока подробно рассмотрим основной метод базового класса - метод DoStoploss().

1.8. Метод DoStoploss()

Метод DoStoploss() является основным рабочим методом, который должен вызываться из функции OnTick() эксперта на каждом тике. Если значение переменной m_onoff равно false (трейлинг стоп выключен), то сразу выполняется завершение работы метода.  

if(!m_onoff)
  {
   return(true);// если трейлинг стоп выключен
  }

Далее, если трейлинг стоп работает в побарном режиме, происходит проверка времени - сравнение времени формирующегося бара со временем последнего успешного выполнения функции. Если время совпадает, то происходит завершение работы метода.

datetime tm[1];
// в побарном режиме получаем время последнего бара 
if(!m_eachtick)
  { 
   // если не удалось скопировать время, завершаем работу метода, повтор произойдет на следующем тике, 
   if(CopyTime(m_symbol,m_timeframe,0,1,tm)==-1)
     {
      return(false); 
     }
   // если время бара равно времени последнего выполнения метода - завершаем работу метода
   if(tm[0]==m_lasttime)
     { 
      return(true);
     }
  }

Если трейлинг стоп включен и проверка времени пройдена, то выполняется основная часть метода - обновляются показания индикатора - вызывается метод Refresh().

if(!Refresh())
  { // получаем значения индикатора
   return(false);
  }

Затем, в зависимости от значения возвращаемого методом Trend(), выполняется трейлинг стоп для позиции buy или sell.

// в зависимости от тренда показываемого индикатором выполняем различные действия
switch (Trend())
  {
   // тренд вверх
   case 1: 
      // код трейлинг стопа позиции buy
      break;
   // тренд вниз
   case -1: 
      // код трейлинг стопа позиции sell
      break;
  }

Рассмотрим его работу на примере позиции buy.

if(PositionSelect(m_symbol,1000))
  {   //--- выделение позиции, если ее удалось выделить, значит, позиция существует
   if(PositionGetInteger(POSITION_TYPE)==POSITION_TYPE_BUY)
     {//--- если позиция buy

      //--- получаем значение стоплосс для позиции buy
      sl=BuyStoploss(); 
      //--- определяем допустимый уровень установки стоплосс для позиции buy
      double minimal=SymbolInfoDouble(m_symbol,SYMBOL_BID)-m_point*SymbolInfoInteger(m_symbol,SYMBOL_TRADE_STOPS_LEVEL);
      //--- нормализация значения
      sl=NormalizeDouble(sl,m_digits); 
      //--- нормализация значения
      minimal=NormalizeDouble(minimal,m_digits); 
      //--- если на полученный от индикатора уровень невозможно установить стоплосс, 
      //    он будет установлен на ближайший возможный уровень
      sl=MathMin(sl,minimal); 
      //--- значение стоплосс позиции
      double possl=PositionGetDouble(POSITION_SL); 
      //--- нормализация значения
      possl=NormalizeDouble(possl,m_digits); 
      if(sl>possl)
        {//--- если новое значение стоплосс выше, чем текущее значение стоплосс, 
         //    будет выполнена попытка переместить стоплосс на новый уровень
         //--- заполнение структуры запроса
         m_request.sl=sl; 
         //--- заполнение структуры запроса
         m_request.tp=PositionGetDouble(POSITION_TP); 
         //--- запрос
         OrderSend(m_request,m_result); 
         if(m_result.retcode!=TRADE_RETCODE_DONE)
           {//--- проверка результата выполнения запроса
            //--- вывод в журнал сообщения об ошибке 
            printf("Не удалось переместить стоплосс позиции %s, ошибка: %I64u",m_symbol,m_result.retcode); 
            //--- не удалось переместить стоплосс, завершаем выполнение
            return(false); 
           }
        }
     }
  }

Если позицию удается выделить, проверяется ее тип. Если тип позиции соответствует тренду, то, используя метод BuyStoploss(), получаем требуемое значение Стоп Лосс (в переменную sl). Далее определяем допустимый уровень, на который может быть установлен Стоп Лосс. Если расчетный уровень ближе допустимого, корректируем значение переменой sl. Затем получаем текущее значение Стоп Лосс позиции (в переменную possl), сравниваем значения sl и possl. Если новое значение Стоп Лосс лучше текущего значения, выполняем модификацию позиции.

Перед выполнением модификации заполняем поля sl и tp структуры MqlTradeRequest. Переменной m_request.sl присваивается требуемое значение Стоп Лосс, переменной m_request.tp - существующее значение Тейк Профит (его оставляем без изменений); остальные поля уже заполнены при выполнении метода Init(). После заполнения структуры вызывается функция OrderSend().

По завершении ее работы проверяется значение переменной m_result.retcode. Если значение не равно TRADE_RETCODE_DONE, значит по какой-то причине не удалось выполнить запрошенное функцией OrderSend() действие. При этом в журнал выводится сообщение с номером ошибки и выполняется завершение метода. Если функция OrderSend() выполнена успешно, то запоминается время бара, на котором производилась последняя работа метода DoStoploss(). В случае же ошибки, даже при побарном режиме, на следующем тике произойдет повторная попытка работы метода. Попытки  будут продолжаться до тех пор, пока он не завершит свою работу успешно.

Ниже приведен весь код метода DoStopLoss().

bool DoStoploss()
  {
//--- если трейлинг стоп выключен
   if(!m_onoff)
     {
      return(true);
     }
   datetime tm[1];
//--- в побарном режиме получаем время последнего бара
   if(!m_eachtick)
     {
      //--- если не удалось скопировать время, завершаем работу метода, повтор произойдет на следующем тике, 
      if(CopyTime(m_symbol,m_timeframe,0,1,tm)==-1)
        {
         return(false);
        }
      //--- если время бара равно времени последнего выполнения метода - завершаем работу метода
      if(tm[0]==m_lasttime)
        {
         return(true);
        }
     }
//--- получаем значения индикатора
   if(!Refresh())
     {
      return(false);
     }
   double sl;
//--- в зависимости от тренда показываемого индикатором выполняем различные действия
   switch(Trend())
     {
      //--- тренд вверх
      case 1:
         //--- выделение позиции, если ее удалось выделить, значит позиция существует
         if(PositionSelect(m_symbol))
           {
            //--- если позиция buy
            if(PositionGetInteger(POSITION_TYPE)==POSITION_TYPE_BUY)
              {
               //--- получаем значение стоплосс для позиции buy
               sl=BuyStoploss();
               //--- определяем допустимый уровень установки стоплосс для позиции buy
               double minimal=SymbolInfoDouble(m_symbol,SYMBOL_BID)-m_point*SymbolInfoInteger(m_symbol,SYMBOL_TRADE_STOPS_LEVEL);
               //--- нормализация значения
               sl=NormalizeDouble(sl,m_digits);
               //--- нормализация значения
               minimal=NormalizeDouble(minimal,m_digits);
               //--- если на полученный от индикатора уровень невозможно установить стоплосс, 
               //    он будет установлен на ближайший возможный уровень
               sl=MathMin(sl,minimal);
               //--- значение стоплосс позиции
               double possl=PositionGetDouble(POSITION_SL);
               //--- нормализация значения
               possl=NormalizeDouble(possl,m_digits);
               //--- если новое значение стоплосс выше, чем текущее значение стоплосс, 
               //    будет выполнена попытка переместить стоплосс на новый уровень
               if(sl>possl)
                 {
                  //--- заполнение структуры запроса
                  m_request.sl=sl;
                  //--- заполнение структуры запроса
                  m_request.tp=PositionGetDouble(POSITION_TP);
                  //--- запрос
                  OrderSend(m_request,m_result);
                  //--- проверка результата выполнения запроса
                  if(m_result.retcode!=TRADE_RETCODE_DONE)
                    {
                     //--- вывод в журнал сообщения об ошибке 
                     printf("Не удалось переместить стоплосс позиции %s, ошибка: %I64u",m_symbol,m_result.retcode);
                     //--- не удалось переместить стоплосс, завершаем выполнение
                     return(false);
                    }
                 }
              }
           }
         break;
         //--- тренд вниз
      case -1:
         if(PositionSelect(m_symbol))
           {
            if(PositionGetInteger(POSITION_TYPE)==POSITION_TYPE_SELL)
              {
               sl=SellStoploss();
               //--- прибавляется спред, поскольку sell закрывается по цене Ask
               sl+=(SymbolInfoDouble(m_symbol,SYMBOL_ASK)-SymbolInfoDouble(m_symbol,SYMBOL_BID));
               double minimal=SymbolInfoDouble(m_symbol,SYMBOL_ASK)+m_point*SymbolInfoInteger(m_symbol,SYMBOL_TRADE_STOPS_LEVEL);
               sl=NormalizeDouble(sl,m_digits);
               minimal=NormalizeDouble(minimal,m_digits);
               sl=MathMax(sl,minimal);
               double possl=PositionGetDouble(POSITION_SL);
               possl=NormalizeDouble(possl,m_digits);
               if(sl<possl || possl==0)
                 {
                  m_request.sl=sl;
                  m_request.tp=PositionGetDouble(POSITION_TP);
                  OrderSend(m_request,m_result);
                  if(m_result.retcode!=TRADE_RETCODE_DONE)
                    {
                     printf("Не удалось переместить стоплосс позиции %s, ошибка: %I64u",m_symbol,m_result.retcode);
                     return(false);
                    }
                 }
              }
           }
         break;
     }
//--- запоминаем время последнего выполнения метода
   m_lasttime=tm[0];
   return(true);
  }

Обратите внимание на различия в коде для позиции buy и позиции sell. Для позиции sell значение, возвращаемое методом SellStoploss(), увеличивается на величину спреда, поскольку позиция sell закрывается по цене Ask. Соответственно, и отсчет минимального уровня Стоп Лосс для buy выполняется от цены Bid, для sell - от Ask.

На этом создание базового класса трейлинга выполнено, переходим к созданию подклассов. 

2. Подкласс трейлинга по индикатору Parabolic SAR

По виртуальным методам класса CTrailingStop уже должен быть понятен состав подкласса - методы SetParameters(), Refresh(), Trend(), BuyStoploss(), SellStoploss() и конструктор класса для установки имени трейлинг стопа. Класс будет иметь имя CParabolicStop. Поскольку класс является подклассом класса CTrailingStop, это будет указано при его объявлении.

class CParabolicStop: public CTrailingStop

За счет такого объявления при вызове виртуальных методов класса CParabolicStop будут выполняться методы подкласса выбранного при загрузке базового класса.  

Рассмотрим подробно все методы подкласса.

2.1. Метод CParabolicStop()

Метод имеет такое же имя, как и сам класс, такой метод называется конструктором. Он выполняется автоматически в момент загрузки класса, еще до того, как в программе будут вызваны другие методы класса. При выполнении метода CParabolicStop() выполняется присваивание названия трейлинг стопа переменной m_typename, которая используется для формирования имени и надписи кнопки (при выполнении метода Init() базового класса).

void CParabolicStop()
  {
   m_typename="SAR"; // установка имени трейлинг стопа
  };

2.2. Метод SetParameters()

При вызове метода SetParameters() ему передаются параметры индикатора, выполняется загрузка индикатора с этими параметрами. Если при выполнении метода Init() базового класса установлен параметр m_indicator, то выполняется присоединение индикатора на график (функция ChartIndicatorAdd()).

// Метод установки параметров и загрузки индикатора
bool SetParameters(double sarstep=0.02,double sarmaximum=0.2)
  {
   m_handle=iSAR(m_symbol,m_timeframe,sarstep,sarmaximum); // загрузка индикатора
   if(m_handle==-1)
     {
      return(false); // если не удалось загрузить индикатор, метод возвращает false
     }
   if(m_indicator)
     {
      ChartIndicatorAdd(0,0,m_handle); // присоединение индикатора к графику
     }
    
   return(true);
  }

2.3. Метод Refresh()

Метод Refresh() обеспечивает получение новой цены и обновление показаний индикатора. Для значения цены в разделе protected класса объявлен массив pricebuf, для значения индикатора - массив indbuf. Оба массива имеют размер в один элемент - необходимо только одно значение цены и одно значение индикатора с формирующегося или сформированного бара (в зависимости от параметра m_shift, установленного при инициализации базового класса).

// Метод получения значений индикатора
bool Refresh()
  {
   if(CopyBuffer(m_handle,0,m_shift,1,indbuf)==-1)
     {
      return(false); // если не удалось скопировать значение в массив, возвращается false
     }
    
   if(CopyClose(m_symbol,m_timeframe,m_shift,1,pricebuf)==-1)
     {
      return(false); // если не удалось скопировать значение в массив, возвращается false
     }    
   return(true); 
  }

2.4. Метод Trend()

В методе Trend() проверятся положение цены относительно линии индикатора. Если цена выше линии, значит тренд восходящий, и метод возвращает значение 1. Если цена ниже линии индикатора, значит тренд нисходящий, и возвращается значение -1. Не исключен случай (редко, но возможно), когда цена равна линии индикатора. В этом случае будет возвращаться значение 0.  

// Метод определения тренда
int Trend()
  {
   if(pricebuf[0]>indbuf[0])
     { // цена выше линии индикатора, тренд вверх
      return(1);
     }
   if(pricebuf[0]<indbuf[0])
     { // цена ниже линии индикатора, тренд вниз
      return(-1);
     }    
   return(0);
  }

2.5. Методы BuyStoploss() и SellStoploss()

Поскольку у индикатора Parabolic SAR только одна линия, оба метода полностью идентичны. Они возвращают значение, полученное при выполнении метода Refresh().

// Метод определения уровня стоплосс для buy
virtual double BuyStoploss()
  {
   return(indbuf[0]);
  };
// Метод определения уровня стоплосс для sell
virtual double SellStoploss()
  {
   return(indbuf[0]);
  };

На этом трейлинг стоп, можно сказать, готов. Пока он содержит только один подкласс, но им уже можно пользоваться. Оформим его в виде отдельного включаемого файла и сохраним в каталоге .\MQL5\Include с именем Sample_TrailingStop.mqh (файл прилагается к статье). 

3. Добавление трейлинга по Parabolic в эксперта

Попробуем добавить созданный трейлинг стоп в какого-нибудь эксперта, например в эксперта My_First_EA из статьи Пошаговое руководство по написанию MQL5-советников для начинающих.

3.1. Откроем файл эксперта My_First_EA в редакторе MetaEditor и сохраним его с именем My_First_EA_SARTrailing.

3.2. Подключим файл трейлинг стопа. В верхнюю часть кода эксперта (желательно до объявления внешних переменных) добавим строку: 

#include <Sample_TrailingStop.mqh> // подключение класса трейлинг стопа

3.3. Ниже внешних переменных создаем экземпляр класса CParabolicStop с именем Trailing.

CParabolicStop Trailing; // создание экземпляра класса 

 3.4. В функции OnInit() эксперта проводим инициализацию класса и установку его параметров. Предварительно объявим внешние переменные с параметрами индикатора:

input double TrailingSARStep=0.02;
input double TrailingSARMaximum=0.2;

Затем добавим код в функцию OnInit().

Trailing.Init(_Symbol,PERIOD_CURRENT,true,true,false); // инициализация (установка основных параметров)
if(!trailing.setparameters(TrailingSARStep,TrailingSARMaximum))
  { // установка параметров используемого типа трейлинг стопа
   Alert("trailing error");
   return(-1);
  } 
Trailing.StartTimer(); // Запуск таймера
Trailing.On();         // Включение

3.5. Ищем в коде эксперта функцию OnTimer(). В эксперте My_First_EA не используется функция OnTimer(), значит добавляем ее, а в нее добавляем вызов метода Refresh(). 

void OnTimer()
  {
   Trailing.Refresh();
  }

3.6. В самый верх функции OnTick() эксперта добавляем вызов метода DoStoploss().

3.7. Компилируем эксперта и пробуем его протестировать. Результаты тестирования эксперта приведены на рис. 7 (без трейлинг стопа) и рис. 8 (с трейлинг стопом).


Рис. 7. Результаты тестирования эксперта без трейлинг стопа. 


 
Рис. 8. Результаты тестирования эксперта с трейлинг стопом.

Повышение эффективности при использовании трейлинг стопа очевидно.

Файл My_First_EA_SARTrailing.mq5 прилагается к статье.

4. Подкласс трейлинга по индикатору NRTR

Индикатор NRTR (Nick Rypock Trailing Reverse) по своему названию (Trailing Reverse - подтягивание и переворот) и внешнему виду (рис. 9) располагает к тому, чтобы попробовать создать на нем трейлинг стоп.

 
Рис. 9. Индикатор NRTR

Индикатор рисует базовую линию (линию поддержки или сопротивления) и линию цели. При превышении ценой значения цели, базовая линия переносится по ходу движения цены, незначительные колебания цены игнорируются. Пересечение ценой базовой линии считается сменой тренда, при этом меняется расположение базовой линии и линии цели относительно цены. Линии поддержки и цели при тренде вверх окрашены синим цветом, при тренде вниз - красным.

Создадим еще один трейлинг стоп, теперь на индикаторе NRTR.

Объявим еще один класс CNRTRStop входящий в базовый класс CNRTRStop. 

class CNRTRStop: public CTrailingStop

Подкласс трейлинга по NRTR будет иметь точно такой же набор методов как трейлинг по Parabolic, за исключением конструктора класса, теперь он будет иметь имя CNRTRStop().  

4.1. Метод CNRTRStop()

Теперь в конструкторе класса переменной m_typename будет присваиваться значение NRTR в соответствии с используемым индикатором.

void CNRTRStop()
  {
   m_typename="NRTR"; // установка имени трейлинг стопа
  };

4.2. Метод SetParameters()

При вызове метода SetParameters() ему будут передаваться параметры индикатора NRTR и выполняться его загрузка. Затем, в зависимости от основных параметров трейлинга, будет выполняться присоединение индикатора на график.

// Метод установки параметров и загрузки индикатора
bool SetParameters(int period,double k)
  {
   m_handle=iCustom(m_symbol,m_timeframe,"NRTR",period,k); // загрузка индикатора
   if(m_handle==-1)
     { // если не удалось загрузить индикатор, метод возвращает false
      return(false); 
     }
   if(m_indicator)
     {
       
      ChartIndicatorAdd(0,0,m_handle); // присоединение индикатора к графику
     }
   return(true);
  }

4.3. Метод Refresh()

В методе Refresh() выполняется копирование двух буферов индикатора NRTR - буфера линии поддержки и буфера линии сопротивления.  

 // Метод получения значений индикатора
bool Refresh()
  {
   if(CopyBuffer(m_handle,0,m_shift,1,sup)==-1)
     {
      return(false); // если не удалось скопировать значение в массив, возвращается false
     }
    
   if(CopyBuffer(m_handle,1,m_shift,1,res)==-1)
     {
      return(false); // если не удалось скопировать значение в массив, возвращается false
     }
    
   return(true);
  }

Для значений индикатора в разделе protected класса объявлено два массива: double sup[] и double res[].

protected:
double sup[1]; // значение уровня поддержки
double res[1]; // значение уровня сопротивления

4.4. Метод Trend()

В методе Trend() проверятся, какая из линий существует в данный момент времени. Если линия поддержки, значит,индикатор показывает тренд вверх, при этом метод возвращает значение 1. Если существует линия сопротивления, то метод возвращает значение -1. 

// Метод определения тренда
int Trend()
  {
   if(sup[0]!=0)
     { // есть линия поддержки, значит тренд вверх
      return(1);
     }
   if(res[0]!=0)
     { // есть линия сопротивления, значит тренд вниз
      return(-1);
     }
    
   return(0);
  }

4.5. Метод BuyStoploss()

Метод BuyStoploss() возвращает значение линии поддержки. 

// Метод определения уровня Стоп Лосс для buy
double BuyStoploss()
  {
   return(sup[0]);
  }

4.6. Метод SellStoploss()

Метод SellStoploss() возвращает значение линии сопротивления. 

// Метод определения уровня Стоп Лосс для sell
double SellStoploss()
  {
   return(res[0]);
  }

Теперь класс трейлинг стопа полностью готов. 

5. Добавление трейлинг стопа по NRTR в эксперта

Так же, как уже делали с трейлинг стопом по Parabolic, добавим в эксперта My_First_EA трейлинг стоп по NRTR.

5.1. Откроем в редакторе MetaEditor доработанного ранее эксперта My_First_EA_SARTrailing и сохраним его с именем My_First_EA_NRTRTrailing.

5.2. Внешние параметры трейлинг стопа по Parabolic заменим на параметры трейлинг стопа по NRTR.

input int TrailingNRTRPeriod = 40;
input double TrailingNRTRK   =  2;

5.3. Вместо создания экземпляра класса CParabolicStop создадим экземпляр класса CNRTRStop; код располагается под внешними переменными. 

CNRTRStop Trailing; // создание экземпляра класса 

5.4. В функции OnInit() эксперта заменим параметры вызова метода SetParameters() на параметры NRTR.

Trailing.SetParameters(TrailingNRTRPeriod,TrailingNRTRK)

5.5. Компилируем эксперта и пробуем его протестировать. 

 
Рис. 10. Результаты тестирования эксперта с трейлинг стопом по NRTR.

Результат работы эксперта (рис. 10) с трейлинг стопом по сравнению с работой эксперта без него (рис. 7) почти не изменился. Использование трейлинг стопа по Parabolic оказалось более эффективным для этого эксперта. Можно сделать вывод, что арсенал из некоторого количества трейлинг стопов может быть очень полезен при разработке экспертов – для проведения экспериментов и выбора наиболее подходящего типа трейлинг стопа.

Файл My_First_EA_NRTRTrailing.mq5 прилагается к статье.

6. Эксперт-помощник

При создании базового класса трейлинга предусматривалась возможность управления включением/выключением трейлинг стопа через кнопку. Создадим эксперта-помощника для сопровождения позиций на различных символах различными типами трейлинг стопа. Эксперт не будет открывать сам позиции, а только сопровождать открытые.

6.1. Создаем в редакторе MetaEditor нового эксперта с именем Sample_TrailingStop.

6.2. Подключаем файл с классом Sample_TrailingStop.mqh. 

#include <Sample_TrailingStop.mqh> // подключение класса трейлинга

6.3. Объявляем внешние параметры для индикаторов.

input double SARStep=0.02;     // Шаг Parabolic
input double SARMaximum=0.02;  // Максимум Parabolic
input int NRTRPeriod=40;       // Период NRTR
input double NRTRK=2;          // Коэффициент NRTR

6.4. Объявляем массив с символами, на которых эксперт сможет работать.

string Symbols[]={"EURUSD","GBPUSD","USDCHF","USDJPY"};

6.5. Объявляем массивы для загрузки классов.

CParabolicStop *SARTrailing[];
CNRTRStop *NRTRTrailing[];

6.6. В функции OnInit() эксперта изменяем размеры массивов для загрузки классов в соответствии с размером массива Symbols. 

ArrayResize(SARTrailing,ArraySize(Symbols));  // изменение размера в соответствии с количеством используемых символов
ArrayResize(NRTRTrailing,ArraySize(Symbols)); // изменение размера в соответствии с количеством используемых символов 

6.7. В цикле для каждого элемента массива выполняем загрузку экземпляра класса.

for(int i=0;i<ArraySize(Symbols);i++)
  { // для всех символов
   SARTrailing[i]=new CParabolicStop(); // создание экземпляра класса CParabolicStop
   SARTrailing[i].Init(Symbols[i],PERIOD_CURRENT,false,true,true,5,15+i*17,Silver,Blue);    // инициализация экземпляра класса CParabolicStop 
   if(!SARTrailing[i].SetParameters(SARStep,SARMaximum))
     { // установка параметров экземпляра класса CParabolicStop 
      Alert("trailing error");
      return(-1);
     }
   SARTrailing[i].StartTimer();         // запуск таймера
//----
   NRTRTrailing[i]=new CNRTRStop();     // создание экземпляра класса CNRTRStop
   NRTRTrailing[i].Init(Symbols[i],PERIOD_CURRENT,false,true,true,127,15+i*17,Silver,Blue); // инициализация экземпляра класса CNRTRStop 
   if(!NRTRTrailing[i].SetParameters(NRTRPeriod,NRTRK))
     { // установка параметров экземпляра класса CNRTRcStop 
      Alert("trailing error");
      return(-1);
     }
   NRTRTrailing[i].StartTimer();        // запуск таймера 
  }

Обратите внимание: при вызове методов Init() выполняется вычисление координат кнопок. Слева будут располагаться кнопки включения трейлинг стопа по Paraboloic, справа - по NRTR.

6.8. В функцию OnTick() добавляем вызов метода DoStoploss() для каждого экземпляра трейлинга.

for(int i=0;i<ArraySize(Symbols);i++)
  {
   SARTrailing[i].DoStoploss();
   NRTRTrailing[i].DoStoploss();
  }

6.9. Добавляем обработку событий графика. 

void OnChartEvent(const int         id,
                  const long   &lparam,
                  const double &dparam,
                  const string &sparam 
                  )
  {
   for(int i=0;i<ArraySize(Symbols);i++)
     {
      SARTrailing[i].EventHandle(id,lparam,dparam,sparam);
      NRTRTrailing[i].EventHandle(id,lparam,dparam,sparam);
     }
    
  }

6.10. В функции Deinit() выполняем деинициализацию всех экземпляров класса и их удаление.

for(int i=0;i<ArraySize(Symbols);i++)
  {
   SARTrailing[i].Deinit(); 
   NRTRTrailing[i].Deinit();
   delete(SARTrailing[i]);  // удаление объекта
   delete(NRTRTrailing[i]); // удаление объекта
  }

Компилируем, присоединяем эксперт на график, на графике появляются индикаторы и кнопки (рис. 11) - эксперт готов к работе.  

 
Рис. 11. Кнопки и индикаторы на графике после запуска эксперта Sample_TrailingStop.

Остается только нажать нужную кнопку для сопровождения соответствующей позиции, когда она будет открыта.

Файл эксперта Sample_TrailingStop.mq5 прилагается к статье.

Заключение

Освежим в памяти порядок использования класса CTrailingStop при создании механической торговой системы:

1. Подключить файл Sample_TrailingStop.mqh.

2. Объявить внешние переменные с параметрами индикатора используемого трейлинг стопа.

3. Создать экземпляр класса.

4. Добавить вызов методов Init(), SetParameters(), StartTimer(), On() из функции OnInit() эксперта.

5. Добавить вызов метода Refresh() из функции OnTimer() эксперта.

6. Добавить вызов метода DoStopLoss() из функции OnTick() эксперта.

7. Добавить вызов метода Deinit() из функции OnDeinit() эксперта. 


Семь шагов, не более 5-ти минут, и в вашем эксперте уже есть функция трейлинг стопа!

Прикрепленные файлы |
Последние комментарии | Перейти к обсуждению на форуме трейдеров (38)
Igor Petrov
Igor Petrov | 27 сент. 2019 в 10:22
Vladimir Karputov:

Вот такие фразы программисты больше всего ненавидят. Это Вам на будущее.

ок. запомнил)))

Vladimir Karputov
Vladimir Karputov | 27 сент. 2019 в 10:25
Igor Petrov:

ок. запомнил)))

Ищите по словам "Trailing" "Balance" "Equity"

Kira27
Kira27 | 8 февр. 2021 в 21:45
Чёт не один из выложенных кодов не открывает сделки, выдавая ошибки 2021.02.08 23:43:23.200 my_first_ea_sartrailing (ES,M1) Alert: Запрос на установку ордера Buy не выполнен - код ошибки:4756    При этом запрос по ценам вроде как актуальный  2021.02.08 23:43:23.200 my_first_ea_sartrailing (ES,M1) Alert: action= 1price= 3900.88sl= 3900.58tp= 3901.88volume= 0.1magic= 12345type= 0type_filling= 0deviation= 1000


Rustam Ayupov
Rustam Ayupov | 8 мая 2022 в 22:28

Очень хочется понять, что написано в комментариях ?

class CTrailingStop{

   protected:
      string m_symbol;              // ñèìâîë
      ENUM_TIMEFRAMES m_timeframe;  // òàéìôðåéì
      bool m_eachtick;              // ðàáîòàòü íà êàæäîì òèêå
      bool m_indicator;             // ïîêàçûâàòü èíäèêàòîð íà ãðàôèêå
      bool m_button;                // ïîêàçûâàòü êíîïêó âêëþ÷åíèÿ/âûêëþ÷åíèÿ
      int m_button_x;               // êîîðäèíàòà õ êíîïêè
      int m_button_y;               // êîîðäèíàòà ó êíîïêè
      color m_bgcolor;              // öâåò êíîïêè
      color m_txtcolor;             // öâåò íàäïèñè êíîïêè
      int m_shift;                  // ñìåùåíèå áàðà
      bool m_onoff;                 // âêëþ÷åíî/âûêëþ÷åíî
      int m_handle;                 // õýíäë èíäèêàòîðà
      datetime m_lasttime;          // âðåìÿ ïîñëåäíåãî âûïîëíåíèÿ òðåéëèíã ñòîïà
      MqlTradeRequest m_request;    // ñòðóêòóðà òîðãîâîãî çàïðîñà
      MqlTradeResult m_result;      // ñòðóêòóðà ðåçóëüòàòà òîðãîâîãî çàïðîñà
      int m_digits;                 // êîëè÷åñòâî çíàêîâ ïîñëå çàïÿòîé ó öåíû
      double m_point;               // çíà÷åíèå ïóíêòà
      string m_objname;             // èìÿ êíîïêè
      string m_typename;            // èìÿ òèïà òðåéëèíã ñòîïà
      string m_caption;             // íàäïèñü íà êíîïêå
MrBrooklin
MrBrooklin | 23 апр. 2023 в 19:15
Rustam Ayupov #:

Очень хочется понять, что написано в комментариях ?

protected:
string m_symbol;             // символ
ENUM_TIMEFRAMES m_timeframe; // таймфрейм
bool m_eachtick;             // работать на каждом тике
bool m_indicator;            // показывать индикатор на графике
bool m_button;               // показывать кнопку включения/выключения
int m_button_x;              // координата х кнопки
int m_button_y;              // координата у кнопки
color m_bgcolor;             // цвет кнопки
color m_txtcolor;            // цвет надписи кнопки
int m_shift;                 // смещение бара
bool m_onoff;                // включено/выключено
int m_handle;                // хэндл индикатора
datetime m_lasttime;         // время последнего выполнения трейлинг стопа
MqlTradeRequest m_request;   // структура торгового запроса
MqlTradeResult m_result;     // структура результата торгового запроса
int m_digits;                // количество знаков после запятой у цены
double m_point;              // значение пункта
string m_objname;            // имя кнопки
string m_typename;           // имя типа трейлинг стопа
string m_caption;            // надпись на кнопке

С уважением, Владимир.

Несколько способов определения тренда на MQL5 Несколько способов определения тренда на MQL5
Любой трейдер отдал бы многое за возможность безошибочного определения тренда в любой момент времени, и, пожалуй, это и есть тот самый Грааль, который все ищут. В данной статье мы рассмотрим несколько способов определения тренда, а точнее, как средствами языка MQL5 запрограммировать несколько классических способов определения тренда.
Исследование паттернов (моделей) японских свечей Исследование паттернов (моделей) японских свечей
Построение графиков японских свечей и анализ свечных моделей — удивительное направление технического анализа. Преимущество японских свечей в том, что они представляют данные таким образом, что появляется возможность увидеть динамику внутри данных. В данной статье мы рассмотрим типы свечей, классификацию свечных моделей и напишем индикатор, распознающий свечные паттерны.
Применение функции TesterWithdrawal() для моделирования снятия прибыли Применение функции TesterWithdrawal() для моделирования снятия прибыли
В статье рассмотрено применение функции TesterWithDrawal() для оценки рисков в торговых системах, выполняющих снятие определенной части средств в процессе работы. Наряду с этим показано, как применение данной функции влияет на алгоритм расчета просадки по средствам в тестере. Использование данной функции может быть полезным при оптимизации параметров вашего советника.
Ограничения и проверки в экспертах Ограничения и проверки в экспертах
Можно ли торговать этим инструментом в понедельник? Хватит ли денег на открытие позиции? Какой размер убытка мы получим, если сработает Stop Loss? Как ограничить количество отложенных ордеров? Была ли выполнена торговая операция на этом баре или это было на предыдущем? Если торговый робот не может сделать подобные проверки, то любая прибыльная торговая система может превратиться в проигрышную. В этой статье показаны примеры проверок, которые пригодятся в любом эксперте.