Autoaprendizaje del lenguaje MQL5 desde cero - página 71

 
MrBrooklin:

Igor, las ganas de aprender el lenguaje MQL5 no se pierden, al contrario, sólo aumentan. Estoy desgarrado, por qué soy tan tonto y no puedo entender cosas simples con este operador de bucle. Lo principal es que sin el operador de bucle el código funciona perfectamente, pero con el bucle es un completo desastre. De todos modos, llegaré al fondo del asunto. Sin embargo, tengo mucho menos tiempo para el autoaprendizaje desde que el periodo de cuarentena ha terminado y tengo raros momentos en el trabajo en los que puedo estudiar el idioma. La mayoría de las veces estudio el idioma por la tarde y a última hora de la noche.

Necesito resolver 2 tareas para finalmente llegar a escribir el código del Trailing Stop:

  1. Necesito escribir un código con el uso del operador de bucle for para buscar todas las posiciones abiertas para todos los símbolos, y si no hay ninguna, entonces abrir una posición de compra entre las 09:00:00 y las 09:01:00, y cerrarla por fuerza a las 23:50:00, si durante el día de negociación no se activó un stop loss. Sin el operador de bucle for, como he escrito antes, todo funciona perfectamente. Ahora quiero conseguir el mismo resultado, pero con el uso del bucle.
  2. Escriba 2 funciones que determinen la dirección de apertura de la posición por el primer tick que aparezca en el marco temporal desde las 09:00:00 hasta las 09:01:00. Si el primer tick es alcista, se debe abrir una posición de compra; si el primer tick es bajista, se debe abrir la posición de venta correspondiente. No es una estrategia, es sólo mi "deseo" de no usar la condición aleatoria.
Atentamente, Vladimir.

Vladimir, te estás metiendo en cosas raras. Te han dado una plantilla: https://www.mql5.com/ru/forum/352460/page58#comment_18726492 La has ignorado y has decidido seguir adelante con tu ingenio. Este camino es difícil y puede llevar años. En su camino, estudie C, C++, Rust, C#, polimorfismo, herencia, desreferenciación de punteros, conversión de tipos dinámicos: Comprueba o conduce. Debes tomar el camino más corto hacia el objetivo. Si quiere que se le ponga un final, escríbalo. No es necesario estudiar C y C++ para hacerlo. Todo esto como optativa si te apetece.

La situación del bucle for es ilustrativa. Es una construcción poderosa que debe aplicarse en el lugar adecuado. Tienes una red y un algoritmo auxiliar. Por eso no hay enumeración de posiciones, no hay magos, así que no tienes este bucle. Por supuesto, todo funcionará sin él, pero el bucle sólo te hace sentir como aceite. Pero te empeñas en utilizarlo, parece ser un fin en sí mismo: utilizar la mayor cantidad posible de todo y de todos, para tener algo en reserva.

 
Vasiliy Sokolov:

Vladimir, te estás metiendo en un pequeño berenjenal. Te dieron una plantilla: https://www.mql5.com/ru/forum/352460/page58#comment_18726492 La ignoraste y decidiste seguir adelante con tu mente. Este camino es difícil y puede llevar años. En su camino, estudie C, C++, Rust, C#, polimorfismo, herencia, desreferenciación de punteros, conversión de tipos dinámicos: Comprueba o conduce. Debes tomar el camino más corto hacia el objetivo. Si quiere que se le ponga un final, escríbalo. No es necesario estudiar C y C++ para hacerlo. Todo esto como optativa si te apetece.

La situación del bucle for es ilustrativa. Es una construcción poderosa que debe aplicarse en el lugar adecuado. Tienes una red y un algoritmo auxiliar. Por eso no hay enumeración de posiciones, no hay magos, por lo que no se tiene este bucle. Naturalmente, todo funcionará sin él, pero el bucle sólo produce aceite. Pero usted se obstina en utilizarlo, por lo que parece ser un fin en sí mismo: puede utilizar la mayor cantidad posible de todo y de cualquier cosa que tenga en stock.

¡Hola Vasily!

