English Русский 中文 Español 日本語 Português
preview
Entwicklung eines Replay-Systems — Marktsimulation (Teil 08): Sperren des Indikators

Entwicklung eines Replay-Systems — Marktsimulation (Teil 08): Sperren des Indikators

MetaTrader 5Beispiele |
297 0
Daniel Jose
Daniel Jose

Einführung

Im vorangegangenen Artikel „Entwicklung eines Replay-Systems — Marktsimulation (Teil 07): Erste Verbesserungen (II)“ wir haben einige Korrekturen und Anpassungen vorgenommen. Dennoch ist ein Fehler aufgetreten, wie das Video im Anhang zu diesem Artikel zeigt.

In diesem Text werden wir uns ansehen, wie man diesen Fehler beheben kann. Dies mag auf den ersten Blick einfach erscheinen, aber es gibt mehrere Schritte, die wir beachten müssen. Der Prozess wird faszinierend und interessant sein. Unser Ziel ist es, dass der Indikator ausschließlich für einen bestimmten Chart und Symbol gilt. Selbst wenn der Nutzer dies versucht, kann er den Indikator nicht auf einen anderen Chart ziehen oder ihn mehr als einmal in einer Sitzung starten.

Ich möchte Sie ermutigen, weiterzulesen, denn der Inhalt verspricht, sehr nützlich zu sein.


Sperren des Indikators auf ein bestimmtes Symbol.

Der erste Schritt besteht darin, den Kontrollindikator mit dem für das Markt-Replay verwendeten Symbol zu verknüpfen. Dieser Schritt scheint zwar einfach zu sein, ist aber notwendig, um unsere Hauptaufgabe zu entwickeln. Schauen wir uns an, wie der Indikatorcode in diesem Zusammenhang aussehen wird:

#property copyright "Daniel Jose"
#property indicator_chart_window
#property indicator_plots 0
//+------------------------------------------------------------------+
#include <Market Replay\C_Controls.mqh>
//+------------------------------------------------------------------+
C_Controls      Control;
//+------------------------------------------------------------------+
int OnInit()
{
        u_Interprocess Info;

        IndicatorSetString(INDICATOR_SHORTNAME, def_ShortName);
        if (_Symbol != def_SymbolReplay)
        {
                ChartIndicatorDelete(ChartID(), 0, def_ShortName);
                return INIT_FAILED;
        }
        if (GlobalVariableCheck(def_GlobalVariableReplay)) Info.Value = GlobalVariableGet(def_GlobalVariableReplay); else Info.Value = 0;
        Control.Init(Info.s_Infos.isPlay);
        
        return INIT_SUCCEEDED;
}
//+------------------------------------------------------------------+
int OnCalculate(const int rates_total, const int prev_calculated, const int begin, const double &price[])
{
        return rates_total;
}
//+------------------------------------------------------------------+
void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam)
{
        Control.DispatchMessage(id, lparam, dparam, sparam);
}
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
        switch (reason)
        {
                case REASON_REMOVE:
                case REASON_CHARTCLOSE:
                        if (_Symbol != def_SymbolReplay) break;
                        GlobalVariableDel(def_GlobalVariableReplay);
                        ChartClose(ChartID());
                        break;
        }
}
//+------------------------------------------------------------------+


Wir prüfen zunächst, ob das betreffende Symbol dasjenige ist, das für das Markt-Replay verwendet wird. Wenn dies nicht der Fall ist, wird der Indikator automatisch geschlossen. Bitte beachten Sie, dass es wichtig ist, den Namen des Indikators zu kennen. Daher ruft die erste Funktion, die während der Initialisierung ausgeführt wird, unseren Indikator auf, was es uns ermöglicht, ihn ohne Komplikationen zu entfernen.

Ein wichtiger Punkt: Wenn Sie ihn aus dem Chart entfernen, erzeugt MetaTrader 5 das Ereignis DeInit. Dieses Ereignis löst die Funktion OnDeInit in Abhängigkeit vom Ereignis REASON_REMOVE aus, das die Entfernung des Indikators aus dem Chart anzeigt. Der Grund dafür ist, dass das Symbol nicht mit dem Symbol übereinstimmt, für das der Indikator konzipiert wurde. Wenn wir den Code nicht erneut überprüfen und verhindern, dass er ausgeführt wird, wird die Symboltabelle geschlossen. Dank unserer Prüfung wird sie jedoch geöffnet bleiben.

Seien Sie nicht überrascht, wenn sich der Code des Indikators von dem im vorigen Artikel vorgestellten Code unterscheidet: Der vorige Text konzentrierte sich auf andere Verbesserungen und Korrekturen. Nachdem ich den Artikel und den Code geschrieben und das Video zu diesem Artikel aufgenommen hatte, stellte ich jedoch fest, dass zwar eines der Probleme behoben wurde, ein anderes jedoch unentdeckt blieb. Deshalb musste ich den Code ändern.

Trotz der Änderungen werden wir hier nicht auf alle Änderungen eingehen. Ein erheblicher Teil davon musste entfernt werden, weil er für die hier erörterte Verriegelung nicht geeignet war. Daher unterscheidet sich der obige Code stark von dem vorherigen. Ich glaube jedoch, dass das in diesem Artikel vermittelte Wissen irgendwann einmal für jemanden nützlich sein kann. Ich habe diesen Artikel gespeichert, um zu zeigen, dass wir alle manchmal Fehler machen, uns aber trotzdem bemühen sollten, die Dinge richtig zu machen.

