English Русский 中文 Español Deutsch 日本語 Português 한국어 Italiano Türkçe
preview
Développer un Expert Advisor de trading à partir de zéro (Partie 28) : Vers l'avenir (III)

Développer un Expert Advisor de trading à partir de zéro (Partie 28) : Vers l'avenir (III)

MetaTrader 5Exemples | 8 avril 2024, 11:09
1 068 0
Daniel Jose
Daniel Jose

Introduction

Lorsque j'ai commencé à développer le système d'ordres, après l'article Développer un Expert Advisor de trading à partir de zéro (Partie 18), je n'avais aucune idée du temps qu'il me faudrait pour en arriver là. Nous sommes passés par différents moments, changements, modifications, etc. Je vous ai montré comment faire certaines choses spécifiques, comme marquer des choses ou rendre le système plus intuitif. Mais il y a aussi des choses que je ne pouvais pas montrer à ce stade parce que le chemin n'était pas complètement préparé. Ce voyage nous a permis d'élaborer le concept de manière à ce que tout le monde puisse comprendre l'idée et savoir comment le système fonctionne.

Dans tous les articles précédents, j'ai préparé le terrain pour que nous arrivions à cet article avec le même niveau de compréhension du fonctionnement du système. J'espère donc que ce matériel ne sera pas extrêmement confus ou complexe. Il y avait une question dès le début, et j'ai évité de l'analyser en détail. Mais elle est très importante pour les traders plus expérimentés. À première vue, cela peut sembler stupide, mais lorsque le moment sera venu de trader, nous comprendrons qu'il nous manque quelque chose dans l'EA. Vous vous demandez peut être ce qu'il manque. Je parle d'un moyen de restaurer les valeurs de Take Profit et de Stop Loss qui ont été supprimées pour une raison quelconque et que nous voulons restaurer sur le graphique.

Si vous avez déjà essayé de le faire, vous comprendrez qu'il s'agit d'une tâche assez difficile et lente, car il faut "suivre un certain scénario" pour que tout se passe bien, sans quoi nous commettrons sans cesse des erreurs.

MetaTrader 5 fournit un système de tickets qui permet de créer et de corriger les valeurs des ordres. L'idée est d'avoir un Expert Advisor qui rendrait le même système de tickets plus rapide et plus efficace. Le système MetaTrader 5 n'est pas parfait ; il est parfois plus lent et plus sujet aux erreurs que l'EA que nous développons.

Mais jusqu'à présent, je n'ai jamais expliqué comment générer des valeurs pour les niveaux TP et SL (Take Profit et Stop Loss). Je pense que la suppression des niveaux d'arrêt est suffisamment claire et intuitive. Mais comment le mettre en œuvre, c'est-à-dire comment procéder pour fixer les stops ou les rétablir directement sur le graphique ? C'est quelque chose de très intriguant, qui pose quelques questions, et bien sûr, c'est la raison pour laquelle nous avons écrit cet article : pour montrer l'une des nombreuses façons de créer les niveaux de stop directement sur le graphique, sans avoir recours à une ressource externe, en utilisant simplement le système d'ordres de l'EA.


2.0. Pour commencer : Implémentation

Tout d'abord, nous devons forcer l'EA à cesser ses contrôles, ce qu'il fait depuis les premiers jours de son développement. Pour supprimer ce contrôle, vous devez supprimer tout le code barré et ajouter le code surligné :

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())
        {
                IndicatorAdd(ticket = PositionGetInteger(POSITION_TICKET));
                SetTextValue(ticket, IT_RESULT, PositionGetDouble(POSITION_VOLUME), Res += PositionGetDouble(POSITION_PROFIT), PositionGetDouble(POSITION_PRICE_OPEN));
                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;
};

Le code barré ne cessera pas d'exister, mais il reviendra à un autre moment. Mais pour le moment, il s'agit plus d'un obstacle que d'un avantage. Une fois que cela est fait, nous pouvons commencer à réfléchir à la façon dont nous allons mettre en œuvre le système TP et SL directement sur le graphique, sans l'aide d'aucune ressource autre que l'EA.

Chaque développeur aura sa propre idée pour résoudre ce problème : certaines seront plus faciles à comprendre pour un traders, d'autres plus difficiles ; certaines seront plus difficiles à mettre en pratique, d'autres plus faciles. Je ne dis pas que la méthode que je vais utiliser et montrer ici est la plus appropriée ou la plus facile, mais elle est de loin la mieux adaptée à ma façon de travailler et d'utiliser la plateforme. Je n'aurai pas non plus besoin de créer de nouveaux éléments. Nous ne ferons que corriger certaines choses dans le code.