Gracias por unirte oportunamente al tema y orientarme en el camino correcto como siempre. Ahora permítanme explicar todo en un orden:

  1. Una plantilla en el Asesor Experto fue insertada exactamente cuando usted me la ha proporcionado amablemente, ¡por lo que quiero expresar mi gratitud!
  2. Decidí que había que probar un EA con Trailin Stop en el probador de estrategias, pero no sabía cómo hacerlo sin abrir automáticamente al menos una posición. Por eso empecé a sentirme ansioso al escribir un Asesor Experto "completo". Lo pongo entre comillas porque mi Asesor Experto no tiene una estrategia como tal. Simplemente no quería utilizar la condición aleatoria para abrir una posición.
  3. Ninguno de los interlocutores del Asesor Experto, excepto tú, sugirió que el bucle for para buscar posiciones en esta situación no es necesario. La culpa es mía, yo mismo podría haberlo adivinado hace tiempo y haber preguntado: ¿era realmente necesario un bucle? Resultó que no. Gracias de nuevo por el consejo.
  4. Para finalmente pasar a escribir el trailing stop y no abandonar el proceso a mitad de camino, sólo necesito escribir el código de 2 funciones que determinan la dirección de apertura de una posición de compra o venta neta.
  5. Sí, estoy completamente de acuerdo contigo en que el autoaprendizaje empieza a alargarse. Pero si se tiene en cuenta que sigo un camino inexplorado y torcido, espero que sea posible hacer un descuento.
  6. Durante todo el periodo de autoestudio he obtenido una gran cantidad de información y ahora estoy intentando no sólo ordenarla sino también utilizarla en la práctica. Para ello descargué un Asesor Experto de CodeBase, lo desarrollé y lo utilicé durante quince días en una cuenta demo de un operador de Forex. El resultado es exactamente lo que esperaba. Pronto empezaré a usarlo en una cuenta real. Esto ya es una ventaja.
  7. No persigo la velocidad de autoestudio porque ya he escrito antes que el exceso de información de vez en cuando viene el estupor.
  8. Durante todo el período de auto-estudio poco a poco comenzó a memorizar palabras y frases en inglés que aparecen después del código de compilación. Ahora miro en Google Translate con menos frecuencia. Este es también un momento positivo en el autoaprendizaje.
  9. Voy a publicar todo lo que he escrito en el Asesor de Expertos exactamente cuando una pieza completa para un trailing stop está listo. Intentaré terminarlo el sábado. Aquí es donde comenzará el trabajo principal de escribir el código del trailing stop.

Vasily, te agradezco enormemente la orientación y la crítica constructiva. También expreso mi agradecimiento a todos los participantes de este tema que me ayudan en mi autoestudio del lenguaje de programación MQL5 desde cero.

El autoaprendizaje continúa. Continuará.

Saludos, Vladimir.

 
Андрей:

¡Buenas tardes a todos!

Si quisiera añadir algo interesante, creo que la idea de "autoformación en MQL5 desde cero" no es del todo correcta. Si una persona es un 0 en programación, tiene que aprender a programar primero. Si el objetivo es aprender a programar en MQL desde cero, una persona debe primero aprender aprogramar en C, al menos lo más básico, y luego aprender a escribir en MQL, porque MQL es de hecho C, pero está especializado para una determinada tarea, una persona no entiende cómo funcionan los bucles, y ya tratando de escribir programas. Es como intentar aprender latín sin saber ruso...

Otra cosa es que C no es un lenguaje muy amigable para aprender a programar, es sencillo, pero es demasiado conciso, es difícil para un principiante leer y entender el código sin tener conocimientos básicos de algoritmos, tipos de datos, álgebra booleana... En C tres caracteres seguidos pueden significar varias cadenas en otro lenguaje.

¡Para aprender a programar simplemente desde cero, para la base, para aprender a hablar con un ordenador en un (no importa qué) lenguaje, yo recomendaría comenzar con Pascal, el libro más fácil para empezar con él (2-3 semanas a gusto), a continuación, C (allí después de Pascal max semana con diligencia, dominar la sintaxis, me refiero a las habilidades básicas del lenguaje!) y luego tomar lenguajes especializados como MQL, porque MQL es C, que tiene un pequeño giro y simplificación, ya que está diseñado para una sola tarea y el compilador sabe lo obvio. Y C a su vez es una especie de Pascal comprimido en RAR, pero Pascal es casi inglés =).

Ahora sobre los bucles:

Cuando aprendí a programar, me ayudó la analogía con las sumas en álgebra, donde se especifica el valor inicial de n, el valor final de n y la fórmula con ese n a calcular.

Hola Andrey! Estoy completamente de acuerdo contigo sobre la base. No lo tengo y nunca lo he tenido. Pero, como dice el refrán, Moscú no se construyó de una vez. Gracias por sus consejos y sugerencias.

Saludos, Vladimir.

 
Roman Shiredchenko:

Sí, estoy de acuerdo: ¡sus códigos también son buenos! Te estás alterando por nada. Tómate tu tiempo para ordenar el código sin prisas y ya está. Todo es elemental aquí - seleccionar y arrastrar: (cuánto más fácil - sólo su tema de elegir la posición NEED... :-))

Además, incluir archivos - significa que están en su código como funciones también y eso es todo.

Gracias, Roman, por los códigos y los enlaces. ¡Estoy tratando los códigos con calma y seguramente no me apresuro a ninguna parte!

Sinceramente, Vladimir.

 

Buen día y buen humor a todos!

Sigo estudiando el lenguaje de programación MQL5 . Hoy, como prometí, estoy publicando para que todos vean el código de la plantilla del Asesor Experto diseñada para escribir un punto final. Debido al hecho de que el código de la plantilla de EA resultó ser bastante engorroso, tuvimos que reducir los comentarios tanto como fuera posible. La versión completa del Asesor Experto con comentarios detallados sobre cada línea de código, en una presentación accesible para un estudiante de 1.° grado de una escuela de programación, se publicará más adelante en forma de archivo con el nombre de trabajo Trailing_Stop.mq5 para que para no producir otro "footcloth" en el sitio.

