MetaTrader 5 herunterladen

3D-Modellierung in MQL5

30 Januar 2017, 16:34
Sergey Pavlov
0
356
Die 3D-Modellierung ermöglicht es, komplexe Prozesse und Erscheinungen zu untersuchen, um Ergebnisse vorherzusagen.

In Bezug auf Finanzmärkte wird die 3D-Modellierung für eine dreidimensionale Darstellung von Zeitreihen verwendet. Als Zeitreihe wird ein dynamisches System bezeichnet, in welches Werte einer zufälligen Größe einer nach dem anderen eintreffen: kontinuierlich oder in gewissen Zeitabständen (Ticks, Balken, Fraktale u.a.). In diesem Artikel betrachten wir die Visualisierung einer 3D-Darstellung von Zeitreihen, solchen wie Timeseries und Indikatoren (s. Abb. 1).

Abb. 1. Beispiele für die 3D-Darstellung von Zeitreihen.

Abb. 1. Beispiele für die 3D-Darstellung von Zeitreihen.

Eine dreidimensionale Darstellung unterscheidet sich von einer zweidimensionalen auf dem Bildschirm dadurch, dass sie das Zeichnen der geometrischen Projektion eines dreidimensionalen Modells auf eine Ebene mithilfe spezieller Funktionen voraussetzt. Um eine dreidimensionale Abbildung auf einer Ebene zu erhalten, werden folgende Schritte benötigt:

  • Modellierung — Erstellung des dreidimensionalen mathematischen Modells einer Zeitreihe;
  • Rendering (Visualisierung) — Erstellung einer Projektion entsprechend dem ausgewählten Modell;
  • Anzeige der Abbildung auf dem Bildschirm.

Der Artikel hat zum Zweck, gewohnte Charts im dreidimensionalen Raum darzustellen. Da es momentan noch keine fertigen Lösungen für die 3D-Modellierung in MQL5 gibt, fangen wir mit grundlegenden Methoden an, und zwar mit 3D-Objekten und Koordinatensystemen. Es kann sein, dass viele Leser skeptisch zum Thema eingestellt sind und den Artikel nur schnell überfliegen, einige Algorithmen und Methoden können aber auch beim Lösen anderer Aufgaben, die mit der 3D-Visualisierung nicht verbunden sind, nützlich sein.


Interaktives grafisches Objekt

Wir fangen mit 3D-Objekten an. Die zahlreichen MQL5 Funktionen erlauben es, mit zweidimensionalen Objekten zu operieren und komplexe grafische Zeichnungen zu erstellen. Es ist ausreichend, nur wenige Funktionen hinzuzufügen, und die dreidimensionale Grafik wird in einem MT5 Terminal verfügbar sein.

Zunächst einmal listen wir auf, welche Voraussetzungen beim Entwerfen von Basisklassen von 3D Objekten erfüllt werden müssen.

  1. Benutzerfreundlichkeit.
  2. Hohe "Vitalität".
  3. Eigenständigkeit.
  4. Interaktivität.

Benutzerfreundlichkeit.

Für den Entwickler und Nutzer muss ein minimaler Set von Funktionen erstellt werden, der für die Lösung der meisten Aufgaben der 3D-Grafik ausreichend sein wird.

Hohe "Vitalität".

Ein 3D-Objekt muss "unkillable" sein, während das Programm läuft. Es muss vor einem zufälligen oder absichtlichen Löschen sowie vor Änderung von Basiseigenschaften geschützt sein.

Eigenständigkeit.

Das Objekt muss auch einen "Intellekt" haben und sich an die sich ändernden Bedingungen anpassen (Drehung des Koordinatensystems, Änderung der Ankerpunkte usw.). Es muss die notwendigen Informationen aus allen eingehenden Ereignissen ausfiltern und entsprechend reagieren.

Interaktivität.

Die 3D-Visualisierung bedeutet, dass man den Blickwinkel auf das dreidimensionales Modell ändern kann (Drehung des Koordinatensystems). Deswegen muss man solche Funktionen erstellen, die keine zusätzlichen Panels oder andere Tools benötigen. Im Grunde genommen sind grafische Objekte in MQL5 bereits interaktiv: man kann sie markieren, verschieben, ihre Eigenschaften ändern usw. Es reicht, diese Eigenschaft bis auf die Ebene einer kollektiven Steuerung und Zusammenwirkung zu vervollkommnen. Zum Beispiel: wir haben das Koordinatenzentrum verschoben, und alle damit verbundenen Objekte haben sich neu gezeichnet, dabei ohne Fehler.

Wenn diese Voraussetzungen erfüllt sind, wird ein 3D-Objekt zu einem interaktiven grafischen Objekt (IGO). Ein interaktives grafisches Objekt ist unbedingt mit einem grafischen Objekt der MQL5-Programmiersprache verbunden. Betrachten wir die Basisklasse CIGO für interaktive grafische Objekte.

class CIGO
  {
protected:
   bool              on_event;      // Flag der Verarbeitung von Ereignissen
   int               m_layer;       // Schicht, zu welcher das IGO gehört
   //---
   double            SetPrice(double def,int prop_modifier=0);
public:
   string            m_name;        // Name des IG-Objekts
   double            m_price;       // Ankerpunkt des IGO [Preis]
   double            m_angle;       // Wert des Ankerwinkels IGO [Grad]
                     CIGO();
                    ~CIGO();
   //---
   virtual     // Methode: IGO erstellen
   void              Create(string name) {on_event=true;}
   virtual     // Methode: IGO neu zeichnen
   void              Redraw() {ChartRedraw();}
   virtual     // Methode zur Verarbeitung des Ereignisses OnChartEvent
   bool              OnEvent(const int id,         // Identifizierer des Ereignisses
                             const long &lparam,   // Parameter des Ereignisses vom Typ long
                             const double &dparam, // Parameter des Ereignisses vom Typ double
                             const string &sparam, // Parameter des Ereignisses vom Typ string
                             int iparamemr=0,      // Identifizierer des Ereignisses des IGO
                             double dparametr=0.0);// Parameter des Ereignisses des IGO vom Typ double
  };

Die Basisklasse enthält Minimum an Feldern und Methoden, die man später neu definieren und in Nachfolgerklassen ergänzen kann. Wir gehen nur auf zwei Methoden der Klasse ein: die virtuelle Methode OnEvent() zur Verarbeitung von Ereignissen OnChartEvent und Methode zum Setzen eines Ankerpunktes SetPrice(). Wir erläutern diese Methoden, denn gerade in ihnen werden die wichtigsten Grundsätze interaktiver grafischer Objekte umgesetzt.

Methode: Verarbeitung eingehender Ereignisse OnEvent.

Verarbeitet Ereignisse, die bei Operationen mit Charts vom Kundenterminal eingehen. Die Methode reagiert nur auf 4 Standardereignisse: Löschen des grafischen Objekts, Änderung der Chartgröße oder -eigenschaften, Verschiebung des grafischen Objekts und Mausklick auf dem grafischen Objekt. Gehen wir ausführlicher darauf ein.

  1. Löschen des grafischen Objekts. Beim Auftreten dieses Ereignisses ist das Objekt nicht mehr auf dem Chart - es wurde gelöscht. Wenn wir aber die Voraussetzung der "Vitalität" ausfüllen wollen, müssen wir es umgehend wiederherstellen, d.h. erneut erstellen, dabei mit den gleichen Eigenschaft, die es vor dem Löschen hatte. Aber beachten Sie: es wird nur das grafische Objekt und nicht die an das Objekt gebundene Instanz der CIGO Klasse gelöscht. Die Instanz der Klasse bleibt weiter bestehen. Sie beinhaltet Informationen über das gelöschte grafische Objekt, deswegen kann die Instanz es mithilfe der Methode Create() ganz einfach wiederherstellen.
  2. Änderung der Chartgröße oder Charteigenschaften. Es gibt sehr viele Ereignisse von diesem Typ: Auftreten eines neuen Balkens, Änderung des Maßstabs des Charts, Änderung der Timeframe usw. Es muss nur eine Reaktion darauf geben: das Objekt muss unter Berücksichtigung der geänderten Umgebung mithilfe der Methode Redraw() neu gezeichnet werden. Es ist wichtig anzumerken, dass bei der Änderung der Timeframe die Instanz der Klasse neu initialisiert wird. Sie verliert die Daten über das erstellte grafische Objekt, die in Feldern der Klasse gespeichert wurden, obwohl das grafische Objekt selbst auf dem Chart geblieben ist. Aus diesem Grund werden die Felder der Klasseninstanz anhand der Werte der Eigenschaften des grafischen Objekts wiederhergestellt. Dies erhöht die Vitalität des IGO.
  3. Bewegung des grafisches Objekts. Auf diesem Ereignis basiert die Interaktivität des grafischen Objekts. Beim Bewegen werden seine Ankerpunkte geändert. Vor dem Bewegen eines Objekts, muss es ausgewählt werden (mit einem Doppelklick markiert). Wenn dieses Ereignis aufgetreten ist, gibt die Methode true zurück, andernfalls — false. Diesen Wert brauchen wir, wenn wir kollektives Arbeiten interaktiver grafischer Objekte gestalten werden. Es ist zu betonen, wenn wir ein grafisches Objekt benötigen, das nicht bewegt werden darf, reicht es bei der Erstellung des Objekts die Option "Auswählen" zu verbieten.
  4. Mausklick auf dem grafischen Objekt. Wenn man auf einem anderen Objekt klickt, wählen wir das Objekt ab, damit es nicht durch Zufall verschoben wird. Dementsprechend kann nur ein IGO Objekt auf dem Chart markiert werden.
    bool CIGO::OnEvent(const int id,
                       const long &lparam,
                       const double &dparam,
                       const string &sparam,
                       int iparamemr=0,
                       double dparametr=0.0)
      {
       bool res=false;
       if(on_event) // Verarbeitung von Ereignissen erlaubt
         {
          // Löschen des grafischen Objekts
          if((ENUM_CHART_EVENT)id==CHARTEVENT_OBJECT_DELETE && sparam==m_name)
            {
             Create(m_name);
            }
          // Änderung der Chartgröße oder Änderung der Charteigenschaften über den Eigenschaftendialog
          if((ENUM_CHART_EVENT)id==CHARTEVENT_CHART_CHANGE)
            {
             Redraw();
            }
          // Bewegung des grafischen Obejkts
          if((ENUM_CHART_EVENT)id==CHARTEVENT_OBJECT_DRAG)
             if(ObjectGetInteger(0,sparam,OBJPROP_SELECTED)==1 && sparam==m_name)
               {
                m_price=ObjectGetDouble(0,m_name,OBJPROP_PRICE);
                Redraw();
                res=true;   // melden, dass sich der Ankerpunkt geändert hat
               }
          // Mausklick nicht auf diesem grafischen Objekt
          if((ENUM_CHART_EVENT)id==CHARTEVENT_OBJECT_CLICK && sparam!=m_name)
            {
             ObjectSetInteger(0,m_name,OBJPROP_SELECTED,0);
             ChartRedraw();
            }
         }
       return(res);
      }

Parameter: 

 id

   [in] Eventbezeichner. Es gibt 9 Typen von Ereignissen, die mithilfe dieser Methode verarbeitet werden können.

 lparam

   [in] Parameter des Events vom Typ long.

 dparam

   [in] Parameter des Events vom Typ double.

 sparam

   [in] Parameter des Events vom Typ string.

 iparametr

   [in] Bezeichner eines benutzerdefinierten Ereignisses.

 dparametr

   [in] Parameter des Events vom Typ double.

Rückgabewert:

 Gibt true zurück, wenn sich die Koordinaten des Ankerpunktes des Objekts geändert haben. Andernfalls — false. 

 

Methode: setzt die Koordinate des Ankerpunkts SetPrice.

Setzt den Wert "price" im Koordinatensystem "Grafik" für das Feld m_price der Instanz der Klasse.

Ich erkläre, was passiert, wenn die Koordinate des Ankerpunktes anhand dieser Methode abgerufen wird.

  1. Bei der Initialisierung der Instanz beinhaltet das Feld m_price (Koordinate des Ankerpunkts) keinen Eingabewert, deswegen m_price=NULL. Die Initialisierung erfolgt entweder bei der Erstellung einer Instanz der Klasse oder bei der Änderung der Timeframe. Aber das grafische Objekt selbst kann auf dem Chart existieren. Es kann sein, dass es vom letzten Abruf des Programms oder bei der Änderung der Timeframe geblieben ist. Wir weisen dem Feld m_price den Wert der entsprechenden Eigenschaft des grafischen Objekts zu.
  2. Wenn kein grafisches Objekt mit dem Namen m_name existiert, ist die Koordinate des Ankerpunkts nach dem ersten Schritt immer noch nicht definiert: m_price=NULL. In diesem Fall wir das Feld m_price standardmäßig auf def gesetzt.
double CIGO::SetPrice(double def,int prop_modifier=0)
  {
   if(m_price==NULL)             // wenn der Variablenwert nicht vorhanden ist
      m_price=ObjectGetDouble(0,m_name,OBJPROP_PRICE,prop_modifier);
   if(m_price==NULL)             // wenn die Koordinaten nicht vorliegen
      m_price=def;               // standardmäßiger Wert
   return(m_price);
  }

Parameter:

 def

   [in] Standardmäßiger Wert der Variablen.

 prop_modifier

   [in] Modifier der abgerufenen Eigenschaft des grafischen Objekts.

Rückgabewert:

 Koordinate des Ankerpunkts.  

Nun schauen wir uns direkte Nachfolger der Basisklasse für interaktive Objekte an. In erster Linie interessieren uns 3D-Objekte für die Erstellung der notwendigen Umgebung für die 3D-Modellierung.

 

Klasse C_OBJ_ARROW_RIGHT_PRICE: Objekt "Rechtes Preisschild"

Direkter Nachfolger der CIGO-Basisklasse. 

//+------------------------------------------------------------------+
//| Klasse OBJ_ARROW_RIGHT_PRICE: Objekt "Rechtes Preisschild"       |
//+------------------------------------------------------------------+
class C_OBJ_ARROW_RIGHT_PRICE:public CIGO
  {
public:
   virtual     // Methode: Objekt erstellen
   void              Create(string name);
   virtual     // Methode: Objekt neu zeichnen
   void              Redraw();
  };
//+------------------------------------------------------------------+
//| Methode: Objekt erstellen                                         |
//+------------------------------------------------------------------+
void C_OBJ_ARROW_RIGHT_PRICE::Create(string name)
  {
   m_name=name;
   m_price=SetPrice((ChartGetDouble(0,CHART_PRICE_MAX)+ChartGetDouble(0,CHART_PRICE_MIN))/2);
   ObjectCreate(0,m_name,OBJ_ARROW_RIGHT_PRICE,0,0,0);
   ObjectSetInteger(0,m_name,OBJPROP_SELECTABLE,true);
   ObjectSetInteger(0,m_name,OBJPROP_WIDTH,1);
   ObjectSetInteger(0,m_name,OBJPROP_COLOR,clrISO);
//---
   ObjectSetInteger(0,m_name,OBJPROP_TIME,0,T(0));
   ObjectSetDouble(0,m_name,OBJPROP_PRICE,0,m_price);
//---
   ChartRedraw();
   on_event=true; // Verarbeitung von Events erlauben
  }
//+------------------------------------------------------------------+
//| Methode: Objekt neu zeichnen                                     |
//+------------------------------------------------------------------+
void C_OBJ_ARROW_RIGHT_PRICE::Redraw()
  {
   ObjectSetInteger(0,m_name,OBJPROP_TIME,0,T(0));
   ChartRedraw();
  }

Diese Klasse passt am besten für die Gestaltung des Zentrums des 3D-Koordinatensystems. Die Instanz der Klasse ist an den aktuellen Balken gebunden und liegt quasi an der Z-Achse. 

 

Klasse C_OBJ_TREND: Objekt "Trendlinie" 

Direkter Nachfolger der CIGO-Basisklasse.

//+------------------------------------------------------------------+
//| Klasse OBJ_TREND: Objekt "Trendlinie"                        |
//+------------------------------------------------------------------+
class C_OBJ_TREND:public CIGO
  {
public:
   virtual     // Methode: Objekt erstellen
   void              Create(string name);
   virtual     // Methode: Objekt neu zeichnen
   void              Redraw();
  };
//+------------------------------------------------------------------+
//| Methode: Objekt erstellen                                         |
//+------------------------------------------------------------------+
void C_OBJ_TREND::Create(string name)
  {
   m_name=name;
   m_price=(ChartGetDouble(0,CHART_PRICE_MAX)+ChartGetDouble(0,CHART_PRICE_MIN))/2;
   ObjectCreate(0,m_name,OBJ_TREND,0,0,0);
   ObjectSetInteger(0,m_name,OBJPROP_COLOR,clrISO);
   ObjectSetInteger(0,m_name,OBJPROP_WIDTH,0);
   ObjectSetInteger(0,m_name,OBJPROP_STYLE,styleISO);
   ObjectSetDouble(0,m_name,OBJPROP_PRICE,0,m_price);
   ObjectSetInteger(0,m_name,OBJPROP_TIME,0,T(0));
   ObjectSetDouble(0,m_name,OBJPROP_PRICE,1,m_price+1);
   ObjectSetInteger(0,m_name,OBJPROP_TIME,1,T(0));
   ObjectSetInteger(0,m_name,OBJPROP_RAY_RIGHT,true);
   ObjectSetInteger(0,m_name,OBJPROP_RAY_LEFT,true);
   ObjectSetInteger(0,m_name,OBJPROP_BACK,true);
   ObjectSetInteger(0,m_name,OBJPROP_SELECTABLE,false);
//---
   ChartRedraw();
   on_event=true; // Verarbeitung von Events erlauben
  }
//+------------------------------------------------------------------+
//| Methode: Objekt neu zeichnen                                     |
//+------------------------------------------------------------------+
void C_OBJ_TREND::Redraw()
  {
   ObjectSetInteger(0,m_name,OBJPROP_TIME,0,T(0));
   ObjectSetInteger(0,m_name,OBJPROP_TIME,1,T(0));
   ChartRedraw();
  }

Es ist sinnvoller, die Z-Achse mithilfe dieser Klasse zu erstellen. Die Möglichkeiten sind minimal, aber sie reichen für die 3D-Modellierung völlig aus.

 

Klasse асс C_OBJ_TRENDBYANGLE: Objekt "Trendlinie nach dem Winkel" 

Direkter Nachfolger der CIGO-Basisklasse.

//+------------------------------------------------------------------+
//| Klasse OBJ_TRENDBYANGLE: Objekt "Trendlinie nach dem Winkel"         |
//+------------------------------------------------------------------+
class C_OBJ_TRENDBYANGLE:public CIGO
  {
protected:
   int               m_bar;   // Nummer des Balkens für die Bindung des zweiten Ankerpunktes
   //---
   double SetAngle(double def)
     {
      if(m_angle==NULL) // wenn es keinen Wert der Variablen gibt
         m_angle=ObjectGetDouble(0,m_name,OBJPROP_ANGLE);
      if(m_angle==NULL)       // wenn es keine Koordinaten gibt
         m_angle=def;         // Standardwert
      return(m_angle);
     }
public:
                     C_OBJ_TRENDBYANGLE();
   virtual     // Methode: Objekt erstellen
   void              Create(string name,double price,double angle);
   virtual     // Methode: Objekt neu zeichnen
   void              Redraw();
   virtual     // Methode für die Verarbeitung des Ereignisses OnChartEvent
   bool              OnEvent(const int id,
                             const long &lparam,
                             const double &dparam,
                             const string &sparam,
                             int iparamemr=0,
                             double dparametr=0.0);
  };
//+------------------------------------------------------------------+
//| Konstruktor                                                      |
//+------------------------------------------------------------------+
C_OBJ_TRENDBYANGLE::C_OBJ_TRENDBYANGLE()
  {
S   m_bar=c_bar;
  }
//+------------------------------------------------------------------+
//| Methode: Objekt erstellen                                         |
//+------------------------------------------------------------------+
void C_OBJ_TRENDBYANGLE::Create(string name,double price,double angle)
  {
   datetime time=T(0);
   datetime deltaT=T(m_bar)-time;
   m_name=name;
   m_price=SetPrice(price);
   m_angle=SetAngle(angle);
   ObjectCreate(0,m_name,OBJ_TRENDBYANGLE,0,time,m_price,time+deltaT,m_price);
   ObjectSetInteger(0,m_name,OBJPROP_COLOR,clrISO);
   ObjectSetInteger(0,m_name,OBJPROP_WIDTH,0);
   ObjectSetInteger(0,m_name,OBJPROP_STYLE,styleISO);
   ObjectSetInteger(0,m_name,OBJPROP_RAY_RIGHT,true);
   ObjectSetInteger(0,m_name,OBJPROP_RAY_LEFT,true);
   ObjectSetInteger(0,m_name,OBJPROP_BACK,true);
   ObjectSetInteger(0,m_name,OBJPROP_SELECTABLE,true);
//--- ändern wir den Steigungswinkel der Trendlinie; bei der Änderung des Winkels wird die Koordinate des zweiten
//--- Punktes der Linie entsprechend dem neuen Winkelwert neu definiert
   ObjectSetDouble(0,m_name,OBJPROP_ANGLE,m_angle);
   ChartRedraw();
//---
   on_event=true; // Verarbeitung von Events erlauben
  }
//+------------------------------------------------------------------+
//| Methode: Objekt neu zeichnen                                     |
//+------------------------------------------------------------------+
void C_OBJ_TRENDBYANGLE::Redraw()
  {
   ObjectSetInteger(0,m_name,OBJPROP_TIME,T(0));
   ObjectSetInteger(0,m_name,OBJPROP_TIME,1,T(0)+T(m_bar)-T(0));
   ObjectSetDouble(0,m_name,OBJPROP_PRICE,m_price);
   ObjectSetDouble(0,m_name,OBJPROP_ANGLE,m_angle);
   ChartRedraw();
  }
//+------------------------------------------------------------------+
//| Methode zur Verarbeitung des Ereignisses OnChartEvent                             |
//+------------------------------------------------------------------+
bool C_OBJ_TRENDBYANGLE::OnEvent(const int id,
                                 const long &lparam,
                                 const double &dparam,
                                 const string &sparam,
                                 int iparamemr=0,
                                 double dparametr=0.0)
  {
//---
   bool res=false;
   if(on_event) // Verarbeitung der Ereignisse erlaubt
     {
      // Löschen des grafischen Objekts
      if((ENUM_CHART_EVENT)id==CHARTEVENT_OBJECT_DELETE && sparam==m_name)
        {
         Create(m_name,m_price,m_angle);
        }
      // Änderung der Chartgröße oder Änderung der Eigenschaften über den Eigenschaftendialog
      if((ENUM_CHART_EVENT)id==CHARTEVENT_CHART_CHANGE)
        {
         Redraw();
        }
      // Bewegung des grafischen Objekts
      if((ENUM_CHART_EVENT)id==CHARTEVENT_OBJECT_DRAG)
        {
         //---
         if(ObjectGetInteger(0,sparam,OBJPROP_SELECTED)==1 && sparam==m_name)
           {
            m_angle=ObjectGetDouble(0,m_name,OBJPROP_ANGLE);
            Create(m_name,m_price,m_angle);
            res=true;   // benachrichtigen, dass der Ankerpunkt geändert wurde
           }
         if(iparamemr==Event_1)// Benachrichtigung über die Änderung des Ankerpunkts erhalten
           {
            m_price=dparametr;
            Create(m_name,m_price,m_angle);
           }
         if(iparamemr==Event_2)// Benachrichtigung über die Änderung des Ankerpunkts erhalten
           {
            m_angle=dparametr;
            Create(m_name,m_price,m_angle);
           }
        }
      // Mausklick nicht auf diesem grafischen Objekt
      if((ENUM_CHART_EVENT)id==CHARTEVENT_OBJECT_CLICK && sparam!=m_name)
        {
         ObjectSetInteger(0,m_name,OBJPROP_SELECTED,0);
         ChartRedraw();
        }
     }
   return(res);
  }

Ursprünglich war geplant, Nachfolger der Klasse "Trendlinie" für die X- und Y-Achsen zu verwenden, aber im Laufe der Arbeit wurde es klar, dass die "Trendlinie nach dem Winkel" für diese Ziele ideal passt. Beim Durchlesen der MQL5 Dokumentation habe ich dieses mächtige Tool für mich entdeckt: nachdem ich die Trendlinie nach dem Winkel gezeichnet hatte, blieb diese bei der Änderung der Timeframe oder des Maßstabs unverändert, d.h. der Winkel änderte sich nicht.

Schlussfolgerung: Lesen Sie die Dokumentation durch, und Sie entdecken eine Vielzahl nützlicher Funktionen und Tools. 

 

Grafischer Speicher (Graphical Memory)

Der grafischen Speicher wurde bereits im Artikel "Statistische Verteilung in Form eines Histogramms ohne Indikator-Puffer und Arrays" beschrieben und anhand Beispiele veranschaulicht. Ich wiederhole hier nur das grundlegende Prinzip: in den Eigenschaften grafischer Objekte kann man Informationen für die weitere Verwendung in anderen Objekten oder Funktionen des Programms speichern.

Zwecks der Bequemlichkeit und der praktischen Anwendung des grafischen Speichers wurde eine spezielle Klasse erstellt:

class CGM
  {
private:
public:
   string            m_name;        // Name des grafischen Objekts
                     CGM(){}
                    ~CGM(){}
   void              Create(string name) {m_name=name;}
   // Lesen der Eigenschaft OBJPROP_PRICE
   double            R_OBJPROP_PRICE(int prop_modifier=0)
     {
      return(ObjectGetDouble(0,m_name,OBJPROP_PRICE,prop_modifier));
     }
   // Lesen der Eigenschaft OBJPROP_TIME
   datetime          R_OBJPROP_TIME(int prop_modifier=0)
S     {
      return((datetime)ObjectGetInteger(0,m_name,OBJPROP_TIME,prop_modifier));
     }
   // Lesen der Eigenschaft OBJPROP_ANGLE
   double            R_OBJPROP_ANGLE()
     {
      return(ObjectGetDouble(0,m_name,OBJPROP_ANGLE));
     }
   // Lesen der Eigenschaft OBJPROP_TEXT
   string            R_OBJPROP_TEXT()
     {
      return(ObjectGetString(0,m_name,OBJPROP_TEXT));
     }
   // gibt den Preis für die angegebene Zeit zurück
   double            R_ValueByTime(datetime time)
     {
      return(ObjectGetValueByTime(0,m_name,time));
     }
   // Lesen der Eigenschaft OBJPROP_TEXT
   void              W_OBJPROP_TEXT(string text)
     {
      ObjectSetString(0,m_name,OBJPROP_TEXT,text);
     }
  };

Wie man sieht, sind Methoden zum Lesen und Schreiben von Eigenschaften grafischer Objekte im Code vorhanden. Diese Klasse ist sehr praktisch, denn sie wird bei der Erstellung an ein grafisches Objekt gebunden und ermöglicht es, die notwendigen Informationen für beliebige interaktive grafische Objekte schnell zu erhalten/zu übermitteln. Zum Beispiel:

  • es wurde das 3D-Objekt А erstellt, an ihn wurde die Instanz der Klasse des grafischen Speichers AA gebunden;
  • nun wird das 3D-Objekt B erstellt, das über AA Information über das Objekt A erhalten kann. Die Objekte A und B sind nicht miteinander verbunden, aber über die Instanz der Klasse AA werden wichtige Daten von A zu B übermittelt;
  • bei der Erstellung des Objekts B wird eine Instanz der Klasse des grafischen Speichers BB erstellt. Nun hat das Objekt A Zugang zur Information des Objekts B usw.;
  • Es kann beliebig viele Objekte geben, jedes Objekt kann dabei die notwendigen Informationen über Klasseninstanzen des grafischen Speichers empfangen und übertragen.

Man kann sich den grafischen Speicher als das Schwarze Brett vorstellen, auf welchem Gebote und Gesuche nach Informationen platziert sind. Auf diese Weise erfolgt der Datenaustausch zwischen interaktiven grasfischen Objekten. Die notwendigen Daten für eine korrekte Arbeit des 3D-Objekts werden bei der Entwicklung eines 3D-Modell programmiert. Über besonders wertvolle Informationen verfügen die Objekte des Koordinatensystems, denn bei der Drehung oder bei der Verschiebung des Koordinatenzentrums müssen alle 3D-Objekte ihre Eigenschaften und die Position im dreidimensionalen Raum ändern.

Schauen Sie sich an, wie der grafische Speicher in der Klasse des interaktiven Koordinatensystems CUSC verwendet wird.

Koordinatensystem

Die dreidimensionale visuelle Analyse erlaubt es, die Daten im 3D-Raum zu analysieren: zum Beispiel, Sequenzen von Eingabedaten (Beobachtungen) für eine oder mehrere ausgewählte Variablen visuell darstellen. Die ausgewählten Variablen werden an der Y-Achse dargestellt, die Reihen von Beobachtungen — an der X-Achse, und die Werte der Variablen werden an die Z-Achse projiziert.

Solche dreidimensionalen Charts werden für die Visualisierung der Sequenzen von Werten mehrerer Variablen verwendet.

Der wichtigste Vorteil der dreidimensionalen Darstellung gegenüber den zweidimensionalen linearen Charts besteht darin, das es für mehrere Massendaten einfacher ist, auf einem dreidimensionalen Bild einzelne Sequenzen von Werten zu erkennen. Bei der Auswahl eines passenden Blickwinkels z. B. anhand einer interaktiven Drehung, werden sich die Grafiklinien nicht überlappen bzw. zusammenfallen, wie es häufig auf linearen zweidimensionalen Charts der Fall ist. 

Bevor wir mit der 3D-Modellierung beginnen, schauen wir uns die Koordinatensysteme an, mit welchen wir Arbeiten werden. Zwei Koordinatensysteme, "Grafik" und "Bild", wurden von Entwicklern der MQL5-Programmiersprache erstellt, und das dreidimensionale System (Axonometrie) setzen wir selbständig um. Gehen wir auf die Unterschiede und den Verwendungszweck jedes Systems ein.

Abb. 2. Das Koordinatensystem "Grafik".

Abb. 2. Das Koordinatensystem "Grafik".

Das Koordinatensystem "Grafik" (Abb. 2). Dies ist ein zweidimensionales Koordinatensystem für Anzeige von Preisdaten, Timeframes und Indikatoren. An der waagerechten Achse liegt die Zeitskala (time), gerichtet von links nach rechts, an der senkrechten Skala — Preis (price) eines Finanzinstruments. Gerade in diesem Koordinatensystem arbeiten die meisten grafischen MQL5-Objekte. Seine Besonderheit besteht darin, dass der aktuelle Preis und Balken an derselben senkrechten Achse liegen. Beim Auftreten eines neuen Balkens wird der Chart automatisch nach links verschoben.

Abb. 3. Das Koordinatensystem "Bild".

Abb. 3. Das Koordinatensystem "Bild".

Das Koordinatensystem "Bild" (Abb. 3). Jedem Punkt auf dem Bildschirm entspricht ein Pixel. Die Koordinaten der Punkte werden von der linken oberen Ecke der Grafik berechnet. Das System ist genauso wie das System "Grafik" zweidimensional und wird von den Objekten verwendet, die an Timeseries nicht gebunden sind. Dieses Koordinatensystem brauchen wir im Rahmen des Artikels nicht.

Abb. 4. Dreidimensionales Koordinatensystem.

Abb. 4. Dreidimensionales Koordinatensystem.

Dreidimensionales Koordinatensystem (Abb. 4). In diesem Koordinatensystem liegen drei Achsen senkrecht zueinander. Aber visuell sind die Winkel zwischen den Achsen XYZ im Koordinatensystem "Bild" nicht gleich 90°. Die X-Achse und Y-Achse bilden, z.B. einen Winkel von 120°. Dieser Winkel kann auch einen anderen Wert haben, aber im Rahmen dieses Artikels lassen wir in so.

Die Z-Achse ist an den aktuellen Balken gebunden, der Maßstab der Achse fällt mit der Skala "price" im Koordinatensystem "Grafik" zusammen. Das ist sehr bequem, denn man muss keine separate Skala für die Z-Achse erstellen, und einfach die Skala "price" verwenden, dabei Z=price.

Die X-Achse ist von rechts nach links gerichtet, d.h. gegenüber der "time" Skala im Koordinatensystem "Grafik". An diese Achse werden wir den Wert der Variablen Bar projizieren. D.h. die Projektion jedes Balkens an die X-Achse werden mit seinem Wert (X=Bar) übereinstimmen.

Die Y-Achse ist für die Reihen zweidimensionaler Daten XZ vorgesehen. Man kann zum Beispiel die Linien der Timeseries Open, Close, High und Low an dieser Achse auseinanderschieben, und sie werden jede in ihrer Ebene liegen. Wenn man alle Punkte dieser Linien in den Ebenen verbindet, die zur YZ-Ebene parallel sind, bekommen wir ein Gewebe der Oberflächen, mit anderen Worten ein dreidimensionales Objekt der Timeseries (s. Abb. 5). 

Abb. 5. Ein 3D-Objekt in einem dreidimensionalen Koordinatensystem.

Abb. 5. Ein 3D-Objekt in einem dreidimensionalen Koordinatensystem.

 

Interaktives Koordinatensystem: CUSC Klasse

Direkter Nachfolger der CIGO-Basisklasse. 

class CUSC:public CIGO
  {
private:
   datetime          m_prev_bar;
   datetime          m_next_bar;
   //---
   C_OBJ_ARROW_RIGHT_PRICE Centr;   // Deklaration der Instanz der Klasse C_OBJ_ARROW_RIGHT_PRICE
   C_OBJ_TREND       AxisZ;         // Deklaration der Instanz der Klasse C_OBJ_TREND
   C_OBJ_TRENDBYANGLE AxisY,AxisX;  // Deklaration der Instanz der Klasse C_OBJ_TRENDBYANGLE
   //---
   CGM               gCentr;        // Deklaration der Instanz der Klasse   CGM               gAxisY,gAxisX; // Deklaration der Instanz der Klasse CGM

public:
                     CUSC();
                    ~CUSC();
   //--- Berechnung der Z-Koordinate
   sPointCoordinates Z(double price,   // Preis im Koordinatensystem "Grafik"
                       int barX,       // Verschiebung an der X-Achse
                       int barY);      // Verschiebung an der Y-Achse
   //--- neuer Balken
   bool on_bar()
     {
      m_next_bar=T(0);
      if(m_next_bar>m_prev_bar)
        {
         m_prev_bar=m_next_bar;
         return(true);
        }
      return(false);
     }
   //---
   virtual     // Methode: IGO erstellen
   void              Create(string name);
   virtual     // Methode zur Verarbeitung des Ereignisses OnChartEvent
   void              OnEvent(const int id,
                             const long &lparam,
                             const double &dparam,
                             const string &sparam);
  };

Wir schauen uns nur drei Methoden dieser Klasse an: Create(), OnEvent() und Z().

Methode Create: Erstellung eines dreidimensionalen Koordinatensystems

Erstellt ein 3D-Objekt des interaktiven Koordinatensystems. Die Interaktivität bedeutet die Drehung des Koordinatensystems um die Z-Achse und die Verschiebung des Koordinatenursprungs.

void CUSC::Create(string name)
  {
//--- Zentrum des dreidimensionalen Koordinatensystems
   Centr.Create("Axis XYZ");        // erstellen wir das Zentrum des Koordinatensystems
   gCentr.Create(Centr.m_name);     // erstellen wir ein Objekt des grafischen Speichers
   m_price=gCentr.R_OBJPROP_PRICE();
//--- Z-Achse
   AxisZ.Create("Axis Z");          // erstellen wir die Z-Achse des Koordnatensystems
//--- Y-Achse
   AxisY.Create("Axis Y",                 // erstellen wir die Y-Achse des Koordnatensystems
                gCentr.R_OBJPROP_PRICE(), // erhalten wir den Wert aus dem GM
                30);                      // setzten wir den Steigungswinkel auf 30°
   gAxisY.Create(AxisY.m_name);           // erstellen wir ein Objekt des grafischen Speichers
   m_angle=gAxisY.R_OBJPROP_ANGLE();
//--- X-Achse
   AxisX.Create("Axis X",                       // erstellen wir die X-Achse des Koordnatensystems
                gCentr.R_OBJPROP_PRICE(),       // erhalten wir den Wert aus dem GM
                gAxisY.R_OBJPROP_ANGLE()+ISO);  // erhalten wir den Wert aus dem GM und erhöhen ihn um ISO Grad
   gAxisX.Create(AxisX.m_name);                 // erstellen wir ein Objekt des grafischen Speichers
//---
   ChartRedraw();
   on_event=true; // Verarbeitung von Events erlauben
  }

Parameter:

 name

   [in] Name des Koordinatensystems.

Rückgabewert:

 Kein Rückgabewert. Wenn erfolgreich wird ein interaktives Koordinatensystem erstellt.

 

Methode OnEvent: Verarbeitung eingehender Ereignisse

Verarbeitet Ereignisse, die bei Operationen mit Charts vom Kundenterminal eingehen. Die Methode reagiert nur auf ein Standardereignis: Bewegung des grafischen Objekts. Andere Ereignisse werden weiter an alle Instanzen der Klasse übermittelt, die beim Programmieren des Koordinatensystems erstellt wurden.

void CUSC::OnEvent(const int id,
                   const long &lparam,
                   const double &dparam,
                   const string &sparam)
  {
   if(on_event) // Verarbeitung der Ereignisse erlaubt
     {
      //--- Übertragen der Ereignisse OnChartEvent
      AxisZ.OnEvent(id,lparam,dparam,sparam);
      //---
      if(Centr.OnEvent(id,lparam,dparam,sparam))
        {// Bewegung des grafischen Objekts
         AxisY.OnEvent(id,lparam,dparam,sparam,Event_1,gCentr.R_OBJPROP_PRICE());
         AxisX.OnEvent(id,lparam,dparam,sparam,Event_1,gCentr.R_OBJPROP_PRICE());
        }
      else
        {
         if(AxisY.OnEvent(id,lparam,dparam,sparam))
           {// Änderung des Winkels des grafischen Objekts
            AxisX.OnEvent(id,lparam,dparam,sparam,Event_2,gAxisY.R_OBJPROP_ANGLE()+ISO);
           }
         else
           {
            if(AxisX.OnEvent(id,lparam,dparam,sparam))
              {// Änderung des Winkels des grafischen Objekts
               AxisY.OnEvent(id,lparam,dparam,sparam,Event_2,gAxisX.R_OBJPROP_ANGLE()-ISO);
              }
           }
        }
      ChartRedraw();
      m_price=gCentr.R_OBJPROP_PRICE();
      m_angle=gAxisY.R_OBJPROP_ANGLE();
     }
  }

Parameter: 

 id

   [in] Eventbezeichner. Es gibt 9 Typen von Ereignissen, die mithilfe dieser Methode verarbeitet werden können.

 lparam

   [in] Parameter des Events vom Typ long.

 dparam

   [in] Parameter des Events vom Typ double.

 sparam

   [in] Parameter des Events vom Typ string.

 iparametr

   [in] Bezeichner eines benutzerdefinierten Ereignisses.

 dparametr

   [in] Parameter des Events vom Typ double.

Rückgabewert:

 Kein Rückgabewert.

 

Struktur für das Erhalten von Koordinatenwerten (sPointCoordinates).

Eine Struktur für das Speichern von Koordinatenwerten im Koordinatensystem "Grafik". Dient zum Erhalten der Koordinaten eines 3D-Punktes. 

struct sPointCoordinates
  {
   datetime          time;    // Koordinate im System "Grafik"
   double            price;   // Koordinate im System "Grafik"
  };

Die Variable vom Typ sPointCoordinates ermöglicht es, mit einem Aufruf der Z() Funktion Koordinatenwerte des 3D-Puntkes im Koordinatensystem "Grafik" zu erhalten.

 

Methode Z: Berechnung der Z-Koordinate. 

Die Methode berechnet die Z-Koordinate.

sPointCoordinates CUSC::Z(double price,int barX,int barY)
  {
   sPointCoordinates res;
   res.price=0;
   res.time=0;
   double dX,dY;
   dX=0;
   dX=gAxisX.R_ValueByTime(T(barX))-m_price;
   dY=0;
   dY=gAxisY.R_ValueByTime(T(-barY))-m_price;
   res.price=price+dX-dY;
   res.time=T(barX-barY);
   return(res);
  }

Parameter: 

 price

   [in] Z-Koordinate des 3D-Objekts.

 barX

   [in] X-Koordinate des 3D-Objekts. Der Wert in Balken festgelegt.

 barY

   [in] Y-Koordinate des 3D-Objekts. Der Wert in Balken festgelegt.

Rückgabewert:

  Wenn erfolgreich, gibt den Wert der Variablen vom Typ sPointCoordinates zurück. 

 

Ein Beispiel für die Erstellung einer 3D-Oberfläche als Gitter

Schauen wir und die Zeichnung einer dreidimensionale Oberfläche als Beispiel an:

fun[i][j]=close[i*StepX]-_Point*j*j
Diese Formel hat keinen praktischen Sinn, dies ist eine einfache und gute Veranschaulichung von Möglichkeiten der erstellten Klassen. Der Code ist relativ kurz und gut verständlich und ist sowohl für erfahrene als auch für angehende Programmierer gut geeignet. Wenn wir diesen Indikator starten, bekommen wir einen 3D-Gitter der Oberfläche nach der angegebenen Funktion (s. Abb. 6). Man kann den Blickwinkel durch die Änderung des Winkels der Achsen Y oder X ändern. Dabei wird das Objekt quasi um die Z-Achse gedreht. Die Verschiebung des Koordinatenursprungs führt zu einer Änderung der Farbe des Modells: rot — die Werte der Punkte an der Z-Achse sind oberhalb des Koordinatenursprungs 0, blau — unterhalb des Koordinatenursprungs.
//--- Konstanten deklarieren
#define  StepX    10    // Schritt an der Х-Achse [bar]
#define  StepY    5     // Schritt an der Y-Achse [bar]
#define  _X       50    // Anzahl der Punkte an der Х-Achse
#define  _Y       15    // Anzahl der Punkte an der Y-Achse
#define  W1       1     // Linienbreite
#define  W2       3     // Breite der Randlinien
//--- Klassendateien hinzufügen
#include <3D\USC.mqh>
//---
#property indicator_chart_window
//--- Anzahl der Puffer für die Berechnung des Indikators
#property indicator_buffers 0
//--- Anzahl grafischer Reihen im Indikator
#property indicator_plots   0
//---
CUSC        USC;
double fun[_X][_Y];
//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
int OnInit()
  {
   USC.Create("3D");
//---
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+
//| Custom indicator iteration function                              |
//+------------------------------------------------------------------+
int OnCalculate(const int rates_total,
                const int prev_calculated,
                const datetime &time[],
                const double &open[],
                const double &high[],
                const double &low[],
                const double &close[],
                const long &tick_volume[],
                const long &volume[],
                const int &spread[])
  {
   ArraySetAsSeries(close,true);
//--- Funktion der 3D-Fläche
   for(int i=0;i<_X;i++)
      for(int j=0;j<_Y;j++)
        {
         fun[i][j]=close[i*StepX]-_Point*j*j;
         //fun[i][j]=close[i*StepX]-_Point*j*j*i/7;
        }

////--- X-Linien
   for(int i=1;i<_X;i++)
      for(int j=0;j<_Y;j++)
        {
         sPointCoordinates a0=USC.Z(fun[(i-1)][j], // Funktion
                                    (i-1)*StepX,   // X
                                    -j*StepY);     // Y
         sPointCoordinates a1=USC.Z(fun[i][j],     // Funktion
                                    i*StepX,       // X
                                    -j*StepY);     // Y
         string name="line x "+"x"+(string)i+"y"+(string)+j;
         ObjectCreate(0,name,OBJ_TREND,0,a0.time,a0.price,a1.time,a1.price);
         if(fun[i][j]>USC.m_price && fun[i-1][j]>USC.m_price)
            ObjectSetInteger(0,name,OBJPROP_COLOR,clrRed);
         else
            ObjectSetInteger(0,name,OBJPROP_COLOR,clrBlue);
         ObjectSetInteger(0,name,OBJPROP_WIDTH,W1);
         if(j==0 || j==_Y-1)
            ObjectSetInteger(0,name,OBJPROP_WIDTH,W2);
         ObjectSetInteger(0,name,OBJPROP_STYLE,STYLE_SOLID);
         ObjectSetInteger(0,name,OBJPROP_RAY_RIGHT,false);
         ObjectSetInteger(0,name,OBJPROP_RAY_LEFT,false);
         ObjectSetInteger(0,name,OBJPROP_BACK,true);
         ObjectSetInteger(0,name,OBJPROP_SELECTABLE,false);
        }
////--- Y-Linien
   for(int i=0;i<_X;i++)
      for(int j=1;j<_Y;j++)
        {
         sPointCoordinates a0=USC.Z(fun[i][j-1],   // Funktion
                                    i*StepX,       // X
                                    -(j-1)*StepY); // Y
         sPointCoordinates a1=USC.Z(fun[i][j],     // Funktion
                                    i*StepX,       // X
                                    -j*StepY);     // Y
         string name="line y "+"x"+(string)i+"y"+(string)+j;
         ObjectCreate(0,name,OBJ_TREND,0,a0.time,a0.price,a1.time,a1.price);
         ObjectSetInteger(0,name,OBJPROP_COLOR,clrGreen);
         ObjectSetInteger(0,name,OBJPROP_WIDTH,1);
         ObjectSetInteger(0,name,OBJPROP_STYLE,STYLE_SOLID);
         ObjectSetInteger(0,name,OBJPROP_RAY_RIGHT,false);
         ObjectSetInteger(0,name,OBJPROP_RAY_LEFT,false);
         ObjectSetInteger(0,name,OBJPROP_BACK,true);
         ObjectSetInteger(0,name,OBJPROP_SELECTABLE,false);
        }
//--- return value of prev_calculated for next call
   return(rates_total);
  }
//+------------------------------------------------------------------+
//| ChartEvent function                                              |
//+------------------------------------------------------------------+
void OnChartEvent(const int id,
                  const long &lparam,
                  const double &dparam,
                  const string &sparam)
  {
   USC.OnEvent(id,lparam,dparam,sparam);
  }

Die Linien in den Ebenen, die parallel zu den XZ und YZ Ebenen sind, werden getrennt gezeichnet. Man kann die Anzahl der Punkte an jeder Achse und die Funktion selbst variieren.

Abb. 6. Ein Beispiel für das Zeichnen einer 3D-Oberfläche.

Abb. 6. Ein Beispiel für das Zeichnen einer 3D-Fläche.

Abb. 7. Ein Beispiel für das Zeichnen des 3D Moving Average Indikators.

Abb. 7. Ein Beispiel für das Zeichnen des 3D Moving Average Indikators. 

Die Antwort auf die Frage von Skeptikern: Wofür braucht man die 3D-Modellierung und wie kann diese im Handel helfen?

Natürlich, ist das MetaTrader 5 Terminal für den Handel auf den Finanzmärkten vorgesehen. Für Händler und Entwickler automatischer Handelsstrategien interessieren in erster Linie Handelsalgorithmen und die Entwicklung profitabler Strategien. Die im Artikel beschriebene Richtung steckt noch in den Anfängen und liefert noch keine konkreten Ergebnisse für den Handel, aber die 3D-Modellierung kann bei der Präsentation Ihrer Handelsideen und Strategien den Investoren hilfreich sein. Darüber hinaus werden dynamische 3D-Modelle im Terminal erstellt, die auf das Eingehen von Marktinformationen reagieren und sich in der Zeit deformieren (ihren Körper verformen), dies ermöglicht die Erstellung von 3D-Indikatoren.

Als Pro-Argument möchte ich einen Vergleich zu Windows ziehen: die ersten Versionen des Systems waren ungeschickt und langsam. Die Anhänger von Norton Commander waren zur Idee grafischer Interfaces skeptisch eingestellt. Und wo ist nun NC?

Fazit

  1. Die 3D-Modellierung ist eine schwierige Aufgabe in jeder Programmierungsumgebung, aber die Entwickler der MQL5-Programmiersprache waren der Zeit einen Schritt voraus und haben ihren Kunden leistungsstarke Funktionen für die Umsetzung der dreidimensionalen Visualisierung im Handelsterminal angeboten.
  2. Wir haben die ersten Ergebnisse der Programmierung einer Bibliothek der Klassen für 3D-Objekte erhalten. Das Thema ist so groß, dass ein Artikel nicht alle Möglichkeiten und Perspektiven der 3D-Modellierung umfassen kann. Ich hoffe, es finden sich Nachfolger, die Wege für die Entwicklung der 3D-Richtung im Trading und in der Programmierung in MQL5 vorschlagen können.
  3. Das erstellte Werkzeug der 3D-Modellierung kann die Entwicklung einer neuen Richtung in der technischen Analyse wesentlich beeinflussen: Es geht um 3D-Indikatoren und deren Analyse.
  4. Es gibt Probleme mit dem Rendering, aber an dieser Etappe der Entwicklung der Bibliothek ist das nicht so dramatisch.
  5. Der im Artikel vorgestellte Grundsatz für das Zeichnen von 3D-Objekten kann auch höchstwahrscheinlich auf OpenCL übertragen werden.

Hinweis:

Die Dateien der Bibliothek müssen in den Ordner ..\Include\3D kopiert werden.

Übersetzt aus dem Russischen von MetaQuotes Software Corp.
Originalartikel: https://www.mql5.com/ru/articles/2828

Beigefügte Dateien |
GM.mqh (3.77 KB)
IGO.mqh (29.95 KB)
USC.mqh (14.63 KB)
testIGO.mq5 (9.91 KB)
Automatische Ermittlung von Extremwerten basierend auf einem angegebenen Kursrückgang Automatische Ermittlung von Extremwerten basierend auf einem angegebenen Kursrückgang

Bei der Automatisierung von Handelsstrategien, die grafische Muster verwenden, ist es notwendig, Extremwerte auf den Charts für eine weitere Verarbeitung und Interpretation zu ermitteln. Bestehende Tools bieten nicht immer diese Möglichkeit. Die im Artikel beschriebenen Algorithmen erlauben es, alle Extremwerte auf den Charts zu ermitteln. Die entwickelten Tools sind effektiv sowohl in einem Trend, als auch in einem Seitwärtsmarkt. Die erhaltenen Ergebnisse hängen von der gewählten Timeframe ab und werden nur durch den angegebenen Rückgang definiert.

Grafische Interfaces X: Text Edit Box, Bild Slider und einfache Controls (build 5) Grafische Interfaces X: Text Edit Box, Bild Slider und einfache Controls (build 5)

In diesem Artikel besprechen wir neue Controls: Text Edit Box, Bild-Slider, sowie weitere zusätzliche einfache Controls: Text-Label und Bild. Die Bibliothek wächst weiter, und neben der Einführung der neuen Steuerelemente, werden auch die zuvor erstellten verbessert.

Grafische Interfaces X: Elemente der Zeit, Listen von Kontrollkästchen und das Sortieren von Tabellen (build 6) Grafische Interfaces X: Elemente der Zeit, Listen von Kontrollkästchen und das Sortieren von Tabellen (build 6)

Weiterentwicklung der Bibliothek zum Erstellen grafischer Benutzeroberflächen. Zeit und Listen von Kontrollkästchen werden diesmal behandelt. Weiters verfügt die Klasse CTable jetzt über die Möglichkeit, Daten auf- oder absteigend zu sortieren.

Grafische Interfaces X: Erweitertes Management von Listen und Tabellen Code Optimierung (build 7) Grafische Interfaces X: Erweitertes Management von Listen und Tabellen Code Optimierung (build 7)

Der Code der Bibliothek muss optimiert werden: Er sollte besser dem Standard folgen, das heißt — leichter lesbar und schneller zu verstehen. Weiters werden wir die als letztes entwickelten Kontrollelemente weiterentwickeln: Listen, Tabellen und Bildlaufleisten.