English Русский 中文 Español 日本語 Português
preview
Entwicklung eines Replay-Systems (Teil 74): Neuer Chart-Handel (I)

Entwicklung eines Replay-Systems (Teil 74): Neuer Chart-Handel (I)

MetaTrader 5Beispiele |
110 0
Daniel Jose
Daniel Jose

Einführung

Im vorigen Artikel, „Entwicklung eines Replay-Systems (Teil 73): Eine ungewöhnliche Kommunikation (II)“ haben wir die zweite Entwicklungsphase unserer Anwendung abgeschlossen, die für die Erstellung der Widergabe, des Simulators zuständig ist. Mit anderen Worten: Wir haben es geschafft, dass sich das gesamte System gut strukturiert und funktional verhält. Genauer gesagt, wir haben jetzt ein System, das in der Lage ist, etwas zu präsentieren, das die realen Marktbewegungen genau widerspiegelt.

Alles, was wir bisher getan haben, stellt jedoch nur einen Teil der Gesamtaufgabe dar. Wir treten jetzt in die dritte Phase der Entwicklung ein. In dieser Phase wird der Schwerpunkt nicht mehr auf dem Wiedergabe-/Simulationssystems selbst liegen. Der Schwerpunkt wird nun auf der direkten Arbeit mit dem Live-Handelsserver liegen. Mit anderen Worten: Wir werden damit beginnen, die notwendigen Instrumente zu entwickeln, um Positionen zu eröffnen, zu verwalten und zu schließen.

Einige dieser Instrumente wurden bereits in dieser Serie besprochen. Da die Wiedergabe-/Simulationsanwendung jedoch im Laufe der Zeit viele Änderungen erfahren hat, müssen wir ältere Tools entweder neu erstellen oder zumindest an das neue Modell anpassen. Das erste Werkzeug, mit dem wir uns beschäftigen werden, ist Chart Trade. Dieses Tool wurde entwickelt, um uns beim Eröffnen und Schließen von Positionen und der Ausführung von Marktaufträgen zu unterstützen. MetaTrader 5 enthält diese Funktionalität bereits in seiner Standardinstallation. Sie verfügt über die Schaltflächen für den Ein-Klick-Handel, wie in der Abbildung unten dargestellt:

Obwohl diese Schaltflächen recht nützlich sind und einwandfrei funktionieren, sind sie für uns nicht von Nutzen, da sie für die direkte Kommunikation mit dem Live-Trading-Server konzipiert sind. Unser Wiedergabe-/Simulationssystem ist jedoch kein echter Server. Es handelt sich um einen Dienst, oder besser gesagt, um eine Reihe von Anwendungen, die einen echten Server simulieren sollen.

Aus diesem Grund müssen wir unsere eigenen Werkzeuge entwickeln. Auf diese Weise können wir die integrierten Funktionen des MetaTrader 5 nachbilden, die wir nicht nutzen können, da wir in einer simulierten Umgebung arbeiten.

Dies bringt uns zu einem wichtigen Punkt: Der ganze Aufwand, den ich hier betrieben habe, beruht auf der Entscheidung, dieses gesamte System ausschließlich in MQL5 zu erstellen. Dieser Zwang macht den Entwicklungsprozess aus technischer Sicht besonders interessant. Es war eine faszinierende Herausforderung, etwas zu entwickeln, das sich wie ein echter Handelsserver verhält, und zwar ausschließlich in MQL5. Aus rein programmiertechnischer Sicht wäre es wesentlich einfacher, eine Anwendung in C oder C++ unter Verwendung von Sockets zu erstellen. Eine solche Anwendung könnte alle erforderlichen Protokolle implementieren, damit MetaTrader 5 sie als echten Server wahrnimmt.

Dieser alternative Ansatz würde zwar viele Aspekte vereinfachen und es uns ermöglichen, MetaTrader 5 in seiner Standardkonfiguration zu verwenden - er würde jedoch nicht zu unserem Verständnis von MQL5 beitragen. Wenn alles in C/C++ implementiert wäre, gäbe es überhaupt keinen Grund, MQL5 zu verwenden. Das würde bedeuten, dass Sie viel wertvolles Wissen darüber verpassen, wie man effektiv in MQL5 programmiert. Seit ich die Herausforderung angenommen habe, etwas so Komplexes wie einen Simulator mit reinem MQL5 zu bauen, habe ich es geschafft, diesem Ziel bis jetzt treu zu bleiben. Aber jetzt kommen wir in eine Phase, in der wir eine Pause von der Wiedergabe/Simulation einlegen werden. Eine Zeit lang werden Sie sehen, wie die Dinge in einem Demokonto laufen, aber die Implementierung wird immer mit dem Simulator im Hinterkopf entworfen werden.