Damit haben wir den ersten Sperrschritt getan, indem wir sichergestellt haben, dass der Kontrollindikator nur im Chart des Symbols des Markt-Replays existiert. Diese Maßnahme verhindert jedoch nicht, dass mehr als ein Indikator zum selben Chart oder zu verschiedenen Charts hinzugefügt wird, was angepasst werden muss.


Es sollte vermieden werden, mehrere Indikatoren auf demselben Chart zu verwenden.

Wir haben ein Problem gelöst, jetzt müssen wir ein anderes in Angriff nehmen. Hier gibt es verschiedene Lösungen, je nachdem, was wir wirklich wollen und bereit sind zu tun. Ich persönlich sehe keine ideale und endgültige Lösung für dieses Problem. Ich werde jedoch versuchen, einen Ansatz zu präsentieren, mit dem sich der Leser vertraut machen und den er verstehen kann. Das Wichtigste ist, dass die Lösung ausschließlich auf MQL5 basieren wird. Ich habe sogar die Möglichkeit einer externen Kodierung in Betracht gezogen, mich dann aber doch für MQL5 entschieden. Die Idee, auf externe Kodierung zurückzugreifen und DLLs zum Sperren zu verwenden, ist verlockend, aber das wäre zu einfach.

Ich denke, dass wir in MQL5 noch viel mehr lernen müssen, bevor wir auf eine externe DLL zurückgreifen, um die Lücken zu füllen, die die MQL5-Sprache nicht ausfüllt. Dadurch entsteht eine Lösung, die bei der Verwendung von externem Code „sauberer“ aussieht. Aber das hilft nicht, um MQL5 besser zu verstehen. Außerdem kann dies das Missverständnis verstärken, dass MetaTrader 5 eine begrenzte Plattform ist. Missverständnisse und eine unzureichende Nutzung der Plattform schüren dieses Missverständnis.

Um unsere vorgeschlagene Lösung anzuwenden, müssen Sie einige Änderungen vornehmen und andere rückgängig machen. Der erste Schritt besteht darin, die Header-Datei InterProcess.mqh so zu ändern, dass sie die folgende Struktur hat:

#property copyright "Daniel Jose"
//+------------------------------------------------------------------+
#define def_GlobalVariableReplay        "Replay Infos"
#define def_GlobalVariableIdGraphics    "Replay ID"
#define def_SymbolReplay                "RePlay"
#define def_MaxPosSlider                400
#define def_ShortName                   "Market Replay"
//+------------------------------------------------------------------+
union u_Interprocess
{
        union u_0
        {
                double  df_Value;
                long    IdGraphic;
        }u_Value;
        struct st_0
        {
                bool    isPlay;
                int     iPosShift;
        }s_Infos;
};
//+------------------------------------------------------------------+

Dies mag vielen von Ihnen, die mit der Programmierung nicht vertraut sind, etwas seltsam erscheinen, aber überraschenderweise benötigt die obige Struktur nur 8 Byte Speicher. Sie haben vielleicht bemerkt, dass in der Struktur des vorherigen Artikels eine Variable entfernt wurde. Der Grund dafür ist, dass wir diese Sperrmethode nicht mehr verwenden werden. Wir werden einen anderen Ansatz wählen, der etwas komplizierter ist, aber den Kontrollindikator viel effektiver auf eine einzige Karte beschränkt. Es handelt sich um einen sehr spezifischen und definierten Replay-Dienst.

HINWEIS: Es wäre interessant, wenn die Entwickler der MetaTrader 5-Plattform und der MQL5-Sprache dem Dienst die Möglichkeit geben würden, einen Indikator zu einem bestimmten Chart hinzuzufügen oder dem Dienst zu erlauben, ein Skript auf dem Chart aufzurufen und auszuführen. Mit Hilfe von Skripten können wir einen Indikator zu einem bestimmten Chart hinzufügen, aber im Moment ist dies mit Diensten nicht möglich. Wir können ein Chart öffnen, aber wir können ihm keinen Indikator hinzufügen. Beim Versuch, diese Aktion durchzuführen, wird immer eine Fehlermeldung angezeigt, auch wenn wir MQL5-Funktionen verwenden. Zum Zeitpunkt der Erstellung dieses Dokuments ist die Version von MetaTrader 5 Build 3280.

Wichtiger Hinweis: In diesem fortgeschrittenen Stadium der Erstellung des Artikels konnte ich dies erreichen. Als ich diesen Artikel schrieb, konnte ich jedoch keine Hinweise finden, die in dieser Angelegenheit hilfreich sein könnten. Verfolgen Sie also diese Wiederholungs-/Simulationsreihe, um zu sehen, wie ich auf die Lösung gekommen bin.

In diesem Zusammenhang können wir durch Ausführen des folgenden Skripts einen Indikator öffnen und dem Chart hinzufügen:

//+------------------------------------------------------------------+
//| Script program start function                                    |
//+------------------------------------------------------------------+
void OnStart() 
{ 
  
        ENUM_TIMEFRAMES time = PERIOD_D1;
        string szSymbol = "EURUSD";
        long id = ChartOpen(szSymbol, time);
        ChartRedraw(id);

        ChartIndicatorAdd(id, 0, iCustom(szSymbol, time, "Media Movel.ex5"));
}