2.0.1. Modélisation du système de glissement

Le code de l’EA lui-même, au stade actuel de son développement, fournit quelques indications sur la manière dont nous devrions modéliser le système que nous allons créer. Regardez le code suivant :

#define macroUpdate(A, B) if (B > 0) {                                                                  \
                if (b0 = (macroGetLinePrice(ticket, A) == 0 ? true : b0)) CreateIndicator(ticket, A);   \
                PositionAxlePrice(ticket, A, B);                                                        \
                SetTextValue(ticket, A, vol, (isBuy ? B - pr : pr - B));                                \
                                     } else RemoveIndicator(ticket, A);
                                                                        
                void UpdateIndicators(ulong ticket, double tp, double sl, double vol, bool isBuy)
                        {
                                double pr;
                                bool b0 = false;
                                
                                if (ticket == def_IndicatorGhost) pr = m_Selection.pr; else
                                {
                                        pr = macroGetLinePrice(ticket, IT_RESULT);
                                        if ((pr == 0) && (macroGetLinePrice(ticket, IT_PENDING) == 0))
                                        {
                                                CreateIndicator(ticket, IT_PENDING);
                                                PositionAxlePrice(ticket, IT_PENDING, m_Selection.pr);
                                                ChartRedraw();
                                        }
                                        pr = (pr > 0 ? pr : macroGetLinePrice(ticket, IT_PENDING));
                                        SetTextValue(ticket, IT_PENDING, vol);
                                }
                                if (m_Selection.tp > 0) macroUpdate(IT_TAKE, tp);
                                if (m_Selection.sl > 0) macroUpdate(IT_STOP, sl);
                                if (b0) ChartRedraw();
                        }
#undef macroUpdate

Les lignes surlignées contiennent une macro qui exécutera la tâche. Nous devons la modifier afin qu'elle fournisse l'aide nécessaire pour mettre en œuvre ce dont nous avons besoin, à savoir l'indicateur de niveau d'arrêt. Examinez de plus près le code de la macro. Le voici :

#define macroUpdate(A, B){ if (B > 0) {                                                                 \
                if (b0 = (macroGetLinePrice(ticket, A) == 0 ? true : b0)) CreateIndicator(ticket, A);   \
                PositionAxlePrice(ticket, A, B);                                                        \
                SetTextValue(ticket, A, vol, (isBuy ? B - pr : pr - B));                                \
                                        } else RemoveIndicator(ticket, A); }

Voici ce qu’il fait : Lorsque la valeur B, qui peut être un Take Profit ou un Stop Loss, est supérieure à 0, vérifie si l'indicateur est sur le graphique. Si ce n'est pas le cas, il crée l’indicateur, le place sur graphique et définit la valeur à afficher. Si la valeur B est égale à 0, l'indicateur sera complètement supprimé du graphique. Ceci est fait avec le code mis en évidence dans le code de la macro. Mais serait-il suffisant qu'au lieu de supprimer complètement l'indicateur du graphique, nous conservions son élément ? Et si cet élément peut être configuré pour afficher ce que nous voulons faire (c'est-à-dire créer le(s) stop(s) manquant(s) pour un ordre ou une position), l'élément redeviendra-t-il un ordre ou une position OCO ? OUI, cela suffirait et c'est l'idée : laisser un élément, en l'occurrence un objet qui sert à déplacer les niveaux d'arrêt et à créer le niveau d'arrêt qui nous manque. Il suffit de faire glisser cet élément pour que la limite soit créée. Il s'agit du cadre théorique que nous utiliserons pour construire le système.

Mais en faisant cela, nous ne pouvons pas obtenir tout ce dont nous avons besoin. Une autre modification est nécessaire. Nous la ferons avant de poursuivre. Cette modification est mise en évidence dans le code ci-dessous :

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

//... Internal code...

        m_Selection.tp = (valueTp == 0 ? 0 : m_Selection.pr + (bKeyBuy ? valueTp : (-valueTp)));
        m_Selection.sl = (valueSl == 0 ? 0 : m_Selection.pr + (bKeyBuy ? (-valueSl) : valueSl));

// ... The rest of the code ...

}

Nous vérifions si les valeurs initiales saisies par Chart Trade sont nulles ou pas. Si oui, aucun indicateur ne sera créé et le graphique n'affichera que le point d'entrée.