Die Geburt eines neuen Chart-Handels

Das letzte Mal haben wir im Artikel „Entwicklung eines Wiedergabe-Systems (Teil 47); Chart Trade Project (VI)“ über den Chart-Handel gesprochen:. Obwohl wir seither nicht mehr daran gearbeitet haben, ist es an der Zeit, es wieder aufzugreifen. Da sich jedoch seit diesem Artikel viel geändert hat, ist ein Großteil des alten Codes nicht mehr nützlich. Er ist inzwischen völlig veraltet und sollte entfernt werden. Dennoch sind viele der grundlegenden Konzepte aus dieser Zeit nach wie vor relevant und anwendbar. Wir werden also die Entwicklung fortsetzen, allerdings mit einem entscheidenden Unterschied: Der Code wird nun vollständig umstrukturiert, um die neuen Konzepte, die bis zu diesem Zeitpunkt entwickelt wurden, zu berücksichtigen.

Eine wichtige Sache sollten Sie jetzt verstehen: Indikatoren können KEINE Positionen oder Aufträge eröffnen, ändern oder schließen. Dies vermag allein ein Expert Advisor. Wir werden Chart Trade jedoch nicht als Expert Advisor implementieren. Stattdessen wird Chart Trade als Indikator entwickelt, der mit einem Expert Advisor kommuniziert, um die erforderlichen Aktionen durchzuführen.

Wenn Sie die vorangegangenen Artikel dieser Reihe verstanden haben, werden Sie sich daran erinnern, dass die Kommunikation zwischen Indikatoren und dem Dienst über nutzerdefinierte Ereignisse erfolgt. Die Verwendung dieser nutzerdefinierten Ereignisse ist bei weitem der praktischste Weg, um Daten zwischen Programmen innerhalb der Wiedergabe-/Simulationsumgebung zu übertragen. Auch wenn wir den Schwerpunkt vorübergehend vom Wiedergabe-/Simulationssystems weg verlagern, wird die gesamte Entwicklung weiterhin auf diesen Kontext ausgerichtet sein. Der erste Schritt besteht daher darin, einige neue nutzerdefinierte Ereigniswerte zu definieren. Die neue Datei Defines.mqh ist unten abgebildet:

01. //+------------------------------------------------------------------+
02. #property copyright "Daniel Jose"
03. //+------------------------------------------------------------------+
04. #define def_VERSION_DEBUG
05. //+------------------------------------------------------------------+
06. #ifdef def_VERSION_DEBUG
07.    #define macro_DEBUG_MODE(A) \
08.                Print(__FILE__, " ", __LINE__, " ", __FUNCTION__ + " " + #A + " = " + (string)(A));
09. #else
10.    #define macro_DEBUG_MODE(A)
11. #endif
12. //+------------------------------------------------------------------+
13. #define def_SymbolReplay         "RePlay"
14. #define def_MaxPosSlider          400
15. #define def_MaskTimeService       0xFED00000
16. #define def_IndicatorTimeFrame    (_Period < 60 ? _Period : (_Period < PERIOD_D1 ? _Period - 16325 : (_Period == PERIOD_D1 ? 84 : (_Period == PERIOD_W1 ? 91 : 96))))
17. #define def_IndexTimeFrame        4
18. //+------------------------------------------------------------------+
19. union uCast_Double
20. {
21.    double   dValue;
22.    long     _long;                                  // 1 Information
23.    datetime _datetime;                              // 1 Information
24.    uint     _32b[sizeof(double) / sizeof(uint)];    // 2 Informations
25.    ushort   _16b[sizeof(double) / sizeof(ushort)];  // 4 Informations
26.    uchar    _8b [sizeof(double) / sizeof(uchar)];   // 8 Informations
27. };
28. //+------------------------------------------------------------------+
29. enum EnumEvents    {
30.          evHideMouse,               //Hide mouse price line
31.          evShowMouse,               //Show mouse price line
32.          evHideBarTime,             //Hide bar time
33.          evShowBarTime,             //Show bar time
34.          evHideDailyVar,            //Hide daily variation
35.          evShowDailyVar,            //Show daily variation
36.          evHidePriceVar,            //Hide instantaneous variation
37.          evShowPriceVar,            //Show instantaneous variation
38.          evCtrlReplayInit,          //Initialize replay control
39.          evChartTradeBuy,           //Market buy event
40.          evChartTradeSell,          //Market sales event 
41.          evChartTradeCloseAll       //Event to close positions
42.                   };
43. //+------------------------------------------------------------------+

Der Quellcode der Datei Defines.mqh

Beachten Sie, dass die einzigen Änderungen in den Zeilen 39, 40 und 41 zu finden sind. Diese Zeilen definieren die nutzerdefinierten Ereignisse, die vom Indikator Chart Trade ausgelöst werden, um den Expert Advisor darüber zu informieren, dass eine Aktion durchgeführt werden muss. Der Expert Advisor ist nämlich für die Ausführung zuständig: das Öffnen und Schließen von Positionen. Es ist wichtig klarzustellen, dass wir hier nicht von Aufträgen im eigentlichen Sinne sprechen. Der eigentliche Zweck des Indikators Chart Trades besteht darin, das Standardsystem von MetaTrader 5 für das Eröffnen und Schließen von Positionen zu ersetzen, sei es durch die Erhöhung der Positionsgröße oder die Durchführung von Teilschließungen. Der Indikator Chart Trade erfüllt dieselben Funktionen, hat aber den zusätzlichen Vorteil, dass wir die gleiche Logik später in der Wiedergabe-/Simulator-Umgebung anwenden können. In diesem Zusammenhang werden die Operationen immer zu Marktbedingungen durchgeführt. In Zukunft werden wir mit der Arbeit an einem separaten System beginnen, das es uns ermöglicht, schwebende Aufträge zu verwenden. Aber lassen Sie uns einen Schritt nach dem anderen tun. Das am einfachsten umzusetzende Konzept ist die Marktausführung.

Damit müssen wir nun den alten Indikator wieder zum Laufen bringen. Sie haben doch nicht ernsthaft geglaubt, dass wir den gesamten Indikator von Grund auf neu schreiben, oder? Das war nie die Absicht. Ein solches Vorgehen wäre völlig irrational.

Werfen wir also einen Blick auf den aktualisierten Indikatorcode, der aus mehreren Gründen geändert wurde, die Sie im weiteren Verlauf verstehen werden. Wir beginnen mit der Untersuchung der Header-Datei C_AdjustTemplate.mqh. Der vollständige Code ist nachstehend aufgeführt:

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

Quellcode der Datei C_AdjustTemplate.mqh

Einige Zeilen in diesem Code wurden durchgestrichen. Jetzt wollen wir verstehen, warum das passiert ist. Zeile 14 wurde geändert und durch den Code ersetzt, der sich jetzt in Zeile 13 befindet. Diese Änderung spiegelt das Verschieben der Vorlagendatei in einen neuen Pfad wider. Machen Sie sich keine Sorgen, denn diese Dateien sind im Anhang enthalten, falls Sie sie noch nicht haben. Alles, was Sie tun müssen, ist, die im Anhang angegebene Ordnerstruktur beizubehalten. Auf diese Weise können die hier angegebenen Ressourcen ohne Probleme gefunden werden. Zusätzlich zu dieser Änderung wurden auch einige andere Zeilen gestrichen.

Zeile 45 wurde aus Gründen, die bereits in früheren Artikeln erörtert wurden, gestrichen. Das heißt, manchmal enthält die Variable _LastError einen Wert, der nicht auf einen Fehler während der Ausführung, sondern auf einen anderen internen Grund zurückzuführen ist. Daher werde ich mich nicht mit dem Wert von _LastError befassen, es sei denn, es handelt sich um einen Sonderfall und ist wirklich relevant.

Schauen wir uns nun die Zeile 90 an. Warum wurde diese Zeile entfernt? Denn _LastError kann einen anderen Wert als ERR_SUCCESS haben, auch wenn er nicht von unserer Anwendung verursacht wurde. Um mögliche Fehlinterpretationen oder Konflikte zu vermeiden, mussten wir hier einige Anpassungen vornehmen.

Was früher ein Verfahren war, ist jetzt zu einer Funktion geworden. Schauen wir uns also Zeile 84 an. Sie werden sehen, dass sie durchgestrichen und durch Zeile 85 ersetzt wurde. Warum wurde diese Änderung vorgenommen? Der Grund ist einfach: Es können gelegentlich Fehler auftreten, und es ist besser, sich nicht stark oder direkt auf die Variable _LastError zu verlassen.

Sie müssen wissen, dass dieser Indikator in einem sehr strengen Umfeld arbeitet. Im Gegensatz zu schwebenden Aufträgen, bei denen kleinere Fehler manchmal (wir werden später sehen, warum „manchmal“) toleriert werden können, ist dies bei diesem Indikator nicht möglich. Das liegt daran, dass es sich um eine Marktausführung handelt.

Darüber hinaus gibt es eine noch kritischere Komplikation, die wir in einem späteren Artikel behandeln werden. Zunächst einmal müssen wir sicherstellen, dass die Daten in der Vorlagendatei absolut korrekt sind.

Wenn also in den Zeilen 91 oder 110 ein Fehler angezeigt wird, müssen wir dies an den Anrufer an anderer Stelle zurückmelden. Wenn jedoch alles korrekt ist, wird die Vorlagendatei aktualisiert, und in Zeile 113 wird ein Erfolg zurückgegeben. So einfach ist das.

Bevor ich diese Erklärung zur Header-Datei abschließe, möchte ich Ihre Aufmerksamkeit auf den Inhalt von Zeile 101 lenken. Die Zeile 100 wurde entfernt und durch diese ersetzt. Jetzt haben wir Zeile 101. Dies ist eine Zeichenkette. Sie gibt die erwartete Position des Indikators Chart Trade an. Mit anderen Worten, der Platz hat sich geändert.

Sie müssen nun sicherstellen, dass der Indikator genau an der Stelle platziert wird, die in der Zeichenfolge angegeben ist, und dass sein Name mit dem angegebenen übereinstimmt. Wenn diese Zeichenfolge verwendet wird, greifen wir nämlich auf Bitmap-Dateien zu, die im Indikator als Ressourcen deklariert sind. Wenn Sie irgendetwas ändern, sei es den Code, den Namen des Indikators oder den Speicherort des Ordners, kann MetaTrader 5 nicht mehr auf die Bitmap-Dateien zugreifen. Infolgedessen wird der Indikator Chart Trade nicht richtig auf dem Chart dargestellt.

Normalerweise gehe ich alle Header-Dateien durch, bevor ich den Hauptquellcode zeige, aber dieses Mal mache ich eine Ausnahme. Wir überspringen eine Header-Datei, die eigentlich das Herzstück des Indikators ist, und springen direkt in den Quellcode des Indikators selbst. Der vollständige Code ist nachstehend aufgeführt:

01. //+------------------------------------------------------------------+
02. #property copyright "Daniel Jose"
03. #property description "Chart Trade Base Indicator."
04. #property description "See the articles for more details."
05. #property version   "1.74"
06. #property icon "/Images/Market Replay/Icons/Indicators.ico"
07. #property link "https://www.mql5.com/pt/articles/12413"
08. #property indicator_chart_window
09. #property indicator_plots 0
10. #property indicator_buffers 1
11. //+------------------------------------------------------------------+
12. #include <Market Replay\Chart Trader\C_ChartFloatingRAD.mqh>
13. //+------------------------------------------------------------------+
14. #define def_ShortName "Indicator Chart Trade"
15. //+------------------------------------------------------------------+
16. C_ChartFloatingRAD *chart = NULL;
17. //+------------------------------------------------------------------+
18. input long     user00 = 0;          //ID
19. input ushort   user01 = 1;          //Leverage
20. input double   user02 = 100.1;      //Finance Take
21. input double   user03 = 75.4;       //Finance Stop
22. //+------------------------------------------------------------------+
23. double m_Buff[];
24. //+------------------------------------------------------------------+
25. int OnInit()
26. {
27.    bool bErr;
28.       
29.    chart = new C_ChartFloatingRAD(user00, "Indicator Chart Trade", new C_Mouse(user00, "Indicator Mouse Study"), user01, user02, user03);
30.    chart = new C_ChartFloatingRAD(def_ShortName, new C_Mouse(0, "Indicator Mouse Study"), user01, user02, user03);
31.    
32.    if (_LastError >= ERR_USER_ERROR_FIRST) return INIT_FAILED;
33. 
34.    if (bErr = (_LastError != ERR_SUCCESS)) Print(__FILE__, " - [Error]: ", _LastError);
35. 
36.    SetIndexBuffer(0, m_Buff, INDICATOR_DATA);
37.    ArrayInitialize(m_Buff, EMPTY_VALUE);
38.    
39.    return (bErr ? INIT_FAILED : INIT_SUCCEEDED);
40.    return INIT_SUCCEEDED;
41. }
42. //+------------------------------------------------------------------+
43. int OnCalculate(const int rates_total, const int prev_calculated, const int begin, const double &price[])
44. {
45.    (*chart).MountBuffer(m_Buff, rates_total);
46.    
47.    return rates_total;
48. }
49. //+------------------------------------------------------------------+
50. void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam)
51. {
52.    if (_LastError < ERR_USER_ERROR_FIRST) 
53.       (*chart).DispatchMessage(id, lparam, dparam, sparam);
54.    (*chart).MountBuffer(m_Buff);
55.    
56.    ChartRedraw();
57. }
58. //+------------------------------------------------------------------+
59. void OnDeinit(const int reason)
60. {
61.    switch (reason)
62.    {
63.       case REASON_INITFAILED:
64.          ChartIndicatorDelete(ChartID(), 0, def_ShortName);
65.          break;
66.       case REASON_CHARTCHANGE:
67.          (*chart).SaveState();
68.          break;
69.    }
70. 
71.    delete chart;
72. }
73. //+------------------------------------------------------------------+