Antes de publicar el código, el EA se probó en el probador de estrategias . No se han identificado problemas. El EA utiliza un sistema de contabilidad de posición de compensación. Este sistema de contabilidad implica que en un momento solo puede haber una posición abierta en la cuenta para el mismo símbolo (instrumento financiero).

Por el momento, el EA tiene la capacidad de:

  1. Verifique la cuenta comercial en la que está planeado instalarlo. Si el asesor está instalado en una cuenta de demostración, aparecerá el permiso para continuar trabajando en la ventana del mensaje. Si se intenta instalar el asesor en una cuenta real , aparecerá una advertencia en la ventana del mensaje sobre la imposibilidad de continuar el trabajo, luego de lo cual se eliminará automáticamente de la ventana de trabajo de la terminal comercial .
  2. Una vez al día de negociación exactamente a las 9:00 a. m. según la hora de Moscú, abre automáticamente una posición en la dirección hacia la que se dirigirá el primer tick. Por ejemplo, si a las 9:00 a.m. Hora de Moscú, el primer tick se dirigirá hacia arriba, lo que significa que se abrirá una posición de COMPRA, si el primer tick es hacia abajo, se abrirá una posición de VENTA.
  3. Establecer el tamaño de stop loss.
  4. Establecer el tamaño del lote.
  5. Establezca otros parámetros que serán necesarios para que funcione el trailing stop.

Además, el EA tiene una plantilla preinstalada, proporcionada amablemente por Vasily Sokolov, que se completará con el código durante el proceso de autoaprendizaje.

¡¡¡UNA ADVERTENCIA!!!

¡El Asesor Experto se desarrolla de acuerdo con el plan de autoaprendizaje para una cuenta de demostración y solo con fines educativos! ¡No está diseñado para operar en una cuenta real y obtener ganancias!

Saludos cordiales, Vladímir.

 //+------------------------------------------------------------------+
//|                                                Trailing_Stop.mq5 |
//|                        Copyright 2020, MetaQuotes Software Corp. |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright    "Copyright 2020, MetaQuotes Software Corp."
#property link          "https://www.mql5.com"
#property description "Советник создан для демо-счёта исключительно в учебных целях!"
#property description "Не предназначен для торговли на реальном счёте и извлечения прибыли!"
#property version      "1.00"
/* Краткое описание советника Trailing_Stop. Код советника разрабатывается в соответствии с
   планом самообучения языку программирования MQL5. В советнике применена неттинговая система
   учёта позиций. Эта система учета подразумевает, что в один момент времени на счете может быть
   только одна открытая позиция по одному и тому же символу (финансовому инструменту). Алгоритм
   работы советника прост: один раз в торговый день ровно в 9 ч. 00 мин. по московскому времени
   будет открываться одна позиция в ту сторону, куда будет направлен первый тик. Например, если
   первый тик будет направлен вверх, то значит откроется позиция BUY, если первый тик будет вниз,
   то значит откроется позиция SELL. У открытой позиции сразу будет устанавливаться ордер Stop Loss
   фиксированного размера для минимизации потерь в том случае, если цена финансового инструмента
   станет двигаться в убыточном направлении. Если цена инструмента достигнет этого уровня, то
   позиция полностью закроется автоматически. Если цена финансового инструмента будет двигаться
   в прибыльном направлении, то тогда автоматически включится в работу Trailing_Stop (Трейлинг Стоп).
   Схема работы Трейлинг Стоп:
   1. С приходом новых котировок советник проверяет, прибыльна ли открытая позиция.
   2. Как только прибыль (в пунктах) станет равной либо большей той величины, которая указана во
      входном параметре советника "Уровень перестановки Stop Loss в безубыток", автоматически
      поступит команда для перемещения ордера Stop Loss на тот уровень цены, по которому открылась
      существующая позиция, т.е. в безубыток.
   3. Если цена и дальше продолжит движение с увеличением прибыльности позиции, то при превышении
      величины, указаной во входном параметре советника "Уровень перестановки Stop Loss в безубыток"
      на величину, которая указана во входном параметре "Шаг трейлинг стопа", Stop Loss вслед за
      текущей ценой автоматически переместится с уровня безубытка на величину этого шага.
   4. Если прибыльность позиции уменьшится, то модификации ордера происходить не будет. Таким
      образом, будет автоматически фиксироваться прибыль торговой позиции.
   Если в течении торгового дня открытая позиция не закроется по Stop Loss или Trailing_Stop, то в
   23 ч. 50 мин. советник принудительно закроет эту позицию.

   ВАЖНО!!! Советник создан для демо-счёта исключительно в учебных целях!
            Не предназначен для торговли на реальном счёте и извлечения прибыли!*/

