English Русский Español 日本語 Português
preview
Entwicklung eines Replay System (Teil 28): Expert Advisor Projekt — Die Klasse C_Mouse (II)

Entwicklung eines Replay System (Teil 28): Expert Advisor Projekt — Die Klasse C_Mouse (II)

MetaTrader 5Tester | 13 März 2024, 17:41
191 0
Daniel Jose
Daniel Jose

Einführung

Im vorherigen Artikel „Entwicklung eines Replay-Systems (Teil 27): Expert Advisor Projekt (II)“, haben wir mit der Entwicklung einer neuen Klasse begonnen. Gegen Ende des Artikels wurde ich jedoch davon überzeugt, dass es wichtig ist, einen anderen Ansatz für die Programmierung zu präsentieren. Dies geschieht nur aus Neugierde, um sich der natürlichen Sprache zu nähern.

Für diejenigen, die schon lange programmieren, mag das, was wir im Folgenden zeigen, nicht viel Sinn ergeben. Warum sollte man sich die Mühe machen, die Programmierung der natürlichen Sprache anzunähern? Die Antwort ist einfach: Sie programmieren nicht für die Maschine, sondern für andere Programmierer. Als es die ersten Systeme gab, die etwas berechnen konnten, hing alles davon ab, wie gut die Ingenieure das Projekt kannten. Das war die Realität in den Anfängen der Computertechnologie, als es noch keine Terminals zum Programmieren gab.

Im Laufe der Entwicklung, als immer mehr Menschen daran interessiert waren, etwas zu erschaffen, entstanden neue Ideen und Wege der Programmierung, die das frühere Wechseln der Steckverbindungen ersetzten. Zu diesem Zeitpunkt erschienen die ersten Terminals. Mit der Zeit war die Programmierung, die ursprünglich ausschließlich im Binärformat erfolgte, nicht mehr die Norm. Dies geschah, weil sich die Programme sehr schnell weiterentwickelten, was dazu führte, dass ein effizienterer Weg gefunden werden musste, um zu lesen, was programmiert wurde. Zu dieser Zeit kam die Assemblersprache auf. Dieses leistungsstarke Framework verwandelt komplexe binäre Codes in Form von OpCodes oder mnemonischen Codes in etwas besser Lesbares. Die Programme wurden immer komplexer und erforderten immer mehr Code, und es entstanden die ersten höheren Sprachen.

Es bestand keine Notwendigkeit mehr, sich direkt mit OpCodes zu befassen, da es möglich wurde, eine Sprache zu verwenden, die der natürlichen Sprache näher kommt. Zunächst wurden diese Sprachen in erster Linie zur Erstellung und Beschreibung mathematischer Konzepte entwickelt, d. h. sie dienten hauptsächlich dazu, die Übersetzung von Formeln in computerlesbare Formen zu erleichtern. Dieser Vorgang musste nicht mehr manuell von einer Person durchgeführt werden. Damit begann eine neue Ära - die Ära der Compiler, die die menschliche Sprache in eine Sprache übersetzen, die eine Maschine verstehen kann. Ich programmiere seit Jahren auf diese Weise, um zu erklären, wie Programme entstehen, und um mehr Menschen dazu zu bringen, es zu lernen und ihre Ideen in etwas umzusetzen, das ein Computer verstehen kann. Ich habe jedoch festgestellt, dass viele Menschen Schwierigkeiten haben, einige Konzepte zu verstehen, da es beim Programmieren hauptsächlich darum geht, Symbole zu kombinieren und zu verwenden, um auszudrücken, was wir schaffen wollen.

Da die Sprache MQL5 jedoch C/C++ ähnelt und die Möglichkeit bietet, Dinge auf eine Weise zu tun, die den Code besser lesbar macht, ist sie ideal, um etwas anderes zu demonstrieren. Dann, nachdem ich die Dinge analysiert hatte, wurde mir klar, dass ich Enthusiasten helfen konnte, zu verstehen, was programmiert wurde, ohne den Code vollständig zu verstehen. Also beschloss ich, die Art und Weise, wie der Code gestaltet wurde, zu ändern. Letztendlich wird alles vom Compiler verstanden, sodass es für den Compiler keine Rolle spielt. Dies ist jedoch für Enthusiasten von großer Bedeutung, da die Sprache dann viel näher an der natürlichen Sprache ist. Obwohl der Code auf den ersten Blick seltsam und ungewöhnlich erscheinen mag, ist er für einen Anfänger viel leichter zu verstehen.

Ich lade Sie ein, mich in diesen kurzen Momenten zu begleiten, in denen wir die MQL5-Sprache auf eine Weise verwenden, die der natürlichen Sprache viel näher kommt.


Erstellen der Datei Defines.mqh