Quellcode des Indikators Chart Trade

Auf den ersten Blick mag dieser Code aufgrund der zahlreichen durchgestrichenen Zeilen kompliziert erscheinen. Der Grund dafür, dass so viele Zeilen entfernt wurden, ist, dass es sich um eine geänderte Version des ursprünglichen Codes handelt.

Falls Sie es noch nicht gesehen haben, finden Sie die Originalversion in dem Artikel „Entwicklung eines Wiedergabe-/Simulationssystems (Teil 74): Chart Trade Project (VI)“. Aber kommen Sie nicht in Versuchung, den Code aus diesem Artikel zu verwenden. Diese Version funktioniert ganz anders. Auch wenn dies kompliziert erscheinen mag, ist es eigentlich ganz einfach. Denn die gesamte Komplexität wurde in die Header-Datei verlagert. Es ist also äußerst wichtig, dass Sie genau verstehen, wie dieser Code funktioniert, um zu verstehen, was in der Header-Datei passiert, die wir später untersuchen werden.

Lassen Sie uns Schritt für Schritt vorgehen. In den meisten Fällen handelt es sich hier eher um Entfernen als um tatsächliche Änderungen. Als erstes haben wir die Zeile 10 gestrichen, wie Sie sehen, wurde sie durchgestrichen.

