English Русский Español Português
preview
Entwicklung eines Replay-Systems (Teil 72): Eine ungewöhnliche Kommunikation (I)

Entwicklung eines Replay-Systems (Teil 72): Eine ungewöhnliche Kommunikation (I)

MetaTrader 5Beispiele |
47 0
Daniel Jose
Daniel Jose

Einführung

Im vorangegangenen Artikel „Entwicklung eines Replay-Systems (Teil 71): Das richtige Bestimmen der Zeit (IV)“ habe ich gezeigt, wie man den in einem anderen Artikel vorgestellten Wiedergabe-/Simulatordienst ergänzen kann.

Insbesondere in dem Artikel „Entwicklung eines Replay-Systems (Teil 70): Das richtige Bestimmen der Zeit (III)“, wo wir einen Testdienst verwendet haben, um besser zu verstehen, wie man mit Ereignisse des Order Books arbeitet. Dies geschieht, wenn der Fokus auf einem nutzerdefinierten Symbol liegt.

Der gesamte Prozess in diesen beiden letzten Artikeln war sehr interessant. Dies ist vor allem auf die Vorgehensweise zurückzuführen, die wir wählen mussten, um die gewünschten Ergebnisse zu erzielen. Ich glaube, viele von Ihnen haben gelernt und verstanden, wie man MetaTrader 5 dazu bringt, das Order Book zu nutzen. Ich betone noch einmal, dass es sich um ein nutzerdefiniertes Symbol handelt. Vergessen wir das nicht.

Es war sehr interessant zu sehen, dass wir einfach durch Hinzufügen des Order Books dem Mausindikator erlauben konnten, die OnCalculate-Funktion zu nutzen, bei der die Daten von MetaTrader 5 in Arrays platziert werden. Dadurch wird der Prozess erheblich gestrafft, da die Funktion iSpread nicht mehr verwendet werden muss, um die Spanne der Balken zu ermitteln.

Ohne die in diesen beiden Artikeln vorgestellten Kenntnisse wäre es nicht möglich, die Daten über die Argumente der Funktion OnCalculate abzurufen, wenn der Zeitrahmen größer als eine Minute ist. Abgesehen davon gibt es keine Probleme beim Abrufen von Daten über die OnCalculate-Argumente. Sobald jedoch nutzerdefinierte Ereignisse des Order Books verwendet werden, können wir die Spanne direkt aus den OnCalculate-Argumenten lesen.

Am Ende des letzten Artikels habe ich jedoch ein Problem erläutert, das gelöst werden muss. Wenn dies nicht der Fall ist, können wir die Anwendung nicht richtig nutzen. Für diejenigen, die sich erst jetzt mit diesem Artikel befassen, möchte ich kurz erläutern, worum es sich handelt.


Zusammenfassung des Problems

Das Problem, das wir lösen müssen, ist ein Fehler, der immer dann auftritt, wenn der Zeitrahmen geändert wird. Das ist zwar nicht katastrophal, aber doch recht unangenehm. Dies äußert sich folgendermaßen: Wenn wir den Wiedergabe-/Simulatordienst starten, können wir den gewünschten Zeitrahmen auswählen. Sobald das Chart geöffnet ist, wird es auf diesem ausgewählten Zeitrahmen arbeiten.

Wenn Sie jedoch mit MetaTrader 5 den Zeitrahmen ändern, ändert sich die vom Mausindikator gelieferte Information von „Auktion aktiv“ zu „Markt geschlossen“. Außerdem verschwindet der Kontrollindikator vollständig aus dem Chart und verhindert jede weitere Interaktion.

Obwohl dies eine Unannehmlichkeit ist, tritt der Fehler auf, weil MetaTrader 5 einfach die Indikatoren im Chart neu lädt und sofort die OnInit-Funktion aufruft. Auf jeden Fall funktioniert der MetaTrader 5 genau so, wie er konzipiert wurde. Wir sind es, die versuchen, sie auf eine ganz andere Weise zu nutzen. Weil wir es für unsere Zwecke anders brauchen.

Was geschieht also in diesen Situationen? Wenn dieser Fehler ausgelöst wird, müssen wir warten, bis der Dienst ein nutzerdefiniertes Ereignis an den Kontrollindikator auslöst, damit dieser mit neuen Werten reinitialisiert werden kann. Das Problem ist, dass dies einige Zeit dauern kann. Damit der Dienst das nutzerdefinierte Ereignis auslösen kann, das die Neuinitialisierung des Kontrollindikators ermöglicht, muss sich der Dienst außerdem im Spielmodus befinden.

Wenn sich der Dienst zufällig nicht im Spielmodus befindet, erhält der Kontrollindikator nie das für seine Initialisierung erforderliche nutzerdefinierte Ereignis, sodass er für den Nutzer nicht zugänglich ist. In diesem Fall müsste der Nutzer den Dienst anhalten und neu starten, was ziemlich mühsam ist. Angenommen, der Dienst befindet sich im Wiedergabemodus, müssten wir immer noch darauf warten, dass das nutzerdefinierte Ereignis ausgelöst wird. Sobald das Ereignis ausgelöst wird, wird der Zugriff auf die Steuerelemente des Kontrollindikators wieder möglich. An diesem Punkt muss der Nutzer den Wiedergabe-/Simulationsdienst unterbrechen und dann erneut die Wiedergabetaste drücken. Dadurch werden die vom Mauszeiger angezeigten Informationen aktualisiert, insbesondere die verbleibende Zeit des Balkens, die nun wieder korrekt angezeigt wird. Ändert der Nutzer den Zeitrahmen erneut, wiederholt sich das ganze Problem.

Im vorigen Artikel habe ich dieses Problem ausführlicher erläutert. Ich fasse sie hier nur zusammen, um den Kontext zu verdeutlichen, insbesondere für diejenigen, die diese Serie mit diesem Artikel beginnen. Auf jeden Fall habe ich meinen Lesern im letzten Artikel eine kleine Herausforderung hinterlassen. Die Herausforderung bestand darin, einen Weg zu finden, dieses Problem zu lösen. Obwohl viele glauben, dass Programmieren einfach nur die Kunst ist, Code zu schreiben, den Computer ausführen können, ist Programmieren viel mehr als das. Das Schreiben von Code ist der einfache Teil. Die eigentliche Herausforderung besteht darin herauszufinden, wie man Probleme lösen kann. Programmieren ist in der Tat die Kunst, Probleme durch Code zu lösen.