Wenn wir jedoch das gleiche Skript in einen Dienst umwandeln, erhalten wir nicht das gleiche Ergebnis.

#property service
//+------------------------------------------------------------------+
void OnStart()
{
        ENUM_TIMEFRAMES time = PERIOD_D1;
        string szSymbol = "EURUSD";
        long id = ChartOpen(szSymbol, time);
        ChartRedraw(id);

        ChartIndicatorAdd(id, 0, iCustom(szSymbol, time, "Media Movel.ex5"));
}

Beachten Sie, dass die einzige Änderung hier die Kompilierungseigenschaft ist, die nun angibt, dass der kompilierte Code ein Dienst sein wird. Die einfache Umwandlung eines Skripts in einen Dienst unter Verwendung eines reservierten Wortes ändert die Funktionsweise des Codes vollständig, auch wenn er die gleiche Aufgabe wie zuvor erfüllt. Daher müssen Sie eine Vorlage verwenden, um dem Chart einen Indikator hinzuzufügen. Wenn es möglich wäre, einen Indikator über einen Dienst hinzuzufügen, könnten wir den Indikator als interne Dienstressource zusammenstellen. Beim Öffnen eines Charts wird der Indikator also direkt vom Dienst empfangen, ohne dass er mit anderen Indikatoren vermischt werden muss.

Selbst wenn wir, wie oben gezeigt, das Hinzufügen eines Indikators zu einem Chart verhindern, das nicht mit dem Wiedergabesymbol verbunden ist, kann der Nutzer einen Indikator in ein Chart einfügen, das Markt-Replay als Symbol hat. Dies kann nicht zugelassen werden. Nachdem wir also Änderungen an der Header-Datei Interprocess.mqh vorgenommen haben, können wir uns auf den Dienstcode konzentrieren. Um genauer zu sein, gehen wir zur Header-Datei C_Replay.mqh.

Kurz gesagt, wir werden Folgendes tun: Der Indikator zeigt an, ob der Dienst aktiv ist oder nicht. Wenn sie aktiv ist, wird angezeigt, welches Chart das Hauptchart ist. Zu diesem Zweck müssen wir den Code ändern, der nun wie folgt aussieht:

long ViewReplay(ENUM_TIMEFRAMES arg1)
{
        u_Interprocess info;
                        
        if ((m_IdReplay = ChartFirst()) > 0) do
        {
                if (ChartSymbol(m_IdReplay) == def_SymbolReplay)
                {
                        ChartClose(m_IdReplay);
                        ChartRedraw();
                }
        }while ((m_IdReplay = ChartNext(m_IdReplay)) > 0);
        info.u_Value.IdGraphic = m_IdReplay = ChartOpen(def_SymbolReplay, arg1);
        ChartApplyTemplate(m_IdReplay, "Market Replay.tpl");
        ChartRedraw(m_IdReplay);
        GlobalVariableDel(def_GlobalVariableIdGraphics);
        GlobalVariableTemp(def_GlobalVariableIdGraphics);
        GlobalVariableSet(def_GlobalVariableIdGraphics, info.u_Value.df_Value);
        return m_IdReplay;
}


Zuerst löschen wir die globale Terminalvariable. Anschließend erstellen wir dieselbe Variable erneut und stellen sicher, dass sie nun temporär ist. Dann schreiben wir in diese Variable die Kennung der vom Dienst geöffneten Karte. Damit haben wir die Arbeit mit dem Indikator bereits erheblich vereinfacht, da wir nur noch diesen vom Dienst erfassten Wert analysieren müssen.

Wir sollten jedoch nicht vergessen, dass wir beim Beenden des Dienstes auch die zusätzliche globale Variable entfernen müssen, die wir erstellt haben, wie im nachstehenden Code angegeben:

void CloseReplay(void)
{
        ArrayFree(m_Ticks.Info);
        ChartClose(m_IdReplay);
        SymbolSelect(def_SymbolReplay, false);
        CustomSymbolDelete(def_SymbolReplay);
        GlobalVariableDel(def_GlobalVariableReplay);
        GlobalVariableDel(def_GlobalVariableIdGraphics);
}

Mit diesen Änderungen können wir dem Indikator ein neues Steuerelement hinzufügen.

int OnInit()
{
        u_Interprocess Info;

        IndicatorSetString(INDICATOR_SHORTNAME, def_ShortName);
        if ((_Symbol != def_SymbolReplay) || (!GlobalVariableCheck(def_GlobalVariableIdGraphics)))
        {
                ChartIndicatorDelete(ChartID(), 0, def_ShortName);
                return INIT_FAILED;
        }
        if (GlobalVariableCheck(def_GlobalVariableReplay)) Info.u_Value.df_Value = GlobalVariableGet(def_GlobalVariableReplay); else Info.u_Value.df_Value = 0;
        Control.Init(Info.s_Infos.isPlay);
        
        return INIT_SUCCEEDED;
}


Selbst wenn wir versuchen, einen Kontrollindikator zu einem Symbol des Markt-Replays hinzuzufügen, können wir dies nicht tun, es sei denn, der Wiedergabedienst erstellt eine globale Terminalvariable. Nur wenn diese Variable vorhanden ist, ist es möglich, einen Indikator auch auf dem Replay-Symbol-Chart laufen zu lassen. Damit ist unser Problem jedoch noch nicht gelöst. Wir müssen noch einige Prüfungen durchführen.