Jetzt könnte es knifflig werden. Durch das Entfernen der Indikatoreigenschaft, die die Anzahl der verwendeten Puffer angibt, teilen wir dem Compiler mit, dass dieser Indikator keine Puffer verwendet. Denn das ist der Standardwert für diese Eigenschaft. Mit anderen Worten, dieser Indikator wird keine internen Pufferdaten ausgeben, auf die über CopyBuffer zugegriffen werden könnte.

Das mag nach einer ernsten Komplikation klingen. Und wenn Sie immer noch nicht verstehen, was das Fehlen von Puffern für einen Indikator bedeutet, bedeutet das wahrscheinlich, dass Sie noch nicht ganz verstanden haben, wie die Dinge in MetaTrader 5 und der MQL5-Umgebung funktionieren. Ich werde versuchen, es für Sie aufzuschlüsseln, besonders wenn Sie noch lernen und danach streben, MQL5 zu beherrschen.

Ein Indikator dient dazu, uns zu unterstützen (indem er Berechnungen durchführt, etwas signalisiert oder, wie in diesem Fall, eine Nutzeroberfläche zur Interaktion bereitstellt). In solchen Anwendungsfällen, insbesondere im letzten, könnten Sie technisch gesehen auch andere Programmtypen, wie z. B. Skripte, verwenden. Das Problem bei Skripten ist jedoch, dass sie beim Wechsel des Zeitrahmens entfernt werden und nicht automatisch neu geladen werden. Sie müssten sie manuell wieder in die Karte einfügen. Das kann unangenehm sein. Deshalb verwenden wir in solchen Szenarien stattdessen Indikatoren.

