Marktsimulation (Teil 05): Erstellen der Klasse C_Orders (II)
Einführung
Im vorangegangenen Artikel, Marktsimulation (Teil 04): Erstellen der Klasse C_Orders (I), habe ich mich in erster Linie darauf konzentriert zu erklären, wie der Code für das Senden von Markthandelsaufträgen aussehen würde. Die gesamte Erklärung zielte darauf ab, zu zeigen, wie Sie den Klassencode strukturieren können, um die vom Chart Trade Indikator erhaltenen Informationen zu entschlüsseln.
Aber auch ohne den Quellcode des Expert Advisors gesehen zu haben, glaube ich, dass diejenigen, die etwas Erfahrung haben, ihn bereits in die Praxis umsetzen können. Auch wenn es den Anschein haben mag, dass der im vorigen Artikel gezeigte Code nicht in der Lage ist, Operationen auf einem Live-Handelsserver auszuführen, so ist dies nicht ganz richtig. Es stimmt jedoch, dass der Code für die Verwendung von Konten mit HEDGING nicht ausreichen würde.
Für NETTING-Konten würde dieser Code bereits in der Lage sein, Positionen zu eröffnen und zu schließen. Bei Konten des Typs HEDGING verhält es sich jedoch ein wenig anders.
Wenn Sie z. B. versuchen, eine Kaufposition mit der Schaltfläche „Verkaufen“ zu schließen, würden Sie eigentlich eine Verkaufsposition eröffnen. Dies gilt für HEDGING-Konten. Bei einem NETTING-Konto würde die gleiche Aktion die Kaufposition schließen oder zumindest irgendeinen Effekt haben, wie zum Beispiel:
- Die Umkehr einer Position (flip). Dies wäre der Fall, wenn Sie ein größeres Volumen verkaufen als Ihre bestehende Kaufposition. In diesem Fall würden Sie von einer Kaufposition zu einer Verkaufsposition wechseln. Der Umfang der neuen Verkaufsposition entspricht der Differenz zwischen dem Volumen der vorherigen Kaufposition und dem Volumen, das bei der Umkehr verkauft wurde.
- Teilweise Schließung. Dies wäre der Fall, wenn das verkaufte Volumen kleiner wäre als die bestehende Kaufposition. In diesem Fall würde ein Teil des Kaufvolumens geschlossen werden, während der verbleibende Teil auf dem Markt offen bliebe.
All dies ist recht interessant und wurde bereits in einer anderen Artikelserie, die ich vor einiger Zeit veröffentlicht habe, ausführlich erläutert. In dieser Serie wurde ausführlich erklärt, wie man einen Expert Advisor erstellt, der automatisch arbeitet. Sie bestand aus 15 Artikeln, die jeweils einen der Aspekte und Vorkehrungen behandelten, Verkaufs,die für die Entwicklung eines solchen Expert Advisors erforderlich sind. Vom manuell bedienten Modell bis hin zum vollautomatischen System. Diejenigen, die mehr darüber erfahren möchten, sollten sich diese Reihe ansehen. Der erste Artikel ist hier zu finden: Lernen, wie man einen automatisch arbeitenden EA aufbaut (Teil 01): Konzepte und Strukturen.
Ein Großteil des Codes, der hier gezeigt wird, stammt aus dieser Serie. Der Schwerpunkt wird hier jedoch etwas anders liegen. Das liegt daran, dass unser Hauptziel ein anderes ist. Mit anderen Worten, es ist nicht unser Ziel, ein System zum Senden von Aufträgen oder zur Kommunikation mit einem Live-Handelsserver zu entwickeln. Was wir eigentlich wollen – und auch tun werden – ist die Entwicklung eines simulierten Servers. Doch bevor wir das tun können, müssen wir einige Dinge umsetzen. Da das ideale Szenario darin besteht, dass unser Wiedergabe-/Simulationssystem und der reale Handelsserver (ob in einem Demo- oder Live-Konto) über gemeinsame Komponenten verfügen, werden wir zunächst den Expert Advisor so implementieren, dass er mit dem realen Server kommunizieren kann. Sobald diese Implementierung abgeschlossen ist, werden wir mit der Entwicklung des simulierten Handelsservers fortfahren.
Der Grund für diese Vorgehensweise ist die Definition und Einschränkung der Arten von Aufrufen, die wir tatsächlich implementieren müssen. Zumindest anfangs habe ich nicht die Absicht, jeden einzelnen Aufruf zu simulieren, auf den ein echter Server reagieren kann, da wir in den meisten Fällen einfach nicht alle benötigen. Vielleicht ändere ich meine Meinung im Laufe der Zeit, aber im Moment reicht dieser Ansatz aus. Damit können wir nun mit dem ersten Thema dieses Artikels beginnen.
Handhabung der Nachricht zum Schließen von Positionen
Na gut. Wenn Sie den Inhalt des vorangegangenen Artikels noch nicht verstanden haben, sollten Sie ihn noch einmal lesen, denn wir werden einfach von dort aus weitermachen. Da jedoch eine kleine Verbesserung an der Codestruktur vorgenommen wurde, können Sie den vollständigen Code weiter unten noch einmal in voller Länge sehen. Die Teile, die bereits im vorigen Artikel erläutert wurden, werden hier nicht noch einmal aufgegriffen, da die vorgenommenen strukturellen Änderungen keine der früheren Erklärungen außer Kraft setzen. Kommen wir nun zum Code selbst.
001. //+------------------------------------------------------------------+ 002. #property copyright "Daniel Jose" 003. //+------------------------------------------------------------------+ 004. #include "..\Defines.mqh" 005. //+------------------------------------------------------------------+ 006. class C_Orders 007. { 008. protected: 009. //+------------------------------------------------------------------+ 010. inline const ulong GetMagicNumber(void) const { return m_Base.MagicNumber; } 011. //+------------------------------------------------------------------+ 012. bool ClosePosition(const ulong ticket) 013. { 014. bool IsBuy; 015. string szContract; 016. 017. if (!PositionSelectByTicket(ticket)) return false; 018. IsBuy = PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_BUY; 019. szContract = PositionGetString(POSITION_SYMBOL); 020. ZeroMemory(m_Base.TradeRequest); 021. m_Base.TradeRequest.action = TRADE_ACTION_DEAL; 022. m_Base.TradeRequest.type = (IsBuy ? ORDER_TYPE_SELL : ORDER_TYPE_BUY); 023. m_Base.TradeRequest.price = NormalizeDouble(SymbolInfoDouble(szContract, (IsBuy ? SYMBOL_BID : SYMBOL_ASK)), (int)SymbolInfoInteger(szContract, SYMBOL_DIGITS)); 024. m_Base.TradeRequest.position = ticket; 025. m_Base.TradeRequest.symbol = szContract; 026. m_Base.TradeRequest.volume = PositionGetDouble(POSITION_VOLUME); 027. m_Base.TradeRequest.deviation = 1000; 028. 029. return SendToPhysicalServer() != 0; 030. }; 031. //+------------------------------------------------------------------+ 032. private : 033. //+------------------------------------------------------------------+ 034. struct stBase 035. { 036. MqlTradeRequest TradeRequest; 037. ulong MagicNumber; 038. bool bTrash; 039. }m_Base; 040. //+------------------------------------------------------------------+ 041. struct stChartTrade 042. { 043. struct stEvent 044. { 045. EnumEvents ev; 046. string szSymbol, 047. szContract; 048. bool IsDayTrade; 049. ushort Leverange; 050. double PointsTake, 051. PointsStop; 052. }Data; 053. //--- 054. bool Decode(const EnumEvents ev, const string sparam) 055. { 056. string Res[]; 057. 058. if (StringSplit(sparam, '?', Res) != 7) return false; 059. stEvent loc = {(EnumEvents) StringToInteger(Res[0]), Res[1], Res[2], (bool)(Res[3] == "D"), (ushort) StringToInteger(Res[4]), StringToDouble(Res[5]), StringToDouble(Res[6])}; 060. if ((ev == loc.ev) && (loc.szSymbol == _Symbol)) Data = loc; 061. else return false; 062. 063. return true; 064. } 065. //--- 066. }m_ChartTrade; 067. //+------------------------------------------------------------------+ 068. ulong SendToPhysicalServer(void) 069. { 070. MqlTradeCheckResult TradeCheck; 071. MqlTradeResult TradeResult; 072. 073. ZeroMemory(TradeCheck); 074. ZeroMemory(TradeResult); 075. if (!OrderCheck(m_Base.TradeRequest, TradeCheck)) 076. { 077. PrintFormat("Order System - Check Error: %d", GetLastError()); 078. return 0; 079. } 080. m_Base.bTrash = OrderSend(m_Base.TradeRequest, TradeResult); 081. if (TradeResult.retcode != TRADE_RETCODE_DONE) 082. { 083. PrintFormat("Order System - Send Error: %d", TradeResult.retcode); 084. return 0; 085. }; 086. 087. return TradeResult.order; 088. } 089. //+------------------------------------------------------------------+ 090. ulong ToMarket(const ENUM_ORDER_TYPE type) 091. { 092. double price = SymbolInfoDouble(m_ChartTrade.Data.szContract, (type == ORDER_TYPE_BUY ? SYMBOL_ASK : SYMBOL_BID)); 093. double vol = SymbolInfoDouble(m_ChartTrade.Data.szContract, SYMBOL_VOLUME_STEP); 094. uchar nDigit = (uchar)SymbolInfoInteger(m_ChartTrade.Data.szContract, SYMBOL_DIGITS); 095. 096. ZeroMemory(m_Base.TradeRequest); 097. m_Base.TradeRequest.magic = m_Base.MagicNumber; 098. m_Base.TradeRequest.symbol = m_ChartTrade.Data.szContract; 099. m_Base.TradeRequest.price = NormalizeDouble(price, nDigit); 100. m_Base.TradeRequest.action = TRADE_ACTION_DEAL; 101. m_Base.TradeRequest.sl = NormalizeDouble(m_ChartTrade.Data.PointsStop == 0 ? 0 : price + (m_ChartTrade.Data.PointsStop * (type == ORDER_TYPE_BUY ? -1 : 1)), nDigit); 102. m_Base.TradeRequest.tp = NormalizeDouble(m_ChartTrade.Data.PointsTake == 0 ? 0 : price + (m_ChartTrade.Data.PointsTake * (type == ORDER_TYPE_BUY ? 1 : -1)), nDigit); 103. m_Base.TradeRequest.volume = NormalizeDouble(vol + (vol * (m_ChartTrade.Data.Leverange - 1)), nDigit); 104. m_Base.TradeRequest.type = type; 105. m_Base.TradeRequest.type_time = (m_ChartTrade.Data.IsDayTrade ? ORDER_TIME_DAY : ORDER_TIME_GTC); 106. m_Base.TradeRequest.stoplimit = 0; 107. m_Base.TradeRequest.expiration = 0; 108. m_Base.TradeRequest.type_filling = ORDER_FILLING_RETURN; 109. m_Base.TradeRequest.deviation = 1000; 110. m_Base.TradeRequest.comment = "Order Generated by Experts Advisor."; 111. 112. MqlTradeRequest TradeRequest[1]; 113. 114. TradeRequest[0] = m_Base.TradeRequest; 115. ArrayPrint(TradeRequest); 116. 117. return (((type == ORDER_TYPE_BUY) || (type == ORDER_TYPE_SELL)) ? SendToPhysicalServer() : 0); 118. }; 119. //+------------------------------------------------------------------+ 120. void CloseAllsPosition(void) 121. { 122. for (int count = PositionsTotal() - 1; count >= 0; count--) 123. { 124. if (PositionGetSymbol(count) != m_ChartTrade.Data.szContract) continue; 125. if (PositionGetInteger(POSITION_MAGIC) != m_Base.MagicNumber) continue; 126. ClosePosition(PositionGetInteger(POSITION_TICKET)); 127. } 128. }; 129. //+------------------------------------------------------------------+ 130. public : 131. //+------------------------------------------------------------------+ 132. C_Orders(const ulong magic) 133. { 134. m_Base.MagicNumber = magic; 135. } 136. //+------------------------------------------------------------------+ 137. void DispatchMessage(const int id, const long &lparam, const double &dparam, const string &sparam) 138. { 139. switch (id) 140. { 141. case CHARTEVENT_CUSTOM + evChartTradeBuy : 142. case CHARTEVENT_CUSTOM + evChartTradeSell : 143. case CHARTEVENT_CUSTOM + evChartTradeCloseAll: 144. if (m_ChartTrade.Decode((EnumEvents)(id - CHARTEVENT_CUSTOM), sparam)) switch (m_ChartTrade.Data.ev) 145. { 146. case evChartTradeBuy: 147. ToMarket(ORDER_TYPE_BUY); 148. break; 149. case evChartTradeSell: 150. ToMarket(ORDER_TYPE_SELL); 151. break; 152. case evChartTradeCloseAll: 153. CloseAllsPosition(); 154. break; 155. } 156. break; 157. } 158. } 159. //+------------------------------------------------------------------+ 160. }; 161. //+------------------------------------------------------------------+
Code der Header-Datei C_Orders.mqh
Und hier ist er. Der oben gezeigte Code ist in der Lage, ordnungsgemäß auf alle Nachrichten zu reagieren, die der Indikator Chart Trade an den Expert Advisor sendet, um mit dem Handelsserver zu kommunizieren. Von diesem Zeitpunkt an sind alle Schaltflächen und Steuerelemente des Indikators Chart Trade funktionsfähig. Sie können diesen Code in jedem Expert Advisor verwenden – vorausgesetzt, Sie nehmen einige Anpassungen vor, auf die wir später in diesem Artikel eingehen werden. Die Idee ist, genau dies zu ermöglichen. Sie werden in der Lage sein, die Module zu nutzen, die den Chart Trade und den Mouse Indicator bilden. Durch die Verwendung des oben gezeigten Codes können Sie Ihren Expert Advisor mit den bereits kompilierten Modulen des Chart Trade und des Mouse Indicator kompatibel machen. Mit anderen Worten: Programmieren Sie es einmal, testen Sie, ob es funktioniert, und fassen Sie es nicht wieder an. Dann gehen Sie zur nächsten Stufe über. Das ist die Idee dahinter.
Für diejenigen, die gerade erst anfangen und den Code noch nicht ganz verstehen, wollen wir die Neuerungen kurz erläutern. Erinnern Sie sich, dass die erwähnte Strukturänderung in Zeile 34 stattfand, wo ich die privaten Variablen der Klasse in einer einzigen Struktur zusammengefasst habe. Wie bereits erwähnt, ändert dies jedoch nichts an dem, was in dem vorangegangenen Artikel erläutert wurde.
Das erste, was Sie sich vielleicht fragen, ist: Woher weiß diese Klasse, welche Position geschlossen werden soll, wenn wir die Schaltfläche zum Schließen von Positionen im Indikator Chart Trade drücken? Die Antwort lautet: Die Klasse weiß nicht, welche Position geschlossen werden soll. Man könnte meinen, das sei absurd, denn wenn man sich die Schaltflächen ansieht, ist diejenige, die für das Schließen von Positionen zuständig ist, mit „CLOSE ALL POSITIONS“ beschriftet. Logischerweise müsste man also erwarten, dass alle Positionen geschlossen werden. Richtig? Falsch! Das ist eigentlich nicht das, was diese Klasse macht. Zumindest nicht ohne ein paar Änderungen am oben gezeigten Code.
An diesem Punkt sind Sie vielleicht verwirrt. Wenn das Drücken von „CLOSE ALL POSITIONS“ nicht alle Positionen schließt, warum steht das dann auf der Schaltfläche? Um dies zu verstehen, müssen wir einen kurzen Schritt zurück in die Vergangenheit machen, bevor wir uns ansehen, wie der Klassencode tatsächlich funktioniert.
In den Artikeln über das Cross-Order-System:
Ich habe erklärt, wie und warum wir manchmal eine besondere Art von Vermögenswerten verwenden müssen – die so genannten historischen Vermögenswerte. Dort habe ich erklärt, dass wir sicherstellen müssen, dass der Indikator Chart Trade dem Expert Advisor mitteilen kann, welcher Vermögenswert tatsächlich gehandelt werden soll. Das liegt daran, dass ein „historischer Vermögenswert“ kein handelbarer Vermögenswert ist und daher keine Handelsanfragen mit ihm gestellt werden können. Viele dieser historischen Werte bestehen aufgrund von Kontraktstrukturen. Ich habe sie auch in diesen beiden Artikeln erklärt. Hier wird das Problem jedoch komplexer. Sie könnten einen Chart eines historischen Vermögenswerts verwenden und den Expert Advisor auffordern, jederzeit von einem vollen Kontrakt zu einem Minikontrakt zu wechseln. Dies führt dazu, dass sowohl der Expert Advisor als auch, was noch wichtiger ist, der Indikator Chart Trade den gehandelten Vermögenswert ändern.
Jetzt kommt das eigentliche Problem. Nehmen wir an, Sie verwenden einen historischen Chart, z.B. für den DOLLAR, und Sie haben dem Expert Advisor gesagt, er solle mit dem gesamten Dollarkontrakt arbeiten. Später ändern Sie Ihre Meinung und bitten den Expert Advisor, zum Minikontrakt zu wechseln. Der Expert Advisor sendet eine Nachricht an den Indikator Chart Trade, um ihn über die Kontraktänderung zu informieren. So weit, so gut. Das Problem beginnt jedoch, wenn Sie eine offene Position für den gesamten Kontrakt haben. Wenn Sie dem Expert Advisor dann mitteilen, dass er zum Minikontrakt wechseln soll, selbst wenn eine Position mit vollem Kontrakt offen ist, wird der Expert Advisor dem einfach nachkommen. Auch der Indikator Chart Trade wird entsprechend aktualisiert. Jetzt haben wir ein Problem. Und das ist der Punkt, den Sie verstehen müssen, um den Code an Ihre Bedürfnisse anzupassen.
Wenn zu diesem Zeitpunkt der Indikator Chart Trade anzeigt, dass der handelbare Vermögenswert der Minikontrakt ist, was passiert dann, wenn Sie beantragen, alle offenen Positionen zu schließen? Denken Sie daran, dass die offene Position im gesamten Kontrakt enthalten ist. Der Indikator Chart Trade sendet tatsächlich eine Nachricht an den Expert Advisor. Diese Nachricht enthält, wie in den vorangegangenen Artikeln erläutert, alle notwendigen Daten, damit der Expert Advisor weiß, was zu tun ist. Der Expert Advisor weiß jedoch nicht, was er tun soll. Dies geschieht, weil er auf einem Chart arbeitet, der nicht gehandelt werden kann (ein historischer Chart des Kontrakts). Um dies zu erreichen, wird die Nachricht in Zeile 153 des Codes abgefangen. Zu diesem Zeitpunkt wird die Nachricht bereits erfolgreich übersetzt worden sein. Dann wird die Codeausführung bis Zeile 120 fortgesetzt. Hier beginnt die eigentliche Arbeit.
In Zeile 122 haben wir eine Schleife. Sie liest alle offenen Positionen. Ausnahmslos alle, ohne Ausnahme. Aber warum alle Positionen lesen, auch die, die nichts mit unserem DOLLAR-Beispiel zu tun haben? Denn wir speichern nicht die Ticketnummer, die der Server beim Öffnen der einzelnen Positionen angegeben hat. Deshalb müssen wir erneut nach diesen Tickets suchen.
Die nächste Frage lautet: Warum bewahren wir diese Tickets nicht auf? Das würde die Dinge sicherlich einfacher machen. In der Tat, das wäre es. Die Speicherung dieser Tickets kann jedoch nicht innerhalb des Expert Advisors selbst erfolgen. Jede Änderung des Charts, an der der Expert Advisor beteiligt ist, würde zum Verlust dieser Ticket-Informationen führen. Daher sind wir gezwungen, sie bei Bedarf erneut abzurufen. Dies geschieht jedes Mal, wenn Sie den Chart-Zeitrahmen oder den Kontrakttyp ändern.
Wir werden also vorerst so vorgehen. Diese Vorgehensweise bedeutet jedoch, dass wir die Prozedur nicht einfach aufrufen können, um Positionen zu schließen, da wir eine Schleife durch alle Positionen durchlaufen, einschließlich derjenigen, die nicht mit der Chart Trade-Nachricht in Verbindung stehen.
Deshalb müssen wir Filter anwenden. Der erste Filter befindet sich in Zeile 124, wo wir den Symbolnamen der Position überprüfen und ihn mit dem vom Indikator Chart Trade erhaltenen Namen vergleichen. Aber Achtung: In unserem Beispiel haben wir eine offene Position im gesamten Kontrakt. Aber die Nachricht von Chart Trade zeigt den Minikontrakt an. Daher wird dieser Test fehlschlagen, da die Namen der Assets nicht übereinstimmen. Das Gleiche würde in der umgekehrten Situation passieren – wenn wir eine offene Minikontrakt-Position hätten und versuchen würden, Chart Trade zu verwenden, um Vollkontrakt-Positionen zu schließen. Dieser Test verhindert solche Verwechslungen.
Das ist ein wichtiger Hinweis. Auch wenn dieser Test kompliziert erscheinen mag, ist er absolut notwendig. Auch wenn Sie einen historischen Kontrakt verwenden, um sowohl Mini- als auch Vollkontrakte über das System der Kreuzaufträge zu handeln, dürfen Sie diesen Test in Zeile 124 nicht entfernen. Auch wenn Sie eine DOLLAR-bezogene Position schließen wollen, muss dieser Test bestehen bleiben. Denn ohne diese Funktion könnten bei der Verwendung von Chart Trade Positionen aus ganz anderen Vermögenswerten geschlossen werden, da wir alle offenen Positionen überprüfen.
Wenn der Test in Zeile 124 erfolgreich ist, erhalten wir ein neues Ticket. Wir gehen zu einem weiteren Test über, der etwas anderes isolieren und filtern soll. Im vorigen Artikel habe ich erwähnt, dass diese Klasse eine eindeutige Einheit darstellt, die durch eine magische Zahl identifiziert wird. So kann ein einzelner Expert Advisor mehrere unabhängige Handelsstrategien enthalten, die jeweils durch eine magische Zahl getrennt sind.
Für NETTING-Konten macht dies jedoch keinen Sinn, da der Handelsserver bei dieser Art von Konto den Durchschnittspreis der Position ermittelt. Das bedeutet, dass es nur eine offene Position pro Vermögenswert geben kann.
Bei HEDGING-Konten sieht die Sache jedoch ganz anders aus. Sie können mehrere Positionen – sowohl Kauf- als auch Verkaufspositionen – für ein und denselben Vermögenswert gleichzeitig offen haben. In solchen Fällen ist es wichtig, dass sich diese Klasse von anderen innerhalb desselben Expert Advisors unterscheidet. Um die Dinge nicht zu verkomplizieren, werde ich nicht im Detail darauf eingehen, wie dies geschieht. Sie müssen nur wissen, dass dies tatsächlich möglich ist und von dieser Klasse unterstützt wird.
Neben den vom Expert Advisor eröffneten Positionen (die mit einer magischen Zahl gekennzeichnet sind, die angibt, welche Klasse sie erstellt hat) gibt es noch eine weitere Möglichkeit: Der Händler kann eine Position manuell eröffnet haben. Hier kann es knifflig werden, denn der Händler möchte möglicherweise nicht, dass seine manuelle Position geschlossen wird, wenn er Chart Trade verwendet. In diesem Fall vergleicht der Expert Advisor die magische Zahl der Position mit der magischen Zahl der Klasse – und wenn sie nicht übereinstimmen, wird diese Position ignoriert und bleibt offen. Wenn der Händler also einen manuellen Handel eröffnet hat, lässt der Expert Advisor ihn unberührt, und er muss manuell geschlossen werden.
Sie können sehen, wie sich diese Verhaltensweisen entfalten. Obwohl sie modifiziert werden können, ist das gesamte System in erster Linie für HEDGING-Konten konzipiert, um sicherzustellen, dass es sich nicht auf andere Bereiche auswirkt. Wenn also beide Tests erfolgreich sind, wird Zeile 126 ausgeführt – das Ticket der Position wird erfasst und die Prozedur aufgerufen, die die angegebene Position schließt. Dieser Vorgang findet sich in Zeile 12. Beachten Sie, dass sie sich innerhalb einer geschützten Klausel befindet, sodass auf sie durch Vererbung, aber nicht direkt von außerhalb des Vererbungssystems zugegriffen werden kann. Kurz gesagt: Er kann nicht direkt vom Code des Expert Advisors oder einem anderen externen Code aufgerufen werden.
Nun gibt es einige Besonderheiten dieses Codes, die ich eventuell entfernen werde. Folgende Situation: Wenn der Code in Zeile 12 aufgerufen wird, sind ihm bereits einige Dinge bekannt, z. B. der Name der Anlage und die Ticketnummer. In Zeile 17 wird jedoch erneut geprüft, ob das Ticket zu einer offenen Position gehört. Warum diese Prüfung wiederholen? Denn zum jetzigen Zeitpunkt bin ich mir noch nicht sicher, wie ein anderes (noch zu entwickelndes) Werkzeug mit diesem Verfahren kommunizieren wird. Dennoch müssen wir die Bibliotheksfunktion PositionSelectByTicket verwenden, um bestimmte Daten, die wir vom Server benötigen, zu aktualisieren. Diese Aktualisierung erfolgte jedoch bereits früher, als PositionGetSymbol in Zeile 124 aufgerufen wurde. Mit anderen Worten, es gibt hier einige Überschneidungen. Aber im Moment ist das kein Problem.
Wie auch immer, wir müssen die Informationen auffrischen. Sobald die Daten aktualisiert und das Ticket als gültige Position bestätigt ist, wird in Zeile 18 geprüft, ob es sich um einen Kauf oder Verkauf handelt. Dies ist wichtig. In Zeile 19 wird dann der Name des Vermögenswerts der Position erfasst. Auch dies könnte sich in Zukunft ändern, sobald das neue Tool implementiert ist, denn wenn man nur diese Klasse betrachtet, sind diese Schritte überflüssig – die Prozedur in Zeile 120 hätte diese Informationen direkt übergeben können. Auch hier weiß ich noch nicht, wie ClosePosition in Zukunft funktionieren wird. Wir gehen also zum Ausfüllen der Struktur über, die an den Server gesendet wird.
Im Gegensatz zum ToMarket-Verfahren, das im vorigen Artikel besprochen wurde, wo ich sagte, dass die Erklärung der einzelnen Felder später folgen würde, kann ich hier die wichtigsten Felder erklären. Sie zu verstehen, wird später wichtig sein. In Zeile 21 teilen wir dem Server mit, welche Art von Aktion er durchführen soll. Nun mag sich mancher fragen: Warum nicht TRADE_ACTION_CLOSE_BY verwenden, da wir ja versuchen, eine Position zu schließen? Das ist eine berechtigte Frage – und der Hauptgrund dafür liegt in den NETTING-Konten. Um das zu verstehen, schauen Sie sich Zeile 26 an, wo wir das Handelsvolumen angeben. Bei der Verwendung von TRADE_ACTION_CLOSE_BY würde der Server die Position einfach sofort schließen.
Das würde uns dazu zwingen, zusätzliche Logik und Code für eine andere Aktion zu erstellen, die bei NETTING-Konten bereits möglich ist: Teilabschlüsse. Bei Teilschließungen schließen wir nur einen Teil unserer Position. Hier habe ich noch keine Logik für Teilschließungen oder Umkehrungen implementiert. Das ist eine Design-Entscheidung – je nachdem, was implementiert werden soll oder nicht. Da dieses System in beiden Kontotypen verwendet werden kann, müssen wir einigen Funktionen Vorrang vor anderen einräumen. NETTING- und HEDGING-Konten haben jeweils ihre eigenen Möglichkeiten, die sich teilweise gegenseitig ausschließen. Ich versuche, beides unter einen Hut zu bringen. Aus diesem Grund ist diese Erklärung wichtig.
In allen anderen Feldern werden Details für den Server angegeben: ob wir kaufen oder verkaufen (Zeile 22); der Preis, zu dem der Handel stattfinden soll (Zeile 23); die Ticketnummer der Position (Zeile 24); der Name des Symbols (Zeile 25) – beachten Sie, dass er mit dem Namen auf dem Ticket übereinstimmen muss, sonst tritt ein Fehler auf; das Volumen, das bestimmt, was getan wird (Zeile 26); und schließlich die Abweichung, d. h. die zulässige Kursabweichung (Zeile 27). All diese Informationen weisen den Server an, die Marktanfrage auszuführen – den Handel zum besten verfügbaren Preis durchzuführen, das Volumen oder die Richtung zu ändern oder die Position nach Bedarf zu schließen.
Beachten Sie, dass dieser Ansatz im Gegensatz zu TRADE_ACTION_CLOSE_BY, bei dem nur Positionen geschlossen werden, eine weitaus größere Flexibilität bietet. Deshalb verwende ich TRADE_ACTION_DEAL. Sie gilt jedoch nur, wenn eine Stelle bereits offen ist. Sie können jedoch keine Stop-Loss- oder Take-Profit-Niveaus ändern. Diese werden über eine andere Aktion abgewickelt, auf die wir später eingehen werden. Für den Moment werden wir weiterhin das System von MetaTrader 5 verwenden. Das bedeutet, dass Sie, obwohl es technisch möglich ist, mit einem historischen Kontrakt zu handeln, keine Kurslinien in diesem Chart sehen werden. Sie werden auf dem aktuellen Kontrakt erscheinen. Auch dies wird sich in Zukunft ändern.
Das letzte, was noch zu beachten ist, ist der Code des Expert Advisors. Und zwar so:
01. //+------------------------------------------------------------------+ 02. #property copyright "Daniel Jose" 03. #property icon "/Images/Market Replay/Icons/Replay - EA.ico" 04. #property description "Demo version between interaction" 05. #property description "of Chart Trade and Expert Advisor" 06. #property version "1.84" 07. #property link "https://www.mql5.com/pt/articles/12598" 08. //+------------------------------------------------------------------+ 09. #include <Market Replay\Order System\C_Orders.mqh> 10. //+------------------------------------------------------------------+ 11. enum eTypeContract {MINI, FULL}; 12. //+------------------------------------------------------------------+ 13. input eTypeContract user00 = MINI; //Cross order in contract 14. //+------------------------------------------------------------------+ 15. C_Orders *Orders; 16. long GL_ID; 17. //+------------------------------------------------------------------+ 18. int OnInit() 19. { 20. GL_ID = 0; 21. Orders = new C_Orders(0xC0DEDAFE78514269); 22. 23. return INIT_SUCCEEDED; 24. } 25. //+------------------------------------------------------------------+ 26. void OnTick() {} 27. //+------------------------------------------------------------------+ 28. void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam) 29. { 30. (*Orders).DispatchMessage(id, lparam, dparam, sparam); 31. switch (id) 32. { 33. case CHARTEVENT_CHART_CHANGE: 34. if (GL_ID > 0) break; else GL_ID = ChartID(); 35. case CHARTEVENT_CUSTOM + evChartTrade_At_EA: 36. EventChartCustom(GL_ID, evEA_At_ChartTrade, user00, 0, ""); 37. break; 38. } 39. } 40. //+------------------------------------------------------------------+ 41. void OnDeinit(const int reason) 42. { 43. switch (reason) 44. { 45. case REASON_REMOVE: 46. case REASON_INITFAILED: 47. EventChartCustom(GL_ID, evEA_At_ChartTrade, -1, 0, ""); 48. break; 49. } 50. 51. delete Orders; 52. } 53. //+------------------------------------------------------------------+
Der Quellcode des Expert Advisors
Dieser Code wurde bereits fast vollständig in dem Artikel Marktsimulation (Teil 02): Kreuzaufträge (II) vorgestellt, an der nur einige kleine Änderungen vorgenommen wurden, um die Klasse C_Orders zu nutzen. Diese Änderungen sind recht einfach. Wenn Sie jedoch nicht viel Erfahrung mit der Programmierung in C haben, könnten Ihnen einige Dinge etwas seltsam vorkommen. Aber hier gibt es nichts Geheimnisvolles, nur etwas anderes. Die erste Änderung erscheint in Zeile 9, wo wir die in diesem Artikel vorgestellte Header-Datei einfügen. Eine weitere Änderung findet sich in Zeile 15, wo wir die Klasse mit einem Zeiger deklarieren.
In Zeile 21 initialisieren wir die Klasse mit dem Operator NEW. Vielleicht finden Sie dort etwas Ungewöhnliches. Der Klassenkonstruktor ist so deklariert, dass er eine 64-Bit-Long-Integer-Zahl erwartet, doch der übergebene Wert sieht seltsam aus: 0xC0DEDAFE78514269 enthält sowohl Buchstaben als auch Zahlen. Was ist das für eine seltsame Sache? Eigentlich ist das gar nicht so seltsam. Es ist ein hexadezimaler Wert. Beachten Sie das Präfix 0x, das dem Compiler mitteilt, dass der folgende Wert hexadezimal ist. Dies ist in vielen Situationen sehr nützlich. Wenn Sie diesen Wert jedoch später als die magische Zahl der Position betrachten, werden Sie etwas ganz anderes sehen – die dezimale Darstellung dieses Hexadezimalwerts. Ich überlasse es Ihnen, herauszufinden, wie dieser Dezimalwert lautet, ich werde ihn hier nicht verraten.
Die letzte Änderung wird in Zeile 30 vorgenommen. In dieser Zeile werden die notwendigen Daten übergeben, damit die Klasse ihre Arbeit ausführen kann. Sie ermöglicht es dem Expert Advisor, die vom Chart Trade Indikator gesendeten Nachrichten zu verarbeiten. Beachten Sie, dass wir mit sehr wenig Code bereits etwas Bemerkenswertes und recht Interessantes haben, da das gesamte System in Module unterteilt ist. Dieser modulare Aufbau bedeutet, dass sich Verbesserungen in einem Modul nicht direkt auf ein anderes auswirken, sodass der Code sicher und viel effizienter wachsen kann. Dies steht in krassem Gegensatz zu einem monolithischen System, bei dem alles in einer einzigen, riesigen Datei enthalten wäre, was die Entwicklung und Wartung viel anstrengender und fehleranfälliger macht.
Abschließende Überlegungen
In den letzten beiden Artikeln habe ich den Expert Advisor zusammen mit dem Klassencode vorgestellt, der für das Senden von Marktaufträgen zuständig ist. Dieses System kann perfekt auf jedem Kontotyp funktionieren – ob NETTING oder HEDGING – und erlaubt Ihnen, denselben Expert Advisor auf dem Forex-, Aktien- oder OTC-Markt zu verwenden. Dadurch wird der gesamte Prozess wesentlich flexibler und zugänglicher.
Dieser Expert Advisor ist jedoch noch nicht vollständig. Es gibt noch viel zu tun, bevor es seinen Hauptzweck erfüllen kann. Dies dient der Simulation des Handelsservers zur Verwendung in der Wiedergabe/Simulation.
Bevor wir das tun können, müssen wir jedoch einige andere Komponenten bauen. Verpassen Sie also nicht den nächsten Artikel, in dem ich Ihnen etwas erkläre, was Sie bereits tun können, obwohl dieser Expert Advisor noch nicht fertig ist. Ich möchte jetzt mit der Diskussion beginnen, um die Dinge zu vereinfachen. Denn wenn wir es auf später verschieben, könnte die Erklärung viel komplexer werden. Und das, liebe Leserin, lieber Leser, könnte es Ihnen schwer machen, alle Einzelheiten zu erfassen.
| Datei | Beschreibung |
|---|---|
| Experts\Expert Advisor.mq5 | Demonstriert die Interaktion zwischen Chart Trade und dem Expert Advisor (für die Interaktion ist ein Mauszeiger erforderlich) |
| Indicators\Chart Trade.mq5 | Erstellt das Fenster für die Konfiguration des zu versendenden Auftrags (Mouse Study ist für die Interaktion erforderlich) |
| Indicators\Market Replay.mq5 | Erstellt Steuerelemente für die Interaktion mit dem Wiedergabe-/Simulationsdienst (Mouse Study ist für die Interaktion erforderlich) |
| Indicators\Mouse Study.mq5 | Ermöglicht die Interaktion zwischen den grafischen Steuerelementen und dem Nutzer (erforderlich für den Betrieb des Replay-Simulators und des Live-Markthandels) |
| Servicios\Market Replay.mq5 | Erstellt und pflegt den Marktwiedergabe- und Simulationsdienst (Hauptdatei des gesamten Systems) |
Übersetzt aus dem Portugiesischen von MetaQuotes Ltd.
Originalartikel: https://www.mql5.com/pt/articles/12598
Warnung: Alle Rechte sind von MetaQuotes Ltd. vorbehalten. Kopieren oder Vervielfältigen untersagt.
Dieser Artikel wurde von einem Nutzer der Website verfasst und gibt dessen persönliche Meinung wieder. MetaQuotes Ltd übernimmt keine Verantwortung für die Richtigkeit der dargestellten Informationen oder für Folgen, die sich aus der Anwendung der beschriebenen Lösungen, Strategien oder Empfehlungen ergeben.
Biologisches Neuron zur Vorhersage von Finanzzeitreihen
Royal-Flush-Optimierung (RFO)
Marktsimulation (Teil 07): Sockets (I)
Neuronale Netze im Handel: Hierarchical Dual-Tower Transforme (letzter Teil)
- Freie Handelsapplikationen
- Über 8.000 Signale zum Kopieren
- Wirtschaftsnachrichten für die Lage an den Finanzmärkte
Sie stimmen der Website-Richtlinie und den Nutzungsbedingungen zu.