Marktsimulation (Teil 24): Positionsansicht (II)
Einführung
Hallo und herzlich willkommen zu einem weiteren Artikel in der Serie über die Erstellung eines Replay-/Simulationssystems.
Im vorherigen Artikel „ Marktsimulation: Positionsansicht (I)“ begannen wir mit der Erörterung der Möglichkeit, einen Indikator zu erstellen und zu implementieren, der es uns ermöglicht, offene Positionen direkt auf dem Symbol-Chart zu visualisieren. Obwohl viele dies für unnötig halten, da MetaTrader 5 bereits eine solche Funktionalität bietet, funktioniert dieser Ansatz nicht, wenn wir ein auftragsübergreifendes Ausführungsmodell verwenden oder sogar, wie in unserem speziellen Fall, ein Replay-/Simulationssystem.
Wie bereits erwähnt, treten einige Probleme auf, wenn wir mit einer großen Anzahl von Objekten auf dem Chart arbeiten, insbesondere wenn diese Objekte auf die eine oder andere Weise miteinander verbunden sind, was die gesamte Implementierung komplexer macht. Dennoch ist es nicht unser Ziel, uns hier mit den Problemen aufzuhalten, mit denen wir konfrontiert sind, sondern die Lösung aufzuzeigen, für deren Implementierung wir uns entschieden haben. Was wir gleich untersuchen werden, ist, wie bereits vor einiger Zeit erwähnt, weder die endgültige noch die einzig mögliche Lösung. Ich schlage Ihnen, lieber Leser, vor, einen Moment innezuhalten und nachzudenken, bevor Sie anfangen, immer mehr Code zu schreiben, ohne dass ein klarer Bedarf besteht.
Da das Thema, das wir besprechen werden, recht umfangreich ist, werden wir in dieser Einleitung nicht ins Detail gehen. Meine Absicht ist es, dass wir bis zum Ende dieses Artikels etwas geschaffen haben, das etwas besser ist als das, was in der vorherigen Veröffentlichung behandelt wurde. Machen wir uns also an die Arbeit.
Vorbereitung der Implementierung
In Ordnung, der Indikator, den wir im vorherigen Artikel untersucht haben, zeigte Daten zu einer einzelnen offenen Position an. Dies geschah jedoch im Terminal in Textform. Das ist nicht sehr anschaulich, da viele Leute, mich eingeschlossen, möchten, dass die Informationen direkt auf dem Chart angezeigt werden. Das ist das Ziel dieses Artikels. Alles, was wir tun müssen, ist, ein horizontales Linienobjekt zu erstellen, dessen Koordinate der Kurs ist, und diese Koordinate auf den Kurswert zu setzen, den wir im vorherigen Artikel erhalten haben. Etwas sehr Einfaches und leicht Verständliches. Es können jedoch einige Probleme auftreten. Aber wir werden alles Schritt für Schritt durchgehen.
Zuerst werden wir also diese Linien implementieren. Und ja, wir benötigen drei Linien, die der Positionsindikator anzeigen wird. Eine Linie für den Eröffnungskurs der Position und zwei zusätzliche Linien: eine für das Stop-Loss-Niveau und eine weitere für das Take-Profit-Niveau. Aber um diese Linien zu erstellen, werden wir das verwenden, was bereits in unserem Replay-/Simulationscode implementiert wurde.
Da Sie, lieber Leser, dies vielleicht anders organisieren möchten, beginnen wir mit der Erstellung einer Prioritätenliste. Dies ist im folgenden Code zu sehen:
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. evTicTac, //Tick-tock event 31. evHideMouse, //Hide mouse price line 32. evShowMouse, //Show mouse price line 33. evHideBarTime, //Hide bar time 34. evShowBarTime, //Show bar time 35. evHideDailyVar, //Hide daily variation 36. evShowDailyVar, //Show daily variation 37. evHidePriceVar, //Hide instantaneous variation 38. evShowPriceVar, //Show instantaneous variation 39. evCtrlReplayInit, //Initialize replay control 40. evChartTradeBuy, //Market buy event 41. evChartTradeSell, //Market sell event 42. evChartTradeCloseAll, //Event to close positions 43. evChartTrade_At_EA, //Event for communication with the EA 44. evEA_At_ChartTrade, //Event for communication with Chart Trade 45. evChatWriteSocket, //Event to Mini Chat 46. evChatReadSocket //Event To Mini Chat 47. }; 48. //+------------------------------------------------------------------+ 49. enum EnumPriority { //Priority list on objects 50. ePriorityNull = -1, 51. ePriorityDefault = 0, 52. ePriorityChartTrade = 5, 53. ePriorityOrders = 10 54. }; 55. //+------------------------------------------------------------------+
Quellcode der Datei Defines.mqh
Schauen Sie sich die Änderung an, die wir an der Datei Defines.mqh vorgenommen haben. Sie besteht darin, die Enumeration in Zeile 49 hinzuzufügen. Aber warum? Der Grund ist einfach: Wie ich im vorherigen Artikel erwähnt habe, können sich grafische Objekte gegenseitig überlagern. Der schlimmste Fall ist, wenn sich zwei linienartige Objekte vollständig überlagern. In einem solchen Fall gibt es keine schnelle und einfache Möglichkeit, eines der Objekte auszuwählen. Wenn wir MetaTrader 5 jedoch mitteilen, dass selbst Objekte desselben Typs unterschiedliche Prioritäten haben, wird MetaTrader 5 dies bei der Auswahl überlappender Objekte berücksichtigen und dem Objekt mit dem höheren Wert den Vorrang geben. Seien Sie dennoch vorsichtig, wenn Sie dies tun. Ich möchte nicht, dass Sie sich angewöhnen, Ihren eigenen Objekten auf Kosten anderer immer höhere Prioritätswerte zuzuweisen.
Beachten Sie, dass wir vier Prioritätsstufen definieren. Die erste Stufe ist diejenige, die allen von unseren Anwendungen erstellten Objekten zugewiesen wird, die keine hohe Priorität erfordern. Das ist der Wert -1. Zeile 51 hingegen gibt den Wert an, den MetaTrader 5 normalerweise verwendet, wenn der Benutzer ein Objekt zum Chart hinzufügt. Mit anderen Worten, solche Objekte erhalten einen Wert von null. Da einige Objekte einen gewissen Grad an Transparenz aufweisen können und beim Neuzeichnen des Charts möglicherweise ganz oben in der Liste landen, können sie Chart Trade teilweise verdecken. Seien Sie jedoch vorsichtig, denn selbst wenn das Chart Trade-Panel vollständig ausgeblendet ist, sich also hinter einem Objekt befindet, gibt MetaTrader 5 ihm beim Klicken auf den Bereich, in dem es sich befindet, Vorrang gegenüber dem Objekt im Vordergrund.
Dies wird erreicht, indem ihm eine Priorität von fünf zugewiesen wird. Dasselbe geschieht in Zeile 53. Bedenken Sie, dass es einen Unterschied in der Priorität zwischen Chart Trade und den Objekten gibt, die wir in Kürze erstellen werden. Dieser Spielraum ermöglicht es uns, weitere Prioritätsstufen hinzuzufügen, falls dies erforderlich wird. Gemäß dem gerade vorgestellten Modell haben die Objekte, die Teil des Visualisierungssystems für Aufträge und Positionen werden, somit die höchste Priorität von allen. Sie können dies ändern, wenn Sie möchten, aber bedenken Sie, was ich gerade erwähnt habe.
Gut. Nachdem dies erledigt ist, müssen wir zwei weitere Änderungen am bestehenden Code vornehmen. Aber machen Sie sich keine Sorgen und seien Sie nicht beunruhigt deswegen. Dies sind sehr einfache Änderungen, die nur erforderlich sind, damit diese Liste verwendet werden kann. Lassen Sie uns nun den Code der Klasse C_Terminal ändern. Die Stelle, die geändert werden muss, ist im folgenden Fragment zu sehen.
201. //+------------------------------------------------------------------+ 202. inline void CreateObjectGraphics(const string szName, const ENUM_OBJECT obj, const color cor = clrNONE, const EnumPriority zOrder = ePriorityNull) const 203. { 204. ChartSetInteger(m_Infos.ID, CHART_EVENT_OBJECT_CREATE, 0, false); 205. ObjectCreate(m_Infos.ID, szName, obj, m_Infos.SubWin, 0, 0); 206.
Fragment der Klasse C_Terminal
Zeile 202 sollte auf die hier gezeigte Version aktualisiert werden. Wir sorgen hier lediglich dafür, dass diese Funktion die soeben eingeführte Prioritätenliste verwendet. Ein weiteres Fragment, das geändert werden muss, ist ebenfalls im folgenden Fragment zu sehen. Es befindet sich in der Klasse C_ChartFloatingRAD.
164. //+------------------------------------------------------------------+ 165. template <typename T > 166. void CreateObjectEditable(eObjectsIDE arg, T value) 167. { 168. DeleteObjectEdit(); 169. CreateObjectGraphics(m_Info.szObj_Editable, OBJ_EDIT, clrBlack, ePriorityChartTrade); 170. ObjectSetInteger(m_Init.id, m_Info.szObj_Editable, OBJPROP_XDISTANCE, m_Info.Regions[arg].x + m_Info.x + 3); 171. ObjectSetInteger(m_Init.id, m_Info.szObj_Editable, OBJPROP_YDISTANCE, m_Info.Regions[arg].y + m_Info.y + 3);
Fragment aus der Klasse C_ChartFloatingRAD
Die Änderung muss in Zeile 169 vorgenommen werden. Wir müssen die ursprüngliche Zeile durch die im vorherigen Fragment gezeigte Zeile ersetzen. Das bedeutet, dass der Indikator Chart Trade automatisch auf die neue Priorität umschaltet, falls seine eigene Priorität geändert wurde, wenn wir jemals die Prioritätenliste ändern und die Anwendungen neu kompilieren. Dadurch müssen wir im bestehenden Code nichts weiter ändern und können mit der Arbeit am neuen Indikator beginnen.
Beginn der Implementierung der neuen Objekte
Bevor wir zu komplexerem Code übergehen, lassen Sie uns einen Gang zurückschalten, damit Sie meiner Argumentation folgen können. Wir beginnen daher mit dem unten stehenden Code, der lediglich eine Verbesserung des Codes darstellt, den wir im vorherigen Artikel untersucht haben.
01. //+------------------------------------------------------------------+ 02. #property copyright "Daniel Jose" 03. #property description "Indicator for tracking an open position on the server." 04. #property version "1.00" 05. #property indicator_chart_window 06. #property indicator_plots 0 07. //+------------------------------------------------------------------+ 08. #define def_SufixLinePrice "Price" 09. #define def_SufixLineTake "Take" 10. #define def_SufixLineStop "Stop" 11. //+------------------------------------------------------------------+ 12. #include <Market Replay\Auxiliar\C_Terminal.mqh> 13. //+------------------------------------------------------------------+ 14. input color user00 = clrBlue; //Color Line Price 15. input color user01 = clrForestGreen; //Color Line Take Profit 16. input color user02 = clrFireBrick; //Color Line Stop Loss 17. //+------------------------------------------------------------------+ 18. C_Terminal *Terminal; 19. struct st 20. { 21. long id; 22. string szPrefixName; 23. }glVariables; 24. //+------------------------------------------------------------------+ 25. void CreateLinePriceOpen(const string szObjName) 26. { 27. (*Terminal).CreateObjectGraphics(szObjName, OBJ_HLINE, user00, ePriorityNull); 28. ObjectSetDouble(glVariables.id, szObjName, OBJPROP_PRICE, PositionGetDouble(POSITION_PRICE_OPEN)); 29. } 30. //+------------------------------------------------------------------+ 31. int OnInit() 32. { 33. ZeroMemory(glVariables); 34. Terminal = new C_Terminal(); 35. if (!PositionSelect((*Terminal).GetInfoTerminal().szSymbol)) return INIT_FAILED; 36. glVariables.id = (*Terminal).GetInfoTerminal().ID; 37. glVariables.szPrefixName = IntegerToString(PositionGetInteger(POSITION_TICKET)); 38. CreateLinePriceOpen(glVariables.szPrefixName + def_SufixLinePrice); 39. 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. return rates_total; 46. } 47. //+------------------------------------------------------------------+ 48. void OnDeinit(const int reason) 49. { 50. delete Terminal; 51. if (glVariables.id > 0) 52. ObjectsDeleteAll(glVariables.id, glVariables.szPrefixName); 53. } 54. //+------------------------------------------------------------------+
Quellcode des Positionsindikators
Sehr gut. Wenn Sie sich den vorherigen Code ansehen, denken Sie wahrscheinlich bereits: „Nun, jetzt ist die Sache wirklich kompliziert geworden.“ Aber nein, wir befinden uns immer noch auf der grundlegenden Ebene. Der obige Code erstellt nur eine Linie im Chart, wenn eine offene Position für das Symbol besteht, auf dessen Chart der Indikator installiert ist. Dieser Code bleibt sehr einfach, kompakt und enthält keine Überprüfungen. Das Programm erstellt einfach ein H_LINE-Objekt und zeigt es im Chart mit der vom Benutzer angegebenen Farbe an. Das ist ganz einfach. Aber wenn Sie es gewohnt sind, Objekte mit MQL5 im Chart zu platzieren, sind Sie vielleicht überrascht, dass keiner der Aufrufe, die Sie zu sehen gewohnt sind, hier erscheint. Es stellt sich also die Frage: Funktioniert dieser Code wirklich? Und falls ja, wie funktioniert er?
Lassen Sie uns mit Zeile 08 beginnen. Dort haben wir eine Definition, genau wie in den nächsten beiden Zeilen, in denen wir das Suffix für die Linienobjekte definieren, die wir erstellen werden. Der Grund dafür wird später klar werden. In den Zeilen 14 bis 16 erlauben wir dem Benutzer, die Farben zu ändern, die für die Linien verwendet werden. Bisher ist hier nichts sehr kompliziert. An dieser Stelle werden die Dinge jedoch interessant. Dies geschieht in Zeile 19, wo wir mit der Definition einer Struktur beginnen, die später verschwinden wird. Vorerst werden wir die Dinge jedoch auf diese Weise tun, denn Sie, lieber Leser, müssen meine Argumentation verstehen. Auf diese Weise werden Sie verstehen können, warum ich es so formuliere.
Lassen Sie uns die Prozedur in Zeile 25 vorerst überspringen und zu Zeile 31 übergehen. Dort beginnt der Indikator in MetaTrader 5 zu arbeiten. Beachten Sie, dass wir als Erstes den Speicher der Struktur glVariables auf null setzen. Warum das? Der Grund dafür ist, wie wir bereits gesagt haben, dass diese Struktur später verschwinden wird. Aber selbst wenn sie unverändert bleibt, müssen wir sicherstellen, dass alle in der Struktur vorhandenen Variablen einen Anfangswert von null haben. Dies mit dem gezeigten Aufruf zu tun, ist einfacher, als Variable für Variable zu deklarieren. Und falls ich eine davon vergessen sollte, bleibt ihr Wert ebenfalls null.
Also gut. Die Zeilen 34 und 35 wurden bereits im vorherigen Artikel erklärt, daher können wir sie überspringen. Zeile 36 hingegen speichert lediglich den in der Klasse C_Terminal gespeicherten Wert für die spätere Verwendung. Nun kommen wir zu den Hauptzeilen. In der ersten von ihnen, also Zeile 37, definieren wir den Wert, der als Präfix für den Namen des zu erstellenden Objekts dient, in diesem Fall ein HLINE-Objekt. Der Wert dieses Präfixes ist genau der numerische Wert, der das Positionsticket identifiziert. Dies stellt sicher, dass es nur ein Objekt mit diesem Namen im Chart gibt.
Da wir diesen gleichen Wert jedoch später wiederverwenden werden, müssen wir etwas tun, damit MetaTrader 5 die Objekte, die wir auf dem Chart platzieren werden, nicht verwechselt und entscheidet, dass das Objekt bereits existiert, obwohl wir eigentlich ein neues erstellen. Jetzt beginnen Sie, die Definitionen in den Zeilen 08 bis 10 zu verstehen. Aber kehren wir zum Code zurück, denn wir sind jetzt bei Zeile 38, wo wir die Prozedur aus Zeile 25 für den folgenden Zweck aufrufen: eine Linie im Chart zu erstellen und sie am Eröffnungskurs der Position zu platzieren.
Gehen wir dann zur Prozedur in Zeile 25, um einige Dinge zu verstehen. In diesem Stadium ist der Code noch recht einfach und leicht zu verstehen. Diese Prozedur erhält den Namen, der dem HLINE-Objekt zugewiesen wird. Aber wo erstellen wir dieses Objekt? Wir tun dies über die Klasse C_Terminal, speziell in Zeile 27. Achten Sie nun genau auf Folgendes: Im vierten Parameter von Zeile 27 verwenden wir eine sehr niedrige Priorität, tatsächlich die niedrigste in der Liste. Warum?
Das könnte man meinen: „Aber ist diese Linie, die wir gerade erstellen, nicht Teil des Positions- und Auftragsindikators? Warum wird diesem Objekt eine niedrigere Auswahlpriorität zugewiesen als den anderen Objekten? Das ergibt keinen Sinn. Meiner Meinung nach sollte die Priorität diejenige sein, die wir als Priorität für Objekte aufgelistet haben, die vom Positions- und Auftragsindikator erstellt werden.“ Nein, lieber Leser, so zu denken, besonders in diesem speziellen Fall, ist falsch, und der Grund ist einfach. Diese horizontale Linie, und das soll absolut klar sein, nur diese Linie, muss die niedrigstmögliche Priorität haben. Der Grund ist einfach: Sie wird nicht verschoben und sollte keine Ereignisse empfangen, da sie den Kurs oder Punkt darstellt, an dem der Handelsserver die offene Position markiert. Verstehen Sie jetzt, warum ihr diese Priorität zugewiesen wurde?
Aber Sie sollten sich trotzdem nicht entspannen. Wir haben nur die Linie erstellt und sie auf dem Chart positioniert. Diese Positionierung erfolgt in Zeile 28 des vorherigen Codes. Das ist alles. Wenn der Benutzer diese Linie auf dem Chart löscht, verschiebt oder sogar auswählt, kann er sie bewegen. Dies widerspricht völlig dem Prinzip, das wir mit diesem Indikator umsetzen wollen. Um dieses Problem zu lösen, müssen wir mehr Code implementieren. Da das Ziel hier jedoch darin besteht, die Dinge so klar wie möglich zu erklären, werden wir dies schrittweise tun, damit Sie verstehen können, warum das Objekt, das in diesem Fall die Kurslinie ist, nicht verschoben werden darf.
Auf jeden Fall wird beim Entfernen des Indikators vom Chart die Prüfung in Zeile 51 durchgeführt. Wenn die Bedingung erfüllt ist, löscht Zeile 52 alle Objekte, deren Namen mit dem Präfix beginnen, das in diesem Code verwendet wird, um sie auf dem Chart zu erstellen und anzuzeigen. Ich hoffe, dieser Teil ist klar, denn das gleiche Prinzip wird verwendet, um die horizontalen Linien hinzuzufügen, die anzeigen, wo sich Stop-Loss und Take-Profit befinden. Es gibt jedoch ein paar Unterschiede, wie im folgenden Fragment zu sehen ist:
01. //+------------------------------------------------------------------+ 02. #property copyright "Daniel Jose" 03. #property description "Indicator for tracking an open position on the server." 04. #property version "1.00" 05. #property indicator_chart_window 06. #property indicator_plots 0 07. //+------------------------------------------------------------------+ 08. #define def_SufixLinePrice "Price" 09. #define def_SufixLineTake "Take" 10. #define def_SufixLineStop "Stop" 11. //+------------------------------------------------------------------+ 12. #include <Market Replay\Auxiliar\C_Terminal.mqh> 13. //+------------------------------------------------------------------+ 14. input color user00 = clrBlue; //Color Line Price 15. input color user01 = clrForestGreen; //Color Line Take Profit 16. input color user02 = clrFireBrick; //Color Line Stop Loss 17. //+------------------------------------------------------------------+ 18. C_Terminal *Terminal; 19. struct st 20. { 21. long id; 22. string szPrefixName; 23. }glVariables; 24. //+------------------------------------------------------------------+ 25. void CreateLinePriceOpen(const string szObjName) 26. { 27. (*Terminal).CreateObjectGraphics(szObjName, OBJ_HLINE, user00, ePriorityNull); 28. ObjectSetDouble(glVariables.id, szObjName, OBJPROP_PRICE, PositionGetDouble(POSITION_PRICE_OPEN)); 29. } 30. //+------------------------------------------------------------------+ 31. void CreateLineStopAndTake(const string szObjName, const double price, const color cor) 32. { 33. if (price <= 0) return; 34. (*Terminal).CreateObjectGraphics(szObjName, OBJ_HLINE, cor, ePriorityOrders); 35. ObjectSetDouble(glVariables.id, szObjName, OBJPROP_PRICE, price); 36. } 37. //+------------------------------------------------------------------+ 38. int OnInit() 39. { 40. ZeroMemory(glVariables); 41. Terminal = new C_Terminal(); 42. if (!PositionSelect((*Terminal).GetInfoTerminal().szSymbol)) return INIT_FAILED; 43. glVariables.id = (*Terminal).GetInfoTerminal().ID; 44. glVariables.szPrefixName = IntegerToString(PositionGetInteger(POSITION_TICKET)); 45. CreateLinePriceOpen(glVariables.szPrefixName + def_SufixLinePrice); 46. CreateLineStopAndTake(glVariables.szPrefixName + def_SufixLineTake, PositionGetDouble(POSITION_TP), user01); 47. CreateLineStopAndTake(glVariables.szPrefixName + def_SufixLineStop, PositionGetDouble(POSITION_SL), user02); 48. 49. return INIT_SUCCEEDED; 50. } 51. //+------------------------------------------------------------------+ 52. int OnCalculate(const int rates_total, const int prev_calculated, const int begin, const double &price[]) 53. { 54. return rates_total; 55. } 56. //+------------------------------------------------------------------+ 57. void OnDeinit(const int reason) 58. { 59. delete Terminal; 60. if (glVariables.id > 0) 61. ObjectsDeleteAll(glVariables.id, glVariables.szPrefixName); 62. } 63. //+------------------------------------------------------------------+
Quellcode des Positionsindikators
Haben Sie bemerkt, dass nur sehr wenig Code hinzugefügt wurde? Wenn Sie diese Anwendung jedoch jetzt auf einem Chart verwenden, auf dem nur eine Position für das Symbol offen ist, können Sie sehen, wo sich Eröffnungskurs, Take-Profit und Stop-Loss befinden. Lassen Sie uns also sehen, was dem Code hinzugefügt wurde, um diese Visualisierung zu ermöglichen.
Nun haben wir in Zeile 31 eine neue Prozedur. Dies ist die Prozedur, die die horizontalen Take-Profit- und Stop-Loss-Linien erstellt. Beachten Sie, dass es sich praktisch um dieselbe Prozedur handelt wie die, die die Eröffnungskurslinie erstellt. Aber warum habe ich sie getrennt gehalten? Ich habe dies getan, damit Sie verstehen, dass die Eröffnungskurslinie eine andere Priorität haben muss als die anderen Linien, bei denen es sich in diesem Fall um die Take-Profit- und Stop-Loss-Linien handelt. Ein weiteres Detail: In Zeile 33 prüfen wir, ob der Kurswert nicht gleich null und nicht kleiner als null ist, da dies keinen Sinn ergeben würde. In diesem Fall wird das horizontale Linienobjekt nicht erstellt.
Aber selbst nachdem die Fähigkeit des Indikators hinzugefügt wurde, zwei weitere Linien auf dem Chart zu platzieren, werden beim Entfernen des Indikators auch alle von ihm erstellten Linien entfernt. Somit mussten wir nur die Zeilen 46 und 47 zum Code hinzufügen, damit alles vollständig ist, zumindest in seiner grundlegendsten Form. Als Ergebnis werden Sie das folgende Bild sehen können, wenn sich der Indikator auf dem Chart befindet und eine Position offen ist.