Vor zwei Artikeln haben wir uns mit der Kompilieranweisung #define beschäftigt. Ich habe erwähnt, dass es einen Sonderfall gibt, in dem die Definition am Ende der Datei nicht entfernt wird, obwohl ich damals die praktische Anwendung dieser Verwendung der Richtlinie nicht gezeigt habe, weil es keine korrekte Methode dafür gab. Deshalb haben wir diese Frage offen gelassen. Der springende Punkt hier ist, dass Sie, wenn Sie einige Dinge und Konzepte über MQL5 verstehen, weil Sie wissen, dass es von C/C++ abgeleitet ist, dann können Sie bestimmte Operationen in der Sprache ohne große Schwierigkeiten durchführen. Dies würde den Code sowohl für Nicht-Programmierer, die all diese Symbole nicht verstehen können, als auch für Programmierer, die verstehen müssen, was getan wird, besser lesbar machen.

Eine Möglichkeit besteht darin, den Code mit Hilfe der Richtlinie #define lesbarer zu machen. Es gibt jedoch einige Einschränkungen. Auf den ersten Blick mag das ziemlich seltsam erscheinen, aber es geht nur darum, einige Symbole oder Symbolkombinationen in der Syntax der MQL5-Sprache korrekt und ohne Übertreibung zu definieren. Wir schaffen keine neue Sprache. Wir ersetzen lediglich einige bestehende Symbole in einer ziemlich einheitlichen Weise. Auf diese Weise entsteht die Datei Defines.mqh. Sie wird Definitionen enthalten, mit denen die zuvor symbolische Syntax in ein Wort oder eine Definition umgewandelt wird, die für den menschlichen Leser aussagekräftiger ist.

Hier ist der vollständige Inhalt dieser Datei:

//+------------------------------------------------------------------+
#property copyright "Daniel Jose"
/*+------------------------------------------------------------------+
Definitions for increasing the level of the coded language in MQL5, for
more details see the article at the following link:
https://www.mql5.com/en/articles/11349
+------------------------------------------------------------------+*/
#define equal =
#define different !=
#define identical ==
#define and &&
//+------------------------------------------------------------------+

Was macht dieses kleine Stück Code, das praktisch keine Funktion hat, eigentlich? Um dies zu verstehen, müssen Sie es in der Praxis anwenden. Jede der Codezeilen stellt einen Zusatz dar, der die Lesbarkeit des Codes verbessern soll. Selbst eine Person mit wenig Erfahrung wird in der Lage sein, wenn auch nicht alle, so doch einige Aspekte der Programmierung zu verstehen. Die Lesbarkeit des Codes ist etwas, das wir immer verbessern müssen. Es ist besser, einen besser lesbaren Code zu haben, auch wenn das im Vorfeld etwas mehr Arbeit bedeutet. Aber am Ende wird es sich lohnen, denn niemand möchte mit einem Code zu tun haben, der wie eine Hieroglyphe aussieht und selbst für den Autor unzugänglich ist. Und wenn diese Art des Codeschreibens verloren geht, weil entweder die Sprache oder die Person nicht mehr existiert, geht auch das gesamte darin enthaltene Wissen verloren.

Eine Möglichkeit, den Code besser lesbar zu machen, ist die Verwendung von Kommentaren. Wenn Sie sich jedoch die Codes in meinen Artikeln ansehen, werden Sie feststellen, dass ich sie nicht kommentiere. Das liegt daran, dass solche Codes meiner Meinung nach recht einfach und gut lesbar sind. Können Sie diese Codes auch ohne die Beschreibungen in den Artikeln verstehen? Das ist genau der Punkt. Irgendwann kann der Code so komplex werden, dass er ohne eine gute Struktur völlig unlesbar wird, und selbst ich werde nicht in der Lage sein, ihn zu pflegen und zu verbessern.

Sobald die Datei Defines.mqh erstellt ist, müssen wir den Compiler irgendwie zwingen, sie zu verwenden. Zu diesem Zweck fügen wir diese Datei in eine der grundlegenden Dateien für den Aufbau des gesamten Systems ein - C_Terminal.mqh. Der Teil C_Terminal.mqh mit Includes enthält die folgende Zeile:

#include "Macros.mqh"
#include "..\..\Defines.mqh"

Seien Sie vorsichtig, denn dies ist sehr wichtig. Wir deklarieren die Datei Macros.mqh in doppelten Anführungszeichen, was dem Compiler mitteilt, dass sich diese Datei im selben Ordner wie die Datei C_Terminal.mqh befindet. Wenn wir jedoch die Datei Defines.mqh einbinden, wird sie ebenfalls in Anführungszeichen gesetzt, allerdings mit einer Besonderheit. Dieser Unterschied besteht in ..\, der dem Compiler mitteilt, wie viele Ebenen, ausgehend von dem Verzeichnis, in dem sich C_Terminal.mqh befindet, wir in der Verzeichnisstruktur nach oben gehen müssen, um die Datei Defines.mqh zu finden. In diesem Fall müssen wir zwei Ebenen nach oben gehen, da die Verzeichnisstruktur verschiedene Code-Ebenen enthält. Die Datei Defines.mqh befindet sich also im Stammverzeichnis der Projektstruktur. Wenn sich aus irgendeinem Grund das Stammverzeichnis des Projekts ändert, hat dies keine Auswirkungen auf den Compiler, der die Datei Defines.mqh immer am gleichen Ort sucht.

