English Русский 中文 Español 日本語 Português
preview
Entwicklung eines Replay Systems (Teil 41): Beginn der zweiten Phase (II)

Entwicklung eines Replay Systems (Teil 41): Beginn der zweiten Phase (II)

MetaTrader 5Beispiele | 11 Juli 2024, 13:24
139 0
Daniel Jose
Daniel Jose

Einführung

Im vorherigen Artikel Entwicklung eines Replay Systems (Teil 40): Beginn der zweiten Phase (I) haben wir mit der Erstellung eines Indikators zur Unterstützung des Studiensystems und der Maus begonnen. Doch bevor wir beginnen, möchte ich Ihnen eine Frage stellen: Können Sie sich vorstellen, warum wir diesen Indikator erstellen? Was steckt hinter unserer Absicht, zu zeigen, wie man so etwas schafft? Was will ich mit der Erstellung eines Indikators für die Maus erreichen?

Nun, vielleicht machen diese Fragen nicht viel Sinn, aber haben Sie schon einmal innegehalten und Ihren eigenen Code beobachtet? Ist Ihnen schon einmal aufgefallen, wie oft Sie die gleichen Dinge in verschiedenen Codes wiederholen? Die verschiedenen Codes sind jedoch weder einheitlich noch stabil. Verstehen Sie mich bitte nicht falsch. Ich sollte Leuten mit langjähriger Programmiererfahrung nicht vorschreiben, was sie zu tun oder zu lassen haben. Und ich werde nicht behaupten, dass Sie die Sprache MQL5 oder eine andere Sprache, auf die Sie sich spezialisiert haben, irgendwie falsch verwenden.

Ich möchte den Leser, der über jahrelange Programmiererfahrung verfügt, irgendwie dazu bringen, nicht immer wieder das Gleiche zu tun. Vielleicht ist es an der Zeit, Ihre eigene Arbeit genau zu überprüfen. Hören Sie auf, immer wieder das Gleiche zu tun, nur weil es an durchdachter Planung mangelt.

Es gibt nämlich einen verborgenen Grund für den Beginn einer neuen Phase im Replay/Simulator-System. Wir haben bereits in einem anderen Artikel darüber gesprochen. Ich bin es leid, in MQL5 alles auf die alte Art und Weise zu erledigen. Meine persönlichen Codes folgen bestimmten Regeln. Die den Artikeln beigefügten Codes waren jedoch unterschiedlich. Ich versuche immer, sie so einfach und klar wie möglich zu halten, aber ich sehe ständig Leute, die etwas über MQL5 oder MetaTrader 5 sagen.

Ich möchte zeigen, dass es möglich ist, viel mehr zu erreichen als das, was andere erreicht haben. Also macht euch bereit, keine Witze mehr. Von nun an werden wir sehen, wie mein Code tatsächlich aussieht. Ich werde Ihnen nichts Spezielles über bestimmte Vermögenswerte oder Indikatoren zeigen, aber ich werde Ihnen zeigen, dass MQL5, wie MetaTrader 5, viel mehr kann, als Sie denken.

Im vorigen Artikel erwähnte ich, dass es noch eine ungelöste Frage zu klären gibt. Fangen wir also damit an, dieses Problem zu lösen.


Modifikation der Klasse C_Mouse

Die Frage ist hier, wie man mit dem Mauszeiger interagiert. Der im vorigen Artikel gezeigte Code ist fertiggestellt. Der Indikator wird sich nicht verändern, zumindest nicht für längere Zeit. Es gibt jedoch ein Problem, das die Verwendung unserer Daten erheblich erschwert.

Damit Sie verstehen können, was ich Ihnen erzähle, müssen Sie die Ideen und Konzepte aufgeben, die Sie über MetaTrader 5 und die Sprache MQL5 kennen. Vergessen Sie alles, was Sie über die Zusammenarbeit mit ihnen wussten. Lassen Sie uns ganz anders denken. Wenn Sie das verstehen, können wir eine Art der Codierung vornehmen, die Ihren Code immer produktiver und stabiler macht.

In früheren Artikeln haben wir darüber gesprochen, dass Prozesse, die in MetaTrader 5 laufen, nicht als Indikatoren, Expert Advisors, Skripte oder Dienste betrachtet werden sollten, sondern als Funktionen. Das Konzept der Funktionen ist vielleicht nicht der beste Begriff dafür. Es gibt jedoch eine Definition, die am besten passt: DLLs. Ja, betrachten Sie alle in MetaTrader 5 laufenden Prozesse, insbesondere die Indikatoren, als DLLs.

Warum sage ich das? Denn die gleichen Konzepte, die bei DLLs eine Rolle spielen, können auch auf das angewendet werden, was ich hier zeigen werde. Im Moment ist der einzige Indikator, den wir haben, die Maus. Auf diese Weise haben Sie keine vollständige und umfassende Vorstellung davon, was passieren wird. Doch jedes große Gebäude beginnt mit der Grundsteinlegung. Wir werden es jetzt sofort tun. Aufgrund einiger Probleme müssen wir die Arbeit jedoch vollständig vereinfachen, um kein völliges Chaos zu schaffen.

Wenn Sie den Artikel Entwicklung eines Replay Systems (Teil 31): Das Expert Advisor Projekt — Klasse C_Mouse (V) gelesen haben werden Sie feststellen, dass die Klasse C_Mouse, wie auch die Klasse C_Study, auf eine bestimmte Weise funktioniert. Aber die Art und Weise, wie sie funktionieren, erlaubt es uns NICHT, eine einfache Programmierung in dem Maße zu verwenden, dass wir die Vorteile eines Indikators mit diesen Klassen nutzen können. Achtung: Ich sage nicht, dass wir nicht das Beste daraus machen können, sondern nur, dass dies eine extrem komplexe Programmierung erfordern würde.

Die Idee ist, dieses Programm zu vereinfachen. Zu diesem Zweck werden wir einige Elemente aus der Klasse C_Study entfernen und in die Klasse C_Mouse verschieben. Wir werden die Klasse C_Mouse aber auch programmiertechnisch und strukturell ergänzen, um die Nutzung des Indikators zu erleichtern, wie wir in den nächsten Artikeln sehen werden.

