MetaTrader 5 herunterladen

Die Erstellung der benutzerdefinierten Indikatoren unter Verwendung der Klasse CCanvas

16 Juni 2017, 08:39
Alexander Fedosov
0
188

Inhalt

Einführung

Der untrennbare Bestandteil der modernen Traiding im Terminal MetaTrader 5 — ist die benutzerdefinierten Indikatoren. Sie werden sowohl in automatischen Handelssystemen (wie ein Teil der Algorithmen)verwendet , auch bei einem manuellen Handel. Momentan konnte man bei der Erstellung eines Indikators die Stile des Zeichnens eingeben und, 18 Typen des graphischen Aufbaus verwenden. Damit werden die graphischen Möglichkeiten des Terminalen nicht beschränkt. Für die Erstellung der eigenen, nicht standarten Indikatoren mit den unendlichen Möglichkeiten der Visualisierung wurde die Bibliothek des Benutzer-Charts CCanvas erstellt. Das Ziel dieses Artikels — die Benutzer mit den Möglichkeiten dieser Bibliothek bekannt zumachen und die Beispiele ihrer Verwendung zu bringen, sowie eine separate Bibliothek der benutzerdefinierten Indikatoren verschiedener Typen zu entwickeln.


Der Aufbau der grundlegenden Klasse des Benutzer-Charts

Als grundlegende Klasse muss man den Satz der Methoden schreiben, die die Grundlage eines Objektes des Benutzer-Charts erstellen, und dabei verfügen auch den allgemeinen Satz der Eigenschaften. Dazu werden wir im <Datenverzeichnis>\MQL5\Include den Ordner CustomGUI erstellen, und darin die Datei CanvasBase.mqh. Diese Datei wird die grundlegende Klasse CCanvasBase für alle zukünftigen Typen des Benutzer-Charts enthalten.

//+------------------------------------------------------------------+
//|                                                  CCanvasBase.mqh |
//|                                Copyright 2017, Alexander Fedosov |
//|                           https://www.mql5.com/de/users/alex2356 |
//+------------------------------------------------------------------+
#include <Canvas\Canvas.mqh>
//+------------------------------------------------------------------+
//| Die grundlegende Klasse für die Erstellung eines Benutzer-Charts |
//+------------------------------------------------------------------+
class CCanvasBase
  {
private:
   //--- Der Name der Leinwand
   string            m_canvas_name;
   //--- Die Koordinaten der Leinwand
   int               m_x;
   int               m_y;
   //--- Der Umfang der Leinwand
   int               m_x_size;
   int               m_y_size;
protected:
   CCanvas           m_canvas;
   //--- Er erstellt die graphische Ressource für das Objekt
   bool              CreateCanvas(void);
   //--- Er entfernt die graphische Ressource
   bool              DeleteCanvas(void);
public:
                     CCanvasBase(void);
                    ~CCanvasBase(void);
   //--- Er setzt und liefert die Koordinaten zurück
   void              X(const int x)                         { m_x=x;                      }
   void              Y(const int y)                         { m_y=y;                      }
   int               X(void)                                { return(m_x);                }
   int               Y(void)                                { return(m_y);                }
   //--- Er setzt und liefert die Umfänge zurück
   void              XSize(const int x_size)                { m_x_size=x_size;            }
   void              YSize(const int y_size)                { m_y_size=y_size;            }
   int               XSize(void)                            { return(m_x_size);           }
   int               YSize(void)                            { return(m_y_size);           }
   //--- Er setzt den Namen des Indikators bei der Erstellung 
   void              Name(const string canvas_name) { m_canvas_name=canvas_name;  }
  };
//+------------------------------------------------------------------+
//| Constructor                                                      |
//+------------------------------------------------------------------+
CCanvasBase::CCanvasBase(void) : m_x(0),
                                 m_y(0),
                                 m_x_size(200),
                                 m_y_size(200)
  {
  }
//+------------------------------------------------------------------+
//| Destructor                                                       |
//+------------------------------------------------------------------+
CCanvasBase::~CCanvasBase(void)
  {
  }
//+------------------------------------------------------------------+
//| Er erstellt die graphische Ressource für das Objekt              |
//+------------------------------------------------------------------+
bool CCanvasBase::CreateCanvas(void)
  {
   ObjectDelete(0,m_canvas_name);
   if(!m_canvas.CreateBitmapLabel(m_canvas_name,m_x,m_y,m_x_size,m_y_size,COLOR_FORMAT_ARGB_NORMALIZE))
      return(false);
   ObjectSetInteger(0,m_canvas_name,OBJPROP_CORNER,CORNER_LEFT_UPPER);
   ObjectSetInteger(0,m_canvas_name,OBJPROP_ANCHOR,ANCHOR_CENTER);
   ObjectSetInteger(0,m_canvas_name,OBJPROP_BACK,false);
   return(true);
  }
//+------------------------------------------------------------------+
//| Er entfernt die graphische Ressource                             |
//+------------------------------------------------------------------+
bool CCanvasBase::DeleteCanvas()
  {
   return(ObjectDelete(0,m_canvas_name)?true:false);
  }
//+------------------------------------------------------------------+

Wie Sie sehen, außer den Anfangskoordinaten des Objektes, seinen Namen und der Umfänge, unter den erstellten Methoden gibt es die Methoden der Erstellung und der Entfernung der graphischen Ressource — das ist ungefähr wie die Grundlage oder die Leinwand, auf der die Elemente des Benutzer-Charts gezeichnet werden. Im Folgenden werden bei einem Anfangsaufbau eines graphischen Objektes die Methoden CreateCanvas() und DeleteCanvas() nötigerweise verwendet. Deshalb Variabel, die in diesen Methoden angewendet sind, müssen initialisiert sein, wie es im Konstrukteur gemacht wurde.


Die Klasse des einfachen runden Indikators CCircleSimple

Also, um die Aufbauprinzipien des Benutzer-Charts zu verstehen, werden wir uns vom Einfachen zum Komplizierten bewegen. Wir werden den einfachen runden Indikator mit einem Rahmen, eines Zahlenwertes und einer Beschreibung erstellen. Auf der Abb.1 wurde die Struktur der grundlegenden Elemente vorgestellt, aus denen er bestehen wird.

  • Der Rahmen. Eine Art einer umrissenen Umrandung.
  • Der Hintergrund. Der Raum, in dem sich die Textelemente befinden werden.
  • Wert. Das Textelement, welches den Zahlenwert des Indikators darstellt.
  • Die Beschreibung. Das Textelement für die Beschreibung des Indikators (sein Name, die Periode oder andere eigentümliche Information).


Die Abb. 1 die Grundlegende Struktur des einfachen runden Indikators

Im früher erstellten Ordner CustomGUI werden wir noch einen Ordner Indicator erstellen. In ihm werden sich alle Klassen der zukünftigen Indikatoren befinden. Wir erstellen den ersten von ihnen unter dem Namen CircleSimple.mqh. Zunächst werden wir schon die früher erstellten Datei CanvaseBase.mqh mit der grundlegenden Klasse für alle Arten der graphischen Objekte hinzufügen und machen die Klasse CCanvaseBase grundlegend für die laufende Klasse, damit alle Methoden dieser Klasse der laufenden Datei CCircleSimple verfügbar werden.

//+------------------------------------------------------------------+
//|                                                 CircleSimple.mqh |
//|                                Copyright 2017, Alexander Fedosov |
//|                           https://www.mql5.com/de/users/alex2356 |
//+------------------------------------------------------------------+
#include "..\CanvasBase.mqh"
//+------------------------------------------------------------------+
//| Der Runde Indikator mit dem Zahlenwert und der Beschreibung      |
//+------------------------------------------------------------------+
class CCircleSimple : public CCanvasBase

Damit wir die graphischen Objekte bei ihrer Verwendung in den Dateien der Experten und der Indikatoren jedesmal nicht manuell verordnen, erstellen wir im Ordner CustomGUI die Datei CustomGUI.mqh, in der alle Hinzufügen der Klassen aus dem Ordner Indicators gesammelt werden. So müssen wir nur diese Datei hinzufügen, um den Zugang auf alle Klassen der Bibliothek zu bekommen. Jetzt werden wir die laufende Datei hinzufügen:

//+------------------------------------------------------------------+
//|                                                    CustomGUI.mqh |
//|                                Copyright 2017, Alexander Fedosov |
//|                           https://www.mql5.com/de/users/alex2356 |
//+------------------------------------------------------------------+
#include "Indicators\CircleSimple.mqh"

Bei der Realisierung der Klasse des einfachen runden Indikators musste man jenen Satz der Eigenschaften und der Methoden bedenken, die maximal ermöglichen könnten, sogar ein solches, auf den ersten Blick, einfaches Modell des graphischen Indikators einzustellen. im Listing unten wird ihr Satz dargestellt:

//+------------------------------------------------------------------+
//| Der Runde Indikator mit dem Zahlenwert und der Beschreibung      |
//+------------------------------------------------------------------+
class CCircleSimple : public CCanvasBase
  {
private:
   //--- Die Hintergrundfarbe 
   color             m_bg_color;
   //--- Die Farbe des Rahmens
   color             m_border_color;
   //--- Die Textfarbe des Wertes
   color             m_font_color;
   //--- Die Textfarbe der Aufschrift 
   color             m_label_color;
   //--- Die Durchsichtigkeit
   uchar             m_transparency;
   //--- Die Dicke des Rahmens
   int               m_border;
   //--- Der Umfang des Indikators
   int               m_radius;
   //--- Die Schriftgröße des Wertes
   int               m_font_size;
   //--- Die Schriftgröße der Aufschrift
   int               m_label_font_size;
   //---
   int               m_digits;
   //--- Die Aufschrift
   string            m_label;
public:
                     CCircleSimple(void);
                    ~CCircleSimple(void);
   //--- Er setzt und liefert die Farbe des Hintergrunds zurück
   color             Color(void)                      { return(m_bg_color);            }
   void              Color(const color clr)           { m_bg_color=clr;                }
   //--- Er setzt und liefert den Umfang zurück
   int               Radius(void)                     { return(m_radius);              }
   void              Radius(const int r)              { m_radius=r;                    }
   //--- Er setzt und liefert die Schriftgröße des Wertes zurück
   int               FontSize(void)                   { return(m_font_size);           }
   void              FontSize(const int fontsize)     { m_font_size=fontsize;          }
   //--- Er setzt und liefert die Schriftgröße der Aufschrift zurück
   int               LabelSize(void)                  { return(m_label_font_size);    }
   void              LabelSize(const int fontsize)    { m_label_font_size=fontsize;   }
   //--- Er setzt und liefert die Schriftfarbe des Wertes zurück
   color             FontColor(void)                  { return(m_font_color);          }
   void              FontColor(const color fontcolor) { m_font_color=fontcolor;        }
   //--- Er setzt und liefert die Schriftfarbe der Aufschrift zurück
   color             LabelColor(void)                 { return(m_label_color);         }
   void              LabelColor(const color fontcolor){ m_label_color=fontcolor;       }
   //--- Er setzt die Farbe und die Dicke des Rahmens
   void              BorderColor(const color clr)     { m_border_color=clr;            }
   void              BorderSize(const int border)     { m_border=border;               }
   //--- Er setzt und liefert die Durchsichtigkeit zurück
   uchar             Alpha(void)                      { return(m_transparency);        }
   void              Alpha(const uchar alpha)         { m_transparency=alpha;          }
   //--- Er setzt und liefert den Wert der Aufschrift zurück
   string            Label(void)                      { return(m_label);               }
   void              Label(const string label)        { m_label=label;                 }
   //--- Er erstellt den Indikator
   void              Create(string name,int x,int y);
   //--- Er entfernt den Indikator
   void              Delete(string name);
   //--- Er setzt und erneut den Wert des Indikators
   void              NewValue(int value);
   void              NewValue(double value);
  };

Der Zweck der Variabel und die Methoden, in den sie verwendet werden, kann man in der Beschreibung sehen. wir halten uns auf jene Methoden, die selbst der Aufbau des Indikators realisiert,in jener Art, in der es auf der Abb.1 dargestellt ist. Die erste Methode, die wir betrachten, — ist CreateCanvas(). Wie man es nach dem Listing sieht, er hat nur drei Argumente. Ich habe sie als wichtigste gefunden. Die Ergänzung der zusätzlichen Argumente ist überschüssig, es wird die Realisierung der Methode komplizieren. Deshalb wurden alle übrigen Eigenschaften in die separaten Methoden übertragen. In diesem Zusammenhang wurden alle Variablen im Konstrukteur der Klasse initialisiert:

//+------------------------------------------------------------------+
//| Constructor                                                      |
//+------------------------------------------------------------------+
CCircleSimple::CCircleSimple(void) : m_bg_color(clrAliceBlue),
                                     m_border_color(clrRoyalBlue),
                                     m_font_color(clrBlack),
                                     m_label_color(clrBlack),
                                     m_transparency(255),
                                     m_border(5),
                                     m_radius(40),
                                     m_font_size(17),
                                     m_label_font_size(20),
                                     m_digits(2),
                                     m_label("label")
  {
  }

Es ist bequem, denn bei der Erstellung eines solchen Indikators wird es Ihnen genug, das Exemplar seiner Klasse zu erstellen und nur die Methode CreateCanvas() zu verwenden. Natürlich, vor der Erstellung können Sie nur jene Eigenschaften eingeben, die Sie ändern würden. Bei der Erstellung der komplizierten graphischen Objekte mittels der Bibliothek CCanvas ist es nötig, zu verstehen, dass die Methoden, die die Ringpuffer realisieren, werden konsequent und Schicht nach Schicht gezeichnet. Eigentlich, wie auf einer Leinwand — zeichnet zuerst der Maler den Hintergrund, später darauf Objekte, und auf ihnen — Details usw. 

//+------------------------------------------------------------------+
//| Er erstellt den Indikator                                        |
//+------------------------------------------------------------------+
void CCircleSimple::Create(string name,int x,int y)
  {
   int r=m_radius;
//--- Die Korrektion des Standortes des Indikators vom Radius
   x=(x<r)?r:x;
   y=(y<r)?r:y;
   Name(name);
   X(x);
   Y(y);
   XSize(2*r+1);
   YSize(2*r+1);
   if(!CreateCanvas())
      Print("Error. Can not create Canvas.");
   if(m_border>0)
      m_canvas.FillCircle(r,r,r,ColorToARGB(m_border_color,m_transparency));
   m_canvas.FillCircle(r,r,r-m_border,ColorToARGB(m_bg_color,m_transparency));
//---
   m_canvas.FontSizeSet(m_font_size);
   m_canvas.TextOut(r,r,"0",ColorToARGB(m_font_color,m_transparency),TA_CENTER|TA_VCENTER);
//---
   m_canvas.FontSizeSet(m_label_font_size);
   m_canvas.TextOut(r,r+m_label_font_size,m_label,ColorToARGB(m_label_color,m_transparency),TA_CENTER|TA_VCENTER);
   m_canvas.Update();
  }

Wir werden nicht auf Einzelheiten der Realisierung dieser Methode eingehen. Sondern betrachten wir die Hauptmomente:

  • Zunächst korrigieren wir den Standort der graphischen Ressource vom tatsächlichen Umfang des Indikators, damit es sich nicht so zeigt, dass sein Teil außerhalb des Hauptcharts ausfallen würde.
  • Dann werden die Einstellung und die Arbeit mit den Methoden des Namens, der Umfänge, der Koordinaten verwendet. Es wird die Grundlage aus der grundlegenden Klasse CCanvasBase erstellt.
  • Bei der Realisierung des Rahmens wurde das Prinzip des Auflegens verwendet, welches höher beschrieben ist. Und zwar — es werden zwei Kreise erstellt: erster (gefärbte) und zweite, dessen Radius kleiner als Radius des Ersten um den Wert der Dicke des Rahmens ist.
  • Über dieser Objekte wurden die Elemente des Zahlenwerts und der Beschreibung erstellt.

Weiter betrachten wir die Methode NewValue(), die die Darstellung der Zahlenwertserneuerung des Indikators online realisiert:

//+------------------------------------------------------------------+
//| Er setzt und erneut den Wert des Indikators                      |
//+------------------------------------------------------------------+
void CCircleSimple::NewValue(int value)
  {
   int r=m_radius;
   m_canvas.FillCircle(r,r,r-m_border,ColorToARGB(m_bg_color,m_transparency));
//---
   m_canvas.FontSizeSet(m_font_size);
   m_canvas.TextOut(r,r,IntegerToString(value),ColorToARGB(m_font_color,m_transparency),TA_CENTER|TA_VCENTER);
//---
   m_canvas.FontSizeSet(m_label_font_size);
   m_canvas.TextOut(r,r+m_label_font_size,m_label,ColorToARGB(m_label_color,m_transparency),TA_CENTER|TA_VCENTER);
   m_canvas.Update();
  }

