English Русский 中文 Español Deutsch 日本語 Português 한국어 Italiano Türkçe
preview
Développer un Expert Advisor à partir de zéro (partie 9) : Un saut conceptuel (2)

Développer un Expert Advisor à partir de zéro (partie 9) : Un saut conceptuel (2)

MetaTrader 5Indicateurs | 4 octobre 2022, 08:54
569 0
Daniel Jose
Daniel Jose

Introduction

Dans la partie précédente, nous avions créé un système de base qui permettait l'utilisation de modèles dans une fenêtre flottante. Malgré les nombreuses modifications que nous avons déjà faites, le code n'est pas encore terminé. C’était intentionnel pour que les explications restent simples. L'utilisation de modèles dans des fenêtres flottantes est plutôt simple, mais utiliser des objets est quelque chose de beaucoup plus compliqué. Alors, soyez prêt pour ce nouvel objectif.

La plus grande difficulté liée à l'utilisation des objets que nous utilisons pour créer l'interface CHART TRADE dans la fenêtre flottante est que MetaTrader 5 n'est pas vraiment prévu à cet effet. Certains d’entre vous peuvent se dire que nous pourrions utiliser la bibliothèque standard pour créer la fenêtre CHART TRADE. Mais j'aime compliquer les choses. Je veux permettre à chacun de créer sa propre interface de la manière dont nous avons discuté dans mes articles. Mais tout était encore simple dans ces articles. Nous devons maintenant comprendre les limites de MetaTrader 5 pour pouvoir les contourner.


Plan

Commençons par la base. Le code suivant se comportera comme nous le voulons :

#property copyright "Daniel Jose"
#property indicator_chart_window
#property indicator_plots 0
//+------------------------------------------------------------------+
int OnInit()
{
        long id = ChartID();
        string sz0 = (string)ObjectsTotal(id, -1, -1) + (string)MathRand();
        ObjectCreate(id, sz0, OBJ_CHART, 0, 0, 0);
        ObjectSetInteger(id, sz0, OBJPROP_XDISTANCE, 10);
        ObjectSetInteger(id, sz0, OBJPROP_YDISTANCE, 10);
        ObjectSetInteger(id, sz0, OBJPROP_XSIZE, 300);
        ObjectSetInteger(id, sz0, OBJPROP_YSIZE, 300);
        ObjectSetInteger(id, sz0, OBJPROP_PRICE_SCALE, false);
        ObjectSetInteger(id, sz0, OBJPROP_DATE_SCALE, false);
        
  
        return INIT_SUCCEEDED;
}
//+------------------------------------------------------------------+
int OnCalculate(const int rates_total,
                const int prev_calculated,
                const int begin,
                const double &price[])
{
        return rates_total;
}
//+------------------------------------------------------------------+


Il n'y a rien de compliqué ici. Nous obtenons ce que nous voulions. Mais MQL5 vous permet d'aller un peu plus loin. Même si des difficultés peuvent survenir lorsque vous essayez de faire quelque chose au-delà de ce pour quoi le système a été conçu à l'origine. Nous allons donc changer le code ci-dessus de la façon suivante. Et les choses deviennent plus intéressantes.

int OnInit()
{
        long id = ChartID(), handle;
        string sz0 = (string)ObjectsTotal(id, -1, -1) + (string)MathRand(), sz1 = (string)MathRand();
        
        ObjectCreate(id, sz0, OBJ_CHART, 0, 0, 0);
        ObjectSetInteger(id, sz0, OBJPROP_XDISTANCE, 10);
        ObjectSetInteger(id, sz0, OBJPROP_YDISTANCE, 10);
        ObjectSetInteger(id, sz0, OBJPROP_XSIZE, 300);
        ObjectSetInteger(id, sz0, OBJPROP_YSIZE, 300);
        ObjectSetInteger(id, sz0, OBJPROP_PRICE_SCALE, false);
        ObjectSetInteger(id, sz0, OBJPROP_DATE_SCALE, false);
        
        handle = ObjectGetInteger(id, sz0, OBJPROP_CHART_ID);
        ObjectCreate(handle, sz1, OBJ_CHART, 0, 0, 0);
        ObjectSetInteger(handle, sz1, OBJPROP_XDISTANCE, 50);
        ObjectSetInteger(handle, sz1, OBJPROP_YDISTANCE, 50);
        ObjectSetInteger(handle, sz1, OBJPROP_XSIZE, 300);
        ObjectSetInteger(handle, sz1, OBJPROP_YSIZE, 300);
        ObjectSetInteger(handle, sz1, OBJPROP_PRICE_SCALE, false);
        ObjectSetInteger(handle, sz1, OBJPROP_DATE_SCALE, false);
        ChartRedraw(handle);    
  
        return INIT_SUCCEEDED;
}

