Plusieurs indicateurs sur un seul graphique (Partie 05) : Transformer MetaTrader 5 en un système RAD (I)

Daniel Jose | 26 juillet, 2022

Introduction

Beaucoup de personnes ne savent pas programmer mais sont assez créatives et ont de grandes idées. Mais le manque de connaissances en programmation les empêche de mettre en œuvre ces idées. Aujourd'hui, nous allons créer notre propre interface Chart Trade pour envoyer des ordres au marché, et pour configurer les paramètres des ordres en attente. Nous le ferons sans programmation, en utilisant simplement les fonctions inclues dans l'Expert Advisor. Voyons ensemble ce que cela donne sur nos écrans :


Vous pourriez me dire : "Mais comment faites-vous ? Je ne connais rien à la programmation, ou ce que je sais ne sera pas suffisant pour le faire." Le graphique Trade que vous voyez dans l'image ci-dessus a été créé dans la plateforme MetaTrader 5 et a été conçu comme indiqué dans l'image ci-dessous :


Maintenant que nous connaissons le sujet de cet article, nous pouvons avoir plein d'idées pour créer notre propre tableau. Quelques étapes doivent tout de même être réalisées pour que tout fonctionne. Une fois le code mis en place, notre créativité sera la seule limite pour la conception de notre propre IDE Chart Trade. Cet article est la suite des précédents. Pour une compréhension complète, je vous recommande donc de lire les articles précédents de cette série.

C’est parti !


Planification

Pour commencer, vous devez éditer les propriétés du graphique que vous utiliserez comme IDE. Ceci permet de réduire les effets secondaires potentiels. L'idée est que partir d’un graphique propre permet de construire plus facilement l'interface de Chart Trade. Ouvrez les propriétés du graphique et définissez les paramètres comme indiqué dans la figure ci-dessous.

     

L'écran sera ainsi absolument propre et libre de tout ce qui peut interférer avec le développement de notre IDE. Un point important : notre IDE sera enregistré comme un fichier de paramètres, c'est-à-dire comme un TEMPLATE. Nous pouvons donc utiliser tous les objets fournis par MetaTrader 5, mais pour que ce soit plus pratique, nous n'en utiliserons que quelques objets. Pour connaître tous les objets disponibles, veuillez consulter la section Types d'objets dans MetaTrader 5.

Objet Type de coordonnées utilisées pour le positionnement Utile pour l'IDE 
Texte Date et prix  NON
Étiquette Localisation en X et Y  OUI
Bouton  Localisation en X et Y  OUI
Graphique  Localisation en X et Y  OUI
Bitmap  Date et prix  NON
Étiquette bitmap  Localisation en X et Y  OUI
Champ d’édition  Localisation en X et Y  OUI
Événement  Seule la date est utilisée  NON
Étiquette rectangle Localisation en X et Y  OUI

Nous allons utiliser un système qui peut être placé dans n'importe quelle zone de l'écran. C'est pourquoi il ne serait pas pratique d'utiliser un objet qui n'utilise pas le système de coordonnées X et Y pour le positionnement. Ce type d’objet pourrait donner à l'IDE un aspect complètement différent. Nous limiterons donc le système à six objets, ce qui est plus que suffisant pour créer une interface.

L'idée est de disposer les objets dans un ordre logique, comme si vous les dessiniez à l'écran. Nous commençons par créer l'arrière-plan. Puis nous superposons les objets, en les plaçant et en les ajustant au fur et à mesure que nous développons l'interface. Voici comment cela se passe :

    

    

Tout est très simple. Il faut juste un peu de pratique pour maîtriser cette façon de concevoir et de créer sa propre interface. L'idée ici est très similaire à celle utilisée dans les programmes de type RAD qui sont utilisés pour créer des interfaces de programmation dans les cas où le développement de l'interface utilisateur par le code peut être très complexe. Nous pourrions créer une interface directement par le code. Mais l'utilisation de cette méthode rend les modifications à venir par la suite beaucoup plus rapides et faciles, ce qui est idéal pour ceux qui veulent une interface avec leur propre style.

