English Русский 中文 Español Deutsch 日本語 Português 한국어 Italiano Türkçe
preview
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)

MetaTrader 5Exemples | 8 avril 2024, 11:06
108 0
Daniel Jose
Daniel Jose

Introduction

Dans l'article précédent, Développer un Expert Advisor en trading à partir de zéro (Partie 26), nous avons corrigé une erreur catastrophique qui existait dans le système d'ordres. Nous avons également commencé à mettre en œuvre des changements pour permettre le fonctionnement du nouveau système d’ordres. Bien que le système initialement implémenté dans cette série d’articles soit assez intéressant, il présente un défaut qui le rend inutilisable. Cette faille a été montrée à la fin de l’article précédent. La raison était de ne pas savoir comment trader, plus précisément comment choisir une heure d'expiration pour un ordre ou une position, en plus d'autres problèmes mineurs. Le système était défini comme un ordre ou une position qui devait être clôturé à la fin de la séance de bourse ou du jour en cours. Mais parfois, nous souhaitons effectuer des transactions à plus long terme, donc tout laisser tel quel n’aide pas vraiment.

Je vais donc vous montrer une correction dans cet article. Nous verrons comment rendre le système d’ordres plus intuitif afin que vous puissiez déterminer immédiatement et avec précision ce qu'est chaque ordre, comment il est traité et quel type de mouvement est attendu.

Ce système est si intéressant, simple et intuitif qu'une fois que vous l'aurez vu en action, vous ne voudrez plus travailler sans lui. Ce que je vais vous montrer dans cet article n'est qu'une des nombreuses possibilités que vous pouvez implémenter dans le système d’ordres. Je vous montrerai peut être plus tard d'autres choses, mais ce que nous allons voir dans cet article peut constituer une excellente base pour créer d'autres modifications utiles et intéressantes pour votre cas particulier. Quoi qu’il en soit, j’essaie de garder tout dans ces articles aussi général que possible.


2.0. Modèle intuitif

Jusqu'à présent, nous avons travaillé avec les ordres comme suit :

Les indications Take Profit et Stop Loss ont une forme reconnaissable et assez intuitive : le vert indique ce qui sera gagné sur notre compte et le rouge ce qui sera perdu. Tout est clair. Si nous avons une indication Stop comme indiqué ci-dessous, cela démontre quand même que l'activation du stop loss entraînera l'ajout de cette somme à notre compte. En d’autres termes, les niveaux d’ordres stop ne nécessitent pas d’efforts, du moins pour le moment. Peut-être qu'à l'avenir, vous souhaiterez changer quelque chose, mais pour le moment, ils sont tout à fait adaptés à leur utilisation.

Cette façon d'utiliser les niveaux d'arrêt est assez simple et intuitive à analyser pour tout trader. Mais il reste quelque chose qui n’est pas très clair. Le premier est l’indication du point d’entrée de l’ordre en attente.

Pouvons-nous savoir si cet ordre en attente est un ordre d’achat ou de vente ? Et encore une chose : est-il possible de savoir si cet ordre en attente sera clôturé en fin de journée ou si une position à plus long terme sera ouverte ? C'est facile. Maintenant, si nous avons déjà des positions ouvertes, l'indicateur ressemblera à ci-dessous :

Et encore une fois, nous avons les mêmes problèmes qu’avec les indicateurs des ordres en attente. Lorsque vous regardez le graphique et que vous voyez ces indicateurs, vous ne pouvez pas dire avec certitude si une position sera fermée à la fin de la journée ou si elle durera plus longtemps. Si elle fermée en fin de journée, vous ne voudriez pas que le courtier l'arrête obligatoirement, puisque vous devrez payer pour cela. Et il n'est pas très raisonnable de clôturer des ordres sans aucun critère, puisque même la boîte à outils MetaTrader n'affiche pas cette information, donc l'avoir sur le graphique via l'indicateur est tout simplement génial.

Nous devrons donc apporter ici des changements, notamment dans les indicateurs qui montrent le point d'entrée de la position, afin de mieux comprendre ce qui se passe.


2.0.1. Comment ajouter de nouvelles informations aux indicateurs