Nous avons ajouté le code en surbrillance. Si vous l'exécutez sur un graphique, le résultat devrait ressembler à ceci :


Que s’est-il passé ? Nous avons placé un graphique sur un autre graphique. Nous pourrions en fait placer n'importe quel objet ici puisque MQL5 le permet. Mais cela présente à la fois des avantages et des inconvénients. Jetez un œil à la prochaine modification du code afin de comprendre l'avantage de cette étape :

int OnInit()
{
        long id = ChartID(), handle;
        string sz0 = (string)ObjectsTotal(id, -1, -1) + (string)MathRand(), sz1 = (string)MathRand();
        
        ObjectCreate(id, sz0, OBJ_CHART, 0, 0, 0);
        ObjectSetInteger(id, sz0, OBJPROP_XDISTANCE, 10);
        ObjectSetInteger(id, sz0, OBJPROP_YDISTANCE, 10);
        ObjectSetInteger(id, sz0, OBJPROP_XSIZE, 300);
        ObjectSetInteger(id, sz0, OBJPROP_YSIZE, 300);
        ObjectSetInteger(id, sz0, OBJPROP_PRICE_SCALE, false);
        ObjectSetInteger(id, sz0, OBJPROP_DATE_SCALE, false);
        ObjectSetInteger(id, sz0, OBJPROP_SELECTABLE, true);
        ObjectSetInteger(id, sz0, OBJPROP_SELECTED, true);
        
        handle = ObjectGetInteger(id, sz0, OBJPROP_CHART_ID);
        ObjectCreate(handle, sz1, OBJ_CHART, 0, 0, 0);
        ObjectSetInteger(handle, sz1, OBJPROP_XDISTANCE, 50);
        ObjectSetInteger(handle, sz1, OBJPROP_YDISTANCE, 50);
        ObjectSetInteger(handle, sz1, OBJPROP_XSIZE, 300);
        ObjectSetInteger(handle, sz1, OBJPROP_YSIZE, 300);
        ObjectSetInteger(handle, sz1, OBJPROP_PRICE_SCALE, false);
        ObjectSetInteger(handle, sz1, OBJPROP_DATE_SCALE, false);
        ChartRedraw(handle);    
  
        return INIT_SUCCEEDED;
}

L'ajout des lignes en surbrillance a permis de pouvoir sélectionner et déplacer le nouveau graphique :


Cela signifie aussi que tout ce qui est à l'intérieur de l'objet restera à l'intérieur de l'objet. C’est nécessaire lorsque nous utilisons des fenêtres flottantes, car la logique de contrôle est grandement simplifiée. Mais malheureusement tout n'est pas parfait : MetaTrader 5 n'a pas été conçu pour cela à l'origine. Il y a un problème lorsqu'un objet est situé à l'intérieur d'un autre : nous ne pouvons pas envoyer d'événements aux objets internes. Pour le comprendre, implémentons quelques modifications supplémentaires dans le code :

