Marktsimulation (Teil 02): Kreuzaufträge (II)
Einführung
Im vorangegangenen Artikel, Marktsimulation (Teil 01): Kreuzaufträge (I) habe ich eine alternative Lösung für ein recht häufiges Problem aufgezeigt und erläutert, insbesondere für diejenigen, die in irgendeiner Weise mit Terminkontrakten handeln. Obwohl ich die endgültige Lösung nicht vorgestellt habe – denn der gesamte Artikel konzentrierte sich ausschließlich auf den Indikator Chart Trade – sind die dort behandelten Inhalte von größter Bedeutung. Sie gibt uns die Möglichkeit, der Klasse C_Terminal einen eigenen Namen für den Handel mit diesen Terminkontrakten zu geben.
Das Problem bleibt jedoch bestehen. Nicht wegen des Chart Trade oder des Expert Advisors, sondern wegen des Systems der schwebenden Aufträge. Auch wenn wir in diesen Artikeln noch nicht damit begonnen haben, darüber zu sprechen, müssen wir uns in irgendeiner Weise darauf vorbereiten. Ein Teil der von diesem System verwendeten Informationen stammt aus dem Chart Trade, und die gesamte Kommunikation mit dem Server erfolgt über den Expert Advisor. Mit anderen Worten: Wir haben ein Dreieck, bei dem jede Ecke eine der zu entwickelnden Anwendungen darstellt. Der Teil, der mit dem Server kommuniziert, stammt vom der Ecke des Expert Advisors.
Daher ist es durchaus sinnvoll, die Wahl des Terminkontrakts vom Expert Advisor steuern zu lassen. So können wir entscheiden, ob wir zu einem bestimmten Zeitpunkt mit Minikontrakten oder mit ganzen Kontrakten handeln. Diese Entscheidung lässt sich jedoch nicht auf alle Anwendungen übertragen. Die Entscheidung, welche Art von Kontrakt gehandelt werden soll, muss an einem einzigen Ort getroffen werden, um Unklarheiten bei der Auswahl und der Information zu vermeiden. Andernfalls würde das System für den Nutzer sehr unübersichtlich werden, wenn die Kontraktart an mehreren Stellen ausgewählt werden könnte.
Indem wir die Auswahl in Chart Trade treffen, wie im vorherigen Artikel gezeigt, können wir den Prozess steuern. Da der Expert Advisor jedoch die einzige Komponente ist, die direkt mit dem Server kommuniziert, um Aufträge zu senden, ist es auch logisch, die Auswahl dort zu treffen. Dies führt zu einer anderen Art von Problem, und die Demonstration einer Möglichkeit, es zu lösen, wird der Schwerpunkt dieses Artikels sein. Bevor Sie jedoch annehmen, dass wir hier eine endgültige Lösung präsentieren, möchte ich betonen, dass es sich im Folgenden um einen Lösungsvorschlag handelt. Die endgültige Lösung wird zu einem späteren Zeitpunkt behandelt, da ich mich noch nicht für den endgültigen Ansatz entschieden habe.
Die Probleme verstehen
Da die vorgeschlagene Lösung in der Tat den Expert Advisor zur Auswahl der Kontraktart verwendet, müssen wir einige Aspekte des Chart Trade ändern. Dies liegt daran, dass ein Problem auftritt, wenn die Auswahl der Kontraktart unter die Kontrolle des Expert Advisors gestellt wird. Das Problem liegt in den Informationen, die in Chart Trade angezeigt werden, wie in der folgenden Abbildung zu sehen ist.