Diese Art der Organisation ist sehr interessant, vor allem, wenn man anfängt, seine Kopfzeile so zu organisieren, dass man Dinge leicht finden und planen kann. So können wir unseren Code oder einen Teil davon einfach weitergeben, ohne uns Sorgen machen zu müssen, dass eine bestimmte Datei fehlt. Wenn alles gut organisiert ist und der Code sofort verteilt werden kann, macht das unser Leben sehr viel einfacher. Nachdem wir nun erklärt haben, wie man die Datei Defines.mqh in das System einbindet, können wir mit ihrer Verwendung beginnen. Sie können die Anzahl der Definitionen erhöhen, um die Lesbarkeit zu verbessern. Nun, die derzeitigen Definitionen sind für unsere Zwecke ausreichend. Aber um wirklich zu verstehen, wie sehr dies dazu beiträgt, den Code lesbarer zu machen und auftretende Probleme einfacher zu lösen, empfehle ich Ihnen, sich das folgende Beispiel anzusehen:

if (m_Info.Study == eStudyExecute) ExecuteStudy(memPrice);
if (m_Info.Study identical eStudyExecute) ExecuteStudy(memPrice);
if_case m_Info.Study identical eStudyExecute then ExecuteStudy(memPrice);

Diese drei Zeilen bedeuten dasselbe, und der Compiler wird sie auf dieselbe Weise interpretieren und genau denselben Code erzeugen. In diesem Fall ist die Datei Defines.mqh jedoch nicht in der Lage, dem Compiler mitzuteilen, was er tun soll. Wir müssen zwei neue Definitionen hinzufügen:

#define if_case if(
#define then )

Wenn Sie diese beiden Zeilen in die Datei Defines.mqh einfügen, kann der Compiler die drei Zeilen des Beispiels richtig verstehen. Achten Sie auf die hervorgehobene Zeile. Hier haben wir eine Sprache, die der natürlichen Sprache sehr ähnlich ist. Von den drei dargestellten Zeilen ist diese der natürlichen Sprache am ähnlichsten und kann daher als die höchste der drei Ebenen bezeichnet werden. Das habe ich gemeint, als ich sagte, dass Code entweder High-Level oder Low-Level ist. Für den Compiler ändert sich nichts, aber für ein menschliches Auge ist die dritte Zeile viel einfacher. Dies ist ein einfacher Fall, aber lassen Sie uns einen komplexeren Fall betrachten, in dem der gesamte Code wie in der Abbildung gezeigt geschrieben wird, ohne zu vergessen, dass auch die in früheren Artikeln gezeigten Codes ähnliche Änderungen erfahren haben, um sie besser lesbar zu machen. Das ist für den Moment.

Kehren wir zu dem Punkt zurück, an dem wir im letzten Artikel aufgehört haben. Wir werden uns die letzte Funktion in der Klasse C_Mouse ansehen.


DispatchMessage: Kommunikation mit der Außenwelt

Alle Klassen, die auf die eine oder andere Weise Ereignisse von der MetaTrader 5-Plattform empfangen müssen, haben diese DispatchMessage-Funktion in ihrem Portfolio. Damit wird die Klasse in der Lage sein, erzeugte Ereignisse zu empfangen und darauf zu reagieren. Das Wichtigste ist zu verstehen, dass MetaTrader 5 ein ereignisbasiertes Programm ist, d.h. ein Programm vom Typ ECHTZEIT. Die Arbeit damit ist, gelinde gesagt, ziemlich schwierig. Daher muss jeder Code sehr spezifisch sein, wenn es um solche Ereignisse geht. Doch bevor wir uns die Funktion DispatchMessage ansehen, müssen wir uns mit dem anderen Code vertraut machen, der in der Klasse C_Terminal vorkommt. Dieser Code ist unten zu sehen:

const double AdjustPrice(const double arg) const { return NormalizeDouble(round(arg / m_Infos.PointPerTick) * m_Infos.PointPerTick, m_Infos.nDigits); }