Le moyen le plus simple d’ajouter de nouvelles informations sans occuper beaucoup de place sur le graphique est d’utiliser des bitmaps, car ils sont faciles à comprendre et assez représentatifs. Ainsi, sans insérer de code supplémentaire, nous ajoutons quatre nouveaux bitmaps à l'EA, visibles dans la classe C_IndicatorTradeView.

#define def_BtnClose            "Images\\NanoEA-SIMD\\Btn_Close.bmp"
#define def_BtnCheckEnabled     "Images\\NanoEA-SIMD\\CheckBoxEnabled.bmp"
#define def_BtnCheckDisabled    "Images\\NanoEA-SIMD\\CheckBoxDisabled.bmp"
#define def_BtnDayTrade         "Images\\NanoEA-SIMD\\Inf_DayTrade.bmp"
#define def_BtnSwing            "Images\\NanoEA-SIMD\\Inf_Swing.bmp"
#define def_BtnInfoBuy          "Images\\NanoEA-SIMD\\Inf_Buy.bmp"
#define def_BtnInfoSell         "Images\\NanoEA-SIMD\\Inf_Sell.bmp"
//+------------------------------------------------------------------+
#resource "\\" + def_BtnClose
#resource "\\" + def_BtnCheckEnabled
#resource "\\" + def_BtnCheckDisabled
#resource "\\" + def_BtnDayTrade
#resource "\\" + def_BtnSwing
#resource "\\" + def_BtnInfoBuy
#resource "\\" + def_BtnInfoSell

Il nous suffit ensuite d’implémenter deux nouveaux objets dans le système d’ordres.

//+------------------------------------------------------------------+
enum eIndicatorTrade {IT_NULL, IT_STOP= 65, IT_TAKE, IT_PENDING, IT_RESULT};
enum eEventType {EV_NULL, EV_GROUND = 65, EV_LINE, EV_CLOSE, EV_EDIT, EV_PROFIT, EV_MOVE, EV_CHECK, EV_TYPE, EV_DS};
//+------------------------------------------------------------------+
C_Object_BackGround     m_BackGround;
C_Object_TradeLine      m_TradeLine;
C_Object_BtnBitMap      m_BtnClose,
                        m_BtnCheck,
                        m_BtnInfoType,
                        m_BtnInfo_DS;
C_Object_Edit           m_EditInfo1,
                        m_EditInfo2;
C_Object_Label          m_BtnMove;

Chaque fois que nous allons ajouter un nouvel objet, nous devrons également ajouter un ÉVÉNEMENT lié à l'objet, ce qui garantira que l'objet a un nom unique.

Vient maintenant la partie la plus intéressante de la programmation. La première chose que nous devons faire est de nous occuper des fantômes. Nous devons les mettre à jour afin qu'ils conservent les informations qui s'y trouvent. Bien sûr, cela pourrait être supprimé, mais je pense qu'il vaut mieux conserver les données de base. Jetez un œil au code suivant :

#define macroSwapName(A, B) ObjectSetString(Terminal.Get_ID(), macroMountName(ticket, A, B), OBJPROP_NAME, macroMountName(def_IndicatorGhost, A, B));
                void CreateGhostIndicator(ulong ticket, eIndicatorTrade it)
                        {
                                if (GetInfosTradeServer(m_Selection.ticket = ticket) != 0)
                                {
                                        ChartSetInteger(Terminal.Get_ID(), CHART_EVENT_OBJECT_DELETE, false);
                                        macroSwapName(it, EV_LINE);
                                        macroSwapName(it, EV_GROUND);
                                        macroSwapName(it, EV_MOVE);
                                        macroSwapName(it, EV_EDIT);
                                        macroSwapName(it, EV_CLOSE);
                                        if (it == IT_PENDING)
                                        {
                                                macroSwapName(it, EV_CHECK);
                                                macroSwapName(it, EV_TYPE);
                                                macroSwapName(it, EV_DS);
                                        }
                                        m_TradeLine.SetColor(macroMountName(def_IndicatorGhost, it, EV_LINE), def_IndicatorGhostColor);
                                        m_BackGround.SetColor(macroMountName(def_IndicatorGhost, it, EV_GROUND), def_IndicatorGhostColor);
                                        m_BtnMove.SetColor(macroMountName(def_IndicatorGhost, it, EV_MOVE), def_IndicatorGhostColor);
                                        ObjectDelete(Terminal.Get_ID(), macroMountName(def_IndicatorGhost, it, EV_CLOSE));
                                        m_TradeLine.SpotLight();
                                        ChartSetInteger(Terminal.Get_ID(), CHART_EVENT_OBJECT_DELETE, true);
                                        m_Selection.it = it;
                                }else m_Selection.ticket = 0;
                        }
