English Русский 中文 Español Deutsch 日本語 Português 한국어 Italiano Türkçe
preview
Développer un Expert Advisor de trading à partir de zéro (Partie 22) : Nouveau système d’ordres (V)

Développer un Expert Advisor de trading à partir de zéro (Partie 22) : Nouveau système d’ordres (V)

MetaTrader 5Trading | 13 novembre 2023, 11:06
584 0
Daniel Jose
Daniel Jose

Introduction

Il n'est pas facile de mettre en œuvre un nouveau système, car nous rencontrons souvent des problèmes qui compliquent considérablement le processus. Lorsque ces problèmes apparaissent, nous devons nous arrêter et réanalyser la direction dans laquelle nous nous engageons, en décidant si nous pouvons laisser les choses en l'état ou si nous devons y apporter un nouveau regard.

De telles décisions peuvent être assez fréquentes lors de la création d'un système, surtout si nous n'avons pas de délai ou de budget précis : lorsque nous ne sommes pas sous pression, nous pouvons tester et ajuster les choses afin de faire de notre mieux pour assurer la stabilité en termes de développement et d'amélioration.

Les programmes sérieux, en particulier les programmes commerciaux développés dans le cadre de certains budgets ou délais, contiennent généralement de nombreuses erreurs qui doivent être corrigées ultérieurement. Souvent, ceux qui développent le système n'ont pas le temps de mettre en œuvre ou d'apprendre certaines solutions qui pourraient être très bénéfiques pour le système. C'est ainsi que nous obtenons des programmes qui, dans de nombreux cas, ne sont pas à la hauteur de ce que les programmeurs pourraient créer. Parfois, les entreprises publient une version après l'autre, avec des améliorations mineures ou des corrections de bugs. Ce n'est pas parce que les bugs ont été détectés après la publication du programme, mais parce que les programmeurs étaient sous pression avant la publication.

Cela se produit en fait tout au long de la chaîne de production. Mais comme nous sommes en train de créer la solution, nous recherchons la meilleure et, de préférence, la plus simple. Nous pouvons aussi nous permettre d'explorer toutes les solutions possibles et réalisables. Si nécessaire, nous pouvons nous arrêter et revenir un peu en arrière afin de modifier et d'améliorer le processus de développement du système. Très souvent, un petit arrêt et un changement de direction peuvent grandement contribuer à la mise en place du système souhaité.

Dans l'article Développer un système de trading à partir de zéro (Partie 21), le système était presque prêt. Il ne lui manquait que la partie responsable du déplacement des ordres directement sur le graphique. Pour mettre en œuvre cette partie, j'ai vérifié et remarqué quelques bizarreries dans le code : il y avait beaucoup de parties répétitives. Nous avons pourtant veillé à éviter les répétitions inutiles. Pire encore, certaines choses n'ont pas fonctionné comme elles auraient dû, par exemple lors de l'utilisation d'actifs très différents. Lorsque nous avons commencé à concevoir l'EA et que j'ai commencé à le documenter dans les articles publiés ici, je pensais l'utiliser principalement pour trader des contrats à terme sur la bourse B3. En fait, j'ai fini par le développer pour trader des contrats à terme sur le dollar et sur les indices connu sous le nom de WDO et WIN. Mais au fur et à mesure que le système se rapprochait du système que je voulais utiliser à l'origine, j'ai réalisé qu'il pouvait être étendu à d'autres marchés ou actifs. Et c'est là que le problème s'est posé.


1.0. Retour au papier

Pour comprendre le problème, il convient de prêter attention à un détail important que de nombreuses personnes négligent lorsqu'elles développent un Expert Advisor. Comment les contrats sont-ils définis dans le système de trading ? MetaTrader 5 fournit ces informations, et MQL5 permet d'y accéder. Nous devons donc comprendre les données afin de créer des mathématiques permettant de généraliser les calculs et d'obtenir le système le plus complet.

Le système d'ordres que nous développons est conçu pour trader directement à partir du graphique, sans avoir besoin d'autres ressources externes ou de quoi que ce soit qui pourrait être créé à l'avenir. Ce développement est une tentative de créer un système de trading très similaire à ce qui est utilisé dans les plateformes, mais complètement open source afin que vous puissiez personnaliser et ajouter les informations dont vous avez besoin.

L'idée est de permettre à l'utilisateur de savoir ce qui se passe avec une position simplement en la regardant. Bien que l'idée semble très bonne, le système de trading ne facilite pas la vie de ceux qui essaient de créer un système pour le FOREX et les marchés boursiers comme B3. Le niveau d'information disponible est suffisant pour personnaliser les ordres pour un marché ou un autre, mais créer quelque chose de général est devenu un problème, presque une insulte personnelle. Mais j'ai décidé d'affronter le problème et d'essayer de créer un système universel.

Bien que je ne sois pas sûr de pouvoir vraiment le faire, je peux au moins vous montrer comment trouver les informations nécessaires. Ainsi, si vous devez adapter l'EA à votre système local, vous aurez l'expérience et les connaissances nécessaires pour le faire. Même si le système ne peut pas gérer la modélisation à l'origine, vous pourrez l'adapter à vos besoins.