//--- Создадим входные параметры советника
input ushort BreakevenLevel= 100 ; //Уровень перестановки Stop Loss в безубыток
input ushort TrailingStop= 10 ;     //Шаг трейлинг стопа
input ushort SL= 200 ;             //Стоп-лосс
input double Lot= 0.1 ;             //Лот
input long    Magic_Number= 3215 ;   //Магический номер
//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
/* Функция инициализации советника OnInit с типом данных int. Если возвращаемое значение для функции
   return(-1), то это "неудачная инициализация". Если возвращаемое значение для функции return(0), то
   это "удачная инициализация". INIT_SUCCEEDED означает, что инициализация прошла успешно и дальше
   можно продолжать тестирование эксперта. Этот код означает то же самое, что и нулевое значение, т.е.
   "удачная инициализация".
*/
int OnInit ()
  {
//--- Определим тип счёта на который устанавливаем советник: демо или реальный счет
   ENUM_ACCOUNT_TRADE_MODE account_type=( ENUM_ACCOUNT_TRADE_MODE ) AccountInfoInteger ( ACCOUNT_TRADE_MODE );
//--- теперь превратим значение перечисления в понятный вид
   string trade_mode;               //создадим переменную для торгового режима
   /* Создадим оператор-переключатель switch, который cравнивает значение выражения с константами во всех
      вариантах case и передает управление оператору с соответствующим значением выражения.*/
   switch (account_type)
     {
       case ACCOUNT_TRADE_MODE_DEMO : //если ключевое слово ACCOUNT_TRADE_MODE_DEMO
         trade_mode= "Счёт DEMO" ;     //значит торговый режим счёта - демо
         break ;                     //оператор break прекращает выполнение ближайшего оператора switch
       case ACCOUNT_TRADE_MODE_REAL : //если ключевое слово ACCOUNT_TRADE_MODE_REAL
         trade_mode= "Счёт REAL" ;     //значит торговый режим счёта - реальный
         break ;                     //оператор break прекращает выполнение ближайшего оператора switch
     }
   if (account_type== ACCOUNT_TRADE_MODE_REAL ) //если торговый режим счёта - реальный
     {
       //--- выводим окно сообщений на торговом терминале и закрываем советник
       MessageBox ( "Работа на реальном счете запрещена, выходим!" , "Советник запущен на реальном счете" );
       return (- 1 ); //возвращаем для функции OnInit ненулевое значение означающее "неудачная инициализация"
     }
   if (account_type== ACCOUNT_TRADE_MODE_DEMO ) //если торговый режим счёта - демо
     {
       //--- выводим окно сообщений на торговом терминале и продолжаем работу советника
       MessageBox ( "Работа на демо-счете разрешена!" , "Советник запущен на демо-счете" );
       return ( 0 ); //возвращаем для функции OnInit нулевое значение означающее "удачная инициализация"
     }
   return ( INIT_SUCCEEDED );
  }
//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit ( const int reason)
  {
//--- сообщим код завершения работы эксперта
   Print ( __FILE__ , ": Код причины деинициализации = " ,reason);
  }
//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick ()
  {
//--- Зададим время открытия и закрытия позиции
   MqlDateTime time_current,time_open,time_open1,time_close;
   TimeToStruct ( TimeCurrent (),time_current);
   TimeToStruct (( D'1970.01.01 09:00:00' ),time_open);
   TimeToStruct (( D'1970.01.01 09:01:00' ),time_open1);
   TimeToStruct (( D'1970.01.01 23:50:00' ),time_close);
//--- Зададим условия для открытия позиций 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);
   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();}
   else
     {
       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
         && (TickDOWN()==(price-point)))
        {OpenSELL();}
     }
   if (time_current.hour==time_close.hour && time_current.min==time_close.min)
      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 позиции
        }
  }

//+------------------------------------------------------------------+
//| Timer function                                                   |
//+------------------------------------------------------------------+
void OnTimer ()
  {
//---

  }
//+------------------------------------------------------------------+
//| Buy Position Open function                                       |
//+------------------------------------------------------------------+

//--- Создадим функцию для открытия позиции Buy и назовём её OpenBUY
void OpenBUY()
  {
//--- Создадим запрос на сервер и получим ответ с результатом
   MqlTradeRequest request= { 0 };
   MqlTradeResult result= { 0 };
//--- Создадим переменные необходимые для работы с сервером
   double price= SymbolInfoDouble ( Symbol (), SYMBOL_ASK );
   double point= SymbolInfoDouble ( Symbol (), SYMBOL_POINT );
   int digits=( int ) SymbolInfoInteger ( Symbol (), SYMBOL_DIGITS );
   request.action= TRADE_ACTION_DEAL ;
   request.symbol= Symbol ();
   request.volume=Lot;
   request.deviation= 2 ;
   request.magic=Magic_Number;
   request.type= ORDER_TYPE_BUY ;
   request.price= NormalizeDouble (price,digits);
   /*Создадим запрос на торговый сервер request.sl, в котором укажем, где должен находиться уровень
     стоп лосс относительно цены, по которой открылась позиция "BUY". Уровень SL должен находиться
     ниже цены, поэтому пишем price-SL*point. Для нормализации уровня SL применим функцию преобразования
     данных NormalizeDouble, где обязательно умножим уровень SL на point (размер одного пункта) и укажем
     digits(количество знаков после запятой).
   */
   request.sl= NormalizeDouble (price-SL*point,digits);
   if (! OrderSend (request,result))
       PrintFormat ( "OrderSend error %d" , GetLastError ());
   PrintFormat ( "retcode=%u  deal=%I64u  order=%I64u" ,result.retcode,result.deal,result.order);
  }