#undef macroSwapName

Les lignes surlignées font passer les objets au fantôme, c'est quelque chose de très simple et clair. Un autre code simple transforme les indicateurs en attente en flottant.

#define macroSwapAtFloat(A, B) ObjectSetString(Terminal.Get_ID(), macroMountName(ticket, A, B), OBJPROP_NAME, macroMountName(def_IndicatorFloat, A, B));
                bool PendingAtFloat(ulong ticket)
                        {
                                eIndicatorTrade it;
                                
                                if (macroGetLinePrice(def_IndicatorFloat, IT_PENDING) > 0) return false;
                                macroSwapAtFloat(IT_PENDING, EV_CHECK);
                                macroSwapAtFloat(IT_PENDING, EV_TYPE);
                                macroSwapAtFloat(IT_PENDING, EV_DS);
                                for (char c0 = 0; c0 < 3; c0++)
                                {
                                        switch(c0)
                                        {
                                                case 0: it = IT_PENDING;        break;
                                                case 1: it = IT_STOP;           break;
                                                case 2: it = IT_TAKE;           break;
                                                default:
                                                        return false;
                                        }
                                        macroSwapAtFloat(it, EV_CLOSE);
                                        macroSwapAtFloat(it, EV_MOVE);
                                        macroSwapAtFloat(it, EV_EDIT);
                                        macroSwapAtFloat(it, EV_GROUND);
                                        macroSwapAtFloat(it, EV_LINE);
                                        m_EditInfo1.SetOnlyRead(macroMountName(def_IndicatorFloat, IT_PENDING, EV_EDIT), false);
                                }
                                return true;
                        }
#undef macroSwapAtFloat

Les lignes en surbrillance convertissent les objets en indicateur flottant, nous permettant d'effectuer les actions requises ultérieurement. Nous devons maintenant implémenter quelques modifications dans le code qui crée l'indicateur. C'est quelque chose que vous testerez et ajusterez jusqu'à ce que vous l'aimiez. Fondamentalement, les modifications ont été apportées aux points mis en évidence dans le code ci-dessous :

#define macroCreateIndicator(A, B, C, D)        {                                                                               \
                m_TradeLine.Create(ticket, sz0 = macroMountName(ticket, A, EV_LINE), C);                                        \
                m_BackGround.Create(ticket, sz0 = macroMountName(ticket, A, EV_GROUND), B);                                     \
                m_BackGround.Size(sz0, (A == IT_RESULT ? 100 : (A == IT_PENDING ? 144 : 92)), (A == IT_RESULT ? 34 : 22));      \
                m_EditInfo1.Create(ticket, sz0 = macroMountName(ticket, A, EV_EDIT), D, 0.0);                                   \
                m_EditInfo1.Size(sz0, 60, 14);                                                                                  \
                if (A != IT_RESULT)     {                                                                                       \
                        m_BtnMove.Create(ticket, sz0 = macroMountName(ticket, A, EV_MOVE), "Wingdings", "u", 17, C);            \
                        m_BtnMove.Size(sz0, 21, 23);                                                                            \
                                        }else                   {                                                               \
                        m_EditInfo2.Create(ticket, sz0 = macroMountName(ticket, A, EV_PROFIT), clrNONE, 0.0);                   \
                        m_EditInfo2.Size(sz0, 60, 14);  }                                                                       \
                                                }
                                                                                                                
