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

 

Faites attention au filtre par symbole et par assistant dans la boucle de position. S'il n'y a pas de filtre, mais que vous parcourez toutes les positions ouvertes sur tous les symboles, c'est mauvais.

Donc, à première vue, tout semble aller bien.

Совершение сделок - Торговые операции - Справка по MetaTrader 5
Совершение сделок - Торговые операции - Справка по MetaTrader 5
  • www.metatrader5.com
Торговая деятельность в платформе связана с формированием и отсылкой рыночных и отложенных ордеров для исполнения брокером, а также с управлением текущими позициями путем их модификации или закрытия. Платформа позволяет удобно просматривать торговую историю на счете, настраивать оповещения о событиях на рынке и многое другое. Открытие позиций...
 
Andrei Novichkov:

Faites attention au filtre par symbole et par assistant dans la boucle de position. S'il n'y a pas de filtre, mais que vous parcourez toutes les positions ouvertes sur tous les symboles, c'est mauvais.

Ainsi, tout semble aller pour le mieux à première vue.

Merci beaucoup, Andrey ! Je comprends tout à propos de Magic car plusieurs positions peuvent être ouvertes pour un symbole, mais une autre question se pose. Le conseiller expert va-t-il parcourir les positions ouvertes pour tous les symboles en même temps, s'il n'est pas explicitement dirigé vers le symbole actuel ? Et ce, malgré le fait qu'il soit défini pour une certaine paire de devises, par exemple, EURUSD ? Honnêtement, je ne comprends pas bien ce point.

Salutations, Vladimir.

 
MrBrooklin:

Merci beaucoup, Andrey ! Je comprends tout à propos de Magic, car plusieurs positions peuvent être ouvertes sur un symbole, mais j'ai une autre question. L'EA va-t-il passer en revue les positions ouvertes pour tous les symboles en même temps, s'il n'est pas explicitement pointé sur le symbole actuel ? Et ce, malgré le fait qu'il soit défini pour une certaine paire de devises, par exemple, EURUSD ? Honnêtement, je ne comprends pas bien ce point.

Sincèrement, Vladimir.


Oui. Il est défini dans toutes les positions ouvertes pour tous les symboles.
De façon constante pour tous les postes ouverts.
Voici un simple coup d'œil dans le manuel.

https://book.mql4.com/ru/build/trading
 
MrBrooklin:

Ainsi, sur la base de la littérature que j'ai lue, j'ai écrit un court algorithme pour créer un Expert Advisor avec la fonction trailing stop :

  1. Créons un Expert Advisor pour automatiser le niveau de trailing stop Loss d'une position déjà ouverte avec des niveaux de Take Profit et de Stopspécifiés Loss.
  2. Dans l'Expert Advisor, créez un bloc de paramètres d'entrée avec deux paramètres : set "trailing level" et set "trailing step".
  3. Lorsque de nouvelles cotations arrivent, traitez-les avec la fonction OnTick( ). Le suivi ne fonctionne que lorsqu'un nouveau tick apparaît pour le symbole actuel.
  4. Créons et exécutons une boucle pour rechercher toutes les positions.
  5. Si nous ne trouvons soudainement aucune position ouverte, nous retournons à la boucle
  6. Nous rafraîchissons les citations.
  7. S'il y a un poste ouvert, nous continuons.
  8. Nous définissons le type de position ouverte : Acheter ou Vendre.
  9. S'il existe une position d'achatouverte , nous définissons où se situe le prix actuel par rapport à la position ouverte .
  10. Si le prix actuel est supérieur au prix auquel la position est ouverte, nous vérifions à quel niveau il a augmenté.
  11. Si le prix actuel a atteint le "niveau de suivi" défini dans les paramètres d'entrée, nous déplaçons leStop Loss au niveau sans perte qui est égal au prix d'ouverture de la position d'achat. Sinon, nous ne faisons rien.
  12. Si le prix actuel dépasse le niveau du Trailing Stop de la valeur égale au niveau duTrailing Stop, leStop Loss est déplacé du niveau du prix d'ouverture de la position d'achat de la valeur égale au niveau du Trailing Stop et ainsi de suite jusqu'à ce que le prix atteigne le niveau du Take Profit spécifié pour cette position .
  13. Si le prix tourne et atteint le niveau duStop Lossdéjà déplacé , la position est fermée .
  14. Si la position estVendue, nous définissons où se situe le prix actuel par rapport à la position ouverte .
  15. Si le prix actuel est inférieur au prix de la position ouverte, nous vérifions à quel niveau il est tombé.
  16. Si le prix actuel a atteint le niveau de suivi spécifié dans les paramètres d'entrée, nous déplaçons le Stop Loss au niveau sans perte égal au prix d'ouverture de la position devente. Sinon, nous ne faisons rien.
  17. Si le prix actuel a dépassé le niveau du Trailing Stop de la valeur égale au niveau duTrailing Stop, leStop Loss est déplacé du niveau de la position de vente d'ouverture de la valeur égale au niveau du Trailing Stop et ainsi de suite jusqu'à ce que le prix atteigne le niveau du Take Profit spécifié pour cette position .
  18. Si le prix tourne et atteint le niveau duStop Loss, la position est fermée .