#property copyright "Daniel Jose"
#property indicator_chart_window
#property indicator_plots 0
//+------------------------------------------------------------------+
int OnInit()
{
        long id = ChartID(), handle;
        string sz0 = (string)ObjectsTotal(id, -1, -1) + (string)MathRand(), sz1 = (string)MathRand();
        
        ObjectCreate(id, sz0, OBJ_CHART, 0, 0, 0);
        ObjectSetInteger(id, sz0, OBJPROP_XDISTANCE, 10);
        ObjectSetInteger(id, sz0, OBJPROP_YDISTANCE, 10);
        ObjectSetInteger(id, sz0, OBJPROP_XSIZE, 300);
        ObjectSetInteger(id, sz0, OBJPROP_YSIZE, 300);
        ObjectSetInteger(id, sz0, OBJPROP_PRICE_SCALE, false);
        ObjectSetInteger(id, sz0, OBJPROP_DATE_SCALE, false);
        ObjectSetInteger(id, sz0, OBJPROP_SELECTABLE, true);
        ObjectSetInteger(id, sz0, OBJPROP_SELECTED, true);
        
        handle = ObjectGetInteger(id, sz0, OBJPROP_CHART_ID);
        ObjectCreate(handle, sz1, OBJ_CHART, 0, 0, 0);
        ObjectSetInteger(handle, sz1, OBJPROP_XDISTANCE, 50);
        ObjectSetInteger(handle, sz1, OBJPROP_YDISTANCE, 50);
        ObjectSetInteger(handle, sz1, OBJPROP_XSIZE, 300);
        ObjectSetInteger(handle, sz1, OBJPROP_YSIZE, 300);
        ObjectSetInteger(handle, sz1, OBJPROP_PRICE_SCALE, false);
        ObjectSetInteger(handle, sz1, OBJPROP_DATE_SCALE, false);
        ObjectSetInteger(handle, sz1, OBJPROP_SELECTABLE, true);
        ObjectSetInteger(handle, sz1, OBJPROP_SELECTED, true);
        ChartRedraw(handle);    
  
        return INIT_SUCCEEDED;
}
//+------------------------------------------------------------------+
int OnCalculate(const int rates_total,
                const int prev_calculated,
                const int begin,
                const double &price[])
{
        return rates_total;
}
//+------------------------------------------------------------------+
void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam)
{
        if (id == CHARTEVENT_OBJECT_CLICK) Print(sparam);
}
//+------------------------------------------------------------------+

Voici le résultat du code lorsque nous l’exécutons dans la plateforme :


Lorsque nous cliquons sur l'objet intérieur, un clic est en fait effectué sur l'objet extérieur. C'est là que les choses se compliquent. Mais en tant que programmeur, nous devenons aussi expert en résolution de problèmes : nous avons toujours à résoudre des problèmes pour obtenir le résultat souhaité. Avec ces connaissances, nous construirons le système de manière à créer CHART TRADE dans une fenêtre flottante, à nous assurer qu'il est fonctionnel et qu’il a une apparence individuelle.

Il y a toutefois une dernière phase dans ce plan. Bien que cette partie ne soit pas aussi aiguë pour les ordinateurs modernes, elle reste à prendre en compte : l’optimisation du temps de traitement. Le problème est lié au nombre d'opérations que le processeur doit effectuer plutôt qu'au temps qu'il faudra pour traiter l'information. Le système de fenêtres flottantes proposé contient quatre objets qui doivent pouvoir se déplacer en réagissant à vos actions. Donc, toute information placée dans la fenêtre sera sujette aux propres modifications de la fenêtre. CHART TRADE augmentera encore le nombre d'objets. Et bien qu'il n'y ait pas de coût de calcul associé, le code devient désagréable et semble mal optimisé. Pour résoudre le problème, on pourrait simplement ajouter un système de contrôle. Mais il existe une solution plus élégante. Bien que cela semble prendre plus de temps et d'efforts, cela réduit en fait le nombre d'objets à maintenir et à manipuler.


Implémentation

Pour commencer, nous allons diviser la création de la fenêtre flottante en plusieurs étapes pour prendre en charge la réutilisation du code. Nous allons ensuite créer deux nouvelles fonctions dans la classe d'objets C_ChartFloating :

//+------------------------------------------------------------------+
bool StageLocal01(string sz0, ENUM_TIMEFRAMES TimeFrame = PERIOD_CURRENT, int Scale = -1)
{
        m_LimitX = (int)ChartGetInteger(Terminal.Get_ID(), CHART_WIDTH_IN_PIXELS);
        m_LimitY = (int)ChartGetInteger(Terminal.Get_ID(), CHART_HEIGHT_IN_PIXELS);
        if (m_MaxCounter >= def_MaxFloating) return false;
        CreateBarTitle();
        CreateCaption(sz0);
        CreateBtnMaxMin();
        CreateRegion(TimeFrame, Scale);
	m_Win[m_MaxCounter].handle = ObjectGetInteger(Terminal.Get_ID(), m_Win[m_MaxCounter].szRegionChart, OBJPROP_CHART_ID);
                                
        return true;
}
//+------------------------------------------------------------------+
void StageLocal02(int x, int y, int w, int h)
{
        y = (y < 0 ? m_MaxCounter * def_SizeBarCaption : y);                            
        m_Win[m_MaxCounter].PosX        = -1;
        m_Win[m_MaxCounter].PosY        = -1;
        m_Win[m_MaxCounter].PosX_Minimized = m_Win[m_MaxCounter].PosX_Maximized = x;
        m_Win[m_MaxCounter].PosY_Minimized = m_Win[m_MaxCounter].PosY_Maximized = y;
        SetDimension(w, h, true, m_MaxCounter);
        SetPosition(x, y, m_MaxCounter);
        ChartRedraw(m_Win[m_MaxCounter].handle);
        m_MaxCounter++;
}
//+------------------------------------------------------------------+