Aber Indikatoren haben ihre eigenen Probleme, vor allem, wenn Sie Timer verwenden möchten. Wenn Sie einem Indikator einen Timer hinzufügen, wirkt sich dies auf alle anderen Indikatoren im selben Chart aus. Wenn wir also eine zeitgesteuerte Ausführung benötigen, ohne uns auf Skripte zu verlassen, wenden wir uns normalerweise an Expert Advisors (EAs) oder Dienste. Die Wahl zwischen ihnen hängt ganz vom Zweck der Zeitmessung ab.

Sicher, Chart Trade hätte als Service oder EA implementiert werden können. Es muss nicht unbedingt ein Indikator sein. Die Verwendung eines Dienstes könnte sogar nützlich sein, wenn wir möchten, dass Chart Trade über alle Charts hinweg funktioniert. Der Dienst könnte dann sicherstellen, dass die Chart-Trade-Objekte in jedem relevanten Chart korrekt angezeigt werden.

Das Problem bei diesem Ansatz liegt in der Ereignisbehandlung. Dienste haben keine integrierte Ereignishandler. Es handelt sich im Wesentlichen um Skripte, die nicht mit einem Chart verbunden sind. Die Verwendung eines Dienstes für Chart Trade würde also bedeuten, dass wir einen Indikator erstellen müssen. Der Indikator würde lediglich Ereignisse wie das Klicken auf eine Schaltfläche oder andere Nutzerinteraktionen verarbeiten.

Dies würde die Einrichtung komplizierter machen, als sie sein müsste. Wie sieht es mit der Verwendung von Chart Trade in einem EA aus? Das klingt vernünftiger. Bis zu einem gewissen Grad ist das der Fall. Aber es ist auch unpraktisch.

Warum? Ein Wort: Diversifizierung. Wenn Sie den Chart Trade in einen EA einbinden, wird er nur in diesem einen EA existieren. Wenn Sie sich später entscheiden, einen anderen EA zu erstellen, aus welchem Grund auch immer, müssen Sie den gesamten Code von Chart Trade erneut einbinden. Sicher, man könnte es mit der #include-Direktive modularisieren, wie in Zeile 12, und das würde gut funktionieren. Was aber, wenn Sie den Code von Chart Trade später verbessern? Sie müssten jeden EA, der es verwendet, neu kompilieren. Das ist ineffizient und fehleranfällig.

Nun, wir könnten in Betracht ziehen, Chart Trade als Teil von EAs einzubinden, ohne sie bei einem Upgrade alle neu kompilieren zu müssen. Das ist auch etwas, worüber man nachdenken sollte. Wir könnten so etwas wie eine DLL verwenden. Aber das würde noch mehr Komplexität schaffen, als es löst. Daher habe ich mich letztendlich entschieden, Chart Trade als Indikator beizubehalten. Aber im Gegensatz zu früher verwendet diese Version keine Puffer, um Daten für den externen Zugriff zu speichern. Die Dinge werden jetzt anders gehandhabt, und wir werden zu gegebener Zeit darauf eingehen. Kehren wir zum Code zurück, denn es gibt noch einige Aspekte zu verstehen.

Schauen Sie sich Zeile 14 an. Diese Definition erscheint nach dem #include in Zeile 12, und das ist sehr wichtig. Im Gegensatz zu globalen Variablen kann die Tatsache, ob ein #define vor oder nach einem #include steht, steuern, wie sich Dinge verhalten oder ob bestimmte Optionen innerhalb der eingeschlossenen Datei verfügbar sind. Seien Sie sich dessen also bewusst.

Da die Definition in Zeile 14 nach der Einbindung von C_ChartFloatingRAD.mqh erfolgt, ist sie in dieser Datei nicht sichtbar oder verwendbar. Hätten wir es vor dem #include platziert, dann wäre es für den Inhalt der Header-Datei C_ChartFloatingRAD.mqh sichtbar. Also ja, beim Programmieren ist die Reihenfolge wichtig. Machen wir weiter, denn es gibt noch viele interessante Dinge zu entdecken.