Cela permet un travail plus linéaire dans le reste du système. Si aucun niveau de stop n'est spécifié, l'ensemble du modèle d'ordre aura le même comportement que lorsque la valeur est spécifiée dès le départ, lorsque nous allons placer un ordre en attente sur le graphique.

Ainsi, aucun trader ne pourra s'interroger : Quels sont ces chiffres accrochés à l'ordre ou à la position ? En effet, le trader saura qu'ils représentent les éléments qui peuvent être déplacés sur le graphique.

Malgré tout, nous avons encore un petit problème, pour lequel nous allons modifier deux macros. Voir ci-dessous :

#define macroSetLinePrice(ticket, it, price)    ObjectSetDouble(Terminal.Get_ID(), macroMountName(ticket, it, EV_LINE), OBJPROP_PRICE, price)
#define macroGetLinePrice(ticket, it)           ObjectGetDouble(Terminal.Get_ID(), macroMountName(ticket, it, EV_LINE), OBJPROP_PRICE)
#define macroGetPrice(ticket, it, ev)           ObjectGetDouble(Terminal.Get_ID(), macroMountName(ticket, it, ev), OBJPROP_PRICE)

Cette modification est très importante pour le reste du système que nous allons construire. Mais pourquoi est-ce que je sors les lignes coupées ? La raison en est que nous devons rendre le système encore plus flexible et, pour ce faire, le code barré a été supprimé et remplacé par une ligne surlignée. Pourquoi avons-nous besoin de macroGetPrice si nous n'avons pas de macro pour attribuer une valeur au prix ? En fait, il n'y a qu'un seul point qui effectue cet ajustement de prix, en l'inscrivant sur l'objet graphique. Ce point est illustré dans le code ci-dessous :

#define macroSetPrice(ticket, it, ev, price) ObjectSetDouble(Terminal.Get_ID(), macroMountName(ticket, it, ev), OBJPROP_PRICE, price)
//---

// ... Additional code inside the class....

//---
inline void PositionAxlePrice(ulong ticket, eIndicatorTrade it, double price)
                        {
                                int x, y, desl;
                                
                                ChartTimePriceToXY(Terminal.Get_ID(), 0, 0, price, x, y);
                                if (it != IT_RESULT) macroSetPrice(ticket, it, EV_MOVE, price);
                                macroSetPrice(ticket, it, EV_LINE, price);
                                macroSetAxleY(it);
                                switch (it)
                                {
                                        case IT_TAKE: desl = 160; break;
                                        case IT_STOP: desl = 270; break;
                                        default: desl = 0;
                                }
                                macroSetAxleX(it, desl + (int)(ChartGetInteger(Terminal.Get_ID(), CHART_WIDTH_IN_PIXELS) * 0.2));
                        }

Il n'est donc pas nécessaire que la macro ajuste le prix des objets pour qu'il soit visible à tous les autres endroits du code. En fait, il n'est même pas nécessaire que ce soit une macro pour l'instant. Mais je vais la laisser telle quelle pour réduire le risque d'erreur plus tard, lorsque ce code sera modifié.

Maintenant que notre système est à jour, nous pouvons passer au sujet suivant et faire en sorte que tout fonctionne comme prévu.


2.0.2. Nouvelle fonction de mise à jour

Je l'ai déjà mentionné dans le sujet précédent : il suffit de configurer la fonction de mise à jour et l'EA résoudra tous les problèmes qui y sont liés. Comme l'accent est mis uniquement sur les indicateurs Take Profit et Stop Loss, et qu'ils sont exécutés à l'intérieur de la macro, il suffit de configurer correctement la macro.

Mais il y a un problème qui n'a pas encore été résolu. Nous devons créer le bouton de déplacement indépendamment du reste de l'indicateur. Pour ce faire, isolons le code de création du bouton :

// ... class code ...

#define def_ColorLineTake       clrDarkGreen
#define def_ColorLineStop       clrMaroon

// ... class code ....

inline void CreateBtnMoveIndicator(ulong ticket, eIndicatorTrade it, color C = clrNONE)
                        {
                                string sz0 = macroMountName(ticket, it, EV_MOVE);

                                ObjectDelete(Terminal.Get_ID(), macroMountName(ticket, it, EV_MOVE));
                                m_BtnMove.Create(ticket, sz0, "Wingdings", "u", 17, (C == clrNONE ? (it == IT_TAKE ? def_ColorLineTake : def_ColorLineStop) : C));
                                m_BtnMove.Size(sz0, 21, 23);
                        }

// ... the rest of the class code ...