Dieser Code hätte auch in der Klasse C_Mouse platziert werden können, aber aufgrund anderer Faktoren habe ich mich entschieden, ihn in der Klasse C_Terminal zu platzieren. Dieser Code ist einfach eine Faktorisierung, die darauf abzielt, den Preis so anzupassen, dass er immer gleich dem vom Handelsserver erwarteten Wert ist. Häufig werden Aufträge vom Server abgelehnt, weil der angegebene Preis falsch ist. Viele Leute weigern sich, einen EA zu erstellen, nur weil sie beim Versuch, einen Auftrag an den Handelsserver zu senden, eine Fehlermeldung erhalten. Einige Anlageklassen haben eine einfachere Faktorisierung für die Preisanpassung, während andere eine sehr viel komplexere haben, die mehrere Aspekte umfasst. In der Praxis verwaltet die oben genannte Funktion jedoch alle diese Faktoren und stellt sicher, dass der Preis immer angemessen ist, unabhängig von der Art des verwendeten Vermögenswerts. Dies ist für uns sehr wichtig, denn um einen EA im Replay/Simulationssystem zu erstellen und zu verwenden, ist es notwendig, dass der Preis unabhängig vom Vermögenswert korrekt ist, sei es auf einem DEMO-Konto oder auf einem REAL-Konto. Daher können Sie diese Funktion nutzen und sogar missbrauchen. Wenn Sie dies tun, können Sie sehen, wie sich das System an jede Art von Markt und Vermögenswert anpasst. Nutzen und studieren Sie also diese Fähigkeit.

Nachdem wir nun eine Funktion gesehen haben, die den Preis nach Bedarf anpasst, können wir uns die Funktion DispatchMessage ansehen. Der vollständige Code ist nachstehend aufgeführt:

virtual void DispatchMessage(const int id, const long &lparam, const double &dparam, const string &sparam)
   {
      int w equal 0;
      static double memPrice equal 0;
                                
      C_Terminal::DispatchMessage(id, lparam, dparam, sparam);
      switch (id)
      {
         case (CHARTEVENT_CUSTOM + ev_HideMouse):
            ObjectSetInteger(GetInfoTerminal().ID, def_NameObjectLineH, OBJPROP_COLOR, clrNONE);
            break;
         case (CHARTEVENT_CUSTOM + ev_ShowMouse):
            ObjectSetInteger(GetInfoTerminal().ID, def_NameObjectLineH, OBJPROP_COLOR, m_Info.corLineH);
            break;
         case CHARTEVENT_MOUSE_MOVE:
            ChartXYToTimePrice(GetInfoTerminal().ID, m_Info.Data.Position.X equal (int)lparam, m_Info.Data.Position.Y equal (int)dparam, w, m_Info.Data.Position.dt, m_Info.Data.Position.Price);
            ObjectMove(GetInfoTerminal().ID, def_NameObjectLineH, 0, 0, m_Info.Data.Position.Price equal AdjustPrice(m_Info.Data.Position.Price));
            if (m_Info.Study different eStudyNull) ObjectMove(GetInfoTerminal().ID, def_NameObjectLineV, 0, m_Info.Data.Position.dt, 0);
            m_Info.Data.ButtonStatus equal (uint) sparam;
            if (CheckClick(eClickMiddle) and ((color)ObjectGetInteger(GetInfoTerminal().ID, def_NameObjectLineH, OBJPROP_COLOR) different clrNONE)) CreateStudy();
            if (CheckClick(eClickLeft) and (m_Info.Study identical eStudyCreate))
            {
               ChartSetInteger(GetInfoTerminal().ID, CHART_MOUSE_SCROLL, false);
               ObjectMove(GetInfoTerminal().ID, def_NameObjectLineT, 0, m_Info.Data.Position.dt, memPrice equal m_Info.Data.Position.Price);
               m_Info.Study equal eStudyExecute;
            }
            if (m_Info.Study identical eStudyExecute) ExecuteStudy(memPrice);
            break;
         case CHARTEVENT_OBJECT_DELETE:
            if (sparam identical def_NameObjectLineH) CreateLineH();
            break;
      }
   }

Vieles von dem, was im obigen Code vor sich geht, kann man auch ohne viel Programmiererfahrung verstehen, wenn man sich den Code ansieht. Wie können Sie den Code verstehen, ohne über umfangreiche Programmierkenntnisse zu verfügen? Wenn Sie in einer Sprache programmieren, die sich der natürlichen Sprache annähert, wird der Code leichter zu verstehen sein. Auf diese Weise können auch Personen, die keine Ahnung vom Programmieren haben, verstehen, was dort programmiert wird. Tatsächlich ist vieles von dem, was dort passiert, sehr einfach zu verstehen, so einfach, dass es nicht einmal einer ausführlichen Erklärung bedarf. Schauen Sie sich bitte die folgenden Zeilen an.

int w equal 0;
static double memPrice equal 0;

Obwohl der Code auf diese Weise deklariert ist, sollten Sie ihn so lesen, wie er geschrieben ist. Der Compiler wird diesen Code wie folgt interpretieren:

int w = 0;
static double memPrice = 0;

Wie Sie sehen können, gibt es keinen Unterschied. In beiden Fällen konnte jeder den Code verstehen. Im ersten Fall handelt es sich um ein „wörtliches“ Format. Aber keine Sorge, wir fangen gerade erst an. Dieses Beispiel spiegelt nicht alle unsere Möglichkeiten zur Verbesserung der Lesbarkeit des Codes wider.

Schauen wir uns einen anderen Code an. Dieser Teil steht nicht in direktem Zusammenhang mit der Lesbarkeit des Codes, bedarf aber dennoch der Klärung.

case (CHARTEVENT_CUSTOM + ev_HideMouse):
   ObjectSetInteger(GetInfoTerminal().ID, def_NameObjectLineH, OBJPROP_COLOR, clrNONE);
   break;

Manchmal müssen wir die Kurslinie im Chart ausblenden. Diejenigen, die das in der Serie über die Entwicklung eines EA von Grund auf gezeigte Ordersystem verfolgt haben, haben gesehen, dass die Kurslinie an einigen Stellen verdeckt war. In diesem speziellen Code wurde dies durch den Aufruf einer bestimmten Methode erreicht, die das Ausblenden der Zeile bewirkt. Für den Moment werden wir jedoch eine Nachricht verwenden, die an die Klasse C_Mouse gesendet wird, um die Preiszeile auszublenden. Ich habe mich für eine Nachricht anstelle einer Methode entschieden, weil ich unter anderem ein modulares und leicht portierbares System aufbauen möchte. Wir haben also eine weitere Nachricht, die die gleichen Kriterien erfüllt. Es ist unten aufgeführt:

case (CHARTEVENT_CUSTOM + ev_ShowMouse):
   ObjectSetInteger(GetInfoTerminal().ID, def_NameObjectLineH, OBJPROP_COLOR, m_Info.corLineH);
   break;

Machen Sie sich noch keine Gedanken darüber, wie Sie diese Nachrichten verwenden sollen. Wir werden dies in Zukunft berücksichtigen. Tatsächlich ist es einfacher, sie zu verstehen, wenn man sich vorstellt, dass es sich bei beiden Nachrichten um Methoden oder Funktionen handeln könnte. Anstatt jedoch eine öffentliche Methode oder Funktion für diesen Zweck hinzuzufügen, werden wir alles an einem zentralen Punkt konzentrieren: der Nachrichtenverarbeitungsfunktion. Nur in besonderen Fällen werden wir eine andere Methode anwenden.

Diese Ereignisse werden durch einen speziellen Aufruf ausgelöst, auf den ich später noch näher eingehen werde, da ich glaube, dass viele keine Ahnung haben, wie das funktioniert. Dies sind keine Ereignisse, die von der MetaTrader 5-Plattform ausgelöst werden, sondern Ereignisse, die von unserem Code zu einem ganz bestimmten Zeitpunkt ausgelöst werden, um eine bestimmte Aktion durchzuführen. Aber wir müssen auch mit Ereignissen arbeiten, die von der MetaTrader 5-Plattform kommen. Dies geschieht wie folgt:

C_Terminal::DispatchMessage(id, lparam, dparam, sparam);

Diese Codezeile aus der Funktion DispatchMessage der Klasse C_Mouse leitet Aufrufe an die Klasse C_Terminal weiter, sodass wir dies in anderen Teilen des Codes nicht tun müssen. Dies hilft, Programmierfehler zu vermeiden und den Code zu standardisieren, sodass er schneller programmiert, analysiert, erstellt und korrigiert werden kann. Allerdings werden nicht alle Ereignisse in der Klasse C_Terminal behandelt: einige werden lokal, d. h. innerhalb der Klasse, mit der wir arbeiten, gelöst. Ein Beispiel für ein solches Ereignis ist unten dargestellt:

case CHARTEVENT_OBJECT_DELETE:
   if (sparam identical def_NameObjectLineH) CreateLineH();
   break;

Dies ist der Code für das Ereignis, das für den Leser bestimmt ist. Ich denke, Sie können verstehen, dass dies vom Compiler wie folgt interpretiert wird:

case CHARTEVENT_OBJECT_DELETE:
   if (sparam == def_NameObjectLineH) CreateLineH();
   break;

Unabhängig davon, wie der Code dargestellt wird, ist sein Ergebnis das folgende: Wenn ein Objekt aus der Symboltabelle entfernt wird, löst die Plattform eine Nachricht oder vielmehr ein Ereignis aus. Sie informiert das Programm, das diese Art von Nachricht angefordert hat, dass das Objekt aus dem Chart entfernt wurde. Beim Eintreffen des Ereignisses CHART_EVENT_OBJECT_DELETE muss das Programm den darin enthaltenen Code ausführen. Wenn der Name des in der Konstante param angegebenen Objekts mit dem zu prüfenden Objekt übereinstimmt, wird der ausgeführte Code die Preislinie neu erstellen.