Als Nächstes werden wir eine Prüfung implementieren, die den Indikator mit dem entsprechenden Chart verknüpfen wird. Der erste Schritt ist unten dargestellt:

int OnInit()
{
#define macro_INIT_FAILED { ChartIndicatorDelete(ChartID(), 0, def_ShortName); return INIT_FAILED; }
        u_Interprocess Info;

        IndicatorSetString(INDICATOR_SHORTNAME, def_ShortName);
        if ((_Symbol != def_SymbolReplay) || (!GlobalVariableCheck(def_GlobalVariableIdGraphics))) macro_INIT_FAILED;
        Info.u_Value.df_Value = GlobalVariableGet(def_GlobalVariableIdGraphics);
        if (Info.u_Value.IdGraphic != ChartID()) macro_INIT_FAILED;
        if (GlobalVariableCheck(def_GlobalVariableReplay)) Info.u_Value.df_Value = GlobalVariableGet(def_GlobalVariableReplay); else Info.u_Value.df_Value = 0;
        Control.Init(Info.s_Infos.isPlay);
        
        return INIT_SUCCEEDED;
        
#undef macro_INIT_FAILED
}


Aufgrund der häufigen Wiederholung des gleichen Codes in der Initialisierungsfunktion habe ich beschlossen, ein Hilfsmakro zu definieren. So können wir mögliche Fehler beim Schreiben des Codes vermeiden. Kommen wir nun zur ersten Stufe der Sperrung. Bei der Erstellung des Symbolcharts wird die ID des Charts über die globale Terminalvariable übergeben. Auf diese Weise können wir diesen Wert erfassen, um zu bestätigen, dass sich der Kontrollindikator tatsächlich in dem erwarteten Chart befindet, das vom Wiedergabedienst erstellt werden sollte. Wenn versucht wird, einen Indikator zu einem Chart hinzuzufügen, das sich von dem durch den Wiedergabedienst erstellten Chart unterscheidet, wird diese Aktion verweigert.

Auch wenn dies in einer Vielzahl von Situationen hilfreich ist, stoßen wir dennoch auf ein Problem: die Möglichkeit, zusätzliche Kontrollindikatoren in dasselbe Chart einzufügen. Um dieses Problem endgültig zu lösen, werden wir einen etwas unkonventionellen Ansatz wählen. Beachten Sie, dass sich die Plattform zum Zeitpunkt der Erstellung dieses Artikels in der in der Abbildung unten dargestellten Version befindet:



Es ist möglich, dass die Plattform zu dem Zeitpunkt, an dem Sie dies lesen, bereits erheblich aktualisiert wurde, sodass der hier vorgestellte Mechanismus überholt ist. Schauen wir uns jedoch den Mechanismus an, mit dem Sie den Kontrollindikator so konfigurieren können, dass neue Kontrollindikatoren nicht zu demselben Chart hinzugefügt werden können, wodurch er auf ein bestimmtes Chart beschränkt wird. Wenn der Indikator entfernt wird, wird das Chart geschlossen und der Wiedergabedienst wird ebenfalls geschlossen.

Bevor ich zum eigentlichen Code übergehe, ist es wichtig zu wissen, dass der von mir verwendete Mechanismus auf boolescher Logik basiert. Wenn Sie mit diesen Konzepten nicht vertraut sind, empfehle ich Ihnen, sich mit dem Thema zu befassen, da diese Konzepte für die Erstellung und Entwicklung jedes Codes grundlegend sind. Manch einer mag denken, dass die einfachste Lösung darin besteht, eine DLL zu verwenden, um die Situation auf direktere Weise zu lösen. Ich stimme teilweise zu, aber es gibt den Nachteil, eine Lösung in einem externen Programm zu erstellen.

Dies wird uns kein vollständiges und genaues Verständnis für die Grenzen der MQL5-Sprache geben, sodass wir nicht in der Lage sein werden, Vorschläge für ihre Verbesserung zu finden. Viele Leute behaupten, dass C/C++ die leistungsfähigste Sprache ist, und sie haben Recht. Es ist jedoch nicht als eine Einheit entstanden, sondern hat sich weiterentwickelt und neue Funktionen erhalten, als seine Entwickler seine Grenzen ausloteten. Wenn diese Grenzen erreicht waren und die gewünschte Funktionalität nicht realisiert werden konnte, wurden neue Funktionen geschaffen, die zuvor nicht realisierbare Projekte möglich machten. Aus diesem Grund hat sich C/C++ als zuverlässige Sprache erwiesen, die fast jedes Projekt bewältigen kann.

Ich bin sicher, dass MQL5 die gleichen Qualitäten und das gleiche Potenzial wie C/C++ hat. Wir müssen nur die MQL5-Sprache so gut wie möglich studieren und testen. Wenn diese Grenzen dann erreicht sind, können die Entwickler Verbesserungen und neue Funktionen für MQL5 anbieten. Mit der Zeit könnte sie sich zu einer äußerst leistungsfähigen Sprache für die Anwendungsentwicklung für MetaTrader 5 entwickeln.