Aber bevor wir zu etwas noch Komplexerem übergehen, lassen Sie uns etwas Text hinzufügen, damit wir wissen, was jede Linie auf dem Chart darstellt, denn ohne dies könnten wir versucht sein, sie manuell über das Fenster „Objekte“ von MetaTrader 5 zu löschen. Dieses Fenster ist auf dem folgenden Bild zu sehen:

Hier können wir alle auf dem Chart vorhandenen Objekte sehen, unabhängig davon, ob sie ausgeblendet sind oder nicht. Wenn ein Objekt jedoch in irgendeiner Weise auf dem Chart vorhanden ist, erscheint es in dieser Liste. Und wir möchten nicht, dass der Benutzer eines dieser Objekte einfach löscht, weil er dessen Zweck nicht kennt. Wir werden also eine kleine Änderung am Code vornehmen. Diese Änderung kann an zwei Stellen vorgenommen werden: hier im Indikator oder in der Klasse C_Terminal. Um ehrlich zu sein, möchte ich jedoch keine Klasse ändern, die bereits vollkommen stabil ist, indem ich ohne triftigen Grund mögliche Instabilitäten einführe. Da wir also den Status einiger Eigenschaften der neu erstellten Objekte ändern müssen, lassen Sie uns dies hier lokal tun.
Weitere Verbesserungen am Code
Eine Sache, die wir hinzufügen können, damit der Benutzer versteht, was ein bestimmtes Objekt ist und wozu es dient, ist die Objektbeschreibung. Es ist ganz einfach. Wir ersetzen einfach den obigen Code durch den nachfolgend gezeigten Code:
01. //+------------------------------------------------------------------+ 02. #property copyright "Daniel Jose" 03. #property description "Indicator for tracking an open position on the server." 04. #property version "1.00" 05. #property indicator_chart_window 06. #property indicator_plots 0 07. //+------------------------------------------------------------------+ 08. #define def_SufixLinePrice "Price" 09. #define def_SufixLineTake "Take" 10. #define def_SufixLineStop "Stop" 11. //+------------------------------------------------------------------+ 12. #include <Market Replay\Auxiliar\C_Terminal.mqh> 13. //+------------------------------------------------------------------+ 14. input color user00 = clrRoyalBlue; //Color Line Price 15. input color user01 = clrForestGreen; //Color Line Take Profit 16. input color user02 = clrFireBrick; //Color Line Stop Loss 17. //+------------------------------------------------------------------+ 18. C_Terminal *Terminal; 19. struct st 20. { 21. long id; 22. string szPrefixName; 23. }glVariables; 24. //+------------------------------------------------------------------+ 25. void CreateLinePriceOpen(const string szObjName, const string szDescription = "\n") 26. { 27. (*Terminal).CreateObjectGraphics(szObjName, OBJ_HLINE, user00, ePriorityNull); 28. ObjectSetDouble(glVariables.id, szObjName, OBJPROP_PRICE, PositionGetDouble(POSITION_PRICE_OPEN)); 29. ObjectSetString(glVariables.id, szObjName, OBJPROP_TEXT, szDescription); 30. ObjectSetString(glVariables.id, szObjName, OBJPROP_TOOLTIP, szDescription); 31. } 32. //+------------------------------------------------------------------+ 33. void CreateLineStopAndTake(const string szObjName, const double price, const color cor, const string szDescription = "\n") 34. { 35. if (price <= 0) return; 36. (*Terminal).CreateObjectGraphics(szObjName, OBJ_HLINE, cor, ePriorityOrders); 37. ObjectSetDouble(glVariables.id, szObjName, OBJPROP_PRICE, price); 38. ObjectSetString(glVariables.id, szObjName, OBJPROP_TEXT, szDescription); 39. ObjectSetString(glVariables.id, szObjName, OBJPROP_TOOLTIP, szDescription); 40. } 41. //+------------------------------------------------------------------+ 42. int OnInit() 43. { 44. ZeroMemory(glVariables); 45. Terminal = new C_Terminal(); 46. if (!PositionSelect((*Terminal).GetInfoTerminal().szSymbol)) return INIT_FAILED; 47. glVariables.id = (*Terminal).GetInfoTerminal().ID; 48. glVariables.szPrefixName = IntegerToString(PositionGetInteger(POSITION_TICKET)); 49. CreateLinePriceOpen(glVariables.szPrefixName + def_SufixLinePrice, "Position opening price."); 50. CreateLineStopAndTake(glVariables.szPrefixName + def_SufixLineTake, PositionGetDouble(POSITION_TP), user01, "Take Profit point."); 51. CreateLineStopAndTake(glVariables.szPrefixName + def_SufixLineStop, PositionGetDouble(POSITION_SL), user02, "Stop Loss point."); 52. 53. return INIT_SUCCEEDED; 54. } 55. //+------------------------------------------------------------------+ 56. int OnCalculate(const int rates_total, const int prev_calculated, const int begin, const double &price[]) 57. { 58. return rates_total; 59. } 60. //+------------------------------------------------------------------+ 61. void OnDeinit(const int reason) 62. { 63. delete Terminal; 64. if (glVariables.id > 0) 65. ObjectsDeleteAll(glVariables.id, glVariables.szPrefixName); 66. } 67. //+------------------------------------------------------------------+
Quellcode des Positionsindikators
Noch einmal, es musste nur eine sehr geringe Menge an Code hinzugefügt werden, um das in der folgenden Abbildung gezeigte Ergebnis zu erhalten.

