English Русский 中文 Español 日本語 Português
preview
Marktsimulation (Teil 03): Eine Frage der Leistung

Marktsimulation (Teil 03): Eine Frage der Leistung

MetaTrader 5Beispiele |
112 0
Daniel Jose
Daniel Jose

Einführung

Im vorangegangenen Artikel „Marktsimulation (Teil 02): Kreuzaufträge (II)“ habe ich gezeigt, wie man die Kontrolle darüber behält, welchen Vermögenswert oder Kontrakt man im Chart Trade beobachtet. Dies gilt für die Verwendung eines Systems, bei dem wir nicht direkt auf den gehandelten Vertrag zugreifen. Stattdessen verwenden wir ein anderes Asset, um solche Operationen durchzuführen. Bei diesem Vermögenswert handelt es sich in der Tat um die historischen Daten des Vertrags, und die Gründe für diese Vorgehensweise wurden im vorherigen Artikel erläutert.

Aber auch wenn wir in der Entwicklung des gesamten Systems schon sehr weit fortgeschritten sind, müssen wir immer dafür sorgen, dass ein angemessenes Maß an Sicherheit, Zuverlässigkeit und Leistung gewährleistet ist. Dies zu erreichen, ist keineswegs eine einfache Aufgabe. Im Gegenteil, es ist alles andere als einfach. Früher oder später stoßen wir unweigerlich auf irgendeine Art von Fehler oder Problem. In diesen Momenten ist es von entscheidender Bedeutung, dass man genau weiß, wie das gesamte System umgesetzt wird. Wenn Sie nicht wissen oder verstehen, was die einzelnen Teile eigentlich tun, können Sie sich in einer Sackgasse wiederfinden, in der sich die Dinge sehr schnell verschlechtern.

Seit einigen Wochen entwickeln wir einige der Anwendungen, die wir tatsächlich brauchen werden. Als ich jedoch mit der Implementierung einer neuen Anwendung begann, die demnächst eingeführt werden soll, traten Probleme auf. Die Systemleistung sank im Vergleich zu den bis dahin beobachteten Werten erheblich. Da ich nicht vorhabe, das System monolithisch, d. h. als einen einzigen Block, aufzubauen, war es notwendig, die Ursachen für diese Leistungsverschlechterung zu analysieren und zu ermitteln.

Beim Studium des Flussdiagramms sind mir einige Lücken aufgefallen. Diese Lücken müssen geschlossen werden, weil sie früher oder später zu Problemen führen werden. Daher ist die Lösung dieser Probleme und die Verhinderung ihres erneuten Auftretens ebenfalls Teil der endgültigen Anwendungsentwicklung. Zusätzlich zu diesen Details, die im Laufe des Artikels erläutert werden, gibt es einen kleinen Fehler, der bei der Verwendung des Mauszeigers zu beobachten ist. Dieser Fehler war unbemerkt geblieben, aber bei näherer Betrachtung wurde er entdeckt und korrigiert, und Sie, liebe Leserin, lieber Leser, werden genau sehen können, was er bedeutet.

Diese Art von Details, die von anderen oft ausgelassen oder nicht erklärt werden, können angehende professionelle Programmierer zu der irrigen Annahme verleiten, dass Programmierer niemals Fehler machen oder dass Code von Geburt an fehlerfrei ist und sich ohne Fehler entwickelt. In Wirklichkeit ist kein Code – egal wie sorgfältig er geplant ist – frei von Fehlern. Ich hoffe, dass dieser Artikel nicht nur erklärend ist, sondern auch dazu dient, Folgendes zu zeigen: Code ist nie wirklich fertig. Es gibt immer etwas zu verbessern oder zu korrigieren.

Kommen wir nun zum eigentlichen Artikel und beginnen mit einem neuen Thema.


Verbesserung der Verkapselung

Eines der schlimmsten Probleme, die bei der Entwicklung von Software auftreten können, sind Informationsverluste. Dieses Leck kann viele Formen annehmen. In diesem Fall handelt es sich jedoch um ein sehr spezifisches Problem. Der Code ist auf die umfassende Verwendung von Klassen und damit auf objektorientierte Programmierung ausgelegt. Ein Leck entsteht, wenn eine Klasse – oder eine Anwendung, die eine bestimmte Klasse verwendet – Zugriff auf etwas erhält, das sie nicht haben sollte. Solche Fehler resultieren in der Regel aus schlechter Kapselung oder aus Funktionen oder Prozeduren, die in einer Klasse vorhanden sind, wo sie nicht hingehören. In unserem speziellen Fall gibt es eine Prozedur, auf die kein anderer Code zugreifen darf, außer dem speziellen Code, zu dem sie gehört. Da ich jedoch bisher nicht auf dieses Problem geachtet hatte, blieb der Fehler bestehen.

Der betreffende Fehler ist die Funktion SetBuffer in der Klasse C_Mouse. Aber warum wird dieses Verfahren als Fehler angesehen? Gibt es etwas, das von Natur aus falsch ist, wenn es in der Klasse C_Mouse ist? Ich würde nicht sagen, dass es falsch ist. Bei näherer Betrachtung habe ich jedoch festgestellt, dass dies nicht ganz korrekt ist. Das Problem ist nicht, dass ein anderes Programm (sei es ein Indikator oder ein Expert Advisor) in den Mausindikatorpuffer schreiben kann. Dies ist aufgrund der Sicherheitsmerkmale von MQL5 nicht möglich. Das Problem ist, dass es keinen Sinn macht, dass diese Prozedur in der Klasse C_Mouse existiert, wenn der einzige Prozess, der sie verwendet, der Mausindikator selbst ist. Indem wir diese Prozedur aus der Klasse C_Mouse entfernen, verbessern wir ihre Kapselung und stellen gleichzeitig sicher, dass nur der Mauszeiger seinen Puffer ändern kann.

Das Entfernen der Prozedur aus der Klasse setzt voraus, dass sie wie bisher weiter funktioniert. Dies garantiert die Kompatibilität mit jedem bestehenden oder zuvor entwickelten Code, der auf die Daten des Mauszeigers angewiesen ist. Bevor wir die aktualisierte Klasse C_Mouse zeigen (an der noch andere Änderungen vorgenommen wurden), wollen wir den Code des Mausindikators überprüfen. Der vollständige Code ist unten abgebildet:

01. //+------------------------------------------------------------------+
02. #property copyright "Daniel Jose"
03. #property description "This is an indicator for graphical studies using the mouse."
04. #property description "This is an integral part of the Replay / Simulator system."
05. #property description "However it can be used in the real market."
06. #property version "1.82"
07. #property icon "/Images/Market Replay/Icons/Indicators.ico"
08. #property link "https://www.mql5.com/pt/articles/12580"
09. #property indicator_chart_window
10. #property indicator_plots 0
11. #property indicator_buffers 1
12. //+------------------------------------------------------------------+
13. double GL_PriceClose;
14. datetime GL_TimeAdjust;
15. //+------------------------------------------------------------------+
16. #include <Market Replay\Auxiliar\Study\C_Study.mqh>
17. //+------------------------------------------------------------------+
18. C_Study *Study       = NULL;
19. //+------------------------------------------------------------------+
20. input color user01   = clrBlack;                   //Price Line
21. input color user02   = clrPaleGreen;               //Positive Study
22. input color user03   = clrLightCoral;              //Negative Study
23. //+------------------------------------------------------------------+
24. C_Study::eStatusMarket m_Status;
25. int m_posBuff = 0;
26. double m_Buff[];
27. //+------------------------------------------------------------------+
28. int OnInit()
29. {
30.    Study = new C_Study(0, "Indicator Mouse Study", user01, user02, user03);
31.    if (_LastError >= ERR_USER_ERROR_FIRST) return INIT_FAILED;
32.    MarketBookAdd((*Study).GetInfoTerminal().szSymbol);
33.    OnBookEvent((*Study).GetInfoTerminal().szSymbol);
34.    m_Status = C_Study::eCloseMarket;
35.    SetIndexBuffer(0, m_Buff, INDICATOR_DATA);
36.    ArrayInitialize(m_Buff, EMPTY_VALUE);
37.    
38.    return INIT_SUCCEEDED;
39. }
40. //+------------------------------------------------------------------+
41. int OnCalculate(const int rates_total, const int prev_calculated, const datetime& time[], const double& open[], 
42.                 const double& high[], const double& low[], const double& close[], const long& tick_volume[], 
43.                 const long& volume[], const int& spread[]) 
44. {
45.    GL_PriceClose = close[rates_total - 1];
46.    if (_Symbol == def_SymbolReplay)
47.       GL_TimeAdjust = spread[rates_total - 1] & (~def_MaskTimeService);
48.    m_posBuff = rates_total;
49.    (*Study).Update(m_Status);   
50.    
51.    return rates_total;
52. }
53. //+------------------------------------------------------------------+
54. void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam)
55. {
56.     uCast_Double info;
57.     C_Mouse::st_Mouse sMouse;
58.     
59.    (*Study).DispatchMessage(id, lparam, dparam, sparam);
60.    sMouse = (*Study).GetInfoMouse();            
61.    info._8b[0] = (uchar)(sMouse.ExecStudy == C_Mouse::eStudyNull ? sMouse.ButtonStatus : 0);
62.    info._16b[1] = (ushort)sMouse.Position.X_Graphics;
63.    info._16b[2] = (ushort)sMouse.Position.Y_Graphics;
64.    if (m_posBuff > 0) m_Buff[m_posBuff - 1] = info.dValue;
65. 
66.    ChartRedraw((*Study).GetInfoTerminal().ID);
67. }
68. //+------------------------------------------------------------------+
69. void OnBookEvent(const string &symbol)
70. {
71.    MqlBookInfo book[];
72.    C_Study::eStatusMarket loc = m_Status;
73.    
74.    if (symbol != (*Study).GetInfoTerminal().szSymbol) return;
75.    MarketBookGet((*Study).GetInfoTerminal().szSymbol, book);
76.    m_Status = (ArraySize(book) == 0 ? C_Study::eCloseMarket : (symbol == def_SymbolReplay ? C_Study::eInReplay : C_Study::eInTrading));
77.    for (int c0 = 0; (c0 < ArraySize(book)) && (m_Status != C_Study::eAuction); c0++)
78.       if ((book[c0].type == BOOK_TYPE_BUY_MARKET) || (book[c0].type == BOOK_TYPE_SELL_MARKET)) m_Status = C_Study::eAuction;
79.    if (loc != m_Status) (*Study).Update(m_Status);
80. }
81. //+------------------------------------------------------------------+
82. void OnDeinit(const int reason)
83. {
84.    MarketBookRelease((*Study).GetInfoTerminal().szSymbol);
85. 
86.    delete Study;
87. }
88. //+------------------------------------------------------------------+