Um zu verstehen, was wir tatsächlich tun werden, müssen wir die derzeitigen Grenzen der Sprache verstehen, in der die Abstraktion bestimmter Informationen nicht möglich ist. Achten Sie darauf, dass ich NICHT gesagt habe, dass es unmöglich ist, dies zu tun, sondern dass wir keine Abstraktion schaffen können, die uns die Arbeit erleichtert. Dies sind unterschiedliche Konzepte. Es ist eine Sache, die Abstraktion von Daten und Informationen zu entwickeln, und eine andere, die Daten so zu manipulieren, wie wir sie brauchen. Verwechseln Sie diese Dinge nicht.

In C/C++ können wir eine Datenabstraktion erstellen, die es uns ermöglicht, ein bestimmtes Bit in einer Folge von Bits zu isolieren. Dies wird ganz einfach erreicht, siehe unten:

union u01
{
        
double  value;
        struct st
        {
                ulong info : 63;
                bool signal;
        }c;
}data;


Auch wenn dies seltsam und albern erscheint, schaffen wir eine Form der Datenabstraktion, die es uns ermöglicht, das Zeichen der Information zu erkennen und sogar zu ändern. Der Code ist hier und jetzt nicht sehr nützlich, aber lassen Sie uns einen anderen Punkt betrachten. Nehmen wir an, wir wollen Informationen senden und verwenden Bits, um etwas zu steuern. Wir könnten etwas in der Art haben:

struct st
{
        bool PlayPause;
        bool Reservad : 6;
        bool RS_Info;
}ctrl;

In diesem Szenario würde uns die Abstraktionsebene dabei helfen, die Bits zu isolieren, auf die wir tatsächlich zugreifen wollen, wodurch der Code leichter zu lesen wäre. Wie bereits erwähnt, erlaubt es uns MQL5 derzeit jedoch nicht, die vorgestellte Abstraktionsebene zu verwenden. Wir müssen einen anderen Ansatz wählen, weil eine reine Abstraktion nicht möglich ist, aber wenn wir die Grenzen der Sprache verstehen, können wir die Daten trotzdem manipulieren. Daher greifen wir auf die boolesche Logik zurück, um Daten zu verarbeiten, die sonst durch Abstraktion verarbeitet würden. Die Verwendung boolescher Logik macht das Programm jedoch schwieriger zu interpretieren. Wir bewegen uns also von einem High-Level-System mit Abstraktionen zu einem Low-Level-System, in dem die Abstraktion auf boolesche Logik reduziert ist.

Wir werden später auf diese Diskussion zurückkommen. Aber Sie werden sehen, dass der Grund dafür viel berechtigter sein wird als das, was jetzt gezeigt wird. Alles, was ich erwähnt habe, mag unwichtig erscheinen, aber wenn Sie sich den Code des endgültigen Kontrollindikators ansehen, werden Sie verstehen, was ich zu illustrieren versuche. Oft liegt es nicht daran, dass MQL5 bestimmte Dinge nicht kann. Viele Programmierer wollen sich nicht auf eine tiefere Ebene begeben, auf der die Datenabstraktion einfach nicht existiert, was es unmöglich macht, bestimmte Dinge zu erstellen.

Nachstehend finden Sie den vollständigen und umfassenden Code für den Kontrollindikator im derzeitigen Entwicklungsstadium. Der Code ermöglicht es, den Indikator auf dem Chart zu sperren und dem Nutzer zu verbieten, andere Kontrollindikatoren in einer MetaTrader 5-Sitzung hinzuzufügen.

#property copyright "Daniel Jose"
#property description "This indicator cannot be used\noutside of the market replay service."
#property indicator_chart_window
#property indicator_plots 0
//+------------------------------------------------------------------+
#include <Market Replay\C_Controls.mqh>
//+------------------------------------------------------------------+
C_Controls      Control;
//+------------------------------------------------------------------+
#define def_BitShift ((sizeof(ulong) * 8) - 1)
//+------------------------------------------------------------------+
int OnInit()
{
#define macro_INIT_FAILED { ChartIndicatorDelete(id, 0, def_ShortName); return INIT_FAILED; }
        u_Interprocess Info;
        long id = ChartID();
        ulong ul = 1;

        ul <<= def_BitShift;
        IndicatorSetString(INDICATOR_SHORTNAME, def_ShortName);
        if ((_Symbol != def_SymbolReplay) || (!GlobalVariableCheck(def_GlobalVariableIdGraphics))) macro_INIT_FAILED;
        Info.u_Value.df_Value = GlobalVariableGet(def_GlobalVariableIdGraphics);
        if (Info.u_Value.IdGraphic != id) macro_INIT_FAILED;
        if ((Info.u_Value.IdGraphic >> def_BitShift) == 1) macro_INIT_FAILED;
        IndicatorSetString(INDICATOR_SHORTNAME, def_ShortName + "Device");
        Info.u_Value.IdGraphic |= ul;
        GlobalVariableSet(def_GlobalVariableIdGraphics, Info.u_Value.df_Value); 
        if (GlobalVariableCheck(def_GlobalVariableReplay)) Info.u_Value.df_Value = GlobalVariableGet(def_GlobalVariableReplay); else Info.u_Value.df_Value = 0;
        Control.Init(Info.s_Infos.isPlay);
        
        return INIT_SUCCEEDED;
        
#undef macro_INIT_FAILED
}
//+------------------------------------------------------------------+
int OnCalculate(const int rates_total, const int prev_calculated, const int begin, const double &price[])
{
        return rates_total;
}
//+------------------------------------------------------------------+
void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam)
{
        Control.DispatchMessage(id, lparam, dparam, sparam);
}
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
        u_Interprocess Info;
        ulong ul = 1;

        switch (reason)
        {
                case REASON_CHARTCHANGE:
                        ul <<= def_BitShift;
                        Info.u_Value.df_Value = GlobalVariableGet(def_GlobalVariableIdGraphics);
                        Info.u_Value.IdGraphic ^= ul;
                        GlobalVariableSet(def_GlobalVariableIdGraphics, Info.u_Value.df_Value);
                        break;
                case REASON_REMOVE:
                case REASON_CHARTCLOSE:
                        if (_Symbol != def_SymbolReplay) break;
                        GlobalVariableDel(def_GlobalVariableReplay);
                        ChartClose(ChartID());
                        Control.Finish();
                        break;
        }
}
//+------------------------------------------------------------------+