Was wir in der Klasse C_Study entfernen werden, ist im folgenden Code zu sehen.

Teil des C_Study Klassencodes:

01. #property copyright "Daniel Jose"
02. //+------------------------------------------------------------------+
03. #include "..\C_Mouse.mqh"
04. #include "..\..\Auxiliar\C_Mouse.mqh"
05. #include "..\..\Auxiliar\Interprocess.mqh"
06. //+------------------------------------------------------------------+
07. #define def_ExpansionPrefix "MouseExpansion_"
08. #define def_ExpansionBtn1 def_ExpansionPrefix + "B1"
09. #define def_ExpansionBtn2 def_ExpansionPrefix + "B2"
10. #define def_ExpansionBtn3 def_ExpansionPrefix + "B3"
11. //+------------------------------------------------------------------+
12. #define def_AcessTerminal (*Terminal)
13. #define def_InfoTerminal def_AcessTerminal.GetInfoTerminal()
14. //+------------------------------------------------------------------+
15. class C_Study : public C_Mouse
16. {
17.     protected:
18.     private :
19. //+------------------------------------------------------------------+
20.             enum eStatusMarket {eCloseMarket, eAuction, eInTrading, eInReplay};
21. //+------------------------------------------------------------------+
22.             struct st00
23.             {
24.                     eStatusMarket   Status;
25.                     MqlRates        Rate;
26.                     string          szInfo;
27.                     color           corP,
28.                                     corN;
29.                     int             HeightText;
30.             }m_Info;

Der Speicherort der Header-Datei C_Study.mqh hat sich geändert. Wir haben Zeile 04 durch Zeile 03 ersetzt, und da wir nicht mehr auf die Header-Datei InterProcess.mqh verweisen, wurde auch Zeile 05 entfernt.

Außerdem haben wir Zeile 20 entfernt und in die Klasse C_Mouse aufgenommen. Diese Verlagerung dient dazu, die Programmierung in einer späteren Phase zu erleichtern. Nachdem wir nun die Änderungen an der Klasse C_Study gesehen haben, wenden wir uns der Klasse C_Mouse zu, bei der die Änderungen wesentlich umfangreicher sind.

Unten sehen Sie den gesamten Code der neuen Klasse C_Mouse.

C_Mouse.mqh Datei-Code:

001. //+------------------------------------------------------------------+
002. #property copyright "Daniel Jose"
003. //+------------------------------------------------------------------+
004. #include "C_Terminal.mqh"
005. #include "Interprocess.mqh"
006. //+------------------------------------------------------------------+
007. #define def_MousePrefixName "MouseBase_"
008. #define def_NameObjectLineH def_MousePrefixName + "H"
009. #define def_NameObjectLineV def_MousePrefixName + "TV"
010. #define def_NameObjectLineT def_MousePrefixName + "TT"
011. #define def_NameObjectStudy def_MousePrefixName + "TB"
012. //+------------------------------------------------------------------+
013. #define def_AcessTerminal (*Terminal)
014. #define def_InfoTerminal def_AcessTerminal.GetInfoTerminal()
015. //+------------------------------------------------------------------+
016. class C_Mouse
017. {
018.    public  :
019.            enum eStatusMarket {eCloseMarket, eAuction, eInTrading, eInReplay};
020.            enum eBtnMouse {eKeyNull = 0x00, eClickLeft = 0x01, eClickRight = 0x02, eSHIFT_Press = 0x04, eCTRL_Press = 0x08, eClickMiddle = 0x10};
021.            struct st_Mouse
022.            {
023.                    struct st00
024.                    {
025.                            int       X,
026.                                      Y;
027.                            double    Price;
028.                            datetime  dt;
029.                    }Position;
030.                    uint    ButtonStatus;
031.                    bool    ExecStudy;
032.            };
033. //+------------------------------------------------------------------+
034.    protected:
035.            enum eEventsMouse {ev_HideMouse, ev_ShowMouse};
036. //+------------------------------------------------------------------+
037.            void CreateObjectInfo(int x, int w, string szName, color backColor = clrNONE) const
038.                    {
039.                            if (m_Mem.IsTranslator) return;
040.                            def_AcessTerminal.CreateObjectGraphics(szName, OBJ_BUTTON, clrNONE);
041.                            ObjectSetInteger(def_InfoTerminal.ID, szName, OBJPROP_STATE, true);
042.                    	ObjectSetInteger(def_InfoTerminal.ID, szName, OBJPROP_BORDER_COLOR, clrBlack);
043.                            ObjectSetInteger(def_InfoTerminal.ID, szName, OBJPROP_COLOR, clrBlack);
044.                            ObjectSetInteger(def_InfoTerminal.ID, szName, OBJPROP_BGCOLOR, backColor);
045.                            ObjectSetString(def_InfoTerminal.ID, szName, OBJPROP_FONT, "Lucida Console");
046.                            ObjectSetInteger(def_InfoTerminal.ID, szName, OBJPROP_FONTSIZE, 10);
047.                            ObjectSetInteger(def_InfoTerminal.ID, szName, OBJPROP_CORNER, CORNER_LEFT_UPPER); 
048.                            ObjectSetInteger(def_InfoTerminal.ID, szName, OBJPROP_XDISTANCE, x);
049.                            ObjectSetInteger(def_InfoTerminal.ID, szName, OBJPROP_YDISTANCE, TerminalInfoInteger(TERMINAL_SCREEN_HEIGHT) + 1);
050.                            ObjectSetInteger(def_InfoTerminal.ID, szName, OBJPROP_XSIZE, w); 
051.                            ObjectSetInteger(def_InfoTerminal.ID, szName, OBJPROP_YSIZE, 18);
052.                    }
053. //+------------------------------------------------------------------+
054.    private :
055.            enum eStudy {eStudyNull, eStudyCreate, eStudyExecute};
056.            struct st01
057.            {
058.                    st_Mouse Data;
059.                    color    corLineH,
060.                             corTrendP,
061.                             corTrendN;
062.                    eStudy   Study;
063.            }m_Info;
064.            struct st_Mem
065.            {
066.                    bool     CrossHair,
067.                             IsFull,
068.                             IsTranslator;
069.                    datetime dt;
070.                    string   szShortName;
071.            }m_Mem;
072. //+------------------------------------------------------------------+
073.            C_Terminal *Terminal;
074. //+------------------------------------------------------------------+
075.            void GetDimensionText(const string szArg, int &w, int &h)
076.                    {
077.                            TextSetFont("Lucida Console", -100, FW_NORMAL);
078.                            TextGetSize(szArg, w, h);
079.                            h += 5;
080.                            w += 5;
081.                    }
082. //+------------------------------------------------------------------+
083.            void CreateStudy(void)
084.                    {
085.                            if (m_Mem.IsFull)
086.                            {
087.                                    def_AcessTerminal.CreateObjectGraphics(def_NameObjectLineV, OBJ_VLINE, m_Info.corLineH);
088.                                    def_AcessTerminal.CreateObjectGraphics(def_NameObjectLineT, OBJ_TREND, m_Info.corLineH);
089.                                    ObjectSetInteger(def_InfoTerminal.ID, def_NameObjectLineT, OBJPROP_WIDTH, 2);
090.                                    CreateObjectInfo(0, 0, def_NameObjectStudy);
091.                            }
092.                            m_Info.Study = eStudyCreate;
093.                    }
094. //+------------------------------------------------------------------+
095.            void ExecuteStudy(const double memPrice)
096.                    {
097.                            double v1 = GetInfoMouse().Position.Price - memPrice;
098.                            int w, h;
099.                            
100.                            if (!CheckClick(eClickLeft))
101.                            {
102.                                    m_Info.Study = eStudyNull;
103.                                    ChartSetInteger(def_InfoTerminal.ID, CHART_MOUSE_SCROLL, true);
104.                                    if (m_Mem.IsFull)       ObjectsDeleteAll(def_InfoTerminal.ID, def_MousePrefixName + "T");
105.                            }else if (m_Mem.IsFull)
106.                            {
107.                                    string sz1 = StringFormat(" %." + (string)def_InfoTerminal.nDigits + "f [ %d ] %02.02f%% ",
108.                                            MathAbs(v1), Bars(def_InfoTerminal.szSymbol, PERIOD_CURRENT, m_Mem.dt, GetInfoMouse().Position.dt) - 1, MathAbs((v1 / memPrice) * 100.0)));
109.                                    GetDimensionText(sz1, w, h);
110.                                    ObjectSetString(def_InfoTerminal.ID, def_NameObjectStudy, OBJPROP_TEXT, sz1);                                                                                                                           
111.                                    ObjectSetInteger(def_InfoTerminal.ID, def_NameObjectStudy, OBJPROP_BGCOLOR, (v1 < 0 ? m_Info.corTrendN : m_Info.corTrendP));
112.                                    ObjectSetInteger(def_InfoTerminal.ID, def_NameObjectStudy, OBJPROP_XSIZE, w);
113.                                    ObjectSetInteger(def_InfoTerminal.ID, def_NameObjectStudy, OBJPROP_YSIZE, h);
114.                                    ObjectSetInteger(def_InfoTerminal.ID, def_NameObjectStudy, OBJPROP_XDISTANCE, GetInfoMouse().Position.X - w);
115.                                    ObjectSetInteger(def_InfoTerminal.ID, def_NameObjectStudy, OBJPROP_YDISTANCE, GetInfoMouse().Position.Y - (v1 < 0 ? 1 : h));                            
116.                                    ObjectMove(def_InfoTerminal.ID, def_NameObjectLineT, 1, GetInfoMouse().Position.dt, GetInfoMouse().Position.Price);
117.                                    ObjectSetInteger(def_InfoTerminal.ID, def_NameObjectLineT, OBJPROP_COLOR, (memPrice > GetInfoMouse().Position.Price ? m_Info.corTrendN : m_Info.corTrendP));
118.                            }
119.                            m_Info.Data.ButtonStatus = eKeyNull;
120.                    }
121. //+------------------------------------------------------------------+
122.    public  :
123. //+------------------------------------------------------------------+
124.            C_Mouse(const string szShortName)
125.                    {
126.                            Terminal = NULL;
127.                            m_Mem.IsTranslator = true;
128.                            m_Mem.szShortName = szShortName;
129.                    }
130. //+------------------------------------------------------------------+
131.            C_Mouse(C_Terminal *arg, color corH = clrNONE, color corP = clrNONE, color corN = clrNONE)
132.                    {
133.                            m_Mem.IsTranslator = false;
134.                            Terminal = arg;
135.                            if (CheckPointer(Terminal) == POINTER_INVALID) SetUserError(C_Terminal::ERR_PointerInvalid);
136.                            if (_LastError != ERR_SUCCESS) return;
137.                            m_Mem.CrossHair = (bool)ChartGetInteger(def_InfoTerminal.ID, CHART_CROSSHAIR_TOOL);
138.                            ChartSetInteger(def_InfoTerminal.ID, CHART_EVENT_MOUSE_MOVE, true);
139.                            ChartSetInteger(def_InfoTerminal.ID, CHART_CROSSHAIR_TOOL, false);
140.                            ZeroMemory(m_Info);
141.                            m_Info.corLineH  = corH;
142.                            m_Info.corTrendP = corP;
143.                            m_Info.corTrendN = corN;
144.                            m_Info.Study = eStudyNull;
145.                            if (m_Mem.IsFull = (corP != clrNONE) && (corH != clrNONE) && (corN != clrNONE))
146.                                    def_AcessTerminal.CreateObjectGraphics(def_NameObjectLineH, OBJ_HLINE, m_Info.corLineH);
147.                    }
148. //+------------------------------------------------------------------+
149.            ~C_Mouse()
150.                    {
151.                            if (CheckPointer(Terminal) == POINTER_INVALID) return;
152.                            ChartSetInteger(def_InfoTerminal.ID, CHART_EVENT_OBJECT_DELETE, 0, false);
153.                            ChartSetInteger(def_InfoTerminal.ID, CHART_EVENT_MOUSE_MOVE, false);
154.                            ChartSetInteger(def_InfoTerminal.ID, CHART_CROSSHAIR_TOOL, m_Mem.CrossHair);
155.                            ObjectsDeleteAll(def_InfoTerminal.ID, def_MousePrefixName);
156.                    }
157. //+------------------------------------------------------------------+
158. inline bool CheckClick(const eBtnMouse value) 
159.                    {
160.                            return (GetInfoMouse().ButtonStatus & value) == value;
161.                    }
162. //+------------------------------------------------------------------+
163. inline const st_Mouse GetInfoMouse(void)
164.                    {
165.                            if (m_Mem.IsTranslator)
166.                            {
167.                                    double Buff[];
168.                                    uCast_Double loc;
169.                                    int handle = ChartIndicatorGet(ChartID(), 0, m_Mem.szShortName);
170.                                    
171.                                    ZeroMemory(m_Info.Data);
172.                                    if (CopyBuffer(handle, 0, 0, 4, Buff) == 4)
173.                                    {
174.                                            m_Info.Data.Position.Price = Buff[0];
175.                                            loc.dValue = Buff[1];
176.                                            m_Info.Data.Position.dt = loc._datetime;
177.                                            loc.dValue = Buff[2];
178.                                            m_Info.Data.Position.X = loc._int[0];
179.                                            m_Info.Data.Position.Y = loc._int[1];
180.                                            loc.dValue = Buff[3];
181.                                            m_Info.Data.ButtonStatus = loc._char[0];
182.                                            IndicatorRelease(handle);
183.                                    }
184.                            }
185. 
186.                            return m_Info.Data;
187.                    }
188. //+------------------------------------------------------------------+
189. virtual void DispatchMessage(const int id, const long &lparam, const double &dparam, const string &sparam)
190.                    {
191.                            int w = 0;
192.                            static double memPrice = 0;
193.                            
194.                            if (!m_Mem.IsTranslator) switch (id)
195.                            {
196.                                    case (CHARTEVENT_CUSTOM + ev_HideMouse):
197.                                            if (m_Mem.IsFull)       ObjectSetInteger(def_InfoTerminal.ID, def_NameObjectLineH, OBJPROP_COLOR, clrNONE);
198.                                            break;
199.                                    case (CHARTEVENT_CUSTOM + ev_ShowMouse):
200.                                            if (m_Mem.IsFull) ObjectSetInteger(def_InfoTerminal.ID, def_NameObjectLineH, OBJPROP_COLOR, m_Info.corLineH);
201.                                            break;
202.                                    case CHARTEVENT_MOUSE_MOVE:
203.                                            ChartXYToTimePrice(def_InfoTerminal.ID, m_Info.Data.Position.X = (int)lparam, m_Info.Data.Position.Y = (int)dparam, w, m_Info.Data.Position.dt, m_Info.Data.Position.Price);
204.                                            if (m_Mem.IsFull) ObjectMove(def_InfoTerminal.ID, def_NameObjectLineH, 0, 0, m_Info.Data.Position.Price = def_AcessTerminal.AdjustPrice(m_Info.Data.Position.Price));
205.                                            m_Info.Data.Position.dt = def_AcessTerminal.AdjustTime(m_Info.Data.Position.dt);
206.                                            ChartTimePriceToXY(def_InfoTerminal.ID, w, m_Info.Data.Position.dt, m_Info.Data.Position.Price, m_Info.Data.Position.X, m_Info.Data.Position.Y);
207.                                            if ((m_Info.Study != eStudyNull) && (m_Mem.IsFull)) ObjectMove(def_InfoTerminal.ID, def_NameObjectLineV, 0, m_Info.Data.Position.dt, 0);
208.                                            m_Info.Data.ButtonStatus = (uint) sparam;
209.                                            if (CheckClick(eClickMiddle))
210.                                                    if ((!m_Mem.IsFull) || ((color)ObjectGetInteger(def_InfoTerminal.ID, def_NameObjectLineH, OBJPROP_COLOR) != clrNONE)) CreateStudy();
211.                                            if (CheckClick(eClickLeft) && (m_Info.Study == eStudyCreate))
212.                                            {
213.                                                    ChartSetInteger(def_InfoTerminal.ID, CHART_MOUSE_SCROLL, false);
214.                                                    if (m_Mem.IsFull)       ObjectMove(def_InfoTerminal.ID, def_NameObjectLineT, 0, m_Mem.dt = GetInfoMouse().Position.dt, memPrice = GetInfoMouse().Position.Price);
215.                                                    m_Info.Study = eStudyExecute;
216.                                            }
217.                                            if (m_Info.Study == eStudyExecute) ExecuteStudy(memPrice);
218.                                            m_Info.Data.ExecStudy = m_Info.Study == eStudyExecute;
219.                                            break;
220.                                    case CHARTEVENT_OBJECT_DELETE:
221.                                            if ((m_Mem.IsFull) && (sparam == def_NameObjectLineH)) def_AcessTerminal.CreateObjectGraphics(def_NameObjectLineH, OBJ_HLINE, m_Info.corLineH);
222.                                            break;
223.                            }
224.                    }
225. //+------------------------------------------------------------------+
226. };
227. //+------------------------------------------------------------------+
228. #undef def_AcessTerminal
229. #undef def_InfoTerminal
230. //+------------------------------------------------------------------+
231. #undef def_MousePrefixName
232. #undef def_NameObjectLineV
233. #undef def_NameObjectLineH
234. #undef def_NameObjectLineT
235. #undef def_NameObjectStudy
236. //+------------------------------------------------------------------+
237. 