Note : les données peuvent être obtenues à l'aide du code MQL5, mais pour simplifier le travail, j'utiliserai MetaTrader 5 pour montrer où se trouvent les données.


1.0.1. Actifs de la société (actions) tradés sur B3

Regardez les deux images suivantes :

     

Les parties en surbrillance indiquent les données dont nous avons besoin pour calculer la manière dont la position sera placée.

Si vous créez une position basée sur une valeur financière, vous aurez besoin de ces valeurs pour calculer les valeurs de stop loss et de take profit. Si vous n'utilisez pas d'ordre OCO, les choses seront encore plus simples. Mais nous supposons ici que tous les ordres sont des ordres ou des positions OCO.

Important : si un actif est tradé sous forme de parts fractionnaires (il existe des différences entre les marchés sur B3), le volume minimum sera de 1% de la valeur spécifiée, c'est pourquoi au lieu de trader 100 à 100, nous traderons 1 à 1. Le calcul sera différent, ne l'oubliez pas.


1.0.2. Contrats à terme sur B3

Les règles applicables aux contrats à terme sont différentes des règles de trading des actions, le volume change selon que vous tradez un contrat complet ou un mini-contrat, et même d'un actif à l'autre. Par exemple, pour trader BOI, nous devrions regarder comment l'effet de levier (multiplicateur) est rempli puisque c'est à partir de ce que nous allons faire ici. Je me concentrerai sur les contrats. Dans certains cas, un contrat complet équivaut à 25 mini-contrats. Mais cela peut varier. Il convient donc de toujours vérifier les règles de volume de la bourse.

Voyons maintenant les images suivantes du mini-dollar :

     

Ces contrats ont une date d'expiration, mais pour cet article ce n'est pas important. Dans l'article Développement d'un Expert Advisor de trading à partir de zéro (Partie 11), nous avons étudié comment créer un système d'ordres croisés pour trader des contrats à terme directement à partir de votre historique. Si vous utilisez cette méthode, vous n'avez pas à vous soucier de savoir quel contrat est actuellement tradé, car l'EA le fera pour vous. Faites attention aux points marqués. Ils sont différents de ceux du système de base. Cela peut parfois poser des problèmes, mais l'EA s'occupe de cette question, même si cette partie n’est pas très intuitive. Quoi qu'il en soit, avec le temps, vous vous habituerez aux valeurs correctes à utiliser dans l'effet de levier pour trader des volumes financiers spécifiques.

Mais j'ai décidé de me fixer un objectif plus élevé. C'est là que les difficultés commencent.


1.0.3. FOREX

Mon problème était lié au système Forex, et mon EA ne pouvait pas le résoudre. Lorsque j'ai réussi à manipuler l'effet de levier du Forex, j'ai découvert que le même code de l'EA B3 ne pouvait pas être utilisé. Cela m'a gêné car je ne vois pas pourquoi vous devriez avoir deux EA qui ne diffèrent que par cette partie. Pour comprendre ce qui se passe, regardez les images suivantes :

     

En B3, nous avons 4 valeurs, alors que nous n'en avons que 2 en Forex. Les calculs entre les deux marchés ne correspondaient pas en raison des deux valeurs manquantes.

De ce fait, les calculs effectués par l'EA rendaient très difficile la compréhension de ce qui était réellement fait : parfois les valeurs calculées étaient trop élevées, parfois le multiplicateur entraînait le rejet de l'ordre par le système de trading parce que le volume était trop faible par rapport au volume minimum attendu, et parfois les points de limite OCO n'étaient pas acceptés en raison de positions incorrectes. La confusion était donc permanente.

Une fois compris, j'ai décidé de modifier l'EA. Nous ne calculerons donc pas la valeur de la manière précédente, mais nous utiliserons une méthode différente. Cette valeur sera désormais ajustée en fonction de l'actif avec lequel nous travaillons, que ce soit sur le marché boursier ou sur le marché des changes. Ainsi, l'EA s'adaptera aux données et effectuera les calculs correctement. Mais nous devons savoir ce que nous négocions afin de définir correctement le multiplicateur. Même si, pour des raisons de calcul, nous autorisons désormais l'utilisation de valeurs à virgule flottante dans le volume, nous vous conseillons d'éviter de le faire. En fait, il vaut mieux utiliser des valeurs entières qui indiquent l'effet de levier utilisé.

Nous n'indiquons donc pas à l'EA le montant que nous souhaitons trader. Mais nous indiquons le multiplicateur à appliquer au volume minimal autorisé. Voici comment j'ai résolu le problème. Vous pouvez le tester vous-même et voir comment il fonctionne.

Pour faciliter la compréhension et réduire le travail, je présente brièvement les résultats dans la section suivante.


2.0. Visualisation des données

Prenons tout d'abord le cas du marché boursier, en particulier le marché boursier brésilien (B3). C'est ainsi que les données sont présentées dans l'EA en 5.