Haben Sie Schwierigkeiten zu verstehen, was wirklich vor sich geht? Sie verstehen nicht, wie unsere Kontrollanzeige funktioniert? Nun, das sind die Punkte. Es gibt keine Abstraktion in diesem Code, die ihn einfach zu verstehen macht. Das liegt daran, dass wir mit MQL5 nicht die gleiche Abstraktionsebene erreichen können, die C/C++ bietet. Daher sind wir gezwungen, auf die boolesche Logik zurückzugreifen, die zwar komplexer ist, aber es uns ermöglicht, Daten zu manipulieren und die gleichen Ergebnisse zu erzielen, die wir durch Abstraktion erreichen würden.

Wenn Sie genau hinsehen, werden Sie feststellen, dass der Typ double 8 Bytes benötigt, um seinen Wert zu speichern. Auch ein Long- (mit Vorzeichen) oder Ulong-Typ (ohne Vorzeichen) nimmt dieselben 8 Bytes in Anspruch. Wenn man bedenkt, dass die über ChartID erhaltene Chart-ID den Long-Typ zurückgibt, haben wir 1 zusätzliches Bit, das genau zur Angabe des Vorzeichens verwendet wird. Wir werden dieses spezielle Bit verwenden, um den Indikator im Chart zu sperren. Und wir werden den Namen des Indikators manipulieren, um zu verhindern, dass ein anderer Kontrollindikator zum selben Chart hinzugefügt wird. Wie? Folgen Sie der Erklärung.

Zunächst legen wir fest, wie viele Bits wir haben werden und mit welchen wir arbeiten werden. Unabhängig davon, ob wir ein 64-Bit-, 32-Bit- oder 128-Bit-System verwenden, wird diese Definition den entsprechenden Typ und die entsprechende Länge verwenden. Auch wenn wir wissen, dass es sich um eine 64-Bit-Version handelt, möchte ich, dass die Einstellungen flexibel und nicht statisch sind. Wir subtrahieren also 1 von diesem Wert und isolieren so das Vorzeichenbit des Long.

Als Nächstes aktivieren wir das niedrigstwertige Bit dieser 64 Bits. Auf diese Weise erhalten wir den Wert 1, der unser Ausgangspunkt sein wird. Als Nächstes führen wir eine 63-Bit-Verschiebung nach links durch, was zu dem Wert 0x80000000000000000000000000000 führt, wobei das höchstwertige Bit den Wert 1 hat, also wahr ist. Dieser Schritt könnte vermieden werden, wenn wir diesen Wert direkt eingeben, aber das Risiko einer falschen Eingabe ist hoch. Auf diese Weise minimieren wir die Möglichkeit von Fehlern.

Sobald wir diesen Wert erhalten, haben wir zwei Möglichkeiten. Die erste besteht darin, das System zu sperren. Die zweite Möglichkeit besteht darin, das System zu entsperren, damit MetaTrader 5 den Indikator bei Bedarf erneut auf das Chart anwenden kann. Die zweite Option ist einfacher, also schauen wir sie uns zuerst an.

Um das System zu entsperren, nehmen wir den Wert der globalen Terminalvariablen, die die Karten-ID enthält, und führen eine XOR-Operation an diesem Wert durch, sodass alle Bits außer dem höchstwertigen erhalten bleiben. Idealerweise sollten wir statt einer XOR-Operation eine NOT-Operation gefolgt von einer AND-Operation durchführen. Dadurch würden alle Informationen im höchstwertigen Bit entfernt. Da diese Operation jedoch nur stattfindet, wenn das angegebene Bit bereits eine Information enthält, sehe ich kein Problem bei der Verwendung der XOR-Operation. Wenn Sie Probleme haben, ersetzen Sie die XOR-Operation durch die folgende Zeile:

Info.u_Value.IdGraphic &= (~ul);

In jedem Fall haben wir unser Ziel erreicht: das Zurücksetzen des höchstwertigen Bits. Auf diese Weise können wir den Wert zurück in eine globale Terminalvariable speichern, bevor MetaTrader 5 versucht, den Kontrollindikator an das Chart zurückzugeben. Damit ist der einfachste Teil des Verschlusssystems abgeschlossen, nun kommen wir zu dem komplexeren Teil.