Der Grund, warum ich hier den vollständigen Code zur Verfügung stelle, ist, dass es keine Dateien in der Anwendung geben wird (zumindest im Moment). Wer also von den Verbesserungen profitieren möchte, kann dies mit Hilfe der angegebenen Codes tun. Wenn das System weiter fortgeschritten ist, werden wir die Investition wieder zurückzahlen. Aber im Moment sind sie noch nicht verfügbar. 

Wenn Sie sich den Code ansehen, werden Sie feststellen, dass es hervorgehobene Punkte gibt. Ich werde nicht im Detail auf sie eingehen, obwohl sie eine gewisse Bedeutung haben. Der Grund dafür ist, dass die meisten von ihnen selbsterklärend sind.

Der aus Zeile 20 der Klasse C_Study entfernte Code befindet sich jetzt in Zeile 19 der Klasse C_Mouse. Sehen Sie sich nun Zeile 18 der Datei C_Mouse.mqh an. Ich erkläre hier den öffentlichen Teil. Aber warum brauchen wir ihn? Der Grund dafür ist, dass ohne ihn alle Informationen nicht in dem Maße zugänglich sind, wie wir es brauchen. Wir müssen diese Daten wirklich öffentlich machen. Normalerweise verwenden wir diesen Teil nur einmal, aber aufgrund meiner Gewohnheit, geschützte Daten zuerst, private Daten als zweites und öffentliche Daten zuletzt auszuführen, sehen Sie diesen Teil in Zeile 122. Aber das liegt an meinem Programmierstil, den ich gewohnt bin.