Une fois que nous aurons terminé, nous pourrions nous retrouver avec une interface comme celle présentée ci-dessous. J'ai essayé ici d'utiliser autant d'objets que possible pour que vous puissiez les essayer à votre tour. Vous pouvez créer votre propre interface.

C'est la première étape de la création de notre IDE. Il faut maintenant créer un code qui prenne réellement en charge cette interface et qui la rende fonctionnelle. Le simple fait de pouvoir créer sa propre interface doit être une source de motivation. C’est ce que nous allons montrer dans le code.

L'étape suivante consiste à enregistrer cette interface sous la forme d’un fichier de paramètres. Nous pouvons maintenant l'enregistrer et utiliser le code de la version précédente pour l'afficher sous la forme d’un pointeur. Cela signifie que nous n'aurons pas besoin d'apporter des modifications importantes au code source. Mais si nous voulions tester la possibilité de recevoir des événements, ou d'envoyer des événements, à notre IDE, nous verrions que ce n'est pas possible. Si l'interface a été créée en utilisant des objets de MetaTrader 5, pourquoi n'est-il pas possible d'envoyer et de recevoir des événements à partir de ces objets ? La réponse à cette question est plus facile à montrer qu'à expliquer. Nous pouvons le vérifier en ajoutant le code suivant à la version originale de l'EA.

void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam)
{
        switch (id)
        {
                case CHARTEVENT_OBJECT_CLICK:
                        Print(sparam);
                        break;
// .... The rest of the code...
        }
}

Ce code récupère le nom de l'objet qui reçoit le clic et qui génère l'événement. Ici, l'événement est de type CHARTEVENT_OBJECT_CLICK. Mais le message affiché sera le nom de l'objet créé par l'EA, et non le nom de l’objet dans l'IDE. Cela pourrait sembler être un gros problème, rendant impossible l'utilisation de notre IDE. Mais il existe une solution très simple : lire le fichier de paramètres et créer ensuite les objets comme spécifiés dans ce fichier. Cela créera notre IDE directement sur le graphique. Ainsi, en analysant le fichier des paramètres (TPL), nous pouvons trouver les données que nous devons utiliser.

PARAMETRE Description
<chart> Début du fichier des paramètres
</chart> Fin du fichier des paramètres
<window> Début de la liste des éléments présents sur le graphique.
</window> Fin de la liste des éléments présents sur le graphique
<indicator> Début de la structure des données relatives à un certain indicateur.
</indicator> Fin de la structure des données relatives à un indicateur donné.
<object> Début de la structure des données d’un objet.
</object> Fin de la structure des données de l'objet.

Cette structure se présente de la façon suivante dans le fichier TPL.

<chart>

.... DATA

<window>

... DATA

<indicator>

... DATA

</indicator>

<object>

... DATA

</object>

</window>
</chart>

La partie qui nous intéresse se situe entre les balises <objet> et </objet>. Il peut y avoir plusieurs structures de ce type, chacune indiquant un objet unique. Nous devons donc d'abord modifier l'emplacement du fichier pour l'ajouter à un endroit d’où il peut être lu. Il s'agit du répertoire FILES. Vous pouvez changer l'emplacement. Mais dans tous les cas, le fichier doit se trouver dans l'arborescence FILE.

Un détail important : bien que l’on ait modifié le système pour pouvoir effacer le tableau lors de l'utilisation du fichier de configuration de l'IDE, l'idéal serait d'avoir également un fichier propre portant le même nom dans le répertoire Profiles\Templates. Cela minimise les restes qui peuvent être présents dans le modèle par défaut, comme nous l'avons vu dans les articles précédents. Les principaux changements sont mis en évidence ci-dessous :