//+------------------------------------------------------------------+
//| Sell Position Open function                                      |
//+------------------------------------------------------------------+

//--- Создадим функцию для открытия позиции Sell и назовём её OpenSELL
void OpenSELL()
  {
//--- Создадим запрос на сервер и получим ответ с результатом
   MqlTradeRequest request= { 0 };
   MqlTradeResult result= { 0 };
//--- Создадим переменные необходимые для работы с сервером
   double price= SymbolInfoDouble ( Symbol (), SYMBOL_ASK );
   double point= SymbolInfoDouble ( Symbol (), SYMBOL_POINT );
   int digits=( int ) SymbolInfoInteger ( Symbol (), SYMBOL_DIGITS );
   request.action= TRADE_ACTION_DEAL ;
   request.symbol= Symbol ();
   request.volume=Lot;
   request.deviation= 2 ;
   request.magic=Magic_Number;
   request.type= ORDER_TYPE_SELL ;
   request.price= NormalizeDouble (price,digits);
   /*Точно также создадим запрос на торговый сервер request.sl, в котором укажем, где должен
     находиться уровень стоп лосс относительно цены, по которой открылась позиция "SELL".
     Уровень SL теперь должен находиться выше цены, поэтому пишем price+SL*point. Снова для
     нормализации уровня SL применим функцию преобразования данных NormalizeDouble, где обязательно
     умножим уровень SL на point (размер одного пункта) и укажем digits(количество знаков после запятой).
   */
   request.sl= NormalizeDouble (price+SL*point,digits);
   if (! OrderSend (request,result))
       PrintFormat ( "OrderSend error %d" , GetLastError ());
   PrintFormat ( "retcode=%u  deal=%I64u  order=%I64u" ,result.retcode,result.deal,result.order);
  }
//+------------------------------------------------------------------+
//| All Position Close function                                      |
//+------------------------------------------------------------------+

//--- Создадим функцию для закрытия всех позиций и назовем её CloseALL
void CloseALL()
  {
//---  Создадим запрос на сервер и получим ответ с результатом
   MqlTradeRequest request;
   MqlTradeResult   result;
//--- перебираем все открытые позиции
   for ( int i= PositionsTotal ()- 1 ; i>= 0 ; i--)
     {
       //--- определяем параметры
       ulong   ticket= PositionGetTicket (i);                                             // тикет позиции
       string symbol= PositionGetString ( POSITION_SYMBOL );                               // символ
       int     digits=( int ) SymbolInfoInteger (symbol, SYMBOL_DIGITS );                     // количество знаков после запятой
       ulong   magic= PositionGetInteger ( POSITION_MAGIC );                               // MagicNumber позиции
       double volume= PositionGetDouble ( POSITION_VOLUME );                               // объем позиции
       ENUM_POSITION_TYPE type=( ENUM_POSITION_TYPE ) PositionGetInteger ( POSITION_TYPE ); // тип позиции
       //--- выводим информацию о позиции
       PrintFormat ( "#%I64u %s  %s  %.2f  %s [%I64d]" ,
                  ticket,
                  symbol,
                   EnumToString (type),
                  volume,
                   DoubleToString ( PositionGetDouble ( POSITION_PRICE_OPEN ),digits),
                  magic);
       //--- если MagicNumber совпадает
       if (magic==Magic_Number)
        {
         //--- обнуляем значения запроса на сервер и результата ответа
         ZeroMemory (request);
         ZeroMemory (result);
         //--- устанавливаем параметры операции
         request.action   = TRADE_ACTION_DEAL ;   // тип торговой операции (немедленное исполнение)
         request.position =ticket;               // тикет позиции
         request.symbol   =symbol;               // символ
         request.volume   =volume;               // объём позиции
         request.deviation= 2 ;                   // допустимое отклонение от цены
         request.magic    =Magic_Number;         // MagicNumber позиции
         if (type== POSITION_TYPE_BUY )             // если тип позиции "покупка"
           {
             //--- запрашиваем лучшее предложение цены на "продажу" по текущему символу
            request.price= SymbolInfoDouble (symbol, SYMBOL_BID );
             //--- запрашиваем тип рыночного ордера на "продажу" и закрываем позицию встречным ордером
            request.type = ORDER_TYPE_SELL ;
           }
         else // в противном случае
           {
             //--- запрашиваем лучшее предложение цены на "покупку" по текущему символу
            request.price= SymbolInfoDouble (symbol, SYMBOL_ASK );
             //--- запрашиваем тип рыночного ордера на "покупку" и закрываем позицию встречным ордером
            request.type = ORDER_TYPE_BUY ;
           }
         //--- выводим информацию о закрытии
         PrintFormat ( "Close #%I64d %s %s" ,ticket,symbol, EnumToString (type));
         //--- отправляем запрос
         if (! OrderSend (request,result))
             PrintFormat ( "OrderSend error %d" , GetLastError ());   // если отправить запрос не удалось, выводим код ошибки
         //--- информация об операции
         PrintFormat ( "retcode=%u  deal=%I64u  order=%I64u" ,result.retcode,result.deal,result.order);
         //---
        }
     }
  }