2.0.1. Actifs B3

Dans le cas d'une action de société cotée à la bourse brésilienne, le volume minimum de transactions est de 100. En indiquant 1, nous configurons l'EA pour qu'il trade le volume minimum autorisé. De cette façon, il est beaucoup plus facile de travailler avec le volume : il n'est pas nécessaire de connaître exactement le lot minimum, nous utilisons simplement le multiplicateur, et l'EA fera les calculs nécessaires pour créer un ordre correct.

Si vous utilisez des valeurs fractionnaires, indiquez simplement le nombre de fractions à utiliser, c'est-à-dire que 50 signifie que vous utiliserez 50 fractions. Si vous spécifiez 15, 15 fractions seront utilisées, et ainsi de suite.

Le résultat est affiché en dessous dans la fenêtre de la Boîte à Outils de MetaTrader 5. Elle montre comment un ordre avec la valeur de lot minimale a été créé. Si vous analysez les valeurs du take et du stop, vous verrez qu'elles correspondent à celles indiquées sur le graphique, ce qui signifie que l'EA a fonctionné ici.


2.0.2. Mini Dollar

Ici, le volume est de 1 pour 1. Mais dans le cas des contrats complets, la valeur est différente. Jetez un coup d'œil à l'opération graphique : les valeurs indiquées comme Take et Stop sont les mêmes que celles trouvées ci-dessus. Mais l'EA ajuste les valeurs correctement. Ainsi, les valeurs à prendre en compte sont celles de l'indicateur graphique. Les valeurs de Chart Trade sont à ignorer, mais elles seront proches des valeurs indiquées sur le graphique.


Les données suivantes sont affichées dans la Boîte à Outils :

Tout comme pour l'actif précédent, si nous exécutons le calcul pour vérifier les niveaux de stop loss et de take profit, nous verrons qu'ils correspondent à ceux spécifiés par l'EA. C'est-à-dire que l'EA a également passé cette étape sans qu'il soit nécessaire de recompiler le code, uniquement en changeant l'actif.


2.0.3. Forex

Cette partie est un peu plus compliquée. Pour ceux qui ne sont pas familiers avec le Forex, les points sont plutôt étranges, mais l'EA parvient tout de même à les comprendre.


MetaTrader 5 nous informera de cette transaction en attente de la manière suivante :


N'oubliez pas que sur le marché des changes, les niveaux d'effet de levier sont différents de ceux indiqués ci-dessus. Mais si vous effectuez les calculs, vous verrez que l'EA a réussi à fournir les points corrects et que l'ordre a été créé parfaitement.

Tout ceci est fait sans aucun changement supplémentaire, à l'exception de ceux que je montrerai dans la partie sur l'implémentation, bien que de nombreux autres changements aient été faits en plus. L’EA est ainsi vraiment adapté à la fois au marché boursier et au marché du Forex, sans avoir besoin d'une recompilation. Nous complétons donc ici les articles précédents. Nous verrons comment déplacer les niveaux de stop loss et de take profit directement sur le graphique.


3.0. Implémentation

Commençons par examiner quelques changements dans le code.

Tout d'abord, j'ai supprimé le système de limites mis en place il y a 3 ou 4 versions. Ceci est dû au fait que l'EA s'adapte parfois de manière incorrecte au calcul des lots entre les marchés boursiers et le forex.

J'ai ajouté un nouveau modèle de calcul pour permettre à l'EA de fonctionner aussi bien sur le marché des changes que sur celui des actions. Cela n'était pas possible avant cette version. Au tout début, l'EA était axé sur les marchés boursiers. Mais j'ai décidé d'étendre ses fonctionnalités au forex car les méthodes de trading ne diffèrent pas beaucoup.

Il y a des détails concernant le calcul des lots que l'EA ne pouvait pas traiter dans les versions précédentes. Avec les changements apportés, il peut maintenant être utilisé à la fois sur le marché des changes et sur le marché boursier sans aucune modification majeure du code. Pour maintenir la compatibilité avec les marchés des changes et les marchés boursiers, j'ai dû procéder à de nombreux changements.

L'un de ces changements se situe au tout début du code :

int OnInit()
{
        static string   memSzUser01 = "";
        
        Terminal.Init();
        WallPaper.Init(user10, user12, user11);
        Mouse.Init(user50, user51, user52);
        if (memSzUser01 != user01)
        {
                Chart.ClearTemplateChart();
                Chart.AddThese(memSzUser01 = user01);
        }
        Chart.InitilizeChartTrade(user20 * Terminal.GetVolumeMinimal(), user21, user22, user23);
        VolumeAtPrice.Init(user32, user33, user30, user31);
        TimesAndTrade.Init(user41);
        TradeView.Initilize();
                
        OnTrade();
        EventSetTimer(1);
   
        return INIT_SUCCEEDED;
}

