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

Entwicklung eines Replay System (Teil 30): Expert Advisor Projekt — Die Klasse C_Mouse (IV)

MetaTrader 5Tester | 20 März 2024, 13:03
96 0
Daniel Jose
Daniel Jose

Einführung

Im vorherigen Artikel „Entwicklung eines Replay-Systems (Teil 29): Expert Advisor Projekt — Die Klasse C_Mouse (III) Wir haben die Klasse C_Mouse so entworfen, dass wir die Forschungsfunktionalität erweitern können, ohne einen Teil unseres Codes zu zerstören. Da wir uns auf Programmiertechniken stützen, die die parallele Erstellung von Code ermöglichen, werden wir ein Hauptprojekt haben, das sich auf organisierte Weise entwickelt. Gleichzeitig kann das System, falls gewünscht, um zusätzliche Funktionen erweitert werden. Auf diese Weise ändert sich unser Hauptcode bei der Implementierung dieser Funktionen überhaupt nicht und bleibt nicht aufgrund der übermäßigen Verwendung der Vererbung stehen.

Die objektorientierte Programmierung (OOP) ist zwar eine großartige Art zu programmieren, eignet sich aber viel besser für Produktionsprojekte, bei denen wir eine genaue Kontrolle über die Vorgänge haben und seltsame Fehler vermeiden wollen, während das System wächst. Manchmal müssen wir einen Teil eines Projekts parallel entwickeln. Es klingt zwar seltsam, aber wenn wir einem Programm eine Funktion oder Methode hinzufügen, ist es nicht sehr sinnvoll, die Funktion, die sich noch im Anfangsstadium (in der Testphase) befindet, in der Mitte des Codes zu platzieren, der sich bereits in einem fortgeschrittenen Stadium befindet. Ich meine, Sie sollten keine ungetestete Funktion zu Ihrem bereits getesteten und funktionierenden Code hinzufügen, da dies zu unvorhersehbaren Fehlern führen kann.

Wegen solcher Fehler muss man oft das gesamte Projekt in die frühen Phasen der Entwicklung zurückversetzen. Manchmal kann die neue Funktion so in den Code eingebettet sein, dass es sehr viel schwieriger wäre, sie zu entfernen, als das Projekt auf eine frühere Stufe zurückzusetzen. Obwohl viele Menschen, insbesondere neue Programmierer, keine Verzeichnisstrukturen verwenden, um zu früheren Entwicklungsstadien zurückkehren zu können, können wir auch ohne solche Verzeichnisstrukturen eine bestimmte Technik anwenden, die es uns ermöglicht, das System an den Punkt zurückzubringen, an dem die hinzugefügte Ressource noch nicht Teil des fertigen Projekts ist.

Auf diese Weise können wir parallel entwickeln, während das endgültige Projekt ohne Probleme voranschreitet. In diesem Zusammenhang haben wir im vorigen Artikel gesehen, wie man Zeiger verwendet. Gehen wir nun einen Schritt weiter und erstellen wir auf der Grundlage des Grundmodells eine komplexere Studie. Erweist sich die Studie oder Ressource als geeignet für das endgültige Projekt, kann sie, sobald sie ein fortgeschritteneres Teststadium durchlaufen hat und sich als ausreichend stabil und zuverlässig erwiesen hat, in das Hauptklassensystem aufgenommen werden. So wird das, was vorher als Nebenprojekt galt, Teil des endgültigen Projekts, das in das Klassensystem vererbt wird.

Um dies zu demonstrieren, werden wir eine Modifikation der Klasse C_Mouse erstellen, jedoch ohne Vererbung und Polymorphismus. Wir erhalten ein völlig anderes, analytisches Modell, das sich von dem in der Klasse C_Mouse vorhandenen Originalsystem unterscheidet. Zu diesem Zweck erstellen wir eine neue Klasse, die von der Klasse C_Studies, die wir uns im vorherigen Artikel angesehen haben, abgeleitet werden kann (oder auch nicht). Ob man die Klasse C_Studys ableiten soll oder nicht, ist eher eine persönliche Frage als eine praktische. Das eine Projekt wird so oder so nichts mit dem anderen zu tun haben, da sie parallel laufen können. Trotzdem wird jeder Code, der zum Hauptsystem gehört, von der Klasse C_Mouse abgeleitet, bis der Code, der diese Klasse erweitert, als stabil und interessant genug für uns angesehen wird, um ihn im endgültigen Projekt zu verwenden.

Bevor wir zur Programmierung übergehen, ist es wichtig zu wissen, dass das System auf zwei verschiedene Arten fortschreiten kann. Welchen Weg wir wählen, hängt davon ab, was wir tun wollen und wie weit wir gehen wollen. Da wir zwei Pfade haben und der Unterschied zwischen ihnen sehr gering ist, sollten wir beide betrachten. Im beigefügten Code haben Sie Zugang zu einem der beiden Pfade. Aber wenn Sie wollen, können Sie die notwendigen Änderungen vornehmen, um einen anderen Weg einzuschlagen.

Mir geht es hier darum, zu zeigen, was wir mit der Plattform tun können, nicht wie wir es tun sollten.


Ergänzungen zur Klasse C_Terminal