Jetzt haben wir das Ereignis CHART_EVENT_MOUSE_MOVE, das etwas erweitert wurde. Obwohl es umfangreicher ist, ist es nicht sehr kompliziert. Sie können den größten Teil des unten stehenden Codes auch ohne Programmierkenntnisse verstehen, wenn Sie versuchen, jede Zeile buchstäblich zu lesen. Probieren Sie es aus und sagen Sie mir, ob es einfacher oder schwieriger ist, zu verstehen, was wir tun. Es macht nichts, dass Sie nicht den gesamten Code verstehen können, solange Sie versuchen, so viel wie möglich ohne zusätzlichen Aufwand zu verstehen.

case CHARTEVENT_MOUSE_MOVE:
   ChartXYToTimePrice(GetInfoTerminal().ID, m_Info.Data.Position.X equal (int)lparam, m_Info.Data.Position.Y equal (int)dparam, w, m_Info.Data.Position.dt, m_Info.Data.Position.Price);
   ObjectMove(GetInfoTerminal().ID, def_NameObjectLineH, 0, 0, m_Info.Data.Position.Price equal AdjustPrice(m_Info.Data.Position.Price));
   if (m_Info.Study different eStudyNull) ObjectMove(GetInfoTerminal().ID, def_NameObjectLineV, 0, m_Info.Data.Position.dt, 0);
   m_Info.Data.ButtonStatus equal (uint) sparam;
   if (CheckClick(eClickMiddle) and ((color)ObjectGetInteger(GetInfoTerminal().ID, def_NameObjectLineH, OBJPROP_COLOR) different clrNONE)) CreateStudy();
   if (CheckClick(eClickLeft) and (m_Info.Study identical eStudyCreate))
   {
      ChartSetInteger(GetInfoTerminal().ID, CHART_MOUSE_SCROLL, false);
      ObjectMove(GetInfoTerminal().ID, def_NameObjectLineT, 0, m_Info.Data.Position.dt, memPrice equal m_Info.Data.Position.Price);
      m_Info.Study equal eStudyExecute;
   }
   if (m_Info.Study identical eStudyExecute) ExecuteStudy(memPrice);
   break;

Abgesehen von den Momenten, in denen wir MQL5-Funktionen aufrufen, denke ich, dass Sie in der Lage waren, den gesamten Code zu lesen, und es war nicht schwer für Sie, einige Punkte zu verstehen. Zum Beispiel:

  • Wenn m_Info.Study von eStudyNull verschieden ist, sollte etwas ausgeführt werden.
  • m_Info.Data.ButtonStatus ist gleich sparam.
  • Wenn die mittlere Taste gedrückt wurde und irgendetwas (Preislinienfarbe) von clrNONE abweicht, dann tun Sie Folgendes.
  • Wenn sie mit der linken Maustaste gedrückt wurde und m_Info.Study gleich eStudyCreate ist, wird diese Aktion ausgeführt.
  • m_Info.Study wird eStudyExecute zugewiesen.
  • Wenn m_Info.Study gleich eStudyExecute ist, führen Sie dies aus.

Sie können sehen, dass selbst wenn Sie alle oben genannten Punkte lesen, die demonstriert wurden, es immer noch zeigt, dass wir noch mehr Dinge zu unserer Datei Defines.mqh hinzufügen können, um die Sprache noch lesbarer zu machen, als ich es demonstriert habe. Wir können einfach mehr Elemente hinzufügen und das Programm besser lesbar machen. Dies ist eine Eigenschaft einer guten Programmiersprache, und sie wird in Programmen von guter technischer Qualität immer vorhanden sein. Eine weitere Möglichkeit, den Code einigermaßen lesbar zu machen, besteht darin, immer Kommentare zu den wichtigsten Funktionen oder Punkten hinzuzufügen, und in dieser Hinsicht ist MQL5 C/C++ deutlich überlegen. Versuchen Sie, Variablen und Prozeduren mit Kommentaren zu versehen. Bei der Verwendung von MetaEditor werden diese Kommentare als Tooltip angezeigt, was sehr hilfreich ist.

Wie würde der obige Code eigentlich programmiert werden? Oder genauer gesagt, wie würde der Compiler den obigen Code tatsächlich sehen? Dies ist im Folgenden dargestellt:

case CHARTEVENT_MOUSE_MOVE:
   ChartXYToTimePrice(GetInfoTerminal().ID, m_Info.Data.Position.X = (int)lparam, m_Info.Data.Position.Y = (int)dparam, w, m_Info.Data.Position.dt, m_Info.Data.Position.Price);
   ObjectMove(GetInfoTerminal().ID, def_NameObjectLineH, 0, 0, m_Info.Data.Position.Price = AdjustPrice(m_Info.Data.Position.Price));
   if (m_Info.Study != eStudyNull) ObjectMove(GetInfoTerminal().ID, def_NameObjectLineV, 0, m_Info.Data.Position.dt, 0);
   m_Info.Data.ButtonStatus = (uint) sparam;
   if (CheckClick(eClickMiddle) && ((color)ObjectGetInteger(GetInfoTerminal().ID, def_NameObjectLineH, OBJPROP_COLOR) != clrNONE)) CreateStudy();
   if (CheckClick(eClickLeft) && (m_Info.Study == eStudyCreate))
   {
      ChartSetInteger(GetInfoTerminal().ID, CHART_MOUSE_SCROLL, false);
      ObjectMove(GetInfoTerminal().ID, def_NameObjectLineT, 0, m_Info.Data.Position.dt, memPrice = m_Info.Data.Position.Price);
      m_Info.Study = eStudyExecute;
   }
   if (m_Info.Study == eStudyExecute) ExecuteStudy(memPrice);
   break;

Beide Codes haben die gleiche Wirkung. Für diejenigen, die die Funktionen der Sprache MQL5 nicht vollständig verstehen, werde ich daher erklären, was wir hier tun. Zunächst wandeln wir die von der Plattform gemeldeten grafischen Koordinaten in Preis- und Zeitkoordinaten um. Auf diese Weise können wir feststellen, wo sich der Mauszeiger im Verhältnis zum Chart befindet. Anschließend passen wir den Preis an die Erwartungen des Handelsservers an. Auf ähnliche Weise können wir die Kurslinie an der gewünschten Stelle im Chart platzieren. Wenn wir eine Analyse durchführen, müssen wir die Zeitachse korrekt verschieben. Wir speichern den Zustand der Maustasten und prüfen, ob die mittlere Taste für ein Erkunden gedrückt wurde. Die Analyse wird jedoch nur durchgeführt, wenn die Kurslinie im Chart sichtbar ist. Sobald die linke Taste gedrückt wird, beginnt die Analyse. Daher müssen wir der Plattform mitteilen, dass sie das Chart nicht verschieben soll, damit wir die Maus mit gedrückter linker Taste einfach ziehen können. Solange Sie die Taste gedrückt halten, wird die Analyse mit allen Objekten durchgeführt, die wir auf dem Chart platzieren wollen.

Bevor wir den Artikel beenden, wollen wir noch einen kurzen Blick auf den EA-Code in diesem Stadium der Entwicklung werfen. Der vollständige Code ist unten angegeben:

#property copyright "Daniel Jose"
#property description "Generic EA for use on Demo account, replay system/simulator and Real account."
#property description "This system has means of sending orders using the mouse and keyboard combination."
#property description "For more information see the article about the system."
#property version   "1.28"
#property link "https://www.mql5.com/en/articles/11349"
//+------------------------------------------------------------------+
#include <Market Replay\System EA\Auxiliar\C_Mouse.mqh>
//+------------------------------------------------------------------+
input group "Mouse";
input color     user00 equal clrBlack;          //Price Line
input color     user01 equal clrDarkGreen;      //Positive Study
input color     user02 equal clrMaroon;         //Negative Study
//+------------------------------------------------------------------+
C_Mouse *mouse;
//+------------------------------------------------------------------+
int OnInit()
{
   mouse equal new C_Mouse(user00, user01, user02);
                
   return INIT_SUCCEEDED;
}
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
   delete mouse;
}
//+------------------------------------------------------------------+
void OnTick() {}
//+------------------------------------------------------------------+
void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam)
{
   (*mouse).DispatchMessage(id, lparam, dparam, sparam);
   ChartRedraw();
}
//+------------------------------------------------------------------+

Beachten Sie, dass der Code recht einfach ist. Daher werde ich an dieser Stelle den Prozess nicht im Detail erläutern, da dies nicht unbedingt notwendig ist.


Schlussfolgerung

Von nun an können wir den Betrieb des Systems mit Hilfe des im System enthaltenen EA überwachen. Allerdings funktioniert dieser EA nur mit der Maus. Aber jetzt können wir es nicht nur im Replay/Simulationssystem verwenden, sondern auch auf Demo- und realen Konten. Auch wenn der EA in solchen Situationen noch nicht sehr nützlich ist, ist er doch sehr interessant, weil er auf jedem Markt, jedem Symbol und in jeder Situation eingesetzt werden kann.