Ehrlich gesagt, ist es mir egal, ob Sie, lieber Leser, das Problem lösen konnten oder nicht. Was zählt, ist, dass Sie sich wenigstens die Mühe gemacht haben, es zu versuchen. Denn Probleme zu lösen und Lösungen zu finden ist wirklich eine Kunst. Die Eingabe von Code ist lediglich eine Formalität. Lassen Sie uns also sehen, wie wir dieses spezielle Problem lösen können.


Erster Lösungsversuch

Wenn Sie das Problem wirklich verstehen, könnten Sie sofort an die folgende Lösung denken: Implementieren Sie eine Möglichkeit, um zu erkennen, wann der Zeitrahmen des Charts geändert wurde. Sobald dies geschieht, würden wir die Indikatoren wieder in den Zustand versetzen, den sie vor ihrer Entfernung aus dem Chart hatten. Wenn MetaTrader 5 sie zum Chart zurückbringt, kennen sowohl die Kontroll- als auch die Mausindikatoren ihren letzten Zustand und können von diesem Punkt aus fortgesetzt werden. Eine großartige Idee. Aber sie hat auch eine Kehrseite. Unabhängig davon, wie Sie den letzten bekannten Zustand des Indikators speichern, darf diese Information nicht direkt mit dem Indikator selbst verbunden sein. Sie sollte dem Indikator zur Verfügung gestellt werden, sobald die OnInit-Funktion aufgerufen wird. Andernfalls werden wir Probleme bekommen.

Es gibt mehrere Möglichkeiten, dies zu erreichen, aber alle erfordern zusätzliche Implementierung und Tests, um zu überprüfen, ob der gespeicherte Zustand sicher verwendet werden kann. Ich persönlich halte diese Lösung für zu umständlich, da zusätzliche Tests erforderlich sind, nur um zu überprüfen, ob die gespeicherten Informationen verwendbar sind.

Wie könnte dies konkret umgesetzt werden? Lassen Sie uns zunächst überlegen, welche Informationen wir speichern müssen. Im Falle des Kontrollanzeigers müsste gespeichert werden, ob er sich im Pausen- oder im Wiedergabemodus befindet. Nun, das scheint genug zu sein. Was den Mauszeiger betrifft, so müsste er nur zurückkommen und anzeigen, dass sich die Anlage im Auktionsmodus befindet. Na gut. Die Speicherung dieser Daten ist recht einfach. Sie können sie in eine Datei oder in eine globale Terminalvariable schreiben. Wie auch immer, dieser Teil ist einfach genug zu implementieren. Betrachten wir nun die Probleme, die dies mit sich bringen würde.

Mit der Kontrollanzeige gäbe es keine großen Probleme. Sie wird nur dann zur Tabelle hinzugefügt, wenn der Wiedergabe-/Simulatordienst läuft. Beide Lösungen, d. h. Datei oder globale Terminalvariable, würden also funktionieren. Wir prüfen, ob die Datei oder Variable existiert. Wenn ja, verwenden wir dessen Wert. Das Problem ist jedoch, dass diese Daten im Laufe der Zeit nicht mehr mit dem aktuellen Stand des Dienstes übereinstimmen könnten. Wir müssten dafür sorgen, dass der Dienst die globale Variable oder Datei löscht, sobald er heruntergefahren wird.

Mit anderen Worten, es gibt nur noch mehr Programmieraufwand, um den man sich kümmern muss. Nun zurück zum Mauszeiger. Wenn wir es mit dem Wert „Auktionsmodus“ initialisieren, könnte dies zu Problemen führen, wenn es zu einem Chart eines echten Vermögenswerts hinzugefügt wird. Das liegt daran, dass der Markt tatsächlich geschlossen sein könnte, während der Mauszeiger fälschlicherweise den Auktionsmodus anzeigt. Außerdem habe ich vor, den Mausindikator weiter zu verbessern, sodass ein Start im Auktionsmodus die Sache nur verkomplizieren würde. Das führt zu unnötigen Problemen in der Zukunft, die ich um jeden Preis vermeiden möchte.

Zusammenfassend lässt sich also sagen, dass die Verwendung einer globalen Terminalvariable völlig vom Tisch ist. Dadurch würden zwei verschiedene Initialisierungspfade für die Indikatoren eingeführt. Auch die Verwendung einer Datei würde zu den gleichen Problemen führen. Diese Lösung wird unseren Bedürfnissen einfach nicht gerecht. Wir brauchen eine andere Lösung, die mit dem Indikator verbunden bleibt, uns aber auch erlaubt, zu testen, was im Chart selbst passiert.


Zweiter Lösungsversuch

Jemand könnte vorschlagen, dass der Dienst den aktuellen Zeitrahmen des nutzerdefinierten Symbols überprüft. Dies wäre in der Tat eine sehr clevere Lösung, denn dann müssten keine Zustandsinformationen für die Indikatoren gespeichert werden. Denken Sie daran: Der Wiedergabe-/Simulatordienst kennt den Status des Indikators zu jedem Zeitpunkt der Ausführung. Diese Lösung bringt jedoch ein kleines Problem mit sich: Es gibt keine Möglichkeit für den Dienst, den aktuellen Chart-Zeitrahmen direkt abzurufen.

Doch bevor wir diese Alternative aufgeben, sollten wir einen Moment darüber nachdenken. Wenn wir irgendwie erkennen können, wann sich der Zeitrahmen ändert, müssten wir nur den Dienst die Methode UpdateIndicatorControl aufrufen lassen, die sich in der Header-Datei C_Replay.mqh befindet, und das Problem wäre behoben. Das ist fast ganz richtig. Wir müssten nur eine geringfügige Anpassung der UpdateIndicatorControl-Prozedur vornehmen, aber das ist viel einfacher, als einen völlig separaten Initialisierungspfad für die Indikatoren zu erstellen. Der Dienst muss also irgendwie erkennen, dass der Zeitrahmen des Charts geändert wurde. Danach kann sie die Mechanismen nutzen, die wir bereits implementiert haben, um die Indikatoren ordnungsgemäß zu reinitialisieren. Selbst wenn innerhalb des Dienstes geringfügige Änderungen erforderlich sind, um UpdateIndicatorControl zu verwenden, wären diese Anpassungen weitaus geringer und überschaubarer, als wenn die Indikatoren selbst geändert werden müssten, um diese Situation zu bewältigen. Kurzum, diese Alternative ist auf jeden Fall eine Überlegung wert.