Der Quellcode des Mauszeigers

Beachten Sie, dass es im Vergleich zum ursprünglichen Code fast keine Änderungen gibt. Sie können den ursprünglichen Code in früheren Artikeln nachlesen. Wenn Sie den Code jedoch genau verfolgt haben, werden Sie einen Unterschied in der OnChartEvent-Prozedur feststellen. Zuvor wurde derselbe Vorgang innerhalb der Klasse C_Mouse mit der Funktion SetBuffer durchgeführt. Nachdem diese Prozedur nun entfernt wurde, stellen wir sicher, dass der Puffer in OnChartEvent weiterhin korrekt geschrieben wird.

Warum wird dieser Vorgang nicht in der Funktion OnCalculate durchgeführt, was naheliegender wäre? Der Grund dafür ist, dass wir es mit mausbezogenen Ereignissen zu tun haben. OnCalculate ist für preisbezogene Berechnungen gedacht. Da der Zweck des Mausindikators darin besteht, mausbezogene Daten zu liefern, müssen wir eine geeignetere Funktion verwenden.

Somit befindet sich zwischen den Zeilen 61 und 64 fast der gleiche Code wie in SetBuffer. In Zeile 64 muss jedoch ein zusätzlicher Test durchgeführt werden, der vorher nicht durchgeführt wurde. Dieser Test befasst sich mit merkwürdigen Fehlern, die bei Experimenten im Replay/Simulator-System im Zusammenhang mit der Mausanzeige beobachtet wurden. Um diese Fehler zu vermeiden, wird dieser Test in Zeile 64 durchgeführt. Durch die Durchführung dieses Tests wurden diese Fehler beseitigt und ein weiteres Problem zur richtigen Zeit gelöst.

Neben dieser Verbesserung des Codes für den Mausindikator gibt es noch zwei weitere Änderungen, die ich hervorheben möchte. Obwohl sie noch nicht aktiv genutzt werden, sind sie bereits im Einsatz, da ich andere Anwendungen in das Replay/Simulator-System integriere. Möglicherweise werde ich diese Änderungen in künftigen Artikeln erörtern, aber selbst wenn ich das nicht tue, verhindert die Einführung dieser Änderungen, dass sie wiederholt entfernt werden müssen. Sie können sich in bestimmten Situationen als nützlich erweisen, und wenn Sie etwas Ähnliches vorhaben, werden Sie bereits Zugang zu ihnen haben.

Die erste ist die Header-Datei, die macros.mqh enthält. Die vollständige Datei ist unten abgebildet.

01. //+------------------------------------------------------------------+
02. #property copyright "Daniel Jose"
03. //+------------------------------------------------------------------+
04. #define macroRemoveSec(A)            (A - (A % 60))
05. #define macroGetDate(A)              (A - (A % 86400))
06. #define macroGetSec(A)               (A - (A - (A % 60)))
07. #define macroGetTime(A)              (A % 86400)
08. #define macroGetMin(A)               (int)((A - (A - ((A % 3600) - (A % 60)))) / 60)
09. #define macroGetHour(A)              (A - (A - ((A % 86400) - (A % 3600))))
10. #define macroHourBiggerOrEqual(A, B) ((A * 3600) < (B - (B - ((B % 86400) - (B % 3600)))))
11. #define macroMinusMinutes(A, B)      (B - ((A * 60) + (B % 60)))
12. #define macroMinusHours(A, B)        (B - (A * 3600))
13. #define macroAddHours(A, B)          (B + (A * 3600))
14. #define macroAddMin(A, B)            (B + (A * 60))
15. #define macroSetHours(A, B)          ((A * 3600) + (B - ((B % 86400))))
16. #define macroSetMin(A, B)            ((A * 60) + (B - (B % 3600)))
17. #define macroSetTime(A, B, C)        ((A * 3600) + (B * 60) + (C - (C % 86400)))
18. //+------------------------------------------------------------------+
19. #define macroColorRGBA(A, B) ((uint)((B << 24) | (A & 0x00FF00) | ((A & 0xFF0000) >> 16) | ((A & 0x0000FF) << 16)))
20. #define macroTransparency(A) (((A > 100 ? 100 : (100 - A)) * 2.55) / 255.0)
21. //+------------------------------------------------------------------+

Die Header-Datei Macros.mqh

Obwohl sich ein Großteil dieser Datei auf die Manipulation der Zeit konzentriert – sei es mit dem Datentyp datetime oder ulong zur Darstellung von Datum und Uhrzeit – sind diese Funktionen für eine Vielzahl von Aufgaben im Zusammenhang mit solchen Manipulationen äußerst nützlich. Dies liegt daran, dass die direkte Durchführung einer Berechnung für den Prozessor schneller ist als der Aufruf einer MQL5-Bibliotheksfunktion, um das gleiche Ergebnis zu erzielen. Sie werden den Nutzen dieser Berechnungen vielleicht nicht sofort bemerken, aber in jedem Fall wird die Datei macros.mqh nun diesen Inhalt enthalten.

Die andere Header-Datei, die ebenfalls Änderungen erfuhr, ist defines.mqh. Die Änderung war jedoch minimal: eine einzige Zeile wurde hinzugefügt. Dennoch wird die Datei in ihrer Gesamtheit unten angezeigt.

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,                     //Event of tic-tac
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 sales event 
42.          evChartTradeCloseAll,         //Event to close positions
43.          evChartTrade_At_EA,           //Event to communication
44.          evEA_At_ChartTrade            //Event to communication
45.                   };
46. //+------------------------------------------------------------------+

Die Header-Datei Defines.mqh

Die betreffende Zeile ist die Zeile 30. Wenn man sie am Anfang der Enumeration hinzufügt, ändert sich alles. Dieses Ereignis – der so genannte Tic-Tac – ist ein Synchronisationsereignis. Derzeit wird es im Code des Wiedergabe-/Simulatorsystems nicht verwendet. Wenn jedoch eine Synchronisierung über mehrere Komponenten hinweg erforderlich ist, wird dieses Ereignis sehr gefragt sein. Die Hinzufügung ist auf einige Anwendungen zurückzuführen, die ich, wie bereits erwähnt, für den persönlichen Gebrauch entwickle, wobei ich alle meine Projekte in das Wiedergabe-/Simulatorsystem integriere.

Das Tic-Tac-Ereignis gehört also zu den Aspekten, die Sie, verehrter Leser, als Teil des Replay/Simulator-Codes derzeit vielleicht noch nicht ganz verstehen. Keine Sorge, vielleicht werde ich in Zukunft Anwendungen zeigen, die dieses Ereignis nutzen.

Damit schließen wir diesen Abschnitt ab und gehen zum nächsten über, in dem es vor allem darum geht, eine Verschlechterung der Systemleistung zu verhindern. Dies ist ein Problem, das sich bereits bemerkbar macht. Um die Themen klar voneinander abzugrenzen, werden wir einen neuen Abschnitt einrichten.


Verringerung von Leistungseinbußen

