Auto-apprentissage du langage MQL5 à partir de zéro - page 71

 
MrBrooklin:

Igor, le désir d'apprendre le langage MQL5 ne se perd pas, au contraire, il ne fait qu'augmenter. Je suis déchiré, pourquoi je suis si bête et ne peux pas comprendre les choses simples avec cet opérateur de boucle. L'essentiel est que, sans l'opérateur de boucle, le code fonctionne parfaitement, mais avec la boucle, c'est un désordre complet. Je vais quand même aller au fond des choses. Cependant, j'ai beaucoup moins de temps pour l'auto-apprentissage depuis que la période de quarantaine est terminée et j'ai de rares moments pour étudier la langue au travail. La plupart du temps, j'étudie la langue le soir et tard dans la nuit.

Je dois résoudre deux tâches pour pouvoir enfin écrire le code du Trailing Stop :

  1. J'ai besoin d'écrire un code avec l'utilisation de l'opérateur for loop pour rechercher toutes les positions ouvertes pour tous les symboles, et s'il n'y en a pas, alors ouvrir une position d'achat entre 09:00:00 et 09:01:00, et la fermer de force à 23:50:00, si pendant la journée de trading un stop loss ne s'est pas déclenché. Sans l'opérateur for loop, comme je l'ai écrit précédemment, tout fonctionne parfaitement. Je veux maintenant obtenir le même résultat, mais en utilisant une boucle.
  2. Ecrivez 2 fonctions qui déterminent la direction de l'ouverture de la position par le premier tick qui apparaît dans le cadre temporel de 09:00:00 à 09:01:00. Si le premier tick est à la hausse, une position d'achat doit être ouverte ; si le premier tick est à la baisse, la position de vente doit être ouverte en conséquence. Ce n'est pas une stratégie, c'est juste mon "envie" de ne pas utiliser la condition aléatoire.
Cordialement, Vladimir.

Vladimir, vous vous lancez dans des trucs bizarres. On vous a donné un modèle : https://www.mql5.com/ru/forum/352460/page58#comment_18726492. Vous l'avez ignoré et avez décidé d'aller de l'avant avec votre esprit. Cette voie est difficile et peut prendre des années. En chemin, étudiez C, C++, Rust, C#, le polymorphisme, l'héritage, le déréférencement des pointeurs, la conversion dynamique des types : Vérifier ou conduire. Vous devez prendre le chemin le plus court vers le but. Si vous voulez du suivi, écrivez du suivi. Vous n'avez pas besoin d'étudier le C et le C++ pour le faire. Tout cela en tant qu'option si vous en avez envie.

La situation de la boucle for est illustrative. C'est une construction puissante qui doit être appliquée au bon endroit. Vous avez un filet et un algorithme auxiliaire. C'est pour cela qu'il n'y a pas d'énumération de postes, il n'y a pas de mages, donc vous n'avez pas cette boucle. Bien sûr, tout fonctionnera sans elle, mais la boucle ne fait que vous donner l'impression d'être du pétrole. Mais vous vous entêtez à l'utiliser, cela semble être une fin en soi : utiliser le plus et le plus possible, avoir le plus possible.

 
Vasiliy Sokolov:

Vladimir, tu t'encroûtes un peu. On vous a donné un modèle : https://www.mql5.com/ru/forum/352460/page58#comment_18726492. Vous l'avez ignoré et avez décidé d'aller de l'avant avec votre esprit. Ce chemin est difficile et peut prendre des années. En chemin, étudiez C, C++, Rust, C#, le polymorphisme, l'héritage, le déréférencement des pointeurs, la conversion dynamique des types : Vérifier ou conduire. Vous devez prendre le chemin le plus court vers le but. Si vous voulez du suivi, écrivez du suivi. Vous n'avez pas besoin d'étudier le C et le C++ pour le faire. Tout cela en tant qu'option si vous en avez envie.

La situation de la boucle for est illustrative. C'est une construction puissante qui doit être appliquée au bon endroit. Vous avez un filet et un algorithme auxiliaire. C'est pour cela qu'il n'y a pas d'énumération de postes, il n'y a pas de mages, donc vous n'avez pas cette boucle. Bien sûr, tout fonctionnera sans elle, mais la boucle ne fait que vous donner l'impression d'être du pétrole. Mais vous vous entêtez à l'utiliser, si bien qu'elle semble être une fin en soi : vous pouvez utiliser autant de tout et n'importe quoi à garder en stock que possible.