#include <Auxiliar\Chart IDE\C_Chart_IDE.mqh>
//+------------------------------------------------------------------+
class C_TemplateChart : public C_Chart_IDE
{

 .... Other parts from code ....

//+------------------------------------------------------------------+
void AddTemplate(const eTypeChart type, const string szTemplate, int scale, int iSize)
{
        if (m_Counter >= def_MaxTemplates) return;
        if (type == SYMBOL) SymbolSelect(szTemplate, true);
        SetBase(szTemplate, (type == INDICATOR ? _Symbol : szTemplate), scale, iSize);
        if (!ChartApplyTemplate(m_handle, szTemplate + ".tpl")) if (type == SYMBOL) ChartApplyTemplate(m_handle, "Default.tpl");
        if (szTemplate == "IDE") C_Chart_IDE::Create(m_IdSubWin);
        ChartRedraw(m_handle);
}
//+------------------------------------------------------------------+
void Resize(void)
{
#define macro_SetInteger(A, B) ObjectSetInteger(Terminal.Get_ID(), m_Info[c0].szObjName, A, B)
        int x0 = 0, x1, y = (int)(ChartGetInteger(Terminal.Get_ID(), CHART_HEIGHT_IN_PIXELS, m_IdSubWin));
        x1 = (int)((ChartGetInteger(Terminal.Get_ID(), CHART_WIDTH_IN_PIXELS, m_IdSubWin) - m_Aggregate) / (m_Counter > 0 ? (m_CPre == m_Counter ? m_Counter : (m_Counter - m_CPre)) : 1));
        for (char c0 = 0; c0 < m_Counter; x0 += (m_Info[c0].width > 0 ? m_Info[c0].width : x1), c0++)
        {
                macro_SetInteger(OBJPROP_XDISTANCE, x0);
                macro_SetInteger(OBJPROP_XSIZE, (m_Info[c0].width > 0 ? m_Info[c0].width : x1));
                macro_SetInteger(OBJPROP_YSIZE, y);
                if (m_Info[c0].szTemplate == "IDE") C_Chart_IDE::Resize(x0);
        }
        ChartRedraw();
#undef macro_SetInteger
}
//+------------------------------------------------------------------+

... The rest of the code

}

Notez que nous ajoutons l'interface IDE sous la forme d’une nouvelle classe, et qu'elle est héritée par notre classe originale. Cela signifie que la fonctionnalité de la classe d'origine sera étendue et qu'elle ne provoquera pas d'effets secondaires dans le code d'origine.

C’était jusque là la partie la plus facile. Nous devons maintenant faire quelque chose de plus compliqué qui supportera notre IDE. Tout d'abord, créons un gestionnaire d’évènements qui sera utilisé par le système. Il permettra au système de fonctionner comme ci-dessous :


Nous pourrions également modifier les données du système, ce qui n'est actuellement pas possible, mais en ajoutant un gestionnaire d’évènements, il sera possible de rendre notre IDE fonctionnel. Définissons donc certaines choses :

Evènement Objectif
MSG_BUY_MARKET Envoie un ordre d'achat sur le marché
MSG_SELL_MARKET Envoi d'un ordre de VENTE sur le marché
MSG_LEVERAGE_VALUE Levier
MSG_TAKE_VALUE Take Profit de la position
MSG_STOP_VALUE Stop loss de la position
MSG_RESULT Résultat actuel de la position ouverte
MSG_DAY_TRADE Indique si la transaction sera fermée en fin de journée ou pas.

Ce protocole est une étape très importante. Après l'avoir défini, vous devez apporter des modifications dans le fichier de configuration. En ouvrant la liste des objets, vous devriez obtenir ceci :