//+------------------------------------------------------------------+
//| Tick Up function                                                 |
//+------------------------------------------------------------------+
double TickUP()
  {
//---
   double price= SymbolInfoDouble ( Symbol (), SYMBOL_ASK );
   double point= SymbolInfoDouble ( Symbol (), SYMBOL_POINT );
   int digits=( int ) SymbolInfoInteger ( Symbol (), SYMBOL_DIGITS );
   double price1= NormalizeDouble (price+point,digits);
   return (price1);
  }
//+------------------------------------------------------------------+
//| Tick DOWN function                                               |
//+------------------------------------------------------------------+
double TickDOWN()
  {
//---
   double price= SymbolInfoDouble ( Symbol (), SYMBOL_ASK );
   double point= SymbolInfoDouble ( Symbol (), SYMBOL_POINT );
   int digits=( int ) SymbolInfoInteger ( Symbol (), SYMBOL_DIGITS );
   double price2= NormalizeDouble (price-point,digits);
   return (price2);
  }
  
//+------------------------------------------------------------------+
//| Шаблон трейлинг стопа предоставленный Василием Соколовым         |
//+------------------------------------------------------------------+
  
//+------------------------------------------------------------------+
//| Устанавливает sl позиции BUY в безубыток                         |
//+------------------------------------------------------------------+
void SetBreakevenForBuyPosition() // установить безубыток для Buy позиции
  {
//---
   ;
  }
//+------------------------------------------------------------------+
//| Тралит стоп-лосс позиции BUY вслед за ценой                      |
//+------------------------------------------------------------------+
void TrailingStopLossForBuyPosition() // перетащить Stop Loss для Buy позиции
  {
//---
   ;
  }
//+------------------------------------------------------------------+
//| Устанавливает sl позиции SELL в безубыток                        |
//+------------------------------------------------------------------+
void SetBreakevenForSellPosition() // установить безубыток для Sell позиции
  {
//---
   ;
  }
//+------------------------------------------------------------------+
//| Тралит стоп-лосс позиции SELL вслед за ценой                     |
//+------------------------------------------------------------------+
void TrailingStopLossForSellPosition() // перетащить Stop Loss для Sell позиции
  {
//---
   ;
  }
//+------------------------------------------------------------------+
 

¡Buen día y buen humor a todos!

Sigo estudiando el lenguaje de programación MQL5. Ayer escribí el código de Stop Loss en el punto de equilibrio para las posiciones de venta y compra. Estoy pegando el código sin ningún comentario todavía. Publicaré la versión completa del Asesor Experto con una descripción detallada de cada línea de código, en una forma accesible para los alumnos de 1er año de la escuela de programación, más adelante en el archivo con el título de trabajo Trailing_Stop.mq5. Antes de publicar el código, comprobamos el rendimiento del Asesor Experto en el probador de estrategias. No se ha encontrado ningún problema.

Saludos, Vladimir.

//+------------------------------------------------------------------+
//| Устанавливает sl позиции BUY в безубыток                         |
//+------------------------------------------------------------------+
void SetBreakevenForBuyPosition() // функция "Установить безубыток для позиции Buy"
  {
//---  Создадим запрос на сервер и получим ответ с результатом
   MqlTradeRequest request={0};
   MqlTradeResult result={0};
//--- Запустим цикл перебора всех открытых позиций
   for(int i=PositionsTotal()-1; i>=0; i--)
     {
      //--- параметры позиции Buy
      ulong  ticket=PositionGetTicket(i);                         // тикет позиции
      string symbol=PositionGetString(POSITION_SYMBOL);           // символ
      int    digits=(int)SymbolInfoInteger(symbol,SYMBOL_DIGITS); // количество знаков после запятой
      ulong  magic=PositionGetInteger(POSITION_MAGIC);            // MagicNumber позиции
      double volume=PositionGetDouble(POSITION_VOLUME);           // объем позиции
      double sl=PositionGetDouble(POSITION_SL);                   // Stop Loss позиции
      ENUM_POSITION_TYPE type=(ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE);  // тип позиции
      //--- выводим информацию о позиции Buy
      PrintFormat("#%I64u %s  %s  %.2f  %s  sl: %s  tp: %s  [%I64d]",
                  ticket,
                  symbol,
                  EnumToString(type),
                  volume,
                  DoubleToString(PositionGetDouble(POSITION_PRICE_OPEN),digits),
                  DoubleToString(sl,digits),
                  magic);
      //--- если MagicNumber совпадает
      if(magic==Magic_Number)
        {
         double point=SymbolInfoDouble(symbol,SYMBOL_POINT);
         double price=SymbolInfoDouble(Symbol(),SYMBOL_ASK);
         double price_open=PositionGetDouble(POSITION_PRICE_OPEN);
         double price_breakeven=NormalizeDouble(price_open+BreakevenLevel*point,digits);
         //--- зададим условие переноса Stop Loss в безубыток для позиции Buy
         if(type==POSITION_TYPE_BUY && price==price_breakeven)
           {
            sl=NormalizeDouble(price_open,digits);
           }
         //--- обнуляем значения запроса и результата
         ZeroMemory(request);
         ZeroMemory(result);
         //--- устанавливаем параметры операции
         request.action=TRADE_ACTION_SLTP; // тип торговой операции
         request.position=ticket;          // тикет позиции
         request.symbol=symbol;            // символ
         request.sl=sl;                    // Stop Loss позиции
         request.magic=Magic_Number;       // MagicNumber позиции
         //--- выводим информацию о модификации
         PrintFormat("Modify #%I64d %s %s",ticket,symbol,EnumToString(type));
         //--- отправка запроса
         if(!OrderSend(request,result)) // если отправить запрос не удалось
            PrintFormat("OrderSend error %d",GetLastError()); // выводим код ошибки
         //--- информация об операции
         PrintFormat("retcode=%u  deal=%I64u  order=%I64u",result.retcode,result.deal,result.order);
        }
     }
  }