Zeile 18 wurde gestrichen, da sie nicht mehr erforderlich ist. Der Nutzer ist nun für das Hinzufügen des Indikators Chart Trade zum Chart verantwortlich, sodass wir die Chart-ID nicht definieren müssen. Zeile 23 wurde ebenfalls gestrichen, aber nicht, weil Zeile 10 gestrichen wurde. Diese beiden Zeilen sind nicht miteinander verbunden. In Zeile 10 wird geprüft, ob der Indikator existiert. Wenn die Antwort „Ja“ lautet, wird angegeben, auf wie viele Puffer von außerhalb des Indikators über CopyBuffer zugegriffen wurde. Aber selbst wenn wir den Zugriff auf die Puffer von außen nicht zulassen, könnten wir immer noch interne Puffer haben. Da wir sie jedoch nicht mehr benötigen, wurde Zeile 23 gestrichen. Die gleiche Argumentation gilt für die Zeilen 36 und 37.

Sie werden feststellen, dass eine Menge Code in OnInit durchgestrichen wurde. Das meiste davon wurde aufgrund von Änderungen entfernt, die im nächsten Artikel erläutert werden, in dem wir die Header-Datei C_ChartFloatingRAD.mqh untersuchen werden. Von dem gesamten entfernten Code sind nur drei Zeilen funktionell wichtig. Gehen wir sie durch, beginnend mit Zeile 30. Sie initialisiert die Klasse C_ChartFloatingRAD. Dies geschieht mit dem Operator „new“. Aber wie Sie wahrscheinlich wissen, geben Konstruktoren keine Werte zurück. Wie können wir also überprüfen, ob die Initialisierung fehlgeschlagen ist? Wir brauchen einen anderen Mechanismus.

In modulareren Programmen sollte der Konstruktor nur die Variablen und den internen Zustand initialisieren. Da es aber keine strikte Regel dagegen gibt, lassen wir den Konstruktor oft ein bisschen mehr tun. An dieser Stelle kann es schwierig werden. Ohne einen Mechanismus, der meldet, dass ein Konstruktor fehlgeschlagen ist, können wir Initialisierungsprobleme nicht lösen.

Glücklicherweise können wir dies mit MQL5 tun. Diese Methode ist zwar nicht unbedingt die beste, aber sie funktioniert. In Zeile 32 wird der Wert von _LastError überprüft. Wenn er einen vordefinierten Fehlercode enthält, betrachten wir die Initialisierung als fehlgeschlagen. Ich bin nicht sicher, ob ich diese Struktur so lassen kann, wie sie ist, aber dank Zeile 52 kann ich das. Wir werden jedoch später darauf eingehen. Wenn Zeile 32 feststellt, dass die Initialisierung fehlschlägt, wird eine Fehlerkonstante von OnInit zurückgegeben. Dies veranlasst MetaTrader 5, OnDeInit aufzurufen, wodurch der Indikator aus dem Chart entfernt wird. Andernfalls wird in Zeile 40 eine Erfolgskonstante zurückgegeben.

Sehen Sie sich nun die Zeilen 45 und 54 an. Beide wurden zuvor zum Einrichten von Puffern verwendet. Da wir jedoch keine Puffer mehr verwenden, sind diese Zeilen nun überflüssig und wurden entfernt. Achten Sie nun auf die Prüfung in Zeile 52. Diese Bedingung mag im Moment nicht viel Sinn machen, aber ohne sie wird Zeile 53 definitiv fehlschlagen. Warum das so ist, wird im nächsten Artikel klar, wenn wir uns die Klasse C_ChartFloatingRAD ansehen.

Abschließend wollen wir uns die Prozedur OnDeInit() ansehen. Sie wird von MetaTrader 5 automatisch ausgelöst, wenn der Indikator aus dem Chart entfernt wird. Jetzt kommt etwas Ungewöhnliches für OnDeInit. Ich meine Zeile 64. Warum ist diese Zeile überhaupt hier? Auf den ersten Blick ergibt das keinen Sinn. Das liegt daran, dass Sie sich den Code ansehen, ohne die Klasse C_ChartFloatingRAD zu kennen. Wenn Sie nur diese Datei betrachten, ist der einzige Grund für OnDeInit(REASON_INITFAILED) die Zeile 32. Aber es gibt noch andere Gründe in der Klasse C_ChartFloatingRAD. Aus diesem Grund habe ich diesen Aufruf hier zentralisiert. Es erscheint also Zeile 64. Diese Zeile sorgt dafür, dass der Indikator Chart Trade aus dem Chart entfernt wird. Die tieferen Gründe dafür werden im nächsten Artikel erläutert.