In dieser Phase ist als Erstes zu prüfen, ob das Chatsymbol mit dem Wiedergabesymbol übereinstimmt und ob der Wiedergabedienst funktioniert. Wenn eine dieser Bedingungen nicht erfüllt ist, wird der Indikator aus dem Chart entfernt. Anschließend wird der Wert in der globalen Terminalvariable erfasst, die die ID des vom Wiedergabedienst erstellten Charts angibt. Anschließend wird dieser Wert mit der ID des Chartfensters verglichen: Sind sie unterschiedlich, wird auch der Indikator gelöscht. Anschließend wird der resultierende Wert um 63 Bits nach rechts verschoben und geprüft, ob dieses Bit aktiv ist. Ist dies der Fall, wird die Kennung wieder entfernt.

Das mag zwar als ausreichend erscheinen, aber es gibt noch ein weiteres Problem, das wir lösen müssen. Dieses spezielle Problem hat mir zusätzliche Arbeit gemacht, während ich alles in MQL5 behalten habe. Ich sage das, weil ich eine bestimmte Zeile in den Code einfügen musste. Ohne diese Funktion fügte das System jedes Mal, wenn ich versuchte, das Hinzufügen eines Indikators zum Chart zu verhindern, dennoch einen Indikator hinzu. Auch wenn es keine Probleme verursachte, blieb es im Anzeigefenster sichtbar, was mich störte. Da kam mir die Idee, den Namen des Indikators zu ändern, aber so, dass nur der erste Indikator diesen neuen Namen erhalten würde. Dieser Indikator wird zusammen mit dem Chart zum Zeitpunkt seiner Erstellung durch den Dienst generiert.

Wenn eine Vorlage auf das Chart angewendet wird, wird der Indikator automatisch aktiviert. Apropos Vorlagen: Es gibt noch einen weiteren Punkt. Aber lassen Sie uns zunächst diese Erklärung zu Ende bringen. Nach all diesen Schritten führen wir schließlich eine ODER-Verknüpfung durch und speichern das Ergebnis in der globalen Terminalvariablen, um den Kontrollindikator zu sperren. Zum Abschluss dieses Themas muss noch eine Änderung vorgenommen werden. Die ganze Arbeit, die wir geleistet haben, wäre nutzlos gewesen und hätte nicht richtig funktioniert, wenn wir die letzte Änderung nicht vorgenommen hätten. Ich könnte diese Information überspringen und mich damit brüsten, etwas getan zu haben, was viele für unmöglich halten. Wenn dann jemand versucht, das Gleiche zu tun, würde er wahrscheinlich scheitern. Aber ich bin nicht hier, um zu prahlen oder zu behaupten, dass ich unentbehrlich bin. Ich suche nicht nach einer solchen Anerkennung, sondern möchte im Gegenteil zeigen, dass es möglich ist, über das hinauszugehen, was viele Menschen für möglich halten, und dass die Lösung eines Problems manchmal an unerwarteter Stelle zu finden ist.

Wenn Sie alle Anweisungen befolgen und versuchen, den gesamten Prozess durchzuführen, werden Sie feststellen, dass Sie in fast allen Aspekten erfolgreich sein können, außer in einem. Egal wie sehr Sie sich bemühen, Sie können nicht verhindern, dass neue Kontrollindikatoren zu dem vom Wiedergabedienst erstellten Chart hinzugefügt werden. Das fragen Sie sich vielleicht: „Aber warum? Ich habe alle Schritte befolgt und sogar eine eigene Zeile dafür eingefügt. Machst du Witze?“

Ich würde gerne sagen, dass dies ein Scherz ist, aber das ist es nicht. In der Tat werden Sie diese Situation nicht vermeiden können. Dies ist auf einen „Fehler“ (beachten Sie die Anführungszeichen) im System zurückzuführen. Ich bin mir nicht sicher, was genau dies verursacht oder wie es passieren könnte, aber sehen Sie sich den folgenden Code an:

long ViewReplay(ENUM_TIMEFRAMES arg1)
{
        u_Interprocess info;
                                
        if ((m_IdReplay = ChartFirst()) > 0) do
        {
                if (ChartSymbol(m_IdReplay) == def_SymbolReplay)
                {
                        ChartClose(m_IdReplay);
                        ChartRedraw();
                }
        }while ((m_IdReplay = ChartNext(m_IdReplay)) > 0);
        info.u_Value.IdGraphic = m_IdReplay = ChartOpen(def_SymbolReplay, arg1);
        ChartApplyTemplate(m_IdReplay, "Market Replay.tpl");
        ChartRedraw(m_IdReplay);
        GlobalVariableDel(def_GlobalVariableIdGraphics);
        GlobalVariableTemp(def_GlobalVariableIdGraphics);
        GlobalVariableSet(def_GlobalVariableIdGraphics, info.u_Value.df_Value);
        return m_IdReplay;
}


Wenn diese Zeile ausgeführt wird, geschieht etwas Seltsames. Ich habe mir lange den Kopf zerbrochen, bis ich mich entschloss, die Vorlage zu öffnen, um zu verstehen, wie sie funktioniert. Da wurde mir klar, dass wir das System zwingen sollten, keine Vorlage zu verwenden, sondern automatisch einen Indikator zu erstellen und auf das Chart anzuwenden, wie ich am Anfang des Artikels erklärt habe. Ich habe dort gezeigt, dass die Verwendung eines Dienstes es uns nicht erlaubt, Indikatoren zum Chart hinzuzufügen, während Charts nur hinzugefügt werden können, wenn wir Skripte verwenden. Das System funktionierte, aber als ich die Vorlage verwenden wollte, funktionierte es nicht.