In Zeile 20 gibt es eine weitere Enumeration, die bereits in der Klasse C_Mouse existierte, aber nicht öffentlich war, sowie eine Struktur, die in Zeile 21 beginnt und bis Zeile 32 reicht. In diesem speziellen Fall wird die Struktur nicht in einer öffentlichen globalen Variablen verwendet. Das Deklarieren von öffentlichen globalen Variablen in Klassen ist keine gute Praxis. Jede globale Klassenvariable muss immer privat oder geschützt sein und darf niemals öffentlich sein.

Ab Zeile 34 sind die Daten nicht mehr öffentlich zugänglich. Hier haben wir den geschützten Teil, der den Schutz von Daten, Funktionen und Verfahren gewährleistet. Das haben wir schon in früheren Artikeln gesehen, aber das ist nicht der Punkt. Das Problem liegt in der Prüfung in Zeile 39. Diese Kontrolle gab es vorher nicht. Wenn die zu prüfende Variable den Wert „true“ hat, ist es nicht möglich, Objekte zu erstellen, die mit der Methode CreateObjectInfo erzeugt werden. Warum tun wir das? Um dies zu verstehen, müssen wir uns den Code genauer ansehen.

Es folgen zwei Zeilen, in denen zwei Variablen deklariert werden: Zeile 68 und 70. Ursprünglich waren sie nicht im veröffentlichten Code enthalten. Aber in diesem Fall sind diese Variablen notwendig. Einer der Gründe wurde in Zeile 39 genannt: die Ausführung einer Methode oder Funktion zulassen oder sperren. Das Gleiche gilt für die Zeilen 165 und 194. Im Fall der Linie 165 gibt es ein stärkeres Motiv, auf das wir später eingehen werden. In Zeile 194 ist der Grund jedoch derselbe wie in Zeile 39: Es soll vermieden werden, eine Funktion oder Methode auszuführen, weil sie im Übersetzungsmodus nicht funktioniert oder nicht korrekt ausgeführt wird.