Damit der Zahlenwert des Indikators korrekt erneuert würde, muss man neu die drei Objekte (der Hintergrund und die Textelemente) umzeichnen, und zwar nach derselber Reihenfolge, welche auch bei der Erstellung verwendet wurde. Deshalb in der Methode NewValue() wird der Hintergrund ungezeichnet, und danach die Textelemente des Wertes und Beschreibung.

Also, die Zeit ist reif, die Realisierung des kreisförmigen Indikators im Terminal MetaTrader 5 zu prüfen. Dazu werden wir den leeren Indikator erstellen, fügen zu ihm die Datei CustomGUI.mqh zu und werden zwei Exemplare der Klasse CCircleSimple erstellen:

//+------------------------------------------------------------------+
//|                                              CustomIndicator.mq5 |
//|                                Copyright 2017, Alexander Fedosov |
//|                           https://www.mql5.com/de/users/alex2356 |
//+------------------------------------------------------------------+
#property copyright "Copyright 2017, Alexander Fedosov"
#property link      "https://www.mql5.com/de/users/alex2356"
#property version   "1.00"
#property indicator_plots 0
#property indicator_chart_window

#include <CustomGUI\CustomGUI.mqh>
//---
CCircleSimple ind1,ind2;

Weiterhin nehmen wir in der Initialization des Indikators die Standardindikatoren, deren Werte später in den kreisförmigen Indikatoren verwendet werden:

//---
int InpInd_Handle,InpInd_Handle1;
double adx[],rsi[];
//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
int OnInit()
  {

//---- Die Erhaltung des Handles des Indikators
   InpInd_Handle=iADX(Symbol(),PERIOD_CURRENT,10);
   InpInd_Handle1=iRSI(Symbol(),PERIOD_CURRENT,10,PRICE_CLOSE);
   if(InpInd_Handle==INVALID_HANDLE)
     {
      Print("Failed to get indicator handle");
      return(INIT_FAILED);
     }
   ArraySetAsSeries(adx,true);
   ArraySetAsSeries(rsi,true);

Wir werden einige Eigenschaften der kreisförmigen Indikatoren zwecks der Demonstration bestimmen und wir werden sie erstellen:

//---
   ind1.Radius(60);
   ind1.Label("ADX");
   ind1.Color(clrWhiteSmoke);
   ind1.LabelColor(clrBlueViolet);
   ind1.Create("adx",100,100);
//---
   ind2.Radius(55);
   ind2.BorderSize(8);
   ind2.FontSize(22);
   ind2.LabelColor(clrCrimson);
   ind2.BorderColor(clrFireBrick);
   ind2.Label("RSI");
   ind2.Create("rsi",250,100);

Im Rechnungsteil des Indikators bleibt es nur übrig, den laufenden Zahlenwert der Standardindikatoren in den erstellten Indikatoren zu ersetzen:

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[])
  {
//---
   if(CopyBuffer(InpInd_Handle,0,0,2,adx)<=0 || 
      CopyBuffer(InpInd_Handle1,0,0,2,rsi)<=0
      )
      return(0);
   ind1.NewValue(adx[0]);
   ind2.NewValue(rsi[0]);
//--- return value of prev_calculated for next call
   return(rates_total);
  }

Für die korrekte Arbeit bleibt es übrig, die Methoden der Entfernung der graphischen Ressource in die Deinitialization zu verordnen, damit sich bei der Entfernung des Indikators die graphischen Objekte zusammen mit ihm korrekt entfernt werden.

//+------------------------------------------------------------------+
//| Custom indicator deinitialization function                       |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
   ind1.Delete();
   ind2.Delete();
   ChartRedraw();
  }
//+------------------------------------------------------------------+

Das Ergebnis der Arbeit ist auf der Abb.2 dargestellt Wie es sichtbar ist, unterscheiden sich die Indikatoren wesentlich voneinander nach vielen Parametern.

in Abb.2. Das Beispiel der Arbeit der kreisförmigen Indikatoren.

Die Klasse des kreisförmigen Indikators mit der Bogen-Indikation CCircleArc

Wir betrachteten eine Klasse, die die einfachste Form der Realisierung des kreisförmigen Indikators ist. wir werden ein wenig die Aufgabe komplizierte machen und wir erstellen die Klasse, der ein zusätzliches graphisches Element der Abbildung, außer einem numerischen Element hat: es ist der Bogen. Die Struktur der grundlegenden Elemente des Indikators mit der Bogen-Indikation ist auf der Abb.3 dargestellt.


Die Abb. 3 die grundlegende Struktur des kreisförmigen Indikators mit der Bogen-Indikation.

Wie Sie im Bild sehen können, unterscheidet sich dieser Typ durch die Ergänzung des Elementes des Bogenindikators. Für die Realisierung der Bogen-Indikation passt sehr gut die Methode, die den gefärbten Sektor der Ellipse zeichnet und heißt Pie(). Aus allen dargestellten Laden dieser Methode habe ich mich entschieden, die Folgende zu verwenden:

void  Pie( 
   int         x,       // Die Koordinate X des Zentrums der Ellipse 
   int         y,       // Die Koordinate Y des Zentrums der Ellipse 
   int         rx,      // Radius der Ellipse nach der Koordinate X 
   int         ry,      // Radius der Ellipse nach der Koordinate Y 
   int         fi3,     // Der Winkel des Strahls aus dem Zentrum der Ellipse, der die erste Grenze des Bogens eingibt
   int         fi4,     // Der Winkel des Strahls aus dem Zentrum der Ellipse, der die zweite Grenze des Bogens eingibt 
   const uint  clr,     // die Farbe der Linie 
   const uint  fill_clr // die Farbe der Ausfüllung 
   );

Hier wird der Wert fi3, der die erste Grenze des Bogens eingibt, als Anfang unserer Bogenskala dienen, und dder Wert fi4 wird sich je nach dem Zahlenwert dynamisch ändern, der in unseren Indikator übergeben wird. Wir werden im Ordner Indicators die Datei CircleArc.mqh erstellen und werden in der Datei CustomGUI.mqh direkt die nächste Zeile hinzufügen:

#include "Indicators\CircleArc.mqh"

Dadurch fügen wir die zukünftige Klasse zur allgemeinen Liste der Hinzufügen aller Indikatoren hinzu. Wir stellen den Satz der allgemeinen Eigenschaften und der Methoden klar. Eigentlich unterscheiden sie sich nicht von den Methoden der vorhergehenden Klasse, aber es lohnt sich, auf die Aufzählung ENUM_ORIENTATION Acht zu geben. Sie hat zwei Werte — VERTICAL und HORIZONTAL — und bestimmt den Standort der ersten Grenze des Bogens der Bogenskala. Bei dem senkrechten Wert fängt der Bogen mit 90 Grad an, und bei dem horizontalen — von der Null an. Die Indikation wird gegen den Uhrzeigersinn abgezählt.

//+------------------------------------------------------------------+
//|                                                    CircleArc.mqh |
//|                                Copyright 2017, Alexander Fedosov |
//|                           https://www.mql5.com/de/users/alex2356 |
//+------------------------------------------------------------------+
#include "..\CanvasBase.mqh"
//+------------------------------------------------------------------+
//| Der Runde Indikator mit dem Zahlenwert und der Bogen-Indikation  |
//+------------------------------------------------------------------+
class CCircleArc : public CCanvasBase
  {
private:
   //--- Die Hintergrundfarbe des Indikators
   color             m_bg_color;
   //--- Die Bogenfarbe des Indikators
   color             m_arc_color;
   //--- Die Farbe des Indikationsbereichs 
   color             m_area_color;
   //--- Die Beschreibungsfarbe des Indikators 
   color             m_label_color;
   //--- die Farbe des Wertes des Indikators
   color             m_value_color;
   //--- Die Durchsichtigkeit des Indikators
   uchar             m_transparency;
   //--- Der Umfang des Indikators
   int               m_radius;
   //--- Die Dicke der Skala des Indikators 
   int               m_scale_width;
   //--- Die Schriftgröße der  Beschreibung
   int               m_label_font_size;
   //--- Die Schriftgröße des Wertes
   int               m_value_font_size;
   //--- Die Beschreibung des Indikators
   string            m_label_value;
   //--- Der Orientierungstyp der Skala des Indikators
   ENUM_ORIENTATION  m_orientation;
public:
                     CCircleArc(void);
                    ~CCircleArc(void);
   //--- Er setzt und liefert die Farbe des Indikatorshintergrunds zurück
   color             BgColor(void)                                   { return(m_bg_color);            }
   void              BgColor(const color clr)                        { m_bg_color=clr;                }
   //--- Er setzt und liefert die Farbe des Bogens des Indikators zurück
   color             ArcColor(void)                                  { return(m_arc_color);           }
   void              ArcColor(const color clr)                       { m_arc_color=clr;               }
   //--- Er setzt und liefert die Farbe des Gebietes der Indikation zurück
   color             AreaColor(void)                                 { return(m_area_color);          }
   void              AreaColor(const color clr)                      { m_area_color=clr;              }
   //--- Er setzt und liefert die Farbe der Beschreibung des Indikators zurück 
   color             LabelColor(void)                                { return(m_label_color);         }
   void              LabelColor(const color clr)                     { m_label_color=clr;             }
   //--- Er setzt und liefert die Farbe des Wertes des Indikators zurück
   color             ValueColor(void)                                { return(m_value_color);         }
   void              ValueColor(const color clr)                     { m_value_color=clr;             }
   //--- Er setzt und liefert die Durchsichtigkeit des Indikators zurück  
   uchar             Alpha(void)                                     { return(m_transparency);        }
   void              Alpha(const uchar trn)                          { m_transparency=trn;            }
   //--- Er setzt und liefert den Umfang des Indikators zurück
   int               Radius(void)                                    { return(m_radius);              }
   void              Radius(const int r)                             { m_radius=r;                    }
   //--- Er setzt und liefert die Dicke der Skala des Indikators zurück
   int               Width(void)                                     { return(m_scale_width);         }
   void              Width(const int w)                              { m_scale_width=w;               }
   //--- Er setzt und liefert die Schriftgröße der Beschreibung zurück
   int               LabelSize(void)                                 { return(m_label_font_size);     }
   void              LabelSize(const int sz)                         { m_label_font_size=sz;          }
   //--- Er setzt und liefert die Schriftgröße des Wertes zurück
   int               ValueSize(void)                                 { return(m_value_font_size);     }
   void              ValueSize(const int sz)                         { m_value_font_size=sz;          }
   //--- Er setzt und liefert die  Beschreibung des Indikators zurück
   string            LabelValue(void)                                { return(m_label_value);         }
   void              LabelValue(const string str)                    { m_label_value=str;             }
   //--- Er setzt und liefert den Orientierungstyp der Skala zurück
   void              Orientation(const ENUM_ORIENTATION orietation)  { m_orientation=orietation;      }
   ENUM_ORIENTATION  Orientation(void)                               { return(m_orientation);         }
   //--- Er erstellt den Indikator
   void              Create(string name,int x,int y);
   //--- Er entfernt den Indikator
   void              Delete(void);
   //--- Er setzt und erneut den Wert des Indikators
   void              NewValue(double value,double maxvalue,int digits);
  };

Wir betrachten mehr die Methoden, deren Realisierung nicht vorgestellt ist: Create() и NewValue(). Wir erinnern uns, dass die Objekte bei der Erstellung aufeinander Schichtweise aufgelegt werden, deshalb wird die Ordnung ihrer Erstellung folgendermaßen aussehen:

  1. Der Hintergrund des Bogenindikators. In Form vom gefärbten Kreis.
  2. Der Bogenindikator. In Form vom gefärbten Sektor der Ellipse.
  3. Der Hintergrund der Abbildung der Textinformationen. In Form vom gefärbten Kreis.
  4. Der Zahlenwert des Indikators und seine Beschreibung.

Unten wird die Realisierung ordnungsgemäß dargestellt:

//+------------------------------------------------------------------+
//| Er erstellt den Indikator                                        |
//+------------------------------------------------------------------+
void CCircleArc::Create(string name,int x,int y)
  {
   int r=m_radius;
   double a,b;
//--- Die Setzung des Anfangsstandortes des Indikators
   a=(m_orientation==VERTICAL)?M_PI_2:0;
   b=(m_orientation==VERTICAL)?M_PI_2:0;
   b+=90*M_PI/180;
//--- Die Korrektion des Standortes des Indikators vom Radius
   x=(x<r)?r:x;
   y=(y<r)?r:y;
   Name(name);
   X(x);
   Y(y);
   XSize(2*r+1);
   YSize(2*r+1);
   if(!CreateCanvas())
      Print("Error. Can not create Canvas.");
//---
   m_canvas.FillCircle(r,r,r,ColorToARGB(m_bg_color,m_transparency));
   m_canvas.Pie(r,r,XSize()/2,YSize()/2,a,b,ColorToARGB(m_arc_color,m_transparency),ColorToARGB(m_arc_color,m_transparency));
   m_canvas.FillCircle(r,r,r-m_scale_width,ColorToARGB(m_area_color,m_transparency));
//---
   m_canvas.FontSizeSet(m_value_font_size);
   m_canvas.TextOut(r,r,"0",ColorToARGB(m_value_color,m_transparency),TA_CENTER|TA_VCENTER);
//---
   m_canvas.FontSizeSet(m_label_font_size);
   m_canvas.TextOut(r,r+m_label_font_size,m_label_value,ColorToARGB(m_label_color,m_transparency),TA_CENTER|TA_VCENTER);
   m_canvas.Update();
  }

Hier braucht man zu beachten, dass beim Eingeben die Anfangswerte der ersten und zweiten Grenze des Bogens berücksichtigen wir ( das heißt der Anfang des Indikators und sein laufender Wert) seine Anfangsorientierung und, dass die Winkel für die Methode Pie() in den Radianten eingegeben werden müssen. Als Beispiel, der standardmäßige laufende Wert, der der Variabel b entspricht, und ist in 90 Grad gesetzt, dabei wird es in die Radianten übersetzt, sonst wird die Darstellung unkorrekt sein. Nach den gleichen Prinzipien wird die Methode NewValue() realisiert:

//+------------------------------------------------------------------+
//| Er setzt und erneut den Wert des Indikators                      |
//+------------------------------------------------------------------+
void CCircleArc::NewValue(double value,double maxvalue,int digits=2)
  {
   int r=m_radius;
   double a,b,result;
//--- Die Prüfungen auf den Ausgang aus den Rahmen des Bereichs
   value=(value>maxvalue)?maxvalue:value;
   value=(value<0)?0:value;
//--- Die Setzung des Anfangsstandortes des Indikators
   a=(m_orientation==VERTICAL)?M_PI_2:0;
   b=(m_orientation==VERTICAL)?M_PI_2:0;
//---
   result=value*(360.0/maxvalue);
   b+=result*M_PI/180;
   if(b>=2*M_PI)
      b=2*M_PI-0.02;
//---
   m_canvas.FillCircle(r,r,r,ColorToARGB(m_bg_color,m_transparency));
   m_canvas.Pie(r,r,XSize()/2,YSize()/2,a,b,ColorToARGB(m_arc_color,m_transparency),ColorToARGB(m_arc_color,m_transparency));
   m_canvas.FillCircle(r,r,r-m_scale_width,ColorToARGB(m_area_color,m_transparency));
//---
   m_canvas.FontSizeSet(m_value_font_size);
   m_canvas.TextOut(r,r,DoubleToString(value,digits),ColorToARGB(m_value_color,m_transparency),TA_CENTER|TA_VCENTER);
//---
   m_canvas.FontSizeSet(m_label_font_size);
   m_canvas.TextOut(r,r+m_label_font_size,m_label_value,ColorToARGB(m_label_color,m_transparency),TA_CENTER|TA_VCENTER);
   m_canvas.Update();
  }
//+------------------------------------------------------------------+

Nach der Prüfung und Setzung der Anfangsstandorte findet die Umrechnung des Winkels in den Radianten der zweiten Grenze des Bogens statt, und dann alle Elemente des Indikators werden mit den neuen Werten umgezeichnet. Als Beispiel der Verwendung des gegebenen Indikators wurde der einfache Indikator-Spread realisiert, der bei dem Schwellenwert die Farbe der Bogenskala der Indikation ändert.

//+------------------------------------------------------------------+
//|                                                                  |
//|                                Copyright 2017, Alexander Fedosov |
//|                           https://www.mql5.com/de/users/alex2356 |
//+------------------------------------------------------------------+
#property copyright "Copyright 2017, Alexander Fedosov"
#property link      "https://www.mql5.com/de/users/alex2356"
#property version   "1.00"
#property indicator_plots 0
#property indicator_chart_window

#include <CustomGUI\CustomGUI.mqh>
//+------------------------------------------------------------------+
//|  Eingangsparameter des Indikators                                |
//+------------------------------------------------------------------+
input double         maxspr=12;                 // Der maximale Wert
input int            stepval=8;                 // Der Schwellenwert
input color          stepcolor=clrCrimson;      // Die Farbe beim Schwellenwert
//---
CCircleArc arc;
double spr;
//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
int OnInit()
  {
//---
   arc.Orientation(HORIZONTAL);
   arc.LabelValue("Spread");
   arc.Create("spread_ind",100,100);
//---
   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[])
  {
   spr=double(SymbolInfoInteger(Symbol(),SYMBOL_SPREAD));
   if(spr>=stepval)
      arc.ArcColor(stepcolor);
   else
      arc.ArcColor(clrForestGreen);
//---
   arc.NewValue(spr,maxspr,0);
//--- return value of prev_calculated for next call
   return(rates_total);
  }
//+------------------------------------------------------------------+
//| Custom indicator deinitialization function                       |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
   arc.Delete();
   ChartRedraw();
  }
//+------------------------------------------------------------------+

Wie man es aus dem Listing sehen kann, ist die Realisierung mit Hilfe der Klasse CCircleArc bei der Erstellung und Einstellung ziemlich einfach. Es sollte bemerkt werden: irgendwelche Eigenschaften einstellen und ändern muss man streng vor der Erstellung des Indikators (die Methode Create()) durchführen, oder vor der Verwendung der Methode NewValue(), da gerade in ihnen das Abzeichnen der Elemente des Indikators stattfindet, und dementsprechend, auch die Anwendung der geänderten Eigenschaften. Auf der Abb.4 wurde das Beispiel der Arbeit des Indikators-Spread vorgeführt.

in Abb.4. Das Beispiel der Arbeit des kreisförmigen Indikators mit der Bogenindikation.


Die Klasse des kreisförmigen Indikators mit der Bogen-Sektionsindikation CCircleSection

Im Unterschied zur einfachen Bogenindikation, die Sektionsindikation sieht so optisch aus, als ob auf sie die Zeichen aufgetragen würden, die die gleichen Abstände der Werte teilen. Beim Aufbau des Modelles des Indikators dieses Typs wurde es entschieden, zehn Sektionen zu machen, es wird dabei ein neues Element hinzugefügt: der innere Rahmen. Die grundlegende Struktur mit der Bogensektionsindikation wurde auf der Abb. 5 dargestellt.


Die Abb. 5 die grundlegende Struktur des kreisförmigen Indikators mit der Bogen-Indikation.

Die Methode der Abbildung der Bogen-Indikation mit den Sektionen unterscheidet sich schon in der Wurzel von der vorhergehenden Klasse. Der Unterschied besteht darin, dass in der vorhergehenden Klasse einmal die Methode Pie() der Bibliothek CCanvas verwendet wurde, in der bei der Veränderung des Wertes sich der Wert und der Standort des zweiten Bogens des Sektors der Ellipse änderte. Hier wird diese Methode zehn Mal verwendet, und ihre Standorte sind statisch. Das heißt, einfacher gesagt, es sind im gegebenen Indikator 10 gefärbte Sektoren der Ellipse, die nach dem Kreis nacheinander gelegen sind. Als visuelle Indikation wird die Veränderung der Farbe bestimmter Sektionen dienen.

Wir erstellen im Ordner Indicators die Datei CircleSection.mqh, werden sie, genauso wie vorher, zu der Datei CustomUI.mqh. hinzufügen. Jetzt wird seine Liste auf folgende Weise aussehen:

//+------------------------------------------------------------------+
//|                                                    CustomGUI.mqh |
//|                                Copyright 2017, Alexander Fedosov |
//|                           https://www.mql5.com/de/users/alex2356 |
//+------------------------------------------------------------------+
#include "Indicators\CircleSimple.mqh"
#include "Indicators\CircleArc.mqh"
#include "Indicators\CircleSection.mqh"

Da die Vorstellung der Klassen der Indikatoren in der Reihe ihre Komplexität bezüglich ihrer Aufbaus und Funktionalität geht, so werde ich nicht für sie allgemeine Eigenschaften und Methoden bringen. Wir werden die Aufmerksamkeit darauf konzentrieren, welche zusätzliche Funktional ergänzen oder haben andere Realisierung. Deshalb ist in der laufenden Klasse der größte Teil der Methoden den vorhergehend identisch, aber es wurden die Folgenden hinzugefügt:

//+------------------------------------------------------------------+
//|                                                CircleRounded.mqh |
//|                                Copyright 2017, Alexander Fedosov |
//|                           https://www.mql5.com/de/users/alex2356 |
//+------------------------------------------------------------------+
#include "..\CanvasBase.mqh"
//+--------------------------------------------------------------------------+
//| Der Runde Indikator mit dem Zahlenwert und der Runde-Sektionsindikation  |
//+--------------------------------------------------------------------------+
class CCircleSection : public CCanvasBase
  {
private:
   //--- Die Farbe der aktiven/inaktiven Teilung der Skala
   color             m_scale_color_on;
   color             m_scale_color_off;
public:
   //--- Er setzt die Farbe der aktiven/inaktiven Teilung der Skala
   void              ScaleColorOn(const color clr)                   { m_scale_color_on=clr;          }
   void              ScaleColorOff(const color clr)                  { m_scale_color_off=clr;         }
   //--- Er erstellt den Indikator
   void              Create(string name,int x,int y);
   //--- Er setzt und erneut den Wert des Indikators
   void              NewValue(double value,double maxvalue,int digits);
  };

Die Methoden Create() und NewValue() wurden infolge des Unterschiedes ihrer Realisierung im Vergleich zu den Vorhergehenden gelassen. Wie man es aus dem Listing unten sieht, geht nach der Korrektion des Standorts vom Radius der Block der Erstellung zehn Sektionen mit Hilfe des Loops. Dabei wird der Anfang des Abzahlens berücksichtigt: horizontal — von dem Null-Grad, senkrecht — von 90 Grad.

//+------------------------------------------------------------------+
//| Er erstellt den Indikator                                        |
//+------------------------------------------------------------------+
void CCircleSection::Create(string name,int x,int y)
  {
   int r=m_radius;
   double a,b;
//--- Die Korrektion des Standortes des Indikators vom Radius
   x=(x<r)?r:x;
   y=(y<r)?r:y;
   Name(name);
   X(x);
   Y(y);
   XSize(2*r+1);
   YSize(2*r+1);
   if(!CreateCanvas())
      Print("Error. Can not create Canvas.");
//--- Die Sektionen des Indikators
   for(int i=0;i<10;i++)
     {
      if(m_orientation==HORIZONTAL)
        {
         a=36*i*M_PI/180;
         b=36*(i+1)*M_PI/180;
         if(a>2*M_PI)
            a-=2*M_PI;
         if(b>2*M_PI)
            b-=2*M_PI;
        }
      else
        {
         a=M_PI_2+36*i*M_PI/180;
         b=M_PI_2+36*(i+1)*M_PI/180;
         if(a>2*M_PI)
            a-=2*M_PI;
         if(b>2*M_PI)
            b-=2*M_PI;
        }
      m_canvas.Pie(r,r,XSize()/2,YSize()/2,a,b,ColorToARGB(m_bg_color,m_transparency),ColorToARGB(m_scale_color_off,m_transparency));
     }
//--- Der Rahmen und der Hintergrund
   m_canvas.FillCircle(r,r,XSize()/2-m_scale_width,ColorToARGB(m_border_color,m_transparency));
   m_canvas.FillCircle(r,r,XSize()/2-m_scale_width-m_border_size,ColorToARGB(m_area_color,m_transparency));
//--- Die Beschreibung
   m_canvas.FontSizeSet(m_label_font_size);
   m_canvas.TextOut(r,r+m_value_font_size,m_label_value,ColorToARGB(m_label_color,m_transparency),TA_CENTER|TA_VCENTER);
//--- Der Zahlenwert
   m_canvas.FontSizeSet(m_value_font_size);
   m_canvas.TextOut(r,r,"0",ColorToARGB(m_value_color,m_transparency),TA_CENTER|TA_VCENTER);
   m_canvas.Update();
  }

Wie wir schon erwähnt haben, außer der Veränderung des Zahlenwerts, wird die visuelle Indikation in Form von der Farbeänderung der Sektionen dargestellt. Dabei muss die Veränderung konsequent sein und, von dem gegebenen maximalen und laufenden Wert ausgehen. So werden, zum Beispiel, beim Zahlenwert 20 und dem maximalen 100 werden zwei Sektionen ihre Farbe ändern, aber bei maximalen 200 nur eine Sektion. Wir betrachten es gründlicher, wie es in der Methode NewValue() aussieht:

//+------------------------------------------------------------------+
//| Er setzt und erneut den Wert des Indikators                      |
//+------------------------------------------------------------------+
void CCircleSection::NewValue(double value,double maxvalue=100,int digits=2)
  {
//---
   int r=m_radius;
   double a,b;
   color clr;
//---
   for(int i=0;i<10;i++)
     {
      if(m_orientation==HORIZONTAL)
        {
         a=36*i*M_PI/180;
         b=36*(i+1)*M_PI/180;
         if(a>2*M_PI)
            a-=2*M_PI;
         if(b>2*M_PI)
            b-=2*M_PI;
        }
      else
        {
         a=M_PI_2+36*i*M_PI/180;
         b=M_PI_2+36*(i+1)*M_PI/180;
         if(a>2*M_PI)
            a-=2*M_PI;
         if(b>2*M_PI)
            b-=2*M_PI;
        }
      clr=(maxvalue/10*(i+1)<=value)?m_scale_color_on:m_scale_color_off;
      m_canvas.Pie(r,r,XSize()/2,YSize()/2,a,b,ColorToARGB(m_bg_color,m_transparency),ColorToARGB(clr,m_transparency));
     }
//---
   m_canvas.FillCircle(r,r,XSize()/2-m_scale_width,ColorToARGB(m_border_color,m_transparency));
   m_canvas.FillCircle(r,r,XSize()/2-m_scale_width-m_border_size,ColorToARGB(m_area_color,m_transparency));
//---
   m_canvas.FontSizeSet(m_label_font_size);
   m_canvas.TextOut(r,r+m_value_font_size,m_label_value,ColorToARGB(m_label_color,m_transparency),TA_CENTER|TA_VCENTER);
//---
   m_canvas.FontSizeSet(m_value_font_size);
   m_canvas.TextOut(r,r,DoubleToString(value,digits),ColorToARGB(m_value_color,m_transparency),TA_CENTER|TA_VCENTER);
   m_canvas.Update();
  }
//+------------------------------------------------------------------+

Wie man es aus dem Listing oben sehen kann, sieht das in der Realisierung ziemlich einfach aus. Es wird der laufende Wert bezüglich des Maximalen geprüft. Wenn es dem maximalen Wert gleich oder mehr ist, der durch die Anzahl der Sektionen geteilt ist und der um die laufende Iteration des Loops multipliziert ist, so ändert sich die Farbe der Sektion von Inaktiv um Aktiv.

Als Beispiel der Verwendung dieser Klasse habe ich den Indikator des Drawdowns realisiert, der das Verhältnis Equity zur laufenden Balance auf dem Konto darstellt. So kann man visuell das Drawdown auf dem Konto kontrollieren, und nicht nach den Zahlen im Terminal ausrechnen. Das Listing der Realisierung eines solchen Indikators wird unten dargestellt.

//+------------------------------------------------------------------+
//|                                              CustomIndicator.mq5 |
//|                                Copyright 2017, Alexander Fedosov |
//|                           https://www.mql5.com/de/users/alex2356 |
//+------------------------------------------------------------------+
#property copyright "Copyright 2017, Alexander Fedosov"
#property link      "https://www.mql5.com/de/users/alex2356"
#property version   "1.00"
#property indicator_plots 0
#property indicator_chart_window

#include <CustomGUI\CustomGUI.mqh>
//---
CCircleSection ind;
//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
int OnInit()
  {
//---
   ind.Radius(70);
   ind.LabelValue("Drawdown");
   ind.Create("drawdown",150,150);
//---
   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[])
  {
//---
   ind.NewValue(AccountInfoDouble(ACCOUNT_EQUITY),AccountInfoDouble(ACCOUNT_BALANCE));
//--- return value of prev_calculated for next call
   return(rates_total);
  }
//+------------------------------------------------------------------+
//| Custom indicator deinitialization function                       |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
   ind.Delete();
   ChartRedraw();
  }
//+------------------------------------------------------------------+

Vergessen wir auch nicht in der Deinitialisierung die Methode Delete() zu verwenden, um den Indikator korrekt aus dem Chart zu entfernen.


Die Klasse des linearen Charts CLineGraph

Für die Erstellung des linearen Charts mittels des Benutzer-Charts muss man zuerst die Elementen des Charts klarstellen, und zwar:

  • der Hintergrund des Charts. Er hat zwei Eigenschaften: es ist der Umfang, der auch der Umfang des Objektes ist, und die Farbe.
  • Der Rahmen des Charts. Es hat nur die Eigenschaft der Farbe. Es besteht aus zwei gefärbten Rechtecken, dabei die Koordinaten des Letzten (der auch als Hintergrund des Charts dient) um 1 verschoben sind, was den Effekt des Rahmens gibt.
  • Der Hintergrund des Charts. Es hat nur die Eigenschaft der Farbe. 
  • Die Achsen des Charts. Es hat nur die Eigenschaft der Farbe. Es wurde nach gleicher Art realisiert, genauso wie der Rahmen des Charts — durch das Auflegen zwei Rechtecke, wo die Obere die Absetzung nach den Koordinaten um 1 hat.
  • Die Teilung der Achse. Es hat nur die Eigenschaft der Farbe. Der Satz aus den Linien, welcher ist mit Hilfe der Methoden LineHorizontal() für die Achse Y und LineVertical() für die Achse Х realisiert worden.
  • Der Wert der Achse. Es hat die Eigenschaft der Farbe und der Schriftgröße. Der Satz aus den Textobjekten mit Hilfe der Methode TextOut().
  • Netz. Es hat nur die Eigenschaft der Farbe. Für die Realisierung des Netzes wurde die Methode LineAA() gewählt, da man darin den Stil der Linie einstellen kann.
  • Chart. Es hat nur die Eigenschaft der Farbe. Es besteht aus den Elementen, die Linie und der gefärbte Kreis — die Methode FillCircle()

In der Beschreibung der Elemente wurden nur die einstellbaren Eigenschaften gegeben. Auf der Abb.6 wurde die Struktur der Elemente des Charts dargestellt. 


in Abb.6. Die grundlegende Struktur des linearen Charts.

Zuerst werden wir im Ordner Indicators die Datei LineGraph.mqh erstellen und werden sie zu der Datei CustomGUI.mqh hinzufügen:

//+------------------------------------------------------------------+
#include "Indicators\CircleSimple.mqh"
#include "Indicators\CircleArc.mqh"
#include "Indicators\CircleSection.mqh"
#include "Indicators\LineGraph.mqh"
//+------------------------------------------------------------------+

Danach werden wir in der Datei LineGraph.mqh die Klasse CLineGraph erstellen und machen für sie, genauso wie in Vorhergehenden, CCanvasBase als Grundlegende. Wir werden alle Eigenschaften und Methoden bestimmen, die höher in der grundlegenden Struktur des linearen Charts beschrieben wurden. 

//+------------------------------------------------------------------+
//|                                                    LineGraph.mqh |
//|                                Copyright 2017, Alexander Fedosov |
//|                           https://www.mql5.com/de/users/alex2356 |
//+------------------------------------------------------------------+
#include "..\CanvasBase.mqh"
//+------------------------------------------------------------------+
//| Das lineare Chart                                                |
//+------------------------------------------------------------------+
class CLineGraph : public CCanvasBase
  {
private:
   //--- Die Hintergrundfarbe des Charts
   color             m_bg_color;
   //--- Die Hintergrundfarbe des Charts
   color             m_bg_graph_color;
   //--- Die Farbe des Rahmens des Charts
   color             m_border_color;
   //--- Die Farbe der Achsenwerte des Charts
   color             m_axis_color;
   //--- Die Netzfarbe des Charts
   color             m_grid_color;
   //--- Die Farbe der Teilungen auf den Achsen des Charts
   color             m_scale_color;
   //--- Die Farbe der Linien des Charts
   color             m_graph_color;
   //--- Der Umfang des Charts
   int               m_x_size;
   int               m_y_size;
   //--- Der Einzug des Charts vom Rand des Hintergrunds
   int               m_gap;
   //--- Die Schriftgröße des Wertes auf den Achsen des Charts
   int               m_font_size;
   //--- Das Array der Werte der Ordinatenachse (y)
   int               m_x[];
   //--- der minimale und maximale Wert der Ordinatenachse
   double            m_y_min;
   double            m_y_max;
   //--- Die Anzahl der Teilungen der Ordinatenachse
   int               m_num_grid;
   //--- Die Durchsichtigkeit des Charts
   uchar             m_transparency;
public:
                     CLineGraph(void);
                    ~CLineGraph(void);
   //--- Er setzt und liefert die Farbe des Chartshintergrunds zurück
   color             BgColor(void)                                   { return(m_bg_color);                  }
   void              BgColor(const color clr)                        { m_bg_color=clr;                      }
   //--- Er setzt und liefert die Farbe des Chartshintergrunds zurück
   color             BgGraphColor(void)                              { return(m_bg_graph_color);            }
   void              BgGraphColor(const color clr)                   { m_bg_graph_color=clr;                }
   //--- Er setzt und liefert die Farbe des Chartsrahmens zurück
   color             BorderColor(void)                               { return(m_border_color);              }
   void              BorderColor(const color clr)                    { m_border_color=clr;                  }
   //--- Er setzt und liefert die Farbe der Achsenwerte des Charts zurück

   color             AxisColor(void)                                 { return(m_axis_color);                }
   void              AxisColor(const color clr)                      { m_axis_color=clr;                    }
   //--- Er setzt und liefert die Farbe des Chartsnetzes zurück
   color             GridColor(void)                                 { return(m_grid_color);                }
   void              GridColor(const color clr)                      { m_grid_color=clr;                    }
   //--- Er setzt und liefert die Farbe der Teilungen auf den Achsen des Charts zurück
   color             ScaleColor(void)                                { return(m_scale_color);               }
   void              ScaleColor(const color clr)                     { m_scale_color=clr;                   }
   //--- Er setzt und liefert die Farbe der Chartslinien zurück
   color             GraphColor(void)                                { return(m_graph_color);               }
   void              GraphColor(const color clr)                     { m_graph_color=clr;                   }
   //--- Er setzt und liefert die Umfänge des Charts zurück
   int               XGraphSize(void)                                { return(m_x_size);                    }
   void              XGraphSize(const int x_size)                    { m_x_size=x_size;                     }
   int               YGraphSize(void)                                { return(m_y_size);                    }
   void              YGraphSize(const int y_size)                    { m_y_size=y_size;                     }
   //--- Er setzt und liefert die Schriftgröße des Wertes auf den Achsen des Charts zurück

   int               FontSize(void)                                  { return(m_font_size);                 }
   void              FontSize(const int fontzise)                    { m_font_size=fontzise;                }
   //--- Er setzt und liefert den Einzug des Charts vom Rand des Hintergrunds zurück
   int               Gap(void)                                       { return(m_gap);                       }
   void              Gap(const int g)                                { m_gap=g;                             }
   //--- Er setzt und liefert den minimalen und maximalen Wert der Ordinatenachse zurück
   double            YMin(void)                                      { return(m_y_min);                     }
   void              YMin(const double ymin)                         { m_y_min=ymin;                        }
   double            YMax(void)                                      { return(m_y_max);                     }
   void              YMax(const double ymax)                         { m_y_max=ymax;                        }
   //--- Er setzt und liefert die Anzahl der Teilungen der Ordinatenachse zurück
   int               NumGrid(void)                                   { return(m_num_grid);                  }
   void              NumGrid(const int num)                          { m_num_grid=num;                      }
   //--- Er erstellt das Chart
   void              Create(string name,int x,int y);
   //--- Er entfernt das Chart 
   void              Delete(void);
   //--- Er setzt das Array der Eingangsdaten
   void              SetArrayValue(double &data[]);
private:
   //---
   void              VerticalScale(double min,double max,int num_grid);
   void              HorizontalScale(int num_grid);
  };

Wir betrachten gründlicher die Realisierung der Methoden, die oben im Listing nicht gegeben wurden. In der Methode Create() wird die graphische Ressource erstellt, und es wird auf das Chart des Instruments gesetzt. Hier werden zwei private Methoden für die Einstellung der senkrechten und horizontalen Skala verwendet: entweder ausgehend von den initialisierten Einstellungen im Konstrukteur der Klasse, oder mit Hilfe der Methoden der Klasse vor der Erstellung.

//+------------------------------------------------------------------+
//| Er erstellt das Chart                                            |
//+------------------------------------------------------------------+
void CLineGraph::Create(string name,int x,int y)
  {
//--- Die Korrektion des Standortes des Indikators von
   x=(x<m_x_size/2)?m_x_size/2:x;
   y=(y<m_y_size/2)?m_y_size/2:y;
   Name(name);
   X(x);
   Y(y);
   XSize(m_x_size);
   YSize(m_y_size);
   if(!CreateCanvas())
      Print("Error. Can not create Canvas.");
//--- Die Erstellung des Rahmens des Charts und des Hintergrunds

   m_canvas.FillRectangle(0,0,XSize(),YSize(),ColorToARGB(m_border_color,m_transparency));
   m_canvas.FillRectangle(1,1,XSize()-2,YSize()-2,ColorToARGB(m_bg_color,m_transparency));
//--- Die Erstellung der Achsen und des Hintergrunds des Charts
   m_canvas.FillRectangle(m_gap-1,m_gap-1,XSize()-m_gap+1,YSize()-m_gap+1,ColorToARGB(m_border_color,m_transparency));
   m_canvas.FillRectangle(m_gap,m_gap,XSize()-m_gap,YSize()-m_gap,ColorToARGB(m_bg_graph_color,m_transparency));
//--- Die Erstellung der Teilungen der Achse und ihrer Werte
   VerticalScale(m_y_min,m_y_max,m_num_grid);
   HorizontalScale(5);
   m_canvas.Update();
  }

Für die Darstellung eines irgendwelchen linearen Charts soll es optimal für die Grundlage ein Arrays der Daten genommen werden, da in MetaTrader die Arrays oft für das Kopieren der Werte aus den Puffer der Indikatoren verwendet werden. Deshalb wird es bei der Realisierung der Methode SetArrayValue() die Darstellung der Charts als Grundlage für das Array der Daten übernommen (es ist ein Argument der Methode), wo nach der Achse Х die Werte verschoben werden, die dem Umfang des Araays entsprechen, und nach der Achse Y — die Werte. 

//+------------------------------------------------------------------+
//| setzt das Array der Eingangsdaten                                |
//+------------------------------------------------------------------+
void CLineGraph::SetArrayValue(double &data[])
  {
   int y0,y1;
//--- Die Erstellung des Rahmens des Charts und des Hintergrunds

   m_canvas.FillRectangle(0,0,XSize(),YSize(),ColorToARGB(m_border_color,m_transparency));
   m_canvas.FillRectangle(1,1,XSize()-2,YSize()-2,ColorToARGB(m_bg_color,m_transparency));
//--- Die Erstellung der Achsen und des Hintergrunds des Charts
   m_canvas.FillRectangle(m_gap-1,m_gap-1,XSize()-m_gap+1,YSize()-m_gap+1,ColorToARGB(m_border_color,m_transparency));
   m_canvas.FillRectangle(m_gap,m_gap,XSize()-m_gap,YSize()-m_gap,ColorToARGB(m_bg_graph_color,m_transparency));
//--- Wenn der maximale Wert im Array der Daten die obere Grenze der Achse Y übertritt, so messen wir die Achse aus.
   if(data[ArrayMaximum(data)]>m_y_max)
      m_y_max=data[ArrayMaximum(data)];
//--- Die Erstellung der Teilungen der Achse und ihrer Werte
   VerticalScale(m_y_min,m_y_max,m_num_grid);
   HorizontalScale(ArraySize(data));
//--- Der Aufbau des Charts nach dem Datenarray
   for(int i=0;i<ArraySize(data)-1;i++)
     {
      y0=int((YSize()-2*m_gap)*(1-data[i]/m_y_max));
      y0=int((YSize()-m_gap)-(YSize()-2*m_gap)/m_y_max*data[i]);
      y0=(y0<m_gap)?m_gap:y0;
      y1=int((YSize()-m_gap)-(YSize()-2*m_gap)/m_y_max*data[i+1]);
      y1=(y1<m_gap)?m_gap:y1;
      m_canvas.LineAA(m_x[i+1],y0,m_x[i+2],y1,ColorToARGB(m_graph_color,m_transparency),STYLE_SOLID);
      m_canvas.FillCircle(m_x[i+1],y0,2,ColorToARGB(m_graph_color,m_transparency));
      m_canvas.FillCircle(m_x[i+2],y1,2,ColorToARGB(m_graph_color,m_transparency));
     }
   m_canvas.Update();
  }

Als Beispiel der Verwendung der gegebenen Klasse wurde es entschieden, das Chart nach den Werten des gewählten Oszillators aufzubauen und ihn mit der originellen Darstellung zu vergleichen. Dafür wurde ein gewöhnlicher RSI genommen. Die Realisierung des Aufbaus nach seinen Werten aus dem Puffer mit Hilfe der Klasse ClineGraph kann man im Listing unten sehen.

//+------------------------------------------------------------------+
//|                                                            4.mq5 |
//|                                Copyright 2017, Alexander Fedosov |
//|                           https://www.mql5.com/de/users/alex2356 |
//+------------------------------------------------------------------+
#property copyright "Copyright 2017, Alexander Fedosov"
#property link      "https://www.mql5.com/de/users/alex2356"
#property version   "1.00"
#property indicator_plots 0
#property indicator_chart_window

#include <CustomGUI\CustomGUI.mqh>
//---
CLineGraph ind;
int InpInd_Handle;
double rsi[];
//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
int OnInit()
  {
//---- Die Erhaltung des Handles des Indikators
   InpInd_Handle=iRSI(Symbol(),PERIOD_CURRENT,10,PRICE_CLOSE);
   if(InpInd_Handle==INVALID_HANDLE)
     {
      Print("Failed to get indicator handle");
      return(INIT_FAILED);
     }
//---
   ind.NumGrid(10);
   ind.YMax(100);
   ind.Create("rsi",350,250);

//---
   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[])
  {
//---
   if(CopyBuffer(InpInd_Handle,0,0,10,rsi)<=0)
      return(0);
   ind.SetArrayValue(rsi);
//--- return value of prev_calculated for next call
   return(rates_total);
  }
//+------------------------------------------------------------------+
//| Custom indicator deinitialization function                       |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
   ind.Delete();
   ChartRedraw();
  }
//+------------------------------------------------------------------+

Jetzt werden wir den originellen Indikator mit den gleichen Parametern setzen und vergleichen die bekommenden Ergebnisse auf der Abb. 7:

in Abb.7. Der Vergleich der Realisierung des Aufbaus mit der Hilfe der Klasse CLineGraph und des originellen RSI.

Bitte beachten Sie, dass das Array, in dem die Daten durch die Methode CopyBuffer aufgenommen werden, muss die Anfangssicht haben. Das heißt die Indexierung ändern, wie es in Timeserien ist, muss man nicht.


Fazit

Im Artikel wurden die Beispiele der stufenweisen Erstellung der benutzerdefinierten Indikatoren einer willkürlichen Form mit Hilfe der Bibliothek des Benutzer-Charts CСanvas betrachtet. Es wurden die Beispiele verschiedener Komplexität beschrieben: vom einfachen runden Indikator, der nur aus vier Elementen besteht, bis zum kreisförmig Indikator mit der Sektionsindikation. Außerdem, wurde die Möglichkeit der Realisierung schon bekannten Arten der Indikatoren auf neuer Weise betrachtet. Dabei sind die Möglichkeiten vom gegebenen Satz der Eigenschaften nicht beschränkt. Die gegebenen Beispiele der Realisierung im Artikel waren in Form von den Klassen strukturiert, das heißt, im Kern sind sie eine Ergänzung oder eine Erweiterung schon fertiger Klasse CCanvas. 

Am Ende des Artikels findet man im Anhang das Archiv mit allen aufgezählten Dateien, die nach den Ordnern sortiert sind. Deshalb ist es für die korrekte Arbeit ausreichend, den Ordner MQL5 im Verzeichnis des Terminalen zu zuordnen. 

Die Programme, die im Artikel verwendet wurden:

#
 Der Name
Typ
Beschreibung
1
CanvasBase.mqh Bibliothek  Die grundlegende Klasse des Benutzer-Charts
2
CustomGUI.mqh Bibliothek  Die Datei der Liste des Hinzufügens aller Klassen der Bibliothek 
3 CircleArc.mqh Bibliothek  Es enthält die Klasse des kreisförmigen Indikators mit der Bogen-Indikation CCircleArc
4 CircleSection.mqh Bibliothek  Es enthält die Klasse des kreisförmigen Indikators mit der Bogen-Sektionsindikation CCircleSection
5 CircleSimple.mqh Bibliothek  Es enthält die Klasse des einfachen runden Indikators CCircleSimle
LineGraph.mqh Bibliothek  Es enthält die Klasse des linearen Charts CLineGraph
7 1.mq5 Indikator  Das Beispiel der Realisierung der Klasse CCirleSimple
8 2.mq5 Indikator  Das Beispiel der Realisierung der Klasse CCirleSimple
9 3.mq5 Indikator  Das Beispiel der Realisierung der Klasse CCirleSection
 10 4.mq5 Indikator  Das Beispiel der Realisierung der Klasse CLineGraph


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

Beigefügte Dateien |
MQL5.zip (234.75 KB)
Cross-Plattform Expert Advisor: Order-Manager Cross-Plattform Expert Advisor: Order-Manager

Dieser Artikel behandelt das Erstellen eines Order-Managers für einen Cross-Plattform Expert Advisor. Der Order-Manager ist verantwortlich, für beide Versionen die Positionen eines Experten zu öffnen oder zu schließen, und die jeweiligen Datensätze für eine weitere Verwendung aktuell zu halten.

Die Rezepte MQL5 - Die Erstellung des Ringpuffers für eine schnelle Berechnung der Indikatoren im gleitenden Fenster Die Rezepte MQL5 - Die Erstellung des Ringpuffers für eine schnelle Berechnung der Indikatoren im gleitenden Fenster

Der Ringpuffer — er ist die einfachste und zugleich wirksamste Organisationsform für die Berechnungen von Daten in einem gleitenden Fenster. Im Artikel wird beschrieben, wie dieser Algorithmus funktioniert, und es wird gezeigt, wie mit seiner Hilfe Berechnungen im gleitenden Fenster einfacher und schneller durchgeführt werden können.

Cross-Platform Expert Advisor: Signale Cross-Platform Expert Advisor: Signale

Dieser Artikel beschreibt die Klassen CSignal und CSignals, die in Cross-Plattform Expert Advisor verwendet werden. Es werden die Unterschiede zwischen MQL4 und MQL5 untersucht, wie sie jeweils auf bestimmte Daten zum Ermitteln eines Handelssignals zugreifen, um einen Code zu schreiben, der für beide Kompiler kompatible ist.

Das Beispiel eines Indikators, der die Linien Unterstützung / Widerstands zeichnet Das Beispiel eines Indikators, der die Linien Unterstützung / Widerstands zeichnet

Im Artikel wird das Realisierungsbeispiel des Indikators für den Aufbau der Linien Unterstützung/Widerstands aufgrund der formalisierten Bedingungen aufgeführt. Sie haben die Möglichkeit, den Indikator zu verwenden, aber verstehen auch nebenbei, wie einfach es ist, das zu realisieren. Nun können Sie selbst die Bedingungen für den Aufbau der Linien formulieren, die Sie nötig finden, dabei den Code des Indikators nach Ihren Wünschen ein wenig ändern.