Lange Zeit war die Leistung des Systems ausreichend. Als ich jedoch in eine neue Phase eintrat, in der ich bestimmte Funktionen intensiver nutzte, wurde eine allgemeine Leistungsverschlechterung des Wiedergabe-/Simulatorsystems festgestellt. Dies gilt auch dann, wenn die dafür entwickelten Programme auf einem Live- oder Demokonto eingesetzt werden. Die Gründe für diese Verschlechterung sind recht spezifisch, und ich werde die Ursache erläutern und eine Lösung vorschlagen, die zumindest für diese Anfangsphase geeignet ist.

Der Hauptverantwortliche für den Leistungsabfall ist der Mauszeiger. Warum? Es liegt nicht daran, dass der Mauszeiger mehr Ressourcen verbraucht als erwartet. Vielmehr wird das System langsamer, wenn der Puffer des Indikators intensiver gelesen wird.

Dies wird durch eine Prozedur innerhalb der Klasse C_Mouse verursacht. Insbesondere beim Lesen des Indikatorpuffers beginnen sich kleine Verzögerungen anzuhäufen. Diese Verzögerungen resultieren daraus, dass das System grafische Koordinaten in Preis-Zeit-Koordinaten umrechnet. Wenn diese Umrechnung zu häufig erfolgt, kommt es zu einer spürbaren Verzögerung bei der Position der mit dem Mausindikator verbundenen Kurslinie.

Die Lösung besteht darin, diesen Effekt zu isolieren. Dies kann entweder durch weniger häufiges Lesen des Puffers oder durch eine andere Technik erreicht werden. Da es sich um ein grafisches System handelt, können wir auf die Verwendung der Maus nicht verzichten. Es gibt jedoch eine Lösung, ohne zu extremen Maßnahmen greifen zu müssen.

Entscheidend ist, dass der Mausindikator zwischen Zeiten, in denen eine Studie durchgeführt wird, und Zeiten, in denen sie nicht durchgeführt wird, unterscheiden muss. Dies ist der Hauptgrund für seine Gründung. Wenn eine Studie läuft, muss jeder Klick von den Anwendungen auf dem Chart ignoriert werden.

Um festzustellen, was die Maus gerade tut – ob sie aktiv ist oder sich im Lernmodus befindet – verwenden wir einen Test. Sie wird über die Funktion CheckClick ausgeführt. Wenn die Funktion true zurückgibt, kann die Mausposition verwendet werden. Wenn sie false zurückgibt, befindet sich die Maus im Lernmodus. In diesem Fall sollte jede Position von der Anwendung, die auf die Maus zugreift, ignoriert werden, sofern ihr der Mausstatus bekannt ist.

Dazu muss der Mausindikator im Chart vorhanden sein. Um all diese Probleme zu lösen, waren mehrere Änderungen erforderlich. Um diese Diskussion nicht zu langwierig und ermüdend zu machen (da die meisten Änderungen das Verschieben von Funktionen von einer Klasse in eine andere beinhalten), werde ich mich nicht mit den kleinsten Details befassen.

Was Sie verstehen müssen, ist, dass zuvor die gesamte Arbeit innerhalb der Klasse C_Mouse durchgeführt wurde, sodass jede Anwendung, die den Mauszeiger verwendet, auf die erforderlichen Daten zugreifen konnte. Diese Arbeit wird nun zwischen C_Mouse und C_Terminal aufgeteilt. Ohne den Mausindikator im Chart funktionieren jedoch Anwendungen, die von ihm abhängig sind, nicht vollständig. Die neue Klassenhierarchie, die die bisher entwickelten Anwendungen widerspiegelt, ist in der folgenden Abbildung dargestellt.

Bild

Dieses Bild veranschaulicht die aktualisierte Klassenstruktur. Das ist jetzt also unsere Grundstruktur. Jedes Rechteck steht für eine andere Anwendung. Obwohl C_ChartFloatingRAD von C_Mouse erbt, funktioniert der Indikator Chart Trade nicht ohne den Mausindikator im Chart.

Dies mag kontraintuitiv erscheinen: Die Tatsache, dass C_ChartFloatingRAD C_Mouse erbt, könnte darauf hindeuten, dass Chart Trade unabhängig davon funktionieren würde. Aufgrund der Art und Weise, wie diese Indikatoren entwickelt wurden, hängt der Chart Trade jedoch vom Mausindikator ab. Fehlt sie, kann der Nutzer nicht auf Chart Trade zugreifen.

Dieses Verhalten war beabsichtigt und wurde als angemessen erachtet. Sowohl Tastatur- als auch Mausinteraktionen sind mit C_Terminal und C_Mouse verknüpft. Diese Klassen wurden entwickelt, um die Gesamtleistung so wenig wie möglich zu beeinträchtigen. Nichtsdestotrotz enthält der Puffer des Mausindikators bei Bedarf Daten, auf die der Chart Trade Indikator – oder jede andere Anwendung – zugreifen kann.

Die Idee ist, dass jede Komponente innerhalb des grafischen Terminals so schnell wie möglich auf C_Mouse-Daten zugreifen sollte. Wenn diese Daten von einer anderen Anwendung oder einem anderen Dienst benötigt werden, kann der Puffer extern gelesen werden, wodurch die Rechenlast verlagert und die Gesamtleistung des Systems erhalten wird.

Außerdem wurden mehrere Änderungen vorgenommen, um die Anzahl der Funktionsaufrufe zu verringern. Oft können Werte in privaten Klassenvariablen gespeichert werden, wenn sie während des Lebenszyklus der Anwendung konstant bleiben. Die Verwendung einer Variablen ist dem wiederholten Ausführen einer Funktion zum Abrufen desselben Wertes vorzuziehen.

Infolgedessen wurden viele Aufrufe zum Abrufen der Chart-ID durch Verweise auf eine gespeicherte Variable ersetzt, die im Klassenkonstruktor initialisiert wurde. Dieser Wert ändert sich nicht, solange das Chart geöffnet ist. Obwohl dadurch nur wenige Prozessorzyklen eingespart werden, können solche Optimierungen einen erheblichen Unterschied ausmachen, insbesondere wenn die Anzahl der Funktionsaufrufe hoch ist.

Einzeln betrachtet mögen diese Änderungen keinen dramatischen Leistungsanstieg bewirken, aber in der Summe werden sie bedeutsam.

Abschließend wollen wir uns ansehen, wie der neue Code aufgebaut ist. Der Zweck dieser Artikel ist nicht die Bereitstellung von Quellcode, sondern die Vermittlung von Wissen. Ich werde den derzeitigen Ansatz beibehalten: Erklärungen zu jeder einzelnen Änderung liefern. Da die Anwendungen mehrere Dateien verwenden, die, wie im Diagramm gezeigt, miteinander verknüpft sind, verhindert die direkte Veröffentlichung des Codes im Artikel, dass Leser ihn einfach kopieren und einfügen, ohne zu verstehen, wie man das System kompiliert.

Für Anfänger entschuldige ich mich für diesen Ansatz. Mein Ziel ist es nicht, das Programmieren mit CTRL+C/CTRL+V zu fördern, das oft zu fehlerhaftem Code führt. Ich möchte vielmehr, dass Sie die zugrunde liegende Logik verstehen. Wer über einige Programmierkenntnisse verfügt, kann leicht auf die Quellen zugreifen. Wir beginnen nun mit der Klasse C_Terminal. Die vollständige neue Klasse ist unten abgebildet:

001. //+------------------------------------------------------------------+
002. #property copyright "Daniel Jose"
003. //+------------------------------------------------------------------+
004. #include "Macros.mqh"
005. #include "..\Defines.mqh"
006. //+------------------------------------------------------------------+
007. class C_Terminal
008. {
009. //+------------------------------------------------------------------+      
010.    public   :
011. //+------------------------------------------------------------------+      
012.       struct st_Mouse
013.       {
014.          struct st00
015.          {
016.             short    X_Adjusted,
017.                      Y_Adjusted,
018.                      X_Graphics,
019.                      Y_Graphics;
020.             double   Price;
021.             datetime dt;
022.          }Position;
023.          uchar      ButtonStatus;
024.          bool       ExecStudy;
025.       };
026. //+------------------------------------------------------------------+
027.    protected:
028.       enum eErrUser {ERR_Unknown, ERR_FileAcess, ERR_PointerInvalid, ERR_NoMoreInstance};
029. //+------------------------------------------------------------------+
030.       struct st_Terminal
031.       {
032.          ENUM_SYMBOL_CHART_MODE   ChartMode;
033.          ENUM_ACCOUNT_MARGIN_MODE TypeAccount;
034.          long           ID;
035.          string         szSymbol;
036.          int            Width,
037.                         Height,
038.                         nDigits,
039.                         SubWin,
040.                         HeightBar;
041.          double         PointPerTick,
042.                         ValuePerPoint,
043.                         VolumeMinimal,
044.                         AdjustToTrade;
045.       };
046. //+------------------------------------------------------------------+
047.       void CurrentSymbol(bool bUsingFull)
048.          {
049.             MqlDateTime mdt1;
050.             string sz0, sz1;
051.             datetime dt = macroGetDate(TimeCurrent(mdt1));
052.             enum eTypeSymbol {WIN, IND, WDO, DOL, OTHER} eTS = OTHER;
053.       
054.             sz0 = StringSubstr(m_Infos.szSymbol = _Symbol, 0, 3);
055.             for (eTypeSymbol c0 = 0; (c0 < OTHER) && (eTS == OTHER); c0++) eTS = (EnumToString(c0) == sz0 ? c0 : eTS);
056.             switch (eTS)
057.             {
058.                case DOL   :
059.                case WDO   : sz1 = "FGHJKMNQUVXZ"; break;
060.                case IND   :
061.                case WIN   : sz1 = "GJMQVZ";       break;
062.                default    : return;
063.             }
064.             sz0 = EnumToString((eTypeSymbol)(((eTS & 1) == 1) ? (bUsingFull ? eTS : eTS - 1) : (bUsingFull ? eTS + 1: eTS)));
065.             for (int i0 = 0, i1 = mdt1.year - 2000, imax = StringLen(sz1);; i0 = ((++i0) < imax ? i0 : 0), i1 += (i0 == 0 ? 1 : 0))
066.                if (dt < macroGetDate(SymbolInfoInteger(m_Infos.szSymbol = StringFormat("%s%s%d", sz0, StringSubstr(sz1, i0, 1), i1), SYMBOL_EXPIRATION_TIME))) break;
067.          }
068. //+------------------------------------------------------------------+
069. inline void DecodeMousePosition(int xi, int yi)
070.          {
071.             int w = 0;
072.             
073.             xi = (xi > 0 ? xi : 0);
074.             yi = (yi > 0 ? yi : 0);
075.             ChartXYToTimePrice(m_Infos.ID, m_Mouse.Position.X_Graphics = (short)xi, m_Mouse.Position.Y_Graphics = (short)yi, w, m_Mouse.Position.dt, m_Mouse.Position.Price);
076.             m_Mouse.Position.dt = AdjustTime(m_Mouse.Position.dt);
077.             m_Mouse.Position.Price = AdjustPrice(m_Mouse.Position.Price);
078.             ChartTimePriceToXY(m_Infos.ID, w, m_Mouse.Position.dt, m_Mouse.Position.Price, xi, yi);
079.             yi -= (int)ChartGetInteger(m_Infos.ID, CHART_WINDOW_YDISTANCE, m_Infos.SubWin);
080.             m_Mouse.Position.X_Adjusted = (short) xi;
081.             m_Mouse.Position.Y_Adjusted = (short) yi;
082.          }
083. //+------------------------------------------------------------------+
084.    private   :
085.       st_Terminal m_Infos;
086.       st_Mouse    m_Mouse;
087.       struct mem
088.       {
089.          long    Show_Descr,
090.                  Show_Date;
091.          bool    AccountLock;
092.       }m_Mem;
093. //+------------------------------------------------------------------+
094. inline void ChartChange(void)
095.          {
096.             int x, y, t;
097.             
098.             m_Infos.Width  = (int)ChartGetInteger(m_Infos.ID, CHART_WIDTH_IN_PIXELS);
099.             m_Infos.Height = (int)ChartGetInteger(m_Infos.ID, CHART_HEIGHT_IN_PIXELS);
100.             ChartTimePriceToXY(m_Infos.ID, 0, 0, 0, x, t);
101.             ChartTimePriceToXY(m_Infos.ID, 0, 0, m_Infos.PointPerTick * 100, x, y);
102.             m_Infos.HeightBar = (int)(t - y) / 100;
103.          }
104. //+------------------------------------------------------------------+
105.    public   :
106. //+------------------------------------------------------------------+      
107.       C_Terminal(const long id = 0, const uchar sub = 0)
108.          {
109.             m_Infos.ID = (id == 0 ? ChartID() : id);
110.             m_Mem.AccountLock = false;
111.             m_Infos.SubWin = (int) sub;
112.             CurrentSymbol(false);
113.             ZeroMemory(m_Mouse);
114.             m_Mem.Show_Descr = ChartGetInteger(m_Infos.ID, CHART_SHOW_OBJECT_DESCR);
115.             m_Mem.Show_Date  = ChartGetInteger(m_Infos.ID, CHART_SHOW_DATE_SCALE);
116.             ChartSetInteger(m_Infos.ID, CHART_SHOW_OBJECT_DESCR, false);
117.             ChartSetInteger(m_Infos.ID, CHART_EVENT_OBJECT_DELETE, true);
118.             ChartSetInteger(m_Infos.ID, CHART_EVENT_OBJECT_CREATE, true);
119.             ChartSetInteger(m_Infos.ID, CHART_SHOW_DATE_SCALE, false);
120.             m_Infos.nDigits = (int) SymbolInfoInteger(m_Infos.szSymbol, SYMBOL_DIGITS);
121.             m_Infos.Width   = (int)ChartGetInteger(m_Infos.ID, CHART_WIDTH_IN_PIXELS);
122.             m_Infos.Height  = (int)ChartGetInteger(m_Infos.ID, CHART_HEIGHT_IN_PIXELS);
123.             m_Infos.PointPerTick  = SymbolInfoDouble(m_Infos.szSymbol, SYMBOL_TRADE_TICK_SIZE);
124.             m_Infos.ValuePerPoint = SymbolInfoDouble(m_Infos.szSymbol, SYMBOL_TRADE_TICK_VALUE);
125.             m_Infos.VolumeMinimal = SymbolInfoDouble(m_Infos.szSymbol, SYMBOL_VOLUME_STEP);
126.             m_Infos.AdjustToTrade = m_Infos.ValuePerPoint / m_Infos.PointPerTick;
127.             m_Infos.ChartMode   = (ENUM_SYMBOL_CHART_MODE) SymbolInfoInteger(m_Infos.szSymbol, SYMBOL_CHART_MODE);
128.             if(m_Infos.szSymbol != def_SymbolReplay) SetTypeAccount((ENUM_ACCOUNT_MARGIN_MODE)AccountInfoInteger(ACCOUNT_MARGIN_MODE));
129.             ChartChange();
130.          }
131. //+------------------------------------------------------------------+
132.       ~C_Terminal()
133.          {
134.             ChartSetInteger(m_Infos.ID, CHART_SHOW_DATE_SCALE, m_Mem.Show_Date);
135.             ChartSetInteger(m_Infos.ID, CHART_SHOW_OBJECT_DESCR, m_Mem.Show_Descr);
136.             ChartSetInteger(m_Infos.ID, CHART_EVENT_OBJECT_DELETE, false);
137.             ChartSetInteger(m_Infos.ID, CHART_EVENT_OBJECT_CREATE, false);
138.          }
139. //+------------------------------------------------------------------+
140. inline void SetTypeAccount(const ENUM_ACCOUNT_MARGIN_MODE arg)
141.          {
142.             if (m_Mem.AccountLock) return; else m_Mem.AccountLock = true;
143.             m_Infos.TypeAccount = (arg == ACCOUNT_MARGIN_MODE_RETAIL_HEDGING ? arg : ACCOUNT_MARGIN_MODE_RETAIL_NETTING);
144.          }
145. //+------------------------------------------------------------------+
146. inline const st_Terminal GetInfoTerminal(void) const
147.          {
148.             return m_Infos;
149.          }
150. //+------------------------------------------------------------------+
151. inline const st_Mouse GetPositionsMouse(void) const
152.          {
153.             return m_Mouse;
154.          }
155. //+------------------------------------------------------------------+
156. const double AdjustPrice(const double arg) const
157.          {
158.             return NormalizeDouble(round(arg / m_Infos.PointPerTick) * m_Infos.PointPerTick, m_Infos.nDigits);
159.          }
160. //+------------------------------------------------------------------+
161. inline datetime AdjustTime(const datetime arg)
162.          {
163.             int nSeconds= PeriodSeconds();
164.             datetime   dt = iTime(m_Infos.szSymbol, PERIOD_CURRENT, 0);
165.             
166.             return (dt < arg ? ((datetime)(arg / nSeconds) * nSeconds) : iTime(m_Infos.szSymbol, PERIOD_CURRENT, Bars(m_Infos.szSymbol, PERIOD_CURRENT, arg, dt)));
167.          }
168. //+------------------------------------------------------------------+
169. inline double FinanceToPoints(const double Finance, const uint Leverage)
170.          {
171.             double volume = m_Infos.VolumeMinimal + (m_Infos.VolumeMinimal * (Leverage - 1));
172.             
173.             return AdjustPrice(MathAbs(((Finance / volume) / m_Infos.AdjustToTrade)));
174.          };
175. //+------------------------------------------------------------------+
176.       void DispatchMessage(const int id, const long &lparam, const double &dparam, const string &sparam)
177.          {
178.             static string st_str = "";
179.             
180.             switch (id)
181.             {
182.                case CHARTEVENT_CHART_CHANGE:
183.                   m_Infos.Width  = (int)ChartGetInteger(m_Infos.ID, CHART_WIDTH_IN_PIXELS);
184.                   m_Infos.Height = (int)ChartGetInteger(m_Infos.ID, CHART_HEIGHT_IN_PIXELS);
185.                   ChartChange();
186.                   break;
187.                case CHARTEVENT_MOUSE_MOVE:
188.                   DecodeMousePosition((int)lparam, (int)dparam);
189.                   break;
190.                case CHARTEVENT_OBJECT_CLICK:
191.                   if (st_str != sparam) ObjectSetInteger(m_Infos.ID, st_str, OBJPROP_SELECTED, false);
192.                   if (ObjectGetInteger(m_Infos.ID, sparam, OBJPROP_SELECTABLE) == true)
193.                      ObjectSetInteger(m_Infos.ID, st_str = sparam, OBJPROP_SELECTED, true);
194.                   break;
195.                case CHARTEVENT_OBJECT_CREATE:
196.                   if (st_str != sparam) ObjectSetInteger(m_Infos.ID, st_str, OBJPROP_SELECTED, false);
197.                   st_str = sparam;
198.                   break;
199.             }
200.          }
201. //+------------------------------------------------------------------+
202. inline void CreateObjectGraphics(const string szName, const ENUM_OBJECT obj, const color cor = clrNONE, const int zOrder = -1) 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.             ObjectSetString(m_Infos.ID, szName, OBJPROP_TOOLTIP, "\n");
207.             ObjectSetInteger(m_Infos.ID, szName, OBJPROP_BACK, false);
208.             ObjectSetInteger(m_Infos.ID, szName, OBJPROP_COLOR, cor);
209.             ObjectSetInteger(m_Infos.ID, szName, OBJPROP_SELECTABLE, false);
210.             ObjectSetInteger(m_Infos.ID, szName, OBJPROP_SELECTED, false);
211.             ObjectSetInteger(m_Infos.ID, szName, OBJPROP_ZORDER, zOrder);
212.             ChartSetInteger(m_Infos.ID, CHART_EVENT_OBJECT_CREATE, 0, true);
213.          }
214. //+------------------------------------------------------------------+
215.       bool IndicatorCheckPass(const string szShortName)
216.          {
217.             string szTmp = szShortName + "_TMP";
218.             
219.             IndicatorSetString(INDICATOR_SHORTNAME, szTmp);
220.             m_Infos.SubWin = ((m_Infos.SubWin = ChartWindowFind(m_Infos.ID, szTmp)) < 0 ? 0 : m_Infos.SubWin);
221.             if (ChartIndicatorGet(m_Infos.ID, m_Infos.SubWin, szShortName) != INVALID_HANDLE)
222.             {
223.                ChartIndicatorDelete(m_Infos.ID, 0, szTmp);
224.                Print("Only one instance is allowed...");
225.                SetUserError(C_Terminal::ERR_NoMoreInstance);
226.                
227.                return false;
228.             }
229.             IndicatorSetString(INDICATOR_SHORTNAME, szShortName);
230.    
231.             return true;
232.          }
233. //+------------------------------------------------------------------+
234. };