Und auch in der Objektliste, wo nun Folgendes erscheint:

Denken Sie daran: Damit der Text wie im Bild oben dargestellt im Chart angezeigt wird, muss die folgende Option in den Chart-Einstellungen aktiviert sein:

Diese Eigenschaft kann auch über MQL5-Code aktiviert werden. Dazu müssen Sie dem Programm nur eine Zeile Code hinzufügen:
41. //+------------------------------------------------------------------+ 42. int OnInit() 43. { 44. ZeroMemory(glVariables); 45. Terminal = new C_Terminal(); 46. if (!PositionSelect((*Terminal).GetInfoTerminal().szSymbol)) return INIT_FAILED; 47. glVariables.id = (*Terminal).GetInfoTerminal().ID; 48. ChartSetInteger(glVariables.id, CHART_SHOW_OBJECT_DESCR, true); 49. glVariables.szPrefixName = IntegerToString(PositionGetInteger(POSITION_TICKET)); 50. CreateLinePriceOpen(glVariables.szPrefixName + def_SufixLinePrice, "Position opening price."); 51. CreateLineStopAndTake(glVariables.szPrefixName + def_SufixLineTake, PositionGetDouble(POSITION_TP), user01, "Take Profit point."); 52. CreateLineStopAndTake(glVariables.szPrefixName + def_SufixLineStop, PositionGetDouble(POSITION_SL), user02, "Stop Loss point."); 53. 54. return INIT_SUCCEEDED; 55. } 56. //+------------------------------------------------------------------+
Hauptcode-Fragment
Beachten Sie, dass die hinzugefügte Zeile speziell Zeile 48 ist. Dies aktiviert dieselbe Option, die im Bild oben gezeigt wird. Da dies jedoch ein weiteres Element hinzufügt, das der Code analysieren müsste, um zu vermeiden, dass Benutzereinstellungen geändert werden, die Sie als Programmierer möglicherweise nicht ändern möchten, werde ich es in der endgültigen Version des Codes nicht verwenden. Aber wenn Sie dies tun möchten, können Sie dies problemlos tun. Ebenso können Sie auch die Farbe ändern, in der der Text angezeigt wird. Dies erfolgt im nächsten Punkt, der im Bild unten markiert ist:

Wir können diese Eigenschaft auch über MQL5-Code ändern. Aber auch hier möchte ich nicht in Einstellungen eingreifen oder diese ändern, an die der Benutzer bereits gewöhnt ist. Ich möchte so wenig Aufwand wie möglich betreiben, um das beabsichtigte Ziel zu erreichen. Aber zurück zum Hauptcode: Lieber Leser, Sie müssen auf etwas achten: Sowohl bei der Deklaration der Prozedur CreateLinePriceOpen als auch bei der Deklaration der Prozedur CreateLineStopAndTake wird dem Wert szDescription ein spezifischer Wert zugewiesen. Wenn für das Objekt keine Beschreibung angegeben ist, erhält es eine Beschreibung mit dem Wert „\n“. Dies ist in der MQL5-Dokumentation angegeben, die Folgendes besagt:
Der „Tooltip“-Text. Wenn die Eigenschaft nicht definiert ist, wird ein vom Terminal automatisch generierter Tooltip angezeigt. Der Tooltip kann deaktiviert werden, indem ihm der Wert „\n“ (Zeilenvorschub) zugewiesen wird.
Somit wird sich dieser Standardwert, den szDescription erhält, nicht tatsächlich auf die Informationen auswirken, die in der Eigenschaft OBJPROP_TEXT in den Zeilen 29 und 38 platziert sind. Es verhindert jedoch, dass das MetaTrader 5-Terminal einen Wert für die in den Zeilen 30 und 39 deklarierte Eigenschaft OBJPROP_TOOLTIP generiert.
„Wie greifen wir auf die Eigenschaft OBJPROP_TOOLTIP zu? Ist sie nicht dasselbe wie OBJPROP_TEXT?“ Nein, sie sind nicht dasselbe. Die Eigenschaft OBJPROP_TEXT ist genau das, was wir sehen, wenn ein Objekt das Hinzufügen von Text erlaubt. Eines der Objekte, das dies erlaubt, ist die horizontale Linie. Diese Eigenschaft ist jedoch nicht bei allen Objekten vorhanden.
Die Eigenschaft OBJPROP_TOOLTIP ist jedoch bei allen Chartobjekten vorhanden, und der Beschreibungstext der beiden Eigenschaften muss nicht unbedingt übereinstimmen. Sie können sich unterscheiden, da OBJPROP_TOOLTIP seinen Text nur anzeigt, wenn der Cursor eine Weile über dem Objekt schwebt. In diesem Moment erscheint ein kleines Fenster mit dem Inhalt der Eigenschaft OBJPROP_TOOLTIP.
Falls Ihnen dies bisher nicht aufgefallen ist, zeigt die untenstehende Animation, wofür dieses kleine Fenster gedacht ist und wie man den Inhalt der Eigenschaft OBJPROP_TOOLTIP anzeigen kann.