Wie zu erwarten, werden diese Variablen im Klassenkonstruktor initialisiert, das ist eine Tatsache. Aber in der Zwischenzeit haben wir nicht mehr einen Klassenkonstruktor. Jetzt haben wir zwei Konstruktoren für die Klasse C_Mouse 😱😵😲. Wenn Sie neu in der OOP-Programmierung sind, wird Sie das wahrscheinlich erschrecken. Dies ist jedoch völlig normal für OOP. Einer der Konstruktoren ist im Code zwischen den Zeilen 124 und 129 enthalten. Ein weiterer Konstruktor befindet sich zwischen den Zeilen 131 und 147. Obwohl wir zwei Konstruktoren haben, haben wir nur einen Destruktor.

Da einer der Konstruktoren für einen bestimmten Zweck und der zweite für einen anderen verwendet werden soll, müssen wir sie irgendwie trennen. Hierfür werden private globale Variablen verwendet. Ohne sie können wir eine solche Trennung kaum vornehmen.

Schauen wir uns nun die Gründe an, warum wir zwei Konstruktoren haben. Erstens macht es keinen Sinn, eine zweite Klasse zu schaffen, nur um die Nachfrage zu befriedigen, die wir brauchen. Zweitens können wir den Ablauf der Ausführung so steuern, dass wir vorhandenen Code wiederverwenden können. All dies ist möglich, ohne dass eine Vererbung oder eine Manipulation von Daten, Funktionen und Prozeduren erforderlich ist.

Wenn Sie genau hinsehen, können Sie erkennen, dass wir bisher nur zwei Codes isoliert haben: einen öffentlichen und einen geschützten. Wenn wir also die Klasse C_Mouse verwenden, um einen bestimmten Zweck zu erfüllen, dann haben wir ein Nutzungsmodell. Wenn es jedoch darum geht, andere Arten von Zielen zu erfüllen, werden wir ein anderes Modell haben. Aber die Art und Weise, wie Sie mit einer Klasse programmieren, wird immer die gleiche sein, wenn wir sie in unseren Funktionen verwenden.

Sehen Sie sich den Konstruktor an, der zwischen den Zeilen 131 und 147 steht. Sie haben wahrscheinlich bemerkt, dass es sich praktisch nicht von dem in „Entwicklung eines Replay Systems (Teil 31): Das Expert Advisor Projekt — C_Mouse Klasse (V)“ vorgestellten System unterscheidet: Wir haben dies absichtlich so gemacht, weil dieser Konstruktor für die Erfüllung der Zwecke der Klasse verantwortlich sein wird, wenn der Code die in der Klasse vorhandenen Objekte verwendet. Der Unterschied zwischen diesem Konstruktor und dem ursprünglichen Konstruktor besteht in Zeile 133, in der eine neue Variable initialisiert wird, um anzuzeigen, dass wir die Klasse in ihrer ursprünglichen Form verwenden werden.

Die Tatsache, dass dies geschieht, bedeutet, dass der größte Teil des Codes derselbe bleibt und alle Erklärungen, die in diesem speziellen Artikel gegeben werden, immer noch relevant sind. Als Nächstes können wir uns auf die zweite Art der Verwendung der Klasse konzentrieren. Sehen Sie sich dazu zunächst den Code des Konstruktors zwischen den Zeilen 124 und 129 an. Trotz seiner Einfachheit ist zu beachten, dass wir alle Variablen initialisieren, die tatsächlich benötigt werden.

Das macht es einfacher, die Klasse als Übersetzer zu verwenden. Vielleicht haben Sie nicht verstanden, wie oder warum Sie das tun sollten. Aber wir werden die Klasse C_Mouse als Mauszeiger-Übersetzer verwenden. Das scheint wahrscheinlich ziemlich kompliziert und schwierig zu sein, nicht wahr? Aber es ist überhaupt nicht schwierig. Es gibt einen Grund für all dies.

Um dies zu verstehen, müssen Sie ein wenig nachdenken: Wenn wir Programme erstellen, seien es Indikatoren, Expert Advisors, Skripte oder etwas anderes, neigen wir dazu, Dinge hinzuzufügen, die sich häufig wiederholen. Einer dieser Punkte ist der Umgang mit Mäusen. Wenn wir in jedem EA Funktionen zur Erstellung von Studien oder Mausanalysen hinzufügen müssen, wird unser Code niemals wirklich zuverlässig sein. Wenn wir jedoch etwas schaffen, das lange Zeit Bestand hat, kann es unabhängig vom restlichen Code verbessert werden. Dann werden wir eine neue Art von Programm haben. Natürlich wird der Code dadurch viel zuverlässiger und effizienter.

In dieser Klasse wird immer davon ausgegangen, dass sich der Mauszeiger auf dem Chart befindet. Denken Sie daran: Wir sollten nie von etwas ausgehen, aber in diesem Fall gehen wir davon aus, dass der Indikator im Chart angezeigt wird. Diejenigen, die die Klasse C_Mouse als Übersetzer verwenden, müssen diese Tatsache kennen. Die Klasse geht davon aus, dass sich der Indikator im Chart befindet.

Wenn mit dieser Tatsache alles klar ist, können Sie zur nächsten Frage übergehen. In der Tat brauchen wir die Klasse C_Mouse nicht, um den Indikator für uns zu übersetzen, da dies direkt in dem erstellten Programm geschehen kann. Es ist jedoch viel einfacher und bequemer, die Klasse diese Übersetzung für uns machen zu lassen. Der Grund dafür ist, dass wir, wenn wir keinen Übersetzer verwenden wollen, nur den Klassenkonstruktor ändern und einen Aufruf zur Ereignisbehandlung in unser Programm einfügen müssen.

Dann werden Sie verstehen, wie viel einfacher es für Sie sein wird, Ihre Aufgaben zu bewältigen. Aber ich möchte trotzdem, dass Sie das Übersetzungssystem nutzen. Um zu verstehen, wie das Übersetzungssystem funktioniert, genügt ein Blick auf die Funktion GetInfoMouse in Zeile 163. Am Anfang war es eine konstante Funktion, aber jetzt nicht mehr. Obwohl die Daten außerhalb der Funktion nicht geändert werden können, müssen wir sie innerhalb der Funktion ändern.

Kehren wir zu dem zurück, was wir im vorherigen Artikel Entwicklung eines Replay Systems (Teil 40) Start der zweiten Phase (I) besprochen haben. Sie werden sehen, dass es sehr schwierig ist, Indikatorpufferdaten zu interpretieren, oder besser gesagt, den Kodierungsstandard beizubehalten. Deshalb zeige ich Ihnen, wie Sie die Klasse C_Mouse ändern können, um diese Standardisierung zu erreichen. Um sich vorzustellen, wie schwierig das ist, stellen Sie sich vor, dass wir jedes Mal, wenn wir den Mauszeiger verwenden müssen, dasselbe schreiben müssen, was wir in den Funktionen CheckClick und GetInfoMouse sehen. Das ist ein ziemliches Ärgernis.

Schauen wir mal, was hier los ist. Beginnen wir mit der Funktion CheckClick. In dieser Funktion wird einfach der Wert in die Mausdaten geladen und überprüft, entweder als Zeiger oder als Übersetzer. Wir tun dies in jedem Fall, sei es, um den Indikator zu analysieren oder um den Indikator selbst zu verwenden. In Zeile 160 wird geprüft, ob die zu analysierende Taste aktiviert wurde, d. h. ob sie gedrückt wurde oder nicht.

Das ist die Frage, die es zu verstehen gilt. Unabhängig davon, ob wir die Klasse C_Mouse als Übersetzer oder als Zeiger verwenden, werden wir immer eine Art von Antwort von den Funktionen CheckClick und GetInfoMouse erhalten. Immer. Diese Antwort wird immer das wiedergeben, was die Maus tut, egal wo wir die Informationen verwenden.

Um die von CheckClick gegebene Antwort zu verstehen, müssen wir die Funktionsweise der Funktion GetInfoMouse nachvollziehen und verstehen. Diese Funktion ist ab Zeile 163 zu sehen.

In Zeile 165 der Funktion prüfen wir, ob wir die Klasse als Übersetzer oder als Zeiger verwenden. Wenn die Prüfung erfolgreich ist, wechselt die Klasse in den Übersetzermodus, und dann müssen wir auf den Zeigerpuffer zugreifen. Diese Frage ist sowohl komplex als auch einfach. Sehen wir uns zuerst den einfachen Teil an.

In Zeile 171 setzen wir die Daten der Rückgabestruktur zurück. Es handelt sich dabei um eine private, globale Klassenvariable, die in Zeile 58 deklariert wird. Danach bitten wir MetaTrader 5, den Indikatorpuffer zu lesen. Wenn das Lesen aus Zeile 172 korrekt ist, beginnen wir mit der Konvertierung der Daten in die Rückgabestruktur.