Header-Datei C_Terminal.mqh

Wer diesen Code zum ersten Mal sieht, mag ihn für umfangreich und komplex halten. Vieles davon ist jedoch erhalten geblieben. Dennoch wurden Änderungen vorgenommen, da sie nun die Klasse C_Mouse bei der Ausführung ihrer Aufgaben unterstützt. Aus diesem Grund sehen wir jetzt das Ereignis CHARTEVENT_MOUSE_MOVE in der Prozedur DispatchMessage. Der größte Teil der Arbeit wird hier erledigt werden.

Beachten Sie, dass wir in Zeile 12 nun eine Struktur zur Bereitstellung von Mausdaten haben. Die Prozedur DecodeMousePosition, die sich in Zeile 69 befindet, gibt jedoch keine Daten zu den Maustasten ein. Diese Verantwortung liegt nun bei der Klasse C_Mouse. Folglich ist der nächste Punkt, den es zu überprüfen gilt, die Klasse C_Mouse selbst, die im Folgenden dargestellt wird.

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

Die Header-Datei C_Mouse.mqh

Obwohl diese Klasse auf den ersten Blick unverändert erscheint, hat sie mehrere Änderungen erfahren. Diese Änderungen zielen darauf ab, die Verwendung des Mauszeigers deutlicher zu machen. Sie können sehen, dass es einige Unterschiede gibt. Ich überlasse es den Enthusiasten und angehenden professionellen Programmierern, diesen Code mit der vorherigen Version der C_Mouse-Klasse zu vergleichen. Glauben Sie mir, es wird sehr lehrreich und unterhaltsam sein und viele interessante Aspekte der Klassenvererbung aufzeigen. Hier nur ein kleiner Hinweis, um Ihre Neugier zu wecken. Schauen Sie sich Zeile 187 an und fragen Sie sich: Warum prüfe ich bei der Erstellung einer Studie, ob bereits eine Studie läuft?

Es überrascht nicht, dass auch der Code der Klasse C_Study geändert wurde. Diese Änderungen zielen ausschließlich darauf ab, zu verhindern, dass ein konstanter Wert – in diesem Fall die Chart-ID – wiederholt durch einen Funktionsaufruf abgerufen wird. Der aktualisierte Code der Klasse C_Study ist auch unten vollständig zu sehen.