Ce code ne créera que le bouton de déplacement et rien d'autre. C'est très simple et direct. J'ai même pensé à laisser ce code sous forme de macro, mais j'ai décidé de le transformer en fonction. Elle est déclarée comme une fonction intégrée, de sorte que le compilateur la traitera de la même manière qu'une macro. Pour nous faciliter la tâche, la même partie du code a deux nouvelles définitions, parce qu'à un moment donné, nous ne créerons qu'un bouton de déplacement et nous voulons qu'il ait les mêmes couleurs que celles utilisées dans l'ensemble du système. Il n'est pas souhaitable que le système se comporte ou se présente différemment dans des cas similaires. Pour éviter les problèmes, nous laissons les couleurs telles qu'elles sont indiquées ci-dessus.

Nous pouvons maintenant passer à la fonction Update, dont le code complet est présenté ci-dessus. Notez que la seule différence entre la version ci-dessous et celle présentée plus haut dans l'article est le code mis en évidence. Ce code est une macro utilisée par la fonction de mise à jour elle-même.

#define macroUpdate(A, B){                                                                                                      \
                if (B == 0) {   if (macroGetPrice(ticket, A, EV_LINE) > 0) RemoveIndicator(ticket, A);                          \
                                if (macroGetPrice(ticket, A, EV_MOVE) == 0) CreateBtnMoveIndicator(ticket, A);                  \
                            } else if (b0 = (macroGetPrice(ticket, A, EV_LINE) == 0 ? true : b0)) CreateIndicator(ticket, A);   \
                PositionAxlePrice(ticket, A, (B == 0 ? pr : B));                                                                \
                SetTextValue(ticket, A, vol, (isBuy ? B - pr : pr - B));                                                        \
                        }
                                                                        
                void UpdateIndicators(ulong ticket, double tp, double sl, double vol, bool isBuy)
                        {
                                double pr;
                                bool b0 = false;
                                
                                if (ticket == def_IndicatorGhost) pr = m_Selection.pr; else
                                {
                                        pr = macroGetPrice(ticket, IT_RESULT, EV_LINE);
                                        if ((pr == 0) && (macroGetPrice(ticket, IT_PENDING, EV_MOVE) == 0))
                                        {
                                                CreateIndicator(ticket, IT_PENDING);
                                                PositionAxlePrice(ticket, IT_PENDING, m_Selection.pr);
                                                ChartRedraw();
                                        }
                                        pr = (pr > 0 ? pr : macroGetPrice(ticket, IT_PENDING, EV_MOVE));
                                        SetTextValue(ticket, IT_PENDING, vol);
                                }
                                macroUpdate(IT_TAKE, tp);
                                macroUpdate(IT_STOP, sl);
                                if (b0) ChartRedraw();
                        }
#undef macroUpdate

Examinons cette macro plus en détail pour comprendre ce qui se passe réellement. Il est nécessaire de comprendre cela pour pouvoir résoudre certains des problèmes qui se posent encore lors de l'utilisation du système. Il n'est pas encore prêt et nous devons encore le modifier.

Dans la première étape, nous avons le comportement suivant : lorsque nous supprimons l'un des ordres stop (Take Profit ou Stop Loss), nous supprimons immédiatement l'indicateur correspondant du graphique du symbole. Pour ce faire, nous vérifions s'il existe une ligne qui est l'un des points indiquant la présence de l'indicateur. Nous vérifions ensuite s'il existe un objet de mouvement de cet indicateur. S'il n'existe pas, nous le créons. Nous n'aurons donc pas l'indicateur sur le graphique, mais nous aurons un reste de l'indicateur toujours présent sur le graphique, qui est l'objet du mouvement.

La deuxième étape se produit dans le cas de la création d'un niveau d'arrêt : l'ordre sur le serveur aura un niveau d'arrêt qui doit apparaître sur le graphique. Dans ce cas, l'objet qui représentait le mouvement sera supprimé et un indicateur complet sera créé et placé à l'endroit approprié, indiquant le niveau actuel du stop (Take Profit ou Stop Loss).

Dans la dernière étape, nous positionnons l'indicateur au bon endroit. Un détail intéressant : si l'indicateur du niveau de stop n'est qu'un objet représentant la possibilité d'un mouvement, le point auquel il sera placé sera exactement le prix de l'ordre ou de la position. En d'autres termes, l'objet de mouvement qui permet de créer un Take Profit et un Stop Loss sera lié à la ligne de prix de l'ordre ou de la position à laquelle il appartient. Il sera ainsi facile de remarquer si un ordre ou une position ne comporte pas l'un des niveaux d'arrêt.

