English Русский Español 日本語 Português
preview
Entwicklung eines Replay Systems (Teil 44): Chart Trade Projekt (III)

Entwicklung eines Replay Systems (Teil 44): Chart Trade Projekt (III)

MetaTrader 5Beispiele | 9 September 2024, 10:49
37 0
Daniel Jose
Daniel Jose

Einführung

Im vorherigen Artikel Entwicklung eines Replay Systems (Teil 43): Chart Trade Project (II) habe ich erklärt, wie Sie Vorlagedaten manipulieren können, um sie in OBJ_CHART zu verwenden. In diesem Artikel habe ich das Thema nur umrissen, ohne auf Einzelheiten einzugehen, da die Arbeit in dieser Version sehr vereinfacht war. Dies geschah, um die Erklärung des Inhalts zu erleichtern, denn trotz der scheinbaren Einfachheit vieler Dinge waren einige davon nicht so offensichtlich, und ohne das Verständnis des einfachsten und grundlegendsten Teils wäre man nicht in der Lage, das gesamte Bild wirklich zu verstehen.

Auch wenn dieser Code funktioniert (wie wir bereits gesehen haben), können wir damit einige Dinge nicht tun. Mit anderen Worten, die Durchführung bestimmter Aufgaben wird sehr viel schwieriger sein, wenn die Datenmodellierung nicht verbessert wird. Die erwähnte Verbesserung beinhaltet eine etwas komplexere Kodierung, aber das verwendete Konzept ist dasselbe. Es ist nur so, dass der Code etwas komplizierter sein wird.

Neben dieser kleinen Tatsache werden wir noch ein weiteres Problem lösen. Wenn Sie bemerkt haben, und ich habe dies auch in dem Artikel erwähnt, ist dieser Code nicht sehr effizient, da er meiner Meinung nach zu viele Aufrufe enthält, um Dinge einzurichten. Um dieses Problem zu lösen, werden wir einige kleine Änderungen am Code vornehmen, die die Anzahl der Aufrufe drastisch reduzieren und gleichzeitig eine angemessenere Datenmodellierung ermöglichen.


Die Geburt einer neuen Klasse: C_AdjustTemplate

Um die notwendigen Verbesserungen umzusetzen, müssen wir eine neue Klasse erstellen. Der vollständige Code ist nachstehend aufgeführt:

01. //+------------------------------------------------------------------+
02. #property copyright "Daniel Jose"
03. //+------------------------------------------------------------------+
04. #include "../Auxiliar/C_Terminal.mqh"
05. //+------------------------------------------------------------------+
06. class C_AdjustTemplate
07. {
08.     private :
09.             string m_szName[];
10.             string m_szFind[];
11.             string m_szReplace[];
12.             string m_szFileName;
13.             int m_maxIndex;
14.             int m_FileIn;
15.             int m_FileOut;
16.             bool m_bSimple;
17.     public  :
18. //+------------------------------------------------------------------+
19.             C_AdjustTemplate(const string szFileNameIn, string szFileNameOut = NULL)
20.                     :m_maxIndex(0),
21.                      m_szFileName(szFileNameIn),
22.                      m_FileIn(INVALID_HANDLE),
23.                      m_FileOut(INVALID_HANDLE)
24.                     {
25.                             ResetLastError();
26.                             if ((m_FileIn = FileOpen(szFileNameIn, FILE_TXT | FILE_READ)) == INVALID_HANDLESetUserError(C_Terminal::ERR_FileAcess);
27.                             if (_LastError == ERR_SUCCESS)
28.                             {
29.                                     if (!(m_bSimple = (StringLen(szFileNameOut) > 0))) szFileNameOut = szFileNameIn + "_T";                                                 
30.                                     if ((m_FileOut = FileOpen(szFileNameOut, FILE_TXT | FILE_WRITE)) == INVALID_HANDLE) SetUserError(C_Terminal::ERR_FileAcess);
31.                             }
32.                     }
33. //+------------------------------------------------------------------+
34.             ~C_AdjustTemplate()
35.                     {
36.                             FileClose(m_FileIn);
37.                             if (m_FileOut != INVALID_HANDLE)
38.                             {
39.                                     FileClose(m_FileOut);
40.                                     if ((!m_bSimple) && (_LastError == ERR_SUCCESS)) FileMove(m_szFileName + "_T", 0, m_szFileName, FILE_REWRITE);
41.                                     if ((!m_bSimple) && (_LastError != ERR_SUCCESS)) FileDelete(m_szFileName + "_T");
42.                             }
43.                             ArrayResize(m_szName, 0);
44.                             ArrayResize(m_szFind, 0);
45.                             ArrayResize(m_szReplace, 0);
46.                     }
47. //+------------------------------------------------------------------+
48.             void Add(const string szName, const string szFind, const string szReplace)
49.                     {
50.                             m_maxIndex++;
51.                             ArrayResize(m_szName, m_maxIndex);
52.                             ArrayResize(m_szFind, m_maxIndex);
53.                             ArrayResize(m_szReplace, m_maxIndex);
54.                             m_szName[m_maxIndex - 1] = szName;
55.                             m_szFind[m_maxIndex - 1] = szFind;
56.                             m_szReplace[m_maxIndex - 1] = szReplace;
57.                     }
58. //+------------------------------------------------------------------+
59.             string Get(const string szName, const string szFind)
60.                     {
61.                             for (int c0 = 0; c0 < m_maxIndex; c0++) if ((m_szName[c0] == szName) && (m_szFind[c0] == szFind)) return m_szReplace[c0];
62.                             
63.                             return NULL;
64.                     }
65. //+------------------------------------------------------------------+
66.             void Execute(void)
67.                     {
68.                             string sz0, tmp, res[];
69.                             int count0 = 0, i0;
70.                             
71.                             if ((m_FileIn != INVALID_HANDLE) && (m_FileOut != INVALID_HANDLE)) while ((!FileIsEnding(m_FileIn)) && (_LastError == ERR_SUCCESS))
72.                             {
73.                                     sz0 = FileReadString(m_FileIn);
74.                                     if (sz0 == "<object>") count0 = 1;
75.                                     if (sz0 == "</object>") count0 = 0;
76.                                     if (count0 > 0) if (StringSplit(sz0, '=', res) > 1)
77.                                     {
78.                                             i0 = (count0 == 1 ? 0 : i0);
79.                                             for (int c0 = 0; (c0 < m_maxIndex) && (count0 == 1); i0 = c0, c0++) count0 = (res[1] == (tmp = m_szName[c0]) ? 2 : count0);
80.                                             for (int c0 = i0; (c0 < m_maxIndex) && (count0 == 2); c0++) if ((res[0] == m_szFind[c0]) && (tmp == m_szName[c0]))
81.                                             {
82.                                                     if (StringLen(m_szReplace[c0])) sz0 =  m_szFind[c0] + "=" + m_szReplace[c0];
83.                                                     else m_szReplace[c0] = res[1];
84.                                             }
85.                                     }
86.                                     FileWriteString(m_FileOut, sz0 + "\r\n");
87.                             };
88.                     }
89. //+------------------------------------------------------------------+
90. };
91. //+------------------------------------------------------------------+

Quellcode: C_AdjustTemplate

Dieser Code enthält genau das, was wir brauchen. Wenn Sie sich diesen Code hier ansehen und den Code der Klasse C_ChartFloatingRAD aus dem vorherigen Artikel betrachten, werden Sie feststellen, dass der Inhalt zwischen den Zeilen 38 und 90 der Klasse C_ChartFloatingRAD auch hier vorhanden ist, aber anders aussieht. Der Grund dafür ist, dass die Datenmodellierung in dieser C_AdjustTemplate-Klasse so erfolgt, dass eine effizientere Ausführung möglich ist. Sie werden dies sehen, wenn der Code der neuen Klasse C_ChartFloatingRAD später in diesem Artikel gezeigt wird. Lassen wir das also für später. Lassen Sie uns zunächst verstehen, was in dieser Klasse C_AdjustTemplate vor sich geht.

Der Code für die Klasse C_AdjustTemplate mag zwar komplex und schwer verständlich erscheinen, ist aber eigentlich recht einfach. Es ist jedoch so konzipiert, dass es als eine einzige Funktion ausgeführt wird, obwohl es mehrere Funktionen hat. Um die ganze Idee wirklich zu verstehen, vergessen Sie, dass Sie mit Code, mit MetaTrader oder MQL5 arbeiten. Wenn Sie sich vorstellen, dass Sie es mit Teilen einer Maschine zu tun haben, wird es leichter sein, alles zu verstehen. Die Klasse C_AdjustTemplate sollte als Vorlage betrachtet werden. Betrachten Sie sie also als eine Vorlagendatei, die wir im vorherigen Artikel besprochen haben.

Wenn Sie auf diese Weise darüber nachdenken, wird klar, was wirklich vor sich geht und warum wir mit dieser Klasse so arbeiten sollten, wie wir es in Zukunft tun werden. Wenn Sie also einen Klassenkonstruktor verwenden, öffnen Sie im Grunde die Vorlage, um mit dem, was darin enthalten ist, zu arbeiten. Wenn Sie einen Destruktor verwenden, ist das so, als würden Sie sagen: „Okay, MetaTrader, du kannst die Vorlage jetzt verwenden.“ Die anderen Funktionen dienen als Werkzeuge zur Anpassung der Vorlage.

Schauen wir uns also an, wie die einzelnen Teile dieser Klasse funktionieren. Beginnen wir mit dem Konstruktor. Es beginnt in Zeile 19, wo wir sehen, dass wir einen Sting angeben müssen, aber wir können optional eine zweite Zeichenkette angeben. Warum wird das so gemacht? Der Grund dafür ist einfach: Überladung Wenn das Überladen nicht möglich wäre, müssten wir zwei Konstruktoren schreiben, aber da es möglich ist, werden wir es verwenden. Tatsächlich ist diese Überladung jedoch nicht normal, sondern für diese Art der Nutzung vorgesehen.

Sobald das erledigt ist, werden zwischen den Zeilen 20 und 23 einige Variablen vorinitialisiert. Dies ist für uns wichtig, denn obwohl der Compiler die Initialisierung implizit vornimmt, ist es immer besser, sie explizit vorzunehmen. Auf diese Weise wissen wir genau, welchen Wert die einzelnen Variablen haben.

Beachten Sie nun die folgende Tatsache: In Zeile 25 wird die Konstante _LastError „zurückgesetzt“. Wenn also vor dem Aufruf des Konstruktors ein Fehler auftritt, sollten Sie ihn überprüfen; andernfalls verlieren Sie den in der Fehlerkonstante angegebenen Wert. Ich habe bereits in früheren Artikeln erklärt, warum Sie auf diese Weise vorgehen müssen; lesen Sie diese Artikel, um weitere Informationen zu erhalten.

In Zeile 26 wird versucht, die Quelldatei zu öffnen, die angegeben werden muss. Wenn dieser Versuch fehlschlägt, wird dies in der Konstante _LastError gemeldet. Wenn es uns gelingt, die Datei zu öffnen, enthält die Konstante _LastError den Wert ERR_SUCCESS, und die in Zeile 27 durchgeführte Prüfung ist erfolgreich, sodass wir mit dem nächsten Schritt fortfahren können.

An dieser Stelle wird in Zeile 29 geprüft, ob ein Name für die Zieldatei angegeben wurde. Wird sie nicht angegeben, wird mit einer temporären Datei gearbeitet, deren Name auf dem Namen der Originaldatei basiert. Wenn wir einen Namen für die Zieldatei haben, können wir Zeile 30 ausführen, die versucht, die Zieldatei zu erstellen. Wenn dieser Versuch fehlschlägt, wird dies in der Konstante _LastError gemeldet. Nach erfolgreicher Ausführung all dieser Schritte enthält die Konstante _LastError den Wert ERR_SUCCESS, und wir haben Dateikennungen. Sie werden zur Manipulation von Dateien verwendet, daher sollten Sie nicht versuchen, irgendetwas Externes mit den Dateien zu tun, bevor die Handles geschlossen sind. Denken Sie daran, dass die Maschine offen ist und dass beim Einschalten Probleme auftreten können.

Gehen wir also den Code in der Reihenfolge durch, in der er bearbeitet werden sollte. Dies bringt uns zu Zeile 34, wo der Code für den Klassen-Destruktor beginnt. Als erstes wird in Zeile 36 die Eingabedatei geschlossen. Anmerkung: Diese Datei wird nur geschlossen, wenn ihr Handle gültig ist. Das heißt, die Datei muss geöffnet worden sein. In Zeile 37 wird geprüft, ob die Ausgabedatei geöffnet ist. Wir tun dies, um die unnötige Ausführung der folgenden Zeilen zu vermeiden.

Wenn also die Zieldatei geöffnet ist, wird in Zeile 40 geprüft, ob ein Name für sie angegeben wurde und ob während des Einrichtungsvorgangs Fehler aufgetreten sind. Wenn alles in Ordnung ist, benennen wir die Datei so um, dass sie mit der Datei übereinstimmt, die vom Rest des Indikators erwartet wird. Wie auch immer, in Zeile 41 löschen wir die temporäre Datei, die wir verwenden, wenn etwas schief geht.

Zwischen den Zeilen 43 und 45 geben wir den zugewiesenen Speicher frei. Solche Dinge sind sehr wichtig, aber viele Menschen vergessen sie zu tun. Nach guter Programmierpraxis sollten Sie, wenn Sie Speicher zuweisen, diesen immer wieder freigeben. Auf diese Weise verbraucht MetaTrader 5 nicht übermäßig und unnötig viele Ressourcen.

Als Nächstes haben wir eine sehr grundlegende und einfache Prozedur, die in Zeile 50 beginnt, wo wir den Zähler inkrementieren und unmittelbar danach Speicher zuweisen, um die Daten zu speichern, die wir später verwenden werden. Diese Zuordnung erfolgt zwischen den Zeilen 51 und 53. Achten Sie auch auf die Zeilen 54 - 56, in denen wir die Informationen speichern werden. Da dieses Verfahren einfach ist, werde ich nicht ins Detail gehen. Die nächste Zeile 59 hat eine interessante Funktion.

Diese Funktion, die in Zeile 59 beginnt, ist zwar ebenfalls sehr einfach, aber sehr interessant. Nicht so sehr wegen dem, was sie tut, sondern wegen dem, wie sie es tut. Sehen Sie sich die Zeile 60 an, die eigentlich die einzige Zeile in dieser Funktion ist. Es gibt eine Schleife, in der wir alle Positionen durchlaufen, die während der in Zeile 50 vorgestellten Funktion hinzugefügt wurden. Die Frage ist, warum sollte ein Programmierer die Informationen lesen wollen, die mit der Funktion in Zeile 50 in die Klasse geschrieben wurden? Es scheint sinnlos zu sein. Wenn man sich nur den Klassencode ansieht, ergibt dies in der Tat keinen Sinn, aber es gibt eine Nuance. Ein kleines Detail, das in Zeile 66 beginnt.

Die Funktion Execute, die in Zeile 66 beginnt, ist äußerst heikel. Der Grund dafür ist, dass viele Dinge schief gehen können. Grundsätzlich können die folgenden Fehler auftreten:

  • Die Eingabedatei konnte nicht gelesen werden;
  • Die Ausgabedatei ist möglicherweise nicht verfügbar;
  • Die Funktion StringSplit kann fehlschlagen.

Jedes dieser Probleme führt dazu, dass sich die Fehlerkonstante ändert. In diesem Fall wird die while-Schleife in Zeile 71 vorzeitig beendet, was dazu führt, dass der gesamte Indikator nicht korrekt funktioniert, wenn er in einem Chart angezeigt wird. Beachten Sie Folgendes: Wenn die Konstante _LastError einen anderen Wert als ERR_SUCCESS enthält, wird die Vorlage zum Zeitpunkt der Ausführung des Destruktors nicht aktualisiert. Und wenn es der erste Aufruf ist, wird er nicht erstellt. Wenn er nicht vorhanden ist, wird der Indikator nicht in das Chart eingefügt. Deshalb ist Execute so wichtig.

Nehmen wir jedoch an, dass alles perfekt funktioniert. Schauen wir uns an, was innerhalb der while-Schleife passiert.

In Zeile 73 lesen wir eine Zeichenkette aus der Quelldatei. Diese Zeichenfolge besteht aus einer vollständigen Zeile. Auf diese Weise fällt es viel leichter, den Rest zu überprüfen. Als Nächstes wird in Zeile 74 geprüft, ob eine Definition eines Objekts eingegeben wird, und in Zeile 75 wird geprüft, ob sie abgeschlossen ist.

Diese Prüfungen sind wichtig, um den Prozess des Lesens und der Anpassung der Vorlage zu beschleunigen. Wenn es sich nicht um ein Objekt handelt, können wir die Informationen einfach in die Ausgabedatei schreiben. Diese Information steht in Zeile 86, bitte beachten Sie dies. Denn jetzt werden wir sehen, wie die ursprüngliche Vorlage angepasst wird, um das zu schaffen, was wir brauchen.

Sobald wir uns innerhalb des Objekts befinden, haben wir eine Prüfung in Zeile 76, die es uns erlaubt, die Zeichenkette „aufzubrechen“. Dieser „Bruch“ findet am Gleichheitszeichen (=) statt, was bedeutet, dass wir einen Parameter für eine Eigenschaft des Objekts definieren. Nun, wenn es stimmt, dass wir eine Eigenschaft definieren, werden diese Prüfungen bestanden, sodass wir Zeile 78 ausführen können. Mit dieser Zeile wird lediglich der temporäre Speicher angepasst. In den folgenden Zeilen stellt sich jedoch eine Frage.

In Zeile 79 durchlaufen wir eine Schleife über alle Daten, die bei den Add-Aufrufen (Methode aus Zeile 48) hinzugefügt wurden. Wenn wir zufällig den Wert des Parameters in der Vorlage finden, bedeutet dies, dass wir es mit dem gewünschten Objekt zu tun haben. Wir speichern den Namen des Objekts vorübergehend und geben an, dass wir die zweite Ebene der Analyse durchführen werden. Da es sich um eine zweite Ebene handelt, wird Zeile 79 innerhalb desselben Objekts nicht noch einmal ausgeführt. Aus diesem Grund müssen wir sicherstellen, dass die Vorlage die gleiche Struktur hat wie im vorherigen Artikel Entwicklung eines Replay System (Teil 43): Chart Trade Project (II). Die Datei muss genau dieselbe sein. Wenn Sie ihn ändern, achten Sie darauf, dass er so nah wie möglich an dem von Ihnen angegebenen Wert bleibt.

Nun, da wir uns bereits in der zweiten Ebene befinden, haben wir in Zeile 80 eine weitere Schleife. Wichtig: Die Schleifen in den Zeilen 79 und 80 werden nie zusammen ausgeführt. Das eine oder das andere wird immer ausgeführt, aber niemals beide gleichzeitig. Die einzige Ausnahme ist der erste Anruf. Es mag seltsam erscheinen, dass die Zeilen 79 und 80 nicht ausgeführt werden, aber das ist tatsächlich der Fall. Wenn jedoch Zeile 80 ausgeführt wird, wird innerhalb der Schleife geprüft, ob die gewünschte Objekteigenschaft vorhanden ist. Beachten Sie, dass der Objektname wichtig ist, deshalb schreiben wir ihn vorübergehend während der Schleife in Zeile 79.

Wenn diese Prüfung ergibt, dass die Objekteigenschaft gefunden wurde, führen wir eine zweite Prüfung durch, die in Zeile 82 gezeigt wird. An dieser Stelle erhalten wir eine Rechtfertigung für die Existenz der Funktion in Zeile 59. Wenn Sie der Klasse C_AdjustTemplate während des später zu besprechenden Programmiervorgangs mitteilen, dass Sie nicht wissen, welchen Parameter Sie in der Eigenschaft des Objekts verwenden sollen, führt die Prüfung in Zeile 82 dazu, dass Zeile 83 ausgeführt wird, wodurch der in der Eigenschaft des Objekts vorhandene Wert festgelegt wird. Wenn Sie den gewünschten Wert angeben, wird er in die Vorlage geschrieben.

Es ist diese Art von Funktionalität, die die Klasse C_AdjustTemplate so interessant macht, denn Sie können sie bitten, den Wert einer Eigenschaft zu melden oder anzugeben, welcher Wert verwendet werden soll.

Damit ist die Erklärung der Klasse C_AdjustTemplate abgeschlossen. Sehen wir uns nun an, was aus der Klasse C_ChartFloatingRAD geworden ist. Wie Sie sich vorstellen können, wurde sie auch verändert und ist noch interessanter geworden.


Neues Aussehen für die Klasse C_ChartFloatingRAD

Obwohl ich den endgültigen Code dieser Klasse in diesem Artikel nicht zeigen werde (der Grund dafür ist, dass ich jedes Detail langsam erklären möchte), werden Sie feststellen, dass er nun wesentlich komplexer sein wird als der im vorherigen Artikel vorgestellte Code. Dennoch bleibt der größte Teil des Codes gleich. Deshalb ist es ratsam, die Artikel der Reihe nach durchzuarbeiten, da Sie sonst möglicherweise einige Details übersehen, die Ihr Gesamtverständnis beeinträchtigen könnten.

Nachfolgend sehen Sie den vollständigen Code für die Klasse C_ChartFloatingRAD, bis zum aktuellen Punkt.

001. //+------------------------------------------------------------------+
002. #property copyright "Daniel Jose"
003. //+------------------------------------------------------------------+
004. #include "../Auxiliar/C_Mouse.mqh"
005. #include "C_AdjustTemplate.mqh"
006. //+------------------------------------------------------------------+
007. class C_ChartFloatingRAD : private C_Terminal
008. {
009.    private :
010.            enum eObjectsIDE {MSG_MAX_MIN, MSG_TITLE_IDE, MSG_DAY_TRADE, MSG_LEVERAGE_VALUE, MSG_TAKE_VALUE, MSG_STOP_VALUE, MSG_BUY_MARKET, MSG_SELL_MARKET, MSG_CLOSE_POSITION, MSG_NULL};
011.            struct st00
012.            {
013.                    int     x, y;
014.                    string  szObj_Chart,
015.                            szFileNameTemplate;
016.                    long    WinHandle;
017.                    double  FinanceTake,
018.                            FinanceStop;
019.                    int     Leverage;
020.                    bool    IsDayTrade,
021.                            IsMaximized;
022.                    struct st01
023.                    {
024.                            int x, y, w, h;
025.                    }Regions[MSG_NULL];
026.            }m_Info;
027. //+------------------------------------------------------------------+
028.            C_Mouse  *m_Mouse;
029. //+------------------------------------------------------------------+
030.            void CreateWindowRAD(int x, int y, int w, int h)
031.                    {
032.                            m_Info.szObj_Chart = (string)ObjectsTotal(GetInfoTerminal().ID);
033.                            ObjectCreate(GetInfoTerminal().ID, m_Info.szObj_Chart, OBJ_CHART, 0, 0, 0);
034.                            ObjectSetInteger(GetInfoTerminal().ID, m_Info.szObj_Chart, OBJPROP_XDISTANCE, m_Info.x = x);
035.                            ObjectSetInteger(GetInfoTerminal().ID, m_Info.szObj_Chart, OBJPROP_YDISTANCE, m_Info.y = y);
036.                            ObjectSetInteger(GetInfoTerminal().ID, m_Info.szObj_Chart, OBJPROP_XSIZE, w);
037.                            ObjectSetInteger(GetInfoTerminal().ID, m_Info.szObj_Chart, OBJPROP_YSIZE, h);
038.                            ObjectSetInteger(GetInfoTerminal().ID, m_Info.szObj_Chart, OBJPROP_DATE_SCALE, false);
039.                            ObjectSetInteger(GetInfoTerminal().ID, m_Info.szObj_Chart, OBJPROP_PRICE_SCALE, false);
040.                            m_Info.WinHandle = ObjectGetInteger(GetInfoTerminal().ID, m_Info.szObj_Chart, OBJPROP_CHART_ID);
041.                    };
042. //+------------------------------------------------------------------+
043. inline void UpdateChartTemplate(void)
044.                    {
045.                            ChartApplyTemplate(m_Info.WinHandle, "/Files/" + m_Info.szFileNameTemplate);
046.                            ChartRedraw(m_Info.WinHandle);
047.                    }
048. //+------------------------------------------------------------------+
049. inline double PointsToFinance(const double Points)
050.                    {                               
051.                            return Points * (GetInfoTerminal().VolumeMinimal + (GetInfoTerminal().VolumeMinimal * (m_Info.Leverage - 1))) * GetInfoTerminal().AdjustToTrade;
052.                    };
053. //+------------------------------------------------------------------+
054. inline void AdjustTemplate(const bool bFirst = false)
055.                    {
056. #define macro_AddAdjust(A) {                         \
057.              (*Template).Add(A, "size_x", NULL);     \
058.              (*Template).Add(A, "size_y", NULL);     \
059.              (*Template).Add(A, "pos_x", NULL);      \
060.              (*Template).Add(A, "pos_y", NULL);      \
061.                            }
062. #define macro_GetAdjust(A) {                                                                          \
063.              m_Info.Regions[A].x = (int) StringToInteger((*Template).Get(EnumToString(A), "pos_x"));  \
064.              m_Info.Regions[A].y = (int) StringToInteger((*Template).Get(EnumToString(A), "pos_y"));  \
065.              m_Info.Regions[A].w = (int) StringToInteger((*Template).Get(EnumToString(A), "size_x")); \
066.              m_Info.Regions[A].h = (int) StringToInteger((*Template).Get(EnumToString(A), "size_y")); \                                               
067.                            }
068.                            
069.                            C_AdjustTemplate *Template;
070.                            
071.                            if (bFirst)
072.                            {
073.                                    Template = new C_AdjustTemplate("Chart Trade/IDE_RAD.tpl", m_Info.szFileNameTemplate = StringFormat("Chart Trade/%u.tpl", GetInfoTerminal().ID));
074.                                    for (eObjectsIDE c0 = 0; c0 <= MSG_CLOSE_POSITION; c0++) macro_AddAdjust(EnumToString(c0));
075.                            }else Template = new C_AdjustTemplate(m_Info.szFileNameTemplate);
076.                            Template.Add("MSG_NAME_SYMBOL", "descr", GetInfoTerminal().szSymbol);
077.                            Template.Add("MSG_LEVERAGE_VALUE", "descr", (string)m_Info.Leverage);
078.                            Template.Add("MSG_TAKE_VALUE", "descr", (string)m_Info.FinanceTake);
079.                            Template.Add("MSG_STOP_VALUE", "descr", (string)m_Info.FinanceStop);
080.                            Template.Add("MSG_DAY_TRADE", "state", (m_Info.IsDayTrade ? "1" : "0"));
081.                            Template.Add("MSG_MAX_MIN", "state", (m_Info.IsMaximized ? "1" : "0"));
082.                            Template.Execute();
083.                            if (bFirst) for (eObjectsIDE c0 = 0; c0 <= MSG_CLOSE_POSITION; c0++) macro_GetAdjust(c0);
084.                            
085.                            delete Template;
086.                            
087.                            UpdateChartTemplate();
088.                                                            
089. #undef macro_AddAdjust
090. #undef macro_GetAdjust
091.                    }
092. //+------------------------------------------------------------------+
093.            eObjectsIDE CheckMousePosition(const int x, const int y)
094.                    {
095.                            int xi, yi, xf, yf;
096.                            
097.                            for (eObjectsIDE c0 = 0; c0 <= MSG_CLOSE_POSITION; c0++)
098.                            {
099.                                    xi = m_Info.x + m_Info.Regions[c0].x;
100.                                    yi = m_Info.y + m_Info.Regions[c0].y;
101.                                    xf = xi + m_Info.Regions[c0].w;
102.                                    yf = yi + m_Info.Regions[c0].h;
103.                                    if ((x > xi) && (y > yi) && (x < xf) && (y < yf) && ((c0 == MSG_MAX_MIN) || (c0 == MSG_TITLE_IDE) || (m_Info.IsMaximized))) return c0;
104.                            }
105.                            return MSG_NULL;
106.                    }
107. //+------------------------------------------------------------------+
108.    public  :
109. //+------------------------------------------------------------------+
110.            C_ChartFloatingRAD(string szShortName, C_Mouse *MousePtr, const int Leverage, const double FinanceTake, const double FinanceStop)
111.                    :C_Terminal()
112.                    {
113.                            m_Mouse = MousePtr;
114.                            m_Info.Leverage = (Leverage < 0 ? 1 : Leverage);
115.                            m_Info.FinanceTake = PointsToFinance(FinanceToPoints(MathAbs(FinanceTake), m_Info.Leverage));
116.                            m_Info.FinanceStop = PointsToFinance(FinanceToPoints(MathAbs(FinanceStop), m_Info.Leverage));
117.                            m_Info.IsDayTrade = true;
118.                            m_Info.IsMaximized = true;
119.                            if (!IndicatorCheckPass(szShortName)) SetUserError(C_Terminal::ERR_Unknown);
120.                            CreateWindowRAD(115, 64, 170, 210);
121.                            AdjustTemplate(true);
122.                    }
123. //+------------------------------------------------------------------+
124.            ~C_ChartFloatingRAD()
125.                    {
126.                            ObjectDelete(GetInfoTerminal().ID, m_Info.szObj_Chart);
127.                            FileDelete(m_Info.szFileNameTemplate);
128.                            
129.                            delete m_Mouse;
130.                    }
131. //+------------------------------------------------------------------+
132.            void DispatchMessage(const int id, const long &lparam, const double &dparam, const string &sparam)
133.                    {
134.                            static int sx = -1, sy = -1;
135.                            int x, y, mx, my;
136.                            static eObjectsIDE it = MSG_NULL;
137.    
138.                            switch (id)
139.                            {
140.                                    case CHARTEVENT_MOUSE_MOVE:
141.                                            if ((*m_Mouse).CheckClick(C_Mouse::eClickLeft))
142.                                            {
143.                                                    switch (it = CheckMousePosition(x = (int)lparam, y = (int)dparam))
144.                                                    {
145.                                                            case MSG_TITLE_IDE:
146.                                                                    if (sx < 0)
147.                                                                    {
148.                                                                            ChartSetInteger(GetInfoTerminal().ID, CHART_MOUSE_SCROLL, false);
149.                                                                            sx = x - m_Info.x;
150.                                                                            sy = y - m_Info.y;
151.                                                                    }
152.                                                                    if ((mx = x - sx) > 0) ObjectSetInteger(GetInfoTerminal().ID, m_Info.szObj_Chart, OBJPROP_XDISTANCE, m_Info.x = mx);
153.                                                                    if ((my = y - sy) > 0) ObjectSetInteger(GetInfoTerminal().ID, m_Info.szObj_Chart, OBJPROP_YDISTANCE, m_Info.y = my);
154.                                                                    break;
155.                                                    }
156.                                            }else
157.                                            {
158.                                                    if (it != MSG_NULL)
159.                                                    {
160.                                                            switch (it)
161.                                                            {
162.                                                                    case MSG_MAX_MIN:
163.                                                                            m_Info.IsMaximized = (m_Info.IsMaximized ? false : true);
164.                                                                            ObjectSetInteger(GetInfoTerminal().ID, m_Info.szObj_Chart, OBJPROP_YSIZE, (m_Info.IsMaximized ? 210 : m_Info.Regions[MSG_TITLE_IDE].h + 6));
165.                                                                            break;
166.                                                                    case MSG_DAY_TRADE:
167.                                                                            m_Info.IsDayTrade = (m_Info.IsDayTrade ? false : true);
168.                                                                            break;
169.                                                            }
170.                                                            it = MSG_NULL;
171.                                                            AdjustTemplate();
172.                                                    }
173.                                                    if (sx > 0)
174.                                                    {
175.                                                            ChartSetInteger(GetInfoTerminal().ID, CHART_MOUSE_SCROLL, true);                                                
176.                                                            sx = sy = -1;
177.                                                    }
178.                                            }
179.                                            break;
180.                            }
181.                    }
182. //+------------------------------------------------------------------+
183. };
184. //+------------------------------------------------------------------+