#define macroInfoBase(A)        {                                                                                               \
                m_BtnInfoType.Create(ticket, sz0 = macroMountName(ticket, A, EV_TYPE), def_BtnInfoBuy, def_BtnInfoSell);        \
                m_BtnInfoType.SetStateButton(sz0, m_Selection.bIsBuy);                                                          \
                m_BtnInfo_DS.Create(ticket, sz0 = macroMountName(ticket, A, EV_DS), def_BtnDayTrade, def_BtnSwing);             \
                m_BtnInfo_DS.SetStateButton(sz0, m_Selection.bIsDayTrade);                                                      \
                                }

                void CreateIndicator(ulong ticket, eIndicatorTrade it)
                        {
                                string sz0;
                                
                                switch (it)
                                {
                                        case IT_TAKE    : macroCreateIndicator(it, clrForestGreen, clrDarkGreen, clrNONE); break;
                                        case IT_STOP    : macroCreateIndicator(it, clrFireBrick, clrMaroon, clrNONE); break;
                                        case IT_PENDING:
                                                macroCreateIndicator(it, clrCornflowerBlue, clrDarkGoldenrod, def_ColorVolumeEdit);
                                                m_BtnCheck.Create(ticket, sz0 = macroMountName(ticket, it, EV_CHECK), def_BtnCheckEnabled, def_BtnCheckDisabled);
                                                m_BtnCheck.SetStateButton(sz0, true);
                                                macroInfoBase(IT_PENDING);
                                                break;
                                        case IT_RESULT  :
                                                macroCreateIndicator(it, clrSlateBlue, clrSlateBlue, def_ColorVolumeResult);
                                                macroInfoBase(IT_RESULT);
                                                break;
                                }
                                m_BtnClose.Create(ticket, macroMountName(ticket, it, EV_CLOSE), def_BtnClose);
                        }
#undef macroInfoBase
#undef macroCreateIndicator

Notez que macroInfoBase crée les objets qui sont utilisés dans l'indicateur, mais ces objets seront créés uniquement dans les indicateurs d'ouverture de position et de résultat de position, alors qu'il n'est pas nécessaire de créer ces objets dans d'autres indicateurs. Mais notez que nous ne positionnons pas les objets à l'endroit où nous les avons créés. Cela se fait à un autre endroit, illustré ci-dessous.

#define macroSetAxleY(A)                {                                                                               \
                m_BackGround.PositionAxleY(macroMountName(ticket, A, EV_GROUND), y);                                    \
                m_TradeLine.PositionAxleY(macroMountName(ticket, A, EV_LINE), y);                                       \
                m_BtnClose.PositionAxleY(macroMountName(ticket, A, EV_CLOSE), y);                                       \
                if (A != IT_RESULT)m_BtnMove.PositionAxleY(macroMountName(ticket, A, EV_MOVE), y, 1);                   \
                else m_EditInfo2.PositionAxleY(macroMountName(ticket, A, EV_PROFIT), y, 1);                             \
                m_EditInfo1.PositionAxleY(macroMountName(ticket, A, EV_EDIT), y, (A == IT_RESULT ? -1 : 0));            \
                if (A == IT_PENDING) m_BtnCheck.PositionAxleY(macroMountName(ticket, A, EV_CHECK), y);                  \
                if ((A == IT_PENDING) || (A == IT_RESULT))      {                                                       \
                        m_BtnInfoType.PositionAxleY(macroMountName(ticket, A, EV_TYPE), y + (A == IT_PENDING ? 0 : 8)); \
                        m_BtnInfo_DS.PositionAxleY(macroMountName(ticket, A, EV_DS), y - (A == IT_PENDING ? 0: 8));     \
                                                                }                                                       \
                                        }
                                                                        
#define macroSetAxleX(A, B)             {                                                                                               \
                m_BackGround.PositionAxleX(macroMountName(ticket, A, EV_GROUND), B);                                                    \
                m_TradeLine.PositionAxleX(macroMountName(ticket, A, EV_LINE), B);                                                       \
                m_BtnClose.PositionAxleX(macroMountName(ticket, A, EV_CLOSE), B + 3);                                                   \
                m_EditInfo1.PositionAxleX(macroMountName(ticket, A, EV_EDIT), B + 21);                                                  \
                if (A != IT_RESULT) m_BtnMove.PositionAxleX(macroMountName(ticket, A, EV_MOVE), B + 80 + (A == IT_PENDING ? 52 : 0));   \
                else m_EditInfo2.PositionAxleX(macroMountName(ticket, A, EV_PROFIT), B + 21);                                           \
                if (A == IT_PENDING) m_BtnCheck.PositionAxleX(macroMountName(ticket, A, EV_CHECK), B + 82);                             \
                if ((A == IT_PENDING) || (A == IT_RESULT))      {                                                                       \
                        m_BtnInfoType.PositionAxleX(macroMountName(ticket, A, EV_TYPE), B + (A == IT_PENDING ? 100 : 82));              \
                        m_BtnInfo_DS.PositionAxleX(macroMountName(ticket, A, EV_DS), B + (A == IT_PENDING ? 118 : 82));                 \
                                                                }                                                                       \
                                        }