C'est essentiellement ce que nous devons faire : lorsque nous cliquons sur l'objet qui indique le mouvement, un fantôme est créé, comme d'habitude, et en même temps une représentation de l'indicateur complet est également créée. Cela se fait sans ajouter ni modifier de code. À partir de maintenant, nous pouvons déplacer et ajuster les niveaux d'arrêt de la manière habituelle, comme auparavant. Mais tant que nous n'aurons pas franchi un certain cap, l'ordre stop n'existera pas. Ceci est clairement démontré dans la vidéo de démonstration à la fin de l'article, où je montre comment le système fonctionne sur un compte réel.

Bien que tout semble aller bien, nous avons quelques incohérences qui nous obligent à créer ou plutôt à modifier le code à certains endroits. Nous verrons cela dans la prochaine rubrique, car cette étape est maintenant terminée.


2.0.3. Résoudre le problème des indicateurs flottants

Le premier des inconvénients apparaît lorsque nous sommes en mode indicateur flottant : il est sur le graphique mais pas sur le serveur. Pour plus d'informations, veuillez consulter les articles Développer un Expert Advisor de trading à partir de zéro (Partie 26) et (Partie 27), dans lesquels je montre comment l'indicateur flottant fonctionne et comment il a été mis en place. Ces indicateurs sont utiles et ne seront donc pas supprimés de l'EA. Mais ils ne conviennent pas au système que nous avons vu ci-dessus car leur fonctionnement est différent de celui des indicateurs qui représentent effectivement les ordres et les positions existant sur le serveur de trading. Pour résoudre les problèmes qui apparaissent lors de l'utilisation d'un indicateur flottant, nous devons accéder à la fonction DispatchMessage et y apporter des modifications, comme indiqué ci-dessous.

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