Quellcode der Klasse C_ChartFloatingRAD

Ich weiß, dass dieser Code sehr verwirrend und kompliziert erscheint, besonders für Anfänger. Aber wenn Sie diese Serie von Anfang an verfolgt haben, sollten Sie inzwischen eine Menge über die MQL5-Programmierung gelernt haben. Wie auch immer, dieser Code ist viel komplexer als das, was viele Leute normalerweise erstellen.

Wenn Sie diesen Code mit dem im vorherigen Artikel vorgestellten vergleichen, werden Sie feststellen, dass er komplizierter geworden ist. Der größte Teil der Komplexität wurde jedoch in die Klasse C_AdjustTemplate verlagert, die im vorherigen Abschnitt beschrieben wurde. Lassen Sie uns herausfinden, was dieser Code bewirkt, denn hier liegt die Magie des Chart Trade Indikators. Dies liegt daran, dass der im vorherigen Artikel gezeigte Indikatorcode derselbe bleibt. Der hier gezeigte Code hat sich jedoch geändert, und zwar so sehr, dass er dem Indikator neue Funktionen hinzufügt.

Beginnen wir mit der Erklärung in Zeile 10, wo wir eine Enumeration haben, die uns den Zugriff auf die in der Vorlage vorhandenen Objekte erleichtert. Die gleiche Enumeration hat jedoch den Wert MSG_NULL, der zur Kontrolle verwendet wird. Dies wird im Laufe der weiteren Ausführungen deutlich werden.