//+------------------------------------------------------------------+
//| Устанавливает sl позиции SELL в безубыток                        |
//+------------------------------------------------------------------+
void SetBreakevenForSellPosition() // функция "Установить безубыток для позиции Sell"
  {
//---  Создадим запрос на сервер и получим ответ с результатом
   MqlTradeRequest request={0};
   MqlTradeResult result={0};
//--- Запустим цикл перебора всех открытых позиций
   for(int i=PositionsTotal()-1; i>=0; i--)
     {
      //--- параметры позиции Sell
      ulong  ticket=PositionGetTicket(i);                         // тикет позиции
      string symbol=PositionGetString(POSITION_SYMBOL);           // символ
      int    digits=(int)SymbolInfoInteger(symbol,SYMBOL_DIGITS); // количество знаков после запятой
      ulong  magic=PositionGetInteger(POSITION_MAGIC);            // MagicNumber позиции
      double volume=PositionGetDouble(POSITION_VOLUME);           // объем позиции
      double sl=PositionGetDouble(POSITION_SL);                   // Stop Loss позиции
      ENUM_POSITION_TYPE type=(ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE);  // тип позиции
      //--- выводим информацию о позиции Sell
      PrintFormat("#%I64u %s  %s  %.2f  %s  sl: %s  tp: %s  [%I64d]",
                  ticket,
                  symbol,
                  EnumToString(type),
                  volume,
                  DoubleToString(PositionGetDouble(POSITION_PRICE_OPEN),digits),
                  DoubleToString(sl,digits),
                  magic);
      //--- если MagicNumber совпадает
      if(magic==Magic_Number)
        {
         double point=SymbolInfoDouble(symbol,SYMBOL_POINT);
         double price=SymbolInfoDouble(Symbol(),SYMBOL_BID);
         double price_open=PositionGetDouble(POSITION_PRICE_OPEN);
         double price_breakeven=NormalizeDouble(price_open-BreakevenLevel*point,digits);
         //--- зададим условие переноса Stop Loss в безубыток для позиции Sell
         if(type==POSITION_TYPE_SELL && price==price_breakeven)
           {
            sl=NormalizeDouble(price_open,digits);
           }
         //--- обнуляем значения запроса и результата
         ZeroMemory(request);
         ZeroMemory(result);
         //--- устанавливаем параметры операции
         request.action=TRADE_ACTION_SLTP; // тип торговой операции
         request.position=ticket;          // тикет позиции
         request.symbol=symbol;            // символ
         request.sl=sl;                    // Stop Loss позиции
         request.magic=Magic_Number;       // MagicNumber позиции
         //--- выводим информацию о модификации
         PrintFormat("Modify #%I64d %s %s",ticket,symbol,EnumToString(type));
         //--- отправка запроса
         if(!OrderSend(request,result)) // если отправить запрос не удалось
            PrintFormat("OrderSend error %d",GetLastError()); // выводим код ошибки
         //--- информация об операции
         PrintFormat("retcode=%u  deal=%I64u  order=%I64u",result.retcode,result.deal,result.order);
        }
     }
  }
//+------------------------------------------------------------------+
 
MrBrooklin:

¡Buen día y buen humor a todos!

Sigo estudiando el lenguaje de programación MQL5. Hoy, como prometí, pego el código de la plantilla de mi Asesor Experto diseñada para escribir un trailing stop. Debido a que el código de la plantilla EA es bastante engorroso, he tenido que acortar al máximo los comentarios. La versión completa del Asesor Experto con comentarios detallados sobre cada línea del código, en una forma accesible para un estudiante de primer grado de la escuela de programación, se publicará más tarde como un archivo con el nombre de trabajo Trailing_Stop.mq5, para evitar crear otro estropicio en el sitio web.

...

Muy bien. La idea principal está bien captada. Me gusta especialmente el proceso de desarrollo:

MrBrooklin:

¡Buen día y buen humor a todos!

Sigo estudiando el lenguaje de programación MQL5. Ayer escribí el código para el Stop Loss en el punto de equilibrio para las posiciones de compra y venta.