La partie mise en évidence n'existait pas auparavant. Il y a également beaucoup d'autres changements. Mais nous ne nous concentrerons pas sur cette mise en œuvre. Nous allons plutôt voir comment utiliser l'EA pour déplacer les niveaux de Take et de Stop présents sur le graphique directement, sans utiliser d'autres astuces. Passons donc à cette partie.


3.0.1 Déplacer les ordres directement sur le graphique

Cette partie était très difficile dans les versions précédentes, car elles comportaient un certain nombre de problèmes non résolus qui sont apparus au fil du temps, ce qui a rendu la tâche difficile. L'une des raisons en était que le code était très clairsemé, ce qui rendait difficile la mise en œuvre efficace du système de déplacement des ordres directement sur le graphique. À l'origine, le système n'était pas censé le faire.

Pour vous donner une idée de l'ampleur des changements requis par l'EA (afin de pouvoir gérer les mouvements d'ordres, y compris les ordres en attente et les positions, de manière à pouvoir contrôler les mouvements d'ordres limités sur le graphique à l'aide de la souris), voyons à quoi ressemblait l'EA auparavant.

Le problème est que lorsque les objets ont été créés, ils ont remplacé la classe C_HLineTrade. Cela a été fait dans l'article Développer un EA de trading à partir de zéro (Partie 20). Le système a désormais une structure beaucoup plus complexe. Afin de ne pas montrer à nouveau l'ensemble du tableau ci-dessus, nous nous contenterons d'examiner ce qui s'est passé.

La flèche pointe vers le point de connexion où la classe C_HLineTrade a été supprimée pour laisser la place à de nouvelles classes. Cela a permis d'effectuer d'autres mises en œuvre, comme nous l'avons fait dans les articles précédents. Mais la présence de la classe C_OrderView interférait avec le développement, et nous avons finalement dû l'exclure. Mais ce n'est pas tout. La classe C_TradeGraphics a été fusionnée avec l'ancienne classe C_OrderView et une nouvelle classe nommée C_IndicatorTradeView est apparue. Cette classe a donc remplacé deux classes, ce qui nous a permis de développer le système de déplacement des ordres.

Ce que je vais présenter ici est la première version de ce système. Une autre version est en cours de développement, mais elle sera présentée dans un autre article.


3.0.1.1 - Écrire le code du nouveau système


Après la fusion, le nouveau système a la configuration suivante :


La zone verte indique un ensemble de classes qui sont libres, c'est-à-dire qu'elles seront gérées par MetaTrader 5 et non par l'EA. Mais comment cela se passe-t-il ? Examinons le processus en détail. En fait, l'EA ne créera, ne placera et ne supprimera que des classes et tous les objets créés par ces classes. Si vous regardez dans le code de l'EA, vous ne trouverez aucune structure ou variable faisant référence aux objets créés dans la zone verte. Cela permet de créer un nombre illimité d'objets, tant que MetaTrader 5 peut allouer de la mémoire dans le système d'exploitation. Le nombre d'objets n'est pas limité par les structures ou les variables à l'intérieur de l'EA.

On pourrait penser que seul un fou peut créer une telle structure. D'accord, vous pouvez me traiter de fou, parce que je l'ai créé et qu'il fonctionne. Il est aussi surprenant qu'il ne surcharge pas trop le système. Les gens me traitent de fou, alors ce n'est pas grave... Allons plus loin. Il se peut que vous remarquiez une certaine lenteur lorsque vous déplacez des ordres en attente ou des ordres limités. Cela n'est pas dû à une défaillance du code ou à un problème avec votre ordinateur ou avec MetaTrader 5. Le problème est que le système déplace les ordres en attente ou les limites en les déplaçant sur le serveur de trading lui-même, et qu'il y a un temps de latence entre le déplacement et la réponse du serveur. C'est la meilleure façon et la plus sûre d'opérer dans certains scénarios, ce qui ne nous permet pas de faire d'autres choses que je souhaite que l'EA fasse. Dans les prochains articles, nous allons donc remédier à ce problème en ajoutant une nouvelle fonctionnalité à l'EA, nous allons également rendre le système plus fluide, mais sans modifier la structure ci-dessus. Nous ne ferons que manipuler le code correctement, bien que cela rende le système moins sûr. Mais d'ici là, qui sait, je pourrais trouver une bonne solution à ce problème.

Examinons quelques points importants du nouveau code actuel. Nous commencerons par une fonction peu étudiée, dont le code se présente comme suit :

void OnTradeTransaction(const MqlTradeTransaction &trans, const MqlTradeRequest &request, const MqlTradeResult &result)
{
#define def_IsBuy(A) ((A == ORDER_TYPE_BUY_LIMIT) || (A == ORDER_TYPE_BUY_STOP) || (A == ORDER_TYPE_BUY_STOP_LIMIT) || (A == ORDER_TYPE_BUY))

        ulong ticket;
        
        if (trans.symbol == Terminal.GetSymbol()) switch (trans.type)
        {
                case TRADE_TRANSACTION_DEAL_ADD:
                case TRADE_TRANSACTION_ORDER_ADD:
                        ticket = trans.order;
                        ticket = (ticket == 0 ? trans.position : ticket);
                        TradeView.IndicatorInfosAdd(ticket);
                        TradeView.UpdateInfosIndicators(0, ticket, trans.price, trans.price_tp, trans.price_sl, trans.volume, (trans.position > 0 ? trans.deal_type == DEAL_TYPE_BUY : def_IsBuy(trans.order_type)));
                        break;
                case TRADE_TRANSACTION_ORDER_DELETE:
                                if (trans.order != trans.position) TradeView.RemoveIndicator(trans.order);
                                else
                                        TradeView.UpdateInfosIndicators(0, trans.position, trans.price, trans.price_tp, trans.price_sl, trans.volume, trans.deal_type == DEAL_TYPE_BUY);
                                if (!PositionSelectByTicket(trans.position))
                                        TradeView.RemoveIndicator(trans.position);
                        break;
                case TRADE_TRANSACTION_ORDER_UPDATE:
                        TradeView.UpdateInfosIndicators(0, trans.order, trans.price, trans.price_tp, trans.price_sl, trans.volume, def_IsBuy(trans.order_type));
                        break;
                case TRADE_TRANSACTION_POSITION:
                        TradeView.UpdateInfosIndicators(0, trans.position, trans.price, trans.price_tp, trans.price_sl, trans.volume, trans.deal_type == DEAL_TYPE_BUY);
                        break;
        }
        
        
#undef def_IsBuy
}

Ce code est très intéressant car il nous évite de devoir vérifier chaque nouvelle position qui apparaît ou qui est modifiée. En fait, c'est le serveur lui-même qui nous informe de ce qui se passe. Nous devons donc seulement nous assurer que l'EA répond correctement aux événements. Etudiez bien cette façon de coder et d'utiliser l'événement OnTradeTransaction, car si j'utilisais le modèle pour analyser les choses de la même façon que dans les versions précédentes, nous passerions beaucoup de temps sur les contrôles. Dans ce cas, le serveur fait tout le travail à notre place, et nous pouvons être sûrs que les valeurs du graphique montrent réellement ce que le serveur voit à ce moment-là.

Avant d'aborder les points essentiels du code ci-dessus, examinons un autre extrait.

void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam)
{
        Mouse.DispatchMessage(id, lparam, dparam, sparam);
        switch (id)
        {
                case CHARTEVENT_CHART_CHANGE:
                        Terminal.Resize();
                        WallPaper.Resize();
                        TimesAndTrade.Resize();
        break;
        }
        Chart.DispatchMessage(id, lparam, dparam, sparam);
        VolumeAtPrice.DispatchMessage(id, sparam);
        TradeView.DispatchMessage(id, lparam, dparam, sparam);
        ChartRedraw();
}

