Self-learning the MQL5 language from scratch - page 72

 
Vasiliy Sokolov:

Since there is no limit to perfection, I will add a few more comments on the code:

I have highlighted two non-trivial places in yellow.

1) Note that the code is repeated in the first if and in the next else. The only difference is in the last line and end action (OpenBUY, OpenSell).

2) Conditions for getting into the else block are not obvious. They are not visible because of the abundance of ? In fact, they only depend on the last line:

This is a sure sign that a function is missing here.

We need to write a function that returns true if the time to open the position corresponds to the specified one (I will write it later).

Yes, Vasily, you are right, we really should have written the function.

Regards, Vladimir.

 
Vasiliy Sokolov:
By the way, pay attention to the total size of your program. It is already quite big. How do you like it? By the way, a novice cannot write such a large code size: variables get mixed up, brackets get too large, compilation errors creep in like mushrooms after the rain. After compilation, a program of such size starts glitching and nobody can understand what's wrong. And everything works in your code for some reason), and it is clear in the functions what is going on and how. In a word, it's a beauty.

Thank you Vasily! A lot of your work here, as the trailing stop template was provided by you. I have only to fill the functions with code. I am currently working on trailing stop. Some of it has been done already, but there are some things I still need to work out before I submit the final EA version to public review.

Regards, Vladimir.

 

I added a few functions. I ended up with code like this:

//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
  {
//--- Зададим условия для открытия позиций BUY и SELL   
   double price=SymbolInfoDouble(Symbol(),SYMBOL_ASK);
   double point=SymbolInfoDouble(Symbol(),SYMBOL_POINT);
   int digits=(int)SymbolInfoInteger(Symbol(),SYMBOL_DIGITS);
   price=NormalizeDouble(price,digits);
   //-- Если открытой позиции нет и время для открытия позволяет,
   //-- открывает BUY или SELL в зависимости от положения тика
   if(IsMainPositionOpen() == false && IsTimeForOpen())
   {
      if(TickUP()==(price+point))
         OpenBUY();
      if(TickDOWN()==(price-point))
         OpenSELL();
   }
   //-- Если наступило время закрытия позиции, закрываем все
   if(IsTimeForClose())
      CloseALL();

//+------------------------------------------------------------------+
//| Шаблон трейлинг стопа предоставленный Василием Соколовым         |
//+------------------------------------------------------------------+

//-- Выбираем позиции по текущему символу. Если позиции нет и выбирать нечего, то выходим!
   if(!PositionSelect(Symbol()))
      return;
//-- Стоп-лосс длинной позиции переставляем в безубыток и тралим его
   if(PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_BUY)
     {
      SetBreakevenForBuyPosition(); // установить безубыток для Buy позиции
      TrailingStopLossForBuyPosition(); // перетащить Stop Loss для Buy позиции
     }
//-- Стоп-лосс короткой позиции переставляем в безубыток и тралим его
   else
      if(PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_SELL)
        {
         SetBreakevenForSellPosition(); // установить безубыток для Sell позиции
         TrailingStopLossForSellPosition(); // перетащить Stop Loss для Sell позиции
        }
  }
  
//+------------------------------------------------------------------+
//| Возвращает истину, если позиция торгового эксперта уже открыта.  |
//| Возвращает ложь в противном случае.                              |
//+------------------------------------------------------------------+  
bool IsMainPositionOpen()
{
   //-- Если позиций нет - то и у эксперта позиции нет, возвращаем ложь.
   if(PositionSelect(Symbol()) == false)
      return false;
   //-- Позиция есть и ее мэджик совпадает с мэджиком эксперта - возвращаем истину
   if(PositionGetInteger(POSITION_MAGIC) == Magic_Number)
      return true;
   //-- Позиция есть но ее мэджик не совподает с мэджиком эксперта -
   //-- это чья-то другая позиция, позвращаем ложь
   else
      return false;
}
//+------------------------------------------------------------------+
//| Возвращает истину, если текущее время попадает в диапазон        |
//| разрешенного времени для открытия позиции. В противном случае    |
//| возвращает ложь.                                                 |
//+------------------------------------------------------------------+  
bool IsTimeForOpen()
  {
   MqlDateTime time_current,time_open,time_open1;
   TimeToStruct(TimeCurrent(),time_current);
   TimeToStruct((D'1970.01.01 09:00:00'),time_open);
   TimeToStruct((D'1970.01.01 09:01:00'),time_open1);
   if(time_current.hour == time_open.hour &&
      time_current.min >= time_open.min &&
      time_current.min < time_open1.min
      )
      return true;
   else
      return false;
   }