Veuillez revoir l'algorithme et me donner des indications sur les points qui ont été oubliés.

Sincèrement, Vladimir.

La théorie n'est pas mauvaise, concentrons-nous maintenant sur la pratique. Est-ce que ça va marcher ?

 
Aliaksandr Hryshyn:

Bon pour la théorie, maintenant la pratique. Vous pouvez le faire ?

Je vais essayer. Mais vous comprenez que cela requiert un tout autre niveau de connaissances, et je ne l'ai pas encore.

Salutations, Vladimir.

 
Aleksey Masterov:

Oui. Sur toutes les poses ouvertes sur tous les symboles...
De manière cohérente dans toutes les poses ouvertes.
Voici un simple chalutage déjà dans le manuel.

https://book.mql4.com/ru/build/trading

Oui, Alexey, j'ai déjà vu ce code. C'est sous la forme d'un fichier d'inclusion. Pour être honnête, je n'ai rien trouvé sur le symbole qu'il contient, bien que je l'aie visionné plusieurs fois. J'ai peut-être mal compris quelque chose ou je cherche mal.

Sincèrement, Vladimir.

 

Pour l'instant, continuons avec les fonctions.

Comme je l'ai écrit précédemment, les fonctions sont partout, il faut les aimer et savoir les écrire. Les fonctions, sont nos petits combattants pour résoudre les problèmes mondiaux. Si nous étions des généraux dans une armée, quel genre de combattants voudrions-nous contrôler ? Voici une liste approximative :

  • Un combattant doit clairement exécuter un ordre. Le niveau d'intelligence moyen d'un fantassin n'est pas très élevé. Il est donc préférable de fixer des objectifs clairs et simples pour ces combattants : "prendre un bunker", "récupérer une langue", "miner un pont".
  • Si la tâche est difficile, ne cherchez pas un combattant super intelligent pour l'accomplir. Il est préférable de diviser la tâche en plusieurs sous-tâches et de prendre deux ou trois combattants plus stupides mais plus efficaces. Laissez chacun résoudre ses sous-tâches sans aucune question, ou mieux encore, laissez-les ignorer le concept et la tâche dans son ensemble. Ensuite, si quelqu'un est fait prisonnier, ce ne sera pas un problème, tout le plan ne sera pas révélé.
  • Un soldat doit suivre l'ordre quel que soit l'environnement : la neige, la pluie, Paris et les femmes - si cet environnement n'a aucun effet sur l'exécution de l'ordre, alors ces conditions et l'environnement externe doivent être ignorés.
  • Il arrive que les tâches soient difficiles. Ils nécessitent de nombreux combattants pour les résoudre. Un général ne peut pas être attribué à chaque combattant. Au lieu de cela, vous devez assigner un soldat plus intelligent pour diriger plusieurs combattants. Ce groupe s'unit à son tour avec les mêmes dans une compagnie de soldats et leur nomme un officier supérieur.

Mais nous nous sommes éloignés du sujet, passons à nouveau aux fonctions.

Si une fonction résout trop de problèmes en général - en suivant l'analogie, il s'agit d'un combattant très intelligent qui, si quelque chose ne va pas avec elle, pourrait ruiner toute l'entreprise. Si vous demandez ce que fait une telle fonction, la réponse pourrait être longue. Si le résultat de cette fonction cesse soudainement d'être correct, il sera très difficile de trouver la cause de l'erreur (car il y a beaucoup de tâches, beaucoup de code, beaucoup d'appels à des sous-procédures et il est difficile de comprendre où se trouve exactement l'erreur).