Bonjour, Vasily !

Merci d'avoir rejoint le sujet au bon moment et de m'avoir orienté sur la bonne voie, comme toujours. Maintenant, laissez-moi tout expliquer dans un seul ordre :

  1. Un modèle dans l'Expert Advisor a été inséré exactement au moment où vous avez eu l'amabilité de me le fournir, ce pour quoi je tiens à vous exprimer ma gratitude !
  2. J'ai décidé qu'un EA avec Trailin Stop devait être testé dans le testeur de stratégie, mais je ne savais pas comment le faire sans ouvrir automatiquement au moins une position. C'est pourquoi j'ai commencé à me sentir anxieux au moment d'écrire un conseiller expert "à part entière". Je l'ai mis entre guillemets car mon conseiller expert n'a pas de stratégie à proprement parler. Je ne voulais tout simplement pas utiliser la condition aléatoire pour ouvrir une position.
  3. Aucun des interlocuteurs de l'Expert Advisor, à l'exception de vous, n'a suggéré que la boucle for pour rechercher des positions dans cette situation n'est pas nécessaire. C'est ma faute, j'aurais pu le deviner depuis longtemps et me demander si une boucle était vraiment nécessaire. Il s'avère que ce n'est pas le cas. Merci encore pour le conseil !
  4. Pour passer enfin à l'écriture du trailing stop et ne pas abandonner le processus à mi-chemin, il me suffit d'écrire le code de 2 fonctions qui déterminent le sens d'ouverture d'une position de compensation Achat ou Vente.
  5. Oui, je suis tout à fait d'accord avec vous pour dire que l'autoformation commence à s'éterniser. Mais si l'on tient compte du fait que je suis une route inexplorée et tortueuse, j'espère qu'il est possible de faire une remise.
  6. Pendant toute la période d'auto-apprentissage, j'ai obtenu une énorme quantité d'informations et j'essaie maintenant non seulement de les organiser mais aussi de les utiliser dans la pratique. À cette fin, j'ai téléchargé un conseiller expert de CodeBase, je l'ai développé et utilisé pendant quinze jours sur le compte de démonstration d'un courtier en devises. Le résultat est exactement ce que j'attendais. Je vais bientôt commencer à l'utiliser sur un compte réel. C'est déjà un plus !
  7. Je ne chasse pas la vitesse de l'auto-apprentissage car j'ai déjà écrit plus haut que l'excès d'informations entraîne de temps en temps la stupeur.
  8. Pendant toute la période d'auto-apprentissage, petit à petit, j'ai commencé à mémoriser les mots et les phrases en anglais qui apparaissent après le code de compilation. Je regarde moins souvent dans Google Translate maintenant. C'est aussi un moment positif de l'auto-apprentissage.
  9. J'afficherai tout ce que j'ai écrit dans le conseiller expert exactement quand une pièce complète pour un stop suiveur sera prête. Je vais essayer de le terminer samedi. C'est ici que commencera le travail principal d'écriture du code du stop suiveur.

Vasily, je vous suis extrêmement reconnaissant pour vos conseils et vos critiques constructives ! J'exprime également ma gratitude à tous les participants de ce thème qui m'ont aidé dans mon auto-apprentissage du langage de programmation MQL5 à partir de zéro.

L'auto-apprentissage se poursuit. A suivre.

Salutations, Vladimir.

 
Андрей:

Bonjour à tous !

Si je voulais ajouter quelque chose d'intéressant, je pense que l'idée d'une "auto-formation à MQL5 à partir de zéro" n'est pas tout à fait juste. Si une personne est 0 en programmation, elle doit d'abord apprendre à programmer. Si le but est d'apprendre à programmer en MQL à partir de zéro, une personne doit d'abord apprendre àprogrammer en C, au moins les bases, et ensuite apprendre à écrire en MQL, car MQL est en fait du C, mais il est spécialisé pour une certaine tâche, une personne ne comprend pas comment les boucles fonctionnent, et essaie déjà d'écrire des programmes. C'est comme essayer d'apprendre le latin sans connaître le russe...