Le nouveau code pour ajouter une fenêtre flottante sera le suivant :

bool AddIndicator(string sz0, int x = 0, int y = -1, int w = 300, int h = 200, ENUM_TIMEFRAMES TimeFrame = PERIOD_CURRENT, int Scale = -1)
{
	if (!StageLocal01(sz0, TimeFrame, Scale)) return false;
        ChartApplyTemplate(m_Win[m_MaxCounter].handle, sz0 + ".tpl");   
        m_Win[m_MaxCounter].szVLine = (string)ObjectsTotal(Terminal.Get_ID(), -1, -1) + (string)MathRand();
        ObjectCreate(m_Win[m_MaxCounter].handle, m_Win[m_MaxCounter].szVLine, OBJ_VLINE, 0, 0, 0);
        ObjectSetInteger(m_Win[m_MaxCounter].handle, m_Win[m_MaxCounter].szVLine, OBJPROP_COLOR, clrBlack);
        StageLocal02(x, y, w, h);

        return true;
}

Cela n'affecte pas le système déjà créé mais permet une meilleure utilisation. Faites attention aux lignes surlignées : nous allons maintenant créer une nouvelle fonction pour utiliser notre IDE. Le début est comme suit :

bool Add_RAD_IDE(string sz0, int x, int y, int w, int h)
{
        if (!StageLocal01(sz0, PERIOD_CURRENT, -1)) return false;
        StageLocal02(x, y, w, h);
        return true;
}

Remarquez que les lignes en surbrillance sont identiques à celles que nous avons utilisées dans le code précédent. C'est-à-dire que nous réutilisons le code. Seules les choses que nous devons ajuster sont différentes. Maintenant, nous pouvons informer notre système que nous avons les outils pour gérer notre IDE. Pour ce faire, nous allons modifier la classe d'objets C_TemplateChart. Le code ci-dessous montre exactement ce qui sera modifié dans la fonction. Nous pouvons donc désormais nous concentrer sur l'implémentation d'une fenêtre flottante avec l'IDE, car tout le support nécessaire fonctionnera déjà correctement.

void AddTemplate(void)
{

// .... Function code....

        if (h == 0)
        {
                SetBase(m_Params.Param[TEMPLATE], (bIsSymbol ? m_Params.Param[TEMPLATE] : _Symbol), timeframe, i, w);
                if (!ChartApplyTemplate(m_Info[m_Counter - 1].handle, m_Params.Param[TEMPLATE] + ".tpl")) if (bIsSymbol) ChartApplyTemplate(m_Info[m_Counter - 1].handle, "Default.tpl");
        }
        if (m_Params.Param[TEMPLATE] == def_NameTemplateRAD)
        {
                if ((h > 0) && (w > 0)) Add_RAD_IDE(m_Params.Param[TEMPLATE], 0, -1, w, h); else
                {
                        C_Chart_IDE::Create(GetIdSubWinEA());
                        m_Info[m_Counter - 1].szVLine = "";
                }
        }else
        {
                if ((w > 0) && (h > 0)) AddIndicator(m_Params.Param[TEMPLATE], 0, -1, w, h, timeframe, i); else
                {
                        m_Info[m_Counter - 1].szVLine = (string)ObjectsTotal(Terminal.Get_ID(), -1, -1) + (string)MathRand();
                        ObjectCreate(m_Info[m_Counter - 1].handle, m_Info[m_Counter - 1].szVLine, OBJ_VLINE, 0, 0, 0);
                        ObjectSetInteger(m_Info[m_Counter - 1].handle, m_Info[m_Counter - 1].szVLine, OBJPROP_COLOR, clrBlack);
                }
        }
}

Voyons comment le code est construit pour être aussi flexible que possible. Cela empêchera le système de se transformer en un monstre. Lorsque nous modifions le code, gardez toujours cela à l'esprit : il n'est pas nécessaire de réécrire le code à partir de zéro et de vérifier la même chose plusieurs fois. Essayez toujours de ne vérifier les choses qu’une seule fois. Vous pouvez ensuite utiliser et explorer les choses autant que possible avant de devoir faire de nouveaux tests. Ainsi, le système sera développé avec de bonnes bases. Le code restera durable et extensible dans le temps.