Wenn wir uns den Code ansehen, sehen wir zwischen den Zeilen 22 und 25 eine Struktur, die von einer Variablen verwendet wird, die ein Array ist. Aber wenn Sie sich die Anzahl der Elemente in dem Array ansehen, werden Sie sich fragen, was das ist. Wir haben keinen Wert, es ist etwas anderes. Vielleicht denken Sie: „Ich kann nirgendwo finden, was das bedeutet“. Beruhigen Sie sich, kein Grund zur Panik. Diese Daten, die die Anzahl der Elemente im Array angeben, sind nur die letzten Daten der Enumeration in Zeile 10. Allerdings gibt es hier ein Detail. Dieser letzte Wert steht nicht für ein Element oder Objekt. Wenn dies der Fall wäre, hätte die Erklärung anders ausfallen müssen.

Die nächste erklärungsbedürftige Zeile ist 54. An dieser Stelle erhalten wir Zugriff auf die Vorlage. Doch bevor wir zu dieser Erklärung kommen, wollen wir noch einen weiteren Punkt betrachten. Auf diese Funktion wird an zwei Stellen zugegriffen. Der erste befindet sich in Zeile 121, dem Konstruktor, und der zweite in Zeile 171, dem Teil der Nachrichtenverarbeitung. Warum ist es wichtig, dies zu wissen? Der Grund dafür ist, was wir tun und was wir in der Vorlage tun wollen.