//+------------------------------------------------------------------+
//| Возвращает истину, если текущее время попадает в диапазон        |
//| времени для закрытия позиции. В противном случае возвращает ложь.|                                                 |
//+------------------------------------------------------------------+     
bool IsTimeForClose()
{
   MqlDateTime time_current,time_open,time_open1,time_close;
   TimeToStruct(TimeCurrent(),time_current);
   TimeToStruct((D'1970.01.01 23:50:00'), time_close);
   if(time_current.hour==time_close.hour && 
      time_current.min==time_close.min)
      return true;
   else
      return false;
}
I still don't understand the magic work. On netting it makes no sense. In any case you can easily remove this check as it is only done in one function.
Files:
MrBrooklin.mq5  38 kb
 

The OnInit block is also overthought and still not written quite correctly. First of all, you should try to write identifiers, not numbers. Return INIT_SUCCEEDED instead of -1. Second, switch is excessive here. Either 'if' or 'switch' should be used. First you have to write one and then the other - just oil.

Thirdly, we need to monitor all of the account types. We have Demo and then we have Real. And then there is Contest. But even if there were no third account, there should be a stub which would catch all the other variants:

int OnInit()
  {
//--- Определим тип счёта на который устанавливаем советник: демо или реальный счет
   ENUM_ACCOUNT_TRADE_MODE account_type=(ENUM_ACCOUNT_TRADE_MODE)AccountInfoInteger(ACCOUNT_TRADE_MODE);
//--- теперь превратим значение перечисления в понятный вид
   string trade_mode;               //создадим переменную для торгового режима
   ACCOUNT_TRADE_MODE_CONTEST
   if(account_type==ACCOUNT_TRADE_MODE_REAL) //если торговый режим счёта - реальный
     {
      //--- выводим окно сообщений на торговом терминале и закрываем советник
      MessageBox("Работа на реальном счете запрещена, выходим!","Советник запущен на реальном счете");
      return(INIT_FAILED); //возвращаем для функции OnInit ненулевое значение означающее "неудачная инициализация"
     }
   if(account_type==ACCOUNT_TRADE_MODE_DEMO) //если торговый режим счёта - демо
     {
      //--- выводим окно сообщений на торговом терминале и продолжаем работу советника
      MessageBox("Работа на демо-счете разрешена!","Советник запущен на демо-счете");
      trade_mode="Счёт REAL";
      return(INIT_SUCCEEDED); //возвращаем для функции OnInit нулевое значение означающее "удачная инициализация"
     }
//-- Заглушка на случай непредвиденных вариантов. Всегда должна быть даже если варианта явно два.
   else
   {
      MessageBox("Неизвестный тип счета. Работа невозможна!");
      return(INIT_FAILED); //возвращаем для функции OnInit нулевое значение означающее "удачная инициализация"
   }
  }
 

And the icing on the cake: comments. When we write functions, there is a lot of space, our hand is drawn to put comments on the right pieces of code:

//+------------------------------------------------------------------+
//| Возвращает истину, если позиция торгового эксперта уже открыта.  |
//| Возвращает ложь в противном случае.                              |
//+------------------------------------------------------------------+  
bool IsMainPositionOpen()
{
   //-- Если позиций нет - то и у эксперта позиции нет, возвращаем ложь.
   if(PositionSelect(Symbol()) == false)
      return false;
   //-- Позиция есть и ее мэджик совпадает с мэджиком эксперта - возвращаем истину
   if(PositionGetInteger(POSITION_MAGIC) == Magic_Number)
      return true;
   //-- Позиция есть но ее мэджик не совподает с мэджиком эксперта -
   //-- это чья-то другая позиция, позвращаем ложь
   else
      return false;
}

When the same code is written "at the same time" in the body of some main function, it becomes as short as possible and without comments:

if(PositionSelect(Symbol())==false
      && PositionGetInteger(POSITION_MAGIC)!=Magic_Number
      && time_current.hour==time_open.hour
      && time_current.min>=time_open.min
      && time_current.min<time_open1.min
      && (TickUP()==(price+point)))
     {OpenBUY();}

So write more functions, they encourage you to write the right comments, and although they make the code more verbose, they also make it clearer.

The third point: here you are writing:

//+------------------------------------------------------------------+
/ | Expert initialization function                                   |
//+------------------------------------------------------------------+
/* Функция инициализации советника OnInit с типом данных int. Если возвращаемое значение для функции
   return(-1), то это "неудачная инициализация". Если возвращаемое значение для функции return(0), то
   это "удачная инициализация". INIT_SUCCEEDED означает, что инициализация прошла успешно и дальше
   можно продолжать тестирование эксперта. Этот код означает то же самое, что и нулевое значение, т.е.
   "удачная инициализация".
*/
int OnInit()
  {
  ...
  }

Why add another type of comment? Just replace what's in the block with your comment:

//+------------------------------------------------------------------------------------------------------+
//| Функция инициализации советника OnInit с типом данных int. Если возвращаемое значение для функции    |
//| return(-1), то это "неудачная инициализация". Если возвращаемое значение для функции return(0), то   |
//| это "удачная инициализация". INIT_SUCCEEDED означает, что инициализация прошла успешно и дальше      |
//| можно продолжать тестирование эксперта. Этот код означает то же самое, что и нулевое значение, т.е.  |
//| "удачная инициализация".                                                                             |
//+------------------------------------------------------------------------------------------------------+
int OnInit()
  {
  ...
  }

Remember, comments are for you, not you for comments. Delete the old ones and add your own instead. Stick to the format - in the function header, briefly but clearly state in the comments what the function does and what values it returns in what cases.

 

By the way, when the position closing time conditions were separated into a separate function, it became clear that it was not written correctly:

//+------------------------------------------------------------------+
//| Возвращает истину, если текущее время попадает в диапазон        |
//| времени для закрытия позиции. В противном случае возвращает ложь.|                                                 |
//+------------------------------------------------------------------+     
bool IsTimeForClose()
{
   MqlDateTime time_current,time_open,time_open1,time_close;
   TimeToStruct(TimeCurrent(),time_current);
   TimeToStruct((D'1970.01.01 23:50:00'), time_close);
   if(time_current.hour==time_close.hour && 
      time_current.min==time_close.min)
      return true;
   else
      return false;
}

I took the internal content from your code. It is clear that the position will be closed only within one minute. В 23:50. This code will work, but if something goes wrong at 23:50, the position will remain dangling at 23:51. That's why you have to at least write it:

time_current.min>=time_close.min)

And even this option is not ideal. A more powerful solution is to use trading modes. However, this is the next level of excellence. So far this design will do just fine.

 
MrBrooklin:

Hello Vasily! Thank you very much for your timely advice and support. Your messages about the role of functions and how to build a program code have helped me a lot in learning the MQL5 programming language:

  1. https://www.mql5.com/ru/forum/352460/page28#comment_18636493
  2. https://www.mql5.com/ru/forum/352460/page28#comment_18637800
  3. https://www.mql5.com/ru/forum/352460/page29#comment_18641729
  4. https://www.mql5.com/ru/forum/352460/page52#comment_18694985

Now that the information in my head is structured, it is easier to understand the code written not only by me, but also by other programmers. I hope this topic will be a good help for those who are starting to learn the MQL5 programming language from the scratch.

Regards, Vladimir.

Good job, Vladimir. Good coders usually have good algorithmists, and they have good goal setters... Everything starts with the right goals, and the right objectives. You can build a house at once and then find water. You can find water first and build the house with water in mind. Goals / purpose, and goal setting is from opportunity.... You go straight to the algorithm.... But in general it is very good!

 
Vasiliy Sokolov:

By the way, when the position closing time conditions were separated into a separate function, it became clear that it was not written correctly:

I took the internal content from your code. It is clear that the position will be closed only within one minute. В 23:50. This code will work, but if something goes wrong at 23:50, the position will remain dangling at 23:51. That's why you have to at least write it:

And even this option is not ideal. A more powerful solution is to use trading modes. However, this is the next level of excellence. As long as this design can handle it.

Vasily, you are a wonderful teacher!

No primer or textbook can provide such an explanation. Everything you suggested I will certainly implement in the final version of the EA.

Regards, Vladimir.

 

I am going off-topic for a moment and will tell you a life story about a teacher. At our institute, when we were already specialising, we had a wonderful teacher. At the time, we were studying the algebra of logic. Many students could not understand for a long time how 1+1 could equal 1? If 1x1, it would obviously equal 1. And there you have it!!! This teacher used simple examples to give us an explanation of what is the logical OR and what is the logical AND, which I will remember for the rest of my life.

Imagine, says the teacher, that you need to arrive at the institute in the morning for classes. You can get to the institute by trolley-bus OR by tram. You have come to the bus stop, but there is neither a trolleybus (conditionfalse or the same as 0), nor a tram (condition false or the same as 0). Naturally, you won't be able to get to the institute (condition false or the same as 0). Check 0+0=0. Wonderful! If you arrive at the bus stop and there is a trolleybus (condition true or the same as 1), OR a tram (condition true or the same as 1) , OR both a trolleybus and a tram together, then you will definitely arrive at the institute and the condition true or the same as 1 will hold! Check: 1+0=1, 0+1=1 and 1+1=1. Everything fits!

Using the same examples with the trolleybus and the tram, he explained to us what logical AND is.

That is the power of a teacher's talent! I will remember it for the rest of my life!

Sincerely, Vladimir.

 
Valeriy Yastremskiy:

Good job. Good coders usually have good algorithmicists, and they have goal setters, and they have goal setters... It all starts with the right goals, and the right objectives. You can build a house at once and then find water. You can find water first and build the house with water in mind. Goals / purpose, and goal setting is from opportunity.... You go straight to the algorithm.... But in general it is very good!

Thanks, Valery, for your participation in the topic and a constructive dialogue.

Regards, Vladimir.