Tout se fait ainsi en un seul endroit. Nous pouvons entrer dans la classe et voir ce qui s'y passe.


3.1. La classe C_IndicatorTradeView

Cette classe est utilisée pour présenter et manipuler les données. Elle comprend essentiellement les anciennes classes C_OrderView et C_TradeGraphics, comme mentionné précédemment. Mais elle manipule les données d'une manière totalement différente. Examinons quelques points de cette classe.

Nous commencerons par la fonction d'initialisation :

void Initilize(void)
{
        int orders = OrdersTotal();
        ulong ticket;
        bool isBuy;
        long info;
        double tp, sl;

        ChartSetInteger(Terminal.Get_ID(), CHART_SHOW_OBJECT_DESCR, false);
        ChartSetInteger(Terminal.Get_ID(), CHART_SHOW_TRADE_LEVELS, false);
        ChartSetInteger(Terminal.Get_ID(), CHART_DRAG_TRADE_LEVELS, false);
        for (int c0 = 0; c0 <= orders; c0++) if ((ticket = OrderGetTicket(c0)) > 0) if (OrderGetString(ORDER_SYMBOL) == Terminal.GetSymbol())
        {
                info = OrderGetInteger(ORDER_TYPE);
                isBuy = ((info == ORDER_TYPE_BUY_LIMIT) || (info == ORDER_TYPE_BUY_STOP) || (info == ORDER_TYPE_BUY_STOP_LIMIT) || (info == ORDER_TYPE_BUY));
                IndicatorInfosAdd(ticket);
                UpdateInfosIndicators(-1, ticket, OrderGetDouble(ORDER_PRICE_OPEN), OrderGetDouble(ORDER_TP), OrderGetDouble(ORDER_SL), OrderGetDouble(ORDER_VOLUME_CURRENT), isBuy);
        }
        orders = PositionsTotal();
        for (int c0 = 0; c0 <= orders; c0++) if (PositionGetSymbol(c0) == Terminal.GetSymbol())
        {
                tp = PositionGetDouble(POSITION_TP);
                sl = PositionGetDouble(POSITION_SL);
                ticket = PositionGetInteger(POSITION_TICKET);
                IndicatorInfosAdd(ticket);
                UpdateInfosIndicators(1, ticket, PositionGetDouble(POSITION_PRICE_OPEN), tp, sl, PositionGetDouble(POSITION_VOLUME), PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_BUY);
        }
        CreateIndicatorTrade(def_IndicatorTicket0, IT_PENDING);
        CreateIndicatorTrade(def_IndicatorTicket0, IT_TAKE);
        CreateIndicatorTrade(def_IndicatorTicket0, IT_STOP);
}