Bitte beachten Sie, dass diese Funktion in Bezug auf ihren Inhalt, ihr Interaktionsmodell und ihren Zweck als Hilfsmittel dient. Es ist etwas Einfaches, Direktes und Praktisches für viele Situationen im Allgemeinen. Alle Objekte können über diese Eigenschaft Informationen empfangen. Wenn Sie Benutzern helfen möchten, die Funktionsweise Ihrer Anwendung zu verstehen, insbesondere wenn sie viele Schaltflächen enthält, die oft als Symbole oder Bilder erscheinen, verwenden Sie diese Eigenschaft, um dem Benutzer die notwendige Hilfe bereitzustellen. Sie werden sehr dankbar sein, da dies ihre Erfahrung mit Ihrer Anwendung verbessern wird. Behalten Sie dies im Hinterkopf.
Wir nähern uns also dem Ende dieses Artikels, aber bevor wir abschließen, wie wäre es, die Erstellung der Linien in einer einzigen Prozedur zu kombinieren? Dies spart in Zukunft Zeit und Mühe, da wir andere Methoden verwenden werden, um diese Linien zu verschieben. Der endgültige Code, der im nächsten Artikel verwendet wird, ist unten dargestellt:
01. //+------------------------------------------------------------------+ 02. #property copyright "Daniel Jose" 03. #property description "Indicator for tracking an open position on the server." 04. #property version "1.00" 05. #property indicator_chart_window 06. #property indicator_plots 0 07. //+------------------------------------------------------------------+ 08. #define def_SufixLinePrice "Price" 09. #define def_SufixLineTake "Take" 10. #define def_SufixLineStop "Stop" 11. //+------------------------------------------------------------------+ 12. #include <Market Replay\Auxiliar\C_Terminal.mqh> 13. //+------------------------------------------------------------------+ 14. input color user00 = clrRoyalBlue; //Color Line Price 15. input color user01 = clrForestGreen; //Color Line Take Profit 16. input color user02 = clrFireBrick; //Color Line Stop Loss 17. //+------------------------------------------------------------------+ 18. C_Terminal *Terminal; 19. struct st 20. { 21. long id; 22. string szPrefixName; 23. }glVariables; 24. //+------------------------------------------------------------------+ 25. void CreateLineInfos(const string szObjName, const double price, const color cor, const string szDescription = "\n") 26. { 27. if (price <= 0) return; 28. (*Terminal).CreateObjectGraphics(szObjName, OBJ_HLINE, cor, (EnumPriority)(cor == user00 ? ePriorityNull : ePriorityOrders)) 29. ObjectSetDouble(glVariables.id, szObjName, OBJPROP_PRICE, price); 30. ObjectSetString(glVariables.id, szObjName, OBJPROP_TEXT, szDescription); 31. ObjectSetString(glVariables.id, szObjName, OBJPROP_TOOLTIP, szDescription); 32. ObjectSetInteger(glVariables.id, szObjName, OBJPROP_SELECTABLE, cor != user00); 33. } 34. //+------------------------------------------------------------------+ 35. int OnInit() 36. { 37. ZeroMemory(glVariables); 38. Terminal = new C_Terminal(); 39. if (!PositionSelect((*Terminal).GetInfoTerminal().szSymbol)) return INIT_FAILED; 40. glVariables.id = (*Terminal).GetInfoTerminal().ID; 41. glVariables.szPrefixName = IntegerToString(PositionGetInteger(POSITION_TICKET)); 42. CreateLineInfos(glVariables.szPrefixName + def_SufixLinePrice, PositionGetDouble(POSITION_PRICE_OPEN), user00, "Position opening price."); 43. CreateLineInfos(glVariables.szPrefixName + def_SufixLineTake, PositionGetDouble(POSITION_TP), user01, "Take Profit point."); 44. CreateLineInfos(glVariables.szPrefixName + def_SufixLineStop, PositionGetDouble(POSITION_SL), user02, "Stop Loss point."); 45. 46. return INIT_SUCCEEDED; 47. } 48. //+------------------------------------------------------------------+ 49. int OnCalculate(const int rates_total, const int prev_calculated, const int begin, const double &price[]) 50. { 51. return rates_total; 52. } 53. //+------------------------------------------------------------------+ 54. void OnDeinit(const int reason) 55. { 56. delete Terminal; 57. if (glVariables.id > 0) 58. ObjectsDeleteAll(glVariables.id, glVariables.szPrefixName); 59. } 60. //+------------------------------------------------------------------+
Quellcode des Positionsindikators
Dies ist der Code, den wir im nächsten Artikel ändern werden. Da sich nichts Wesentliches geändert hat, denke ich, dass Sie durch das einfache Lesen dieses Artikels und kurzes Nachdenken leicht verstehen werden, wie es funktioniert. Der einzige Hinweis, und hier muss ich Sie warnen: Verwenden Sie nicht dieselbe Farbe für alle drei Linien. Sie können sogar dieselbe Farbe für die Stop-Loss- und Take-Profit-Linien verwenden, aber verwenden Sie nicht dieselbe Farbe für die Eröffnungskurslinie. Dies liegt daran, dass wir in Zeile 28 sowie in Zeile 32 die Farbe als Mittel zur Unterscheidung verwenden. Dies ist jedoch kein großes Problem. Es könnte Sie nur verwirren und Sie daran hindern, zu verstehen, warum die Dinge nicht so laufen, wie Sie es erwarten.
Abschließende Überlegungen
In diesem Artikel habe ich versucht, so einfach und klar wie möglich zu zeigen, wie ein Indikator verwendet werden kann, um offene Positionen auf dem Handelsserver zu überwachen. Wir haben dies schrittweise getan, um zu zeigen, dass Sie nicht all dies in einen Expert Advisor aufnehmen müssen. Viele von Ihnen haben sich wahrscheinlich aus dem einen oder anderen Grund daran gewöhnt, dies zu tun. Tatsächlich ergibt dieser Ansatz jedoch nicht viel Sinn, da mit der Weiterentwicklung dieser Implementierung deutlich wird, dass verschiedene Arten von Indikatoren erstellt oder implementiert werden können, um Positionen oder Aufträge zu verfolgen und mit ihnen zu interagieren, die auf dem Server oder, in unserem Fall, im Replay-/Simulationssystem registriert sind.
Obwohl die Hauptidee hier jedoch darin besteht, den Indikator im Replay-/Simulationssystem zu verwenden, möchte ich, dass Sie beachten, dass wir ihn noch nicht auf das simulierte Symbol oder auf eine Situation angewendet haben, in der wir das Replay ausführen. Ich verwende und teste den Indikator an echten Symbolen und echten Positionen. Daher ist es sinnlos, ihn nur im Replay-/Simulationssystem zu verwenden. Ich möchte Sie vor der folgenden Vorsichtsmaßnahme bei der Verwendung warnen: Er zeigt nur eine Position an. Bei HEDGING-Konten reicht es nicht aus, zwei oder mehr offene Positionen zu haben und zu erwarten, dass der Indikator funktioniert, da er dazu noch nicht in der Lage ist.
Bald werden wir die richtige Art und Weise sehen, diesen Indikator zu verwenden, wodurch mehr als eine offene Position visualisiert werden kann. Bei NETTING-Konten werden Sie dieses Problem nicht haben, da diese es nicht erlauben, dasselbe Symbol gleichzeitig zu kaufen und zu verkaufen. HEDGING-Konten erlauben dies jedoch. In diesem speziellen Fall müssen wir eine kleine Anpassung an der Art und Weise vornehmen, wie dieser Indikator verwendet wird. Aber der Implementierungsaufwand ist so gering, dass es sich ehrlich gesagt lohnt, darüber nachzudenken, diese Arbeit vom Expert Advisor auszulagern. Dies liegt daran, dass ich noch nicht alle Fähigkeiten dieses Indikators gezeigt habe. Daher werden wir diesen Indikator im nächsten Artikel ein wenig verbessern, da er hier einen kleinen Nachteil hat. Wenn Sie wissen möchten, worin das Problem besteht, lesen Sie den nächsten Artikel in derselben Serie.
| Datei | Beschreibung |
|---|---|
| Experts\Expert Advisor.mq5 | Zeigt die Interaktion zwischen Chart Trade und dem Expert Advisor (für die Interaktion ist „Mouse Study“ erforderlich). |
| Indicators\Chart Trade.mq5 | Erstellt ein Fenster zur Konfiguration eines Auftrags, bevor dieser versendet wird (für die Interaktion ist „Mouse Study“ erforderlich). |
| Indicators\Market Replay.mq5 | Erstellt Steuerelemente für die Interaktion mit dem Replay-/Simulationsdienst (für die Interaktion ist „Mouse Study“ erforderlich). |
| Indicators\Mouse Study.mq5 | Ermöglicht die Interaktion zwischen grafischen Bedienelementen und dem Benutzer (sowohl für das Replay-/Simulationssystem als auch für den realen Markt erforderlich). |
| Indicators\Order Indicator.mq5 | Verantwortlich für die Anzeige von Marktaufträgen, was die Interaktion mit ihnen und deren Verwaltung ermöglicht. |
| Indicators\Position View.mq5 | Verantwortlich für die Anzeige von Marktpositionen, was die Interaktion mit ihnen und deren Verwaltung ermöglicht. |
| Services\Market Replay.mq5 | Erstellt und verwaltet den Markt-Replay-/Simulationsdienst (die Hauptdatei des gesamten Systems). |
Übersetzt aus dem Portugiesischen von MetaQuotes Ltd.
Originalartikel: https://www.mql5.com/pt/articles/13137
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.
Von der Grundstufe zur Mittelstufe: Objekte (IV)
Neuronale Netze im Trading: LSTM-Optimierung zur Prognose multivariater Zeitreihen (DA-CG-LSTM)
Eine alternative Log-datei mit der Verwendung der HTML und CSS
Von der Grundstufe zur Mittelstufe: Objekte (III)
- 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.