//---
        void ReDrawAllsIndicator(void)
                        {
                                C_IndicatorTradeView::st00 Local;
                                int             max = ObjectsTotal(Terminal.Get_ID(), -1, OBJ_EDIT);
                                ulong           ticket;
                                eIndicatorTrade it;
                                eEventType ev;
                                
                                Local = m_Selection;
                                m_Selection.ticket = 0;
                                for (int c0 = 0; c0 <= max; c0++)
                                   if (GetIndicatorInfos(ObjectName(Terminal.Get_ID(), c0, -1, OBJ_EDIT), ticket, it, ev))
                                      if ((it == IT_PENDING) || (it == IT_RESULT))
                                      {
                                        PositionAxlePrice(ticket, IT_STOP, macroGetLinePrice(ticket, IT_STOP));
                                        PositionAxlePrice(ticket, IT_TAKE, macroGetLinePrice(ticket, IT_TAKE));
                                        PositionAxlePrice(ticket, it, macroGetLinePrice(ticket, it));
                                        }
                                m_Selection = Local;
                                ChartRedraw();
                        }
//---
inline void PositionAxlePrice(ulong ticket, eIndicatorTrade it, double price)
                        {
                                int x, y, desl;
                                
                                ChartTimePriceToXY(Terminal.Get_ID(), 0, 0, price, x, y);
                                macroSetLinePrice(ticket, it, 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));
                        }
#undef macroSetAxleX
#undef macroSetAxleY

Je voudrais également souligner que je n'aime pas apporter de changements drastiques et radicaux au code. Fondamentalement, les seuls changements ont été mis en évidence dans le code ci-dessus.


2.0.2. Problèmes en vue

Même si tout fonctionne parfaitement, nous avons un problème. J'ai cherché dans toute la documentation MQL5, mais je n'ai trouvé aucun moyen de résoudre le problème de manière simple. Le problème est de savoir si une position récemment ouverte est un Day Trade (transactions courtes dans la même journée) ou un Swing Trade (transactions plus longues). Dans le cas d'une position plus ancienne, ouverte la veille, il est assez simple de faire ce type d'analyse, car il suffirait de comparer le jour en cours et le jour d'ouverture de la position : s'ils sont différents, la position est une Swing. Mais que se passe-t-il si l’EA est fermé et que vous l’initiez le jour même de l’ouverture de la position ? Dans ce cas, il n'y a aucun moyen de savoir si une position est un Day Trade ou un Swing Trade.

Ce problème n'existe pas pour les ordres en attente puisqu'il existe un moyen de le vérifier. Lors de l'appel à OrderGetInteger avec le paramètre ORDER_TYPE_TIME, une valeur de l’énumérationENUM_ORDER_TYPE_TIME est renvoyée, qui indique si un ordre est un Day Trade ou un Swing Trade. Mais il n’en va pas de même pour les positions.

Pour cette raison, ma solution est d'ajouter quelque chose à l’ordre ou à la position pour informer l'EA de la durée de l'opération, indépendamment de toute autre information. Mais ce n’est pas une solution parfaite, car elle résout le problème dans plusieurs cas, mais pas dans tous. Parce que le trader peut modifier le système utilisé par l'EA pour identifier si une transaction est un Swing ou un Day Trade avant la période requise pour l'analyse.

Pour mieux le comprendre, voyons comment la solution est implémentée.