Une autre chose est que le C n'est pas un langage très convivial pour apprendre la programmation, il est simple, mais il est trop concis, il est difficile pour un débutant de lire et de comprendre du code sans avoir une compréhension de base des algorithmes, des types de données, de l'algèbre booléenne. En C, trois caractères à la suite peuvent signifier plusieurs chaînes de caractères dans un autre langage.

Pour apprendre à programmer simplement à partir de zéro, pour la base, pour apprendre à parler à un ordinateur dans un (peu importe) langage, je recommanderais de commencer avec Pascal, le livre le plus facile pour commencer avec lui (2-3 semaines à l'aise), puis C (là après Pascal max semaine avec assiduité, maîtriser la syntaxe, je veux dire les compétences de base du langage !) et ensuite se lancer dans des langages spécialisés comme MQL, parce que MQL est du C, qui a un peu de torsion et de simplification, puisqu'il est conçu pour une seule tâche et que le compilateur connaît l'évidence. Et le C est à son tour une sorte de Pascal compressé en RAR, mais le Pascal est presque anglais =).

Maintenant, à propos des boucles :

Lorsque j'ai appris la programmation, l'analogie avec les sommes en algèbre m'a aidé, où vous spécifiez la valeur initiale de n, la valeur finale de n et la formule avec ce n à calculer.

Bonjour Andrey ! Je suis tout à fait d'accord avec vous concernant la base. Je ne l'ai pas et ne l'ai jamais eu. Mais comme le dit le proverbe, Moscou ne s'est pas construite en une fois ! Merci pour vos conseils et astuces !

Salutations, Vladimir.

 
Roman Shiredchenko:

Oui. Je suis d'accord - ses codes sont bons aussi! Tu t'énerves pour rien. Prenez votre temps pour trier le code sans vous précipiter et c'est tout. Tout est élémentaire ici - sélectionner et chaluter : (combien plus facile - juste votre sujet de choisir la position NEED... :-))

En plus de cela, les fichiers d'inclusion - cela signifie qu'ils sont dans votre code en tant que fonctions également et c'est tout.

Merci, Roman, pour les codes et les liens ! J'aborde les codes avec calme et ne me presse sûrement pas !

Sincèrement, Vladimir.

 

Bonne journée et bonne humeur à tous !

Je continue à étudier le langage de programmation MQL5 . Aujourd'hui, comme promis, je publie pour que tout le monde puisse voir le code du modèle Expert Advisor conçu pour écrire un stop suiveur. En raison du fait que le code du modèle EA s'est avéré assez lourd, nous avons donc dû réduire les commentaires autant que possible. La version complète de l'Expert Advisor avec des commentaires détaillés sur chaque ligne de code, dans une présentation accessible à un étudiant de 1ère année d'une école de programmation, sera publiée ultérieurement sous la forme d'un fichier avec le nom de travail Trailing_Stop.mq5 donc afin de ne pas produire une autre "chaussure" sur le site.

Avant de publier le code, l'EA a été testé dans le testeur de stratégie . Aucun problème n'a été identifié. L'EA utilise un système de comptabilisation des positions nettes. Ce système comptable implique qu'à un moment donné, il ne peut y avoir qu'une seule position ouverte sur le compte pour le même symbole (instrument financier).

À l'heure actuelle, l'EA a la capacité de :

  1. Vérifiez le compte de trading sur lequel il est prévu de l'installer. Si le conseiller est installé sur un compte de démonstration, l'autorisation de continuer à travailler apparaîtra dans la fenêtre de message. Si une tentative est faite pour installer le conseiller sur un compte réel , un avertissement apparaîtra dans la fenêtre de message concernant l'impossibilité de poursuivre le travail, après quoi il sera automatiquement supprimé de la fenêtre de travail du terminal de trading .
  2. Une fois par jour de bourse à 9h00 exactement. selon l'heure de Moscou, ouvrir automatiquement une position dans la direction où le premier tick sera dirigé. Par exemple, si à 9h00. Heure de Moscou, le premier tick sera dirigé vers le haut, ce qui signifie qu'une position d'ACHAT s'ouvrira, si le premier tick est en baisse, alors une position de VENTE s'ouvrira.
  3. Définir la taille du stop loss.
  4. Définir la taille du lot.
  5. Définissez les autres paramètres qui seront nécessaires pour que le stop suiveur fonctionne.

De plus, l'EA dispose d'un modèle préinstallé, gracieusement fourni par Vasily Sokolov, qui sera rempli de code pendant le processus d'auto-apprentissage.

UN AVERTISSEMENT!!!

L'Expert Advisor est développé selon le plan d'auto-apprentissage pour un compte de démonstration et à des fins éducatives uniquement ! Non destiné au trading sur un compte réel et à faire des bénéfices !

Cordialement, Vladimir.

 //+------------------------------------------------------------------+
//|                                                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 позиции
  {
//---
   ;
  }
//+------------------------------------------------------------------+
 

Bonne journée et bonne humeur à tous !

Je continue à étudier le langage de programmation MQL5. Hier, j'ai écrit le code du Stop Loss au Breakeven pour les positions de vente et d'achat. Je colle le code sans aucun commentaire pour l'instant. Je publierai la version complète de l'Expert Advisor avec une description détaillée de chaque ligne de code, sous une forme accessible aux élèves de 1ère année de l'école de programmation, plus tard dans le fichier avec le titre de travail Trailing_Stop.mq5. Avant de publier le code, nous avons vérifié les performances du conseiller expert dans le testeur de stratégie. Aucun problème n'a été trouvé.

Salutations, 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:

Bonne journée et bonne humeur à tous !

Je continue à étudier le langage de programmation MQL5. Aujourd'hui, comme promis, je colle le code du modèle de mon Expert Advisor conçu pour écrire un trailing stop. Comme le code du modèle EA est assez lourd, j'ai dû réduire les commentaires au maximum. La version complète de l'Expert Advisor avec des commentaires détaillés sur chaque ligne du code, sous une forme accessible à un élève de 1ère année d'école de programmation, sera publiée ultérieurement comme un fichier avec le nom de travail Trailing_Stop.mq5, afin de ne pas produire un autre gâchis.

...

Très bien. L'idée principale est bien saisie. J'aime particulièrement le processus de développement :

MrBrooklin:

Bonne journée et bonne humeur à tous !

Je continue à étudier le langage de programmation MQL5. Hier, j'ai écrit le code pour le Stop Loss au Breakeven pour les positions d'achat et de vente.

C'est-à-dire que tout est correct. Vous ne devez pas résoudre le problème d'un seul coup et en un seul endroit. Vous devriez le résoudre progressivement, comme vous l'avez fait. Tout d'abord, vous décrivez la logique de base de la fonction OnTick et des fonctions de base telles que OpenBUY, OpenSELL, TickUP, TickDown.

Ensuite, lorsque ce code de base est débogué, compilé et fonctionne dans les limites de ses capacités, vous commencez à décrire les autres fonctions. Cela peut être fait le jour suivant ou même une semaine plus tard. Cela signifie que vous ne devez pas modifier le code principal. Je pense que vous pouvez voir pourquoi.

Vous devez maintenant affiner ce mode de développement : continuez à écrire des fonctions, en reliant par leur intermédiaire des fonctionnalités au code existant. Le programme existant devrait fonctionner correctement. La complexité ne doit pas augmenter. Les fonctions à ajouter doivent renvoyer des résultats simples et clairs. Leurs noms doivent être aussi simples et clairs pour vous. Si vous n'y parvenez pas, c'est peut-être parce que vous devez écrire non pas une, mais deux, voire trois fonctions pour résoudre le problème. Au début, le cadre général de l'application sera difficile à définir. C'est pourquoi il est préférable de demander conseil à des collègues plus expérimentés. Avec le temps, vous apprendrez à développer de tels modèles par vous-même.

Au fil du temps,vous devrez utiliser de plus en plus de constructions linguistiques dans les fonctions pour les adapter à vos besoins.À ce stade, vous élargirez progressivement et surtout de manière organique votre connaissance des constructions du langage, vous apprendrez les boucles, les tableaux et même à travailler avec des pointeurs.

En fait, vous avez déjà appris la partie la plus difficile de la programmation, même si vous n'avez pas encore consolidé le matériel que vous avez appris. Il ne vous reste plus qu'à le répéter plusieurs fois pour le consolider. A partir de là, il n'y aura rien de fondamentalement nouveau, tout est identique : Modèle général -> Description des noms des fonctions et de leurs paramètres-> Rédaction du contenu de ces fonctions -> Disposition de ces fonctions dans l'unité centrale. C'est tout. Il ne reste plus qu'à perfectionner cette compétence et à faire preuve de constance. Les diverses constructions supplémentaires que vous commencez à utiliser, comme les boucles et les tableaux, ne sont que des détails, parfois astucieux, mais rien de plus que des détails.

 
À propos, faites attention à la taille globale de votre programme. C'est déjà très important. Comment l'aimez-vous ? D'ailleurs, un novice ne peut pas écrire un code d'une telle taille : les variables commencent à être confuses, les parenthèses deviennent trop grandes, les erreurs de compilation commencent à apparaître comme des champignons après la pluie. Après la compilation, un programme de cette taille commence à avoir des problèmes et personne ne peut comprendre ce qui ne va pas. Et tout fonctionne dans votre code pour une raison quelconque), et il est clair dans les fonctions ce qui se passe et comment. En un mot, c'est une beauté.
 