Im ersten Fall, der im Konstruktor auftritt, wollen wir die Vorlage so anpassen, dass sie auf eine bestimmte Weise funktioniert. Im zweiten Fall werden wir mit dem arbeiten, was wir bereits haben; wir werden die Vorlage nicht ändern, aber wir werden sie genau an das anpassen, was wir wollen.

Diese Erklärung mag ein wenig verwirrend erscheinen, aber sehen wir uns an, wie die Methode in Zeile 54 funktioniert. Vielleicht hilft Ihnen das, die Situation besser zu verstehen. Zwischen den Zeilen 56 und 67 werden zwei Makros definiert, die uns die Programmierung erleichtern. Genauso wie die Zeilen 89 und 90 dazu dienen, solche Makros zu eliminieren. Normalerweise verwende ich Makros, wenn ich denselben Code oder einige Parameter mehrfach wiederhole. In diesem speziellen Fall wird ein Parameter wiederholt. Der Makrocode ist recht einfach.

Die erste, die zwischen den Zeilen 56 und 61 liegt, fügt die Elemente hinzu, die die Klasse C_AdjustTemplate an uns zurückgibt. Im zweiten Makro, das sich zwischen den Zeilen 62 und 67 befindet, nehmen wir die Werte, die uns die Klasse C_AdjustTemplate mitteilt, und wandeln sie in einen Wert um, der in dem in Zeile 25 deklarierten Array gespeichert wird. Achten Sie auf Folgendes. Wir raten nicht einfach, wo sich die Objekte befinden, sondern wir fragen die Vorlage, wo sie sich befinden.

In Zeile 71 prüfen wir, ob wir mit der Anpassung der Vorlage beginnen. Wenn dies nicht der Fall ist, führen wir den Aufruf in Zeile 75 aus. Wenn dies jedoch der erste Aufruf ist, teilen wir der Klasse C_AdjustTemplate mit, welche Namen verwendet werden sollen. Dies geschieht in Zeile 73. Achten Sie nun besonders auf Zeile 74. Wie Sie sehen können, verwenden wir in dieser Zeile eine Enumeration, um der Klasse C_AdjustTemplate mitzuteilen, von welchen Objekten wir die Daten beziehen müssen. Hierfür verwenden wir eine Schleife. Auf diese Weise weiß die Klasse C_AdjustTemplate, welche Eigenschaften korrigiert werden müssen.

In jedem Fall übergeben wir in den Zeilen 76 und 81 die Werte, die in den Objekteigenschaften verwendet werden sollen, an die Vorlage. Jede Zeile gibt ein Objekt, eine zu ändernde Eigenschaft und einen zu verwendenden Wert an.

In Zeile 82 schließlich teilen wir der Klasse C_AdjustTemplate mit, dass sie die Vorlage entsprechend den bereitgestellten Informationen anpassen kann. Dies geschieht wie im vorherigen Thema beschrieben. Nach Abschluss aller Arbeiten wird in Zeile 83 geprüft, ob dies der erste Anruf ist. Wenn ja, passen wir die Werte des in Zeile 25 deklarierten Arrays an. Dazu wird eine Schleife verwendet, die der Klasse C_AdjustTemplate mitteilt, über welche Objekte und Eigenschaften wir etwas wissen wollen.

Sobald diese Arbeit abgeschlossen ist, schließen wir die Klasse C_Template mit Zeile 85. Und schließlich wird in Zeile 87 das Objekt OBJ_CHART zur Aktualisierung aufgefordert. Auf diese Weise werden wir die Magie in Aktion sehen, wie im Demo-Video am Ende des Artikels gezeigt.

Bitte beachten Sie: Wir prüfen hier nicht auf Fehler, es wird davon ausgegangen, dass alles in Ordnung ist und wie erwartet funktioniert. Wir werden jedoch den Code des Indikators auf Fehler überprüfen. Wenn also etwas schief geht, wird es nicht hier, sondern im Code des Indikators behandelt.

Sehen wir uns nun etwas anderes an: In Zeile 93 wird eine recht interessante Funktion ausgeführt, die genau dort platziert werden könnte, wo sie verwendet wird. Dies geschieht, um den Code lesbarer zu machen. Diese Funktion hat eine Schleife, die in Zeile 97 beginnt und jedes der in OBJ_CHART vorhandenen Objekte durchläuft. Denken Sie an Folgendes: Das Objekt OBJ_CHART enthält eine Vorlage, und diese Vorlage enthält die Objekte, die wir prüfen werden. Während der Prüfung erstellen wir ein Rechteck, das den Klickbereich für jedes Objekt darstellt. Dies geschieht in den Zeilen 99 und 102.

Sobald wir diesen Klickbereich haben, können wir ihn mit dem als Aufrufparameter angegebenen Bereich vergleichen. Dieser Vergleich wird in Zeile 103 vorgenommen. Neben dem Gebiet gibt es einige zusätzliche Bedingungen. Wenn alles in Ordnung ist, wird der Index der Objekte zurückgegeben; andernfalls wird MSG_NULL zurückgegeben. Aus diesem Grund brauchen wir die zu Beginn definierte Enumeration, um diesen Wert aufzunehmen. Ohne diesen Wert wäre es unmöglich, zu melden, dass ein Klick auf ein ungültiges Objekt im Chart Trade Indikator erfolgt ist.

Der nächste zu erklärende Punkt befindet sich in Zeile 132. Es ist der Ereignisbehandler. Jetzt enthält er einige neue Teile. Aber es sind diese neuen Teile, die das, was Sie im Demo-Video sehen, möglich machen. Schauen wir uns also ganz genau an, was hier vor sich geht. Beachten Sie, dass wir bis zu diesem Punkt kein anderes Objekt als OBJ_CHART erstellt haben. Und dennoch funktionieren wir wie erwartet.

Der größte Teil des Codes ähnelt dem, der im vorherigen Artikel gezeigt wurde. Es gibt jedoch einige kleine Unterschiede, die es wert sind, kommentiert zu werden, damit auch weniger erfahrene Personen verstehen können, worum es geht. In den Zeilen 134-136 definieren wir einige Variablen. Die in Zeile 136 definierte Variable ist im Rahmen dieses Artikels von besonderem Interesse, da die anderen bereits erläutert wurden.

Wir werden diese Variable in Zeile 136 als Speicher verwenden. Dies liegt daran, dass wir uns nicht auf die zusätzliche Hilfe von MetaTrader 5 verlassen können, um klickbezogene Probleme zu lösen. Wenn sich Objekte auf dem Chart befinden, zeigt MetaTrader 5 normalerweise den Namen des Objekts an, auf das geklickt wurde. Dies geschieht durch das Ereignis CHARTEVENT_OBJECT_CLICK. Aber in diesem Fall haben wir keine anderen echten Objekte als OBJ_CHART. Somit wird jeder Klick im Bereich des Chart Trade Indikators von MetaTrader 5 als Klick auf OBJ_CHART interpretiert.

Das einzige Ereignis, mit dem wir es zu tun haben, ist CHARTEVENT_MOUSE_MOVE, was für uns ausreichend ist. Klicks werden jedoch nur verarbeitet, wenn sich der Mauszeiger nicht im Lernzustand befindet. Dies wird in Zeile 141 überprüft. Befindet sich der Mauszeiger im Lernzustand oder ist etwas anderes passiert, gehen wir zu Zeile 156 über. Und hier ist eine Frage. Wenn die in Zeile 136 deklarierte Variable einen anderen Wert hat, muss etwas passieren. Doch zunächst wollen wir uns ansehen, wann und wo diese Variable ihren Wert erhält.

Wenn der Mauszeiger frei ist und ein Klick erfolgt, wird durch die in Zeile 141 durchgeführte Prüfung festgestellt, wo und worauf genau der Klick erfolgt ist. Dies geschieht in Zeile 143. An dieser Stelle teilen wir der Analysefunktion mit, wo sich die Maus zum Zeitpunkt des Klicks befindet. Allerdings gibt es hier einen kleinen Fehler, auf den ich jetzt nicht näher eingehen will, da er im nächsten Artikel behoben wird, ebenso wie andere Kleinigkeiten, die wir noch zu erledigen haben. Gleichzeitig mit der Prüfung gibt die Funktion den Namen des Objekts zurück, das den Klick erhalten hat. Dieser Name wird in eine statische Variable geschrieben.

Aus praktischen Gründen überprüfen wir jetzt nur das Titelobjekt. Wenn es angeklickt wurde, wird in Zeile 145 der Code zum Ziehen ausgeführt. Wir könnten hier auch andere Objekte platzieren, aber das würde die Prüflogik verkomplizieren, zumindest in diesem Stadium. Da die Maustaste gedrückt ist, empfängt das Objekt weiterhin Klickmeldungen.

Wie bereits erwähnt, können wir dies verbessern. Aber im Moment möchte ich den Code so einfach wie möglich halten und nach und nach Änderungen vornehmen, da das hier gezeigte Konzept sich sehr von dem unterscheidet, was viele Leute normalerweise programmieren.

Aber kehren wir zu Zeile 156 zurück. Wenn diese Zeile ausgeführt wird, führen wir innerhalb dieser Bedingung zwei Prüfungen durch. Die erste ermittelt, ob ein in OBJ_CHART vorhandenes Objekt angeklickt wurde. Wenn dies der Fall ist, haben wir die gleiche Bedingung, die erfüllt wäre, wenn das Objekt tatsächlich im Chart vorhanden wäre und MetaTrader 5 das Ereignis CHARTEVENT_OBJECT_CLICK erzeugt hätte. Das heißt, wir „emulieren“ die Funktionsweise eines bestehenden Systems. Wir tun dies, um ein angemessenes Verhalten des gesamten Indikators zu gewährleisten.

Wenn die in Zeile 158 dargestellte Prüfung erfolgreich verläuft, führen wir zunächst die entsprechende Ereignisbehandlung durch. Dies geschieht in den Zeilen 160 und 169, dann wird in Zeile 170 der Objektverweis entfernt und in Zeile 171 der aktuelle Zustand der Vorlage aktualisiert. Auf diese Weise wird der gesamte Indikator aktualisiert, wodurch die Illusion entsteht, dass Objekte auf dem Chart vorhanden sind, obwohl das einzige wirkliche Objekt OBJ_CHART ist.

Der gesamte übrige Code der Klasse C_ChartFloatingRAD wurde bereits im vorigen Artikel erläutert, sodass ich es nicht für nötig halte, ihn hier noch einmal zu kommentieren.

Demo-Video

Schlussfolgerung

Wie Sie sehen, habe ich in diesem Artikel eine Möglichkeit zur Verwendung von Vorlagen in OBJ_CHART vorgestellt, mit der Sie ein Verhalten erreichen können, das dem von echten Objekten im Chart sehr ähnlich ist. Der vielleicht größte Vorteil dessen, was ich hier zeige, ist die Möglichkeit, schnell eine Schnittstelle aus Elementen zu erstellen, die im MetaTrader 5 selbst vorhanden sind, ohne komplexe MQL5-Programmierung zu verwenden.

Auch wenn das, was ich hier zeige, ziemlich verwirrend und kompliziert erscheint, so liegt das nur daran, dass es für Sie, liebe Leserin, lieber Leser, etwas völlig Neues ist. Mit der Zeit werden Sie feststellen, dass Sie dieses Wissen in einer Vielzahl von Szenarien und Situationen einsetzen können. Das System ist jedoch noch nicht vollständig. Außerdem hat es einen kleinen Makel. Aber das wird im nächsten Artikel behoben, in dem wir dem Nutzer endlich erlauben werden, Werte direkt in Chart Trade einzugeben. Dieser Teil wird sehr interessant zu implementieren sein. Verpassen Sie also nicht den nächsten Artikel in dieser Reihe.


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

Beigefügte Dateien |
Anexo.zip (420.65 KB)
Entwicklung eines Replay Systems (Teil 45): Chart Trade Projekt (IV) Entwicklung eines Replay Systems (Teil 45): Chart Trade Projekt (IV)
Der Hauptzweck dieses Artikels ist die Einführung und Erläuterung der Klasse C_ChartFloatingRAD. Wir haben einen Chart Trade-Indikator, der auf recht interessante Weise funktioniert. Wie Sie vielleicht bemerkt haben, haben wir immer noch eine relativ kleine Anzahl von Objekten im Chart, und dennoch erhalten wir die erwartete Funktionalität. Die im Indikator enthaltenen Werte können bearbeitet werden. Die Frage ist, wie ist das möglich? Dieser Artikel wird die Dinge etwas klarer machen.
Entwicklung eines Expert Advisors (EA) auf Basis der Consolidation Range Breakout Strategie in MQL5 Entwicklung eines Expert Advisors (EA) auf Basis der Consolidation Range Breakout Strategie in MQL5
Dieser Artikel beschreibt die Schritte zur Erstellung eines Expert Advisors (EA), der Kursausbrüche nach Konsolidierungsphasen ausnutzt. Durch die Identifizierung von Konsolidierungsbereichen und die Festlegung von Ausbruchsniveaus können Händler ihre Handelsentscheidungen auf der Grundlage dieser Strategie automatisieren. Der Expert Advisor zielt darauf ab, klare Einstiegs- und Ausstiegspunkte zu bieten und gleichzeitig falsche Ausbrüche zu vermeiden.
Matrix-Faktorisierung: Die Grundlagen Matrix-Faktorisierung: Die Grundlagen
Da das Ziel hier didaktisch ist, werden wir so einfach wie möglich vorgehen. Das heißt, wir werden nur das implementieren, was wir brauchen: Matrixmultiplikation. Sie werden heute sehen, dass dies ausreicht, um die Matrix-Skalar-Multiplikation zu simulieren. Die größte Schwierigkeit, auf die viele Menschen bei der Implementierung von Code mit Matrixfaktorisierung stoßen, ist folgende: Im Gegensatz zur skalaren Faktorisierung, bei der in fast allen Fällen die Reihenfolge der Faktoren das Ergebnis nicht verändert, ist dies bei der Verwendung von Matrizen nicht der Fall.
Einführung in MQL5 (Teil 8): Leitfaden für Einsteiger zur Erstellung von Expert Advisors (II) Einführung in MQL5 (Teil 8): Leitfaden für Einsteiger zur Erstellung von Expert Advisors (II)
Dieser Artikel behandelt häufige Anfängerfragen aus MQL5-Foren und zeigt praktische Lösungen auf. Lernen Sie, grundlegende Aufgaben wie Kaufen und Verkaufen, die Kursabfrage der Kerzen und die Verwaltung automatisierter Handelsaspekte wie Handelslimits, Handelszeiträume und Gewinn-/Verlustschwellen durchzuführen. Erhalten Sie eine schrittweise Anleitung, um Ihr Verständnis und Ihre Implementierung dieser Konzepte in MQL5 zu verbessern.