L'interface que je montre contiendra une liste d'objets comme dans l'image. Faites également attention à ceci : le NOM des objets correspond à chacun des évènements/messages que nous allons utiliser. Le nom des autres objets n'a pas d'importance, car ils seront utilisés pour aider à la modélisation de l'IDE. Mais les objets portant les noms des évènements/messages recevront et enverront des messages. Si vous souhaitez utiliser davantage d’évènements ou de messages (voire un autre type de messages), il suffit d'apporter les modifications correspondantes au code de la classe. MetaTrader 5 fournira lui-même les moyens d'échanger des messages entre l'IDE et le code de l'EA.

Nous devons maintenant étudier le fichier TPL pour apprendre comment créer notre classe d'objets. Voyons maintenant comment les objets sont déclarés dans le fichier TPL. Nous aurons bien sûr moins accès aux propriétés des objets depuis le fichier TPL que par la programmation, puisque l'interface du terminal elle-même donne moins accès aux propriétés des objets. Mais l'accès que nous avons sera suffisant pour faire fonctionner notre IDE.

Ainsi, dans le fichier TPL, on trouve la structure dont nous avons besoin, entre les balises <objet> et </objet>. Depuis les données contenues dans la structure, il peut sembler difficile de savoir de quel type d'objet il s'agit. Mais le type d'objet est simplement déterminé par la variable type. Il prend une valeur différente pour chacun des types d’objets. Le tableau ci-dessous montre les objets que nous voulons utiliser :

Variable TYPE Type d’objet
102 OBJ_LABEL
103 OBJ_BUTTON
106 OBJ_BITMAP_LABEL
107  OBJ_EDIT
110  OBJ_RECTANGLE_LABEL

La classe commence déjà à prendre forme. Voici la première fonction :

bool Create(int nSub)
{
        m_CountObject = 0;
        if ((m_fp = FileOpen("Chart Trade\\IDE.tpl", FILE_BIN | FILE_READ)) == INVALID_HANDLE) return false;
        FileReadInteger(m_fp, SHORT_VALUE);
                                
        for (m_CountObject = eRESULT; m_CountObject <= eEDIT_STOP; m_CountObject++) m_ArrObject[m_CountObject].szName = "";
        m_SubWindow = nSub;
        m_szLine = "";
        while (m_szLine != "</chart>")
        {
                if (!FileReadLine()) return false;
                if (m_szLine == "<object>")
                {
                        if (!FileReadLine()) return false;
                        if (m_szLine == "type")
                        {
                                if (m_szValue == "102") if (!LoopCreating(OBJ_LABEL)) return false;
                                if (m_szValue == "103") if (!LoopCreating(OBJ_BUTTON)) return false;
                                if (m_szValue == "106") if (!LoopCreating(OBJ_BITMAP_LABEL)) return false;
                                if (m_szValue == "107") if (!LoopCreating(OBJ_EDIT)) return false;
                                if (m_szValue == "110") if (!LoopCreating(OBJ_RECTANGLE_LABEL)) return false;
                        }
                }
        }
        FileClose(m_fp);
        return true;
}