Die Datenkonvertierung folgt der gleichen Logik wie das Schreiben von Daten. Der entsprechende Code ist in der Funktion SetBuffer aus dem vorherigen Artikel zu sehen. Um die Sache zu vereinfachen, stelle ich die gleiche Funktion unten zur Verfügung.

Codefragment des Indikators:

102. inline void SetBuffer(void)
103. {
104.    uCast_Double Info;
105.    
106.    m_posBuff = (m_posBuff < 0 ? 0 : m_posBuff);
107.    m_Buff[m_posBuff + 0] = (*Study).GetInfoMouse().Position.Price;
108.    Info._datetime = (*Study).GetInfoMouse().Position.dt;
109.    m_Buff[m_posBuff + 1] = Info.dValue;
110.    Info._int[0] = (*Study).GetInfoMouse().Position.X;
111.    Info._int[1] = (*Study).GetInfoMouse().Position.Y;
112.    m_Buff[m_posBuff + 2] = Info.dValue;
113.    Info._char[0] = ((*Study).GetInfoMouse().ExecStudy == C_Mouse::eStudyNull ? (char)(*Study).GetInfoMouse().ButtonStatus : 0);
114.    m_Buff[m_posBuff + 3] = Info.dValue;
115. }

Achten Sie darauf, dass die Information in Zeile 107 des Indikatorfragments in Zeile 174 der Klasse dekodiert wird. Meistens ist die Übersetzung sehr einfach, aber man muss sich an die Regeln halten, die bei der Kodierung festgelegt wurden. Dies lässt sich besser verstehen, wenn Sie die Daten in Zeile 112 des Indikatorfragments speichern. Beachten Sie, dass wir in diesem Fall mit zwei Werten arbeiten, die zu einem Double komprimiert sind. Wenn es um die Übersetzung geht, müssen wir das Gegenteil tun. Dies geschieht in Zeile 177 der Klasse, wo wir den Wert erfassen, während wir in den Zeilen 178 und 179 die Werte zur späteren Verwendung an der richtigen Stelle ablegen.

Das Gleiche geschieht in Zeile 113 des Indikatorfragments, wo wir den Mausklickwert speichern. Dann übersetzen wir es in Zeile 181 der Klasse. Aber schauen wir uns jetzt noch einmal die Zeile 113 des Indikatorfragments an. Beachten Sie, dass der ternäre Operator den Wert Null beibehält, wenn wir uns im Studienmodus befinden. Es ist wichtig, dass Sie das verstehen. Wenn wir die Studie über den Indikator durchführen und die Klasse für die Übersetzung verwendet wird, gibt die Funktion CheckClick bei der Überprüfung auf einen Klick false zurück. Dies wird immer der Fall sein, wenn wir einen Indikator und eine Klasse als Übersetzer verwenden.

Dies war der einfachste und verständlichste Teil, aber wie bereits erwähnt, gibt es noch einen anderen Teil: komplex und schwierig.

Sie erscheint, wenn wir eine Klasse als Übersetzer verwenden. Wir haben jedoch keinen Zugriff auf den Indikatorpuffer. Dies geschieht normalerweise, wenn der Indikator aus dem Chart entfernt wird. In diesem Fall wird in Zeile 169 ein Null-Handle erzeugt, und wir haben keinen Puffer zum Lesen. Wir müssen noch Zeile 171 ausführen, um die Daten wiederherzustellen.

Dies kann zu verschiedenen Verstößen und Fehlern führen, wenn man versucht, etwas mit den Indikatordaten zu machen. Obwohl das System immer Null meldet, haben wir keinen positiven Beweis für einen Klick oder eine Bewegung, haben wir immer noch Probleme damit. Nicht in diesem speziellen Fall, aber in anderen Fällen, die auch für uns ein Problem darstellen werden. Sobald dies geschehen ist, werden wir auf dieses Thema zurückkommen.


Verwendung der Klasse C_Mouse als Übersetzer

Der folgende Teil dient hauptsächlich zu Demonstrationszwecken, da wir dieses Thema später vertiefen werden.

Schauen wir uns den Expert Advisor an, der auf die gleiche Weise funktioniert wie im Artikel „Entwicklung eines Replay-Systems (Teil 31): Das Expert Advisor Projekt — C_Mouse class (V)“, aber der Code wird auf eine andere Weise geschrieben.

Der Code des Expert Advisors:

01. //+------------------------------------------------------------------+
02. #property copyright "Daniel Jose"
03. #property description "Generic EA for use on Demo account, replay system/simulator and Real account."
04. #property description "This system has means of sending orders using the mouse and keyboard combination."
05. #property description "For more information see the article about the system."
06. #property version   "1.41"
07. #property icon "/Images/Market Replay/Icons/Replay - EA.ico"
08. #property link "https://www.mql5.com/en/articles/11607"
09. //+------------------------------------------------------------------+
10. #include <Market Replay\Auxiliar\C_Mouse.mqh>
11. //+------------------------------------------------------------------+
12. C_Mouse *mouse = NULL;
13. //+------------------------------------------------------------------+
14. int OnInit()
15. {
16.     mouse = new C_Mouse("Indicator Mouse Study");
17.     
18.     return INIT_SUCCEEDED;
19. }
20. //+------------------------------------------------------------------+
21. void OnTick() 
22. { }
23. //+------------------------------------------------------------------+
24. void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam)
25. {
26.     C_Mouse::st_Mouse Infos;
27.     
28.     switch (id)
29.     {
30.             case CHARTEVENT_MOUSE_MOVE:
31.                     Infos = (*mouse).GetInfoMouse();
32.                     Comment((string)Infos.Position.Price + " :: [" + TimeToString(Infos.Position.dt), "]");
33.                     break;
34.     }
35. }
36. //+------------------------------------------------------------------+
37. void OnDeinit(const int reason)
38. {
39.     delete mouse;
40. }
41. //+------------------------------------------------------------------+

Obwohl es nicht so aussieht, hat der obige Code das gleiche Verhalten wie der Code in dem genannten Artikel, ist aber viel einfacher, praktischer und zuverlässiger. Der Grund dafür ist jetzt nicht leicht zu verstehen, aber Sie werden ihn nach und nach verstehen, wenn wir auf unserer Reise durch die Programmierung vorankommen.