// ... Internal code...
                        
        switch (id)
        {

// ... Internal code...

                case CHARTEVENT_OBJECT_CLICK:
                        if (GetIndicatorInfos(sparam, ticket, it, ev)) switch (ev)
                        {
                                case EV_TYPE:
                                        if (ticket == def_IndicatorFloat)
                                        {
                                                macroGetDataIndicatorFloat;
                                                m_Selection.tp = (m_Selection.tp == 0 ? 0 : m_Selection.pr + (MathAbs(m_Selection.tp - m_Selection.pr) * (m_Selection.bIsBuy ? 1 : -1)));
                                                m_Selection.sl = (m_Selection.sl == 0 ? 0 : m_Selection.pr + (MathAbs(m_Selection.sl - m_Selection.pr) * (m_Selection.bIsBuy ? -1 : 1)));
                                                m_Selection.ticket = 0;
                                                UpdateIndicators(def_IndicatorFloat, m_Selection.tp, m_Selection.sl, m_Selection.vol, m_Selection.bIsBuy);
                                        } else m_BtnInfoType.SetStateButton(sparam, !m_BtnInfoType.GetStateButton(sparam));
                                        break;
                                case EV_DS:
                                        if (ticket != def_IndicatorFloat) m_BtnInfo_DS.SetStateButton(sparam, !m_BtnInfo_DS.GetStateButton(sparam));
                                        break;
                                case EV_CLOSE:
                                        if (ticket == def_IndicatorFloat)
                                        {
                                                macroGetDataIndicatorFloat;
                                                RemoveIndicator(def_IndicatorFloat, it);
                                                if (it != IT_PENDING) UpdateIndicators(def_IndicatorFloat, (it == IT_TAKE ? 0 : m_Selection.tp), (it == IT_STOP ? 0 : m_Selection.sl), m_Selection.vol, m_Selection.bIsBuy);
                                        }else if ((cRet = GetInfosTradeServer(ticket)) != 0) switch (it)

// ... The rest of the code...

En effectuant les changements soulignés ci-dessus, nous éliminons pratiquement tout autre problème lié à la configuration des indicateurs flottants, car nous avons maintenant la même façon d'ajuster les données de l'indicateur flottant et celles qui représentent les données existant sur le serveur de trading. Mais cela ne résout pas complètement nos problèmes. Nous devons résoudre un autre problème qui persiste depuis longtemps. Pour en discuter, passons au sujet suivant, car il mérite une discussion distincte.


2.0.4. L'inconvénient de la valeur négative du Take Profit

Le dernier inconvénient dont nous parlons dans cet article est que la valeur du Take Profit peut souvent être configurée pour être négative, ce qui se produit depuis longtemps. Mais cela n'a aucun sens pour le système de trading : si vous essayez d'envoyer la valeur au serveur, un message d'erreur sera renvoyé. Nous devons donc résoudre ce problème ainsi qu'un autre problème, à savoir qu'un ordre en attente peut avoir une valeur stop modifiée en positif.

L'EA permet maintenant de le faire, et pire encore : le système d'ordres indique que cette valeur est sur le serveur, alors qu'en fait le serveur renvoie une erreur, et l'EA l'ignore tout simplement. Le problème est plus compliqué dans le cas des ordres en attente, car dans le cas des positions, le comportement devrait être différent, et ce bug n'a pas encore été corrigé. Dès que nous aurons la possibilité de définir les niveaux de stop directement sur le graphique, cet inconvénient devrait disparaître.

Il convient de mentionner ici que dans le cas d'une position ouverte, nous pouvons avoir un stop loss avec une valeur positive, ce qui indique que si le stop loss se déclenche, la valeur correspondante sera créditée sur notre compte. Mais pour un ordre en attente, il s'agira d'une erreur qui empêchera le serveur de créer un ordre correct. Pour résoudre ce problème, nous devons vérifier la valeur du Take Profit : lorsqu'elle devient égale ou inférieure à 0, nous devons l'empêcher de passer à une valeur inférieure. De la même façon, pour les ordres en attente, nous ne devrions pas permettre que le Stop Loss soit supérieur à 0. En fait, nous forçons l'EA à utiliser une valeur minimale autorisée lorsque la condition 0 est remplie. Dans ce cas, l'ordre ou la position aura un certain sens pour le système de trading, mais il est absurde d'ouvrir une position avec un stop loss ou un take profit égal au prix d'ouverture.

Pour faciliter les choses, nous devons créer une variable dans le système, comme indiqué ci-dessous :

struct st00
{
        eIndicatorTrade it;
        bool            bIsBuy,
                        bIsDayTrade;
        ulong           ticket;
        double          vol,
                        pr,
                        tp,
                        sl,
                        MousePrice;
}m_Selection;

Pourquoi ne pas simplement modifier le prix de la ligne de la souris ? La raison en est que pour manipuler correctement la souris, il est nécessaire d'utiliser un appel système. C'est-à-dire qu'il serait nécessaire de manipuler les valeurs de position de la souris via l'API WINDOWS, ce qui nous obligerait à permettre l'utilisation de dll externes, ce que je ne veux pas faire. De cette façon, il est plus simple d'assembler une valeur locale à l'intérieur de l'EA, et les données mises en évidence stockeront cette valeur pour nous.

Cette valeur sera utilisée à 3 endroits différents. La première est dans la fonction de mouvement. Le code ci-dessous montre exactement où cela se produit :

void MoveSelection(double price)
{
        if (m_Selection.ticket == 0) return;
        switch (m_Selection.it)
        {
                case IT_TAKE:
                        UpdateIndicators(m_Selection.ticket, price, m_Selection.sl, m_Selection.vol, m_Selection.bIsBuy);
                        break;
                case IT_STOP:
                        UpdateIndicators(m_Selection.ticket, m_Selection.tp, price, m_Selection.vol, m_Selection.bIsBuy);
                        break;
                case IT_PENDING:
                        PositionAxlePrice(m_Selection.ticket, IT_PENDING, price);
                        UpdateIndicators(m_Selection.ticket, (m_Selection.tp == 0 ? 0 : price + m_Selection.tp - m_Selection.pr), (m_Selection.sl == 0 ? 0 : price + m_Selection.sl - m_Selection.pr), m_Selection.vol, m_Selection.bIsBuy);
                        m_Selection.MousePrice = price;
                        break;
        }
        if (Mouse.IsVisible())
        {
                m_TradeLine.SpotLight(macroMountName(m_Selection.ticket, m_Selection.it, EV_LINE));
                Mouse.Hide();
        }
}

Pourquoi ne pas tout mettre dans la fonction ci-dessus, puisqu'elle est responsable du déplacement des points des ordres stop ? Nous devons en effet effectuer certains calculs afin de définir correctement les niveaux de prise de profit ou de stop loss, et il est beaucoup plus facile de le faire à un autre moment. Nous devons également modifier cette valeur à un autre endroit. Voici le code :

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

// ... Internal code ....      
                                
        switch (id)
        {
                case CHARTEVENT_MOUSE_MOVE:
                        Mouse.GetPositionDP(dt, price);
                        mKeys   = Mouse.GetButtonStatus();
                        bEClick  = (mKeys & 0x01) == 0x01;    //Left mouse button click
                        bKeyBuy  = (mKeys & 0x04) == 0x04;    //SHIFT pressed
                        bKeySell = (mKeys & 0x08) == 0x08;    //CTRL pressed
                        if (bKeyBuy != bKeySell)
                        {
                                if (!bMounting)
                                {
                                        m_Selection.bIsDayTrade = Chart.GetBaseFinance(m_Selection.vol, valueTp, valueSl);
                                        valueTp = Terminal.AdjustPrice(valueTp * Terminal.GetAdjustToTrade() / m_Selection.vol);
                                        valueSl = Terminal.AdjustPrice(valueSl * Terminal.GetAdjustToTrade() / m_Selection.vol);
                                        m_Selection.it = IT_PENDING;
                                        m_Selection.pr = price;
                                }
                                m_Selection.tp = (valueTp == 0 ? 0 : m_Selection.pr + (bKeyBuy ? valueTp : (-valueTp)));
                                m_Selection.sl = (valueSl == 0 ? 0 : m_Selection.pr + (bKeyBuy ? (-valueSl) : valueSl));
                                m_Selection.bIsBuy = bKeyBuy;
                                m_BtnInfoType.SetStateButton(macroMountName(def_IndicatorTicket0, IT_PENDING, EV_TYPE), bKeyBuy);
                                if (!bMounting)
                                {
                                        IndicatorAdd(m_Selection.ticket = def_IndicatorTicket0);
                                        bMounting = true;
                                }
                                MoveSelection(price);
                                if ((bEClick) && (memLocal == 0)) SetPriceSelection(memLocal = price);
                        }else if (bMounting)
                        {
                                RemoveIndicator(def_IndicatorTicket0);
                                memLocal = 0;
                                bMounting = false;
                        }else if ((!bMounting) && (bKeyBuy == bKeySell) && (m_Selection.ticket > def_IndicatorGhost))
                        {
                                if (bEClick) SetPriceSelection(m_Selection.MousePrice); else MoveSelection(price);
                        }
                        break;

// ... Rest of the code...

Grâce à cela, nous avons un comportement prévisible dans le système. Il y a un dernier point où la valeur est référencée. Mais en raison de la complexité, j'ai décidé de modifier l'ensemble de manière à ce que la macro n'existe plus. Il s'agit maintenant d'une fonction. La nouvelle fonction de mise à jour est donc présentée ci-dessous :

void UpdateIndicators(ulong ticket, double tp, double sl, double vol, bool isBuy)
{
        double pr;
        bool b0, bPen = true;
                                
        if (ticket == def_IndicatorGhost) pr = m_Selection.pr; else
        {
                bPen = (pr = macroGetPrice(ticket, IT_RESULT, EV_LINE)) == 0;
                if (bPen && (macroGetPrice(ticket, IT_PENDING, EV_MOVE) == 0))
                {
                        CreateIndicator(ticket, IT_PENDING);
                        PositionAxlePrice(ticket, IT_PENDING, m_Selection.pr);
                        ChartRedraw();
                }
                pr = (pr > 0 ? pr : macroGetPrice(ticket, IT_PENDING, EV_MOVE));
                SetTextValue(ticket, IT_PENDING, vol);
        }
        b0 = UpdateIndicatorsLimits(ticket, IT_TAKE, tp, vol, pr, isBuy, bPen);
        b0 = (UpdateIndicatorsLimits(ticket, IT_STOP, sl, vol, pr, isBuy, bPen) ? true : b0);
        if (b0) ChartRedraw();
}

Les points surlignés remplacent la macro précédente, mais comme j'ai dit que le code requis était beaucoup plus compliqué, voyons où le troisième et dernier point est en fait référencé. Dans le code ci-dessus, il n'y a pas de différence entre les indicateurs take profit et stop loss : ils sont tous deux traités de la même manière. Jetez un coup d'œil au code ci-dessous.

inline bool UpdateIndicatorsLimits(ulong ticket, eIndicatorTrade it, double price, double vol, double pr, bool isBuy, bool isPen)
{
        bool b0 = false;
        double d1 = Terminal.GetPointPerTick();
                
        if (
    price  == 0)
        {
                if (macroGetPrice(ticket, it, EV_LINE) > 0) RemoveIndicator(ticket, it);
                if (macroGetPrice(ticket, it, EV_MOVE) == 0) CreateBtnMoveIndicator(ticket, it);
        } else if (b0 = (macroGetPrice(ticket, it, EV_LINE) == 0 ? true : b0)) CreateIndicator(ticket, it);
        switch (it)
        {
                case IT_TAKE:
                        price = (price == 0 ? 0 : (((isBuy ? price - pr : pr - price) > 0) ? price : (isBuy ? pr + d1 : pr - d1)));
                        break;
                case IT_STOP:
                        price = (price == 0 ? 0 : (isPen ? (((isBuy ? price - pr : pr - price) < 0) ? price : (isBuy ? pr - d1 : pr + d1)) : price));
                        break;
        }
        if (m_Selection.it == it) m_Selection.MousePrice = price;
        PositionAxlePrice(ticket, it, (price == 0 ? pr : price));
        SetTextValue(ticket, it, vol, (isBuy ? price - pr : pr - price));
                        
        return b0;
}

A partir de maintenant, la valeur du take profit d'un ordre en attente ne peut pas être erronée puisque nous avons une limite pour les valeurs autorisées. Il n'est plus possible de placer un ordre d'achat en attente et de déplacer ensuite le take profit vers une valeur négative (c'est-à-dire en dessous du point d'entrée), car le calcul de l'indicateur de take profit l'empêche. L'avantage d'écrire le code de cette façon est que, qu'il s'agisse d'un ordre ou d'une position, la valeur de la prise ne peut jamais être négative, car l'EA lui-même ne le permet pas.

En ce qui concerne le stop loss, le calcul est légèrement différent et nous vérifions ce que nous gérons - un ordre ou une position. S'il s'agit d'un ordre, la valeur du stop ne sera jamais positive, et s'il s'agit d'une position, l'EA ignorera simplement toutes les autres conditions. Dans ce cas, l'EA doit accepter la valeur spécifiée par le trader. Nous pouvons donc maintenant avoir un stop loss positif, mais uniquement dans le cas de positions, sans que le reste du code du système d'ordres ne soit affecté. De cette façon, l'EA pourra enfin interagir avec le serveur de trading sans que les données soumises ne soient rejetées.


Conclusion

Enfin, après plusieurs articles, nous avons atteint un point culminant, et nous disposons maintenant d'un système d’ordres presque complet et tout à fait adaptable à diverses situations et conditions de marché. Nous pouvons désormais faire du trading avec un système entièrement graphique en utilisant la souris et le clavier, en plus de votre analyse du marché, pour entrer ou sortir des transactions.

Pour ceux qui viennent d'arriver et qui veulent voir comment le système se comporte ou à quoi il ressemble au stade actuel de son développement, veuillez regarder la vidéo ci-dessous. Et merci à tous ceux qui ont suivi cette série d'articles jusqu'à présent. Mais le travail n'est pas terminé, et nous avons encore beaucoup à faire pour que cet Expert Advisor devienne quelque chose de mémorable. Rendez-vous dans le prochain article ! 👍



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

Le marché et la physique de ses modèles globaux Le marché et la physique de ses modèles globaux
Dans cet article, j'essaierai de vérifier l'hypothèse selon laquelle tout système ayant une compréhension, même limitée, du marché peut fonctionner à l'échelle mondiale. Je n'inventerai pas de théories ou de modèles, mais j'utiliserai uniquement des faits connus, que je traduirai progressivement dans le langage de l'analyse mathématique.
Développer un Expert Advisor de trading à partir de zéro (Partie 27) : Vers le futur (II) Développer un Expert Advisor de trading à partir de zéro (Partie 27) : Vers le futur (II)
Passons à un système d’ordres plus complet directement sur le graphique. Dans cet article, je vais montrer un moyen de corriger le système d’ordres, ou plutôt de le rendre plus intuitif.
Combinatoires et probabilités pour le trading (Partie V) : Analyse des courbes Combinatoires et probabilités pour le trading (Partie V) : Analyse des courbes
Dans cet article, j'ai décidé de mener une étude sur la possibilité de réduire les états multiples à des systèmes à deux états. L'objectif principal de cet article est d'analyser et de tirer des conclusions utiles qui pourraient contribuer au développement d'algorithmes de trading évolutifs basés sur la théorie des probabilités. Bien entendu, ce sujet fait appel aux mathématiques. Mais au vu de l'expérience des articles précédents, je constate que les informations générales sont plus utiles que les détails.
Développer un Expert Advisor de trading à partir de zéro (Partie 26) : En route vers le futur (1) Développer un Expert Advisor de trading à partir de zéro (Partie 26) : En route vers le futur (1)
Aujourd'hui, nous allons faire passer notre système d’ordres au niveau supérieur. Mais avant cela, nous devons résoudre quelques problèmes. Nous nous posons maintenant quelques questions liées à la manière dont nous voulons travailler et aux choses que nous faisons pendant notre journée de trading.