English Русский Español Português
preview
Entwicklung eines Replay Systems (Teil 46): Chart Trade Projekt (V)

Entwicklung eines Replay Systems (Teil 46): Chart Trade Projekt (V)

MetaTrader 5Beispiele | 9 Oktober 2024, 16:26
24 0
Daniel Jose
Daniel Jose

Einführung

Im vorherigen Artikel „Entwicklung eines Wiedergabesystems (Teil 45): Chart Trade Project (IV)“ habe ich erklärt, wie man die Funktionalität des Chart Trade Indikators startet. Sie haben vielleicht bemerkt, dass im Anhang zu diesem Artikel eine Menge Dateien zu übertragen waren. Das Risiko, einen Fehler zu machen, z. B. das Vergessen, eine Datei hinzuzufügen oder versehentlich etwas Wichtiges zu löschen, macht diese Methode nicht sehr attraktiv.

Ich weiß, dass viele Menschen diese Form der Verteilung und Speicherung nutzen, aber es gibt einen viel geeigneteren Weg. Zumindest wenn es um die Verteilung und Speicherung von ausführbaren Dateien geht.

Die hier vorgestellte Methode kann sehr nützlich sein, da Sie den MetaTrader 5 selbst als hervorragenden Assistenten verwenden können, ebenso wie MQL5. Außerdem ist es nicht so schwer zu verstehen. Was ich in diesem Artikel erkläre, kann Ihnen helfen, Probleme mit dem korrekten Speichern von ausführbaren Dateien und allem, was für ihren korrekten Betrieb notwendig ist, zu vermeiden.

 

Die Idee verstehen

Bevor wir beginnen, müssen wir uns darüber im Klaren sein, dass es bei der Umsetzung unserer Pläne einige Probleme und Grenzen gibt. Diese Einschränkungen ergeben sich aus der Tatsache, dass sowohl die MQL5-Sprache als auch die MetaTrader 5-Plattform nicht für bestimmte Verwendungszwecke vorgesehen sind.

Sowohl die Sprache als auch die Plattform sind für den Einsatz als Marktvisualisierungsinstrument konzipiert. Die Tatsache, dass wir wollen oder versuchen, sie anders arbeiten zu lassen, bedeutet nicht unbedingt, dass sie etwas nicht tun können.

Dies ist die erste der Einschränkungen, die es gibt und die Sie kennen sollten. Als Nächstes ist zu beachten, dass Sie ausführbaren Dateien fast alles hinzufügen können. Ich habe wohlgemerkt „fast“ gesagt. Theoretisch kann man fast alles hinzufügen, wenn man weiß, wie man es in die ausführbare Datei einfügt oder aufnimmt. Der Weg, Informationen in eine ausführbare Datei einzubinden, besteht natürlich darin, sie zu einer internen Ressource der ausführbaren Datei selbst zu machen.

Mir ist aufgefallen, dass viele Leute Bilder, Sounds und ähnliche Dinge hinzufügen, aber können wir eine Vorlagendatei hinzufügen, sodass sie Teil der ausführbaren Datei wird? Nachdem Sie die Dokumentation gelesen haben, werden Sie feststellen, dass dies nicht möglich ist. Wenn Sie dies versuchen, gibt der Compiler einen Fehler zurück, wenn er versucht, das Programm zu kompilieren.

#define def_IDE_RAD  "Files\\Chart Trade\\IDE_RAD.tpl"
#resource "\\" + def_IDE_RAD;

Wenn Sie versuchen, eine ausführbare Datei zu kompilieren, die den obigen Code enthält, werden Sie feststellen, dass der Compiler einen Fehler erzeugt, weil sich die Definition auf eine Vorlage bezieht. Dies ist für eine ausführbare Datei, die auf der MetaTrader 5-Plattform verwendet wird, generell verboten.

Ich muss zugeben, dass diese Art von Beschränkungen recht unangenehm ist, aber sie können leicht umgangen werden, wenn auch nicht ohne Kosten. Es gibt Einschränkungen und Probleme, die gelöst werden müssen. Sie können, und ich werde Ihnen zeigen, wie, eine Vorlage zu der von MetaEditor kompilierten ausführbaren Datei hinzufügen. Das Hauptproblem ist jedoch nicht, wie man die Vorlage in die ausführbare Datei einfügt, sondern wie man genau diese Vorlage verwendet.

Warum soll ich erklären, wie man das macht, ist es nicht einfacher, die Dinge einfach wie gewohnt zu benutzen? Nun, es ist viel einfacher, alles so zu benutzen, wie wir es gewohnt sind. Wie ich jedoch bereits zu Beginn dieses Artikels erwähnt habe, ist es viel praktischer, alles in die ausführbare Datei aufzunehmen. Stellen Sie sich vor, Sie müssten alle für eine bestimmte Funktion in MetaTrader 5 erforderlichen Dateien zusammenpacken und auf den Rechner übertragen, der für den Handel verwendet werden soll. Wenn Sie etwas vergessen, müssen Sie die verlorene(n) Datei(en) erneut suchen. Sie müssen sie an der richtigen Stelle im Verzeichnisbaum ablegen, damit die Anwendung auf sie zugreifen kann.

Meiner Meinung nach ist das ziemlich ermüdend. Viele Leute benutzen dasselbe Gerät, um MetaTrader 5 zu entwickeln und zu benutzen. Ich möchte diesen Leuten sagen, dass dies ein großer Fehler ist, um es vorsichtig auszudrücken. NIEMALS, verwenden Sie NIEMALS ein und denselben Rechner oder dieselbe MetaTrader5-Installation für den Handel und die Entwicklung. Auf diese Weise können Sie eine Störung oder Schwachstelle in der Anwendung verursachen, die die korrekte Funktion des automatischen EA beeinträchtigen könnte, wenn Sie ihn verwenden.

Ich habe schon viele seltsame Dinge während der Entwicklungsphase erlebt. Manchmal interagiert ein Programm oder eine Anwendung einfach mit einer anderen Anwendung, die auf dem Chart läuft, was zu einem sehr merkwürdigen Verhalten führt. Dies kann zu Fehlern oder unerklärlichen Dingen führen. Manch einer könnte sagen, dass das daran liegt, dass ich ständig Dinge teste. Nun, das ist wahr. Manchmal kann dies tatsächlich durch die Prüfung verursacht werden, aber manchmal macht es einfach keinen Sinn.

Aus diesem Grund ist es äußerst wichtig, zwei Installationen für die Verwendung von MetaTrader 5 zu haben: eine für die Entwicklung und eine ausschließlich für die Arbeit im Markt. Dies bezieht sich natürlich auf Entwickler. In jedem anderen Fall ist dies nicht erforderlich.

Ich habe viel darüber nachgedacht, ob ich dir zeigen soll, wie man bestimmte Dinge macht. Die meisten Menschen können die Grundlagen von MQL5 oder die Funktionsweise von MetaTrader 5 nicht verstehen. Stellen Sie sich also vor, was passieren wird, wenn sie versuchen zu verstehen, was ich Ihnen gleich zeigen werde. Manche Leute werden mich für verrückt halten. Aber Sie können die Anhänge in diesem und im vorigen Artikel vergleichen, sowohl was die Funktionalität als auch die Tragbarkeit betrifft. Die Funktionalität ist dieselbe. Was die Tragbarkeit anbelangt, so halte ich diese Option für viel besser. Sie brauchen nur eine Datei zu senden, ohne sich um irgendwelche Verzeichnisstrukturen zu kümmern.

Das ist wichtig: Auch wenn ich sage, dass man sich keine Gedanken über die Verzeichnisstruktur machen muss, gilt das nur für die Speicherung der ausführbaren Datei in dem jeweiligen Verzeichnis. Wenn Sie die Datei von einem Verzeichnis in ein anderes verschieben oder sie sogar umbenennen, funktioniert das System nicht richtig.

Schauen wir uns also an, wie dies umgesetzt wurde und wie Sie es in Ihrem eigenen Code verwenden können.


Ressourcen, Ressourcen und Ressourcen

Um dieses Modell zu verwenden, müssen wir einige Änderungen am Code vornehmen. Trotzdem blieb der Quellcode des Indikators unverändert, sodass er hier im Artikel nicht aufgeführt wird. Der Code in der Klasse C_ChartFloatingRAD hat einige kleinere Änderungen erfahren, aber da diese nicht die gesamte Klasse betreffen, werde ich mich nur auf den Teil konzentrieren, in dem die Änderung tatsächlich stattgefunden hat.

Unten sehen Sie diesen Teil und die Erklärung dazu.

068. inline void AdjustTemplate(const bool bFirst = false)
069.                    {
070. #define macro_AddAdjust(A) {                   \
071.            (*Template).Add(A, "size_x", NULL); \
072.            (*Template).Add(A, "size_y", NULL); \
073.            (*Template).Add(A, "pos_x", NULL);  \
074.            (*Template).Add(A, "pos_y", NULL);  \
075.                            }
076. #define macro_GetAdjust(A) {                                                                        \
077.            m_Info.Regions[A].x = (int) StringToInteger((*Template).Get(EnumToString(A), "pos_x"));  \
078.            m_Info.Regions[A].y = (int) StringToInteger((*Template).Get(EnumToString(A), "pos_y"));  \
079.            m_Info.Regions[A].w = (int) StringToInteger((*Template).Get(EnumToString(A), "size_x")); \
080.            m_Info.Regions[A].h = (int) StringToInteger((*Template).Get(EnumToString(A), "size_y")); \
081.                            }
082. #define macro_PointsToFinance(A) A * (GetInfoTerminal().VolumeMinimal + (GetInfoTerminal().VolumeMinimal * (m_Info.Leverage - 1))) * GetInfoTerminal().AdjustToTrade
083.                            
084.                            C_AdjustTemplate *Template;
085.                            
086.                            if (bFirst)
087.                            {
088.                                    Template = new C_AdjustTemplate(m_Info.szFileNameTemplate = IntegerToString(GetInfoTerminal().ID) + ".tpl", true);
089.                                    for (eObjectsIDE c0 = 0; c0 <= MSG_CLOSE_POSITION; c0++) macro_AddAdjust(EnumToString(c0));
090.                                    AdjustEditabled(Template, true);
091.                            }else Template = new C_AdjustTemplate(m_Info.szFileNameTemplate);
092.                            m_Info.Leverage = (m_Info.Leverage <= 0 ? 1 : m_Info.Leverage);
093.                            m_Info.FinanceTake = macro_PointsToFinance(FinanceToPoints(MathAbs(m_Info.FinanceTake), m_Info.Leverage));
094.                            m_Info.FinanceStop = macro_PointsToFinance(FinanceToPoints(MathAbs(m_Info.FinanceStop), m_Info.Leverage));
095.                            (*Template).Add("MSG_NAME_SYMBOL", "descr", GetInfoTerminal().szSymbol);
096.                            (*Template).Add("MSG_LEVERAGE_VALUE", "descr", IntegerToString(m_Info.Leverage));
097.                            (*Template).Add("MSG_TAKE_VALUE", "descr", DoubleToString(m_Info.FinanceTake, 2));
098.                            (*Template).Add("MSG_STOP_VALUE", "descr", DoubleToString(m_Info.FinanceStop, 2));
099.                            (*Template).Add("MSG_DAY_TRADE", "state", (m_Info.IsDayTrade ? "1" : "0"));
100.                            (*Template).Add("MSG_MAX_MIN", "state", (m_Info.IsMaximized ? "1" : "0"));
101.                            (*Template).Execute();
102.                            if (bFirst)
103.                            {
104.                                    for (eObjectsIDE c0 = 0; c0 <= MSG_CLOSE_POSITION; c0++) macro_GetAdjust(c0);
105.                                    m_Info.Regions[MSG_TITLE_IDE].w = m_Info.Regions[MSG_MAX_MIN].x;
106.                                    AdjustEditabled(Template, false);
107.                            };
108.                            ObjectSetInteger(GetInfoTerminal().ID, m_Info.szObj_Chart, OBJPROP_YSIZE, (m_Info.IsMaximized ? 210 : m_Info.Regions[MSG_TITLE_IDE].h + 6));
109.                            ObjectSetInteger(GetInfoTerminal().ID, m_Info.szObj_Chart, OBJPROP_XDISTANCE, (m_Info.IsMaximized ? m_Info.x : m_Info.minx));
110.                            ObjectSetInteger(GetInfoTerminal().ID, m_Info.szObj_Chart, OBJPROP_YDISTANCE, (m_Info.IsMaximized ? m_Info.y : m_Info.miny));
111. 
112.                            delete Template;
113.                            
114.                            ChartApplyTemplate(m_Info.WinHandle, "/Files/" + m_Info.szFileNameTemplate);
115.                            ChartRedraw(m_Info.WinHandle);
116. 
117. #undef macro_PointsToFinance
118. #undef macro_GetAdjust
119. #undef macro_AddAdjust
120.                    }

Codefragment der Klasse C_ChartFloatingRAD

Sie werden vielleicht keine Unterschiede bemerken, aber sie sind da. Der Unterschied ist in Zeile 88 zu erkennen, auch wenn er nicht so offensichtlich ist. Wenn Sie also den Code der Klasse C_ChartFloatingRAD aus dem vorherigen Artikel nehmen und ihn in Zeile 88 genau wie in diesem Teil gezeigt ändern, erhalten Sie das neue Datenmodell, das wir verwenden werden.

Sie sehen, dass wir im Gegensatz zum ursprünglichen Code hier nur eine Zeichenkette definieren. Warum? Der Grund dafür ist, dass wir die Vorlage nicht mehr wie bisher verwenden werden. Wir werden eine Ressource verwenden. Mit anderen Worten: Die Vorlage wird in die ausführbare Datei des Indikators eingebettet. Wir brauchen also nicht mehr viele Daten zu übermitteln.

Diese Änderung wird jedoch bald eine weitere Frage aufwerfen. Wie können wir eine Vorlage als Ressource verwenden, wenn wir die Vorlage nicht als Teil der ausführbaren Datei kompilieren, d. h. als Ressource einbinden können? In der Tat können wir alles in die ausführbare Datei aufnehmen. Die Frage ist nur, wie genau wir das tun sollen.

Um dies zu verstehen, schauen wir uns den Code der Klasse C_AdjustTemplate an, um zu verstehen, warum die Änderung in Zeile 88 der Klasse C_ChartFloatingRAD vorgenommen wurde. Der vollständige Code der Klasse C_AdjustTemplate ist unten dargestellt. Da weitere Änderungen vorgenommen wurden, die allerdings nicht so bedeutend sind, wird es interessant sein zu erfahren, was tatsächlich geschehen ist.

001. //+------------------------------------------------------------------+
002. #property copyright "Daniel Jose"
003. //+------------------------------------------------------------------+
004. #include "../Auxiliar/C_Terminal.mqh"
005. //+------------------------------------------------------------------+
006. #define def_PATH_BTN "Images\\Market Replay\\Chart Trade"
007. #define def_BTN_BUY  def_PATH_BTN + "\\BUY.bmp"
008. #define def_BTN_SELL def_PATH_BTN + "\\SELL.bmp"
009. #define def_BTN_DT   def_PATH_BTN + "\\DT.bmp"
010. #define def_BTN_SW   def_PATH_BTN + "\\SW.bmp"
011. #define def_BTN_MAX  def_PATH_BTN + "\\MAX.bmp"
012. #define def_BTN_MIN  def_PATH_BTN + "\\MIN.bmp"
013. #define def_IDE_RAD  "Files\\Chart Trade\\IDE_RAD.tpl"
014. //+------------------------------------------------------------------+
015. #resource "\\" + def_BTN_BUY
016. #resource "\\" + def_BTN_SELL
017. #resource "\\" + def_BTN_DT
018. #resource "\\" + def_BTN_SW
019. #resource "\\" + def_BTN_MAX
020. #resource "\\" + def_BTN_MIN
021. #resource "\\" + def_IDE_RAD as string IdeRad;
022. //+------------------------------------------------------------------+
023. class C_AdjustTemplate
024. {
025.    private :
026.            string m_szName[],
027.                   m_szFind[],
028.                   m_szReplace[],
029.                   m_szFileName;
030.            int    m_maxIndex,
031.                   m_FileIn,
032.                   m_FileOut;
033.            bool   m_bFirst;
034. //+------------------------------------------------------------------+
035.    public  :
036. //+------------------------------------------------------------------+
037.            C_AdjustTemplate(const string szFile, const bool bFirst = false)
038.                    :m_maxIndex(0),
039.                     m_szFileName(szFile),
040.                     m_bFirst(bFirst),
041.                     m_FileIn(INVALID_HANDLE),
042.                     m_FileOut(INVALID_HANDLE)
043.                    {
044.                            ResetLastError();                               
045.                            if (m_bFirst)
046.                            {
047.                                    int handle = FileOpen(m_szFileName, FILE_TXT | FILE_WRITE);
048.                                    FileWriteString(handle, IdeRad);
049.                                    FileClose(handle);
050.                            }
051.                            if ((m_FileIn = FileOpen(m_szFileName, FILE_TXT | FILE_READ)) == INVALID_HANDLE)        SetUserError(C_Terminal::ERR_FileAcess);
052.                            if ((m_FileOut = FileOpen(m_szFileName + "_T", FILE_TXT | FILE_WRITE)) == INVALID_HANDLE) SetUserError(C_Terminal::ERR_FileAcess);
053.                    }
054. //+------------------------------------------------------------------+
055.            ~C_AdjustTemplate()
056.                    {
057.                            FileClose(m_FileIn);
058.                            FileClose(m_FileOut);
059.                            FileMove(m_szFileName + "_T", 0, m_szFileName, FILE_REWRITE);
060.                            ArrayResize(m_szName, 0);
061.                            ArrayResize(m_szFind, 0);
062.                            ArrayResize(m_szReplace, 0);
063.                    }
064. //+------------------------------------------------------------------+
065.            void Add(const string szName, const string szFind, const string szReplace)
066.                    {
067.                            m_maxIndex++;
068.                            ArrayResize(m_szName, m_maxIndex);
069.                            ArrayResize(m_szFind, m_maxIndex);
070.                            ArrayResize(m_szReplace, m_maxIndex);
071.                            m_szName[m_maxIndex - 1] = szName;
072.                            m_szFind[m_maxIndex - 1] = szFind;
073.                            m_szReplace[m_maxIndex - 1] = szReplace;
074.                    }
075. //+------------------------------------------------------------------+
076.            string Get(const string szName, const string szFind)
077.                    {
078.                            for (int c0 = 0; c0 < m_maxIndex; c0++) if ((m_szName[c0] == szName) && (m_szFind[c0] == szFind)) return m_szReplace[c0];
079.                            
080.                            return NULL;
081.                    }
082. //+------------------------------------------------------------------+
083.            void Execute(void)
084.                    {
085.                            string sz0, tmp, res[];
086.                            int count0 = 0, i0;
087.                                                            
088.                            if ((m_FileIn != INVALID_HANDLE) && (m_FileOut != INVALID_HANDLE)) while ((!FileIsEnding(m_FileIn)) && (_LastError == ERR_SUCCESS))
089.                            {
090.                                    sz0 = FileReadString(m_FileIn);
091.                                    if (sz0 == "<object>") count0 = 1;
092.                                    if (sz0 == "</object>") count0 = 0;
093.                                    if (count0 > 0) if (StringSplit(sz0, '=', res) > 1)
094.                                    {
095.                                            if ((m_bFirst) && ((res[0] == "bmpfile_on") || (res[0] == "bmpfile_off")))
096.                                                    sz0 = res[0] + "=\\Indicators\\Replay\\Chart Trade.ex5::" + def_PATH_BTN + res[1];
097.                                            i0 = (count0 == 1 ? 0 : i0);
098.                                            for (int c0 = 0; (c0 < m_maxIndex) && (count0 == 1); i0 = c0, c0++) count0 = (res[1] == (tmp = m_szName[c0]) ? 2 : count0);
099.                                            for (int c0 = i0; (c0 < m_maxIndex) && (count0 == 2); c0++) if ((res[0] == m_szFind[c0]) && (tmp == m_szName[c0]))
100.                                            {
101.                                                    if (StringLen(m_szReplace[c0])) sz0 =  m_szFind[c0] + "=" + m_szReplace[c0];
102.                                                    else m_szReplace[c0] = res[1];
103.                                            }
104.                                    }
105.                                    FileWriteString(m_FileOut, sz0 + "\r\n");
106.                            };
107.                    }
108. //+------------------------------------------------------------------+
109. };
110. //+------------------------------------------------------------------+

Quellcode der Klasse C_AdjustTemplate

Anfängern mag der Code seltsam erscheinen. Obwohl der Code recht aufwändig ist, erspart er uns das Portieren von Dateien, wie wir es im vorherigen Artikel getan haben. Hier fügen wir alles in die ausführbare Datei ein. Aber es ist natürlich nicht kostenlos, alles hat seinen Preis. Die Hauptschwierigkeit besteht darin, dass wir das Verzeichnis oder den Namen der ausführbaren Datei nicht mehr ändern können, nachdem sie kompiliert wurde. Wir können dies tun, aber dazu müssen wir andere Dinge tun, und ich werde jetzt nicht den ganzen Prozess erklären.

Schauen Sie sich die Definitionen in den Zeilen 6 bis 13 genau an. Im Grunde ist uns hier alles schon bekannt. Sehen Sie sich Zeile 13 genau an. Dies ist eine Vorlage. Die gleiche Vorlage, die wir zuvor verwendet haben. Jetzt ist sie nicht mehr eine separate Datei, sondern wird Teil der ausführbaren Datei. Im Folgenden wird beschrieben, wie Sie dies tun können.

In den Zeilen 15 bis 20 nehmen wir wie üblich Definitionen vor. Dies wird gemacht, wenn wir Bilder oder Töne hinzufügen wollen. Aber in Zeile 21 haben wir etwas anderes, etwas, das in Codes wirklich selten ist. Das liegt daran, dass wir in der MQL5-Programmierung normalerweise keine Aliase verwenden, wenn wir Ressourcen in ausführbare Dateien einbinden wollen. Sie können sich eine Vorstellung davon machen, was passieren wird, wenn Sie sich die RESSOURCEN in der Dokumentation ansehen. Daneben müssen wir aber noch einige andere Details klären. Nur auf diese Weise können Sie alles verstehen.

Aliasnamen sind in einigen Sprachen sehr verbreitet. In Visual Basic oder VBA (Visual Basic for Applications), das häufig in Excel verwendet wird, werden diese Aliase beispielsweise verwendet, um auf Dinge auf eine etwas andere Weise zuzugreifen. Wenn wir auf eine Ressource zugreifen, verwenden wir in der Regel „::“, was ein Bereichsauflöser ist. Beim Zugriff auf eine Ressource wird mit dem Namen der Ressourcendefinition gearbeitet. Das mag kompliziert erscheinen, aber in Wirklichkeit ist es viel einfacher. Um dies zu verstehen, sehen Sie sich den folgenden Code an:

01. #define def_BTN_BUY  "Images\\Market Replay\\Chart Trade\\BUY.bmp"
02. #define def_BTN_SELL "Images\\Market Replay\\Chart Trade\\SELL.bmp"
03. #resource "\\" + def_BTN_BUY
04. #resource "\\" + def_BTN_SELL
05. //+------------------------------------------------------------------+
06. int OnInit()
07. {
08.     long id;
09.     string sz;
10.     
11.     ObjectCreate(id = ChartID(), sz = IntegerToString(ObjectsTotal(id)), OBJ_BITMAP_LABEL, 0, 0, 0);
12.     ObjectSetString(id, sz, OBJPROP_BMPFILE, 0, "::" + def_BTN_BUY);
13.     ResourceSave("::" + def_BTN_SELL, "BTN_SELL.bmp");
14.     ObjectSetString(id, sz, OBJPROP_BMPFILE, 1, "\\Files\\BTN_SELL.bmp");
15.     
16.     return INIT_SUCCEEDED;
17. }
18. //+------------------------------------------------------------------+

Fragment 01 - Beispiel für die Verwendung

In diesem Fragment 01 können wir sehen und besser verstehen, wie alles auf der ersten Ebene der Ressourcennutzung funktioniert. Vergessen Sie an dieser Stelle die Klasse C_AdjustTemplate. Zunächst wollen wir lernen, wie man Ressourcen richtig einsetzt, um die Portierung unseres bereits kompilierten Codes zu erleichtern.

In den Zeilen 1 und 2 definieren wir zwei Zeichenketten. Der Inhalt, auf den diese Zeichenketten verweisen, wird vom Compiler aufgrund der Zeilen 3 und 4 kompiliert und in die ausführbare Datei eingebaut. Bis zu diesem Punkt haben wir nichts Kompliziertes, wir arbeiten auf die übliche Weise.

In Zeile 11 geben wir an, dass wir ein Objekt erstellen werden. In diesem Fall handelt es sich um eine Bitmap. Auch hier nichts Besonderes. Doch nun kommt die erste Phase, in der wir die in die ausführbare Datei eingebetteten Ressourcen verwenden werden. In Zeile 12 geben wir mit „::“ an, dass wir eine Ressource verwenden werden. In diesem Fall handelt es sich um die Ressource, die in der ausführbaren Datei vorhanden ist. Wir könnten uns einem anderen Programm zuwenden, aber der Einfachheit halber wollen wir uns zunächst mit diesem einfacheren Konzept befassen. Beim Lesen von Zeile 12 wird der Compiler Folgendes verstehen:

12.     ObjectSetString(id, sz, OBJPROP_BMPFILE, 0, "::Images\\Market Replay\\Chart Trade\\BUY.bmp");

Wenn Sie sich jedoch den Inhalt der Textzeichenfolgen im Objekt ansehen, werden Sie nicht das sehen, was oben gezeigt wird, sondern etwas anderes. Lassen Sie uns zunächst zu den Grundlagen zurückkehren, denn es ist notwendig, das Wesen der Dinge zu verstehen.

Diese Zeile 12 war also leicht zu verstehen, aber was ist mit den folgenden Zeilen? Hier beginnen die Unterschiede. Sie werden diese Dinge in den Codes normalerweise nicht sehen, aber es ist wichtig, sie zu verstehen, um den Chart Trade Indikator wirklich zu verstehen.

In Zeile 13 wird die angegebene Ressource unter dem angegebenen Namen und am angegebenen Ort gespeichert. Der Compiler wird die Zeile 13 wie folgt interpretieren:

13.     ResourceSave("::Images\\Market Replay\\Chart Trade\\SELL.bmp", "\\Files\\BTN_SELL.bmp");

Auch hier verwende ich nur das einfachste Konzept. Dieses Mal sind die Dinge ein wenig komplizierter. Achten Sie beim Aufruf von ResourceSave auf Folgendes: Die in der ausführbaren Datei vorhandene Ressource wird als reguläre Datei gespeichert. Dies entspräche im Wesentlichen der Verwendung der Funktion FileMove. Zeile 13 kann also wie folgt verstanden werden:

FileMove("::Images\\Market Replay\\Chart Trade\\SELL.bmp", 0, "\\Files\\BTN_SELL.bmp", FILE_REWRITE);

So wird es in der Praxis funktionieren.

In Zeile 14 greifen wir nicht mehr auf die interne Ressource der ausführbaren Datei zu, sondern verwenden diese indirekt. Das liegt daran, dass wir sie in Zeile 13 in einer Datei gespeichert haben und nun in Zeile 14 auf sie verweisen. Bitte beachten Sie die folgenden Hinweise. Im Gegensatz zu Zeile 12 verwenden wir in Zeile 14 tatsächlich eine physische Datei auf der Festplatte. Sie verbleibt dort, bis sie entfernt oder geändert wird.

Der in den Zeilen 13 und 14 gezeigte Ansatz ist in MQL5-Programmen nicht sehr verbreitet. Normalerweise führen wir eine Ressource in einer ausführbaren Datei aus und verwenden sie direkt in dieser Datei. So machen wir es normalerweise. Wie ich bereits erwähnt habe, ist die Situation hier jedoch etwas komplizierter. Gleichzeitig ist es aber auch interessanter. Sie müssen nicht in jede Ihrer ausführbaren Dateien Ressourcen einfügen. Dies würde die Standardisierung erheblich erschweren. Wir können eine in der Programmierung sehr verbreitete Technik anwenden: Eine Datei für die Ressourcen verwenden. Im Falle von Windows befindet sie sich in der Regel in einer DLL. Dies würde eine Art von Standardisierung ermöglichen.

Etwas Ähnliches kann in MQL5 gemacht werden. Aber bedenken Sie, dass eine Art von Code standardisiert werden muss. Andernfalls erzeugen Sie eine ungeheure Menge an nutzlosen Daten.

Wie macht man das in MQL5? Es ist ganz einfach. Geben Sie einfach den Dateinamen zu dem Zeitpunkt an, zu dem Sie die Ressource verwenden wollen. Erinnern Sie sich daran, dass ich sagte, dass die Dinge viel komplizierter sind? Genau das ist geschehen. Der Name der ausführbaren Datei wurde ignoriert. Nehmen wir an, Sie haben eine ausführbare Datei namens ICONS.LIB, die dieselben Bilder wie Fragment 01 enthält, und diese ausführbare Datei ICONS.LIB befindet sich im Stammverzeichnis des Ordners „Indicators“. Um dasselbe zu tun, aber mit ICONS.LIB, brauchen wir den folgenden Code:

01. //+------------------------------------------------------------------+
02. #define def_BTN_BUY  "Images\\Market Replay\\Chart Trade\\BUY.bmp"
03. #define def_BTN_SELL "Images\\Market Replay\\Chart Trade\\SELL.bmp"
04. #define def_LIB      "\\Indicators\\Icons.Lib"
05. //+------------------------------------------------------------------+
06. int OnInit()
07. {
08.     long id;
09.     string sz;
10.     
11.     ObjectCreate(id = ChartID(), sz = IntegerToString(ObjectsTotal(id)), OBJ_BITMAP_LABEL, 0, 0, 0);
12.     ObjectSetString(id, sz, OBJPROP_BMPFILE, 0, def_LIB + "::" + def_BTN_BUY);
13.     ResourceSave(def_LIB + "::" + def_BTN_SELL, "BTN_SELL.bmp");
14.     ObjectSetString(id, sz, OBJPROP_BMPFILE, 1, "\\Files\\BTN_SELL.bmp");
15.             
16.     return INIT_SUCCEEDED;
17. }
18. //+------------------------------------------------------------------+

Fragment 02

Beachten Sie, dass wir jetzt die ausführbare Datei definieren. Daher gelten alle oben genannten Erklärungen für dieses Fragment 02. Natürlich müssen wir auch verstehen, wie der Compiler den Code sieht. Schauen wir uns also den folgenden Code an.

01. //+------------------------------------------------------------------+
02. int OnInit()
03. {
04.     long id;
05.     string sz;
06.     
07.     ObjectCreate(id = ChartID(), sz = IntegerToString(ObjectsTotal(id)), OBJ_BITMAP_LABEL, 0, 0, 0);
08.     ObjectSetString(id, sz, OBJPROP_BMPFILE, 0, "\\Indicators\\Icons.Lib::Images\\Market Replay\\Chart Trade\\BUY.bmp");
09.     ResourceSave("\\Indicators\\Icons.Lib::Images\\Market Replay\\Chart Trade\\SELL.bmp", "\\Files\\BTN_SELL.bmp");
10.     ObjectSetString(id, sz, OBJPROP_BMPFILE, 1, "\\Files\\BTN_SELL.bmp");
11.             
12.     return INIT_SUCCEEDED;
13. }
14. //+------------------------------------------------------------------+

Fragment 02 (Erweiterter Code)

Es ist unwahrscheinlich, dass jemand einen solchen erweiterten Code schreibt. Der Grund dafür ist, dass die Pflege eines solchen Codes sehr viel arbeitsintensiver ist. Aber für den Compiler wird alles gleich funktionieren.

Nachdem ich nun den einfacheren Teil erklärt habe, lassen Sie uns zu den Aliasen zurückkehren. Wenn Sie hier in MQL5 Aliase verwenden, „zwingen“ wir den Compiler, ein wenig anders zu handeln. Es wird ein Datenkomprimierungssystem verwendet, um die Größe der Dateien zu reduzieren. Gleichzeitig wird jeder direkte Zugriff auf die Ressource ignoriert. Das heißt, Sie können die Definition NICHT direkt verwenden. Es ist sehr wichtig, dass Sie dies verstehen. Sie können der ausführbaren Datei alles hinzufügen, aber Sie können das, was Sie hinzugefügt haben, NICHT direkt verwenden.

Um etwas zu verwenden, das der ausführbaren Datei über einen Alias hinzugefügt wurde, müssen Sie diesen Alias verwenden, nicht die Ressource. Erscheint Ihnen das verwirrend? Lassen Sie uns versuchen, das herauszufinden.

01. #resource "\\Images\\euro.bmp" as bitmap euro[][] 
02. #resource "\\Images\\dollar.bmp" 
03. //+------------------------------------------------------------------+ 
04. void Image(string name,string rc,int x,int y) 
05. { 
06.     ObjectCreate(0, name, OBJ_BITMAP_LABEL, 0, 0, 0); 
07.     ObjectSetInteger(0, name, OBJPROP_XDISTANCE, x); 
08.     ObjectSetInteger(0, name, OBJPROP_YDISTANCE, y); 
09.     ObjectSetString(0, name, OBJPROP_BMPFILE, rc); 
10. } 
11. //+------------------------------------------------------------------+ 
12. void OnStart() 
13. { 
14.     for(int x = 0; x < ArrayRange(euro, 1); x++) 
15.             euro[ArrayRange(euro, 1) / 2][x] = 0xFFFF0000; 
16.     ResourceCreate("euro_icon", euro, ArrayRange(euro, 1), ArrayRange(euro, 0), 0, 0, ArrayRange(euro, 1), COLOR_FORMAT_ARGB_NORMALIZE); 
17.     Image("Euro" , "::euro_icon", 10, 40); 
18.     Image("USD", "::Images\\dollar.bmp", 15 + ArrayRange(euro,1), 40); 
19.     Image("E2", "::Images\\euro.bmp", 20 + ArrayRange(euro, 1) * 2, 40);
20. }
21. //+------------------------------------------------------------------+ 

In der Dokumentation enthaltener Quellcode

Der obige Quellcode demonstriert genau eine der Möglichkeiten, Ressourcen zu verwenden, wenn wir Aliase verwenden. Der Alias wurde in Zeile 1 definiert. Von diesem Zeitpunkt an sollten wir den Ressourcennamen nicht mehr verwenden. Wir sollten seinen Alias verwenden. Wenn wir also Zeile 19 ausführen, erhalten wir einen Fehler, weil wir versuchen, den Ressourcennamen zu verwenden, obwohl wir eigentlich seinen Alias verwenden sollten.

In dem oben gezeigten Code gibt der Alias an, welche Struktur für die Modellierung der Daten zu verwenden ist. Es könnte jeder der möglichen sein. Wir könnten auch Werkzeuge verwenden, um einen Datentyp in einen anderen umzuwandeln. Auf diese Weise können Sie viel mehr Dinge tun, als sich viele Menschen normalerweise vorstellen. Der Unterschied zwischen diesem aus der Dokumentation entlehnten Code und dem, den ich verwende, liegt in der Art der Informationen.

In diesem Code aus der Dokumentation verwenden wir das System, um ein Beispiel für die Verwendung von Aliasen zu demonstrieren. In diesem Fall ist es jedoch nicht erforderlich, die vorhandenen Daten als Ressourcen der ausführbaren Datei zu speichern. Der Grund dafür ist, dass es sich um Daten handelt, die wir direkt verwenden können. Was aber, wenn die Ressource eine Vorlage ist? Nach den Regeln von MetaTrader 5 ist es nicht möglich, Vorlagen als Ressourcen zu verwenden. Daher muss die Vorlage aus der ausführbaren Datei extrahiert werden, damit sie keine Ressource mehr ist, sondern eine normale Datei wird.

Dies kann auf die gleiche Weise wie im Code in Fragment 01 erreicht werden. In Zeile 13 wird das Bild, das in der ausführbaren Datei als Ressource enthalten ist, in eine Datei umgewandelt, damit es im Objekt in Zeile 14 verwendet werden kann.

In der Theorie funktioniert das, aber in der Praxis ist es etwas komplizierter. Wie Sie sehen, verwenden wir in Zeile 13 von Code 01 den Ressourcennamen und nicht den Alias. Die Art und Weise, wie wir mit Aliasen arbeiten, unterscheidet sich ein wenig von der Arbeit mit Ressourcen, aber das ist nicht unser Hauptproblem. Unser Hauptproblem besteht darin, dass wir Vorlagen nicht als Ressourcen in ausführbare Dateien aufnehmen können. Konzentrieren wir uns also auf den Code der Klasse C_AdjustTemplate. Dies wird Ihnen helfen zu verstehen, wie es mir gelungen ist, diese beiden Probleme zu überwinden: die Unmöglichkeit, Vorlagen in ausführbare Dateien einzubinden und die Möglichkeit, eine in einer ausführbaren Datei gespeicherte Vorlage zu verwenden.

In Zeile 13 der Klasse C_AdjustTemplate definiere ich also die Vorlage, die ich verwenden werde. Es handelt sich um dieselbe Vorlage, die bereits in früheren Artikeln über den Charthandel verwendet wurde. Beachten Sie aber, dass ich in Zeile 21, wo ich die Definition in eine Ressource umwandle, die Ressource nicht verwende. Ich verwende dazu jedoch einen Alias. Dieser Alias basiert auf dem Datentyp String, sodass der gesamte in der Vorlagendatei enthaltene Code der ausführbaren Datei hinzugefügt wird, als wäre er eine lange Zeichenkette. Diese Linie ist jedoch wie eine große Konstante. Es ist sehr wichtig, dies gut zu verstehen. Mit anderen Worten, um es noch deutlicher zu machen: Das Vorlagendatei wird vom System als Konstante wahrgenommen, obwohl sie es in Wirklichkeit nicht ist.

Die Vorlagendatei wird als Textstring mit dem Alias IdeRad innerhalb der ausführbaren Datei angenommen. Wir können anfangen, darüber nachzudenken, wie wir damit arbeiten können.

Das erste, was wir verstehen müssen, ist, dass wir diese Vorlage mit dem Alias IdeRad nicht direkt im Objekt OBJ_CHART verwenden können. Das ist unmöglich. Wir müssen diese Daten wieder in eine Datei umwandeln. Die Funktion ResourceSave kann einen solchen Fall jedoch nicht behandeln, da die Vorlage KEINE RESOURCE ist. Aber wir können etwas anderes tun. Deshalb haben wir einen Code in den Zeilen 45 bis 50.

Anmerkung: Ich verwende diese Methode nicht, weil sie erforderlich ist, sondern weil ich den Code der Klasse, der bereits funktioniert, nicht ändern möchte. Wir könnten den Inhalt der Vorlage direkt mit dem Alias IdeRad lesen und die erforderlichen Änderungen vornehmen. Dies würde jedoch die bereits erstellte und getestete Logik verkomplizieren.

Lassen Sie uns also herausfinden, was in den Zeilen 45 bis 50 passiert. Wenn wir einen Konstruktor aufrufen, um eine Vorlage zu bearbeiten, geben wir die Aufrufebene sowie den Namen der Datei an, auf die zugegriffen wird. Wenn dies der erste Aufruf ist, wird in Zeile 45 die Vorlagendatei erstellt. Die Datei wird in Zeile 47 erstellt und in Zeile 49 geschlossen. Würden nur diese beiden Zeilen existieren, wäre die Vorlage leer. Aber in Zeile 48 geschieht das Wunder.

Hier fügen wir den gesamten Inhalt der Zeile in die Datei ein. Wie lautet diese Zeile? Es ist die in der Variablen IdeRad. Ups, Moment mal. Wollen Sie damit sagen, dass wir die Vorlage innerhalb der ausführbaren Datei als Ressource speichern? Um Probleme zu vermeiden, weisen wir ihm einen Alias zu. Wenn wir dann den Inhalt wiederherstellen wollen, nehmen wir den Inhalt dieses Alias und legen ihn in einer Datei ab. Wirklich? Ja, genau. Jetzt werden Sie sich vielleicht fragen: Warum hat noch niemand daran gedacht, dies zu tun, oder gezeigt, wie man es macht? Nun, ich weiß es nicht. Vielleicht, weil noch niemand versucht hat, es zu tun. Vielleicht konnte sich aber auch einfach niemand vorstellen, wie man es machen sollte.

Nachdem die Datei in Zeile 49 geschlossen wurde, ist der Rest des Prozesses derselbe wie zuvor beschrieben. Dies liegt daran, dass MetaTrader 5 nun nicht mehr mit einer Ressource, sondern mit einer Datei auf der Festplatte umgehen muss.

Schließlich gibt es noch ein kleines Detail, das ich nicht übersehen kann. Viele Menschen stellen sich vielleicht etwas vor oder versuchen, die Daten zu manipulieren, um zu verstehen, was passiert, wenn sie mit dem Chart Trade Indikator interagieren. Die Frage bezieht sich auf die Tasten. Wenn Sie den Indikator in einem Chart ausführen, werden Sie nicht verstehen, wie auf die Schaltflächen zugegriffen wird. Das wird nur passieren, wenn Sie wenig Erfahrung mit MQL5 haben. Die Bilder, die als Schaltflächen angegeben wurden, sind nirgends zu finden. Wenn Sie sich den Inhalt der Vorlage ansehen, werden Sie etwas wie folgt sehen:

bmpfile_on=\Indicators\Replay\Chart Trade.ex5::Images\Market Replay\Chart Trade\BUY.bmp

bmpfile_off=\Indicators\Replay\Chart Trade.ex5::Images\Market Replay\Chart Trade\BUY.bmp

Das ergibt keinen Sinn. Keinen, wenn Sie gerade erst anfangen, MQL5 zu lernen. Wenn Sie jedoch das gleiche Muster in einem Chart wiedergeben, werden Sie feststellen, dass es korrekt angezeigt wird. Wie ist das möglich? Wo befinden sich die Bilder, auf die verwiesen wird? Das ist die Frage. Die Bilder befinden sich innerhalb der ausführbaren Datei. Das heißt, die Vorlage gibt direkt das zu verwendende Bild an. Dies ist dasselbe wie in dem zuvor vorgestellten Code und in der Erweiterung des Fragments 02. Der fragliche Code ist genau die Zeile 08, die ich unten noch einmal zeige, damit Sie sie besser verstehen können:

08.     ObjectSetString(id, sz, OBJPROP_BMPFILE, 0, "\\Indicators\\Icons.Lib::Images\\Market Replay\\Chart Trade\\BUY.bmp");

Beachten Sie, wie ähnlich die Zeilen sowohl im Code als auch in der Vorlage sind. Dies zeigt, dass MetaTrader 5 und die Sprache MQL5 noch nicht ausreichend erforscht wurden. Glauben Sie mir, MetaTrader 5 und MQL5 können viel mehr, als Sie sich vorstellen können. Trotz all der Einschränkungen, von denen viele Menschen sprechen, können wir viel, viel mehr tun, als wir denken. Ohne auf andere Sprachen zurückzugreifen.


Schlussfolgerung

In diesem Artikel habe ich gezeigt, wie man das macht, was viele Leute für unmöglich halten: Vorlagen innerhalb einer ausführbaren Datei verwenden. Obwohl dieses Wissen hier als etwas einfach zu Verstehendes dargestellt wird, können Sie damit in Wirklichkeit viel mehr tun. Nun, mit einer DLL können wir die gleiche Arbeit, die hier gezeigt wird, viel umfangreicher durchführen.

Es hängt alles von der Kreativität, den Fähigkeiten und der Persönlichkeit des Programmierers ab. Es gibt Leute, die sagen, dass man etwas nicht machen kann. Andere sagen, es gäbe keine Mittel. Aber es gibt Menschen, die sich bemühen und Erfolg haben. Gehören Sie nicht zu den Menschen, die aufgeben, wenn sie vor einer Herausforderung stehen. Sei derjenige, der die Probleme löst. Mein Motto ist:

Ein echter Profi-Programmierer: Lösen Sie ein Problem, während andere es nur sehen.


Übersetzt aus dem Portugiesischen von MetaQuotes Ltd.
Originalartikel: https://www.mql5.com/pt/articles/11737

Beigefügte Dateien |
Indicators.zip (149.64 KB)
Risikomanager für den algorithmischen Handel Risikomanager für den algorithmischen Handel
Ziel dieses Artikels ist es, die Notwendigkeit des Einsatzes eines Risikomanagers zu beweisen und die Prinzipien der Risikokontrolle im algorithmischen Handel in einer eigenen Klasse zu implementieren, damit jeder die Wirksamkeit des Ansatzes der Risikostandardisierung im Intraday-Handel und bei Investitionen auf den Finanzmärkten überprüfen kann. In diesem Artikel werden wir eine Risikomanager-Klasse für den algorithmischen Handel erstellen. Dies ist eine logische Fortsetzung des vorangegangenen Artikels, in dem wir die Erstellung eines Risikomanagers für den manuellen Handel besprochen haben.
MQL5-Assistent-Techniken, die Sie kennen sollten (Teil 31): Auswahl der Verlustfunktion MQL5-Assistent-Techniken, die Sie kennen sollten (Teil 31): Auswahl der Verlustfunktion
Die Verlustfunktion ist die wichtigste Kennzahl für Algorithmen des maschinellen Lernens, die eine Rückmeldung für den Trainingsprozess liefert, indem sie angibt, wie gut ein bestimmter Satz von Parametern im Vergleich zum beabsichtigten Ziel funktioniert. Wir untersuchen die verschiedenen Formate dieser Funktion in einer nutzerdefinierten MQL5-Assistenten-Klasse.
Entwicklung eines Expertenberaters für mehrere Währungen (Teil 10): Erstellen von Objekten aus einer Zeichenkette Entwicklung eines Expertenberaters für mehrere Währungen (Teil 10): Erstellen von Objekten aus einer Zeichenkette
Der EA-Entwicklungsplan umfasst mehrere Stufen, wobei die Zwischenergebnisse in der Datenbank gespeichert werden. Sie können von dort nur als Zeichenketten oder Zahlen wieder abgerufen werden, nicht als Objekte. Wir brauchen also eine Möglichkeit, die gewünschten Objekte im EA anhand der aus der Datenbank gelesenen Strings neu zu erstellen.
Erstellen eines integrierten MQL5-Telegram Expert Advisors (Teil 2): Senden von Signalen von MQL5 an Telegram Erstellen eines integrierten MQL5-Telegram Expert Advisors (Teil 2): Senden von Signalen von MQL5 an Telegram
In diesem Artikel erstellen wir einen in MQL5-Telegram integrierten Expert Advisor, der Moving Average Crossover Signale an Telegram sendet. Wir erläutern den Prozess der Erzeugung von Handelssignalen aus gleitenden Durchschnittsübergängen, die Implementierung des erforderlichen Codes in MQL5 und die Sicherstellung der nahtlosen Integration. Das Ergebnis ist ein System, das Handelswarnungen in Echtzeit direkt an Ihren Telegram-Gruppenchat sendet.