Beachten Sie, dass ich den Namen der Anlage oder des Kontrakts hervorhebe. Einer der wichtigsten Punkte ist, dass dieser Name zur Berechnung der Finanzwerte verwendet wird, die auf der Oberfläche von Chart Trade angezeigt werden. Das Problem wird dadurch verschärft, dass Chart Trade die bereits in Ticks umgewandelten Werte an den Expert Advisor sendet, sodass der Expert Advisor nur noch für das Senden von Marktaufträgen zuständig ist.
Und nun die Herausforderung: Wenn der Expert Advisor den Kontrakttyp in einen Minikontrakt oder einen Vollkontrakt ändert, muss Chart Trade diese Information replizieren, damit der Nutzer oder Betreiber keinen Fehler macht. Die Replikation dieser Informationen ist an sich nicht schwierig. Wir können dies erreichen, indem wir Chart Trade mit dem Expert Advisor verknüpfen. Im Gegensatz zum derzeitigen Setup, bei dem der Nutzer den Chart Trade zum Chart hinzufügen muss, würde der Expert Advisor dies übernehmen. Der Nutzer würde den Expert Advisor auf dem Chart platzieren, und er würde anschließend den Indikator Chart Trade hinzufügen und die Kontraktinformationen automatisch anpassen.
Dies wäre die einfachste und naheliegendste Lösung. Dies würde jedoch voraussetzen, dass der Indikator Chart Trade in jeden erstellten Expert Advisor integriert wird – ein völlig undurchführbarer Ansatz. Nicht weil es nicht möglich wäre, sondern weil jede Verbesserung von Chart Trade eine Neukompilierung aller Expert Advisors erfordern würde. Aus diesem Grund wurde Chart Trade so konzipiert, dass es vom Expert Advisor getrennt bleibt.
Wir werden also die Komponenten getrennt halten. Wie also lösen wir das Problem in diesem Fall? Die Lösung wird bereits vorgeschlagen: der Austausch von Nachrichten zwischen dem Expert Advisor und dem Chart Trade Indikator. Einfach, nicht wahr? In Wirklichkeit ist es nicht so einfach. Aus diesem Grund habe ich beschlossen, die Umsetzung in diesem Artikel zu erläutern.
Das erste Problem betrifft die Reihenfolge, in der die Anträge in das Chart eingefügt werden. Wie das? Wir könnten den Nutzer zwingen, den Expert Advisor vor dem Chart Trade hinzuzufügen. Wenn dann Chart Trade geladen wird, wird der Expert Advisor nach dem Kontraktstyp abgefragt. Klingt gut, aber genau an dieser Stelle gibt es ein weiteres Problem. Wir können den Nutzer zwar über die richtige Reihenfolge informieren, aber wir können MetaTrader 5 nicht dazu zwingen, sich daran zu halten. Vielleicht haben Sie das folgende Problem nicht bedacht, das Problem Nummer zwei: Wenn MetaTrader 5 den Zeitrahmen des Chart s ändert, wird das Chart zerstört und neu geladen. Dies gilt für Indikatoren und Expert Advisors. Die Frage ist nun: Wer lädt zuerst – der Chart Trade oder der Expert Advisor? Wenn der Expert Advisor zuerst geladen wird, löst Problem eins das Problem zwei. Was aber, wenn Chart Trade zuerst geladen wird?
Wie Sie sehen, ist die Situation komplizierter als sie scheint. Außerdem gibt es ein drittes Problem: Wenn der Nutzer den Kontraktparameter im Expert Advisor oder einen anderen Parameter ändert, wird Chart Trade nicht korrekt aktualisiert. Alle bisherigen Planungen bezogen sich nur auf die ersten beiden Probleme. Dieses Problem tritt auf, weil der Expert Advisor von MetaTrader 5 entfernt und dem Chart wieder hinzugefügt wird. Außerdem muss Chart Trade das Vorhandensein des Expert Advisors irgendwie mitbekommen. Andernfalls entstehen weitere Probleme.
Glücklicherweise kann die Frage der Sensibilisierung vorübergehend beiseite gelassen werden. Das System ist so konzipiert, dass es dem Nutzer eine Rückmeldung über seinen Status gibt. Daher wird diese Frage der Sensibilisierung zu einem anderen Zeitpunkt zu einem echten Problem werden. Dies ist im aktuellen Kontext der Interaktion von Chart Trade und Expert Advisor kein kritisches Problem. Um große Änderungen am Code zu vermeiden, werden wir also gewisse Zugeständnisse machen. Die Trennung der Komponenten wird in einem neuen Abschnitt behandelt.
Einige Zugeständnisse machen
Die Änderungen, die wir hier vornehmen, werden dauerhaft sein, das heißt, der Code wird sich weiterentwickeln und nicht zurückgehen. Diese Zugeständnisse ermöglichen die Nutzung bestimmter Funktionen, die zuvor nicht verfügbar waren. Daher sollten diese neuen Möglichkeiten sorgfältig und aufmerksam genutzt werden.
Die erste Änderung ist in der Datei C_Terminal.mqh zu sehen. Nachstehend finden Sie den vollständigen aktualisierten Code, der zu verwenden ist.
001. //+------------------------------------------------------------------+ 002. #property copyright "Daniel Jose" 003. //+------------------------------------------------------------------+ 004. #include "Macros.mqh" 005. #include "..\Defines.mqh" 006. //+------------------------------------------------------------------+ 007. class C_Terminal 008. { 009. //+------------------------------------------------------------------+ 010. protected: 011. enum eErrUser {ERR_Unknown, ERR_FileAcess, ERR_PointerInvalid, ERR_NoMoreInstance}; 012. //+------------------------------------------------------------------+ 013. struct st_Terminal 014. { 015. ENUM_SYMBOL_CHART_MODE ChartMode; 016. ENUM_ACCOUNT_MARGIN_MODE TypeAccount; 017. long ID; 018. string szSymbol; 019. int Width, 020. Height, 021. nDigits, 022. SubWin, 023. HeightBar; 024. double PointPerTick, 025. ValuePerPoint, 026. VolumeMinimal, 027. AdjustToTrade; 028. }; 029. //+------------------------------------------------------------------+ 030. void CurrentSymbol(bool bUsingFull) 031. { 032. MqlDateTime mdt1; 033. string sz0, sz1; 034. datetime dt = macroGetDate(TimeCurrent(mdt1)); 035. enum eTypeSymbol {WIN, IND, WDO, DOL, OTHER} eTS = OTHER; 036. 037. sz0 = StringSubstr(m_Infos.szSymbol = _Symbol, 0, 3); 038. for (eTypeSymbol c0 = 0; (c0 < OTHER) && (eTS == OTHER); c0++) eTS = (EnumToString(c0) == sz0 ? c0 : eTS); 039. switch (eTS) 040. { 041. case DOL : 042. case WDO : sz1 = "FGHJKMNQUVXZ"; break; 043. case IND : 044. case WIN : sz1 = "GJMQVZ"; break; 045. default : return; 046. } 047. sz0 = EnumToString((eTypeSymbol)(((eTS & 1) == 1) ? (bUsingFull ? eTS : eTS - 1) : (bUsingFull ? eTS + 1: eTS))); 048. for (int i0 = 0, i1 = mdt1.year - 2000, imax = StringLen(sz1);; i0 = ((++i0) < imax ? i0 : 0), i1 += (i0 == 0 ? 1 : 0)) 049. if (dt < macroGetDate(SymbolInfoInteger(m_Infos.szSymbol = StringFormat("%s%s%d", sz0, StringSubstr(sz1, i0, 1), i1), SYMBOL_EXPIRATION_TIME))) break; 050. } 051. //+------------------------------------------------------------------+ 052. private : 053. st_Terminal m_Infos; 054. struct mem 055. { 056. long Show_Descr, 057. Show_Date; 058. bool AccountLock; 059. }m_Mem; 060. //+------------------------------------------------------------------+ 061. inline void ChartChange(void) 062. { 063. int x, y, t; 064. m_Infos.Width = (int)ChartGetInteger(m_Infos.ID, CHART_WIDTH_IN_PIXELS); 065. m_Infos.Height = (int)ChartGetInteger(m_Infos.ID, CHART_HEIGHT_IN_PIXELS); 066. ChartTimePriceToXY(m_Infos.ID, 0, 0, 0, x, t); 067. ChartTimePriceToXY(m_Infos.ID, 0, 0, m_Infos.PointPerTick * 100, x, y); 068. m_Infos.HeightBar = (int)((t - y) / 100); 069. } 070. //+------------------------------------------------------------------+ 071. public : 072. //+------------------------------------------------------------------+ 073. C_Terminal(const long id = 0, const uchar sub = 0) 074. { 075. m_Infos.ID = (id == 0 ? ChartID() : id); 076. m_Mem.AccountLock = false; 077. m_Infos.SubWin = (int) sub; 078. CurrentSymbol(false); 079. m_Mem.Show_Descr = ChartGetInteger(m_Infos.ID, CHART_SHOW_OBJECT_DESCR); 080. m_Mem.Show_Date = ChartGetInteger(m_Infos.ID, CHART_SHOW_DATE_SCALE); 081. ChartSetInteger(m_Infos.ID, CHART_SHOW_OBJECT_DESCR, false); 082. ChartSetInteger(m_Infos.ID, CHART_EVENT_OBJECT_DELETE, true); 083. ChartSetInteger(m_Infos.ID, CHART_EVENT_OBJECT_CREATE, true); 084. ChartSetInteger(m_Infos.ID, CHART_SHOW_DATE_SCALE, false); 085. m_Infos.nDigits = (int) SymbolInfoInteger(m_Infos.szSymbol, SYMBOL_DIGITS); 086. m_Infos.Width = (int)ChartGetInteger(m_Infos.ID, CHART_WIDTH_IN_PIXELS); 087. m_Infos.Height = (int)ChartGetInteger(m_Infos.ID, CHART_HEIGHT_IN_PIXELS); 088. m_Infos.PointPerTick = SymbolInfoDouble(m_Infos.szSymbol, SYMBOL_TRADE_TICK_SIZE); 089. m_Infos.ValuePerPoint = SymbolInfoDouble(m_Infos.szSymbol, SYMBOL_TRADE_TICK_VALUE); 090. m_Infos.VolumeMinimal = SymbolInfoDouble(m_Infos.szSymbol, SYMBOL_VOLUME_STEP); 091. m_Infos.AdjustToTrade = m_Infos.ValuePerPoint / m_Infos.PointPerTick; 092. m_Infos.ChartMode = (ENUM_SYMBOL_CHART_MODE) SymbolInfoInteger(m_Infos.szSymbol, SYMBOL_CHART_MODE); 093. if(m_Infos.szSymbol != def_SymbolReplay) SetTypeAccount((ENUM_ACCOUNT_MARGIN_MODE)AccountInfoInteger(ACCOUNT_MARGIN_MODE)); 094. ChartChange(); 095. } 096. //+------------------------------------------------------------------+ 097. ~C_Terminal() 098. { 099. ChartSetInteger(m_Infos.ID, CHART_SHOW_DATE_SCALE, m_Mem.Show_Date); 100. ChartSetInteger(m_Infos.ID, CHART_SHOW_OBJECT_DESCR, m_Mem.Show_Descr); 101. ChartSetInteger(m_Infos.ID, CHART_EVENT_OBJECT_DELETE, false); 102. ChartSetInteger(m_Infos.ID, CHART_EVENT_OBJECT_CREATE, false); 103. } 104. //+------------------------------------------------------------------+ 105. inline void SetTypeAccount(const ENUM_ACCOUNT_MARGIN_MODE arg) 106. { 107. if (m_Mem.AccountLock) return; else m_Mem.AccountLock = true; 108. m_Infos.TypeAccount = (arg == ACCOUNT_MARGIN_MODE_RETAIL_HEDGING ? arg : ACCOUNT_MARGIN_MODE_RETAIL_NETTING); 109. } 110. //+------------------------------------------------------------------+ 111. inline const st_Terminal GetInfoTerminal(void) const 112. { 113. return m_Infos; 114. } 115. //+------------------------------------------------------------------+ 116. const double AdjustPrice(const double arg) const 117. { 118. return NormalizeDouble(round(arg / m_Infos.PointPerTick) * m_Infos.PointPerTick, m_Infos.nDigits); 119. } 120. //+------------------------------------------------------------------+ 121. inline datetime AdjustTime(const datetime arg) 122. { 123. int nSeconds= PeriodSeconds(); 124. datetime dt = iTime(m_Infos.szSymbol, PERIOD_CURRENT, 0); 125. 126. return (dt < arg ? ((datetime)(arg / nSeconds) * nSeconds) : iTime(m_Infos.szSymbol, PERIOD_CURRENT, Bars(m_Infos.szSymbol, PERIOD_CURRENT, arg, dt))); 127. } 128. //+------------------------------------------------------------------+ 129. inline double FinanceToPoints(const double Finance, const uint Leverage) 130. { 131. double volume = m_Infos.VolumeMinimal + (m_Infos.VolumeMinimal * (Leverage - 1)); 132. 133. return AdjustPrice(MathAbs(((Finance / volume) / m_Infos.AdjustToTrade))); 134. }; 135. //+------------------------------------------------------------------+ 136. void DispatchMessage(const int id, const long &lparam, const double &dparam, const string &sparam) 137. { 138. static string st_str = ""; 139. 140. switch (id) 141. { 142. case CHARTEVENT_CHART_CHANGE: 143. m_Infos.Width = (int)ChartGetInteger(m_Infos.ID, CHART_WIDTH_IN_PIXELS); 144. m_Infos.Height = (int)ChartGetInteger(m_Infos.ID, CHART_HEIGHT_IN_PIXELS); 145. ChartChange(); 146. break; 147. case CHARTEVENT_OBJECT_CLICK: 148. if (st_str != sparam) ObjectSetInteger(m_Infos.ID, st_str, OBJPROP_SELECTED, false); 149. if (ObjectGetInteger(m_Infos.ID, sparam, OBJPROP_SELECTABLE) == true) 150. ObjectSetInteger(m_Infos.ID, st_str = sparam, OBJPROP_SELECTED, true); 151. break; 152. case CHARTEVENT_OBJECT_CREATE: 153. if (st_str != sparam) ObjectSetInteger(m_Infos.ID, st_str, OBJPROP_SELECTED, false); 154. st_str = sparam; 155. break; 156. } 157. } 158. //+------------------------------------------------------------------+ 159. inline void CreateObjectGraphics(const string szName, const ENUM_OBJECT obj, const color cor = clrNONE, const int zOrder = -1) const 160. { 161. ChartSetInteger(m_Infos.ID, CHART_EVENT_OBJECT_CREATE, 0, false); 162. ObjectCreate(m_Infos.ID, szName, obj, m_Infos.SubWin, 0, 0); 163. ObjectSetString(m_Infos.ID, szName, OBJPROP_TOOLTIP, "\n"); 164. ObjectSetInteger(m_Infos.ID, szName, OBJPROP_BACK, false); 165. ObjectSetInteger(m_Infos.ID, szName, OBJPROP_COLOR, cor); 166. ObjectSetInteger(m_Infos.ID, szName, OBJPROP_SELECTABLE, false); 167. ObjectSetInteger(m_Infos.ID, szName, OBJPROP_SELECTED, false); 168. ObjectSetInteger(m_Infos.ID, szName, OBJPROP_ZORDER, zOrder); 169. ChartSetInteger(m_Infos.ID, CHART_EVENT_OBJECT_CREATE, 0, true); 170. } 171. //+------------------------------------------------------------------+ 172. bool IndicatorCheckPass(const string szShortName) 173. { 174. string szTmp = szShortName + "_TMP"; 175. 176. IndicatorSetString(INDICATOR_SHORTNAME, szTmp); 177. m_Infos.SubWin = ((m_Infos.SubWin = ChartWindowFind(m_Infos.ID, szTmp)) < 0 ? 0 : m_Infos.SubWin); 178. if (ChartIndicatorGet(m_Infos.ID, m_Infos.SubWin, szShortName) != INVALID_HANDLE) 179. { 180. ChartIndicatorDelete(m_Infos.ID, 0, szTmp); 181. Print("Only one instance is allowed..."); 182. SetUserError(C_Terminal::ERR_NoMoreInstance); 183. 184. return false; 185. } 186. IndicatorSetString(INDICATOR_SHORTNAME, szShortName); 187. 188. return true; 189. } 190. //+------------------------------------------------------------------+ 191. };
Quellcode von C_Terminal.mqh
Beachten Sie, dass die Änderungen minimal sind. Tatsächlich haben wir im Vergleich zum Code im vorherigen Artikel nur die Möglichkeit eröffnet, die CurrentSymbol-Prozedur innerhalb anderer Klassen aufzurufen, die von der Klasse C_Terminal erben. Zuvor war diese Prozedur privat für die Klasse, aber ich habe sie jetzt in protected geändert. Viele würden sie vielleicht sofort veröffentlichen, aber ich ziehe es vor, keine so radikalen Änderungen vorzunehmen. Ich gewähre gerne das notwendige Minimum an Privilegien, bis das Programm nachweist, dass ein echter Bedarf an mehr Zugang besteht. Da es sich um ein geschütztes Verfahren handelt, kann nicht wahllos darauf zugegriffen werden.
Zusätzlich zu dieser kleinen Änderung wurde eine weitere Änderung vorgenommen. Beachten Sie, dass ich im Konstruktor in Zeile 73 den im vorherigen Artikel hinzugefügten Parameter entfernt habe. In Zeile 78 habe ich jedoch die erstmalige Verwendung des Minikontrakts erzwungen. Diese Art der Anpassung schafft einen Präzedenzfall für weitere Änderungen. Der Grund für diese Vorgehensweise ist, dass wir bei Beibehaltung der alten Struktur gezwungen gewesen wären, Chart Trade zu schließen, um den im Indikator angezeigten Kontrakt zu aktualisieren. Dies ist nun nicht mehr notwendig. Wir können den Kontrakt über eine Nachricht umschalten lassen. Als Folge dieser Änderung bleiben jedoch andere Änderungen aus dem vorherigen Artikel für den Code von Chart Trade relevant, aber wir werden uns im Moment nicht darauf konzentrieren. Zuerst muss etwas anderes in Angriff genommen werden.
Apropos Nachrichten, wir müssen neue Nachrichten einführen, um die Lösung dieser Probleme zu erfassen. Doch bevor wir uns mit den neuen Nachrichten befassen, sollten wir einen Moment innehalten und nachdenken. Sehen Sie sich die folgende Abbildung an:

Hier sehen wir die Punkte, an denen der Nachrichtenaustausch tatsächlich stattfinden wird. Beachten Sie eine Sache: Im Falle des Expert Advisors gibt es zwei Punkte, an denen Nachrichten an Chart Trade gesendet werden. Für Chart Trade gibt es nur einen Punkt.
Um zu verstehen, wie dieser Austausch abläuft, ist es wichtig zu wissen, dass OnChartEvent nach OnInit ausgeführt wird, wenn dem Chart etwas hinzugefügt wird. Aber wissen Sie, welches Ereignis OnChartEvent auslöst, wenn ein Objekt auf dem Chart platziert wird? Wenn Sie nachforschen, werden Sie feststellen, dass MetaTrader 5 ein CHARTEVENT_CHART_CHANGE-Ereignis auslöst, wenn ein beliebiges Objekt – Indikator oder Expert Advisor – zum Chart hinzugefügt wird. Dies ist wichtig zu verstehen, da wir dieses Ereignis nutzen werden, um das System richtig funktionieren zu lassen.
Bevor wir fortfahren, sollten wir Folgendes bedenken: Wozu brauchen wir Schaltflächen zum Senden von Aufträgen, wenn der Expert Advisor, der sie ausführt, möglicherweise nicht einmal auf dem Chart zu sehen ist? Das macht keinen Sinn. Um zu zeigen, dass der Nachrichtenaustausch tatsächlich stattgefunden hat und dass die Informationen in Chart Trade vom Nutzer oder Betreiber verwendet werden können und sollen, werden wir ein kleines, aber wichtiges Detail in Chart Trade ändern. Damit haben wir nun die konzeptionelle Grundlage, um die Lösung unseres Problems umzusetzen.
Implementierung der Lösung
Der erste Schritt besteht darin, drei neue Ereignisse in unser System aufzunehmen. Diese sind in der Datei Defines.mqh zu sehen, die im Folgenden vollständig dargestellt wird:
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. evChartTrade_At_EA, //Event to communication 43. evEA_At_ChartTrade //Event to communication 44. }; 45. //+------------------------------------------------------------------+
Der Quellcode der Datei Defines.mqh
Neu hinzugekommen sind die Zeilen 42 und 43. Diese Zeilen sind fast selbsterklärend, denn der Schlüssel liegt in der Richtung der Kommunikation. Zeile 42 betrifft die Kommunikation zwischen Chart Trade und dem Expert Advisor. Wichtig: Verwechseln Sie dies nicht mit Handelsereignisse. Dieses Ereignis ist für eine andere Art der Kommunikation gedacht – im Wesentlichen ein spezieller Kommunikationskanal.
Zeile 43 gibt den Namen des Ereignisses an, das als Antwort auf die von Chart Trade initiierte Anfrage an den Expert Advisor dient. Das ist im Moment alles – nur diese beiden neuen Zeilen. Aber ihre Auswirkungen werden erheblich sein. Als Nächstes wollen wir uns den vollständigen Code des Chart Trade Indikators ansehen. Sie ist unten abgebildet. Sie unterscheidet sich geringfügig von der Version im vorherigen Artikel. Insbesondere ist der Parameter, den der Nutzer oder Händler zuvor einstellen konnte, nicht mehr vorhanden.
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.81" 06. #property icon "/Images/Market Replay/Icons/Indicators.ico" 07. #property link "https://www.mql5.com/en/articles/12537" 08. #property indicator_chart_window 09. #property indicator_plots 0 10. //+------------------------------------------------------------------+ 11. #include <Market Replay\Chart Trader\C_ChartFloatingRAD.mqh> 12. //+------------------------------------------------------------------+ 13. #define def_ShortName "Indicator Chart Trade" 14. //+------------------------------------------------------------------+ 15. C_ChartFloatingRAD *chart = NULL; 16. //+------------------------------------------------------------------+ 17. input ushort user01 = 1; //Leverage 18. input double user02 = 100.1; //Finance Take 19. input double user03 = 75.4; //Finance Stop 20. //+------------------------------------------------------------------+ 21. int OnInit() 22. { 23. chart = new C_ChartFloatingRAD(def_ShortName, new C_Mouse(0, "Indicator Mouse Study"), user01, user02, user03); 24. 25. if (_LastError >= ERR_USER_ERROR_FIRST) return INIT_FAILED; 26. 27. return INIT_SUCCEEDED; 28. } 29. //+------------------------------------------------------------------+ 30. int OnCalculate(const int rates_total, const int prev_calculated, const int begin, const double &price[]) 31. { 32. return rates_total; 33. } 34. //+------------------------------------------------------------------+ 35. void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam) 36. { 37. if (_LastError < ERR_USER_ERROR_FIRST) 38. (*chart).DispatchMessage(id, lparam, dparam, sparam); 39. } 40. //+------------------------------------------------------------------+ 41. void OnDeinit(const int reason) 42. { 43. switch (reason) 44. { 45. case REASON_INITFAILED: 46. ChartIndicatorDelete(ChartID(), 0, def_ShortName); 47. break; 48. case REASON_CHARTCHANGE: 49. (*chart).SaveState(); 50. break; 51. } 52. 53. delete chart; 54. } 55. //+------------------------------------------------------------------+
Quellcode des Indikators Chart Trade
Aber wo sind die neuen Veranstaltungen? Man würde erwarten, sie in der OnChartEvent-Prozedur zu sehen. Sie sind zwar vorhanden, aber der Einfachheit halber wird alles an einer Stelle berücksichtigt, nämlich in der Prozedur DispatchMessage. Und diese Prozedur befindet sich in der Klasse C_ChartFloatingRAD, die im Folgenden vollständig dargestellt wird.
001. //+------------------------------------------------------------------+ 002. #property copyright "Daniel Jose" 003. //+------------------------------------------------------------------+ 004. #include "../Auxiliar/C_Mouse.mqh" 005. #include "C_AdjustTemplate.mqh" 006. //+------------------------------------------------------------------+ 007. #define macro_NameGlobalVariable(A) StringFormat("ChartTrade_%u%s", GetInfoTerminal().ID, A) 008. #define macro_CloseIndicator(A) { \ 009. OnDeinit(REASON_INITFAILED); \ 010. SetUserError(A); \ 011. return; \ 012. } 013. //+------------------------------------------------------------------+ 014. class C_ChartFloatingRAD : private C_Terminal 015. { 016. private : 017. enum eObjectsIDE {MSG_LEVERAGE_VALUE, MSG_TAKE_VALUE, MSG_STOP_VALUE, MSG_MAX_MIN, MSG_TITLE_IDE, MSG_DAY_TRADE, MSG_BUY_MARKET, MSG_SELL_MARKET, MSG_CLOSE_POSITION, MSG_NULL}; 018. struct st00 019. { 020. short x, y, minx, miny, 021. Leverage; 022. string szObj_Chart, 023. szObj_Editable, 024. szFileNameTemplate; 025. long WinHandle; 026. double FinanceTake, 027. FinanceStop; 028. bool IsMaximized, 029. IsDayTrade, 030. IsSaveState; 031. struct st01 032. { 033. short x, y, w, h; 034. color bgcolor; 035. int FontSize; 036. string FontName; 037. }Regions[MSG_NULL]; 038. }m_Info; 039. struct st01 040. { 041. short y[2]; 042. bool bOk; 043. }m_Init; 044. C_Mouse *m_Mouse; 045. //+------------------------------------------------------------------+ 046. void CreateWindowRAD(int w, int h) 047. { 048. m_Info.szObj_Chart = "Chart Trade IDE"; 049. m_Info.szObj_Editable = m_Info.szObj_Chart + " > Edit"; 050. ObjectCreate(GetInfoTerminal().ID, m_Info.szObj_Chart, OBJ_CHART, 0, 0, 0); 051. ObjectSetInteger(GetInfoTerminal().ID, m_Info.szObj_Chart, OBJPROP_XDISTANCE, m_Info.x); 052. ObjectSetInteger(GetInfoTerminal().ID, m_Info.szObj_Chart, OBJPROP_YDISTANCE, m_Info.y); 053. ObjectSetInteger(GetInfoTerminal().ID, m_Info.szObj_Chart, OBJPROP_XSIZE, w); 054. ObjectSetInteger(GetInfoTerminal().ID, m_Info.szObj_Chart, OBJPROP_YSIZE, h); 055. ObjectSetInteger(GetInfoTerminal().ID, m_Info.szObj_Chart, OBJPROP_DATE_SCALE, false); 056. ObjectSetInteger(GetInfoTerminal().ID, m_Info.szObj_Chart, OBJPROP_PRICE_SCALE, false); 057. m_Info.WinHandle = ObjectGetInteger(GetInfoTerminal().ID, m_Info.szObj_Chart, OBJPROP_CHART_ID); 058. }; 059. //+------------------------------------------------------------------+ 060. void AdjustEditabled(C_AdjustTemplate &Template, bool bArg) 061. { 062. for (eObjectsIDE c0 = MSG_LEVERAGE_VALUE; c0 <= MSG_STOP_VALUE; c0++) 063. if (bArg) 064. { 065. Template.Add(EnumToString(c0), "bgcolor", NULL); 066. Template.Add(EnumToString(c0), "fontsz", NULL); 067. Template.Add(EnumToString(c0), "fontnm", NULL); 068. } 069. else 070. { 071. m_Info.Regions[c0].bgcolor = (color) StringToInteger(Template.Get(EnumToString(c0), "bgcolor")); 072. m_Info.Regions[c0].FontSize = (int) StringToInteger(Template.Get(EnumToString(c0), "fontsz")); 073. m_Info.Regions[c0].FontName = Template.Get(EnumToString(c0), "fontnm"); 074. } 075. } 076. //+------------------------------------------------------------------+ 077. inline void AdjustTemplate(const bool bFirst = false) 078. { 079. #define macro_PointsToFinance(A) A * (GetInfoTerminal().VolumeMinimal + (GetInfoTerminal().VolumeMinimal * (m_Info.Leverage - 1))) * GetInfoTerminal().AdjustToTrade 080. 081. C_AdjustTemplate *Template; 082. 083. if (bFirst) 084. { 085. Template = new C_AdjustTemplate(m_Info.szFileNameTemplate = IntegerToString(GetInfoTerminal().ID) + ".tpl", true); 086. for (eObjectsIDE c0 = MSG_LEVERAGE_VALUE; c0 <= MSG_CLOSE_POSITION; c0++) 087. { 088. (*Template).Add(EnumToString(c0), "size_x", NULL); 089. (*Template).Add(EnumToString(c0), "size_y", NULL); 090. (*Template).Add(EnumToString(c0), "pos_x", NULL); 091. (*Template).Add(EnumToString(c0), "pos_y", NULL); 092. } 093. AdjustEditabled(Template, true); 094. }else Template = new C_AdjustTemplate(m_Info.szFileNameTemplate); 095. if (_LastError >= ERR_USER_ERROR_FIRST) 096. { 097. delete Template; 098. 099. return; 100. } 101. m_Info.Leverage = (m_Info.Leverage <= 0 ? 1 : m_Info.Leverage); 102. m_Info.FinanceTake = macro_PointsToFinance(FinanceToPoints(MathAbs(m_Info.FinanceTake), m_Info.Leverage)); 103. m_Info.FinanceStop = macro_PointsToFinance(FinanceToPoints(MathAbs(m_Info.FinanceStop), m_Info.Leverage)); 104. (*Template).Add("MSG_NAME_SYMBOL", "descr", GetInfoTerminal().szSymbol); 105. (*Template).Add("MSG_LEVERAGE_VALUE", "descr", IntegerToString(m_Info.Leverage)); 106. (*Template).Add("MSG_TAKE_VALUE", "descr", DoubleToString(m_Info.FinanceTake, 2)); 107. (*Template).Add("MSG_STOP_VALUE", "descr", DoubleToString(m_Info.FinanceStop, 2)); 108. (*Template).Add("MSG_DAY_TRADE", "state", (m_Info.IsDayTrade ? "1" : "0")); 109. (*Template).Add("MSG_MAX_MIN", "state", (m_Info.IsMaximized ? "1" : "0")); 110. if (!(*Template).Execute()) 111. { 112. delete Template; 113. 114. macro_CloseIndicator(C_Terminal::ERR_FileAcess); 115. }; 116. if (bFirst) 117. { 118. for (eObjectsIDE c0 = MSG_LEVERAGE_VALUE; c0 <= MSG_CLOSE_POSITION; c0++) 119. { 120. m_Info.Regions[c0].x = (short) StringToInteger((*Template).Get(EnumToString(c0), "pos_x")); 121. m_Info.Regions[c0].y = (short) StringToInteger((*Template).Get(EnumToString(c0), "pos_y")); 122. m_Info.Regions[c0].w = (short) StringToInteger((*Template).Get(EnumToString(c0), "size_x")); 123. m_Info.Regions[c0].h = (short) StringToInteger((*Template).Get(EnumToString(c0), "size_y")); 124. } 125. m_Info.Regions[MSG_TITLE_IDE].w = m_Info.Regions[MSG_MAX_MIN].x; 126. AdjustEditabled(Template, false); 127. }; 128. ObjectSetInteger(GetInfoTerminal().ID, m_Info.szObj_Chart, OBJPROP_YSIZE, (m_Info.IsMaximized ? m_Init.y[m_Init.bOk] : m_Info.Regions[MSG_TITLE_IDE].h + 6)); 129. ObjectSetInteger(GetInfoTerminal().ID, m_Info.szObj_Chart, OBJPROP_XDISTANCE, (m_Info.IsMaximized ? m_Info.x : m_Info.minx)); 130. ObjectSetInteger(GetInfoTerminal().ID, m_Info.szObj_Chart, OBJPROP_YDISTANCE, (m_Info.IsMaximized ? m_Info.y : m_Info.miny)); 131. 132. delete Template; 133. 134. ChartApplyTemplate(m_Info.WinHandle, "/Files/" + m_Info.szFileNameTemplate); 135. ChartRedraw(m_Info.WinHandle); 136. 137. #undef macro_PointsToFinance 138. } 139. //+------------------------------------------------------------------+ 140. eObjectsIDE CheckMousePosition(const short x, const short y) 141. { 142. int xi, yi, xf, yf; 143. 144. for (eObjectsIDE c0 = MSG_LEVERAGE_VALUE; c0 <= MSG_CLOSE_POSITION; c0++) 145. { 146. xi = (m_Info.IsMaximized ? m_Info.x : m_Info.minx) + m_Info.Regions[c0].x; 147. yi = (m_Info.IsMaximized ? m_Info.y : m_Info.miny) + m_Info.Regions[c0].y; 148. xf = xi + m_Info.Regions[c0].w; 149. yf = yi + m_Info.Regions[c0].h; 150. if ((x > xi) && (y > yi) && (x < xf) && (y < yf)) return c0; 151. } 152. return MSG_NULL; 153. } 154. //+------------------------------------------------------------------+ 155. inline void DeleteObjectEdit(void) 156. { 157. ChartRedraw(); 158. ObjectsDeleteAll(GetInfoTerminal().ID, m_Info.szObj_Editable); 159. } 160. //+------------------------------------------------------------------+ 161. template <typename T > 162. void CreateObjectEditable(eObjectsIDE arg, T value) 163. { 164. long id = GetInfoTerminal().ID; 165. 166. DeleteObjectEdit(); 167. CreateObjectGraphics(m_Info.szObj_Editable, OBJ_EDIT, clrBlack, 0); 168. ObjectSetInteger(id, m_Info.szObj_Editable, OBJPROP_XDISTANCE, m_Info.Regions[arg].x + m_Info.x + 3); 169. ObjectSetInteger(id, m_Info.szObj_Editable, OBJPROP_YDISTANCE, m_Info.Regions[arg].y + m_Info.y + 3); 170. ObjectSetInteger(id, m_Info.szObj_Editable, OBJPROP_XSIZE, m_Info.Regions[arg].w); 171. ObjectSetInteger(id, m_Info.szObj_Editable, OBJPROP_YSIZE, m_Info.Regions[arg].h); 172. ObjectSetInteger(id, m_Info.szObj_Editable, OBJPROP_BGCOLOR, m_Info.Regions[arg].bgcolor); 173. ObjectSetInteger(id, m_Info.szObj_Editable, OBJPROP_ALIGN, ALIGN_CENTER); 174. ObjectSetInteger(id, m_Info.szObj_Editable, OBJPROP_FONTSIZE, m_Info.Regions[arg].FontSize - 1); 175. ObjectSetString(id, m_Info.szObj_Editable, OBJPROP_FONT, m_Info.Regions[arg].FontName); 176. ObjectSetString(id, m_Info.szObj_Editable, OBJPROP_TEXT, (typename(T) == "double" ? DoubleToString(value, 2) : (string) value)); 177. ChartRedraw(); 178. } 179. //+------------------------------------------------------------------+ 180. bool RestoreState(void) 181. { 182. uCast_Double info; 183. bool bRet; 184. C_AdjustTemplate *Template; 185. 186. if (bRet = GlobalVariableGet(macro_NameGlobalVariable("POST"), info.dValue)) 187. { 188. m_Info.x = (short) info._16b[0]; 189. m_Info.y = (short) info._16b[1]; 190. m_Info.minx = (short) info._16b[2]; 191. m_Info.miny = (short) info._16b[3]; 192. Template = new C_AdjustTemplate(m_Info.szFileNameTemplate = IntegerToString(GetInfoTerminal().ID) + ".tpl"); 193. if (_LastError >= ERR_USER_ERROR_FIRST) bRet = false; else 194. { 195. (*Template).Add("MSG_LEVERAGE_VALUE", "descr", NULL); 196. (*Template).Add("MSG_TAKE_VALUE", "descr", NULL); 197. (*Template).Add("MSG_STOP_VALUE", "descr", NULL); 198. (*Template).Add("MSG_DAY_TRADE", "state", NULL); 199. (*Template).Add("MSG_MAX_MIN", "state", NULL); 200. if (!(*Template).Execute()) bRet = false; else 201. { 202. m_Info.IsDayTrade = (bool) StringToInteger((*Template).Get("MSG_DAY_TRADE", "state")) == 1; 203. m_Info.IsMaximized = (bool) StringToInteger((*Template).Get("MSG_MAX_MIN", "state")) == 1; 204. m_Info.Leverage = (short)StringToInteger((*Template).Get("MSG_LEVERAGE_VALUE", "descr")); 205. m_Info.FinanceTake = (double) StringToDouble((*Template).Get("MSG_TAKE_VALUE", "descr")); 206. m_Info.FinanceStop = (double) StringToDouble((*Template).Get("MSG_STOP_VALUE", "descr")); 207. } 208. }; 209. delete Template; 210. }; 211. 212. GlobalVariablesDeleteAll(macro_NameGlobalVariable("")); 213. 214. return bRet; 215. } 216. //+------------------------------------------------------------------+ 217. public : 218. //+------------------------------------------------------------------+ 219. C_ChartFloatingRAD(string szShortName, C_Mouse *MousePtr, const short Leverage, const double FinanceTake, const double FinanceStop) 220. :C_Terminal(0) 221. { 222. m_Mouse = MousePtr; 223. m_Info.IsSaveState = false; 224. if (!IndicatorCheckPass(szShortName)) return; 225. if (!RestoreState()) 226. { 227. m_Info.Leverage = Leverage; 228. m_Info.IsDayTrade = true; 229. m_Info.FinanceTake = FinanceTake; 230. m_Info.FinanceStop = FinanceStop; 231. m_Info.IsMaximized = true; 232. m_Info.minx = m_Info.x = 115; 233. m_Info.miny = m_Info.y = 64; 234. } 235. m_Init.y[false] = 150; 236. m_Init.y[true] = 210; 237. CreateWindowRAD(170, m_Init.y[m_Init.bOk = false]); 238. AdjustTemplate(true); 239. } 240. //+------------------------------------------------------------------+ 241. ~C_ChartFloatingRAD() 242. { 243. ChartRedraw(); 244. ObjectsDeleteAll(GetInfoTerminal().ID, m_Info.szObj_Chart); 245. if (!m_Info.IsSaveState) 246. FileDelete(m_Info.szFileNameTemplate); 247. 248. delete m_Mouse; 249. } 250. //+------------------------------------------------------------------+ 251. void SaveState(void) 252. { 253. #define macro_GlobalVariable(A, B) if (GlobalVariableTemp(A)) GlobalVariableSet(A, B); 254. 255. uCast_Double info; 256. 257. info._16b[0] = m_Info.x; 258. info._16b[1] = m_Info.y; 259. info._16b[2] = m_Info.minx; 260. info._16b[3] = m_Info.miny; 261. macro_GlobalVariable(macro_NameGlobalVariable("POST"), info.dValue); 262. m_Info.IsSaveState = true; 263. 264. #undef macro_GlobalVariable 265. } 266. //+------------------------------------------------------------------+ 267. void DispatchMessage(const int id, const long &lparam, const double &dparam, const string &sparam) 268. { 269. #define macro_AdjustMinX(A, B) { \ 270. B = (A + m_Info.Regions[MSG_TITLE_IDE].w) > x; \ 271. mx = x - m_Info.Regions[MSG_TITLE_IDE].w; \ 272. A = (B ? (mx > 0 ? mx : 0) : A); \ 273. } 274. #define macro_AdjustMinY(A, B) { \ 275. B = (A + m_Info.Regions[MSG_TITLE_IDE].h) > y; \ 276. my = y - m_Info.Regions[MSG_TITLE_IDE].h; \ 277. A = (B ? (my > 0 ? my : 0) : A); \ 278. } 279. 280. static short sx = -1, sy = -1, sz = -1; 281. static eObjectsIDE obj = MSG_NULL; 282. short x, y, mx, my; 283. double dvalue; 284. bool b1, b2, b3, b4; 285. ushort ev = evChartTradeCloseAll; 286. 287. switch (id) 288. { 289. case CHARTEVENT_CUSTOM + evEA_At_ChartTrade: 290. if (m_Init.bOk = ((lparam >= 0) && (lparam < 2))) 291. CurrentSymbol((bool)lparam); 292. AdjustTemplate(true); 293. break; 294. case CHARTEVENT_CHART_CHANGE: 295. if (!m_Init.bOk) 296. EventChartCustom(GetInfoTerminal().ID, evChartTrade_At_EA, 0, 0, ""); 297. x = (short)ChartGetInteger(GetInfoTerminal().ID, CHART_WIDTH_IN_PIXELS); 298. y = (short)ChartGetInteger(GetInfoTerminal().ID, CHART_HEIGHT_IN_PIXELS); 299. macro_AdjustMinX(m_Info.x, b1); 300. macro_AdjustMinY(m_Info.y, b2); 301. macro_AdjustMinX(m_Info.minx, b3); 302. macro_AdjustMinY(m_Info.miny, b4); 303. if (b1 || b2 || b3 || b4) AdjustTemplate(); 304. break; 305. case CHARTEVENT_MOUSE_MOVE: 306. if ((*m_Mouse).CheckClick(C_Mouse::eClickLeft)) 307. { 308. switch (CheckMousePosition(x = (short)lparam, y = (short)dparam)) 309. { 310. case MSG_MAX_MIN: 311. if (sz < 0) m_Info.IsMaximized = (m_Info.IsMaximized ? false : true); 312. break; 313. case MSG_DAY_TRADE: 314. if ((m_Info.IsMaximized) && (sz < 0)) m_Info.IsDayTrade = (m_Info.IsDayTrade ? false : true); 315. break; 316. case MSG_LEVERAGE_VALUE: 317. if ((m_Info.IsMaximized) && (sz < 0)) CreateObjectEditable(obj = MSG_LEVERAGE_VALUE, m_Info.Leverage); 318. break; 319. case MSG_TAKE_VALUE: 320. if ((m_Info.IsMaximized) && (sz < 0)) CreateObjectEditable(obj = MSG_TAKE_VALUE, m_Info.FinanceTake); 321. break; 322. case MSG_STOP_VALUE: 323. if ((m_Info.IsMaximized) && (sz < 0)) CreateObjectEditable(obj = MSG_STOP_VALUE, m_Info.FinanceStop); 324. break; 325. case MSG_TITLE_IDE: 326. if (sx < 0) 327. { 328. ChartSetInteger(GetInfoTerminal().ID, CHART_MOUSE_SCROLL, false); 329. sx = x - (m_Info.IsMaximized ? m_Info.x : m_Info.minx); 330. sy = y - (m_Info.IsMaximized ? m_Info.y : m_Info.miny); 331. } 332. if ((mx = x - sx) > 0) ObjectSetInteger(GetInfoTerminal().ID, m_Info.szObj_Chart, OBJPROP_XDISTANCE, mx); 333. if ((my = y - sy) > 0) ObjectSetInteger(GetInfoTerminal().ID, m_Info.szObj_Chart, OBJPROP_YDISTANCE, my); 334. if (m_Info.IsMaximized) 335. { 336. m_Info.x = (mx > 0 ? mx : m_Info.x); 337. m_Info.y = (my > 0 ? my : m_Info.y); 338. }else 339. { 340. m_Info.minx = (mx > 0 ? mx : m_Info.minx); 341. m_Info.miny = (my > 0 ? my : m_Info.miny); 342. } 343. break; 344. case MSG_BUY_MARKET: 345. ev = evChartTradeBuy; 346. case MSG_SELL_MARKET: 347. ev = (ev != evChartTradeBuy ? evChartTradeSell : ev); 348. case MSG_CLOSE_POSITION: 349. if ((m_Info.IsMaximized) && (sz < 0) && (m_Init.bOk)) //<< 350. { 351. string szTmp = StringFormat("%d?%s?%s?%c?%d?%.2f?%.2f", ev, _Symbol, GetInfoTerminal().szSymbol, (m_Info.IsDayTrade ? 'D' : 'S'), 352. m_Info.Leverage, FinanceToPoints(m_Info.FinanceTake, m_Info.Leverage), FinanceToPoints(m_Info.FinanceStop, m_Info.Leverage)); 353. PrintFormat("Send %s - Args ( %s )", EnumToString((EnumEvents) ev), szTmp); 354. EventChartCustom(GetInfoTerminal().ID, ev, 0, 0, szTmp); 355. } 356. break; 357. } 358. if (sz < 0) 359. { 360. sz = x; 361. AdjustTemplate(); 362. if (obj == MSG_NULL) DeleteObjectEdit(); 363. } 364. }else 365. { 366. sz = -1; 367. if (sx > 0) 368. { 369. ChartSetInteger(GetInfoTerminal().ID, CHART_MOUSE_SCROLL, true); 370. sx = sy = -1; 371. } 372. } 373. break; 374. case CHARTEVENT_OBJECT_ENDEDIT: 375. switch (obj) 376. { 377. case MSG_LEVERAGE_VALUE: 378. case MSG_TAKE_VALUE: 379. case MSG_STOP_VALUE: 380. dvalue = StringToDouble(ObjectGetString(GetInfoTerminal().ID, m_Info.szObj_Editable, OBJPROP_TEXT)); 381. if (obj == MSG_TAKE_VALUE) 382. m_Info.FinanceTake = (dvalue <= 0 ? m_Info.FinanceTake : dvalue); 383. else if (obj == MSG_STOP_VALUE) 384. m_Info.FinanceStop = (dvalue <= 0 ? m_Info.FinanceStop : dvalue); 385. else 386. m_Info.Leverage = (dvalue <= 0 ? m_Info.Leverage : (short)MathFloor(dvalue)); 387. AdjustTemplate(); 388. obj = MSG_NULL; 389. ObjectDelete(GetInfoTerminal().ID, m_Info.szObj_Editable); 390. break; 391. } 392. break; 393. case CHARTEVENT_OBJECT_DELETE: 394. if (sparam == m_Info.szObj_Chart) macro_CloseIndicator(C_Terminal::ERR_Unknown); 395. break; 396. } 397. ChartRedraw(); 398. } 399. //+------------------------------------------------------------------+ 400. }; 401. //+------------------------------------------------------------------+ 402. #undef macro_NameGlobalVariable 403. #undef macro_CloseIndicator 404. //+------------------------------------------------------------------+
Quellcode der Datei C_ChartFloatingRAD.mqh
Obwohl der Code in der Header-Datei C_ChartFloatingRAD.mqh für viele überwältigend erscheinen mag und es nicht unbedingt notwendig ist, ihn vollständig anzusehen, habe ich mich entschlossen, ihn in diesem Artikel vollständig aufzunehmen. Dies ist auf die Änderungen im Vergleich zum vorherigen Artikel zurückzuführen. Die einzige wesentliche Änderung betrifft jedoch den Klassenkonstruktor. Da sich die Zeilennummern verschoben haben, wollte ich Sie, den Leser, nicht im Unklaren lassen, wenn Sie versuchen, die richtigen Zeilen zu finden. Daher wird der vollständige Code zur Verfügung gestellt.
Konzentrieren wir uns darauf, was sich tatsächlich geändert hat. Nicht im Vergleich zum vorigen Artikel, sondern zur Unterstützung dessen, was im Video am Ende dieses Artikels zu sehen sein wird. Die erste Änderung ist eine kleine Struktur, die in Zeile 39 erscheint. Achten Sie genau auf meine Erklärung, sonst verstehen Sie nicht, was im Video gezeigt wird. Innerhalb dieser Struktur gibt es die Zeile 41. Sie enthält zwei Werte, die die Fenstergröße darstellen, die eigentlich ein OBJ_CHART-Objekt ist. Keine Sorge – Sie werden das bald verstehen. Zeile 42 enthält eine Variable, die zusammen mit diesem Array aus zwei Elementen die im Video gezeigte Funktionalität ermöglicht.
Untersuchen wir nun den Konstruktor der Klasse, der in Zeile 219 beginnt. Beachten Sie, dass der zusätzliche Parameter aus dem vorherigen Artikel nicht mehr vorhanden ist. In Zeile 220 wird der Konstruktor der Klasse C_Terminal aufgerufen und der Zustand vor dem letzten Artikel wiederhergestellt. Achtung: Das Fenster von Chart Trade ist ein OBJ_CHART-Objekt, das minimiert oder maximiert werden kann. Diese Funktionalität wurde bereits implementiert. Wenn der Expert Advisor jedoch nicht auf dem Chart vorhanden ist, auf dem sich Chart Trade befindet, sollen die Schaltflächen ausgeblendet werden.
Um dies zu erreichen, müssen wir die Y-Dimension des Koordinatensystems anpassen. Diese Abmessungen sind in den Zeilen 235 und 236 angegeben. Achten Sie sehr genau darauf: Wenn die Variable m_Init.bOk falsch ist, bedeutet dies, dass etwas nicht ausgerichtet ist. In diesem Fall werden die Interaktionsschaltflächen ausgeblendet, um zu verhindern, dass der Nutzer oder Anwender fälschlicherweise denkt, dass der Auftrag an den Server gesendet werden. Damit die Schaltflächen ausgeblendet werden, wird die Y-Koordinate auf 150 gesetzt. Wenn m_Init.bOk wahr ist, kann der Nutzer oder Anwender Aufträge über den Expert Advisor senden. Die Y-Koordinate wird also auf 210 gesetzt. Dieser Wert von 210 war der zuvor verwendete Standardwert, wie in Zeile 237 zu sehen ist.
Beachten Sie, dass sich die Zeile 237, in der das Objekt OBJ_CHART zur Aufnahme von Chart Trade erstellt wird, geändert hat. Anstatt den festen Wert 210 für die Dimension zu verwenden, greifen wir nun auf den im Array definierten Wert zu. Gleichzeitig wird m_Init.bOk mit dem Wert false initialisiert. Im Wesentlichen wird das Objekt OBJ_CHART zunächst mit einer Y-Koordinate von 150 erstellt. Warum wird dieser Wert nicht einfach direkt verwendet? Der Grund dafür ist, zu testen und zu bestätigen, dass der Code wie vorgesehen funktioniert. Ein direktes Bestehen von 150 Punkten würde nicht die gleiche Sicherheit bieten, dass das Modell korrekt funktioniert. Auf diese Weise wird sichergestellt, dass sich die Implementierung wie erwartet verhält.
Ein neues Detail erscheint in der Prozedur AdjustTemplate, die kurz danach aufgerufen wird, und deren Code in Zeile 77 beginnt. Die einzige Änderung in diesem Verfahren findet sich in Zeile 128. Der Grund dafür ist folgender: Wenn Chart Trade maximiert oder minimiert wird, ändern wir die Y-Koordinate. Um sicherzustellen, dass Chart Trade mit den im Konstruktor festgelegten Regeln übereinstimmt, müssen wir die Y-Koordinate entsprechend anpassen. Selbst wenn Sie versuchen, Chart Trade zu maximieren oder zu minimieren, um auf die Schaltflächen zuzugreifen, werden diese erst sichtbar, wenn die Bedingungen für die Anzeige erfüllt sind.
Dies war ein einfacher Teil. Die Änderungen sind gering und einfach. Schauen wir uns nun die Nachrichtenfunktion an, die ebenfalls einige kleinere Änderungen erfahren hat. Die Prozedur DispatchMessage beginnt in Zeile 267. Bevor Sie sich die Meldungen ansehen, beachten Sie die Zeile 349, in der ein kleiner, aber wichtiger Unterschied eingeführt wurde. Um die Mausprüfung zu vereinfachen, wurde in dieser Zeile ein neuer Prüfwert hinzugefügt. Wenn m_Init.bOk falsch ist und der Nutzer in den Bereich klickt, in dem die Schaltflächen erscheinen würden, verhindert die Prüfung, dass ein Ereignis ausgelöst wird. Dieses Maß an Einfachheit macht das Programmieren elegant. Viele Entwickler würden sonst versuchen, diese Prüfung an anderer Stelle zu implementieren, was das Verfahren unnötig verkompliziert.
Kehren wir nun zum Nachrichtensystem zurück. Wenn der Indikator dem Chart hinzugefügt wird, ist das erste empfangene Ereignis CHARTEVENT_CHART_CHANGE. In Zeile 295 prüfen wir, ob unsere Variable falsch ist. Ist dies der Fall, wird in Zeile 296 ein nutzerdefiniertes Ereignis ausgelöst. Unabhängig davon, ob der Expert Advisor das ausgelöste Ereignis erfasst hat oder dem Chart gerade hinzugefügt wurde, tritt ein weiteres Ereignis ein, das von Chart Trade in Zeile 289 erfasst wird.
Jetzt beginnt der lustige Teil. Wenn der in Zeile 290 geprüfte Wert größer oder gleich Null ist, wird Zeile 291 ausgeführt. Achtung: Wenn der Wert Null ist, steht er für Falsch; wenn er nicht Null ist, steht er für Wahr. Werte, die größer als eins sind, sind jedoch ungültig. Daher sollte der Expert Advisor nur einen Wert von Null oder Eins senden, der angibt, ob ein Minikontrakt oder ein vollständiger Kontrakt verwendet wird. Schließlich wird in Zeile 292 eine Aktualisierung von Chart Trade angefordert. Das mag kompliziert klingen, vor allem weil ein anderer Wert als Null oder Eins eintreffen könnte. In diesem Fall werden die Schaltflächen ausgeblendet. Dies bedeutet, dass der Expert Advisor aus dem Chart entfernt wurde oder ein unerwartetes Ereignis eingetreten ist.
Sie werden jetzt vielleicht denken, dass ich übervorsichtig bin. Wie kann Chart Trade wissen, dass der Expert Advisor entfernt wurde oder dass etwas Ungewöhnliches passiert ist? Um dies zu verstehen, müssen wir den Expert Advisor selbst untersuchen. Dies wird im Folgenden dargestellt.
Wie der Expert Advisor jetzt funktioniert
Der Code des Expert Advisors zu Demonstrationszwecken ist im Folgenden vollständig dargestellt.
01. //+------------------------------------------------------------------+ 02. #property copyright "Daniel Jose" 03. #property description "Virtual Test..." 04. #property description "Demo version between interaction" 05. #property description "of Chart Trade and Expert Advisor" 06. #property version "1.81" 07. #property link "https://www.mql5.com/en/articles/12537" 08. //+------------------------------------------------------------------+ 09. #include <Market Replay\Defines.mqh> 10. //+------------------------------------------------------------------+ 11. class C_Decode 12. { 13. private : 14. struct stInfoEvent 15. { 16. EnumEvents ev; 17. string szSymbol, 18. szContract; 19. bool IsDayTrade; 20. ushort Leverange; 21. double PointsTake, 22. PointsStop; 23. }info[1]; 24. public : 25. //+------------------------------------------------------------------+ 26. C_Decode() 27. { 28. info[0].szSymbol = _Symbol; 29. } 30. //+------------------------------------------------------------------+ 31. bool Decode(const int id, const string sparam) 32. { 33. string Res[]; 34. 35. if (StringSplit(sparam, '?', Res) != 7) return false; 36. stInfoEvent loc = {(EnumEvents) StringToInteger(Res[0]), Res[1], Res[2], (bool)(Res[3] == "D"), (ushort) StringToInteger(Res[4]), StringToDouble(Res[5]), StringToDouble(Res[6])}; 37. if ((id == loc.ev) && (loc.szSymbol == info[0].szSymbol)) info[0] = loc; 38. 39. ArrayPrint(info, 2); 40. 41. return true; 42. } 43. }*GL_Decode; 44. //+------------------------------------------------------------------+ 45. enum eTypeContract {MINI, FULL}; 46. //+------------------------------------------------------------------+ 47. input eTypeContract user00 = MINI; //Cross order in contract 48. //+------------------------------------------------------------------+ 49. bool bOk; 50. //+------------------------------------------------------------------+ 51. int OnInit() 52. { 53. bOk = false; 54. GL_Decode = new C_Decode; 55. 56. return INIT_SUCCEEDED; 57. } 58. //+------------------------------------------------------------------+ 59. void OnTick() {} 60. //+------------------------------------------------------------------+ 61. void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam) 62. { 63. switch (id) 64. { 65. case CHARTEVENT_CUSTOM + evChartTradeBuy : 66. case CHARTEVENT_CUSTOM + evChartTradeSell : 67. case CHARTEVENT_CUSTOM + evChartTradeCloseAll: 68. GL_Decode.Decode(id - CHARTEVENT_CUSTOM, sparam); 69. break; 70. case CHARTEVENT_CHART_CHANGE: 71. if (bOk) break; 72. case CHARTEVENT_CUSTOM + evChartTrade_At_EA: 73. bOk = true; 74. EventChartCustom(ChartID(), evEA_At_ChartTrade, user00, 0, ""); 75. break; 76. } 77. } 78. //+------------------------------------------------------------------+ 79. void OnDeinit(const int reason) 80. { 81. switch (reason) 82. { 83. case REASON_REMOVE: 84. case REASON_INITFAILED: 85. EventChartCustom(ChartID(), evEA_At_ChartTrade, -1, 0, ""); 86. break; 87. } 88. delete GL_Decode; 89. } 90. //+------------------------------------------------------------------+
EA-Quellcode
Nun stellt sich die Frage: Wie funktioniert dieser Code? Bevor wir diese Frage beantworten, möchten wir auf eine kleine Änderung hinweisen, die im letzten Artikel vorgenommen wurde, als Chart Trade anfing, den angezeigten Kontrakt anzugeben. Wenn Sie sich den Code von Chart Trade ansehen – genauer gesagt, die Klasse C_ChartFloatingRAD – werden Sie feststellen, dass in Zeile 351 eine neue Information hinzugefügt wurde. Diese Information betrifft den Namen des Kontrakts und wird in Zeile 36 des Expert Advisor Codes entschlüsselt. Dies ist ein kleines Detail, aber es ist wichtig zu erwähnen. Schauen wir uns nun an, wie der Code des Expert Advisors funktioniert.
Beachten Sie, dass der Code, der dem Nutzer oder Betreiber die Auswahl des Kontrakttyps ermöglicht und sich zuvor im Chart Trade befand, nun in den Zeilen 45 und 47 des Expert Advisors zu finden ist. In diesem Expert Advisor haben wir jedoch nirgends Zugriff auf die Klasse C_Terminal. Warum? Denn wir sind noch nicht mit dem Server verbunden. Der Expert Advisor befindet sich noch im Demonstrationsmodus. Beachten Sie auf jeden Fall die Zeile 49, in der eine Variable deklariert wird, die verhindern soll, dass Änderungen im Chart unnötigerweise ein Ereignis in Chart Trade auslösen. In Zeile 53 wird diese Variable mit false initialisiert. In Zeile 73 wird er auf true gesetzt. Und in Zeile 71 wird geprüft, ob unnötige Nachrichten gesendet werden.
Achtung: Wie der Indikator ist auch das erste Ereignis des Expert Advisors ein CHARTEVENT_CHART_CHANGE, das in Zeile 70 deklariert wird. Die Variable ist falsch. Es wird also derselbe Prozess ausgeführt, der stattfinden würde, wenn Chart Trade etwas anfordern würde. Dies führt uns zu Zeile 72. Daraufhin wird in Zeile 74 ein Ereignis ausgelöst, das entweder den Wert Null oder Eins hat. Aus diesem Grund muss die Enumeration in Zeile 45 mit der angegebenen Reihenfolge übereinstimmen. Eine Änderung der Reihenfolge führt zu falschen Ergebnissen in Chart Trade. Ändern Sie diese Werte nur, wenn Sie sich über die Folgen im Klaren sind. Andernfalls wird Chart Trade den falschen Kontrakt anzeigen.
Der zweite Teil besteht darin, dass der Expert Advisor dem Chart Trade signalisiert, dass er nicht mehr verfügbar ist. Dazu muss Chart Trade die Schaltflächen für die Aufträge ausblenden. Dies geschieht, wenn die Routine in Zeile 79 ausgeführt wird. Unter normalen Umständen löst MetaTrader 5 ein DeInit-Ereignis aus, um ein Objekt aus dem Chart zu entfernen. Die Variable reason enthält die Ursache für diesen Anruf. In Zeile 81 überprüfen wir diesen Grund. Wenn er den angegebenen Kriterien entspricht, wird in Zeile 85 ein Ereignis ausgelöst. Beachten Sie, dass der Wert nach dem Ereignistyp -1 ist. Dies teilt Chart Trade mit, dass der Expert Advisor nicht mehr verfügbar ist, wodurch die Auftragssteuerung automatisch ausgeblendet wird.
Der Rest des Codes bleibt unverändert zu dem, was in dem Artikel über die Chart Trade Kommunikation erklärt wurde. Daher betrachte ich diese Phase als abgeschlossen.
Abschließende Überlegungen
Obwohl ich gezeigt habe, wie man die Kommunikation zur Initialisierung des Expert Advisors und des Chart Trade Indikators einfach implementieren kann, bin ich mir zum jetzigen Zeitpunkt nicht sicher, ob der in diesem Artikel vorgestellte Code tatsächlich verwendet werden wird. Dies ist auf das System der ausstehenden Aufträge zurückzuführen, das sich als „Dorn im Auge“ erweisen wird. Bis zu diesem Punkt kann der Nutzer oder Betreiber Aufträge an den Server senden, indem er entweder einen MiniKontrakt oder einen VollKontrakt verwendet, indem er einfach einen Systemparameter ändert. Dieser Teil war relativ einfach. Die notwendigen Schritte wurden in früheren Artikeln dieser Reihe beschrieben. Im Gegensatz zum einfachen Nachrichtenaustausch zwischen den Anwendungen wird der Prozess durch die Verwaltung von Informationen über ausstehende Aufträge jedoch erheblich erschwert.
Auf jeden Fall sind die Ergebnisse der bisher beschriebenen Änderungen im Video zu sehen. Außerdem sind die im Video verwendeten ausführbaren Dateien unten angehängt, sodass Sie genau sehen und verstehen können, was implementiert wurde.
Übersetzt aus dem Portugiesischen von MetaQuotes Ltd.
Originalartikel: https://www.mql5.com/pt/articles/12537
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.
Neuronale Netze im Handel: Ein Multi-Agent Self-Adaptive Modell (letzter Teil)
Von der Grundstufe bis zur Mittelstufe: Template und Typename (IV)
Trendkriterien im Handel
Neuronale Netze im Handel: Ein selbstanpassendes Multi-Agenten-Modell (MASA)
- 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.