inline char GetInfosTradeServer(ulong ticket)
{
        long info;
                                
        if (ticket == 0) return 0;
        if (OrderSelect(ticket))
        {
                if (OrderGetString(ORDER_SYMBOL) != Terminal.GetSymbol()) return 0;
                info = OrderGetInteger(ORDER_TYPE);
                m_Selection.bIsBuy = ((info == ORDER_TYPE_BUY_LIMIT) || (info == ORDER_TYPE_BUY_STOP) || (info == ORDER_TYPE_BUY_STOP_LIMIT) || (info == ORDER_TYPE_BUY));
                m_Selection.pr = OrderGetDouble(ORDER_PRICE_OPEN);
                m_Selection.tp = OrderGetDouble(ORDER_TP);
                m_Selection.sl = OrderGetDouble(ORDER_SL);
                m_Selection.vol = OrderGetDouble(ORDER_VOLUME_CURRENT);
                m_Selection.bIsDayTrade = ((ENUM_ORDER_TYPE_TIME)OrderGetInteger(ORDER_TYPE_TIME) == ORDER_TIME_DAY);
                                        
                return -1;
        }
        if (PositionSelectByTicket(ticket))
        {
                if (PositionGetString(POSITION_SYMBOL) != Terminal.GetSymbol()) return 0;
                m_Selection.bIsBuy = PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_BUY;
                m_Selection.pr = PositionGetDouble(POSITION_PRICE_OPEN);
                m_Selection.tp = PositionGetDouble(POSITION_TP);
                m_Selection.sl = PositionGetDouble(POSITION_SL);
                m_Selection.vol = PositionGetDouble(POSITION_VOLUME);
                if (macroGetDate(PositionGetInteger(POSITION_TIME)) == macroGetDate(TimeTradeServer()))
                        m_Selection.bIsDayTrade = PositionGetString(POSITION_COMMENT) == def_COMMENT_TO_DAYTRADE;
                else m_Selection.bIsDayTrade = false;
                                        
                return 1;
        }
        return 0;
}

Comme mentionné ci-dessus, dans le cas d’un ordre en attente, il suffit d'appeler OrderGetInteger et d'obtenir la valeur dont nous avons besoin. C'est un peu plus compliqué avec les positions. Cela fonctionne comme suit : vérifiez le jour d'ouverture de la position et le jour en cours du serveur de trading. Si les deux sont identiques, vérifiez le commentaire dans l’ordre. Si le commentaire indique la chaîne utilisée dans la classe C_Router pour indiquer que si la position est ouverte, ce sera du Day Trade, l'EA l'interprétera et l'affichera dans l'indicateur de position. Mais le commentaire ne doit pas changer avant la fin de la journée, car s'il change, alors l'EA peut signaler que la position Day Trade est en fait un Swing Trade, auquel cas ce n'est pas la faute de l'EA, mais parce que le trader a changé le commentaire trop tôt.

C'est l'inconvénient de cette solution, mais si quelqu'un a une idée sur la façon de déterminer si une position est un Day Trade ou non simplement en examinant les données de position, veuillez la partager dans les commentaires.

La vidéo ci-dessous montre à quoi cela ressemble dans le cas des ordres en attente :


Maintenant que nous avons presque tout prêt, il ne nous reste plus qu'à faire quelques ajouts supplémentaires au code pour que l'EA devienne intéressant à utiliser.


2.0.3. Répondre aux messages de la plateforme

L'ensemble de notre système d’ordres est basé sur les messages envoyés par MetaTrader 5 afin que l'EA puisse savoir ce qui doit ou ne doit pas être fait. C'est pourquoi il est si important de savoir comment mettre en œuvre le système de messagerie.

Le code complet associé à la messagerie est affiché ci-dessous :