Si vous exécutez le système maintenant, il affichera déjà quelque chose sur le graphique. Mais nous en avons besoin pour montrer l'interface que nous avons écrite plus tôt. Par conséquent, nous devons apporter d’autres modifications au code. Nous obtenons maintenant le code suivant :

bool Add_RAD_IDE(string sz0, int x, int y, int w, int h)
{
        if (!StageLocal01(sz0, PERIOD_CURRENT, -1)) return false;
        ChartApplyTemplate(m_Win[m_MaxCounter].handle, "\\Files\\Chart Trade\\IDE.tpl");
        StageLocal02(x, y, w, h);
        return true;
}

Voici le résultat au lancement de l'application :


Ce serait génial s'il était possible d'accéder aux objets qui sont dans le template (le template est chargé dans la ligne surlignée dans le code ci-dessus). Mais ce n'est malheureusement pas possible. Et voici le détail le plus important : au lieu de créer des objets comme nous l'avons envisagé précédemment, nous allons créer uniquement les objets qui doivent être manipulés ! Cela permettra d'économiser beaucoup de temps de traitement lorsque nous déplacerons la fenêtre. Nous avons encore un autre problème. Mais tout d’abord, résolvons le problème de manipulation et rendons le système fonctionnel. Cette partie est en fait déjà construite. Il faut juste quelques ajustements pour que les choses fonctionnent.

Commençons d’abord par apporter des modifications à la séquence d'héritage entre les classes. Nous devons le faire car nous n'avons pas d'héritage multiple. La nouvelle structure ressemblera donc à ceci :


Mais ce changement ne devrait pas être un souci : un changement dans la séquence d'héritage ne changera en rien le code, et il lui permettra d'être presque prêt. Les parties qui ont été modifiées sont mises en évidence dans le code ci-dessous :

bool Add_RAD_IDE(string sz0, int x, int y, int w, int h)
{
	if ((w <= 0) || (h <= 0)) return false;
        if (!StageLocal01(sz0, PERIOD_CURRENT, -1)) return false;
        ChartApplyTemplate(m_Win[m_MaxCounter].handle, "\\Files\\Chart Trade\\IDE.tpl");
        StageLocal02(x, y, w, h);
        return true;
}
void AddTemplate(void)
{
// ..... Código ....
        if (h == 0)
        {
                SetBase(m_Params.Param[TEMPLATE], (bIsSymbol ? m_Params.Param[TEMPLATE] : _Symbol), timeframe, i, w);
                if (!ChartApplyTemplate(m_Info[m_Counter - 1].handle, m_Params.Param[TEMPLATE] + ".tpl")) if (bIsSymbol) ChartApplyTemplate(m_Info[m_Counter - 1].handle, "Default.tpl");
        }
        if (m_Params.Param[TEMPLATE] == def_NameTemplateRAD)
        {
		C_Chart_IDE::Create(Add_RAD_IDE(m_Params.Param[TEMPLATE], 0, -1, w, h));
                m_Info[m_Counter - 1].szVLine = "";
        }else
        {
                if ((w > 0) && (h > 0)) AddIndicator(m_Params.Param[TEMPLATE], 0, -1, w, h, timeframe, i); else
                {
                        m_Info[m_Counter - 1].szVLine = (string)ObjectsTotal(Terminal.Get_ID(), -1, -1) + (string)MathRand();
                        ObjectCreate(m_Info[m_Counter - 1].handle, m_Info[m_Counter - 1].szVLine, OBJ_VLINE, 0, 0, 0);
                        ObjectSetInteger(m_Info[m_Counter - 1].handle, m_Info[m_Counter - 1].szVLine, OBJPROP_COLOR, clrBlack);
                }
        }
}
bool Create(bool bFloat)
{
        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 = ((m_IsFloating = bFloat) ? 0 : GetIdSubWinEA());
        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);
        DispatchMessage(CHARTEVENT_CHART_CHANGE, 0, 0, szMsgIDE[eLABEL_SYMBOL]);
        return true;
}

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++);
                        if (!b0 && m_IsFloating) return true; else c0 = (b0 ? c0 : m_CountObject);
                        m_ArrObject[c0].szName = StringFormat("%s%04s>%s", def_HeaderMSG, sz0, m_szValue);

//... The rest of the function...

}

Cela pourrait sembler étrange. Mais il n'y a en fait rien de compliqué ici. N'oubliez pas que lorsque qu’aucune fenêtre flottante n’est utilisée, le système est déjà capable de gérer les événements exécutés dans notre IDE. Mais nous devons tout reconstruire à cause de cette fenêtre flottante. Cependant, nous n'avons pas à le faire depuis zéro. Nous modifierons le code existant pour ajouter l’IDE au bon endroit. Il suffit juste d'ajouter les objets qui reçoivent des événements. Ces changements nous permettent de savoir si nous devons créer tous les éléments, ou seulement certains d'entre eux.

Après ces modifications, nous avons les informations de l’IDE sur le graphique. Mais les objets de l’IDE vont créer un vrai bazar puisque les objets ne sont pas associés à la fenêtre flottante. Cela doit maintenant être corrigé.

Nous devons tout d'abord obtenir les points où se trouve la fenêtre flottante sur le graphique. Il suffit de connaître les points de l'objet représentant la fenêtre. Mais puisque nous nous en tenons à la Programmation Orientée Objet, nous devrons apporter quelques modifications :

//+------------------------------------------------------------------+
struct IDE_Struct
{
        int     X,
                Y,
                Index;
        bool    IsMaximized;
};
//+------------------------------------------------------------------+
class C_ChartFloating
{

// ... Class code ...

//+------------------------------------------------------------------+
                void SetPosition(const int X, const int Y, const int c0)
                        {

// ... Function code ...

                                if (c0 == m_IDEStruct.Index)
                                {
                                        m_IDEStruct.X = m_Win[c0].PosX + 3;
                                        m_IDEStruct.Y = m_Win[c0].PosY + def_SizeBarCaption + 3;
                                        m_IDEStruct.IsMaximized = m_Win[c0].IsMaximized;
                                }
                        }
//+------------------------------------------------------------------+

// ... Class code ...

//+------------------------------------------------------------------+
inline IDE_Struct GetIDE_Struct(void) const { return m_IDEStruct; }
//+------------------------------------------------------------------+
                bool Add_RAD_IDE(string sz0, int x, int y, int w, int h)
                        {
                                if ((w <= 0) || (h <= 0)) return false;
                                if (!StageLocal01(sz0, PERIOD_CURRENT, -1)) return false;
                                ChartApplyTemplate(m_Win[m_MaxCounter].handle, "\\Files\\Chart Trade\\IDE.tpl");
                                m_IDEStruct.Index = m_MaxCounter;
                                StageLocal02(x, y, w, h);
                                return true;
                        }
//+------------------------------------------------------------------+

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

Étant donné que la solution se concentre sur notre unique fenêtre du CHART TRADER, il est inutile de compliquer les choses après ce point. Nous pouvons maintenant positionner correctement les objets dans la fenêtre (du moins au début). C’est ce que fait le code suivant :

void Resize(int x)
{
        for (int c0 = 0; c0 < m_CountObject; c0++) if (m_IsFloating)
        {
                ObjectSetInteger(Terminal.Get_ID(), m_ArrObject[c0].szName, OBJPROP_XDISTANCE, GetIDE_Struct().X + m_ArrObject[c0].iPosX);
                ObjectSetInteger(Terminal.Get_ID(), m_ArrObject[c0].szName, OBJPROP_YDISTANCE, GetIDE_Struct().Y + m_ArrObject[c0].iPosY);
        }else   ObjectSetInteger(Terminal.Get_ID(), m_ArrObject[c0].szName, OBJPROP_XDISTANCE, x + m_ArrObject[c0].iPosX);
};

La fonction ci-dessus fournit le positionnement initial correct des objets. Voici son résultat :


En plus du positionnement initial correct, les objets réagissent aussi déjà aux événements. Mais , le système a encore des bugs que nous devons corriger. Voici la 1ère chose que nous allons changer :


Comme vous pouvez le voir, la fenêtre a été réduite, mais les objets restent à l'écran. C'est parce que les objets ne sont pas à l'intérieur de la zone de la fenêtre (comme je l'ai expliqué plus tôt). Nous devons le corriger avant de continuer. Cela peut être fait par cette simple modification de code :

void Resize(int x)
{
        for (int c0 = 0; c0 < m_CountObject; c0++) if (m_IsFloating)
        {
                ObjectSetInteger(Terminal.Get_ID(), m_ArrObject[c0].szName, OBJPROP_XDISTANCE, GetIDE_Struct().X + m_ArrObject[c0].iPosX + (GetIDE_Struct().IsMaximized ? 0 : Terminal.GetWidth()));
                ObjectSetInteger(Terminal.Get_ID(), m_ArrObject[c0].szName, OBJPROP_YDISTANCE, GetIDE_Struct().Y + m_ArrObject[c0].iPosY + (GetIDE_Struct().IsMaximized ? 0 : Terminal.GetHeight()));
        }else   ObjectSetInteger(Terminal.Get_ID(), m_ArrObject[c0].szName, OBJPROP_XDISTANCE, x + m_ArrObject[c0].iPosX);
};

Voici le résultat :

Réglons maintenant le problème du déplacement. Il peut être résolu avec le code ci-dessous :

void DispatchMessage(int id, long lparam, double dparam, string sparam)
{
        static double AccumulatedRoof = 0.0;
        bool            b0;
        double  d0;
        static int px = -1, py = -1;
                                
        C_ChartFloating::DispatchMessage(id, lparam, dparam, sparam);
        if (m_CountObject < eEDIT_STOP) return;
        switch (id)
        {
                case CHARTEVENT_MOUSE_MOVE:
                        if ((GetIDE_Struct().X != px) || (GetIDE_Struct().Y != py))
                        {
                                px = GetIDE_Struct().X;
                                py = GetIDE_Struct().Y;
                                Resize(-1);
                        }
                        break;

//... The rest of the function ...

Et le résultat final est le suivant :

 


Conclusion

Vous pouvez voir à quel point la programmation est géniale : nous commençons par un problème, puis, sans modifications majeures du code, nous résolvons les problèmes un par un, et à la fin, nous avons un code qui fonctionne avec la plus petite quantité de code possible.


IMPORTANT ! Si vous voyez des informations étranges sur l'arrière-plan de Chart Trade, cela est dû à la mise à jour 3228, qui rend opaques les objets avec la couleur clrNONE. Vous pouvez utiliser le fichier IDE joint qui résout le problème.


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

Fichiers joints |
EA_1.09.zip (3617.03 KB)
IDE.tpl (10.13 KB)
Apprenez à concevoir un système de trading basé sur Le SAR Parabolique Apprenez à concevoir un système de trading basé sur Le SAR Parabolique
Dans cet article, nous poursuivons notre série sur la conception d'un système de trading utilisant des indicateurs les plus populaires. Dans cet article, nous allons découvrir en détail l'indicateur SAR Parabolique, puis comment nous pouvons concevoir un système de trading à utiliser dans MetaTrader 5 en utilisant quelques stratégies simples.
Développer un Expert Advisor à partir de zéro (partie 8) : Un saut conceptuel Développer un Expert Advisor à partir de zéro (partie 8) : Un saut conceptuel
Quel est le moyen le plus facile d'implémenter de nouvelles fonctionnalités ? Dans cet article, nous allons faire un pas en arrière, puis deux pas en avant.
Visualisez le ! Bibliothèque graphique MQL5 similaire à 'plot' du langage R Visualisez le ! Bibliothèque graphique MQL5 similaire à 'plot' du langage R
Lors de l'étude de la logique de trading, la représentation visuelle sous forme de graphiques est d’une grande importance. Un certain nombre de langages de programmation populaires dans la communauté scientifique (tels que R et Python) contiennent une fonction spéciale "plot" utilisée pour la visualisation. Elle permet de dessiner des lignes, des distributions de points et des histogrammes pour visualiser les modèles. En MQL5, vous pouvez faire de même en utilisant la classe CGraphics.
Développer un Expert Advisor à partir de zéro (partie 7) : Ajout du Volume au Prix (I) Développer un Expert Advisor à partir de zéro (partie 7) : Ajout du Volume au Prix (I)
Il s'agit de l'un des indicateurs les plus puissants actuellement. Tout trader essayant d'avoir un certain degré de confiance doit avoir cet indicateur sur son graphique. Le plus souvent, l'indicateur est utilisé par ceux qui préfèrent "lire les bandes" lorsqu'ils tradent. Cet indicateur peut également être utilisé par ceux qui n'utilisent que l'action des prix dans leurs transactions.