Fondamentalement, nous créons les indicateurs nécessaires pour fonctionner et présentons tout ce qui existe actuellement sur le compte, comme les positions ou les ordres en attente. Mais les lignes surlignées sont importantes ici, car si vous n'utilisez pas un système d'ordres croisés, vous aurez sur le graphique les points d'ordre (de MetaTrader 5). Et si vous cliquez et déplacez ces points, l'EA mettra à jour les nouveaux points avec les changements dans les indicateurs. Cela n'est pas trop gênant, mais nous devons réellement utiliser le système que nous développons, sinon quel est l'intérêt de le développer ?

Il convient ensuite de prêter attention au code suivant :

void UpdateInfosIndicators(char test, ulong ticket, double pr, double tp, double sl, double vol, bool isBuy)
{
        bool isPending;
                                
        isPending = (test > 0 ? false : (test < 0 ? true : (ticket == def_IndicatorTicket0 ? true : OrderSelect(ticket))));
        PositionAxlePrice(ticket, (isPending ? IT_RESULT : IT_PENDING), 0);
        PositionAxlePrice(ticket, (isPending ? IT_PENDING : IT_RESULT), pr);
        SetTextValue(ticket, (isPending ? IT_PENDING : IT_RESULT), vol);
        PositionAxlePrice(ticket, IT_TAKE, tp);
        PositionAxlePrice(ticket, IT_STOP, sl);
        SetTextValue(ticket, IT_TAKE, vol, (isBuy ? tp - pr : pr - tp));
        SetTextValue(ticket, IT_STOP, vol, (isBuy ? sl - pr : pr - sl));
}

Il reçoit et met à jour les données, en présentant les valeurs correctes en termes de valeurs financières et les points où les ordres sont situés. Nous ne voulons pas nous préoccuper de l'existence d'un ordre en attente ou d'une position : la fonction la positionnera de manière à ce que nous puissions la voir correctement sur le graphique.

Voici la fonction suivante :

inline double SecureChannelPosition(void)
{
        double Res = 0, sl, profit, bid, ask;
        ulong ticket;
                                
        bid = SymbolInfoDouble(Terminal.GetSymbol(), SYMBOL_BID);
        ask = SymbolInfoDouble(Terminal.GetSymbol(), SYMBOL_ASK);
        for (int i0 = PositionsTotal() - 1; i0 >= 0; i0--) if (PositionGetSymbol(i0) == Terminal.GetSymbol())
        {
                ticket = PositionGetInteger(POSITION_TICKET);
                SetTextValue(ticket, IT_RESULT, PositionGetDouble(POSITION_VOLUME), profit = PositionGetDouble(POSITION_PROFIT), PositionGetDouble(POSITION_PRICE_OPEN));
                sl = PositionGetDouble(POSITION_SL);
                if (PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_BUY)
                {
                        if (ask < sl) ClosePosition(ticket);
                }else
                {
                        if ((bid > sl) && (sl > 0)) ClosePosition(ticket);
                }
                Res += profit;
        }
        return Res;
};

Elle est appelée par l'événement OnTick, ce qui la rend assez critique en termes de vitesse et de charge du système. La seule chose qu'elle fait, à l'exception des contrôles, est de mettre à jour la valeur sur le graphique : Veuillez noter que le ticket de la position est très important ici.

Examinons de plus près la fonction mise en évidence ci-dessus :

void SetTextValue(ulong ticket, eIndicatorTrade it, double value0, double value1 = 0.0, double priceOpen = 0.0)
{
        double finance;
                                
        switch (it)
        {
                case IT_RESULT  :
                        PositionAxlePrice(ticket, it, priceOpen);
                        PositionAxlePrice(ticket, IT_PENDING, 0);
                        m_EditInfo2.SetTextValue(MountName(ticket, it, EV_PROFIT), value1);
                case IT_PENDING:
                        m_EditInfo1.SetTextValue(MountName(ticket, it, EV_EDIT), value0 / Terminal.GetVolumeMinimal(), def_ColorVolumeEdit);
                        break;
                case IT_TAKE    :
                case IT_STOP    :
                        finance = (value1 / Terminal.GetAdjustToTrade()) * value0;
                        m_EditInfo1.SetTextValue(MountName(ticket, it, EV_EDIT), finance);
                        break;
        }
}

C'est ainsi que les valeurs correctes s'affichent. Mais la question est de savoir comment les déplacer. Pour ce faire, trois autres codes sont utilisés. Bien sûr, vous pouvez les éviter et utiliser le système MetaTrader 5 lui-même, qui est beaucoup plus rapide que le système EA actuel. Mais, comme je l'ai dit, je préfère utiliser l'EA, qui recevra bientôt d'autres améliorations.