Dritter Lösungsversuch

Wie wäre es, wenn wir etwas Unkonventionelleres versuchen? Kombinieren wir die beiden vorherigen Ansätze zu einer einzigen Lösung, allerdings ohne globale Terminalvariablen oder Dateien zu verwenden. Stattdessen verwenden wir den Datenpuffer, um die benötigten Informationen zu übertragen - in diesem Fall den Zeitrahmen. Auf diese Weise kann der Dienst überwachen, was vor sich geht. Wenn sich also der Zeitrahmen ändert, wird dies vom Dienst sofort erkannt. Wenn dies geschieht, kann der Dienst die Indikatoren benachrichtigen, dass sie selbst aktualisiert werden müssen.

Unsere eigentliche Herausforderung besteht dann darin, die Zeitrahmeninformationen in den Datenpuffer zu schreiben und diese Informationen aus dem Dienst zu lesen. Wenn der Nutzer den Zeitrahmen ändert, sollte die gesamte Anwendung ein nutzerdefiniertes Ereignis vom Dienst erhalten. Dieses Ereignis stellt sicher, dass der Mauszeiger den richtigen Wert erhält, der dem Nutzer angezeigt werden soll, und der Kontrollzeiger wird ebenfalls entsprechend aktualisiert. Dadurch wird verhindert, dass sie für die Steuerung des Wiedergabe- und Pausenmodus unzugänglich wird.


Start des Implementierungstests

Im Gegensatz zu dem, was manche denken mögen, testen wir Programmierer immer Dinge, bevor wir sie in die Praxis umsetzen. Wir beginnen nie damit, ein Programm oder eine Anwendung zu ändern, die sich bereits in einem fortgeschrittenen Entwicklungsstadium befindet. Wann immer eine neue Funktion implementiert werden soll, erstellen wir zunächst eine Testversion. Diese Version ist so einfach wie möglich. Wir nutzen sie zur Feinabstimmung der Umsetzung und untersuchen, wie der endgültige Prozess tatsächlich funktionieren sollte. Dies erspart es uns, Teile des Codes zu entfernen oder umzuschreiben, die sich später als ungeeignet für die endgültige Version erweisen.

Lassen Sie uns also einen sehr einfachen Indikator erstellen, um das Problem der Zeitrahmenänderungen zu testen. Der vollständige Quellcode für diesen Indikator ist unten zu sehen.

01. //+------------------------------------------------------------------+
02. #property copyright "Daniel Jose"
03. #property version   "1.00"
04. #property indicator_chart_window
05. #property indicator_plots 0
06. //+------------------------------------------------------------------+
07. int OnInit()
08. {
09.    Print(_Period);
10. 
11.    return
     INIT_SUCCEEDED;
12. }
13. //+------------------------------------------------------------------+
14. int OnCalculate(const int rates_total, const int prev_calculated, const int begin, const double &price[])
15. {
16.    return rates_total;
17. }
18. //+------------------------------------------------------------------+

Quellcode des Testindikators

Was bewirkt dieser kleine Code? Kurz gesagt, alles, was es tut, geschieht in Zeile neun. Das heißt, er gibt den in der internen Variablen _Period gespeicherten Wert auf dem Terminal aus. Ein Beispiel für die Ausführung ist in der nachstehenden Abbildung zu sehen:

Abbildung 01

Ausführung Ergebnis

Beachten Sie hier etwas Interessantes. In der obigen Abbildung sehen wir in der Spalte SOURCE den Namen des Indikators, aber was für uns wirklich wichtig ist, ist das, was in den Klammern steht. Genauer gesagt, der Wert nach dem Komma. Wie Sie sehen können, habe ich den Zeitrahmen mehrmals geändert, und bei jeder Änderung wurde ein anderer Wert in der Spalte der Meldungen ausgegeben. Dies mag offensichtlich erscheinen. Aber ignorieren Sie vorerst die Tatsache, dass die Werte unterschiedlich sind. Und versuchen Sie nicht, daraus eine bestimmte Logik abzuleiten. Was uns wirklich interessiert, ist: Was ist der höchste Wert, den _Period tatsächlich annehmen kann? Nun, MetaTrader 5 erlaubt uns, den monatlichen Zeitrahmen zu verwenden. In der Abbildung sehen Sie diesen Wert in der vorletzten Zeile: Der höchste Wert ist 49153. Warum ist es wichtig, diesen Wert zu kennen? Denn wir brauchen eine Möglichkeit für den Dienst, zu erkennen, wenn sich dieser Wert ändert. Aber wir können es uns nicht leisten, eine beliebige Bitlänge zu verwenden, um dies zu erreichen. Wir müssen sicherstellen, dass die Anzahl der verwendeten Bits so gering wie möglich ist. Sie werden den Grund dafür bald verstehen.

Lassen Sie uns eine kleine Änderung am obigen Code vornehmen. Hier ist, was wir haben:

01. //+------------------------------------------------------------------+
02. #property copyright "Daniel Jose"
03. #property version   "1.00"
04. #property indicator_chart_window
05. #property indicator_plots 0
06. //+------------------------------------------------------------------+
07. int OnInit()
08. {
09.    Print(_Period < 60 ? _Period : (_Period < PERIOD_D1 ? _Period - 16325 : (_Period == PERIOD_D1 ? 84 : (_Period == PERIOD_W1 ? 91 : 96))));
10. 
11.    return INIT_SUCCEEDED;
12. }
13. //+------------------------------------------------------------------+
14. int OnCalculate(const int rates_total, const int prev_calculated, const int begin, const double &price[])
15. {
16.    return rates_total;
17. }
18. //+------------------------------------------------------------------+

Quellcode des Testindikators

Bevor wir analysieren, was Zeile neun jetzt tut, sehen wir uns das Ausführungsergebnis in der folgenden Abbildung an.

Abbildung 02

Ausführung Ergebnis

Achten Sie nun auf den folgenden Punkt. Bisher brauchten wir 16 Bits, um den Wert zu speichern, mit dem wir erkennen konnten, wann sich der Zeitrahmen geändert hatte. Aber beachten Sie jetzt den höchstmöglichen Wert: 96. Wow! Aber können wir das wirklich tun? Ja, wir können und wir werden. Denn offenbar hat MetaTrader 5 diese Zeitrahmenwerte bereits konsolidiert. Wir können also die Dinge so ändern, dass sie mit diesem vereinfachten Wert arbeiten.

Wenn wir nun zum Code zurückkehren, stellen wir fest, dass der Reduktionsprozess recht einfach ist. Sie kann in einer einzigen Zeile ausgeführt werden, wobei nur Konstanten verwendet werden. Das ist für unsere Zwecke mehr als ausreichend, da wir jetzt nur noch 7 Bits benötigen, um den Wert zu übertragen. Wir müssen also ein wenig darüber nachdenken, wie wir das umsetzen können. Denken Sie daran: Uns interessiert nicht der Zeitrahmen selbst, sondern ob er sich verändert hat oder nicht.


Beginn der Informationsübertragung an den Dienst

Um die Informationen über den Zeitrahmen an den Dienst zu übertragen, sodass dieser erkennen kann, wann er sich ändert, müssen wir den Indikatorpuffer verwenden, da wir keinen anderen geeigneten Mechanismus dafür haben. Es gibt aber auch andere Möglichkeiten. Ich möchte aber absichtlich den Indikatorpuffer für diese Aufgabe verwenden. Bei diesem Ansatz bleibt alles für den Nutzer unsichtbar, während gleichzeitig ein gewisses Maß an Informationskapselung während der Übertragung gewährleistet ist.

Aber bevor wir das tun, sollten wir den letzten Code, den wir getestet haben, in unser System integrieren. Sie wird als Definition hinzugefügt. Der Code in der Header-Datei Defines.mqh sieht daher wie folgt aus:

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. //+------------------------------------------------------------------+
18. union uCast_Double
19. {
20.    double    dValue;
21.    long      _long;                                 // 1 Information
22.    datetime _datetime;                              // 1 Information
23.    uint     _32b[sizeof(double) / sizeof(uint)];    // 2 Informations
24.    ushort   _16b[sizeof(double) / sizeof(ushort)];  // 4 Informations
25.    uchar    _8b [sizeof(double) / sizeof(uchar)];   // 8 Informations
26. };
27. //+------------------------------------------------------------------+
28. enum EnumEvents    {
29.          evHideMouse,               //Hide mouse price line
30.          evShowMouse,               //Show mouse price line
31.          evHideBarTime,             //Hide bar time
32.          evShowBarTime,             //Show bar time
33.          evHideDailyVar,            //Hide daily variation
34.          evShowDailyVar,            //Show daily variation
35.          evHidePriceVar,            //Hide instantaneous variation
36.          evShowPriceVar,            //Show instantaneous variation
37.          evCtrlReplayInit           //Initialize replay control
38.                   };
39. //+------------------------------------------------------------------+

Der Quellcode der Datei Defines.mqh

Na gut. Sehen Sie sich nun Zeile 16 an. Dies ist der Code, der bei den im vorherigen Abschnitt beschriebenen Tests verwendet wurde. Ausgezeichnet. Dies garantiert, dass wir einen Code verwenden, der bereits getestet wurde und korrekt funktioniert. Jetzt kommt der kompliziertere Teil, zumindest wenn Sie diese Artikelserie nicht von Anfang an verfolgt haben oder erst jetzt dazukommen.

Der Datenpuffer des Indikators ist immer vom Typ double, sodass er 8 Byte Speicherplatz belegt. Aus Gründen, die ich in früheren Artikeln dieser Reihe erläutert habe, müssen alle Informationen, die wir vom Indikator zum Dienst übertragen, in diese 8 Bytes passen. Wir können es uns nicht leisten, mehr als 8 Bytes für diesen Informationsaustausch zu benötigen. Alles muss in diese 8 Bytes gepackt werden. Lassen Sie uns das durchdenken. Der Mausindikator kann auf einem Live-Markt verwendet werden. Mit anderen Worten, es kann auf ein Symbol einwirken, das Daten vom eigentlichen Handelsserver empfängt. Das Problem, um das es hier geht, tritt jedoch nur bei der Verwendung des Wiedergabe-/Simulationsdienstes auf. Bisher ist der einzige Indikator, der ausschließlich für den Wiederholungs-/Simulationsmodus erforderlich ist, der Kontrollindikator. Daher ist es durchaus sinnvoll, sich darauf zu konzentrieren, wie der Datenpuffer des Steuerkennzeichens derzeit aufgebaut ist. Dies können Sie unten sehen:

Abbildung 3

QWORD ist ein Begriff, der aus der Assemblersprache stammt. Es zeigt an, dass es sich um einen 64-Bit-Wert handelt. Das Byte ganz rechts ist Byte Null, das Byte ganz links ist Byte Sieben. Im Falle des Kontrollkennzeichens werden derzeit 4 Bytes verwendet. Um dies besser zu verstehen, sehen Sie sich den unten abgebildeten Code-Auszug aus der Header-Datei C_Controls.mqh an:

168. //+------------------------------------------------------------------+
169.       void SetBuffer(const int rates_total, double &Buff[])
170.          {
171.             uCast_Double info;
172.             
173.             info._16b[eCtrlPosition] = m_Slider.Minimal;
174.             info._16b[eCtrlStatus] = (ushort)(m_Slider.Minimal > def_MaxPosSlider ? m_Slider.Minimal : (m_Section[ePlay].state ? ePlay : ePause));
175.             if (rates_total > 0)
176.                Buff[rates_total - 1] = info.dValue;
177.          }
178. //+------------------------------------------------------------------+

Auszug aus der Datei C_Controls.mqh

In Zeile 171 deklarieren wir die Vereinigung, die zuvor in der oben gezeigten Datei Defines.mqh definiert wurde. Beachten Sie, dass wir zwei Array-Werte verwenden, von denen jeder 16 Bit verwendet. Damit haben wir insgesamt 32 Bits. Zwar werden nicht alle Bits tatsächlich verwendet, aber der Einfachheit halber nehmen wir an, dass sie verwendet werden. Somit sind vier der acht verfügbaren Bytes bereits belegt. Da unser Modellierungssystem es uns jedoch ermöglicht hat, den Zeitrahmen mit einem einzigen Byte zu kodieren, werden wir nun fünf der acht verfügbaren Bytes verwenden. Der kritischste Teil ist jedoch die Entscheidung, welchen Index wir für diese neuen Daten verwenden sollen. Diese Art von Detail verwirrt viele Anfänger in der Programmierung, weil sie oft vergessen, dass bei der Verwendung einer Union einige Bytes bereits mit nützlichen Informationen belegt sind. Wenn Sie versehentlich den falschen Index verwenden, besteht die Gefahr, dass Sie gültige Daten überschreiben, was zu beschädigten Werten führt. Beziehen wir uns also noch einmal auf das QWORD-Bild. Dort sind bereits vier Bytes zugewiesen, beginnend bei Index Null. Das bedeutet, dass die Indizes von 0 bis 3 in Gebrauch sind und nicht für andere Zwecke wiederverwendet werden dürfen. Der erste verfügbare Index für neue Daten ist daher Index 4.

Wir aktualisieren also die Header-Datei Defines.mqh wie folgt:

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

Der Quellcode der Datei Defines.mqh

In Zeile 17 haben wir eine neue Definition, die es uns ermöglicht, sicher auf den richtigen Index zu verweisen. Jetzt haben wir also die Grundlage, die wir brauchen. Da die Aufgabe jedoch etwas komplexer ist, als es auf den ersten Blick scheint, wollen wir uns zunächst ansehen, wie die Header-Datei C_Controls.mqh tatsächlich aufgebaut sein wird. Der vollständige Code ist unten zu sehen:

001. //+------------------------------------------------------------------+
002. #property copyright "Daniel Jose"
003. //+------------------------------------------------------------------+
004. #include "..\Auxiliar\C_DrawImage.mqh"
005. #include "..\Defines.mqh"
006. //+------------------------------------------------------------------+
007. #define def_PathBMP           "Images\\Market Replay\\Control\\"
008. #define def_ButtonPlay        def_PathBMP + "Play.bmp"
009. #define def_ButtonPause       def_PathBMP + "Pause.bmp"
010. #define def_ButtonCtrl        def_PathBMP + "Ctrl.bmp"
011. #define def_ButtonCtrlBlock   def_PathBMP + "Ctrl_Block.bmp"
012. #define def_ButtonPin         def_PathBMP + "Pin.bmp"
013. #resource "\\" + def_ButtonPlay
014. #resource "\\" + def_ButtonPause
015. #resource "\\" + def_ButtonCtrl
016. #resource "\\" + def_ButtonCtrlBlock
017. #resource "\\" + def_ButtonPin
018. //+------------------------------------------------------------------+
019. #define def_ObjectCtrlName(A)   "MarketReplayCTRL_" + (typename(A) == "enum eObjectControl" ? EnumToString((C_Controls::eObjectControl)(A)) : (string)(A))
020. #define def_PosXObjects         120
021. //+------------------------------------------------------------------+
022. #define def_SizeButtons         32
023. #define def_ColorFilter         0xFF00FF
024. //+------------------------------------------------------------------+
025. #include "..\Auxiliar\C_Mouse.mqh"
026. //+------------------------------------------------------------------+
027. class C_Controls : private C_Terminal
028. {
029.    protected:
030.    private   :
031. //+------------------------------------------------------------------+
032.       enum eMatrixControl {eCtrlPosition, eCtrlStatus};
033.       enum eObjectControl {ePause, ePlay, eLeft, eRight, ePin, eNull, eTriState = (def_MaxPosSlider + 1)};
034. //+------------------------------------------------------------------+
035.       struct st_00
036.       {
037.          string   szBarSlider,
038.                   szBarSliderBlock;
039.          ushort   Minimal;
040.       }m_Slider;
041.       struct st_01
042.       {
043.          C_DrawImage *Btn;
044.          bool         state;
045.          short        x, y, w, h;
046.       }m_Section[eObjectControl::eNull];
047.       C_Mouse   *m_MousePtr;
048. //+------------------------------------------------------------------+
049. inline void CreteBarSlider(short x, short size)
050.          {
051.             ObjectCreate(GetInfoTerminal().ID, m_Slider.szBarSlider = def_ObjectCtrlName("B1"), OBJ_RECTANGLE_LABEL, 0, 0, 0);
052.             ObjectSetInteger(GetInfoTerminal().ID, m_Slider.szBarSlider, OBJPROP_XDISTANCE, def_PosXObjects + x);
053.             ObjectSetInteger(GetInfoTerminal().ID, m_Slider.szBarSlider, OBJPROP_YDISTANCE, m_Section[ePin].y + 11);
054.             ObjectSetInteger(GetInfoTerminal().ID, m_Slider.szBarSlider, OBJPROP_XSIZE, size);
055.             ObjectSetInteger(GetInfoTerminal().ID, m_Slider.szBarSlider, OBJPROP_YSIZE, 9);
056.             ObjectSetInteger(GetInfoTerminal().ID, m_Slider.szBarSlider, OBJPROP_BGCOLOR, clrLightSkyBlue);
057.             ObjectSetInteger(GetInfoTerminal().ID, m_Slider.szBarSlider, OBJPROP_BORDER_COLOR, clrBlack);
058.             ObjectSetInteger(GetInfoTerminal().ID, m_Slider.szBarSlider, OBJPROP_WIDTH, 3);
059.             ObjectSetInteger(GetInfoTerminal().ID, m_Slider.szBarSlider, OBJPROP_BORDER_TYPE, BORDER_FLAT);
060.             ObjectCreate(GetInfoTerminal().ID, m_Slider.szBarSliderBlock = def_ObjectCtrlName("B2"), OBJ_RECTANGLE_LABEL, 0, 0, 0);
061.             ObjectSetInteger(GetInfoTerminal().ID, m_Slider.szBarSliderBlock, OBJPROP_XDISTANCE, def_PosXObjects + x);
062.             ObjectSetInteger(GetInfoTerminal().ID, m_Slider.szBarSliderBlock, OBJPROP_YDISTANCE, m_Section[ePin].y + 6);
063.             ObjectSetInteger(GetInfoTerminal().ID, m_Slider.szBarSliderBlock, OBJPROP_YSIZE, 19);
064.             ObjectSetInteger(GetInfoTerminal().ID, m_Slider.szBarSliderBlock, OBJPROP_BGCOLOR, clrRosyBrown);
065.             ObjectSetInteger(GetInfoTerminal().ID, m_Slider.szBarSliderBlock, OBJPROP_BORDER_TYPE, BORDER_RAISED);
066.          }
067. //+------------------------------------------------------------------+
068.       void SetPlay(bool state)
069.          {
070.             if (m_Section[ePlay].Btn == NULL)
071.                m_Section[ePlay].Btn = new C_DrawImage(GetPointer(this), def_ObjectCtrlName(ePlay), def_ColorFilter, "::" + def_ButtonPause, "::" + def_ButtonPlay);
072.             m_Section[ePlay].Btn.Paint(m_Section[ePlay].x, m_Section[ePlay].y, m_Section[ePlay].w, m_Section[ePlay].h, 20, (m_Section[ePlay].state = state) ? 1 : 0, state ? "Press to Pause" : "Press to Start");
073.             if (!state) CreateCtrlSlider();
074.          }
075. //+------------------------------------------------------------------+
076.       void CreateCtrlSlider(void)
077.          {
078.             if (m_Section[ePin].Btn != NULL) return;
079.             CreteBarSlider(77, 436);
080.             m_Section[eLeft].Btn = new C_DrawImage(GetPointer(this), def_ObjectCtrlName(eLeft), def_ColorFilter, "::" + def_ButtonCtrl, "::" + def_ButtonCtrlBlock);
081.             m_Section[eRight].Btn = new C_DrawImage(GetPointer(this), def_ObjectCtrlName(eRight), def_ColorFilter, "::" + def_ButtonCtrl, "::" + def_ButtonCtrlBlock, true);
082.             m_Section[ePin].Btn = new C_DrawImage(GetPointer(this), def_ObjectCtrlName(ePin), def_ColorFilter, "::" + def_ButtonPin, NULL);
083.             PositionPinSlider(m_Slider.Minimal);
084.          }
085. //+------------------------------------------------------------------+
086. inline void RemoveCtrlSlider(void)
087.          {         
088.             ChartSetInteger(GetInfoTerminal().ID, CHART_EVENT_OBJECT_DELETE, false);
089.             for (eObjectControl c0 = ePlay + 1; c0 < eNull; c0++)
090.             {
091.                delete m_Section[c0].Btn;
092.                m_Section[c0].Btn = NULL;
093.             }
094.             ObjectsDeleteAll(GetInfoTerminal().ID, def_ObjectCtrlName("B"));
095.             ChartSetInteger(GetInfoTerminal().ID, CHART_EVENT_OBJECT_DELETE, true);
096.          }
097. //+------------------------------------------------------------------+
098. inline void PositionPinSlider(ushort p)
099.          {
100.             int iL, iR;
101.             string szMsg;
102.             
103.             m_Section[ePin].x = (short)(p < m_Slider.Minimal ? m_Slider.Minimal : (p > def_MaxPosSlider ? def_MaxPosSlider : p));
104.             iL = (m_Section[ePin].x != m_Slider.Minimal ? 0 : 1);
105.             iR = (m_Section[ePin].x < def_MaxPosSlider ? 0 : 1);
106.             m_Section[ePin].x += def_PosXObjects;
107.              m_Section[ePin].x += 95 - (def_SizeButtons / 2);
108.              for (eObjectControl c0 = ePlay + 1; c0 < eNull; c0++)   if (m_Section[c0].Btn != NULL)
109.              {
110.                 switch (c0)
111.                 {
112.                    case eLeft  : szMsg = "Previous Position";            break;
113.                    case eRight : szMsg = "Next Position";                break;
114.                    case ePin   : szMsg = "Go To: " + IntegerToString(p); break;
115.                    default     : szMsg = "\n";
116.                 }
117.                m_Section[c0].Btn.Paint(m_Section[c0].x, m_Section[c0].y, m_Section[c0].w, m_Section[c0].h, 20, (c0 == eLeft ? iL : (c0 == eRight ? iR : 0)), szMsg);
118.             }
119. 
120.             ObjectSetInteger(GetInfoTerminal().ID, m_Slider.szBarSliderBlock, OBJPROP_XSIZE, m_Slider.Minimal + 2);
121.          }
122. //+------------------------------------------------------------------+
123. inline eObjectControl CheckPositionMouseClick(short &x, short &y)
124.          {
125.             C_Mouse::st_Mouse InfoMouse;
126.             
127.             InfoMouse = (*m_MousePtr).GetInfoMouse();
128.             x = (short) InfoMouse.Position.X_Graphics;
129.             y = (short) InfoMouse.Position.Y_Graphics;
130.             for (eObjectControl c0 = ePlay; c0 < eNull; c0++)
131.             {   
132.                if ((m_Section[c0].Btn != NULL) && (m_Section[c0].x <= x) && (m_Section[c0].y <= y) && ((m_Section[c0].x + m_Section[c0].w) >= x) && ((m_Section[c0].y + m_Section[c0].h) >= y))
133.                   return c0;
134.             }
135.             
136.             return eNull;
137.          }
138. //+------------------------------------------------------------------+
139.    public   :
140. //+------------------------------------------------------------------+
141.       C_Controls(const long Arg0, const string szShortName, C_Mouse *MousePtr)
142.          :C_Terminal(Arg0),
143.           m_MousePtr(MousePtr)
144.          {
145.             if ((!IndicatorCheckPass(szShortName)) || (CheckPointer(m_MousePtr) == POINTER_INVALID)) SetUserError(C_Terminal::ERR_Unknown);
146.             if (_LastError >= ERR_USER_ERROR_FIRST) return;
147.             ChartSetInteger(GetInfoTerminal().ID, CHART_EVENT_OBJECT_DELETE, false);
148.             ObjectsDeleteAll(GetInfoTerminal().ID, def_ObjectCtrlName(""));
149.             ChartSetInteger(GetInfoTerminal().ID, CHART_EVENT_OBJECT_DELETE, true);
150.             for (eObjectControl c0 = ePlay; c0 < eNull; c0++)
151.             {
152.                m_Section[c0].h = m_Section[c0].w = def_SizeButtons;
153.                m_Section[c0].y = 25;
154.                m_Section[c0].Btn = NULL;
155.             }
156.             m_Section[ePlay].x = def_PosXObjects;
157.             m_Section[eLeft].x = m_Section[ePlay].x + 47;
158.             m_Section[eRight].x = m_Section[ePlay].x + 511;
159.             m_Slider.Minimal = eTriState;
160.          }
161. //+------------------------------------------------------------------+
162.       ~C_Controls()
163.          {
164.             for (eObjectControl c0 = ePlay; c0 < eNull; c0++) delete m_Section[c0].Btn;
165.             ObjectsDeleteAll(GetInfoTerminal().ID, def_ObjectCtrlName(""));
166.             delete m_MousePtr;
167.          }
168. //+------------------------------------------------------------------+
169.       void SetBuffer(const int rates_total, double &Buff[])
170.          {
171.             uCast_Double info;
172.             
173.             info._16b[eCtrlPosition] = m_Slider.Minimal;
174.             info._16b[eCtrlStatus] = (ushort)(m_Slider.Minimal > def_MaxPosSlider ? m_Slider.Minimal : (m_Section[ePlay].state ? ePlay : ePause));
175.             info._8b[def_IndexTimeFrame] = (uchar) def_IndicatorTimeFrame;
176.             if (rates_total > 0)
177.                Buff[rates_total - 1] = info.dValue;
178.          }
179. //+------------------------------------------------------------------+
180.       void DispatchMessage(const int id, const long &lparam, const double &dparam, const string &sparam)
181.          {
182.             short x, y;
183.             static ushort iPinPosX = 0;
184.             static short six = -1, sps;
185.             uCast_Double info;
186.             
187.             switch (id)
188.             {
189.                case (CHARTEVENT_CUSTOM + evCtrlReplayInit):
190.                   info.dValue = dparam;
191.                   if ((info._8b[7] != 'D') || (info._8b[6] != 'M')) break;
192.                   iPinPosX = m_Slider.Minimal = (info._16b[eCtrlPosition] > def_MaxPosSlider ? def_MaxPosSlider : (info._16b[eCtrlPosition] < iPinPosX ? iPinPosX : info._16b[eCtrlPosition]));
193.                   SetPlay((eObjectControl)(info._16b[eCtrlStatus]) == ePlay);
194.                   break;
195.                case CHARTEVENT_OBJECT_DELETE:
196.                   if (StringSubstr(sparam, 0, StringLen(def_ObjectCtrlName(""))) == def_ObjectCtrlName(""))
197.                   {
198.                      if (sparam == def_ObjectCtrlName(ePlay))
199.                      {
200.                         delete m_Section[ePlay].Btn;
201.                         m_Section[ePlay].Btn = NULL;
202.                         SetPlay(m_Section[ePlay].state);
203.                      }else
204.                      {
205.                         RemoveCtrlSlider();
206.                         CreateCtrlSlider();
207.                      }
208.                   }
209.                   break;
210.                case CHARTEVENT_MOUSE_MOVE:
211.                   if ((*m_MousePtr).CheckClick(C_Mouse::eClickLeft))   switch (CheckPositionMouseClick(x, y))
212.                   {
213.                      case ePlay:
214.                         SetPlay(!m_Section[ePlay].state);
215.                         if (m_Section[ePlay].state)
216.                         {
217.                            RemoveCtrlSlider();
218.                            m_Slider.Minimal = iPinPosX;
219.                         }else CreateCtrlSlider();
220.                         break;
221.                      case eLeft:
222.                         PositionPinSlider(iPinPosX = (iPinPosX > m_Slider.Minimal ? iPinPosX - 1 : m_Slider.Minimal));
223.                         break;
224.                      case eRight:
225.                         PositionPinSlider(iPinPosX = (iPinPosX < def_MaxPosSlider ? iPinPosX + 1 : def_MaxPosSlider));
226.                         break;
227.                      case ePin:
228.                         if (six == -1)
229.                         {
230.                            six = x;
231.                            sps = (short)iPinPosX;
232.                            ChartSetInteger(GetInfoTerminal().ID, CHART_MOUSE_SCROLL, false);
233.                         }
234.                         iPinPosX = sps + x - six;
235.                         PositionPinSlider(iPinPosX = (iPinPosX < m_Slider.Minimal ? m_Slider.Minimal : (iPinPosX > def_MaxPosSlider ? def_MaxPosSlider : iPinPosX)));
236.                         break;
237.                   }else if (six > 0)
238.                   {
239.                      six = -1;
240.                      ChartSetInteger(GetInfoTerminal().ID, CHART_MOUSE_SCROLL, true);                     
241.                   }
242.                   break;
243.             }
244.             ChartRedraw(GetInfoTerminal().ID);
245.          }
246. //+------------------------------------------------------------------+
247. };
248. //+------------------------------------------------------------------+
249. #undef def_PosXObjects
250. #undef def_ButtonPlay
251. #undef def_ButtonPause
252. #undef def_ButtonCtrl
253. #undef def_ButtonCtrlBlock
254. #undef def_ButtonPin
255. #undef def_PathBMP
256. //+------------------------------------------------------------------+

Quellcode der Datei C_Controls.mqh

Aufgrund verschiedener Änderungen, die in früheren Artikeln vorgenommen wurden, ist es notwendig, dass Sie zumindest einige Änderungen an der Header-Datei C_Controls.mqh verstehen und anwenden, zusammen mit einigen anderen Änderungen, die ich jetzt zeigen werde. Schauen Sie sich Zeile 10 an: Diese Zeile verweist auf das Bild Ctrl.bmp. Aber um welches Bild handelt es sich? Es handelt sich um dasselbe Bild, das zuvor LEFT.BMP hieß. In ähnlicher Weise verweist Zeile 11 auf die ehemalige Datei LEFT_BLOCK.BMP. Von nun an werden wir andere Namen für diese Bilder verwenden. Auf jeden Fall habe ich diese Bilder im Anhang beigefügt, sodass Sie sie im Zweifelsfall leicht in Ihr Projekt einfügen können.

Es gibt noch eine weitere Änderung, die hervorzuheben ist - sie befindet sich im Konstruktor der Klasse C_Controls. Sehen Sie sich Zeile 146 an. Der Konstruktor wird jetzt nur dann vorzeitig beendet, wenn der aufgetretene Fehler einer ist, den wir als Entwickler ausdrücklich im Code angegeben haben. Andere Arten von Fehlern werden - zumindest vorläufig - ignoriert.