#define macroGetDataIndicatorFloat      {                                                                                                               \
                m_Selection.vol = m_EditInfo1.GetTextValue(macroMountName(def_IndicatorFloat, IT_PENDING, EV_EDIT)) * Terminal.GetVolumeMinimal();      \
                m_Selection.bIsBuy = m_BtnInfoType.GetStateButton(macroMountName(def_IndicatorFloat, IT_PENDING, EV_TYPE));                             \
                m_Selection.pr = macroGetLinePrice(def_IndicatorFloat, IT_PENDING);                                                                     \
                m_Selection.sl = macroGetLinePrice(def_IndicatorFloat, IT_STOP);                                                                        \
                m_Selection.tp = macroGetLinePrice(def_IndicatorFloat, IT_TAKE);                                                                        \
                m_Selection.bIsDayTrade = m_BtnInfo_DS.GetStateButton(macroMountName(def_IndicatorFloat, IT_PENDING, EV_DS));                           \
                                        }
                                                                                                
                void DispatchMessage(int id, long lparam, double dparam, string sparam)
                        {
                                ulong   ticket;
                                double  price;
                                bool   	bKeyBuy,
                                        bKeySell,
                                        bEClick;
                                datetime dt;
                                uint     mKeys;
                                char     cRet;
                                eIndicatorTrade  it;
                                eEventType       ev;
                                
                                static bool bMounting = false;
                                static double valueTp = 0, valueSl = 0, memLocal = 0;
                                
                                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 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 = m_Selection.pr + (bKeyBuy ? valueTp : (-valueTp));
                                                        m_Selection.sl = 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(price); else MoveSelection(price);
                                                }
                                                break;
                                        case CHARTEVENT_OBJECT_DELETE:
                                                if (GetIndicatorInfos(sparam, ticket, it, ev))
                                                {
                                                        if (GetInfosTradeServer(ticket) == 0) break;
                                                        CreateIndicator(ticket, it);
                                                        if ((it == IT_PENDING) || (it == IT_RESULT))
                                                                PositionAxlePrice(ticket, it, m_Selection.pr);
                                                        ChartRedraw();
                                                        m_TradeLine.SpotLight();
                                                        m_Selection.ticket = 0;
                                                        UpdateIndicators(ticket, m_Selection.tp, m_Selection.sl, m_Selection.vol, m_Selection.bIsBuy);
                                                }
                                                break;
                                        case CHARTEVENT_OBJECT_ENDEDIT:
                                                macroGetDataIndicatorFloat;
                                                m_Selection.ticket = 0;
                                                UpdateIndicators(def_IndicatorFloat, m_Selection.tp, m_Selection.sl, m_Selection.vol, m_Selection.bIsBuy);
                                                break;
                                        case CHARTEVENT_CHART_CHANGE:
                                                ReDrawAllsIndicator();
                                                break;
                                        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) RemoveIndicator(def_IndicatorFloat, it);
                                                                else if ((cRet = GetInfosTradeServer(ticket)) != 0) switch (it)
                                                                {
                                                                        case IT_PENDING:
                                                                        case IT_RESULT:
                                                                                if (cRet < 0) RemoveOrderPendent(ticket); else ClosePosition(ticket);
                                                                                break;
                                                                        case IT_TAKE:
                                                                        case IT_STOP:
                                                                                m_Selection.ticket = ticket;
                                                                                m_Selection.it = it;
                                                                                SetPriceSelection(0);
                                                                                break;
                                                                }
                                                                break;
                                                        case EV_MOVE:
                                                                        if (ticket == def_IndicatorFloat)
                                                                        {
                                                                                macroGetDataIndicatorFloat;
                                                                                m_Selection.ticket = ticket;
                                                                                m_Selection.it = it;
                                                                        }else   CreateGhostIndicator(ticket, it);
                                                                break;
                                                        case EV_CHECK:
                                                                if (ticket != def_IndicatorFloat)
                                                                {
                                                                        if (PendingAtFloat(ticket)) RemoveOrderPendent(ticket);
                                                                        else m_BtnCheck.SetStateButton(macroMountName(ticket, IT_PENDING, EV_CHECK), true);
                                                                } else
                                                                {
                                                                        macroGetDataIndicatorFloat;
                                                                        m_Selection.ticket = def_IndicatorTicket0;
                                                                        m_Selection.it = IT_PENDING;
                                                                        SetPriceSelection(m_Selection.pr);
                                                                        RemoveIndicator(def_IndicatorFloat);
                                                                }
                                                                break;
                                                }
                                                break;
                                }
                        }
#undef macroGetDataIndicatorFloat

N'ayez pas peur de ce code. Même s’il semble grand et compliqué, c’est en réalité assez simple. Je me concentrerai sur les parties mises en évidence pour expliquer les nouveautés du code de traitement des messages.