La première fonction responsable du déplacement peut être vue ci-dessous, mais elle ne montre que les fragments nécessaires pour déplacer les points, soit les limites ou l'ordre lui-même, puisque le code complet est beaucoup plus étendu et n'est pas nécessaire pour comprendre comment se déplacer à l'aide des mouvements de la souris.

void DispatchMessage(int id, long lparam, double dparam, string sparam)
{
        ulong   ticket;

// ... Code ....

        switch (id)
        {
                case CHARTEVENT_MOUSE_MOVE:
                        Mouse.GetPositionDP(dt, price);
                        mKeys   = Mouse.GetButtonStatus();
                        bEClick  = (mKeys & 0x01) == 0x01;    //Left mouse click 
                        bKeyBuy  = (mKeys & 0x04) == 0x04;    //SHIFT press 
                        bKeySell = (mKeys & 0x08) == 0x08;    //CTRL press 
                        if (bKeyBuy != bKeySell)
                        {
                                if (!bMounting)
                                {
                                        Mouse.Hide();
                                        bIsDT = Chart.GetBaseFinance(leverange, valueTp, valueSl);
                                        valueTp = Terminal.AdjustPrice(valueTp * Terminal.GetAdjustToTrade() / leverange);
                                        valueSl = Terminal.AdjustPrice(valueSl * Terminal.GetAdjustToTrade() / leverange);
                                        m_TradeLine.SpotLight(MountName(def_IndicatorTicket0, IT_PENDING, EV_LINE));
                                        bMounting = true;
                                }
                                tp = price + (bKeyBuy ? valueTp : (-valueTp));
                                sl = price + (bKeyBuy ? (-valueSl) : valueSl);
                                UpdateInfosIndicators(0, def_IndicatorTicket0, price, tp, sl, leverange, bKeyBuy);
                                if ((bEClick) && (memLocal == 0)) CreateOrderPendent(leverange, bKeyBuy, memLocal = price, tp, sl, bIsDT);
                        }else if (bMounting)
                        {
                                UpdateInfosIndicators(0, def_IndicatorTicket0, 0, 0, 0, 0, false);
                                Mouse.Show();
                                memLocal = 0;
                                bMounting = false;
                        }else if ((!bMounting) && (bKeyBuy == bKeySell))
                        {
                                if (bEClick)
                                {
                                        bIsMove = false;
                                        m_TradeLine.SpotLight();
                                }
                                MoveSelection(price, mKeys);
                        }
                        break;

// ... Code ...
                case CHARTEVENT_OBJECT_CLICK:
                        if (GetIndicatorInfos(sparam, ticket, price, it, ev)) switch (ev)
                        {

// ... Code ...

                                case EV_MOVE:
                                        if (bIsMove)
                                        {
                                                m_TradeLine.SpotLight();
                                                bIsMove = false;
                                        }else
                                        {
                                                m_TradeLine.SpotLight(MountName(ticket, it, EV_LINE));
                                                bIsMove = true;
                                        }
                                        break;
                        }
                        break;
        }
}

Essayons de comprendre ce qui se passe. Une vidéo à la fin de l'article montre comment procéder et ce qui se passera réellement. Mais essayons d'abord de comprendre.

Chaque indication a un objet qui permet de la sélectionner (sauf le résultat qui ne peut pas être déplacé). Un clic sur ce point modifiera la ligne d'indication : elle deviendra plus épaisse. Lorsque cela se produit, les mouvements de la souris sont capturés et convertis en une nouvelle position pour cet objet, jusqu'à ce que nous donnions un nouveau clic en dehors de l'objet de sélection qui permette le déplacement de l'objet. Voyez qu'il n'est pas nécessaire de maintenir le bouton de la souris enfoncé, il suffit de cliquer une fois, de faire glisser, puis de cliquer à nouveau.

Mais en réalité, seule une partie du travail est effectuée ici. Il existe encore deux autres fonctions pour nous aider. L'une d'entre elles a déjà été vue ci-dessus et est responsable de l'affichage des valeurs calculées. La seconde est responsable de la lenteur qui fait ressembler l'EA à une limace lors de l'utilisation du système de déplacement de l'ordre ou de la limite : La voici :

void MoveSelection(double price, uint keys)
{
        static string memStr = NULL;
        static ulong ticket = 0;
        static eIndicatorTrade it;
        eEventType ev;
        double tp, sl, pr;
        bool isPending;
                                
        string sz0 = m_TradeLine.GetObjectSelected();
        
        if (sz0 != NULL)
        {
                if (memStr != sz0) GetIndicatorInfos(memStr = sz0, ticket, pr, it, ev);
                isPending = OrderSelect(ticket);
                switch (it)
                {
                        case IT_TAKE:
                                if (isPending) ModifyOrderPendent(ticket, macroGetPrice(IT_PENDING), price, macroGetPrice(IT_STOP));
                                else ModifyPosition(ticket, price, macroGetPrice(IT_STOP));
                                break;
                        case IT_STOP:
                                if (isPending) ModifyOrderPendent(ticket, macroGetPrice(IT_PENDING), macroGetPrice(IT_TAKE), price);
                                else ModifyPosition(ticket, macroGetPrice(IT_TAKE), price);
                                break;
                        case IT_PENDING:
                                pr = macroGetPrice(IT_PENDING);
                                tp = macroGetPrice(IT_TAKE);
                                sl = macroGetPrice(IT_STOP);
                                ModifyOrderPendent(ticket, price, (tp == 0 ? 0 : price + tp - pr), (sl == 0 ? 0 : price + sl - pr));
                                break;
                }
        };
}