Der wichtige Teil, der uns wirklich interessiert, erscheint in Zeile 175. Hier legen wir fest, wo und was in den Indikatorpuffer geschrieben werden soll, wie in diesem Artikel vorgeschlagen. Da die Nutzung dieser Informationen jedoch etwas komplexer ist als das einfache Schreiben in den Puffer, werde ich den vollständigen Code noch nicht zeigen. Das liegt daran, dass es wichtig ist, denjenigen, die noch lernen, wie man für MetaTrader 5 programmiert, zu erklären, wie die Dinge funktionieren. Also, lieber Leser, wenn Sie schon etwas erfahrener sind, entschuldige ich mich dafür, dass ich jetzt nicht den kompletten Code vorstelle.


Abschließende Überlegungen

Da ich noch einige zusätzliche Änderungen am Servicecode und am Indikatorcode erläutern muss, werde ich in diesem Artikel nicht den vollständigen Code präsentieren. Ich entschuldige mich bei denjenigen, die bereits gute Erfahrungen mit MQL5 gemacht haben. Im Laufe der Zeit, in der ich diese Artikel veröffentliche, habe ich jedoch festgestellt, dass viele Leser gerade erst mit MQL5 anfangen - aufstrebende Programmierer, die unbedingt lernen wollen. Ich versuche also, die Dinge gründlich zu erklären. Nichtsdestotrotz finden Sie im Anhang zu diesem Artikel zwei Bilder. Dies sind die Bilder, auf die in der Header-Datei C_Controls.mqh verwiesen wird.

Vergessen Sie nicht, diese Anpassung an Ihrem Projekt vorzunehmen, wenn Sie es weiterverfolgen wollen.

Im nächsten Artikel werden wir uns mit folgenden Aufgaben befassen: Vorstellung und Erläuterung der am Quellcode des Kontrollanzeigers vorgenommenen Änderungen. Wir haben bereits Änderungen in der Header-Datei gesehen, aber wir müssen noch den Code des Indikators selbst ändern. Außerdem werde ich zeigen, wie man den Quellcode der Header-Datei C_Replay.mqh anpasst. Ohne diese Änderungen kann der Dienst nicht feststellen, dass der Zeitrahmen geändert wurde, indem er einfach auf den Indikator im Chart „schaut“.

Also, bis zum nächsten Mal. Und vergessen Sie nicht, diesen Inhalt sorgfältig zu studieren.

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

Beigefügte Dateien |
Anexo.zip (420.65 KB)
Von der Grundstufe bis zur Mittelstufe: Array (IV) Von der Grundstufe bis zur Mittelstufe: Array (IV)
In diesem Artikel sehen wir uns an, wie wir etwas sehr Ähnliches wie in Sprachen wie C, C++ und Java implementieren können. Ich spreche von der Übergabe einer praktisch unendlichen Anzahl von Parametern innerhalb einer Funktion oder Prozedur. Auch wenn dies ein ziemlich fortgeschrittenes Thema zu sein scheint, kann das, was hier gezeigt wird, meiner Meinung nach von jedem, der die vorherigen Konzepte verstanden hat, leicht umgesetzt werden. Vorausgesetzt, sie wurden wirklich richtig verstanden.
Neuronale Netze im Handel: Transformer mit relativer Kodierung Neuronale Netze im Handel: Transformer mit relativer Kodierung
Selbstüberwachtes Lernen kann ein effektives Mittel sein, um große Mengen ungekennzeichneter Daten zu analysieren. Die Effizienz wird durch die Anpassung der Modelle an die spezifischen Merkmale der Finanzmärkte gewährleistet, was zur Verbesserung der Wirksamkeit der traditionellen Methoden beiträgt. In diesem Artikel wird ein alternativer Aufmerksamkeitsmechanismus vorgestellt, der die relativen Abhängigkeiten und Beziehungen zwischen den Eingaben berücksichtigt.
Neuronale Netze im Handel: Der Contrastive Muster-Transformer Neuronale Netze im Handel: Der Contrastive Muster-Transformer
Der Contrastive Transformer wurde entwickelt, um Märkte sowohl auf der Ebene einzelner Kerzen als auch auf der Basis ganzer Muster zu analysieren. Dies trägt dazu bei, die Qualität der Modellierung von Markttrends zu verbessern. Darüber hinaus fördert der Einsatz des kontrastiven Lernens zum Abgleich der Darstellungen von Kerzen und Mustern die Selbstregulierung und verbessert die Genauigkeit der Prognosen.
Entwicklung eines Replay-Systems (Teil 71): Das richtige Bestimmen der Zeit (IV) Entwicklung eines Replay-Systems (Teil 71): Das richtige Bestimmen der Zeit (IV)
In diesem Artikel werden wir uns ansehen, wie man das, was im vorigen Artikel über unseren Wiedergabe-/Simulationsdienst gezeigt wurde, implementiert. Wie bei vielen anderen Dingen im Leben sind auch hier Probleme vorprogrammiert. Und dieser Fall war keine Ausnahme. In diesem Artikel werden wir die Dinge weiter verbessern. Der hier dargestellte Inhalt ist ausschließlich für Bildungszwecke bestimmt. Die Anwendung sollte unter keinen Umständen zu einem anderen Zweck als zum Erlernen und Beherrschen der vorgestellten Konzepte verwendet werden.