Indikatoren mit Hintergrund: Kanäle mit Transparenz
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
- Eigenschaften des Chartfensters
- Verstehen der Eigenschaften des Chartfensters
- Indikator für Charteigenschaften
- Umrechnung von Koordinaten
- DRAW_FILLING mit Transparenz
- Erweiterung der Methode für die Arbeit in Unterfenster-Indikatoren
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:
- Die Farben des Indikators werden mit den Farben anderer Indikatoren, Kerzen und Chart-Objekte gemischt.
- DRAW_FILLING erlaubt keine Transparenz.
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.
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.
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.
- Aktualisieren wir unsere Redraw-Funktion, indem wir die Parameter entfernen, die im vorherigen Indikator hinzugefügt wurden, und die DrawFilling-Funktion hinzufügen.
- Fügen wir unsere Redraw-Funktion in die OnCalculation ein, um die Zeichnung zu aktualisieren, wenn sich die Indikatorwerte ändern.
- 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.
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.
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.
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
- Freie Handelsapplikationen
- Über 8.000 Signale zum Kopieren
- Wirtschaftsnachrichten für die Lage an den Finanzmärkte
Sie stimmen der Website-Richtlinie und den Nutzungsbedingungen zu.