Sie werden wahrscheinlich verstehen, dass wir in Wirklichkeit nichts Super-Komplexes machen. Wir gehen hier einfach davon aus, dass alles, was angegeben wird, auch in der Tabelle steht. In Zeile 16 geben wir den Kurznamen des Indikators an, und in Zeile 39 entfernen wir die Klasse C_Mouse.

Schauen Sie sich nun die Zeilen 31 und 32 an, in denen die Maus über die Chartdaten informiert wird, damit wir sie betrachten können.

Das Beste daran ist, dass wir, wenn wir in Zeile 16 erklären, dass wir die Klasse als Indikator verwenden, nur einen Aufruf der Funktion DispatchMessage in der Funktion OnChatEvent hinzufügen müssen, um denselben Effekt wie bei einem grafischen Indikator zu erzielen. Mit anderen Worten: Die Programmierung wird sich nicht ändern. Stattdessen wird dies nur eine Anpassung an das sein, was wir brauchen.

Wenn Sie die Funktion als Übersetzer verwenden möchten, analysiert der EA so weit, dass er weiß, wo und was die Maus tut, und meldet die Dinge immer entsprechend.


Schlussfolgerung

Es ist wichtig, dass Sie verstehen, wie die Dinge funktionieren. Andernfalls werden Sie in den nächsten Artikeln, in denen wir nicht mehr so handeln werden wie bisher, völlig verloren sein. Wir werden alles in einer viel komplexeren Form machen. Obwohl das, was in den letzten beiden Artikeln gezeigt wurde, komplex erscheint, ist es für diejenigen gedacht, die nicht viel Programmierkenntnisse haben. Alles, was wir in den letzten beiden Artikeln behandelt haben, ist nur eine kleine Vorbereitung für das Hinzufügen der Chart Trader-App. In den kommenden Artikeln werden wir damit beginnen, Chart Trader in unser System einzubinden und zu entwickeln. Diese App wird es uns ermöglichen, direkt am Markt zu handeln, was sehr nützlich und wichtig ist, zumal wir uns nicht auf das bestehende Marktorder-System verlassen können. Deshalb müssen wir unsere eigene Lösung entwickeln, nämlich Chart Trader.

Trotzdem arbeiten wir immer noch auf der Grundstufe von MQL5. Selbst wenn Sie glauben, dass dieser Stoff schwierig ist, handelt es sich doch um ein sehr grundlegendes Niveau. Aber es ist nicht schlimm, wenn man es zu schwierig findet, es ist ganz natürlich. Das kann passieren, weil Sie sich nicht intensiv mit MQL5 beschäftigt haben. Wenn Sie auf einer beliebigen Fläche laufen, denken Sie, dass Sie auf einem hohen Berg sind, bis Ihnen jemand zeigt, dass Sie die ganze Zeit auf Meereshöhe gelaufen sind. Glauben Sie mir, bald werden Sie denken, dass alles, was Sie in den letzten Artikeln gesehen haben, Kinderspiele sind, die jeder Junior machen kann. Also, macht euch bereit. Es kommen harte Zeiten auf uns zu. Wenn einer dieser Momente eintritt, werden wir auf diese Mauszeigerprobleme zurückkommen.


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

Beigefügte Dateien |
Anexo.zip (420.65 KB)
Entwicklung eines MQL5 RL-Agenten mit Integration von RestAPI (Teil 4): Organisieren von Funktionen in Klassen in MQL5 Entwicklung eines MQL5 RL-Agenten mit Integration von RestAPI (Teil 4): Organisieren von Funktionen in Klassen in MQL5
In diesem Artikel wird der Übergang von der prozeduralen Codierung zur objektorientierten Programmierung (OOP) in MQL5 mit Schwerpunkt auf der Integration mit der REST-API erörtert. Heute werden wir besprechen, wie HTTP-Anfragefunktionen (GET und POST) in Klassen organisiert werden können. Wir werden einen genaueren Blick auf das Refactoring von Code werfen und zeigen, wie isolierte Funktionen durch Klassenmethoden ersetzt werden können. Der Artikel enthält praktische Beispiele und Tests.
Entwicklung eines Replay Systems (Teil 40): Beginn der zweiten Phase (I) Entwicklung eines Replay Systems (Teil 40): Beginn der zweiten Phase (I)
Heute werden wir über die neue Phase des Replay/Simulator-Systems sprechen. In dieser Phase wird das Gespräch wirklich interessant und sehr inhaltsreich. Ich empfehle Ihnen dringend, den Artikel sorgfältig zu lesen und die darin enthaltenen Links zu nutzen. Dies wird Ihnen helfen, den Inhalt besser zu verstehen.
Neuronale Netze leicht gemacht (Teil 72): Entwicklungsvorhersage in verrauschten Umgebungen Neuronale Netze leicht gemacht (Teil 72): Entwicklungsvorhersage in verrauschten Umgebungen
Die Qualität der Vorhersage zukünftiger Zustände spielt eine wichtige Rolle bei der Methode des Goal-Conditioned Predictive Coding, die wir im vorherigen Artikel besprochen haben. In diesem Artikel möchte ich Ihnen einen Algorithmus vorstellen, der die Vorhersagequalität in stochastischen Umgebungen, wie z. B. den Finanzmärkten, erheblich verbessern kann.
Entwicklung eines Replay Systems (Teil 39): Den Weg ebnen (III) Entwicklung eines Replay Systems (Teil 39): Den Weg ebnen (III)
Bevor wir zur zweiten Stufe der Entwicklung übergehen, müssen wir einige Ideen überarbeiten. Wissen Sie, wie Sie MQL5 dazu bringen können, das zu tun, was Sie brauchen? Haben Sie jemals versucht, über das hinauszugehen, was in der Dokumentation enthalten ist? Wenn nicht, dann machen Sie sich bereit. Denn wir werden etwas tun, was die meisten Menschen normalerweise nicht tun.