Veuillez noter que la première chose à faire est d'ouvrir le fichier en lecture, et comme un fichier binaire. Ceci permet de ne rien manquer. En utilisant l'éditeur HEXA, le fichier TPL se présente comme suit (notez qu'il commence par une valeur très intéressante) :

Cela vous semble confus ? En fait, ce n'est pas si difficile. Le fichier utilise l’encodage UTF-16. Nous savons que les données sont organisées par ligne. Créons donc une fonction pour lire la ligne entière en une seule fois. Pour cela, écrivons le code suivant :

bool FileReadLine(void)
{
        int utf_16 = 0;
        bool b0 = false;
        m_szLine = m_szValue = "";
        for (int c0 = 0; c0 < 500; c0++)
        {
                utf_16 = FileReadInteger(m_fp, SHORT_VALUE);
                if (utf_16 == 0x000D) { FileReadInteger(m_fp, SHORT_VALUE); return true; } else
                if (utf_16 == 0x003D) b0 = true; else
                if (b0) m_szValue = StringFormat("%s%c", m_szValue, (char)utf_16); else m_szLine = StringFormat("%s%c", m_szLine, (char)utf_16);
                if (FileIsEnding(m_fp)) break;
        }
        return (utf_16 == 0x003E);
}

La lecture doit être aussi efficace que possible. Donc lorsque le signe égal ( = ) est trouvé, nous le séparons pendant la lecture pour ne pas avoir à le faire plus tard. La boucle limite la chaîne de caractères à un maximum de 500 caractères. Mais cette valeur est arbitraire et peut être modifiée si nécessaire. Pour chaque nouvelle chaîne trouvée, la fonction renvoie son contenu afin que nous puissions procéder à son analyse.

Nous aurons besoin de certaines variables supplémentaires pour prendre en charge le protocole du message. Elles sont définies dans le code ci-dessous :

class C_Chart_IDE
{
        protected:
                enum eObjectsIDE {eRESULT, eBTN_BUY, eBTN_SELL, eCHECK_DAYTRADE, eBTN_CANCEL, eEDIT_LEVERAGE, eEDIT_TAKE, eEDIT_STOP};
//+------------------------------------------------------------------+
#define def_HeaderMSG "IDE_"
#define def_MaxObject eEDIT_STOP + 32
//+------------------------------------------------------------------+
        private :
                int             m_fp,
                                m_SubWindow,
                                m_CountObject;
                string          m_szLine,
                                m_szValue;
                bool            m_IsDayTrade;
                struct st0
                        {
                                string  szName;
                                int     iPosX;
                        }m_ArrObject[def_MaxObject];

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

La variable globale def_MaxObject indique le nombre maximum d'objets que l'on peut conserver. Ce nombre est obtenu en partant du nombre de messages plus un nombre supplémentaire d'objets que nous allons utiliser. Dans notre cas, nous avons un maximum de 40 objets, mais cela peut être modifié si nécessaire. Les 8 premiers objets seront utilisés pour envoyer des messages entre l'IDE et MetaTrader 5. L'alias de ces messages correspond à l’énumération eObjectsIDE. Il est important de garder cela à l'esprit au cas où vous voudriez étendre le système ou l'adapter pour autre chose.

Ce n'est que la première partie du système. Il y a également un autre point sur lequel il faut être vigilant : la constante qui traite du système de messages. La façon dont MQL5 traite les constantes peut être parfois un peu déroutante pour ceux qui programment en C/C++. En C/C++, une constante est déclarée dans la déclaration de la variable elle-même. En MQL5, la façon dont elle est créée peut rendre le code un peu plus compliqué. Il est toutefois facile de s’en accommoder car les constantes sont en fait utilisées assez rarement. Voici comment faire :

        public  :
                static const string szMsgIDE[];

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

};
//+------------------------------------------------------------------+
static const string C_Chart_IDE::szMsgIDE[] = {
                                                "MSG_RESULT",
                                                "MSG_BUY_MARKET",
                                                "MSG_SELL_MARKET",
                                                "MSG_DAY_TRADE",
                                                "MSG_CLOSE_POSITION",
                                                "MSG_LEVERAGE_VALUE",
                                                "MSG_TAKE_VALUE",
                                                "MSG_STOP_VALUE"
                                             };
//+------------------------------------------------------------------+

Les constantes définies sont exactement les mêmes que celles utilisées dans les noms d'objets de l'interface. Le système a été conçu pour ne pas être sensible à la casse (majuscules/minuscules). Vous pouvez modifier ce comportement si vous le souhaitez, mais ce n’est pas recommandé.

Nous pouvons maintenant passer à l’étape suivante après avoir accompli toutes ces étapes. Donc, retournons dans le fichier TPL. Regardez l’extrait de fichier ci-dessous :


Après avoir défini le type d'objet à utiliser, nous avons une série de données indiquant les propriétés de l'objet, telles que son nom, sa position, sa couleur, sa police, etc. Ces propriétés doivent être transmises aux objets internes. Puisque c'est une chose à répéter, nous pouvons créer une fonction générique pour cela. Elle sera comme suit :

bool LoopCreating(ENUM_OBJECT type)
{
#define macro_SetInteger(A, B) ObjectSetInteger(Terminal.Get_ID(), m_ArrObject[c0].szName, A, B)
#define macro_SetString(A, B) ObjectSetString(Terminal.Get_ID(), m_ArrObject[c0].szName, A, B)
        int c0;
        bool b0;
        string sz0 = m_szValue;
        while (m_szLine != "</object>") if (!FileReadLine()) return false; else
        {
                if (m_szLine == "name")
                {
                        b0 = false;
                        StringToUpper(m_szValue);
                        for(c0 = eRESULT; (c0 <= eEDIT_STOP) && (!(b0 = (m_szValue == szMsgIDE[c0]))); c0++);
                        c0 = (b0 ? c0 : m_CountObject);
                        m_ArrObject[c0].szName = StringFormat("%s%04s>%s", def_HeaderMSG, sz0, m_szValue);
                        ObjectDelete(Terminal.Get_ID(), m_ArrObject[c0].szName);
                        ObjectCreate(Terminal.Get_ID(), m_ArrObject[c0].szName, type, m_SubWindow, 0, 0);
                }
                if (m_szLine == "pos_x"                 ) m_ArrObject[c0].iPosX = (int) StringToInteger(m_szValue);
                if (m_szLine == "pos_y"                 ) macro_SetInteger(OBJPROP_YDISTANCE    , StringToInteger(m_szValue));
                if (m_szLine == "size_x"                ) macro_SetInteger(OBJPROP_XSIZE        , StringToInteger(m_szValue));
                if (m_szLine == "size_y"                ) macro_SetInteger(OBJPROP_YSIZE        , StringToInteger(m_szValue));
                if (m_szLine == "offset_x"              ) macro_SetInteger(OBJPROP_XOFFSET      , StringToInteger(m_szValue));
                if (m_szLine == "offset_y"              ) macro_SetInteger(OBJPROP_YOFFSET      , StringToInteger(m_szValue));
                if (m_szLine == "bgcolor"               ) macro_SetInteger(OBJPROP_BGCOLOR      , StringToInteger(m_szValue));
                if (m_szLine == "color"                 ) macro_SetInteger(OBJPROP_COLOR        , StringToInteger(m_szValue));
                if (m_szLine == "bmpfile_on"            ) ObjectSetString(Terminal.Get_ID()     , m_ArrObject[c0].szName, OBJPROP_BMPFILE, 0, m_szValue);
                if (m_szLine == "bmpfile_off"           ) ObjectSetString(Terminal.Get_ID()     , m_ArrObject[c0].szName, OBJPROP_BMPFILE, 1, m_szValue);
                if (m_szLine == "fontsz"                ) macro_SetInteger(OBJPROP_FONTSIZE     , StringToInteger(m_szValue));
                if (m_szLine == "fontnm"                ) macro_SetString(OBJPROP_FONT          , m_szValue);
                if (m_szLine == "descr"                 ) macro_SetString(OBJPROP_TEXT          , m_szValue);
                if (m_szLine == "readonly"              ) macro_SetInteger(OBJPROP_READONLY     , StringToInteger(m_szValue) == 1);
                if (m_szLine == "state"                 ) macro_SetInteger(OBJPROP_STATE        , StringToInteger(m_szValue) == 1);
                if (m_szLine == "border_type"           ) macro_SetInteger(OBJPROP_BORDER_TYPE  , StringToInteger(m_szValue));
        }
        m_CountObject += (b0 ? 0 : (m_CountObject < def_MaxObject ? 1 : 0));
        return true;
                        
#undef macro_SetString
#undef macro_SetInteger
}

Chaque objet aura un nom et sera stocké au bon emplacement. Mais la ligne en surbrillance montre quelque chose de différent. Lorsque nous créons un IDE, son origine est dans le coin supérieur gauche du graphique. Mais cette position X n'est pas nécessairement le coin supérieur gauche de la sous-fenêtre. Cette position doit correspondre au coin supérieur gauche de l'objet OBJ_CHART auquel l'IDE sera lié. Cet objet est indiqué lors du chargement du modèle de l’IDE. Il peut donc se trouver n'importe où dans la sous-fenêtre. L'IDE n'apparaîtra pas au bon endroit si cela n'est pas corrigé. Vous devez donc enregistrez la valeur X et l’utiliser ultérieurement pour afficher l'objet au bon endroit. La fonction qui affiche l'IDE correctement est présentée ci-dessous.

Les informations de base utilisées dans les objets sont déjà définies. Mais si vous devez ajouter une autre information, il suffit de l'ajouter à l'ensemble des commandes et de modifier la propriété avec la valeur appropriée.

void Resize(int x)
{
        for (int c0 = 0; c0 < m_CountObject; c0++)
                ObjectSetInteger(Terminal.Get_ID(), m_ArrObject[c0].szName, OBJPROP_XDISTANCE, x + m_ArrObject[c0].iPosX);
};

Avant de voir comment les messages sont traités, analysons deux autres fonctions importantes. Le système recevra des valeurs de l'EA pendant l'initialisation. Ces valeurs doivent être correctement représentées et ajustées afin que les ordres puissent être configurés directement dans Chart Trade. Ceci permettra d’envoyer un ordre au marché ou un ordre en attente, sans avoir besoin d'appeler l'EA. Les deux fonctions sont affichées ci-dessous :

void UpdateInfos(bool bSwap = false)
{
        int nContract, FinanceTake, FinanceStop;

        nContract       = (int) StringToInteger(ObjectGetString(Terminal.Get_ID(), m_ArrObject[eEDIT_LEVERAGE].szName, OBJPROP_TEXT));
        FinanceTake = (int) StringToInteger(ObjectGetString(Terminal.Get_ID(), m_ArrObject[eEDIT_TAKE].szName, OBJPROP_TEXT));
        FinanceStop = (int) StringToInteger(ObjectGetString(Terminal.Get_ID(), m_ArrObject[eEDIT_STOP].szName, OBJPROP_TEXT));
        m_IsDayTrade = (bSwap ? (m_IsDayTrade ? false : true) : m_IsDayTrade);
        ObjectSetInteger(Terminal.Get_ID(), m_ArrObject[eCHECK_DAYTRADE].szName, OBJPROP_STATE, m_IsDayTrade);
        NanoEA.Initilize(nContract, FinanceTake, FinanceStop, clrNONE, clrNONE, clrNONE, m_IsDayTrade);
}
//+------------------------------------------------------------------+
void InitilizeChartTrade(int nContracts, int FinanceTake, int FinanceStop, color cp, color ct, color cs, bool b1)
{
        NanoEA.Initilize(nContracts, FinanceTake, FinanceStop, cp, ct, cs, b1);
        if (m_CountObject < eEDIT_STOP) return;
        ObjectSetString(Terminal.Get_ID(), m_ArrObject[eEDIT_LEVERAGE].szName, OBJPROP_TEXT, IntegerToString(nContracts));
        ObjectSetString(Terminal.Get_ID(), m_ArrObject[eEDIT_TAKE].szName, OBJPROP_TEXT, IntegerToString(FinanceTake));
        ObjectSetString(Terminal.Get_ID(), m_ArrObject[eEDIT_STOP].szName, OBJPROP_TEXT, IntegerToString(FinanceStop));
        ObjectSetInteger(Terminal.Get_ID(), m_ArrObject[eCHECK_DAYTRADE].szName, OBJPROP_STATE, m_IsDayTrade = b1);
}

Veuillez noter que l'IDE est lié au système d’ordres. Donc les modifications apportées au système seront reflétées dans le système de d’ordres. Nous n’aurons donc pas à modifier les données dans l'EA comme nous le faisions auparavant. Nous pouvons maintenant le faire directement dans l'IDE ou dans notre Chart Trade - cela est fait avec les deux fonctions mentionnées ci-dessus et liées au système de messagerie.

void DispatchMessage(int iMsg, string szArg, double dValue = 0.0)
{
        if (m_CountObject < eEDIT_STOP) return;
        switch (iMsg)
        {
                case CHARTEVENT_CHART_CHANGE:
                        if (szArg == szMsgIDE[eRESULT])
                        {
                                ObjectSetInteger(Terminal.Get_ID(), m_ArrObject[eRESULT].szName, OBJPROP_BGCOLOR, (dValue < 0 ? clrLightCoral : clrLightGreen));
                                ObjectSetString(Terminal.Get_ID(), m_ArrObject[eRESULT].szName, OBJPROP_TEXT, DoubleToString(dValue, 2));
                        }
                        break;
                case CHARTEVENT_OBJECT_CLICK:
                        if (StringSubstr(szArg, 0, StringLen(def_HeaderMSG)) != def_HeaderMSG) return;
                        szArg = StringSubstr(szArg, 9, StringLen(szArg));
                        StringToUpper(szArg);
                        if ((szArg == szMsgIDE[eBTN_SELL]) || (szArg == szMsgIDE[eBTN_BUY])) NanoEA.OrderMarket(szArg == szMsgIDE[eBTN_BUY]);
                        if (szArg == szMsgIDE[eBTN_CANCEL])
                        {
                                NanoEA.ClosePosition();
                                ObjectSetInteger(Terminal.Get_ID(), m_ArrObject[eBTN_CANCEL].szName, OBJPROP_STATE, false);
                        }
                        if (szArg == szMsgIDE[eCHECK_DAYTRADE]) UpdateInfos(true);
                        break;
                case CHARTEVENT_OBJECT_ENDEDIT:
                        UpdateInfos();
                        break;
        }
}

Vous pourriez finalement me poser une dernière question : Est-ce que c'est tout ce qu’il y a à faire ? Oui, c'est le gestionnaire d’évènements qui permet à la plateforme MetaTrader 5 d'interagir avec l'IDE. Il est vrai que c'est très simple, mais sans cette fonction, l'IDE ne fonctionnerait pas, et il ne serait pas possible de construire le système. Cela peut paraître lourd de faire fonctionner cela dans un EA. Mais en fait, grâce à la POO, le code de l'EA restera très simple. Obtenir la mise à jour du résultat à faire apparaître dans l'IDE sera par contre plus délicat. Les valeurs sont mises à jour dans la fonction OnTick. Mais pour simplifier, j'ai utilisé les données fournies dans MetaTrader 5, donc la fonction ressemble à ceci : Cette partie est la plus importante : cette fonction est la plus appelée de toutes, elle doit donc être aussi la plus rapide.

void OnTick()
{
        SubWin.DispatchMessage(CHARTEVENT_CHART_CHANGE, C_Chart_IDE::szMsgIDE[C_Chart_IDE::eRESULT], NanoEA.CheckPosition());
}

En d'autres termes, à chaque nouvelle cotation, un évènement est envoyé à la classe et son résultat est mis à jour dans l'opération. N'oubliez pas que cette fonction doit être bien optimisée, sinon nous pouvons avoir de sérieux problèmes.


Conclusion

Parfois il semble parfois impossible de faire certaines choses. Mais j'aime les défis. Et ce projet, qui montre comment réaliser un système RAD à l'intérieur d'une plateforme qui n'a pas été développée à l'origine pour cela, était assez intéressant. J'espère que ce système, qui était simple au début vous donne envie d'explorer quelque chose de nouveau.

J’ajouterai bientôt de nouvelles fonctionnalités à cet Expert Advisor, alors restez à l'écoute !