Si une fonction calcule des résultats corrects les lundis, mercredis et dimanches et les jours de repos en fonction de notre "humeur", peut-on se fier à cette fonction ? Imaginons que la fonction OrderSend, par exemple, ouvre des positions uniquement le jeudi et qu'un paramètre magique de valeur 13 soit défini. Et ce n'est pas du tout un non-sens ou une fantaisie. Ce comportement peut être organisé en un claquement de doigts - il suffit de rendre la fonction dépendante de certains paramètres de l'environnement externe.

Supposons que la fonction :

double sum(double a, double b)
{
   return a+b;
}

retournera toujours la somme de deux valeurs, quel que soit l'environnement externe. Cela signifie que même si nous copions cette fonction dans un autre script ou conseiller expert, elle y fonctionnera parfaitement bien. Cette fonction peut être écrite une fois et utilisée dans plusieurs de nos programmes par simple copie obtuse. Nous pourrons toujours compter sur son résultat, sachant que son fonctionnement ne dépend de rien. De telles fonctions, dont le résultat ne dépend pas de leur environnement, sont appelées fonctions sans effet secondaire ou fonctions pures. Si l'on s'efforce d'écrire des fonctions pures, on en obtient rapidement un grand nombre. Cela signifie que vous pouvez les combiner dans un fichier et les inclure dans vos nouveaux projets. C'est ce qu'on appelle la réutilisation du code. Nous ne faisons pas le travail deux fois. Au lieu de cela, nous utilisons des fonctions déjà écrites que nous connaissons et dont la fiabilité a été testée plus d'une fois.

Examinons maintenant l'anti-exemple :

double c = 0.0;
double sum(double a, double b)
{
   return a+b+c;
}

Le résultat semble être le même, car c est toujours égal à zéro. Ou n'est-ce pas toujours le cas ? Et si quelqu'un changeait c quelque part ? Et alors ? Que se passe-t-il si quelqu'un, quelque part, utilise également la variable externe c, mais pour ses propres besoins, et qu'il possède une variable c d'un type différent, disons une chaîne de caractères ? La combinaison de ces deux fonctions n'est plus possible (le compilateur ne permet pas de déclarer deux variables avec le même nom). Leurs dépendances communes sont également difficiles à résoudre. Je ne sais pas du tout quoi en faire. Par exemple, je ne connais toujours pas de moyen fiable et facile de faire fonctionner ces fonctions ensemble.

Même s'il n'y a pas d'autre fonction et qu'une seule fonction lit une variable externe, il n'est pas si facile de la copier ailleurs. Nous devons copier à la fois cette fonction et sa dépendance. Mais que se passe-t-il si nous copions ces fonctions dans un fichier commun ? Nous recevons 50 ou 100 de ces fonctions là-bas. Et chacune d'entre elles copie avec elle un tas de ses propres variables dépendantes. Nous obtenons un enchevêtrement de variables liées avec des fonctions peu claires. Mais à quoi sert tout cela ? Quels sont les problèmes qu'elle résout ? Pourquoi créer des dépendances inutiles quand on peut s'en passer dans la grande majorité des cas ?

Les fonctions ont une autre caractéristique surprenante. Les fonctions sont autodescriptives. En d'autres termes, il n'est pas nécessaire de dessiner un schéma, il suffit de choisir de bons noms et de diviser l'algorithme général en fonctions. Voici un exemple :

void OnTick()
{
   if(SelectFirstPendingOrder(ORDER_TYPE_BUY))
       CancelSelectPendingOrder();
}

Je ne sais pas ce que fait ce code, car les fonctions ne sont même pas écrites. Mais si je devais le lire, cela signifierait probablement que si le premier <premier> ordre en attente avec direction ORDER_TYPE_BUY est sélectionné avec succès, il serait annulé (la première fonction sélectionne, la seconde annule). Comme le code s'exécute chaque tick, quel que soit le nombre d'ordres en attente, chacun d'entre eux sera annulé tôt ou tard. Cela signifie également que toute tentative de placer un ordre d'achat en attente serait supprimée - l'ordre serait immédiatement retiré. Dans le même temps, les ordres de vente seront passés sans aucun problème.

Il n'y a que deux lignes de code et deux fonctions. Et l'algorithme n'est pas trivial, et ce qui est plus important, il est fiable.