Ich habe lange gesucht, um herauszufinden, was das Problem ist. Ich habe niemanden gefunden, der die Antwort wusste. Mit Hilfe von Techniken und Fähigkeiten, die ich in jahrelanger Programmiertätigkeit erworben habe, habe ich das Problem jedoch erkannt. Wenn Sie sich die vorherigen Artikel und Daten in der Vorlagendatei ansehen, finden Sie die folgenden Daten:

<indicator>
name=Custom Indicator
path=Indicators\Market Replay.ex5
apply=0
show_data=1
scale_inherit=0
scale_line=0
scale_line_percent=50
scale_line_value=0.000000
scale_fix_min=0
scale_fix_min_val=0.000000
scale_fix_max=0
scale_fix_max_val=0.000000
expertmode=0
fixed_height=-1



Auf den ersten Blick mag es so aussehen, als sei alles richtig, und das ist auch tatsächlich so. Ändert man jedoch etwas im vorherigen Fragment, beginnt das System wie erwartet zu funktionieren, indem es den Kontrollindikator sperrt und nicht zulässt, dass weitere hinzugefügt werden.

Die korrigierte Fassung ist unten abgebildet:

<indicator>
name=Custom Indicator
path=Indicators\Market Replay.ex5
apply=1
show_data=1
scale_inherit=0
scale_line=0
scale_line_percent=50
scale_line_value=0.000000
scale_fix_min=0
scale_fix_min_val=0.000000
scale_fix_max=0
scale_fix_max_val=0.000000
expertmode=0
fixed_height=-1

Ich werde Ihnen nicht genau den geänderten Punkt zeigen. Ich möchte, dass Sie es sehen und versuchen zu verstehen. Aber keine Sorge, die beigefügte Code-Version sorgt für einen ordnungsgemäßen Betrieb des Systems.


Schlussfolgerung

Wie ich bereits erwähnt habe, könnte ich diese Informationen weglassen, und wenn mich jemand fragt, warum das System nicht funktioniert, könnte ich so tun, als wäre ich ein besserer Programmierer. Aber ich gebe nicht gerne an. Ich möchte, dass die Menschen lernen, verstehen und sich motiviert fühlen, Lösungen zu finden. Wann immer möglich, sollten Sie Ihr Wissen weitergeben, denn so tragen wir zur Evolution bei. Wissen zu verbergen ist kein Zeichen von Überlegenheit, sondern von Angst oder mangelndem Vertrauen.

Diese Phase habe ich überwunden. Deshalb erkläre ich, wie das funktioniert. Ich hoffe, dass viele dazu inspiriert werden, das Gleiche zu tun. Herzliche Grüße an alle und bis zum nächsten Artikel. Unsere Arbeit hat gerade erst begonnen.




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

Beigefügte Dateien |
Market_Replay.zip (13058.5 KB)
Neuronale Netze leicht gemacht (Teil 39): Go-Explore, ein anderer Ansatz zur Erkundung Neuronale Netze leicht gemacht (Teil 39): Go-Explore, ein anderer Ansatz zur Erkundung
Wir setzen die Untersuchung der Umgebung in Modellen des verstärkten Lernens fort. Und in diesem Artikel werden wir uns einen weiteren Algorithmus ansehen – Go-Explore. Er ermöglicht es Ihnen, die Umgebung in der Phase der Modellbildung effektiv zu erkunden.
Der erste Einsatz des MetaTrader VPS: eine Schritt-für-Schritt-Anleitung Der erste Einsatz des MetaTrader VPS: eine Schritt-für-Schritt-Anleitung
Jeder, der Handelsroboter oder Signalabonnements verwendet, erkennt früher oder später die Notwendigkeit, einen zuverlässigen 24/7-Hosting-Server für seine Handelsplattform zu mieten. Wir empfehlen die Verwendung von MetaTrader VPS aus einer Reihe von Gründen. Sie können den Dienst bequem über Ihr MQL5.community-Konto bezahlen und das Abonnement verwalten.
Neuronale Netze leicht gemacht (Teil 40): Verwendung von Go-Explore bei großen Datenmengen Neuronale Netze leicht gemacht (Teil 40): Verwendung von Go-Explore bei großen Datenmengen
In diesem Artikel wird die Verwendung des Go-Explore-Algorithmus über einen langen Trainingszeitraum erörtert, da die Strategie der zufälligen Aktionsauswahl mit zunehmender Trainingszeit möglicherweise nicht zu einem profitablen Durchgang führt.
Tests von verschiedenen gleitenden Durchschnitten, um zu sehen, wie aufschlussreich sie sind Tests von verschiedenen gleitenden Durchschnitten, um zu sehen, wie aufschlussreich sie sind
Wir alle wissen, wie wichtig der Indikator des gleitenden Durchschnitts für viele Händler ist. Es gibt noch andere Arten von gleitenden Durchschnitten, die für den Handel nützlich sein können. Wir werden diese Arten in diesem Artikel identifizieren und einen einfachen Vergleich zwischen jeder von ihnen und dem beliebtesten einfachen gleitenden Durchschnitt anstellen, um zu sehen, welcher die besten Ergebnisse liefern kann.