Autoaprendizaje del lenguaje MQL5 desde cero - página 72

 
Vasiliy Sokolov:

Como la perfección no tiene límites, añadiré algunos comentarios más sobre el código:

He resaltado en amarillo dos lugares no triviales.

1) Observa que el código se repite en el primer if y en el siguiente else. La única diferencia está en la última línea y la acción final (OpenBUY, OpenSell).

2) Las condiciones para entrar en el otro bloque no son obvias. No son visibles debido a la abundancia de "piedras". De hecho, sólo dependen de la última línea:

Esto es una señal segura de que aquí falta una función.

Tenemos que escribir una función que devuelva true si el tiempo de apertura de la posición se corresponde con el especificado (lo escribiré más adelante).

Sí, Vasily, tienes razón, deberíamos haber escrito una función.

Saludos, Vladimir.

 
Vasiliy Sokolov:
Por cierto, presta atención al tamaño total de tu programa. Ya es bastante grande. ¿Qué te parece? Por cierto, un novato no puede escribir un código de tamaño tan grande: las variables se mezclan, los paréntesis son demasiado grandes, los errores de compilación se arrastran como setas después de la lluvia. Después de la compilación, un programa de tal tamaño empieza a tener fallos y nadie puede entender qué es lo que está mal. Y todo funciona en su código por alguna razón), y está claro en las funciones lo que está pasando y cómo. En una palabra, es una belleza.

Gracias, Vasily. Mucho de su trabajo aquí, ya que la plantilla de trailing stop fue proporcionada por usted. Sólo tengo que rellenar las funciones con código. Actualmente estoy trabajando en el trailing stop. Una parte ya está hecha, pero hay algunas cosas que todavía tengo que entender para poder mostrar la versión final de EA al público.

Saludos, Vladimir.

 

He añadido algunas funciones. Terminé con un código como este:

//+------------------------------------------------------------------+
//| 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;
}
Todavía no entiendo el trabajo de magia. En la red no tiene sentido. En cualquier caso, puede eliminar fácilmente esta comprobación, ya que sólo se realiza en una función.
Archivos adjuntos:
MrBrooklin.mq5  38 kb
 

El bloque OnInit también está sobredimensionado y todavía no está escrito correctamente. En primer lugar, hay que intentar escribir identificadores, no números. Devuelve INIT_SUCCEED en lugar de -1. En segundo lugar, el cambio es excesivo aquí. Este código debe contener un if o un switch. Primero hay que escribir uno y luego el otro - sólo aceite.

En tercer lugar, tenemos que controlar todos los tipos de cuentas. Tenemos Demo y luego tenemos Real. Y luego está el Concurso. Pero incluso si no hubiera una tercera cuenta, debería haber un talón que recogiera todas las demás variantes:

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 нулевое значение означающее "удачная инициализация"
   }
  }
 

Y la guinda del pastel: los comentarios. Cuando escribimos funciones, hay mucho espacio, nuestra mano se ve arrastrada a poner comentarios en las partes correctas del código:

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

Cuando el mismo código se escribe "a la vez" en el cuerpo de alguna función principal, se hace lo más corto posible y sin comentarios:

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();}

Así que escribe más funciones, te animan a escribir los comentarios adecuados, y aunque hacen el código más verboso, también lo hacen más claro.

El tercer punto: aquí estás escribiendo:

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

¿Por qué añadir otro tipo de comentario? Sólo tienes que sustituir lo que hay en el bloque por tu comentario:

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

Recuerda que los comentarios son para ti, no tú para los comentarios. Elimine los antiguos y añada los suyos propios. Cíñase al formato: en la cabecera de la función, indique breve pero claramente en los comentarios qué hace la función y qué valores devuelve en qué casos.

 

Por cierto, cuando se separaron las condiciones de tiempo de cierre de la posición en una función separada, quedó claro que no estaba escrita correctamente:

//+------------------------------------------------------------------+
//| Возвращает истину, если текущее время попадает в диапазон        |
//| времени для закрытия позиции. В противном случае возвращает ложь.|                                                 |
//+------------------------------------------------------------------+     
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;
}

Tomé el contenido interno de su código. Está claro que la posición se cerrará sólo en un minuto. В 23:50. Este código funcionará, pero si algo va mal a las 23:50, la posición quedará pendiente a las 23:51. Por eso hay que escribirlo al menos:

time_current.min>=time_close.min)

E incluso esta opción no es la ideal. Una solución más potente es utilizar los modos de negociación. Sin embargo, este es el siguiente nivel de excelencia. Hasta ahora, este diseño se ajusta a la perfección.

 
MrBrooklin:

¡Hola Vasily! Muchas gracias por sus oportunos consejos y su apoyo. Tus mensajes sobre el papel de las funciones y cómo construir el código de un programa me han ayudado mucho en el aprendizaje del lenguaje de programación MQL5:

  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

Ahora que la información en mi cabeza está estructurada, es más fácil entender el código escrito no sólo por mí, sino también por otros programadores. Espero que este tema sea una buena ayuda para aquellos que están empezando a aprender el lenguaje de programación MQL5 desde cero.

Saludos, Vladimir.

Buen trabajo, Vladimir. Los buenos codificadores suelen tener buenos algoritmos, y cuentan con buenos fijadores de objetivos... Todo comienza con las metas y los objetivos correctos. Puedes construir una casa de una vez y luego encontrar agua. Puedes encontrar agua primero y construir la casa pensando en el agua. Objetivos / propósito, y la fijación de objetivos es de oportunidad.... Vas directamente al algoritmo .... ¡Pero en general es muy bueno!

 
Vasiliy Sokolov:

Por cierto, cuando se separaron las condiciones de tiempo de cierre de la posición en una función separada, quedó claro que no estaba escrito correctamente:

Tomé el contenido interno de su código. Está claro que la posición se cerrará sólo en un minuto. В 23:50. Este código funcionará, pero si algo va mal a las 23:50, la posición seguirá colgada a las 23:51. Por eso hay que escribirlo al menos:

E incluso esta opción no es la ideal. Una solución más potente es utilizar los modos de negociación. Sin embargo, este es el siguiente nivel de excelencia. Siempre que este diseño pueda soportarlo.

Vasily, ¡eres un gran maestro!

Ninguna cartilla o libro de texto puede dar esa explicación. Todo lo que has sugerido lo implementaré sin duda en la versión final del EA.

Saludos, Vladimir.

 

Me desvío del tema por un momento y les contaré la historia de un profesor. En nuestro instituto, cuando ya nos estábamos especializando, teníamos un profesor maravilloso. En ese momento, estábamos estudiando el álgebra de la lógica. Muchos estudiantes no pudieron entender durante mucho tiempo cómo 1+1 podía ser igual a 1? Si 1x1, obviamente sería igual a 1. ¡¡¡Y ahí lo tienes!!! Este profesor utilizó ejemplos sencillos para darnos una explicación de lo que es el OR lógico y lo que es el AND lógico, que recordaré el resto de mi vida.

Imagina, dice el profesor, que tienes que llegar al instituto por la mañana para las clases. Puede tomar un trolebús o un tranvía para llegar al instituto. Ha llegado a la parada del autobús, pero no hay ni trolebús (condiciónfalsa o igual a 0), ni tranvía (condición falsa o igual a 0). Naturalmente, no podrá llegar al instituto (condición false o igual a 0). Comprueba 0+0=0. ¡Maravilloso! Si llega a la parada y hay un trolebús (condición verdadera o igual a 1), O un tranvía (condición verdadera o igual a 1) , O ambos, un trolebús y un tranvía juntos, entonces llegará definitivamente al instituto y la condición verdadera o igual a 1 se mantendrá. Comprobación: 1+0=1, 0+1=1 y 1+1=1. ¡Todo encaja!

Utilizando los mismos ejemplos con el trolebús y el tranvía, nos explicó lo que es el AND lógico.

¡Ese es el poder del talento de un profesor! Lo recordaré el resto de mi vida.

Sinceramente, Vladimir.

 
Valeriy Yastremskiy:

Buen trabajo. Los buenos codificadores suelen contar con buenos algoritmos, y tienen a los que fijan los objetivos... Todo comienza con las metas y los objetivos correctos. Puedes construir una casa de una vez y luego encontrar agua. Puedes encontrar agua primero y construir la casa pensando en el agua. Objetivos / propósito, y la fijación de objetivos es de oportunidad.... Vas directamente al algoritmo .... ¡Pero en general es muy bueno!

Gracias, Valery, por tu participación en el tema y por un diálogo constructivo.

Saludos, Vladimir.