001. //+------------------------------------------------------------------+
002. #property copyright "Daniel Jose"
003. //+------------------------------------------------------------------+
004. #include "..\C_Mouse.mqh"
005. //+------------------------------------------------------------------+
006. #define def_ExpansionPrefix def_MousePrefixName + "Expansion_"
007. //+------------------------------------------------------------------+
008. class C_Study : public C_Mouse
009. {
010.    private   :
011. //+------------------------------------------------------------------+
012.       struct st00
013.       {
014.          eStatusMarket  Status;
015.          MqlRates       Rate;
016.          string         szInfo,
017.                         szBtn1,
018.                         szBtn2,
019.                         szBtn3;
020.          color          corP,
021.                         corN;
022.          int            HeightText;
023.          bool           bvT, bvD, bvP;
024.          long           id;
025.       }m_Info;
026. //+------------------------------------------------------------------+
027.       void Draw(void)
028.          {
029.             double v1;
030.             
031.             if (m_Info.bvT)
032.             {
033.                ObjectSetInteger(m_Info.id, m_Info.szBtn1, OBJPROP_YDISTANCE, GetPositionsMouse().Position.Y_Adjusted - 18);
034.                ObjectSetString(m_Info.id, m_Info.szBtn1, OBJPROP_TEXT, m_Info.szInfo);
035.             }
036.             if (m_Info.bvD)
037.             {
038.                v1 = NormalizeDouble((((GetPositionsMouse().Position.Price - m_Info.Rate.close) / m_Info.Rate.close) * 100.0), 2);
039.                ObjectSetInteger(m_Info.id, m_Info.szBtn2, OBJPROP_YDISTANCE, GetPositionsMouse().Position.Y_Adjusted - 1);
040.                ObjectSetInteger(m_Info.id, m_Info.szBtn2, OBJPROP_BGCOLOR, (v1 < 0 ? m_Info.corN : m_Info.corP));
041.                ObjectSetString(m_Info.id, m_Info.szBtn2, OBJPROP_TEXT, StringFormat("%.2f%%", MathAbs(v1)));
042.             }
043.             if (m_Info.bvP)
044.             {
045.                v1 = NormalizeDouble((((GL_PriceClose - m_Info.Rate.close) / m_Info.Rate.close) * 100.0), 2);
046.                ObjectSetInteger(m_Info.id, m_Info.szBtn3, OBJPROP_YDISTANCE, GetPositionsMouse().Position.Y_Adjusted - 1);
047.                ObjectSetInteger(m_Info.id, m_Info.szBtn3, OBJPROP_BGCOLOR, (v1 < 0 ? m_Info.corN : m_Info.corP));
048.                ObjectSetString(m_Info.id, m_Info.szBtn3, OBJPROP_TEXT, StringFormat("%.2f%%", MathAbs(v1)));
049.             }
050.          }
051. //+------------------------------------------------------------------+
052. inline void CreateObjInfo(EnumEvents arg)
053.          {
054.             switch (arg)
055.             {
056.                case evShowBarTime:
057.                   C_Mouse::CreateObjToStudy(2, 110, m_Info.szBtn1 = (def_ExpansionPrefix + (string)ObjectsTotal(0)), clrPaleTurquoise);
058.                   m_Info.bvT = true;
059.                   break;
060.                case evShowDailyVar:
061.                   C_Mouse::CreateObjToStudy(2, 53, m_Info.szBtn2 = (def_ExpansionPrefix + (string)ObjectsTotal(0)));
062.                   m_Info.bvD = true;
063.                   break;
064.                case evShowPriceVar:
065.                   C_Mouse::CreateObjToStudy(58, 53, m_Info.szBtn3 = (def_ExpansionPrefix + (string)ObjectsTotal(0)));
066.                   m_Info.bvP = true;
067.                   break;
068.             }
069.          }
070. //+------------------------------------------------------------------+
071. inline void RemoveObjInfo(EnumEvents arg)
072.          {
073.             string sz;
074.             
075.             switch (arg)
076.             {
077.                case evHideBarTime:
078.                   sz = m_Info.szBtn1;
079.                   m_Info.bvT = false;
080.                   break;
081.                case evHideDailyVar:
082.                   sz = m_Info.szBtn2;
083.                   m_Info.bvD   = false;
084.                   break;
085.                case evHidePriceVar:
086.                   sz = m_Info.szBtn3;
087.                   m_Info.bvP = false;
088.                   break;
089.             }
090.             ChartSetInteger(m_Info.id, CHART_EVENT_OBJECT_DELETE, false);
091.             ObjectDelete(m_Info.id, sz);
092.             ChartSetInteger(m_Info.id, CHART_EVENT_OBJECT_DELETE, true);
093.          }
094. //+------------------------------------------------------------------+
095.    public   :
096. //+------------------------------------------------------------------+
097.       C_Study(long IdParam, string szShortName, color corH, color corP, color corN)
098.          :C_Mouse(IdParam, szShortName, corH, corP, corN)
099.          {
100.             ZeroMemory(m_Info);
101.             m_Info.id = GetInfoTerminal().ID;
102.             if (_LastError >= ERR_USER_ERROR_FIRST) return;
103.             m_Info.corP = corP;
104.             m_Info.corN = corN;
105.             CreateObjInfo(evShowBarTime);
106.             CreateObjInfo(evShowDailyVar);
107.             CreateObjInfo(evShowPriceVar);
108.             ResetLastError();
109.          }
110. //+------------------------------------------------------------------+
111.       void Update(const eStatusMarket arg)
112.          {
113.             int i0;
114.             datetime dt;
115.                
116.             if (m_Info.Rate.close == 0)
117.                m_Info.Rate.close = iClose(NULL, PERIOD_D1, ((_Symbol == def_SymbolReplay) || (macroGetDate(TimeCurrent()) != macroGetDate(iTime(NULL, PERIOD_D1, 0))) ? 0 : 1));
118.             switch (m_Info.Status = (m_Info.Status != arg ? arg : m_Info.Status))
119.             {
120.                case eCloseMarket   :
121.                   m_Info.szInfo = "Closed Market";
122.                   break;
123.                case eInReplay      :
124.                case eInTrading     :
125.                   i0 = PeriodSeconds();
126.                   dt = (m_Info.Status == eInReplay ? (datetime) GL_TimeAdjust : TimeCurrent());
127.                   m_Info.Rate.time = (m_Info.Rate.time <= dt ? (datetime)(((ulong) dt / i0) * i0) + i0 : m_Info.Rate.time);
128.                   if (dt > 0) m_Info.szInfo = TimeToString((datetime)m_Info.Rate.time - dt, TIME_SECONDS);
129.                   break;
130.                case eAuction      :
131.                   m_Info.szInfo = "Auction";
132.                   break;
133.                default            :
134.                   m_Info.szInfo = "ERROR";
135.             }
136.             Draw();
137.          }
138. //+------------------------------------------------------------------+
139. virtual void DispatchMessage(const int id, const long &lparam, const double &dparam, const string &sparam)
140.          {
141.             C_Mouse::DispatchMessage(id, lparam, dparam, sparam);
142.             switch (id)
143.             {
144.                case CHARTEVENT_CUSTOM + evHideBarTime:
145.                   RemoveObjInfo(evHideBarTime);
146.                   break;
147.                case CHARTEVENT_CUSTOM + evShowBarTime:
148.                   CreateObjInfo(evShowBarTime);
149.                   break;
150.                case CHARTEVENT_CUSTOM + evHideDailyVar:
151.                   RemoveObjInfo(evHideDailyVar);
152.                   break;
153.                case CHARTEVENT_CUSTOM + evShowDailyVar:
154.                   CreateObjInfo(evShowDailyVar);
155.                   break;
156.                case CHARTEVENT_CUSTOM + evHidePriceVar:
157.                   RemoveObjInfo(evHidePriceVar);
158.                   break;
159.                case CHARTEVENT_CUSTOM + evShowPriceVar:
160.                   CreateObjInfo(evShowPriceVar);
161.                   break;
162.                case CHARTEVENT_MOUSE_MOVE:
163.                   Draw();
164.                   break;
165.             }
166.             ChartRedraw(m_Info.id);
167.          }
168. //+------------------------------------------------------------------+
169. };
170. //+------------------------------------------------------------------+
171. #undef def_ExpansionPrefix
172. #undef def_MousePrefixName
173. //+------------------------------------------------------------------+

Die Header-Datei C_Study.mqh