En parlant de MCL, nous devrions mentionner un peu plus les fonctions pures. Comme il s'agit d'un langage d'application, il est difficile d'écrire quoi que ce soit sans s'appuyer sur les données fournies par le terminal. Après tout, il s'agit là de la tâche principale : interagir correctement avec l'environnement de négociation. Formellement, tout environnement de négociation est modifiable : prix, nombre d'ordres, modification de la balance, etc. Par conséquent, toute fonction interagissant avec un environnement commercial aussi changeant n'est pas claire. Parce que l'environnement commercial externe peut également être considéré comme une variable globale, qui change constamment. Mais lorsque nous écrivons OrdersTotal(), nous ne nous attendons pas à ce que cette fonction renvoie toujours la même valeur. Au lieu de cela, nous nous attendons à ce qu'il renvoie le nombre d'ordres en attente qui variera naturellement. Par conséquent, dans MQL, nous considérerons les fonctions comme propres et réutilisables, même si elles appellent des fonctions d'API externes, comme OrdersTotal(). Ce sera notre indulgence raisonnable.

 
Vasiliy Sokolov:

Continuons avec les fonctions...

Merci beaucoup, Vasily, pour les connaissances inestimables que vous partagez non seulement avec moi, mais aussi avec les programmeurs novices qui lisent ou liront ce sujet !

Avec le même grand respect, Vladimir.

 

Je continue à étudier le langage de programmation MQL5. Alors qu'il n'y avait pas de remarques sérieuses sur l'algorithme d'écriture du code de l'Expert Advisor Trailing_Stop (je me souviens du symbole et de Magic, je l'ajouterai à l'algorithme plus tard !), j'ai créé des paramètres d'entrée pour l'EA et écrit le code de la boucle qui lance la recherche des positions ouvertes.

Lorsque j'ai exécuté l'EA, j'ai constaté un problème - dans l'onglet "Experts" du terminal de trading, 2 messages identiques "Une boucle a commencé" apparaissent à chaque tick, malgré le fait que le terminal de trading n' a qu'un seul graphique de la paire de devises EURUSD et qu'une seule position est ouverte sur celui-ci. Et ces messages ont exactement la même heure de sortie.

J'ai lutté jusqu'à minuit mais je n'ai pas pu gagner. Je ne comprends pas quel est le problème.


Le code du conseiller expert est écrit en anglais, tandis que les commentaires sont en russe afin de faciliter le processus. Dans cette EA, j'ai essayé de tout décrire, comme je l'ai promis précédemment, d'une manière compréhensible pour un élève de 1ère année d'une école de programmation.

Salutations, 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 version   "1.00"

input ushort TrailingLevel=100; //Уровень трейлинга (для включения)
input ushort TrailingStep=10;   //Шаг трейлинга (для перемещения)
//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
//---

   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
//---

  }
//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
  {
//---
   /* Для цикла for создаем локальную переменную i и присваиваем ей значение "торговая функция PositionsTotal",
      которая возвращает нам количество открытых позиций*/
   int i=PositionsTotal();
   /* Разберемся, что такое оператор for.
      Оператор for состоит из трех Выражений и выполняемого Оператора:
      for(Выражение_1; Выражение_2; Выражение_3)
         Оператор;
      Выражение_1 описывает инициализацию цикла. За инициализацию цикла будет отвечать "Торговая функция PositionsTotal".
      Выражение_2 проверяет условия завершения цикла. Если оно истинно, то выполняется Оператор в теле цикла for.
      Все повторяется до тех пор, пока Выражение_2 не станет ложным. Если оно ложно, цикл заканчивается
      и управление передается следующему оператору.
      Выражение_З вычисляется после каждой итерации (т.е. после каждого повторения действия).
   */
   for(i; i>=0; i--) //запускаем цикл перебора открытых позиций (i) от максимума до нуля (i>=0) с шагом минус 1 (i--)
      Print("Запущен цикл");
  }
//+------------------------------------------------------------------+
//| Timer function                                                   |
//+------------------------------------------------------------------+
void OnTimer()
  {
//---

  }
//+------------------------------------------------------------------+
 
MrBrooklin:


i est égal au nombre de postes ouverts, donc beaucoup de cycles seront avec impression

Print("Запущен цикл");
vous devez enlever le signe "=" dans
   for(i; i>=0; i--)
Pourquoi avez-vous besoin de passer par la boucle lorsque le nombre de positions ouvertes est de 0. Cet appel à zéro est l'origine de la deuxième impression.