Je l'appelle la fonction pierre parce qu'elle est responsable de la lenteur du système de positionnement. Si vous ne comprenez pas, jetez un coup d'œil aux points mis en évidence. Chacune d'entre elles est une fonction qui se trouve dans la classe C_Router et qui enverra une requête au serveur de trading. Si le serveur met du temps à répondre pour une raison ou une autre (et cela arrivera toujours à cause de la latence), le système de positionnement sera plus ou moins lent. Mais si le serveur répond rapidement, le système sera fluide, ou plutôt, les choses se dérouleront de manière plus harmonieuse. Nous modifierons cela plus tard, car ce système ne nous permet pas de faire autre chose. Quoi qu'il en soit, vous devez garder à l'esprit que de cette manière, vous serez un peu plus en sécurité, en particulier ceux qui aiment opérer dans des mouvements très volatils, où les prix peuvent évoluer très rapidement, mais même dans ce cas, vous courez le risque de voir les limites sauter. Il n'y a pas de solution, il faut sacrifier quelque chose. Pour ceux qui acceptent d'opérer en sachant que c'est exactement ce qui se trouvera à l'intérieur du serveur, l'EA est prêt à ce stade. Mais pour ceux qui veulent gagner en fluidité dans le fonctionnement de l'EA, même au prix de ne pas être précis aux points près, nous changerons les choses dans les prochains articles.

La vidéo ci-dessous montre comment le système fonctionne réellement. Soyez attentifs aux valeurs ​sur le graphique et dans la boîte à outils.


Conclusion

Bien que nous ayons maintenant un Expert Advisor très intéressant pour le trading, je vous conseille de l'utiliser sur un compte de démonstration pendant un certain temps pour vous habituer à son fonctionnement. Je vous promets qu'il n'y aura pas d'autres changements importants dans la façon dont il fonctionne. Il n'y aura que des améliorations. Dans le prochain article, nous ajouterons certaines choses qui manquent à cet EA, au détriment de la sécurité qu'elle offre. Quoi qu'il en soit, il s'agira d'une excellente source pour apprendre comment fonctionne le système de trading et comment manipuler la plateforme pour obtenir le type de modélisation de données dont nous avons besoin.

N'oubliez pas, si vous trouvez que déplacer les ordres ou les niveaux de limite est trop lent, vous pouvez supprimer les points que j'ai montrés dans l'article et utiliser MetaTrader 5 lui-même pour déplacer les ordres ou les limites et utiliser un EA comme support pour aider à interpréter les données. Que vous le fassiez ou non, c'est votre choix...


Traduit du portugais par MetaQuotes Ltd.
Article original : https://www.mql5.com/pt/articles/10516

Développer un Expert Advisor de trading à partir de zéro (Partie 23) : Nouveau système d'ordres (VI) Développer un Expert Advisor de trading à partir de zéro (Partie 23) : Nouveau système d'ordres (VI)
Nous allons rendre le système d’ordres plus flexible. Nous examinerons ici les modifications à apporter au code pour le rendre plus flexible, ce qui nous permettra de modifier les niveaux d'arrêt des positions beaucoup plus rapidement.
Apprenez à concevoir un système de trading basé sur l’Awesome Oscillator Apprenez à concevoir un système de trading basé sur l’Awesome Oscillator
Dans ce nouvel article de notre série, nous découvrirons un nouvel outil technique qui peut être utile dans notre trading. Il s’agit de l’indicateur Awesome Oscillator (AO). Nous apprendrons comment concevoir un système de trading basé sur cet indicateur.
Comment utiliser les modèles ONNX dans MQL5 Comment utiliser les modèles ONNX dans MQL5
ONNX (Open Neural Network Exchange) est un format ouvert, conçu pour représenter des modèles d'apprentissage automatique. Dans cet article, nous verrons comment créer un modèle CNN-LSTM pour prévoir des séries temporelles financières. Nous montrerons également comment utiliser le modèle ONNX créé dans un Expert Advisor MQL5.
Apprenez à concevoir un système trading basé sur le Relative Vigor Index Apprenez à concevoir un système trading basé sur le Relative Vigor Index
Un nouvel article de notre série sur la façon de concevoir un système de trading à l'aide de l'indicateur technique le plus populaire. Dans cet article, nous apprendrons comment procéder grâce à l’indicateur Relative Vigor Index.