La première nouveauté réside dans le code de gestion des événements CHARTEVENT_OBJECT_ENDEDIT. Il est déclenché par MetaTrader 5 chaque fois que nous finissons de modifier le contenu présent dans l'objet EDIT. Qu'est-ce que ça veut dire ? C'est très important, car si nous ne traitons pas cet événement et essayons de manipuler les données des indicateurs de niveau d'arrêt après avoir modifié la valeur de levier, nous aurons alors une inadéquation dans les valeurs. Bien que l'EA force la valeur à revenir à sa valeur d'origine, si nous traitons cet événement comme indiqué dans le code, ce problème n'existera pas et nous pourrons trader en douceur avec les données de levier. Gardez à l’esprit que lorsque vous demanderez à l’EA de vous autoriser à effectuer ces ajustements, vous souhaiterez en fait vérifier si c’est une bonne idée ou non d’entrer dans l’opération avec plus ou moins d’effet de levier. De cette façon, vous pouvez vérifier sans prendre aucun risque, car l'EA enverra l’ordre au serveur uniquement lorsque vous lui demanderez de le faire, et le moment où cela se produit est lorsque la case à cocher est activée.

Examinons maintenant de plus près l’évènement CHARTEVENT_OBJECT_CLICK. Pour cela, prenons le fragment mis en évidence dans le code précédent.

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;

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

Que fait réellement ce code ? Avez-vous une idée ? Eh bien, les vidéos de cet article le démontrent, mais pouvez-vous comprendre comment ce genre de chose se fait ? Beaucoup imagineraient qu’il s’agissait d’un code extrêmement complexe, mais il est là, juste au-dessus.

Nous devons faire deux choses. La première est que lorsqu'on clique sur un objet BitMap, son statut change et nous devons vérifier si son ticket provient de quelque chose qui existe déjà sur le serveur ou s'il s'agit de quelque chose qui se trouve uniquement sur le graphique. Cela se fait par les points surlignés en vert. Si le ticket existe sur le serveur, le changement de statut doit être annulé, alors l'EA le corrigera en effectuant la modification requise.

Jetez maintenant un œil à la section surlignée en jaune. L'idée est basée sur les éléments suivants : Pourquoi devrais-je passer un autre ordre sur le graphique, s'il existe déjà sur le graphique, et que je souhaite simplement inverser la direction ? En d’autres termes, si c’était Acheter, maintenant je veux que ce soit Vendre, et vice versa. C'est exactement ce que fait le fragment jaune : lorsque l'on clique sur le BitMap responsable de l'achat ou de la vente, la direction change automatiquement. Un détail : cela ne peut se faire que pour un ordre flottant ; c'est interdit pour les ordres déjà sur le serveur.

Avec tous ces changements, les indicateurs ressemblent désormais à ceci :

Types d’ordres en attente

 

Indicateur de position :

Il est désormais beaucoup plus facile de déterminer ce que fait un ordre en attente ou une position ouverte, car vous pouvez connaître exactement le mouvement attendu ou la durée de vie de la position. La flèche verte pointant vers le haut indique une position d'achat ; une flèche rouge pointant vers le bas correspond à une position de vente. La lettre D indique un Day Trade, qui sera clôturé à la fin de la journée. Si c'est S, alors il s'agit d'un Swing Trade, et l'opération ne sera pas forcément clôturée en fin de journée.

La vidéo suivante montre comment fonctionne le nouveau système d’ordres. Je me suis concentré sur les ordres en attente car ils sont sujets à d'autres modifications alors que les indicateurs de position ne peuvent pas être modifiés. Ils afficheront uniquement les données fournies par le serveur concernant la position. Regardez de plus près comment tout cela fonctionne avant de l'essayer sur un compte réel, car le système est pratique, mais il faut se familiariser avec lui pour tirer le meilleur parti de ses fonctionnalités.




Conclusion

Eh bien, notre système d’ordres est désormais assez polyvalent. Il peut faire plusieurs choses et nous aide beaucoup, mais il manque encore un détail important qui sera implémenté dans le prochain article. Alors à bientôt...


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

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)
Il reste une tâche pour laquelle notre système d’ordres n'est pas à la hauteur, mais nous allons ENFIN la résoudre. 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.
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.
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.
Apprendre à concevoir un système de trading basé sur les Fractales Apprendre à concevoir un système de trading basé sur les Fractales
Voici un nouvel article de notre série sur la façon de concevoir un système de trading basé sur les indicateurs techniques les plus populaires. Nous apprendrons un nouvel indicateur, l'indicateur Fractals, et nous apprendrons comment concevoir un système de trading basé sur celui-ci pour être exécuté dans le terminal MetaTrader 5.