Ein weiteres wichtiges Detail: Im Anhang finden Sie die letzten beiden Klassen (C_Terminal und C_Mouse), die den Inhalt der Datei Defines.mqh verwenden. Dadurch wird der Code besser lesbar. Dies unterscheidet sich jedoch von dem, was wir zu Beginn des Artikels sagten, wo wir annahmen, dass der gesamte Code dieser Formatierung folgen würde. Sie können diese Technik auch anwenden, wenn Sie es wünschen. Zu Beginn meiner Karriere als C/C++-Programmierer habe ich diesen Ansatz eine Zeit lang verwendet, um die Syntax der Sprache besser zu verstehen. Ich weiß, dass es am Anfang ziemlich verwirrend sein kann, aber man gewöhnt sich mit der Zeit daran. Diese Technik kann vor allem bei komplexen Projekten nützlich sein, die die Analyse umfangreicher boolescher und logischer Kombinationen erfordern. An dieser Stelle kann das Vorhandensein von Doppelsymbolen die Sache für Anfänger erschweren. Ein Beispiel dafür ist die folgende Tatsache:

Wer hat noch nie, selbst unter erfahrenen Programmierern, die Verwendung von LOGICAL AND (&) und BOOLEAN AND (&&) verwechselt? Es ist fast dasselbe. Im ersten Fall wird die Operation jedoch Bit für Bit ausgeführt, im zweiten Fall wird die gesamte Variable analysiert und true oder false zurückgegeben. Das ist für viele ein Problem, wenn wir sehr schnell Programme erstellen müssen.

Unterschätzen Sie daher nicht das Wissen über bestimmte Techniken. Auch wenn sie einfach erscheinen, tragen sie doch erheblich dazu bei, ein Programm lesbarer zu machen und damit seine Entwicklung zu beschleunigen. Ich denke, ich habe auf recht interessante Weise gezeigt, wie man Aufgaben viel schneller erledigen und gleichzeitig sicherstellen kann, dass der Code immer korrekt ist, ohne Zeit damit zu verschwenden, herauszufinden, warum ein bestimmter Teil nicht so funktioniert, wie er sollte.


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

Beigefügte Dateien |
Files_-_BOLSA.zip (1358.24 KB)
Files_-_FOREX.zip (3743.96 KB)
Files_-_FUTUROS.zip (11397.51 KB)
Erstellen von Multi-Symbol- und Multi-Perioden-Indikatoren Erstellen von Multi-Symbol- und Multi-Perioden-Indikatoren
In diesem Artikel werden wir uns mit den Grundsätzen der Erstellung von Multi-Symbol- und Multi-Perioden-Indikatoren befassen. Wir werden auch sehen, wie man auf die Daten solcher Indikatoren von Expert Advisors und anderen Indikatoren zugreifen kann. Wir werden die Hauptmerkmale der Verwendung von Multi-Indikatoren in Expert Advisors und Indikatoren besprechen und sehen, wie man sie durch nutzerdefinierte Indikatorpuffer darstellen kann.
Experimente mit neuronalen Netzen (Teil 7): Übergabe von Indikatoren Experimente mit neuronalen Netzen (Teil 7): Übergabe von Indikatoren
Beispiele für die Übergabe von Indikatoren an ein Perzeptron. Der Artikel beschreibt allgemeine Konzepte und stellt den einfachsten fertigen Expert Advisor vor, gefolgt von den Ergebnissen seiner Optimierung und seines Vorwärtstests.
Integration von ML-Modellen mit dem Strategy Tester (Schlussfolgerung): Implementierung eines Regressionsmodells für die Preisvorhersage Integration von ML-Modellen mit dem Strategy Tester (Schlussfolgerung): Implementierung eines Regressionsmodells für die Preisvorhersage
Dieser Artikel beschreibt die Implementierung eines Regressionsmodells auf der Grundlage eines Entscheidungsbaums. Das Modell soll die Preise von Finanzanlagen vorhersagen. Wir haben die Daten bereits aufbereitet, das Modell trainiert und evaluiert, sowie angepasst und optimiert. Es ist jedoch wichtig zu beachten, dass dieses Modell nur für Studienzwecke gedacht ist und nicht im realen Handel eingesetzt werden sollte.
Entwicklung eines Replay System (Teil 27): Expert Advisor Projekt — Die Klasse C_Mouse (II) Entwicklung eines Replay System (Teil 27): Expert Advisor Projekt — Die Klasse C_Mouse (II)
In diesem Artikel werden wir die Klasse C_Mouse implementieren. Es bietet die Möglichkeit, auf höchstem Niveau zu programmieren. Wenn man über High-Level- oder Low-Level-Programmiersprachen spricht, geht es jedoch nicht darum, obszöne Wörter oder Jargon in den Code aufzunehmen. Es ist genau andersherum. Wenn wir von High-Level- oder Low-Level-Programmierung sprechen, meinen wir, wie leicht oder schwer der Code für andere Programmierer zu verstehen ist.