Comme il n'y a pas de limite à la perfection, je vais ajouter quelques commentaires supplémentaires sur le code :

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

J'ai souligné en jaune deux endroits non triviaux.

1) Notez que le code est répété dans le premier if et dans le else suivant. La seule différence réside dans la dernière ligne et l'action finale (OpenBUY, OpenSell).

2) Les conditions pour entrer dans l'autre bloc ne sont pas évidentes. Ils ne sont pas visibles en raison de l'abondance de ? En fait, ils ne dépendent que de la dernière ligne :

TickUP()==(price+point)

C'est un signe certain qu'une fonction manque ici.

Nous devons écrire une fonction qui retourne vrai si le temps pour ouvrir la position correspond à celui donné (je l'écrirai plus tard).

 
Vasiliy Sokolov:

Très bien. Le point principal est correctement saisi. J'ai particulièrement apprécié le processus de développement :

C'est-à-dire que tout va bien. Il n'est pas nécessaire de résoudre le problème d'un seul coup et en un seul endroit. Vous le faites progressivement, comme cela s'est produit dans votre cas. Tout d'abord, vous décrivez la logique de base de la fonction OnTick et des fonctions de base telles que OpenBUY, OpenSELL, TickUP, TickDown.

Ensuite, lorsque ce code de base est débogué, compilé et fonctionne dans les limites de ses capacités, vous commencez à décrire les autres fonctions. Cela peut être fait le lendemain ou même une semaine plus tard. Cela signifie que vous ne devez pas modifier le code principal. Je pense que vous pouvez voir pourquoi.

Vous devez maintenant affiner ce mode de développement : continuez à écrire des fonctions, en reliant par leur intermédiaire des fonctionnalités au code existant. Le programme existant devrait fonctionner correctement. La complexité ne doit pas augmenter. Les fonctions à ajouter doivent renvoyer des résultats simples et clairs. Leurs noms doivent être aussi simples et clairs pour vous. Si vous n'y parvenez pas, il se peut que vous deviez écrire non pas une, mais deux, voire trois fonctions pour résoudre le problème.

Pour qu'une fonction puisse accomplir les tâches prévues,vous devrez éventuellement utiliser de plus en plus de constructions linguistiques dans ces fonctions. C'est à ce moment-là que vous allez progressivement et surtout organiquement étendre votre connaissance des constructions du langage, apprendre les boucles, les tableaux et même travailler avec des pointeurs.

Bonjour, Vasily ! Merci beaucoup pour vos suggestions et votre soutien opportuns. Vos commentaires sur le rôle des fonctions et les principes de construction du code de programme m'ont vraiment aidé à apprendre le langage de programmation 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

Maintenant que les informations dans ma tête sont structurées, il est plus facile de comprendre le code écrit non seulement par moi, mais aussi par d'autres programmeurs. J'espère que ce sujet sera une bonne aide pour ceux qui commencent à apprendre le langage de programmation MQL5 depuis le début.

Salutations, Vladimir.