Wenn Sie den gesamten bis zu diesem Punkt überprüften Quellcode verwenden, können Sie den Mauszeiger erstellen. Es fehlen jedoch noch zwei weitere Dateien, um diesen Artikel zu vervollständigen. Ich ziehe es vor, sie nicht auf einen späteren Artikel zu verschieben, da der nächste Artikel ein anderes Thema behandeln wird. Auf diese Weise ist alles vorbereitet, und wir vermeiden es, dieses Thema in nächster Zeit erneut aufzugreifen. Betrachten wir nun die letzten beiden Quelldateien, beginnend mit der Klasse C_ChartFloatingRAD, die im Folgenden vollständig dargestellt ist:

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", m_Init.id, A)
008. #define macro_CloseIndicator(A)   {           \
009.                OnDeinit(REASON_INITFAILED);   \
010.                SetUserError(A);               \
011.                return;                        \
012.                                  }
013. //+------------------------------------------------------------------+
014. class C_ChartFloatingRAD : private C_Mouse
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.          long      id;
044.       }m_Init;
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(m_Init.id, m_Info.szObj_Chart, OBJ_CHART, 0, 0, 0);
051.             ObjectSetInteger(m_Init.id, m_Info.szObj_Chart, OBJPROP_XDISTANCE, m_Info.x);
052.             ObjectSetInteger(m_Init.id, m_Info.szObj_Chart, OBJPROP_YDISTANCE, m_Info.y);
053.             ObjectSetInteger(m_Init.id, m_Info.szObj_Chart, OBJPROP_XSIZE, w);
054.             ObjectSetInteger(m_Init.id, m_Info.szObj_Chart, OBJPROP_YSIZE, h);
055.             ObjectSetInteger(m_Init.id, m_Info.szObj_Chart, OBJPROP_DATE_SCALE, false);
056.             ObjectSetInteger(m_Init.id, m_Info.szObj_Chart, OBJPROP_PRICE_SCALE, false);
057.             m_Info.WinHandle = ObjectGetInteger(m_Init.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(m_Init.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(m_Init.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(m_Init.id, m_Info.szObj_Chart, OBJPROP_XDISTANCE, (m_Info.IsMaximized ? m_Info.x : m_Info.minx));
130.             ObjectSetInteger(m_Init.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(short &x, short &y)
141.          {
142.             int xi, yi, xf, yf;
143.             st_Mouse loc;
144.             
145.             loc = GetPositionsMouse();
146.             x = loc.Position.X_Graphics;
147.             y = loc.Position.Y_Graphics;
148.             for (eObjectsIDE c0 = MSG_LEVERAGE_VALUE; c0 <= MSG_CLOSE_POSITION; c0++)
149.             {
150.                xi = (m_Info.IsMaximized ? m_Info.x : m_Info.minx) + m_Info.Regions[c0].x;
151.                yi = (m_Info.IsMaximized ? m_Info.y : m_Info.miny) + m_Info.Regions[c0].y;
152.                xf = xi + m_Info.Regions[c0].w;
153.                yf = yi + m_Info.Regions[c0].h;
154.                if ((x > xi) && (y > yi) && (x < xf) && (y < yf)) return c0;
155.             }
156.             return MSG_NULL;
157.          }
158. //+------------------------------------------------------------------+
159. inline void DeleteObjectEdit(void)
160.          {
161.             ChartRedraw();
162.             ObjectsDeleteAll(m_Init.id, m_Info.szObj_Editable);
163.          }
164. //+------------------------------------------------------------------+
165.       template <typename T >
166.       void CreateObjectEditable(eObjectsIDE arg, T value)
167.          {
168.             DeleteObjectEdit();
169.             CreateObjectGraphics(m_Info.szObj_Editable, OBJ_EDIT, clrBlack, 0);
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);
172.             ObjectSetInteger(m_Init.id, m_Info.szObj_Editable, OBJPROP_XSIZE, m_Info.Regions[arg].w);
173.             ObjectSetInteger(m_Init.id, m_Info.szObj_Editable, OBJPROP_YSIZE, m_Info.Regions[arg].h);
174.             ObjectSetInteger(m_Init.id, m_Info.szObj_Editable, OBJPROP_BGCOLOR, m_Info.Regions[arg].bgcolor);
175.             ObjectSetInteger(m_Init.id, m_Info.szObj_Editable, OBJPROP_ALIGN, ALIGN_CENTER);
176.             ObjectSetInteger(m_Init.id, m_Info.szObj_Editable, OBJPROP_FONTSIZE, m_Info.Regions[arg].FontSize - 1);
177.             ObjectSetString(m_Init.id, m_Info.szObj_Editable, OBJPROP_FONT, m_Info.Regions[arg].FontName);
178.             ObjectSetString(m_Init.id, m_Info.szObj_Editable, OBJPROP_TEXT, (typename(T) == "double" ? DoubleToString(value, 2) : (string) value));
179.             ChartRedraw();
180.          }
181. //+------------------------------------------------------------------+
182.       bool RestoreState(void)
183.          {
184.             uCast_Double info;
185.             bool bRet;
186.             C_AdjustTemplate *Template;
187.             
188.             if (bRet = GlobalVariableGet(macro_NameGlobalVariable("POST"), info.dValue))
189.             {
190.                m_Info.x = (short) info._16b[0];
191.                m_Info.y = (short) info._16b[1];
192.                m_Info.minx = (short) info._16b[2];
193.                m_Info.miny = (short) info._16b[3];
194.                Template = new C_AdjustTemplate(m_Info.szFileNameTemplate = IntegerToString(m_Init.id) + ".tpl");
195.                if (_LastError >= ERR_USER_ERROR_FIRST) bRet = false; else
196.                {
197.                   (*Template).Add("MSG_LEVERAGE_VALUE", "descr", NULL);
198.                   (*Template).Add("MSG_TAKE_VALUE", "descr", NULL);
199.                   (*Template).Add("MSG_STOP_VALUE", "descr", NULL);
200.                   (*Template).Add("MSG_DAY_TRADE", "state", NULL);
201.                   (*Template).Add("MSG_MAX_MIN", "state", NULL);
202.                   if (!(*Template).Execute()) bRet = false; else
203.                   {
204.                      m_Info.IsDayTrade = (bool) StringToInteger((*Template).Get("MSG_DAY_TRADE", "state")) == 1;
205.                      m_Info.IsMaximized = (bool) StringToInteger((*Template).Get("MSG_MAX_MIN", "state")) == 1;
206.                      m_Info.Leverage = (short)StringToInteger((*Template).Get("MSG_LEVERAGE_VALUE", "descr"));
207.                      m_Info.FinanceTake = (double) StringToDouble((*Template).Get("MSG_TAKE_VALUE", "descr"));
208.                      m_Info.FinanceStop = (double) StringToDouble((*Template).Get("MSG_STOP_VALUE", "descr"));
209.                   }
210.                };               
211.                delete Template;
212.             };
213.             
214.             GlobalVariablesDeleteAll(macro_NameGlobalVariable(""));
215.             
216.             return bRet;
217.          }
218. //+------------------------------------------------------------------+
219.    public   :
220. //+------------------------------------------------------------------+
221.       C_ChartFloatingRAD(string szShortName, const short Leverage, const double FinanceTake, const double FinanceStop)
222.          :C_Mouse(0, "")
223.          {
224.             m_Init.id = GetInfoTerminal().ID;
225.             m_Info.IsSaveState = false;
226.             if (!IndicatorCheckPass(szShortName)) return;
227.             if (!RestoreState())
228.             {
229.                m_Info.Leverage = Leverage;
230.                m_Info.IsDayTrade = true;
231.                m_Info.FinanceTake = FinanceTake;
232.                m_Info.FinanceStop = FinanceStop;
233.                m_Info.IsMaximized = true;
234.                m_Info.minx = m_Info.x = 115;
235.                m_Info.miny = m_Info.y = 64;
236.             }
237.             m_Init.y[false] = 150;
238.             m_Init.y[true] = 210;
239.             CreateWindowRAD(170, m_Init.y[m_Init.bOk = false]);
240.             AdjustTemplate(true);
241.          }
242. //+------------------------------------------------------------------+
243.       ~C_ChartFloatingRAD()
244.          {
245.             ChartRedraw();
246.             ObjectsDeleteAll(m_Init.id, m_Info.szObj_Chart);
247.             if (!m_Info.IsSaveState)
248.                FileDelete(m_Info.szFileNameTemplate);
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.             C_Mouse::DispatchMessage(id, lparam, dparam, sparam);
288.             switch (id)
289.             {
290.                case CHARTEVENT_CUSTOM + evEA_At_ChartTrade:
291.                   if (m_Init.bOk = ((lparam >= 0) && (lparam < 2)))
292.                      CurrentSymbol((bool)lparam);
293.                   AdjustTemplate(true);
294.                   break;
295.                case CHARTEVENT_CHART_CHANGE:
296.                   if (!m_Init.bOk)
297.                      EventChartCustom(m_Init.id, evChartTrade_At_EA, 0, 0, "");
298.                   x = (short)ChartGetInteger(m_Init.id, CHART_WIDTH_IN_PIXELS);
299.                   y = (short)ChartGetInteger(m_Init.id, CHART_HEIGHT_IN_PIXELS);
300.                   macro_AdjustMinX(m_Info.x, b1);
301.                   macro_AdjustMinY(m_Info.y, b2);
302.                   macro_AdjustMinX(m_Info.minx, b3);
303.                   macro_AdjustMinY(m_Info.miny, b4);
304.                   if (b1 || b2 || b3 || b4) AdjustTemplate();
305.                   break;
306.                case CHARTEVENT_MOUSE_MOVE:
307.                   if (CheckClick(C_Mouse::eClickLeft))
308.                   {                  
309.                      switch (CheckMousePosition(x, y))
310.                      {
311.                         case MSG_MAX_MIN:
312.                            if (sz < 0) m_Info.IsMaximized = (m_Info.IsMaximized ? false : true);
313.                            break;
314.                         case MSG_DAY_TRADE:
315.                            if ((m_Info.IsMaximized) && (sz < 0)) m_Info.IsDayTrade = (m_Info.IsDayTrade ? false : true);
316.                            break;
317.                         case MSG_LEVERAGE_VALUE:
318.                            if ((m_Info.IsMaximized) && (sz < 0)) CreateObjectEditable(obj = MSG_LEVERAGE_VALUE, m_Info.Leverage);
319.                            break;
320.                         case MSG_TAKE_VALUE:
321.                            if ((m_Info.IsMaximized) && (sz < 0)) CreateObjectEditable(obj = MSG_TAKE_VALUE, m_Info.FinanceTake);
322.                            break;
323.                         case MSG_STOP_VALUE:
324.                            if ((m_Info.IsMaximized) && (sz < 0)) CreateObjectEditable(obj = MSG_STOP_VALUE, m_Info.FinanceStop);
325.                            break;
326.                         case MSG_TITLE_IDE:
327.                            if (sx < 0)
328.                            {
329.                               ChartSetInteger(m_Init.id, CHART_MOUSE_SCROLL, false);
330.                               sx = x - (m_Info.IsMaximized ? m_Info.x : m_Info.minx);
331.                               sy = y - (m_Info.IsMaximized ? m_Info.y : m_Info.miny);
332.                            }
333.                            if ((mx = x - sx) > 0) ObjectSetInteger(m_Init.id, m_Info.szObj_Chart, OBJPROP_XDISTANCE, mx);
334.                            if ((my = y - sy) > 0) ObjectSetInteger(m_Init.id, m_Info.szObj_Chart, OBJPROP_YDISTANCE, my);
335.                            if (m_Info.IsMaximized)
336.                            {
337.                               m_Info.x = (mx > 0 ? mx : m_Info.x);
338.                               m_Info.y = (my > 0 ? my : m_Info.y);
339.                            }else
340.                            {
341.                               m_Info.minx = (mx > 0 ? mx : m_Info.minx);
342.                               m_Info.miny = (my > 0 ? my : m_Info.miny);
343.                            }
344.                            break;
345.                         case MSG_BUY_MARKET:
346.                            ev = evChartTradeBuy;
347.                         case MSG_SELL_MARKET:
348.                            ev = (ev != evChartTradeBuy ? evChartTradeSell : ev);
349.                         case MSG_CLOSE_POSITION:
350.                            if ((m_Info.IsMaximized) && (sz < 0) && (m_Init.bOk))
351.                            {
352.                               string szTmp = StringFormat("%d?%s?%s?%c?%d?%.2f?%.2f", ev, _Symbol, GetInfoTerminal().szSymbol, (m_Info.IsDayTrade ? 'D' : 'S'),
353.                                                           m_Info.Leverage, FinanceToPoints(m_Info.FinanceTake, m_Info.Leverage), FinanceToPoints(m_Info.FinanceStop, m_Info.Leverage));                           
354.                               PrintFormat("Send %s - Args ( %s )", EnumToString((EnumEvents) ev), szTmp);
355.                               EventChartCustom(m_Init.id, ev, 0, 0, szTmp);
356.                            }
357.                            break;
358.                      }
359.                      if (sz < 0)
360.                      {
361.                         sz = x;
362.                         AdjustTemplate();
363.                         if (obj == MSG_NULL) DeleteObjectEdit();
364.                      }
365.                   }else
366.                   {
367.                      sz = -1;
368.                      if (sx > 0)
369.                      {
370.                         ChartSetInteger(m_Init.id, CHART_MOUSE_SCROLL, true);                  
371.                         sx = sy = -1;
372.                      }
373.                   }
374.                   break;
375.                case CHARTEVENT_OBJECT_ENDEDIT:
376.                   switch (obj)
377.                   {
378.                      case MSG_LEVERAGE_VALUE:
379.                      case MSG_TAKE_VALUE:
380.                      case MSG_STOP_VALUE:
381.                         dvalue = StringToDouble(ObjectGetString(m_Init.id, m_Info.szObj_Editable, OBJPROP_TEXT));
382.                         if (obj == MSG_TAKE_VALUE)
383.                            m_Info.FinanceTake = (dvalue <= 0 ? m_Info.FinanceTake : dvalue);
384.                         else if (obj == MSG_STOP_VALUE)
385.                            m_Info.FinanceStop = (dvalue <= 0 ? m_Info.FinanceStop : dvalue);
386.                         else
387.                            m_Info.Leverage = (dvalue <= 0 ? m_Info.Leverage : (short)MathFloor(dvalue));
388.                         AdjustTemplate();
389.                         obj = MSG_NULL;
390.                         ObjectDelete(m_Init.id, m_Info.szObj_Editable);
391.                         break;
392.                   }
393.                   break;
394.                case CHARTEVENT_OBJECT_DELETE:
395.                   if (sparam == m_Info.szObj_Chart) macro_CloseIndicator(C_Terminal::ERR_Unknown);
396.                   break;
397.             }
398.             ChartRedraw();
399.          }
400. //+------------------------------------------------------------------+
401. };
402. //+------------------------------------------------------------------+
403. #undef macro_NameGlobalVariable
404. #undef macro_CloseIndicator
405. //+------------------------------------------------------------------+

Die Header-Datei C_ChartFloatingRAD.mqh

Obwohl dieser Code umfangreich erscheinen mag, wird jeder, der diese Wiederholungs-/Simulationsreihe verfolgt, feststellen, dass das meiste davon unverändert bleibt. Die Änderungen sind für diejenigen, die den Code studieren, minimal. Sie werden jedoch feststellen, dass viele Aufrufe von GetInfoTerminal durch m_Init.id ersetzt wurden. Diese Änderung wurde vorgenommen, weil der Zugriff auf eine Variable wesentlich schneller ist als der Aufruf einer Funktion.

In Zeile 224 können Sie sehen, wo die Chart-ID für die spätere Verwendung in der Klasse erfasst wird. Der wichtigste Punkt ist jedoch das Ereignis CHARTEVENT_MOUSE_MOVE in Zeile 306. Beachten Sie, dass Zeile 307 zwar die Klasse C_Mouse aufruft, aber false zurückgibt, wenn der Mauszeiger nicht vorhanden ist. Wenn der Indikator vorhanden ist, wird er true zurückgeben.

Achten Sie auf Zeile 309, wo die CheckMousePosition-Prozedur die lparam- und dparam-Daten, die normalerweise Mauspositionswerte enthalten würden, nicht verwendet. Wenn Sie sich dieses Verfahren in Zeile 140 ansehen, werden Sie feststellen, dass Zeile 145 die Positionsdaten abruft. Sie wird von der Klasse C_Terminal interpretiert. In den Zeilen 145 und 146 werden die Werte angegeben, die wir verwenden wollen. In diesem Fall verwenden wir absolute Werte, obwohl auch relative Werte verwendet werden könnten.

Die Werte stammen aus der Interpretation von C_Terminal und nicht aus dem Mauszeigerpuffer. Dieser Ansatz verbessert die Effizienz und Geschwindigkeit der Ausführung. Die gleichen Werte werden jedoch auch im Mauszeigerpuffer gespeichert.

Schließlich kommen wir zur letzten Quelldatei, die aufgrund von Änderungen im C_ChartFloatingRAD-Konstruktor ebenfalls Änderungen erfuhr. Dies ist der Quellcode des Indikators Chart Trade. Dies ist gleich unten zu sehen:

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.82"
06. #property icon "/Images/Market Replay/Icons/Indicators.ico"
07. #property link "https://www.mql5.com/pt/articles/12580"
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, 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. //+------------------------------------------------------------------+

Der Quellcode des Indikators Chart Trade

Beachten Sie, dass die einzige Änderung in Zeile 23 vorgenommen wurde. Für diejenigen, die frühere Versionen nicht gesehen haben, ist der vollständige Code als Referenz angegeben.


Abschließende Überlegungen

In diesem Artikel stelle ich alle Änderungen vor, die ich am Code vorgenommen habe, um seine Nachhaltigkeit und Wartbarkeit zu verbessern. Im nächsten Artikel werden wir ein anderes Thema behandeln. Im Anhang zu diesem Artikel haben Sie Zugang zu den kompilierten Anwendungen für den Maus-Indikator und den Chart Trade-Indikator, sodass Sie die neuesten Versionen für Ihren Handel nutzen können. Wir werden im nächsten Artikel fortfahren.

Datei 03 Beschreibung
Experts\Expert Advisor.mq5
Demonstriert die Interaktion zwischen Chart Trade und dem Expert Advisor (für die Interaktion ist ein Mauszeiger erforderlich)
Indicadores\Chart Trade.mq5 Erstellt das Fenster für die Konfiguration des zu versendenden Auftrags (Mouse Study ist für die Interaktion erforderlich)
Indicadores\Market Replay.mq5 Erstellt Steuerelemente für die Interaktion mit dem Wiedergabe-/Simulationsdienst (Mausstudie ist für die Interaktion erforderlich)
Indicadores\Mouse Study.mq5 Ermöglicht die Interaktion zwischen den grafischen Steuerelementen und dem Nutzer (erforderlich für den Betrieb des Replay-Simulators und des Live-Markthandels)
Servicios\Market Replay.mq5 Erstellt und pflegt den Marktwiedergabe- und Simulationsdienst (Hauptdatei des gesamten Systems)


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

Beigefügte Dateien |
Anexo.zip (490.53 KB)
Neuronale Netze im Handel: Ein Ensemble von Agenten mit Aufmerksamkeitsmechanismen (MASAAT) Neuronale Netze im Handel: Ein Ensemble von Agenten mit Aufmerksamkeitsmechanismen (MASAAT)
Wir stellen das Multi-Agent Self-Adaptive Portfolio Optimization Framework (MASAAT) vor, das Aufmerksamkeitsmechanismen und Zeitreihenanalyse kombiniert. MASAAT generiert eine Reihe von Agenten, die Preisreihen und Richtungsänderungen analysieren und so die Identifizierung signifikanter Fluktuationen in Vermögenspreisen auf verschiedenen Detailebenen ermöglichen.
Trendkriterien im Handel Trendkriterien im Handel
Trends sind ein wichtiger Bestandteil vieler Handelsstrategien. In diesem Artikel werden wir einige der Instrumente zur Ermittlung von Trends und deren Merkmale betrachten. Das Verständnis und die richtige Interpretation von Trends können die Handelseffizienz erheblich verbessern und die Risiken minimieren.
Multimodul-Handelsroboter in Python und MQL5 (Teil I): Erstellung der Grundarchitektur und erster Module Multimodul-Handelsroboter in Python und MQL5 (Teil I): Erstellung der Grundarchitektur und erster Module
Wir werden ein modulares Handelssystem entwickeln, das Python für die Datenanalyse mit MQL5 für die Handelsausführung kombiniert. Vier unabhängige Module überwachen parallel verschiedene Marktaspekte: Volumen, Arbitrage, Ökonomie und Risiken und wir verwenden RandomForest mit 400 Bäumen für die Analyse. Besonderer Wert wird auf das Risikomanagement gelegt, da selbst die fortschrittlichsten Handelsalgorithmen ohne ein angemessenes Risikomanagement nutzlos sind.
Neuronale Netze im Handel: Ein Multi-Agent Self-Adaptive Modell (letzter Teil) Neuronale Netze im Handel: Ein Multi-Agent Self-Adaptive Modell (letzter Teil)
Im vorangegangenen Artikel haben wir das adaptive Multi-Agenten-System MASA vorgestellt, das Reinforcement-Learning-Ansätze und selbstanpassende Strategien kombiniert und so ein harmonisches Gleichgewicht zwischen Rentabilität und Risiko unter turbulenten Marktbedingungen ermöglicht. Wir haben die Funktionalität der einzelnen Agenten in diesem Rahmen aufgebaut. In diesem Artikel setzen wir die begonnene Arbeit fort und bringen sie zu einem logischen Abschluss.