Abschließende Überlegungen

In diesem Artikel habe ich einige der Änderungen am Indikator Chart Trade vorgestellt. Versuchen Sie nicht, diesen Code vollständig zu verstehen oder zu verwenden, ohne vorher den nächsten Artikel zu lesen, in dem die Klasse C_ChartFloatingRAD erklärt wird. Ohne dies werden Sie wahrscheinlich schwerwiegende Fehler machen. Bitte haben Sie ein wenig Geduld und warten Sie auf den nächsten Artikel, denn das, was er Ihnen sagen wird, ist es wirklich wert. Sie werden eine Klasse sehen, in der der Indikator Chart Trade genau implementiert ist.

Wenn Sie diesen Code kompilieren wollen, stellen Sie sicher, dass Sie die von der Header-Datei C_AdjustTemplate.mqh verwendeten Daten haben. Diese sind in der Anlage enthalten. Sie können aber auch Ihre eigenen Bilder und Vorlagen für Chart Trade erstellen. Stellen Sie nur sicher, dass sie mit den Erwartungen der Klasse C_AdjustTemplate kompatibel sind. Das war's für den Moment. Vergessen Sie nicht, sich den nächsten Teil dieser Serie anzusehen.

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

Beigefügte Dateien |
Anexo.zip (9.92 KB)
Algorithmus der Atomic Orbital Search (AOS) Algorithmus der Atomic Orbital Search (AOS)
Der Artikel befasst sich mit dem Algorithmus der atomare Orbitalsuche (AOS), der die Konzepte des atomaren Orbitalmodells nutzt, um die Suche nach Lösungen zu simulieren. Der Algorithmus basiert auf Wahrscheinlichkeitsverteilungen und der Dynamik von Wechselwirkungen im Atom. In dem Artikel werden die mathematischen Aspekte von AOS im Detail erörtert, einschließlich der Aktualisierung der Positionen der Lösungsvorschläge und der Mechanismen der Energieaufnahme und -abgabe. AOS eröffnet neue Horizonte für die Anwendung von Quantenprinzipien auf Computerprobleme, indem es einen innovativen Ansatz zur Optimierung bietet.
Analyse der Auswirkungen des Wetters auf die Währungen der Agrarländer mit Python Analyse der Auswirkungen des Wetters auf die Währungen der Agrarländer mit Python
Welcher Zusammenhang besteht zwischen Wetter und Devisen? In der klassischen Wirtschaftstheorie wurde der Einfluss von Faktoren wie dem Wetter auf das Marktverhalten lange Zeit ignoriert. Aber alles hat sich geändert. Versuchen wir, Zusammenhänge zwischen den Witterungsbedingungen und der Stellung der Agrarwährungen auf dem Markt zu finden.
Entwicklung eines Replay-Systems (Teil 75): Neuer Chart-Handel (II) Entwicklung eines Replay-Systems (Teil 75): Neuer Chart-Handel (II)
In diesem Artikel geht es um die Klasse C_ChartFloatingRAD. Das ist es, was Chart Trade ausmacht. Doch damit ist die Erklärung noch nicht zu Ende. Wir werden sie im nächsten Artikel vervollständigen, da der Inhalt dieses Artikels recht umfangreich ist und ein tiefes Verständnis erfordert. Der hier dargestellte Inhalt ist ausschließlich für Bildungszwecke bestimmt. Die Anwendung sollte unter keinen Umständen zu einem anderen Zweck als zum Erlernen und Beherrschen der vorgestellten Konzepte verwendet werden.
Neuronale Netze im Handel: Direktionale Diffusionsmodelle (DDM) Neuronale Netze im Handel: Direktionale Diffusionsmodelle (DDM)
In diesem Artikel werden gerichtete Diffusionsmodelle diskutiert, die datenabhängiges anisotropes und gerichtetes Rauschen in einem Vorwärtsdiffusionsprozess ausnutzen, um aussagekräftige Graphendarstellungen zu erfassen.