Das System, das wir zu Demonstrationszwecken programmieren, erfordert keine Ergänzungen zum Hauptcode, aber aus praktischen Gründen und um mit dem Testen der Erstellung von Objekten zu beginnen, die wir bei der Entwicklung des Codes häufig verwenden werden, fügen wir einen allgemeinen Code hinzu, der Objekte in der Symboltabelle erstellt. Auf diese Weise können wir von Anfang an mit Tests und Verbesserungen beginnen. Der Code dafür wurde schon vor langer Zeit entwickelt, und wir haben bereits überlegt, einen geeigneteren Standort zu wählen. Bis wir einen besseren Ort finden, wird die Funktion create in der Klasse C_Terminal untergebracht, wie unten gezeigt:

inline void CreateObjectGraphics(const string szName, const ENUM_OBJECT obj, const color cor, const int zOrder = -1)
   {
      ObjectCreate(m_Infos.ID, szName, obj, 0, 0, 0);
      ObjectSetString(m_Infos.ID, szName, OBJPROP_TOOLTIP, "\n");
      ObjectSetInteger(m_Infos.ID, szName, OBJPROP_BACK, false);
      ObjectSetInteger(m_Infos.ID, szName, OBJPROP_COLOR, cor);
      ObjectSetInteger(m_Infos.ID, szName, OBJPROP_SELECTABLE, false);
      ObjectSetInteger(m_Infos.ID, szName, OBJPROP_SELECTED, false);
      ObjectSetInteger(m_Infos.ID, szName, OBJPROP_ZORDER, zOrder);
   }

Diese Funktion wird zu einer allgemeinen Funktion für die Erstellung von Objekten, die im Chart angezeigt werden sollen. In mehreren Fällen werden wir sehen, dass sie in Code mit nur zwei deklarierten Elementen auftaucht. Der Grund dafür ist, dass das Element mit einem Standardwert deklariert ist, sodass es zum Zeitpunkt des Aufrufs nicht deklariert werden muss, es sei denn, sein Wert stellt sich aus irgendeinem Grund als anders heraus. Außerdem wird die Funktion immer mit zwei Elementen in ihrer Deklaration aufgerufen. Der Punkt ist, dass diese Eigenschaft des Objekts OBJPROP_ZORDER verwendet wird, um ein Problem zu lösen, das wir an einigen Stellen sehen werden. Wenn wir diese Eigenschaft nicht korrekt definieren, werden wir ernsthafte Probleme mit Objekten haben, die auf dem Symbolchart platziert sind, wenn wir mit dem Programm in einem beliebigen Modus arbeiten: Replay/Simulation, Handel auf einem Demo- oder Live-Konto. Wir haben gesehen, wie sich der Hauptcode geändert hat, und wir wissen jetzt, wie man das System anders als mit dem ursprünglichen Code nutzen kann. Weitere Einzelheiten werden in separaten Themen behandelt.


Erster Weg: Vererbung nutzen

Im ersten Weg verwenden wir die Vererbung, allerdings nicht von der Klasse C_Mouse, sondern von der Klasse C_Study, die wir uns im vorherigen Artikel angesehen haben. Unsere Header-Datei C_StudyS2.mqh wird den folgenden Code enthalten:

//+------------------------------------------------------------------+
#include "C_StudyS1.mqh"
//+------------------------------------------------------------------+

// ... Local definitions ....

//+------------------------------------------------------------------+

// ... Local alias ...

//+------------------------------------------------------------------+
class C_StudyS2 : public C_StudyS1
{
   protected:
   private :

// ... Code and internal functions ...

//+------------------------------------------------------------------+
   public  :
//+------------------------------------------------------------------+
      C_StudyS2(C_Mouse *arg, color corP, color corN)
         :C_StudyS1(arg, corP, corN)
      {                               
// ... Internal code ....

      }
//+------------------------------------------------------------------+
virtual void DispatchMessage(const int id, const long &lparam, const double &dparam, const string &sparam)
      {
         double v1, v2;
         int w, h;
         string sz1;
                                
         C_StudyS1::DispatchMessage(id, lparam, dparam, sparam);

// ... Internal code ...

      }
//+------------------------------------------------------------------+
};

Hier sehen wir, dass die Klasse aus dem vorherigen Artikel nach den Prinzipien der Ableitung verwendet wird, wobei wir die Klasse ableiten und Eigenschaften hinzufügen. In vielen Fällen wird dies die beste Option sein, aber nicht immer. Es ist wichtig zu wissen, wie sich diese Wege unterscheiden und ergänzen. Wenn wir diese Methode anwenden, können wir das Beste daraus machen. Bitte beachten Sie alle im obigen Auszug hervorgehobenen Punkte. Keine Sorge, wir werden Ihnen erklären, wie Sie damit arbeiten können, um etwas Interessantes zu schaffen.

Es ist logisch, dass der EA-, Indikator- oder Skriptcode in diesem Fall etwas anders aussieht, als wenn wir die Vererbung nicht verwenden. Um diese Unterschiede zu verstehen, sehen wir uns zunächst den EA-Code für diesen ersten Fall an. Der vollständige Code ist unten zu sehen:

#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.30"
#property icon "../../Images/Icons/Replay - EA.ico"
#property link "https://www.mql5.com/en/articles/11372"
//+------------------------------------------------------------------+
#include <Market Replay\System EA\Auxiliar\C_Mouse.mqh>
#include <Market Replay\System EA\Auxiliar\Study\C_StudyS2.mqh>
//+------------------------------------------------------------------+
input group "Mouse";
input color     user00 = clrBlack;      //Price Line
input color     user01 = clrPaleGreen;  //Positive Study
input color     user02 = clrLightCoral; //Negative Study
//+------------------------------------------------------------------+
C_Mouse *mouse = NULL;
C_StudyS2 *extra = NULL;
//+------------------------------------------------------------------+
int OnInit()
{
   mouse = new C_Mouse(user00, user01, user02);
   extra = new C_StudyS2(mouse, user01, user02);
                
   MarketBookAdd((*mouse).GetInfoTerminal().szSymbol);
   OnBookEvent((*mouse).GetInfoTerminal().szSymbol);
   EventSetMillisecondTimer(500);

   return INIT_SUCCEEDED;
}
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
   MarketBookRelease((*mouse).GetInfoTerminal().szSymbol);
   EventKillTimer();
        
   delete extra;
   delete mouse;
}
//+------------------------------------------------------------------+
void OnTick() {}
//+------------------------------------------------------------------+
void OnTimer()
{
   (*extra).Update();
}
//+------------------------------------------------------------------+
void OnBookEvent(const string &symbol)
{
   MqlBookInfo book[];
   
   if (mouse.GetInfoTerminal().szSymbol == def_SymbolReplay) ArrayResize(book, 1, 0); else
   {
      if (symbol != (*mouse).GetInfoTerminal().szSymbol) return;
      MarketBookGet((*mouse).GetInfoTerminal().szSymbol, book);
   }
   (*extra).Update(book);
}
//+------------------------------------------------------------------+
void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam)
{
   (*mouse).DispatchMessage(id, lparam, dparam, sparam);
   (*extra).DispatchMessage(id, lparam, dparam, sparam);
        
   ChartRedraw();
}
//+------------------------------------------------------------------+

Die einzigen Unterschiede zum Code im vorherigen Artikel sind die hervorgehobenen Teile. Das liegt daran, dass wir ein Vererbungssystem verwenden. Wie wir bereits wissen, funktioniert das Vererbungssystem sehr gut, wenn wir wollen, dass sich das System reibungslos und ohne viele unerwartete Ereignisse entwickelt. Aber es kann sein, dass wir auf andere Probleme stoßen, die uns das Leben erschweren. Manchmal müssen wir eine etwas andere Methode verwenden, die in der Anwendung verfügbar sein wird. Wenn wir das System als ein auf Vererbung basierendes Modell verwenden wollen, ist das in Ordnung. Denken Sie einfach daran, die erwähnten Änderungen vorzunehmen, und alles wird reibungslos funktionieren.


Zweiter Weg: Verwendung von Zeigern

In diesem zweiten Pfad werden wir eine detaillierte Erklärung des Klassencodes sehen. Schauen wir uns zunächst an, wie der EA-Code aussieht. In diesem Stadium werden wir große Unterschiede im Klassencode haben, der praktisch nur durch das ergänzt wird, was im vorherigen Thema gezeigt wurde. Hier ist der vollständige Code des EA, der dem zweiten Pfad folgt:

#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.30"
#property icon "../../Images/Icons/Replay - EA.ico"
#property link "https://www.mql5.com/en/articles/11372"
//+------------------------------------------------------------------+
#include <Market Replay\System EA\Auxiliar\C_Mouse.mqh>
#include <Market Replay\System EA\Auxiliar\Study\C_StudyS1.mqh>
#include <Market Replay\System EA\Auxiliar\Study\C_StudyS2.mqh>
//+------------------------------------------------------------------+
input group "Mouse";
input color     user00 = clrBlack;      //Price Line
input color     user01 = clrPaleGreen;  //Positive Study
input color     user02 = clrLightCoral; //Negative Study
//+------------------------------------------------------------------+
C_Mouse *mouse = NULL;
C_StudyS1 *extra1 = NULL;
C_StudyS2 *extra2 = NULL;
//+------------------------------------------------------------------+
int OnInit()
{
   mouse = new C_Mouse(user00, user01, user02);
   extra1 = new C_StudyS1(mouse, user01, user02);
   extra2 = new C_StudyS2(mouse, user01, user02);
                
   MarketBookAdd((*mouse).GetInfoTerminal().szSymbol);
   OnBookEvent((*mouse).GetInfoTerminal().szSymbol);
   EventSetMillisecondTimer(500);

   return INIT_SUCCEEDED;
}
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
   MarketBookRelease((*mouse).GetInfoTerminal().szSymbol);
   EventKillTimer();
        
   delete extra1;
   delete extra2;
   delete mouse;
}
//+------------------------------------------------------------------+
void OnTick() {}
//+------------------------------------------------------------------+
void OnTimer()
{
   (*extra1).Update();
}
//+------------------------------------------------------------------+
void OnBookEvent(const string &symbol)
{
   MqlBookInfo book[];
   
   if ((*mouse).GetInfoTerminal().szSymbol == def_SymbolReplay) ArrayResize(book, 1, 0); else
   {
      if (symbol != (*mouse).GetInfoTerminal().szSymbol) return;
      MarketBookGet((*mouse).GetInfoTerminal().szSymbol, book);
   }
   (*extra1).Update(book);
}
//+------------------------------------------------------------------+
void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam)
{
   (*mouse).DispatchMessage(id, lparam, dparam, sparam);
   (*extra1).DispatchMessage(id, lparam, dparam, sparam);
   (*extra2).DispatchMessage(id, lparam, dparam, sparam);
        
   ChartRedraw();
}
//+------------------------------------------------------------------+

Hier zeige ich, wo wir das Hauptklassensystem verwenden (gelb markiert). Das System der Erweiterungsklassen, das wir im vorherigen Artikel gezeigt haben, ist grün markiert. Das System, das eine andere Art von Analyse durchführt, aber auch etwas anderes sein könnte, ist in orange dargestellt. Beachten Sie, dass wir, da wir keine Vererbung verwenden, mehr Code im EA deklarieren müssen. Gleichzeitig können wir so mehr parallelen Code veröffentlichen, um zu testen, welche Dinge wir in der endgültigen Version haben werden. Das Beste daran ist, dass wir diesen parallelen Code ohne große Schwierigkeiten aus dem Code entfernen können, wenn er irgendwelche Störungen oder Fehler anzeigt. Allerdings gibt es hier ein weiteres Problem: Sowohl der orangefarbene als auch der grüne Code können polymorph sein. Dadurch können wir mehr Aspekte des Systems testen, das parallel entwickelt wird. Wir werden das Thema Polymorphismus ein anderes Mal behandeln. Wenn wir jetzt darüber sprechen, werden wir die Erklärung zu sehr verkomplizieren, sodass Enthusiasten vielleicht nicht alle Argumente für die Verwendung von Polymorphismus nachvollziehen können.

Nach diesen Erläuterungen können Sie mit dem Klassencode fortfahren. Denken Sie daran, dass der Code für den ersten und den zweiten Pfad fast identisch ist, mit Ausnahme der im Thema über den ersten Pfad genannten Punkte.


Analysieren wir nun den Code der Klasse C_StudyS2

In gewissem Sinne werden alle Codes, die mit dem Analysesystem verbunden sind, einander sehr ähnlich sein, mit ein paar Ausnahmen. Es gibt jedoch eine Reihe von Dingen, die den Code für die Generierung von Analysen interessant machen, um ihn zu analysieren und zu verstehen. Schauen wir uns das einmal genauer an. Bitte beachten Sie, dass der hier bereitgestellte Code nur zu Demonstrationszwecken dient und keineswegs eine vollständige Methode darstellt. C_StudyS2.mqh beginnt folgendermaßen:

#property copyright "Daniel Jose"
//+------------------------------------------------------------------+
#include "..\C_Mouse.mqh"
#include "..\..\..\Service Graphics\Support\Interprocess.mqh"
//+------------------------------------------------------------------+
#define def_ExpansionPrefix "Expansion2_"
#define def_ExpansionBtn1 def_ExpansionPrefix + "B1"
#define def_ExpansionFibo def_ExpansionPrefix + "FB"
//+------------------------------------------------------------------+
#define def_InfoTerminal (*mouse).GetInfoTerminal()
#define def_InfoMousePos (*mouse).GetInfoMouse().Position
//+------------------------------------------------------------------+
class C_StudyS2
{
   protected:
   private :
//+------------------------------------------------------------------+
      C_Mouse *mouse;
//+------------------------------------------------------------------+
      struct st00
      {
         bool            ExecStudy,
                         ClearStudy;
         double          MemPrice;
         datetime        MemDT;
         color           corP,
                         corN;
       }m_Info;
//+------------------------------------------------------------------+

Hier geben wir die Dateien an, die in das System aufgenommen werden müssen. Bitte beachten Sie, dass die Pfade relativ zu dem Pfad angegeben sind, in dem sich die Datei C_StudyS2.mqh befindet. Dadurch wird es einfacher, das Projekt in andere Verzeichnisse zu verschieben und gleichzeitig seine Struktur beizubehalten. Als Nächstes werden wir einige Objektnamen definieren, die wir im Forschungsprozess verwenden werden. Es gibt auch Alias-Deklarationen, um den Programmierprozess zu erleichtern, da sie beim Schreiben von Code an vielen Stellen verwendet werden. Und das letzte, was wir in diesem Fragment sehen, ist die Struktur, auf die über eine private globale Variable zugegriffen wird.

Hier ist der nächste Code-Teil:

#define def_FontName "Lucida Console"
#define def_FontSize 10
       void GetDimensionText(const string szArg, int &w, int &h)
          {
             TextSetFont(def_FontName, -10 * def_FontSize, FW_NORMAL);
             TextGetSize(szArg, w, h);
             h += 5;
             w += 5;
          }
//+------------------------------------------------------------------+
       void CreateBTNInfo(int x, int w, int h, string szName, color backColor)
          {
             (*mouse).CreateObjectGraphics(szName, OBJ_BUTTON, clrNONE);
             ObjectSetInteger(def_InfoTerminal.ID, szName, OBJPROP_STATE, true);
             ObjectSetInteger(def_InfoTerminal.ID, szName, OBJPROP_BORDER_COLOR, clrBlack);
             ObjectSetInteger(def_InfoTerminal.ID, szName, OBJPROP_COLOR, clrBlack);
             ObjectSetInteger(def_InfoTerminal.ID, szName, OBJPROP_BGCOLOR, backColor);
             ObjectSetString(def_InfoTerminal.ID, szName, OBJPROP_FONT, def_FontName);
             ObjectSetInteger(def_InfoTerminal.ID, szName, OBJPROP_FONTSIZE, def_FontSize);
             ObjectSetInteger(def_InfoTerminal.ID, szName, OBJPROP_CORNER, CORNER_LEFT_UPPER); 
             ObjectSetInteger(def_InfoTerminal.ID, szName, OBJPROP_XDISTANCE, x);
          }
#undef def_FontSize
#undef def_FontName

Hier haben wir zwei Deklarationen, die nur an dieser Stelle verwendet werden, also definieren und entfernen wir die Definition, sobald sie vom Rest des Codes nicht mehr benötigt wird. An diesem Punkt rufen wir bereits eine Funktion auf, die Objekte für das Chart erstellt. Die Aufgabe besteht darin, ein Objekt zu erstellen und dann einige seiner Eigenschaften so anzupassen, dass es so gebaut wird, wie Sie es wünschen. Wie Sie jedoch feststellen können, verwenden wir die Schaltfläche wie ein Fenster, das schreibgeschützten Text enthält. Vielleicht wäre es sinnvoller, hier OBJ_LABEL oder OBJ_EDIT zu verwenden. Hier geht es jedoch nur darum, eine der Möglichkeiten aufzuzeigen, wie das beste Ergebnis erzielt werden kann. Daher können wir ein anderes Objekt verwenden, um die Daten im Chart darzustellen.

Das Besondere an dieser Klasse sind die beiden Funktionen, die sie enthält. Die erste ist unten abgebildet. Auf die andere wird am Ende des Artikels eingegangen. Sehen wir uns nun an, wie diese Klasse die in Video 01 vorgestellte Analyse erstellt, die das Fibonacci-Objekt verwendet. Der Code zur Erstellung dieses Objekts ist unten dargestellt:

void CreateStudy(void)
   {
      const double FiboLevels[] = {0, 0.236, 0.382, 0.50, 0.618, 1, 1.618, 2};
      ENUM_LINE_STYLE ls;
      color cor;
                                
      ObjectDelete(def_InfoTerminal.ID, def_ExpansionFibo);
      ObjectDelete(def_InfoTerminal.ID, "MOUSE_TB");
      ObjectDelete(def_InfoTerminal.ID, "MOUSE_TI");
      ObjectDelete(def_InfoTerminal.ID, "MOUSE_TT");
      (*mouse).CreateObjectGraphics(def_ExpansionFibo, OBJ_FIBO, clrNONE);
      ObjectSetInteger(def_InfoTerminal.ID, def_ExpansionFibo, OBJPROP_HIDDEN, false);
      ObjectSetInteger(def_InfoTerminal.ID, def_ExpansionFibo, OBJPROP_RAY_LEFT, false);
      ObjectSetInteger(def_InfoTerminal.ID, def_ExpansionFibo, OBJPROP_LEVELS, ArraySize(FiboLevels));
      for (int c0 = 0, c1 = ArraySize(FiboLevels); c0 < c1; c0++)
      {
         ls = ((FiboLevels[c0] == 0) || (FiboLevels[c0] == 1) || (FiboLevels[c0] == 2)  ? STYLE_SOLID : STYLE_DASHDOT);
         ls = (FiboLevels[c0] == 0.5 ? STYLE_DOT : ls);
         switch (ls)
         {
            case STYLE_DOT    : cor = clrBlueViolet;  break;
            case STYLE_DASHDOT: cor = clrViolet;      break;
            default           : cor = clrIndigo;
         }
         ObjectSetInteger(def_InfoTerminal.ID, def_ExpansionFibo, OBJPROP_LEVELSTYLE, c0, ls);
         ObjectSetInteger(def_InfoTerminal.ID, def_ExpansionFibo, OBJPROP_LEVELCOLOR, c0, cor);                                  
         ObjectSetInteger(def_InfoTerminal.ID, def_ExpansionFibo, OBJPROP_LEVELWIDTH, c0, 1);
         ObjectSetString(def_InfoTerminal.ID, def_ExpansionFibo, OBJPROP_LEVELTEXT, c0, (string)NormalizeDouble(FiboLevels[c0] * 100, 2));
      }
      ObjectSetDouble(def_InfoTerminal.ID, def_ExpansionFibo, OBJPROP_PRICE, 1, m_Info.MemPrice = def_InfoMousePos.Price);
      ObjectSetInteger(def_InfoTerminal.ID, def_ExpansionFibo, OBJPROP_TIME, 1, m_Info.MemDT = def_InfoMousePos.dt);
      CreateBTNInfo(def_InfoMousePos.X, 50, 18, def_ExpansionBtn1, clrNONE);
      m_Info.ExecStudy = true;
      m_Info.ClearStudy = false;
   }

Obwohl dieser Code auf den ersten Blick kompliziert erscheinen mag, besteht er eigentlich aus drei Teilen. In jedem dieser Teile führen wir eine spezifische Analyse mit OBJ_FIBO durch.

  1. Im ersten Teil haben wir die „unerwünschten“ Objekte entfernt, die von der Klasse C_Mouse erstellt werden, wenn ein Ereignis von der Plattform eintrifft, das anzeigt, dass der Nutzer mit der Analyse des Symbolcharts begonnen hat. Achten Sie beim Löschen dieser Objekte darauf, dass Sie nichts entfernen, was wirklich wichtig ist. Wir können also eine ganz besondere Analyse erstellen, indem wir in der Studie, die wir hier erstellen werden, alles entfernen, was wir nicht sehen müssen. Bitte beachten Sie, dass wir den Gegenstand der alten Analyse entfernt haben. Dies geschah, damit wir die Analyse anhand bestimmter Kriterien durchführen konnten. Der Grund dafür könnte auch sein, dass wir eine Schlüsselkombination verwenden wollen, um Analysen auf der Grundlage von Variationen von OBJ_FIBO zu erstellen. Diese Varianten können OBJ_FIBOTIMES, OBJ_FIBOFAN, OBJ_FIBOARC, OBJ_FIBOCHANNEL und OBJ_EXPANSION sein. Sie folgen alle den gleichen Prinzipien, die hier dargestellt sind.
  2. Im zweiten Teil erstellen und definieren wir die Eigenschaften des Objekts. Hier sind einige interessante Punkte: In diesem Stadium teilen wir der Plattform mit, dass das Objekt in der Liste der Objekte sichtbar sein wird. Hier geben wir an, welche Ebenen das Objekt enthalten soll. Wir haben hier statische Pegel verwendet, Sie können aber auch dynamische oder andere Pegel in Ihrem System verwenden. In diesem Abschnitt. Ich werde Ihnen sagen, wie alle Ebenen aussehen werden, sowohl in Farbe als auch in Form von Linien, die für ihre Konstruktion verwendet werden. Wir können nach eigenem Ermessen Änderungen vornehmen, um eine angemessene Darstellung zu erhalten, denn wenn wir Analysen durchführen, wollen wir, dass sie schnell verstanden werden, damit wir davon profitieren können.
  3. Und im dritten und letzten Teil beginnen wir, das Objekt direkt auf dem Chart zu konstruieren, d.h. wir beginnen zu plotten. Wir werden auch sehen, was mit den Variablen passiert. Dies ist notwendig, damit während der Funktion, die wir später sehen werden, alles richtig gemacht wird.

Im Grunde genommen werden wir auf diese Weise eine Studie erstellen, die auf dem bereits erstellten und getesteten System basiert, das sich in der Klasse C_Mouse befindet. Mit anderen Worten: Wir werden nicht etwas von Grund auf neu aufbauen, sondern das, was wir bereits haben, wiederverwenden und anpassen, um etwas anderes zu erreichen. Alles wird klarer werden, wenn wir das zweite Verfahren untersuchen. Schauen wir uns nun den Konstruktor und den Destruktor der Klasse an. Sie können sie unten sehen:

C_StudyS2(C_Mouse *arg, color corP, color corN)
   {                               
      mouse = arg;
      ZeroMemory(m_Info);
      m_Info.corP = corP;
      m_Info.corN = corN;
   }
//+------------------------------------------------------------------+
~C_StudyS2()
   {
      ObjectsDeleteAll(def_InfoTerminal.ID, def_ExpansionPrefix);
   }

Sehen Sie sich diese beiden Funktionen genau an. Ziel ist es, ein System zu verwenden, das auf dem zweiten Weg basiert. Um das Vererbungsmodell zu verwenden, müssen Sie die im ersten Pfad-Thema verwendeten Zeilen hinzufügen. Das Gleiche muss in der letzten Funktion der Klasse gemacht werden. Dieser Teil ermöglicht die Interaktion mit der Plattform. Nachstehend finden Sie den vollständigen Code der Funktion, mit der Sie die Analysen erstellen können:

virtual void DispatchMessage(const int id, const long &lparam, const double &dparam, const string &sparam)
   {
      double v1, v2;
      int w, h;
      string sz1;
                                
      switch (id)
      {
         case CHARTEVENT_KEYDOWN:
            if (TerminalInfoInteger(TERMINAL_KEYSTATE_ESCAPE) && (m_Info.ExecStudy)) m_Info.ClearStudy = true;
            break;
         case CHARTEVENT_MOUSE_MOVE:
            if (mouse.GetInfoMouse().ExecStudy)
            {
               if (!m_Info.ExecStudy) CreateStudy();
               v1 = def_InfoMousePos.Price - m_Info.MemPrice;
               v2 = MathAbs(100.0 - ((m_Info.MemPrice / def_InfoMousePos.Price) * 100.0));
               sz1 = StringFormat(" %." + (string)def_InfoTerminal.nDigits + "f [ %d ] %02.02f%% ", MathAbs(v1), Bars(def_InfoTerminal.szSymbol, PERIOD_CURRENT, m_Info.MemDT, def_InfoMousePos.dt) - 1, v2);
               GetDimensionText(sz1, w, h);
               ObjectSetDouble(def_InfoTerminal.ID, def_ExpansionFibo, OBJPROP_PRICE, 0, def_InfoMousePos.Price);
               ObjectSetInteger(def_InfoTerminal.ID, def_ExpansionFibo, OBJPROP_TIME, 0, def_InfoMousePos.dt);
               ObjectSetInteger(def_InfoTerminal.ID, def_ExpansionFibo, OBJPROP_COLOR, (v1 < 0 ? m_Info.corN : m_Info.corP));
               ObjectSetString(def_InfoTerminal.ID, def_ExpansionBtn1, OBJPROP_TEXT, sz1);                                                                                                                             
               ObjectSetInteger(def_InfoTerminal.ID, def_ExpansionBtn1, OBJPROP_BGCOLOR, (v1 < 0 ? m_Info.corN : m_Info.corP));
               ObjectSetInteger(def_InfoTerminal.ID, def_ExpansionBtn1, OBJPROP_XSIZE, w);
               ObjectSetInteger(def_InfoTerminal.ID, def_ExpansionBtn1, OBJPROP_YSIZE, h);
               ObjectSetInteger(def_InfoTerminal.ID, def_ExpansionBtn1, OBJPROP_XDISTANCE, def_InfoMousePos.X - w);
               ObjectSetInteger(def_InfoTerminal.ID, def_ExpansionBtn1, OBJPROP_YDISTANCE, def_InfoMousePos.Y - (v1 < 0 ? 1 : h));
            }else if (m_Info.ExecStudy)
            {
               ObjectSetInteger(def_InfoTerminal.ID, def_ExpansionFibo, OBJPROP_COLOR, clrNONE);
               ObjectDelete(def_InfoTerminal.ID, def_ExpansionBtn1);
               if (m_Info.ClearStudy) ObjectDelete(def_InfoTerminal.ID, def_ExpansionFibo);
               m_Info.ExecStudy = false;
            }
            break;
         }
      }

Dieser Code ist sehr interessant, nicht wahr? Achten Sie darauf, dass wir Mausaktionen nicht berücksichtigt haben. Wir sehen uns nur an, was die Klasse C_Mouse tut. Während die Klasse C_Mouse angibt, dass wir eine Analyse durchführen, folgt diese Klasse dieser Richtung und führt die Analyse entsprechend den Anweisungen der Klasse C_Mouse durch. Sobald die Klasse C_Mouse nicht mehr in der Analyse verwendet wird, löschen wir das Objekt, das als Host für den Informationstext verwendet wurde. Wenn Sie während der Analyse die ESC-Taste drücken, wird das Analyseobjekt ebenfalls gelöscht. Die Größe des Objekts, das für die Anzeige des Textes verwendet wird, wird dynamisch berechnet, d. h. sie kann je nach Fall größer oder kleiner sein, und all dies wird in diesem Code gesteuert. Hier steuern wir auch die Farben und die Platzierung von Objekten.

Dieser Code hat einen interessanten Teil, der eine nähere Erläuterung verdient. Schauen wir ihn uns als Nächstes an. Wir müssen verstehen, was wir darstellen und warum diese Werte verwendet werden.

v1 = def_InfoMousePos.Price - m_Info.MemPrice;
v2 = MathAbs(100.0 - ((m_Info.MemPrice / def_InfoMousePos.Price) * 100.0));
sz1 = StringFormat(" %." + (string)def_InfoTerminal.nDigits + "f [ %d ] %02.02f%% ", MathAbs(v1), Bars(def_InfoTerminal.szSymbol, PERIOD_CURRENT, m_Info.MemDT, def_InfoMousePos.dt) - 1, v2);

Um diese drei Zeilen zu verstehen, die Informationen für die Darstellung faktorisieren und formatieren, müssen Sie sehen, dass sich das System dynamisch an das Symbol anpasst, auf dem der Code ausgeführt wird. Manche Symbole benötigen 4 Dezimalstellen, um Werte darzustellen, andere wiederum 5. Bei einigen Bestandsinstrumenten gibt es nur 2 Dezimalstellen. Um das System leicht anpassbar zu machen, verwenden wir den oben genannten Code. Das hört sich seltsam an, ist aber in Wirklichkeit einfach nur ungewöhnlich.

Zunächst wird die Differenz zwischen dem Kurs, mit dem die Analyse begann, und der Kurslinie berücksichtigt. Hier wird der Wert in Punkten oder in Geldwert angegeben; dies ist der Offset-Wert zwischen der Position, an der wir die Analyse begonnen haben, und der aktuellen Position, an der sich die Maus befindet. Um sie korrekt darzustellen, müssen wir wissen, wie viele Zeichen benötigt werden. Dazu verwenden wir die folgende Methode. Mit dem Prozentzeichen (%) können Sie die Art der Informationen festlegen, die in eine Zeichenkette umgewandelt werden sollen. Mit dem folgenden Format <%.2f> erhalten wir einen Wert mit zwei Nachkommastellen, mit <%.4f> einen Wert mit 4 Nachkommastellen und so weiter. Dies muss jedoch zur Laufzeit definiert werden. 

Die Funktion StringFormat erstellt das entsprechende Format selbst. Ich weiß, dass dies verwirrend erscheint, aber sobald der Wert, den wir mit Hilfe der Differenz berechnet haben, platziert ist, wird er genau nach dem Format platziert, das wir erstellt haben. Dadurch erhalten wir die Anzahl der Dezimalstellen, die dem angezeigten Wert entsprechen. Um zu verstehen, wie dies in der Praxis funktioniert, müssen Sie denselben Code auf Assets mit einer anderen Anzahl von Zeichen anwenden, um dies zu verdeutlichen. Eine andere Frage ist, wie man auf einfache Weise die Anzahl der Balken von einem bestimmten Punkt aus ermitteln kann.

Einige Plattformen bieten einen Indikator, der die Balken zählt und im Chart anzeigt. Wir können einen solchen Indikator leicht erstellen. Gleichzeitig erscheinen jedoch immer mehr Informationen auf dem Chart, was die Lesbarkeit oft erschwert, da es mit einer großen Menge an Informationen gefüllt wird, von denen die meisten oft unnötig sind. Mit der etwas exotischeren Sprache MQL5 können wir berechnen, wie viele Balken sich im Analysebereich befinden, und diesen Wert in Echtzeit direkt im Chart anzeigen. Sobald die Analyse abgeschlossen ist, werden nur noch die Informationen, die wir benötigen, in der Tabelle angezeigt.

Um eine solche Analyse durchzuführen, verwenden wir diese Funktion mit diesen Parametern. Aber Vorsicht, wenn die Analyse in dem Bereich durchgeführt wird, in dem es keine Balken gibt, dann wird der angezeigte Wert -1 sein, und wenn die Analyse auf einem Balken durchgeführt wird, wird der Wert Null sein. Um dies zu ändern, da sich die Anzahl der Balken auf die Anzahl der Balken im untersuchten Bereich bezieht, streichen wir einfach diesen Wert von -1 aus der Faktorisierung. Der Wert entspricht also immer der tatsächlichen Anzahl der Balken, einschließlich des Balkens, mit dem die Analyse begann. Aus diesem Grund ergibt die Analyse manchmal den Wert -1.

Da wir die Anzeige auch als Prozentsatz der Abweichung ausweisen wollen, verwenden wir diese Berechnung, um diesen Prozentsatz zu erhalten. Um die Visualisierung verständlicher zu machen, verwenden wir dieses Format, damit das Prozentzeichen zusammen mit den übrigen Informationen angezeigt werden kann.


Schlussfolgerung

Hier habe ich eine Technik demonstriert, die an verschiedenen Punkten in Ihrem professionellen Programmierleben sehr nützlich sein kann. Ich habe gezeigt, dass im Gegensatz zu dem, was viele denken, nicht die Plattform selbst begrenzt ist, sondern das Wissen derjenigen, die sagen, dass die Plattform oder die Sprache es nicht erlaubt, verschiedene Dinge zu schaffen. Was ich hier erklärt habe, beweist, dass die MetaTrader 5-Plattform mit gesundem Menschenverstand und Kreativität viel interessanter und vielseitiger gestaltet werden kann. Und Sie müssen keine verrückten Programme oder Ähnliches erstellen. Sie können einfachen, sicheren und zuverlässigen Code erstellen. Nutzen Sie Ihre Kreativität, um bestehenden Code zu ändern, ohne eine einzige Zeile des Quellcodes zu löschen oder hinzuzufügen. Wenn also Ihr Code, den Sie schon seit einiger Zeit verwenden, irgendwann wirklich nützlich ist, können Sie ihn kontinuierlich und mühelos in Ihren robusten Code einfügen. Aus diesem Grund wird das Konzept der Klassen verwendet, bei dem auf einfache Weise eine Code-Vererbungshierarchie erstellt wird.

Es gibt keine Arbeit, die nicht erledigt werden kann. Es gibt Arbeit, die manche Menschen nicht tun können. Das bedeutet jedoch nicht, dass die Aufgabe nicht erfüllt werden kann.

Im Anhang finden Sie den vollständigen Code, den wir in diesen Artikeln erstellt haben. Im nächsten Teil werden wir uns dieses System genauer ansehen, allerdings ohne den Analysecode. Vielleicht wird ein Teil davon in die Klasse C_Mouse eingebaut. Und wenn das passiert, werde ich nicht ins Detail gehen, weil der gesamte Code in früheren Artikeln erklärt worden ist. Wir sehen uns bald wieder.


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

Beigefügte Dateien |
Files_-_BOLSA.zip (1358.24 KB)
Files_-_FOREX.zip (3743.96 KB)
Files_-_FUTUROS.zip (11397.51 KB)
Entwicklung eines Replay System (Teil 31): Expert Advisor Projekt — Die Klasse C_Mouse (V) Entwicklung eines Replay System (Teil 31): Expert Advisor Projekt — Die Klasse C_Mouse (V)
Wir brauchen einen Timer, der anzeigt, wie viel Zeit bis zum Ende der Wiedergabe/Simulation verbleibt. Dies mag auf den ersten Blick eine einfache und schnelle Lösung sein. Viele versuchen einfach, sich anzupassen und das gleiche System zu verwenden, das der Handelsserver verwendet. Aber es gibt eine Sache, die viele Leute nicht bedenken, wenn sie über diese Lösung nachdenken: Bei der Wiederholung und noch mehr bei der Simulation funktioniert die Uhr anders. All dies erschwert die Schaffung eines solchen Systems.
Entwicklung eines Replay System (Teil 29): Expert Advisor Projekt — Die Klasse C_Mouse (II) Entwicklung eines Replay System (Teil 29): Expert Advisor Projekt — Die Klasse C_Mouse (II)
Nachdem wir die Klasse C_Mouse verbessert haben, können wir uns auf die Erstellung einer Klasse konzentrieren, die einen völlig neuen Rahmen für unsere Analyse schaffen soll. Wir werden weder Vererbung noch Polymorphismus verwenden, um diese neue Klasse zu erstellen. Stattdessen werden wir die Preislinie ändern, oder besser gesagt, neue Objekte hinzufügen. Genau das werden wir in diesem Artikel tun. In der nächsten Ausgabe werden wir uns ansehen, wie man die Analyse ändern kann. All dies geschieht, ohne den Code der Klasse C_Mouse zu ändern. Nun, eigentlich wäre es einfacher, dies durch Vererbung oder Polymorphismus zu erreichen. Es gibt jedoch auch andere Methoden, um das gleiche Ergebnis zu erzielen.
Neuronale Netze leicht gemacht (Teil 61): Optimismusproblem beim Offline-Verstärkungslernen Neuronale Netze leicht gemacht (Teil 61): Optimismusproblem beim Offline-Verstärkungslernen
Während des Offline-Lernens optimieren wir die Strategie des Agenten auf der Grundlage der Trainingsdaten. Die daraus resultierende Strategie gibt dem Agenten Vertrauen in sein Handeln. Ein solcher Optimismus ist jedoch nicht immer gerechtfertigt und kann zu erhöhten Risiken während des Modellbetriebs führen. Heute werden wir uns mit einer der Methoden zur Verringerung dieser Risiken befassen.
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.