Es decir, todo es correcto. No es necesario resolver el problema de una vez en un solo lugar. Deberías resolverlo poco a poco, como lo has hecho tú. Primero, describe la lógica básica en la función OnTick y las funciones básicas como OpenBUY, OpenSELL, TickUP, TickDown.

Luego, cuando este código básico está depurado, compilado y funciona dentro de sus posibilidades, se empieza a describir las demás funciones. Esto puede hacerse al día siguiente o incluso a la semana. Esto significa que no hay que cambiar el código principal. Creo que puedes ver por qué.

Ahora hay que perfeccionar esta forma de desarrollo: seguir escribiendo funciones, vinculando la funcionalidad a través de ellas al código existente. El programa existente debería funcionar correctamente. La complejidad no debería aumentar. Las funciones que se añadan deben devolver resultados sencillos y claros. Sus nombres deben ser tan sencillos como claros para usted. Si no puede hacerlo, puede ser porque necesita escribir no una, sino dos o incluso tres funciones para resolver el problema. Al principio, el marco general de la aplicación será difícil de definir. Por eso es mejor que pidas consejo a colegas más experimentados. Con el tiempo, aprenderás a desarrollar esas plantillas por tu cuenta.

A medida que pase el tiempo,tendrá que utilizar más y más construcciones del lenguaje dentro de las funciones para que se adapten a sus necesidades.En este punto, ampliarás gradualmente, y sobre todo de forma orgánica, tus conocimientos sobre las construcciones del lenguaje, aprenderás los bucles, las matrices e incluso a trabajar con punteros.

De hecho, ya ha aprendido la parte más difícil de la programación, aunque todavía no ha consolidado el material que ha aprendido. Lo único que tienes que hacer ahora es repetirlo muchas veces para consolidarlo. A partir de aquí no habrá nada fundamentalmente nuevo, todo es lo mismo: Plantilla general -> Descripción de los nombres de las funciones y sus parámetros-> Escritura del contenido de estas funciones -> Disposición de estas funciones en la unidad central. Eso es todo. Lo único que queda es perfeccionar la habilidad y ser constante. Varias construcciones adicionales que se empiezan a utilizar, como los bucles y las matrices, son sólo detalles, a veces muy bien hechos, pero nada más que detalles.

 
Por cierto, presta atención al tamaño total de tu programa. Ya es bastante importante. ¿Qué te parece? Por cierto, un novato no puede escribir un código de tamaño tan grande: las variables comienzan a confundirse, los paréntesis se hacen demasiado grandes, los errores de compilación comienzan a arrastrarse como hongos 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.
 

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

void OnTick()
  {
//--- Зададим время открытия и закрытия позиции
   MqlDateTime time_current,time_open,time_open1,time_close;
   TimeToStruct(TimeCurrent(),time_current);
   TimeToStruct((D'1970.01.01 09:00:00'),time_open);
   TimeToStruct((D'1970.01.01 09:01:00'),time_open1);
   TimeToStruct((D'1970.01.01 23:50:00'),time_close);
//--- Зададим условия для открытия позиций 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);
   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();}
   else
     {
      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
         && (TickDOWN()==(price-point)))
        {OpenSELL();}
     }
   if(time_current.hour==time_close.hour && time_current.min==time_close.min)
      CloseALL();
}

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 "bichos". De hecho, sólo dependen de la última línea:

TickUP()==(price+point)

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 dado (lo escribiré más adelante)

 
Vasiliy Sokolov:

Muy bien. El punto principal se capta correctamente. Me gustó especialmente el proceso de desarrollo:

Es decir, todo está bien. No hay que resolver el problema de una vez en un solo lugar. Hazlo gradualmente, como lo has hecho. Primero, describe la lógica básica en la función OnTick y las funciones básicas como OpenBUY, OpenSELL, TickUP, TickDown.

Luego, cuando este código básico está depurado, compilado y funciona dentro de sus posibilidades, se empieza a describir las demás funciones. Esto puede hacerse al día siguiente o incluso a la semana. Esto significa que no tienes que cambiar el código principal. Creo que puedes ver por qué.

Ahora hay que perfeccionar esta forma de desarrollo: seguir escribiendo funciones, vinculando la funcionalidad a través de ellas al código existente. El programa existente debería funcionar correctamente. La complejidad no debería aumentar. Las funciones que se añadan deben devolver resultados sencillos y claros. Sus nombres deben ser tan sencillos como claros para usted. Si no puede hacerlo, es posible que tenga que escribir no una, sino dos o incluso tres funciones para resolver el problema.

Para que una función pueda realizar las tareas que se le han encomendado, con eltiempo tendrá que utilizar más y más construcciones del lenguaje dentro de dichas funciones. Es entonces cuando poco a poco, y sobre todo de forma orgánica, ampliarás tus conocimientos sobre las construcciones del lenguaje, aprenderás los bucles, las matrices e incluso trabajarás con punteros.

¡Hola, Vasily! Muchas gracias por sus oportunas sugerencias y apoyo. Tus comentarios sobre el papel de las funciones y los principios de construcción del código del 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.

Razón de la queja: