English Русский 中文 Español 日本語 Português
preview
Indikatoren mit Hintergrund: Kanäle mit Transparenz

Indikatoren mit Hintergrund: Kanäle mit Transparenz

MetaTrader 5Beispiele | 8 Mai 2023, 11:21
338 0
Samuel Manoel De Souza
Samuel Manoel De Souza

Einführung

In diesem Artikel werde ich eine Methode zur Erstellung von nutzerdefinierten Indikatoren vorstellen, deren Zeichnungen mit der Klasse CCanvas aus der Standardbibliothek erstellt werden. Ich werde speziell auf Indikatoren eingehen, die den Bereich zwischen zwei Linien mit einer Volltonfarbe füllen müssen. Bevor wir beginnen, werden wir die Gründe sehen, warum die Verwendung von Canvas (Hintergrundleinwand) die beste Wahl gegenüber den derzeit verfügbaren Optionen für diese Art von Indikatoren sein kann. Danach sehen wir uns einige Charteigenschaften an, die zur Berechnung von Koordinaten erforderlich sind, sowie den grundlegenden Prozess, der die Arbeit mit CCanvas beinhaltet.

Das endgültige Ziel besteht darin, alles, was bis zu diesem Zeitpunkt gesehen wurde, zu nutzen, um einen Indikator mit Transparenz zu erstellen. Die gesamte Arbeit bezieht sich nur auf das Hauptfenster des Charts. Sobald wir unser Ziel erreicht haben, können wir das auf die Arbeit mit Unterfenster-Indikatoren ausweiten.

Die Themen dieses Artikels sind nachstehend aufgeführt:

Gründe für die Nutzung von Canvas

Jemand würde fragen, warum man Canvas anstelle des bereits für nutzerdefinierte Indikatoren verfügbaren DRAW_FILLING verwenden sollte? Dafür gibt es mindestens zwei Gründe:

  1. Die Farben des Indikators werden mit den Farben anderer Indikatoren, Kerzen und Chart-Objekte gemischt.
  2. DRAW_FILLING erlaubt keine Transparenz.

Chart mit 2 Indikatoren und 1 Objekt

Eigenschaften des Chartfensters

Um mit dem Zeichnen eines nutzerdefinierten Charts zu beginnen, müssen wir uns über einige Charteigenschaften Gedanken machen. Alle Eigenschaften können Sie in der Dokumentation nachlesen. Um die Werte dieser Eigenschaften zu erhalten, müssen wir die entsprechenden Funktionen ChartGetInteger und ChartGetDouble verwenden. Es gibt auch einen ChartGetString, aber mit dem werden wir hier nicht arbeiten.

Die Eigenschaften, die wir verwenden werden, sind unten mit einer kurzen Beschreibung aufgeführt. Wenn wir mehr brauchen, werde ich später darauf hinweisen.

  • CHART_WIDTH_IN_PIXELS — die Breite des Chartfensters, ohne die Preisskala.
  • CHART_HEIGTH_IN_PIXELS — die Höhe des Unterfensters, ohne Datumsskala.
  • CHART_PRICE_MIN — der Preis, der dem unteren Rand des Unterfensters entspricht.
  • CHART_PRICE_MAX — der Preis, der dem oberen Rand des Unterfensters entspricht.
  • CHART_SCALE — bestimmt die Abstände zwischen den Balken. Nach einigen Tests habe ich herausgefunden, dass es sich um eine Zweierpotenz handelt, die sich aus pow(2, CHART_SCALE) ergibt.
  • CHART_FISRT_VISIBLE_BAR — der erste sichtbare Balken im Chart, von links nach rechts.
  • CHART_VISIBLE_BARS — die Anzahl der sichtbaren Balken im Chart.

Verstehen der Eigenschaften des Chartfensters

Diese Eigenschaften sind auf dem nächsten Bild gut zu erkennen.

Chart-Eigenschaften für Koordinaten

Mit den Eigenschaften CHART_WIDTH_IN_PIXELS und CHART_HEIGTH _IN_PIXELS bestimmen wir die Größe des Canvas-Objekts, das wir für die Erstellung der Zeichnungen benötigen. Und wenn sich das Chartfenster ändert, müssen wir, wenn sich diese Eigenschaften geändert haben, die Größe von Canvas anpassen.

Zum besseren Verständnis werden wir einen einfachen Indikator erstellen, der die Eigenschaften anzeigt und wie sie sich aufgrund von Preisänderungen und Nutzerinteraktionen verändern. Wir werden bereits mit der Verwendung von Canvas beginnen, um den Prozess des Zeichnens auf der Leinwand zu verstehen.


Indikator für Charteigenschaften

An dieser Stelle wird davon ausgegangen, dass Sie bereits wissen, wie man einen nutzerdefinierten Indikator erstellt. Wenn das nicht der Fall ist, können Sie mit diesem Artikel MQL5 beginnen: Erstellen Sie Ihren eigenen Indikator und Erkunden der Möglichkeiten mehrfarbige Kerzen zu erstellen. Fangen wir an.

Ich habe meinen Indikator auf diesem Weg erstellt. Ich schlage Ihnen vor, dasselbe für die Organisation zu tun.

Sobald das Indikatorskelett fertig ist, müssen wir die CCanvas-Bibliothek zu der Datei hinzufügen. Wir können dies mit der Direktive #include erreichen.

Dann erstellen wir eine Instanz der Klasse CCanvas. All dies unmittelbar nach den Richtlinien für den Indikator mit #property.

#property copyright "Copyright 2023, Samuel Manoel De Souza"
#property link      "https://www.mql5.com/en/users/samuelmnl"
#property version   "1.00"
#property indicator_chart_window

#include <Canvas/Canvas.mqh>
CCanvas Canvas;

Das erste, was wir tun müssen, wenn wir mit CCanvas arbeiten, ist, ein OBJ_BITMAP_LABEL zu erstellen und ihm eine Ressource zuzuordnen. Das muss geschehen, wenn Sie es dem Chart hinzufügen wollen, normalerweise bei der Initialisierung des Indikators, mit der Methode CreateBitampLabel(...). Als Letztes wird der OBJ_BITMAP_LABEL und die damit verbundene Ressource gelöscht. Dies muss geschehen, wenn Sie den Indikator aus dem Chart entfernen wollen, normalerweise bei der Deinitialisierung des Indikators mit der Methode Destory(). In der Zwischenzeit führen wir den grundlegenden Zeichenprozess aus, der darin besteht, die Zeichnungen zu löschen (Löschen oder Festlegen von Standardpixelwerten für die Ressource), die Zeichnungen zu erstellen und die Ressource zu aktualisieren. Der komplette Lebenszyklus eines Canvas-Prozesses ist in dem folgenden Chart dargestellt.

canvas_process

Der Einfachheit halber werden wir das Löschen, Zeichnen und Aktualisieren in einer einzigen Funktion namens Redraw zusammenfassen. Wenn wir alles in den Code schreiben, erhalten wir die folgende Struktur.

//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
int OnInit()
  {
//--- indicator buffers mapping
   Canvas.CreateBitmapLabel(0, 0, "Canvas", 0, 0, 200, 150, COLOR_FORMAT_ARGB_NORMALIZE);
//---
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+
//| Custom indicator deinitialization function                       |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
   Canvas.Destroy();
  }
//+------------------------------------------------------------------+
//| Custom indicator redraw function                                 |
//+------------------------------------------------------------------+
void Redraw(void)
  {
   uint default_color = ColorToARGB(clrBlack);
   uint text_color = ColorToARGB(clrWhite);
//--- canvas erase
   Canvas.Erase(default_color);
//--- add first draw

//--- add second draw

//--- add ... draw

//--- add last draw

//--- canvas update
   Canvas.Update();
  }

Um die Eigenschaften anzuzeigen, werden wir die Methode TextOut verwenden. Diese Eigenschaftswerte werden als String in einer Struct-Array Variablen gespeichert. 

struct StrProperty
  {
   string name;
   string value;
  };
Die Struktur kann wie folgt aussehen. Dann können wir sie kurz in einer Schleife ausgeben. Da wir das Array noch nicht haben, übergeben wir das Array als Parameter in der Redraw-Funktion. Dann sieht die Funktion Redraw wie folgt aus:
void Redraw(StrProperty &array[])
  {
   uint default_color = ColorToARGB(clrBlack);
   uint text_color = ColorToARGB(clrWhite);
//--- canvas erase
   Canvas.Erase(default_color);
//--- add first draw
   int total = ArraySize(array);
   for(int i=0;i<total;i++)
     {
      int padding = 2;
      int left = padding, right = Canvas.Width() - padding, y = i * 20 + padding;
      Canvas.TextOut(left, y, array[i].name, text_color, TA_LEFT);
      Canvas.TextOut(right, y, array[i].value, text_color, TA_RIGHT);
     }
//--- canvas update
   Canvas.Update();
  }
Schließlich können wir die Eigenschaftswerte abrufen und ausgeben. Wenn Ihr Code nicht über die Ereignisbehandlung von OnChartEvent verfügt, müssen Sie das hinzufügen. Und dort überprüfen wir die Ereignis-ID CHARTEVENT_CHART_CHANGE. Wenn das Ereignis eintritt, deklarieren wir einige Variablen, um die Eigenschaftswerte zu übernehmen und sie an ein Struktur-Array zu übergeben, und rufen dann die Funktion Redraw auf. Und das war's. Wir können den Indikator kompilieren, ihn zum Chart hinzufügen und das Chart manipulieren, um die Aktualisierungen der Leinwand zu sehen.
//+------------------------------------------------------------------+
//| Custom indicator chart event handler function                    |
//+------------------------------------------------------------------+
void OnChartEvent(const int id, const long& lparam, const double& dparam, const string& sparam)
  {
   if(id != CHARTEVENT_CHART_CHANGE)
      return;
   int chart_width         = (int)ChartGetInteger(0, CHART_WIDTH_IN_PIXELS);
   int chart_height        = (int)ChartGetInteger(0, CHART_HEIGHT_IN_PIXELS);
   int chart_scale         = (int)ChartGetInteger(0, CHART_SCALE);
   int chart_first_vis_bar = (int)ChartGetInteger(0, CHART_FIRST_VISIBLE_BAR);
   int chart_vis_bars      = (int)ChartGetInteger(0, CHART_VISIBLE_BARS);
   double chart_prcmin     = ChartGetDouble(0, CHART_PRICE_MIN);
   double chart_prcmax     = ChartGetDouble(0, CHART_PRICE_MAX);
//---
   StrProperty array[]
     {
        {"Width", (string)chart_width},
        {"Height", (string)chart_height},
        {"Scale", (string)chart_scale},
        {"First Vis. Bar", (string)chart_first_vis_bar},
        {"Visible Bars", (string)chart_vis_bars},
        {"Price Min", (string)chart_prcmin},
        {"Price Max", (string)chart_prcmax},
     };
   Redraw(array);
  }

Umrechnung von Koordinaten

An dieser Stelle benötigen wir einige grundlegende Funktionen, um die Konvertierung der Zeitangabe oder des Indexes des Balkens zu x in Pixeln, des Preises zu y in Pixeln, von x zu Balkenindex und von y zu Preis durchzuführen (einige davon werden wir jetzt nicht verwenden, aber wir können sie alle auf einmal machen). Aus diesem Grund werden wir die Variablen der Charteigenschaften in den globalen Bereich verschieben, während wir in der Funktion OnChartEvent nur die Werte aktualisieren und die Redraw-Funktion bei Bedarf aufrufen. Die ideale Lösung besteht darin, die Variablen und Konvertierungsfunktionen in einer Klasse oder Struktur zu kapseln, aber wir wollen es erst einmal einfach halten. Ich empfehle Ihnen jedoch, mit dem Erlernen von OOP zu beginnen, indem Sie den Artikel Die Grundlagen der objektorientierten Programmierung und das entsprechende Thema in der Dokumentation (Objektorientierte Programmierung) lesen. Wir werden bei nächster Gelegenheit davon Gebrauch machen.

Da es sich bei den Funktionen im Wesentlichen um proportionale Beziehungen handelt, werden wir keine Zeit und keine Worte auf Erklärungen verwenden. Sie lauten wie folgt.

//+------------------------------------------------------------------+
//| Converts the chart scale property to bar width/spacing           |
//+------------------------------------------------------------------+
int BarWidth(int scale) {return (int)pow(2, scale);}
//+------------------------------------------------------------------+
//| Converts the bar index(as series) to x in pixels                 |
//+------------------------------------------------------------------+
int ShiftToX(int shift) {return (chart_first_vis_bar - shift) * BarWidth(chart_scale) - 1;}
//+------------------------------------------------------------------+
//| Converts the price to y in pixels                                |
//+------------------------------------------------------------------+
int PriceToY(double price)
  {
// avoid zero divider
   if(chart_prcmax - chart_prcmin == 0.)
      return 0.;
   return (int)round(chart_height * (chart_prcmax - price) / (chart_prcmax - chart_prcmin) - 1);
  }
//+------------------------------------------------------------------+
//| Converts x in pixels to bar index(as series)                     |
//+------------------------------------------------------------------+
int XToShift(int x)
  {
// avoid zero divider
   if(BarWidth(chart_scale) == 0)
      return 0;
   return chart_first_vis_bar - (x + BarWidth(chart_scale) / 2) / BarWidth(chart_scale);
  }
//+------------------------------------------------------------------+
//| Converts y in pixels to price                                    |
//+------------------------------------------------------------------+
double YToPrice(int y)
  {
// avoid zero divider
   if(chart_height == 0)
      return 0;
   return chart_prcmax - y * (chart_prcmax - chart_prcmin) / chart_height;
  }

DRAW_FILLING mit Transparenz

Jetzt haben wir alles, was wir brauchen, um unser DRAW_FILLING mit CCanvas zu implementieren.

Wir werden keine Zeit damit verbringen, einen neuen Indikator zu erstellen. Nehmen wir stattdessen ein Beispiel, das in allen MetaTrader 5-Plattformen vorhanden ist, und fügen wir die Füllung zwischen zwei Linien ein. Ich verwende den Indikator Envelopes, der sich im Verzeichnis „\\MQL5\\Indicators\\Examples\\“ im Terminal-Datenordner befindet. Ich kopiere die Datei Envelopes.mq5 in das gleiche Verzeichnis, in dem ich den Indikator ChartPropertiesViwer erstellt habe. Sie können einen beliebigen Indikator wählen, aber ich schlage vor, denselben Indikator zu verwenden, während Sie die in diesem Artikel beschriebenen Schritte verfolgen.

Als Erstes müssen wir alles, was wir im Indikator ChartPropertiesViewer gemacht haben, in die Envelopes kopieren.

Wie oben erwähnt, werden wir den Kanal zwischen zwei Linien füllen. Zu diesem Zweck werden wir eine Funktion erstellen, die die Arrays übergibt, die diesen Linienwerten entsprechen. Im Envelope-Indikator werden die Arrays durch die Variablen ExtUpBuffer und ExtMABuffer angegeben.

double                   ExtUpBuffer[];
double                   ExtDownBuffer[];

Zusammen mit den Arrays werden wir einige weitere Variablen übergeben, die es uns ermöglichen, zwei Farben zu verwenden, eine Transparenzstufe festzulegen und den Indikator nach links oder rechts im Chart zu verschieben.

Parameter Beschreibung der Variablen
 serie1  Array von Werten, die der ersten Linie entsprechen
 serie2  Array von Werten, die der zweiten Linie entsprechen
 clr1  Farbe, wenn serie1 >= serie2
 clr2  Farbe, wenn serie1 < serie2
 alpha  Wert für die Transparenz des Kanals
 plot_shift  Verschieben Sie den Indikator nach rechts oder links im Chart

Die Funktion, die die vorhandenen Variablen und die genannten Parameter verwendet, lautet wie folgt.

//+------------------------------------------------------------------+
//| Fill the area between two lines                                  |
//+------------------------------------------------------------------+
void DrawFilling(double &serie1[], double &serie2[], color clr1, color clr2, uchar alpha = 255, int plot_shift = 0)
  {
   int start  = chart_first_vis_bar;
   int total  = chart_vis_bars + plot_shift;
   uint argb1 = ColorToARGB(clr1, alpha);
   uint argb2 = ColorToARGB(clr2, alpha);
   int limit  = fmin(ArraySize(serie1), ArraySize(serie2));
   int px, py1, py2;
   for(int i = 0; i < total; i++)
     {
      int bar_position = start - i;
      int bar_shift = start - i + plot_shift;
      int bar_index = limit - 1 - bar_shift;
      if(serie1[bar_index] == EMPTY_VALUE || serie1[bar_index] == EMPTY_VALUE || bar_shift >= limit)
         continue;
      int x  = ShiftToX(bar_position);
      int y1 = PriceToY(serie1[bar_index]);
      int y2 = PriceToY(serie2[bar_index]);
      uint argb = serie1[bar_index] < serie2[bar_index] ? argb2 : argb1;
      if(i > 0 && serie1[bar_index - 1] != EMPTY_VALUE && serie2[bar_index - 1] != EMPTY_VALUE)
        {
         if(py1 != py2)
            Canvas.FillTriangle(px, py1, px, py2, x, y1, argb);
         if(y1 != y2)
            Canvas.FillTriangle(px, py2, x, y1, x, y2, argb);
        }
      px  = x;
      py1 = y1;
      py2 = y2;
     }
  }

Bis zu diesem Punkt haben wir Canvas mit einer festen Größe verwendet. Bei Indikatorzeichnungen muss Canvas jedoch den gesamten Bereich des Charts ausfüllen. Wenn sich die Größe des Chartfensters ändert, z. B. durch Maximieren, Minimieren, Dehnen zu einer Seite oder Hinzufügen von Unterfenster-Indikatoren, müssen wir sicherstellen, dass die Arbeitsfläche immer noch den gesamten Bereich des Charts ausfüllt. Um dies zu tun, werden wir die Größe von Canvas ändern und eine kleine Änderung in unserer Funktion OnChartEvent vornehmen.

void OnChartEvent(const int id, const long& lparam, const double& dparam, const string& sparam)
  {
   if(id != CHARTEVENT_CHART_CHANGE)
      return;
   chart_width          = (int)ChartGetInteger(0, CHART_WIDTH_IN_PIXELS);
   chart_height         = (int)ChartGetInteger(0, CHART_HEIGHT_IN_PIXELS);
   chart_scale          = (int)ChartGetInteger(0, CHART_SCALE);
   chart_first_vis_bar  = (int)ChartGetInteger(0, CHART_FIRST_VISIBLE_BAR);
   chart_vis_bars       = (int)ChartGetInteger(0, CHART_VISIBLE_BARS);
   chart_prcmin         = ChartGetDouble(0, CHART_PRICE_MIN, 0);
   chart_prcmax         = ChartGetDouble(0, CHART_PRICE_MAX, 0);
   if(chart_width != Canvas.Width() || chart_height != Canvas.Height())
      Canvas.Resize(chart_width, chart_height);

Jetzt werden wir einige kleine Updates vornehmen, damit es funktioniert.

  1. Aktualisieren wir unsere Redraw-Funktion, indem wir die Parameter entfernen, die im vorherigen Indikator hinzugefügt wurden, und die DrawFilling-Funktion hinzufügen.
  2. Fügen wir unsere Redraw-Funktion in die OnCalculation ein, um die Zeichnung zu aktualisieren, wenn sich die Indikatorwerte ändern.
  3. Wir ändern den als Parameter übergebenen Objektnamen beim Aufruf von CreateBitmapLabel.

//+------------------------------------------------------------------+
//| Custom indicator redraw function                                 |
//+------------------------------------------------------------------+
void Redraw(void)
  {
   uint default_color = 0;
   color clrup = (color)PlotIndexGetInteger(0, PLOT_LINE_COLOR, 0);
   color clrdn = (color)PlotIndexGetInteger(1, PLOT_LINE_COLOR, 0);
//--- canvas erase
   Canvas.Erase(default_color);
//--- add first draw
   DrawFilling(ExtUpBuffer, ExtDownBuffer,clrup, clrdn, 128, InpMAShift);
//--- canvas update
   Canvas.Update();
  }
//--- the main loop of calculations
   for(int i=start; i<rates_total && !IsStopped(); i++)
     {
      ExtUpBuffer[i]=(1+InpDeviation/100.0)*ExtMABuffer[i];
      ExtDownBuffer[i]=(1-InpDeviation/100.0)*ExtMABuffer[i];
     }
   Redraw();
//--- OnCalculate done. Return new prev_calculated.
   return(rates_total);
   Canvas.CreateBitmapLabel(0, 0, short_name, 0, 0, 200, 150, COLOR_FORMAT_ARGB_NORMALIZE);

Und das ist erledigt. Wir können sehen, wie es jetzt aussieht, mit zwei Envelope-Indikatoren mit unterschiedlichen Periodenlängen und einem Rechteckobjekt.

Envelope-Indikatoren unter Verwendung von CCanvas mit Alpha = 128

Envelope-Indikatoren unter Verwendung von CCanvas mit aplha = 255

Wie man sehen kann, ist das Problem mit den Indikatoren gelöst, während das Problem mit den Chartobjekten immer noch vorhanden ist, aber das ist ein Thema für ein anderes Kapitel.


Erweiterung der Methode für die Arbeit in Unterfenster-Indikatoren

Siehe das Bild unten. Hier sehen wir einen Unterfenster-Indikator, der die Option DRAW_FILLING verwendet. Dieses Bild wurde aus der MQL-Dokumentation entnommen. Wir werden dasselbe tun, aber die Transparenz mit CCanvas ermöglichen und, was noch wichtiger ist, die Probleme mit überlappenden Bereichen vermeiden.

Unterfenster-Indikator mit DRAW_FILLING

Die Änderungen, die wir vornehmen müssen, sind unten aufgeführt:

  • Wir erstellen die Bitmap-Beschriftung in demselben Unterfenster, in dem sich der Indikator befindet.
  • Ändern der Größe der Leinwand basierend auf der Größe des Unterfensters anstelle des Hauptfensters des Charts.

Um die Bitmap-Beschriftung in demselben Unterfenster zu erstellen und die Größe des Unterfensters zu ermitteln, müssen wir herausfinden, in welchem Unterfenster der Indikator platziert ist. Man könnte meinen, dass es sich einfach um das letzte Unterfenster des Charts handelt, aber das Terminal erlaubt es, zwei oder mehr Indikatoren im selben Unterfenster zu platzieren, nicht unbedingt im letzten Unterfenster. Dann brauchen wir eine Funktion, die die Nummer des Unterfensters zurückgibt, in dem sich der Indikator befindet. Schauen Sie sich die folgende Funktion an:

//+------------------------------------------------------------------+
//| return the number of the subwindow where the indicator is located|
//+------------------------------------------------------------------+
int ChartIndicatorFind(string shortname)
  {
   int subwin = ChartGetInteger(0, CHART_WINDOWS_TOTAL);
   while(subwin > 0)
     {
      subwin--;
      int total = ChartIndicatorsTotal(0, subwin);
      for(int i = 0; i < total; i++)
        {
         string name = ChartIndicatorName(0, subwin, i);
         if(name == shortname)
            return subwin;
        }
     }
   return -1;
  }

Im letzten Indikator haben wir den Envelope-Indikator als Beispiel verwendet. Nun werden wir den Code aus der Dokumentation, dem Thema DRAW_FILLING als Quelle für unser Beispiel verwenden. Wir können einen neuen Indikator in demselben Verzeichnis erstellen, in dem wir zuvor zwei Indikatoren erstellt haben. Nennen wir sie „SubwindowIndicator“ (Unterfenster-Indikator). Kopieren Sie dann den Code aus der Dokumentation.

Dieser Indikator wird mit Hilfe von DRAW_FILLING gezeichnet. Da wir CCanvas verwenden werden, um den Kanal zu füllen, können wir den Plot-Typ durch Linien ersetzen. Nachfolgend sind die Änderungen der Indikatoreigenschaften aufgeführt.

#property indicator_plots   2
//--- plot Intersection
#property indicator_label1  "Fast"
#property indicator_type1   DRAW_LINE
#property indicator_color1  clrRed
#property indicator_width1  1
#property indicator_label2  "Slow"
#property indicator_type2   DRAW_LINE
#property indicator_color2  clrBlue
#property indicator_width2  1

Und die Änderungen in der OnInit-Funktion.

//--- indicator buffers mapping
   SetIndexBuffer(0,IntersectionBuffer1,INDICATOR_DATA);
   SetIndexBuffer(1,IntersectionBuffer2,INDICATOR_DATA);
//---
   PlotIndexSetInteger(0,PLOT_SHIFT,InpMAShift);
   PlotIndexSetInteger(1,PLOT_SHIFT,InpMAShift);

Außerdem brauchen wir den Indikator nicht, um das Aussehen der Linie zu ändern, dann können wir diese Zeile in der Funktion OnCalculate auskommentieren.

//--- If a sufficient number of ticks has been accumulated
   if(ticks>=N)
     {
      //--- Change the line properties
      //ChangeLineAppearance();
      //--- Reset the counter of ticks to zero
      ticks=0;
     }

Jetzt können wir unsere Charteigenschaftsvariablen und unsere in diesem Artikel erstellten Funktionen hinzufügen. In diesem Indikator haben die Arrays, die wir als Parameter an die Funktion DrawFilling übergeben müssen, unterschiedliche Namen. Wir müssen sie also in der Redraw-Funktion ändern

double         IntersectionBuffer1[];
double         IntersectionBuffer2[];

Und die Redraw-Funktion wird:

//+------------------------------------------------------------------+
//| Custom indicator redraw function                                 |
//+------------------------------------------------------------------+
void Redraw(void)
  {
   uint default_color = 0;
   color clrup = (color)PlotIndexGetInteger(0, PLOT_LINE_COLOR, 0);
   color clrdn = (color)PlotIndexGetInteger(1, PLOT_LINE_COLOR, 0);
//--- canvas erase
   Canvas.Erase(default_color);
//--- add first draw
   DrawFilling(IntersectionBuffer1, IntersectionBuffer2, clrup, clrdn, 128, InpMAShift);
//--- canvas update
   Canvas.Update();
  }

Nach dem Kompilieren des Codes erhalten wir das erwartete Ergebnis.

Unterfenster-Indikator mit transparenter Kanalfüllung


Schlussfolgerung

In diesem Artikel haben wir den grundlegenden Prozess, der die Arbeit mit CCanvas, einige Charteigenschaften, wie man ihre Werte zu erhalten und verwenden Sie sie, um einige grundlegende Koordinaten Konvertierungen, die nützlich und anwendbar auf zahlreiche Zwecke zu machen ist gesehen. Danach waren wir in der Lage, einen Indikator mit Transparenz zu entwickeln. Unsere letzte Aufgabe bestand darin, die Methode für die Arbeit mit Unterfenster-Indikatoren zu erweitern.

Die Dateien der in diesem Artikel entwickelten Indikatoren finden Sie am Ende dieses Artikels zum Download.


Übersetzt aus dem Englischen von MetaQuotes Ltd.
Originalartikel: https://www.mql5.com/en/articles/12357

Beigefügte Dateien |
Envelopes.mq5 (10.43 KB)
Algorithmen zur Optimierung mit Populationen: Saplings Sowing and Growing up (SSG) Algorithmen zur Optimierung mit Populationen: Saplings Sowing and Growing up (SSG)
Der Algorithmus Saplings Sowing and Growing up (SSG, Setzen, Säen und Wachsen) wurde von einem der widerstandsfähigsten Organismen der Erde inspiriert, der unter den verschiedensten Bedingungen überleben kann.
Erstellen eines EA, der automatisch funktioniert (Teil 11): Automatisierung (III) Erstellen eines EA, der automatisch funktioniert (Teil 11): Automatisierung (III)
Ein automatisiertes System wird ohne angemessene Sicherheit nicht erfolgreich sein. Die Sicherheit wird jedoch nicht gewährleistet sein, wenn man bestimmte Dinge nicht richtig versteht. In diesem Artikel werden wir untersuchen, warum es so schwierig ist, ein Maximum an Sicherheit in automatisierten Systemen zu erreichen.
Lernen Sie, wie man ein Handelssystem nach Fibonacci entwickelt Lernen Sie, wie man ein Handelssystem nach Fibonacci entwickelt
In diesem Artikel setzen wir unsere Serie zur Erstellung eines Handelssystems auf der Grundlage des beliebtesten technischen Indikators fort. Hier ist ein neues technisches Werkzeug, das Fibonacci und wir werden lernen, wie man ein Handelssystem auf der Grundlage dieses technischen Indikators zu entwerfen.
Erstellen eines EA, der automatisch funktioniert (Teil 10): Automatisierung (II) Erstellen eines EA, der automatisch funktioniert (Teil 10): Automatisierung (II)
Automatisierung bedeutet nichts, wenn Sie den Zeitplan nicht kontrollieren können. Kein Arbeitnehmer kann effizient sein, wenn er 24 Stunden am Tag arbeitet. Viele sind jedoch der Meinung, dass ein automatisiertes System 24 Stunden am Tag funktionieren sollte. Aber es ist immer gut, eine Möglichkeit zu haben, einen Arbeitsbereich für den EA festzulegen. In diesem Artikel geht es darum, wie man einen solchen Zeitbereich richtig festlegt.