English Русский 中文 Español 日本語 Português
preview
Erstellen eines Ticker-Panels: Verbesserte Version

Erstellen eines Ticker-Panels: Verbesserte Version

MetaTrader 5Indikatoren | 17 Februar 2023, 14:50
342 0
Daniel Jose
Daniel Jose

Einführung

Im vorherigen Artikel Erstellen eines Ticker-Panels: Basisversion haben wir gesehen, wie man einen Indikator in Form eines Panels erstellt, das ein Band mit Echtzeit-Preisen von Symbolen anzeigt. Im vorherigen Artikel haben wir den Indikator jedoch nicht vollständig implementiert, nicht weil es unmöglich war, sondern weil unser Ziel darin bestand, den Prozess der Erstellung des Indikators zu zeigen, um zu sehen, wie er mit möglichst wenig Code funktioniert, damit der Eindruck entsteht, dass er sich bewegt.

Wir haben gesehen, dass es nicht notwendig ist, einen speziellen Code oder komplexe Berechnungen zu erstellen, um das Panel von rechts nach links oder umgekehrt zu bewegen. Der Nutzer musste lediglich angeben, ob der Wert steigt oder sinkt. Nur so ist es möglich, den Indikator nach rechts oder links zu bewegen oder anzuhalten.

Diese Daten reichen jedoch nicht immer aus und spiegeln nicht immer wider, was die Menschen wirklich auf dem Panel sehen wollen. Es wäre schön, mehr Details zu erfahren. Genau das werden wir hier umsetzen. Wir werden auch einige weitere Dinge implementieren, um das Panel noch nützlicher zu machen. Die bloße Tatsache, dass es eine Panel in einer neuen Form gibt, ist noch völlig nutzlos, da es andere, geeignetere Möglichkeiten gibt, sie zu erstellen.

Daher werden wir als erstes die Ansicht ändern, indem wir ein Bild hinzufügen, z. B. das Logo eines Finanzinstruments oder ein anderes Bild, damit der Nutzer das angezeigte Finanzinstrument schnell und einfach identifizieren kann. Es heißt, ein Bild sagt mehr als tausend Worte. Wir werden sehen, ob das wirklich der Fall ist.


Einführung eines neuen Panel-Systems für Kurse

Das erste, was wir in dieser neuen Implementierung tun werden, ist eine neue Objektklasse zu erstellen, um das Objekt zu abstrahieren, das das Bild des Finanzinstruments darstellen wird. Wir werden hier den folgenden Code verwenden:

#property copyright "Daniel Jose"
//+------------------------------------------------------------------+
#include "C_Object_Base.mqh"
//+------------------------------------------------------------------+
class C_Object_BtnBitMap : public C_Object_Base
{
        public  :
//+------------------------------------------------------------------+
                void Create(string szObjectName, string szResource1)
                        {
                                C_Object_Base::Create(szObjectName, OBJ_BITMAP_LABEL);
                                ObjectSetString(Terminal.Get_ID(), szObjectName, OBJPROP_BMPFILE, 0, "\\Images\\Widget\\Bitmaps\\" + szResource1 + ".bmp");
                                ObjectSetInteger(Terminal.Get_ID(), szObjectName, OBJPROP_STATE, false);
                        };
//+------------------------------------------------------------------+
};

Bevor ich erkläre, was hier passiert, möchte ich kurz erläutern, warum wir so vorgehen. Wir beginnen damit, dass wir unter ein Bitmap-Objekt zum Speichern des Bildes erstellen. Wir verwenden eine allgemeine Klasse, um die Objekterstellung zu erleichtern. Dann teilen wir dem Objekt mit, welches Bild es auf der Grundlage des Bildorts und -namens verwenden soll. Und schließlich teilen wir dem Objekt mit, dass das anzuzeigende Bild das Bild mit dem Index 0 ist. Auf diese Weise erreichen wir eine sehr hohe Abstraktionsebene im System.

Jetzt wollen wir sehen, warum wir es genau so machen.

Zunächst ist daran zu erinnern, dass es bestimmte Einschränkungen in Bezug auf Objekte gibt. Es geht nicht darum, was getan werden kann, sondern wie es getan werden sollte. Die Verwendung einer Klasse zur Schaffung einer Systemabstraktion ermöglicht es uns, zu verbergen, was im Rest des Codes tatsächlich vor sich geht. Es ist uns eigentlich egal, was die Klasse C_Object_Bitmap tun soll, um ein Bild auf dem Bildschirm anzuzeigen.

Beachten Sie nun Folgendes: Wenn das Bild nicht existiert oder ein anderes Format hat, werden weder der Rest des Codes noch der Nutzer darüber informiert. Das Ergebnis ist ein leerer Bereich, der einen Fehler anzeigt. Um herauszufinden, ob ein Fehler vorliegt, sollten wir daher die Rückgabe der Funktion ObjectSetString überprüfen. Wenn false zurückgegeben wird, bedeutet dies, dass das Bild nicht geladen werden konnte und das Objekt aus der Liste der Objekte gelöscht werden kann. Aber das haben wir nicht getan. Warum?

Für mich macht das nicht den geringsten Unterschied, denn alle Finanzinstrumente, die auf dem Panel platziert werden, haben ein Bild, das das Finanzinstrument darstellt. Aber es gibt noch einen weiteren Grund, der vielleicht noch wichtiger ist.

Wenn wir genau hinsehen, werden wir feststellen, dass der Code in diesem Formular die Verwendung eines anderen Bildtyps als der Bitmap-Datei nicht zulässt, zum Beispiel können wir kein Bild auf einem transparenten Hintergrund platzieren. Der transparente Hintergrund selbst kann zwar manchmal nützlich sein, aber wenn wir versuchen, ihn mit dem vorhandenen Code zu implementieren, erhalten wir möglicherweise ein Bild, das sehr seltsame Informationen anstelle des erforderlichen Logos des Finanzinstruments zeigt. Wir müssten also einen anderen Weg finden, solche Bilder zusätzlich zu dem im obigen Code gezeigten darzustellen.

Eine dieser Methoden wurde in dem Artikel gezeigt, um Charts interessanter zu machen: Hinzufügen eines Hintergrunds. In diesem Artikel haben wir Bilder direkt über eine Ressource erstellt. Obwohl ich auch eine Bitmap-Datei verwendet habe (da sie ein einfacheres internes Code-Konstruktionsmodell hat), hindert Sie nichts daran, andere Formate zu verwenden, zum Beispiel eine GIF-Datei für eine Animation oder ein anderes Dateiformat. Der wichtigste Punkt ist, dass wir mit dieser Methode beliebige Bilder erstellen und verwenden können, einschließlich des transparenten Hintergrunds, was mit dem zuvor in diesem Artikel betrachteten Code nicht möglich ist.

Daher ist diese Abstraktion sehr wichtig: Wenn Sie ein anderes Bildformat oder sogar ein transparentes Hintergrundbild verwenden möchten, müssen Sie keine Änderungen am restlichen Code vornehmen - ändern Sie einfach den Code dieser Klasse, um zu implementieren, was Sie benötigen.

Aus diesem Grund ist dieser Code als Klasse implementiert, obwohl er recht kompakt ist. Durch die Verwendung der Klasse können wir alles, was hier passiert, vor den anderen Teilen des Panels verbergen.

Wir haben eine weitere Änderung in der Klasse C_Object_Edit. Schauen wir mal, was sich hier geändert hat:

template < typename T >
void Create(string szObjectName, color corTxt, color corBack, T InfoValue, string szFont = "Lucida Console", int iSize = 10)
        {
                C_Object_Base::Create(szObjectName, OBJ_EDIT);
                ObjectSetString(Terminal.Get_ID(), szObjectName, OBJPROP_FONT, szFont);
                ObjectSetInteger(Terminal.Get_ID(), szObjectName, OBJPROP_FONTSIZE, iSize);
                ObjectSetInteger(Terminal.Get_ID(), szObjectName, OBJPROP_ALIGN, ALIGN_LEFT);
                ObjectSetInteger(Terminal.Get_ID(), szObjectName, OBJPROP_BGCOLOR, corBack);
                ObjectSetInteger(Terminal.Get_ID(), szObjectName, OBJPROP_BORDER_COLOR, corBack);
                ObjectSetInteger(Terminal.Get_ID(), szObjectName, OBJPROP_READONLY, true);
                SetTextValue(szObjectName, InfoValue, corTxt);
        };

Da wir mehr Informationen auf dem Panel implementieren, müssen wir eine Klasse erstellen, die für die Platzierung eines Textes auf dem Chart verantwortlich ist. Sie ist ein wenig komplizierter als die ursprüngliche Version. Jetzt werden wir die Art der verwendeten Schrift und ihre Größe kontrollieren. Auf diese Weise können wir viel vielfältigere Dinge auf dem Panel platzieren als es vorher möglich war. Diese größere Vielfalt erfordert auch Änderungen in einer anderen Funktion dieser Klasse.

template < typename T >
void SetTextValue(string szObjectName, T InfoValue, color cor = clrNONE, const string szSufix = "")
        {
                color clr = (cor != clrNONE ? cor : (color)ObjectGetInteger(Terminal.Get_ID(), szObjectName, OBJPROP_COLOR));
                string sz0;
                
                if (typename(T) == "string") sz0 = (string)InfoValue; else
                if (typename(T) == "double")
                {
                        clr = (cor != clrNONE ? cor : ((double)InfoValue < 0.0 ? def_ColorNegative : def_ColoPositive));
                        sz0 = Terminal.ViewDouble((double)InfoValue < 0.0 ? -((double)InfoValue) : (double)InfoValue) + szSufix;
                }else   sz0 = "?#?";
                ObjectSetString(Terminal.Get_ID(), szObjectName, OBJPROP_TEXT, sz0);
                ObjectSetInteger(Terminal.Get_ID(), szObjectName, OBJPROP_COLOR, clr);
        };

Sie sehen, dass die Abstraktion einen großen Unterschied macht: Wir können mehrere Dinge angeben, mehr als in der vorherigen Version möglich war. Schauen wir uns also an, was wir mit der obigen Funktion tatsächlich tun.

Wenn eine Farbe angegeben ist, wird diese Farbe verwendet. Wenn keine Farbe angegeben wird, wird die Farbe verwendet, die bereits im Objekt vorhanden ist. Das ist sehr hilfreich, weil wir manchmal einfach den Text ändern können, ohne die Farbe oder irgendetwas anderes zu ändern, was das Objekt bereits hat. In dieser Phase prüfen wir, ob der angegebene Typ eine Zeichenkette ist. Wir tun dies zur RUN TIME, also müssen wir sicherstellen, dass die Dinge korrekt sind.

Wir können auch den Typ double verwenden, der hauptsächlich zur Angabe des Preises verwendet wird. Wenn dies zur RUN TIME erkannt wird, werden die Werte entsprechend gesetzt und die entsprechende Farbe verwendet. Jetzt haben wir eine interessante Tatsache, die es uns erlaubt, ein wenig mehr über den Wert zu sprechen - wir werden ein Suffix verwenden, das während des Anrufs mitgeteilt wird. Wenn etwas nicht klar ist, haben wir auch eine Formatierung, um dies anzuzeigen. Als Nächstes stellen wir den Text dar und passen seine Farbe an.

Durch die einfache Umsetzung dieser Änderungen können wir bereits Informationen wie unten dargestellt erstellen:


Wir haben ein Bild in der linken Ecke. Der Code für das Finanzinstrument erscheint in der oberen linken Ecke. In der oberen rechten Ecke wird die tägliche Vermögensveränderung angezeigt. Im unteren linke Eck gibt es einen Pfeil, der anzeigt, ob die Änderung des Finanzinstruments positiv oder negativ ist. Der letzte Kurs des Finanzinstruments wird in der unteren rechten Ecke angezeigt.

Um all diese Informationen darstellen zu können, müssen wir jedoch einige Änderungen in der Klasse C_Widget vornehmen. Mal sehen, was wir jetzt haben werden.


Die neue Klasse C_Widget

Öffnen wir die Header-Datei C_Widget.mqh, um zu sehen, was sich gegenüber der ersten Grundversion des Systems geändert hat. Werfen wir einen Blick auf diese Änderungen:

#include "Elements\C_Object_Edit.mqh"
#include "Elements\C_Object_BackGround.mqh"
#include "Elements\C_Object_BtnBitMap.mqh"
//+------------------------------------------------------------------+
C_Terminal Terminal;
//+------------------------------------------------------------------+
#define def_PrefixName  "WidgetPrice"
#define def_MaxWidth    160
//+------------------------------------------------------------------+
#define macro_MaxPosition (Terminal.GetWidth() >= (m_Infos.nSymbols * def_MaxWidth) ? Terminal.GetWidth() : m_Infos.nSymbols * def_MaxWidth)
#define macro_ObjectName(A, B) (def_PrefixName + (string)Terminal.GetSubWin() + CharToString(A) + "#" + B)

Im obigen Code, haben wir die Klasse C_Object_BtnBitMap.mqh eingebunden. Diese Header-Datei unterstützt Bilder, die als Logos verwendet werden können. Außerdem wurde die Zellenbreite geändert, um mehr Details anzuzeigen.

Am interessantesten ist vielleicht das Makro, das den Namen erzeugt, der in den Objekten verwendet werden soll. Achten Sie darauf, denn es wird noch einige Male verwendet werden.

Als Nächstes gehen wir zu dem Teil über, in dem wir alle privaten Elemente der Objektklasse deklarieren:

class C_Widget
{
        protected:
                enum EventCustom {Ev_RollingTo};
        private :
                enum EnumTypeObject {en_Background = 35, en_Symbol, en_Price, en_Percentual, en_Icon, en_Arrow};
                struct st00
                {
                        color CorBackGround,
                              CorSymbol;
                        int   nSymbols,
                              MaxPositionX;
                        struct st01
                        {
                              string szCode;
                        }Symbols[];
                }m_Infos;

// ... Rest of the code

Es mag so aussehen, als gäbe es keine Veränderungen. Aber der Code hat einige Änderungen erfahren. Werfen wir einen kurzen Blick darauf. Diese Dinge werden später immer wieder auftauchen und Sie werden sie besser verstehen können, wenn sie verwendet werden. Das erste, was auffällt, ist die Enumeration der Objekttypen. Dies gibt eigentlich nicht an, welche Art von Objekt wir erstellen, sondern was der Zweck dieses Objekts ist. Dies wird deutlicher, wenn wir mit dem Klassencode fortfahren.

Sie fragen sich vielleicht, warum wir nicht Definitionen anstelle dieser Enumeration verwenden. Das liegt daran, dass wir garantieren müssen, dass jedes Objekt einzigartig ist. Daher garantieren wir diese durch die Verwendung der Enumeration. Würden wir Definitionen verwenden, bestünde die Gefahr, dass zwei Definitionen denselben Wert haben und die Objekte somit nicht eindeutig wären. Denken Sie bei der Programmierung an dieses Detail.

Schauen wir uns nun die Änderungen an, die im System hinsichtlich der internen Verfahren vorgenommen wurden. Wir beginnen mit der Funktion, die den Hintergrund erstellt.

void CreateBackGround(void)
        {
                C_Object_BackGround backGround;
                string sz0 = macro_ObjectName(en_Background, "");
                                
                backGround.Create(sz0, m_Infos.CorBackGround);
                backGround.Size(sz0, TerminalInfoInteger(TERMINAL_SCREEN_WIDTH), Terminal.GetHeight());
        }

Hier verwenden wir die erste der Enumeration. Es lohnt sich also zu erklären, warum die Enumeration mit dem Wert 35 und nicht mit einem anderen Wert beginnen. Aber lassen Sie uns zunächst mit einer letzten wichtigen Sache abschließen: Wenn wir die Größe des Chart ändern, wird der OnChartEvent-Aufruf generiert, der es uns ermöglicht, die neue Größe zu überprüfen und das Panel entsprechend zu aktualisieren.

Aber wir können auf eine Größenänderung des Hintergrunds verzichten, da die Höhe des Panels immer fest ist. Wir werden später sehen, wo wir diesen Wert definieren. Um aber die Breite zu fixieren und somit Änderungen in der Panel-Breite ignorieren zu können, werden wir eine Methode anwenden, die vielleicht nicht sehr effektiv erscheint, aber garantiert, dass der Hintergrund groß genug ist.

Nun wollen wir sehen, warum wir die Enumeration bei 35 beginnen, obwohl wir auch einen kleineren, aber nicht irgendeinen Wert verwenden könnten.

Wenn Sie sich das Makro ansehen, das die Namen der Objekte erzeugt, wird Ihnen etwas auffallen:

#define macro_ObjectName(A, B) (def_PrefixName + (string)Terminal.GetSubWin() + CharToString(A) + "#" + B)

Das erste Argument des Makros ist die Enumeration, die wir in der Klasse definiert haben. Die Enumeration wird in die entsprechende Zeichenkette umgewandelt, und so erhalten wir auf der Grundlage des Wertes ein Zeichen, das verwendet wird, um den Namen eindeutig zu machen. Wenn Sie sich die ASCII-Tabelle ansehen, werden Sie feststellen, dass der erste gültige Wert 32 ist, was für das Leerzeichen steht. Wir könnten den Wert also mit 32 initialisieren, aber die Initialisierung einer niedrigeren Zahl macht keinen Sinn.

Dies geschieht also nur, weil wir den Wert in ein Zeichen umwandeln. Wenn der Wert in einen entsprechenden String umgewandelt würde, könnten wir die Enumeration mit 0 initialisieren. Aber in unserem Fall, wo es um Zeichen geht, ist der geeignete Mindestwert 32.

Sehen wir uns nun die Funktion an, die für das Hinzufügen der zu verwendenden Objekte zuständig ist.

void AddSymbolInfo(const string szArg, const bool bRestore = false)
        {
                C_Object_Edit edit;
                C_Object_BtnBitMap bmp;
                string sz0;
                const int x = 9999;

                bmp.Create(sz0 = macro_ObjectName(en_Icon, szArg), szArg);
                bmp.PositionAxleX(sz0, x);
                bmp.PositionAxleY(sz0, 15);
                edit.Create(sz0 = macro_ObjectName(en_Symbol, szArg), m_Infos.CorSymbol, m_Infos.CorBackGround, szArg);
                edit.PositionAxleX(sz0, x);
                edit.PositionAxleY(sz0, 10);
                edit.Size(sz0, 56, 16);
                edit.Create(sz0 = macro_ObjectName(en_Percentual, szArg), m_Infos.CorSymbol, m_Infos.CorBackGround, 0.0, "Lucida Console", 8);
                edit.PositionAxleX(sz0, x);
                edit.PositionAxleY(sz0, 10);
                edit.Size(sz0, 50, 11);
                edit.Create(sz0 = macro_ObjectName(en_Arrow, szArg), m_Infos.CorSymbol, m_Infos.CorBackGround, "", "Wingdings 3", 10);
                edit.PositionAxleX(sz0, x);
                edit.PositionAxleY(sz0, 26);
                edit.Size(sz0, 20, 16);
                edit.Create(sz0 = macro_ObjectName(en_Price, szArg), 0, m_Infos.CorBackGround, 0.0);
                edit.PositionAxleX(sz0, x);
                edit.PositionAxleY(sz0, 26);
                edit.Size(sz0, 60, 16);
                if (!bRestore)
                {
                        ArrayResize(m_Infos.Symbols, m_Infos.nSymbols + 1, 10);
                        m_Infos.Symbols[m_Infos.nSymbols].szCode = szArg;
                        m_Infos.nSymbols++;
                }
        }

Hier können wir etwas einführen, das viele seltsam finden werden. Aber für mich ist das nur ein Spaß. Ich spiele gern und erkunde die Möglichkeiten der Dinge. Und ich frage: „Warum habe ich das getan?“ 

Warum habe ich eine konstante Variable deklariert und sie dann in den Aufrufen verwendet? Und der Grund dafür ist, wie ich bereits sagte, dass ich mir gerne einen Spaß mit den Möglichkeiten der Sprache mache, was auch immer sie sein mag, um herauszufinden, was sie uns ermöglicht.

Man könnte sogar darauf verzichten, eine solche Konstante zu deklarieren. Sie können die Kompilierungsdefinition (#define) oder einfach den konstanten Wert in jeden der Aufrufe einfügen. Das Ergebnis wäre das gleiche. Schauen wir uns trotzdem den Rest des Funktionscodes an: Sie sehen, dass die Elemente so hinzugefügt werden, dass ihre Namen eindeutig sind, d. h. es kann keine zwei Elemente mit demselben Namen geben. Wir erstellen sie in der folgenden Reihenfolge: BitMap, ID des Finanzinstruments, tägliche prozentuale Veränderung, Pfeil, der die Richtung der Veränderung angibt, und der aktuelle Preis des Finanzinstruments.

Denken Sie an einen wichtigen Punkt: Die Bitmaps, die im Finanzinstrument verwendet werden, müssen 32 x 32 Bit groß sein. Wenn Sie eine andere Größe verwenden, müssen Sie die Größe ändern - aber nicht hier. Später werden wir sehen, wo es gemacht wird. Sie müssen jedoch bedenken, dass die Abmessungen aller anderen Objekte hier ebenfalls angepasst werden sollten. Wenn Sie also Ihr Panel größer oder kleiner machen wollen als das, das wir im Anhang haben, stellen Sie hier die erforderlichen Werte ein. Denken Sie daran, dass jede dieser Funktionen mit einem Objekt verbunden ist, das wir kurz vor dem Aufruf deklariert haben.

Nun wollen wir sehen, wie die Daten tatsächlich dargestellt werden.

inline void UpdateSymbolInfo(int x, const string szArg)
        {
                C_Object_Edit edit;
                C_Object_BtnBitMap bmp;
                string sz0;
                double v0[], v1;
                                
                ArraySetAsSeries(v0, true);
                if (CopyClose(szArg, PERIOD_D1, 0, 2, v0) < 2) return;
                v1 = ((v0[0] - v0[1]) / v0[(v0[0] > v0[1] ? 0 : 1)]) * 100.0;
                bmp.PositionAxleX(sz0 = macro_ObjectName(en_Icon, szArg), x);
                x += (int) ObjectGetInteger(Terminal.Get_ID(), sz0, OBJPROP_XSIZE);
                edit.PositionAxleX(macro_ObjectName(en_Symbol, szArg), x + 2);
                edit.PositionAxleX(sz0 = macro_ObjectName(en_Arrow, szArg), x  + 2);
                edit.SetTextValue(sz0, CharToString(v1 >= 0 ? (uchar)230 : (uchar)232), (v1 >= 0 ? def_ColoPositive : def_ColorNegative));
                edit.SetTextValue(sz0 = macro_ObjectName(en_Percentual, szArg), v1 , clrNONE, "%");
                edit.PositionAxleX(sz0, x + 62);
                edit.SetTextValue(sz0 = macro_ObjectName(en_Price, szArg), v0[0] * (v1 >= 0 ? 1 : -1));
                edit.PositionAxleX(sz0, x + 24);
        }

Hier stellen wir sicher, dass die Reihe der Werte immer vom jüngsten zum ältesten geht. Sobald wir uns sicher sind, können wir die Schlusskurse erfassen. Beachten Sie, dass wir die tägliche Ablesung durchführen, da wir die tägliche Veränderung im Panel anzeigen. Ein weiterer Punkt: Wenn die Menge der zurückgegebenen Daten geringer ist als erwartet, werden wir die Funktion sofort beenden, denn wenn wir fortfahren, werden wir den Indikator schließlich zerstören — die entsprechenden Informationen werden in der MetaTrader 5 Toolbox erscheinen. Wir wollen aber nicht, dass der Indikator wegen eines Fehlers, der in der Regel vorübergehend ist, unterbrochen wird.

Jetzt beachten Sie die folgende Berechnung. Sie ist für die Richtigkeit der Informationen über die prozentuale Veränderung des Vermögenspreises verantwortlich. Ich denke, es ist nicht schwer, diese Berechnung zu verstehen, da es sich um eine sehr gebräuchliche Berechnung handelt, die zur Bestimmung des prozentualen Preisanstiegs oder -rückgangs verwendet wird.

Als Nächstes werden wir die Objekte auf der Leinwand positionieren. Zuerst platzieren wir das Bild. Erinnern Sie sich daran, dass wir bei der Erstellung der Objekte erwähnt haben, dass die Größe an anderer Stelle verwendet werden wird. So ist es auch hier, obwohl die einzige Information, die wir brauchen, die Breite des Bildes ist. Aber, wie wir bereits gesagt haben, ist es besser, 32 x 32 zu verwenden, denn wenn Sie eine andere Größe benötigen, insbesondere eine größere, müssen Sie andere Werte entsprechend anpassen.

Wenn die Größe kleiner ist, gibt es kein Problem, da der Code sich an eine kleinere Bildgröße anpasst. Wenn Sie jedoch ein größeres Panel erstellen, insbesondere ein breiteres, bedeutet dies, dass ohne Anpassung des Wertes in def_MaxWidth die Informationen auf eine sehr seltsame Art und Weise rotieren. Hier sollten Sie den Wert in der Kompilationsdefinition erhöhen. Aber Vorsicht: Wenn Sie einen zu hohen Wert einstellen, müssen Sie länger warten, bis die Informationen wieder auftauchen, als wenn Sie angemessene Werte verwenden.

Alle nachfolgenden Werte hängen von diesem Wert ab, der in der Breite des Bildes enthalten ist, sodass er die Situation bestimmt. Es gibt einen Moment, der vielleicht keinen Sinn ergibt: Wir haben einen Wert, der in ein Zeichen umgewandelt wird, aber das Zeichen selbst erscheint nicht auf dem Bildschirm. Was geschieht in diesem Moment? Hier erstellen wir die Pfeile, die anzeigen, ob der Preis auf der Grundlage der täglichen Veränderung nach oben oder unten geht. Das mag ein wenig verwirrend erscheinen, aber es funktioniert gut. Aber bitte beachten Sie, dass wir genau die Schriftart Wingdings 3 verwenden. Wenn Sie eine andere Schriftart verwenden möchten, müssen Sie diese Werte anpassen, um eine korrekte Anzeige zu erhalten.

Es gibt einen weiteren interessanten Punkt. Wir senden String- und Double-Werte an dieselbe Funktion. Wir würden zwei verschiedene Funktionen erwarten, aber der Objektcode enthält nur eine. Hier ist diese Funktion überladen. Die Überladung wird jedoch durch den Compiler und den Linker erzeugt. Wir mussten also Tests innerhalb der Funktion durchführen, aber wenn Sie genauer hinschauen, können Sie sehen, dass wir ein Symbol haben, das häufig zur Bezeichnung von Prozentwerten verwendet wird (%). Gehen Sie nun in das Bearbeitungsobjekt und sehen Sie sich an, wo das Prozentzeichen hinzugefügt wird und wie es gemacht wird.

Zum Abschluss dieser Funktion müssen wir den Preis so anpassen, dass seine Farbe auf der Panel korrekt angezeigt wird.

Alle diese Änderungen werden direkt in der Funktion vorgenommen. Wir hatten keine Änderungen in Bezug auf die Art und Weise, wie das System tatsächlich aufgerufen werden sollte. Alle notwendigen Änderungen werden in der Klasse C_Widget vorgenommen. Aber wir haben auch einige kleine Änderungen im Code des Indikators vorgenommen, die wir uns ansehen wollen.

#property indicator_separate_window
#property indicator_plots 0
#property indicator_height 32
//+------------------------------------------------------------------+
#include <Widget\Rolling Price\C_Widget.mqh>
//+------------------------------------------------------------------+
input string user00 = "Config.cfg";  //Settings file
input int    user01 = -1;            //Shift
input int    user02 = 10;            //Pause in milliseconds
input
 color  user03 = clrWhiteSmoke; //Asset color
input color  user04 = clrBlack;      //Price color
//+------------------------------------------------------------------+
C_Widget Widget;
//+------------------------------------------------------------------+
int OnInit()
{
        if (!Widget.Initilize(user00, "Widget Price", user03, user04))
                return INIT_FAILED;
        EventSetMillisecondTimer(user02);
        
        return INIT_SUCCEEDED;
}

Der einzige erwähnenswerte Moment ist der, in dem wir den Wert der Indikatorhöhe festlegen. Alles andere ist dasselbe wie in der Basisversion.

Wir könnten den Indikator hier beenden. Aber was halten Sie von der Idee, einige zusätzliche Funktionen zu implementieren, die während der Handelsperiode nützlich sind? Darüber werden wir im nächsten Artikel sprechen.


Zusätzliche Funktionen

Wie wäre es, wenn Sie die Vermögenswerte, mit denen Sie handeln möchten, dem Panel hinzufügen? Und nicht nur das. Gehen wir noch einen Schritt weiter.

Wie wäre es damit: Wenn Sie auf den im Panel angezeigten Vermögenswert klicken, wird sofort das entsprechende Chart des Finanzinstruments geöffnet, sodass Sie die Situation verfolgen und einen Handel eingehen können, wenn Sie sehen, dass sich ein Trend anbahnt. Und das alles können Sie ganz einfach tun, ohne etwas eintippen zu müssen!

Eine großartige Idee, nicht wahr? Sie denken sicher, dass das etwas sehr, sehr schwer zu realisieren ist, dass Sie ein professioneller Programmierer mit Erfahrung und Kenntnissen in Kernphysik oder Alien-Technologie sein müssen. Aber nein. Dies ist mit MQL5 und der Plattform MetaTrader 5 problemlos möglich.

Dazu müssen wir einen kleinen Teil des Codes in das Nachrichtenbearbeitungssystem einfügen. Dies geschieht im Folgenden:

void DispatchMessage(const int id, const long &lparam, const double &dparam, const string &sparam)
        {
                static int tx = 0;
                string szRet[];
                                                        
                switch (id)
                {

// ... Internal code...

                        case CHARTEVENT_OBJECT_CLICK:
                                if (StringSplit(sparam, '#', szRet) == 2)
                                {
                                        SymbolSelect(szRet[1], true);
                                        szRet[0] = ChartSymbol(Terminal.Get_ID());
                                        if (ChartSetSymbolPeriod(Terminal.Get_ID(), szRet[1], PERIOD_CURRENT)) SymbolSelect(szRet[0], false);
                                        else SymbolSelect(szRet[1], false);
                                }
                                break;
                }
        }

Wenn wir Objekte erstellen, erstellen wir sie nicht einfach auf irgendeine Art und Weise - wir verwenden ein ganz bestimmtes Format, und dank dieses Formats können wir analysieren, welches Objekt angeklickt wurde und mit welchem Finanzinstrument es verknüpft war. Hier finden wir also heraus, mit welchem Finanzinstrument das Objekt verbunden ist. Dazu verwenden wir eine sehr interessante Funktion. 

StringSplit kann Daten auftrennen, je nachdem, wie sie formatiert wurden. So erhalten wir einen Hinweis darauf, mit welchem Finanzinstrument das angeklickte Objekt verbunden ist. Auf dieser Grundlage weisen wir die MetaTrader 5-Plattform an, den entsprechenden Chart des Finanzinstruments zu öffnen.

Damit dies funktioniert, muss der Vermögenswert jedoch im Fenster Market Watch vorhanden sein. Wir führen also diese Zeile aus, damit das Symbol in Market Watch erscheint. Bevor wir dann das aktuelle Finanzinstrument aus dem Fenster löschen, erfassen wir seine ID und versuchen, das angeklickte Finanzinstrument im Fenster erscheinen zu lassen. Wenn wir erfolgreich sind, versuchen wir, das Finanzinstrument, das auf dem Chart war, aus Market Watch zu entfernen. Wenn jedoch der Versuch, das Finanzinstrument zu ändern, fehlschlägt, wird das Finanzinstrument, das wir zu öffnen versuchen, aus der Marktüberwachung entfernt.

Bitte beachten Sie einige Punkte im Zusammenhang mit dieser Änderung der Finanzinstrumente im Panel. Zunächst wird das gewünschte Finanzinstrument mit demselben Zeitrahmen eröffnet, den die vorherige Anlage hatte. Sie können den Zeitrahmen zu einem späteren Zeitpunkt ändern, aber anfangs wird derselbe Zeitrahmen verwendet. Ein weiterer, ebenso wichtiger Punkt ist, dass wir die Finanzinstrumente so auswählen müssen, dass eine angemessene Anzahl von Finanzinstrumenten im Panel vorhanden ist, da es viel Zeit in Anspruch nimmt, sie wieder im Panel anzuzeigen. Bei jeder Änderung des Zeitrahmens oder des Finanzinstruments beginnt das Panel immer mit dem ersten Finanzinstrument in der Liste.

Das folgende Video zeigt, wie das System in der Praxis funktioniert.



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

Beigefügte Dateien |
Erstellen eines EA, der automatisch funktioniert (Teil 01): Konzepte und Strukturen Erstellen eines EA, der automatisch funktioniert (Teil 01): Konzepte und Strukturen
Heute werden wir sehen, wie man einen Expert Advisor erstellt, der einfach und sicher im automatischen Modus arbeitet.
Erstellen eines Ticker-Panels: Basisversion Erstellen eines Ticker-Panels: Basisversion
Hier zeige ich Ihnen, wie Sie Bildschirme mit Preistickern erstellen, die normalerweise zur Anzeige von Börsenkursen verwendet werden. Ich werde es nur mit MQL5 machen, ohne eine komplexe externe Programmierung zu verwenden.
DoEasy. Steuerung (Teil 31): Scrollen des Inhalts des ScrollBar-Steuerelements DoEasy. Steuerung (Teil 31): Scrollen des Inhalts des ScrollBar-Steuerelements
In diesem Artikel werde ich die Funktionsweise des Scrollens des Inhalts des Containers mithilfe der Schaltflächen der horizontalen Bildlaufleiste implementieren.
Lernen Sie, wie man ein Handelssystem mit Gator Oscillator entwickelt Lernen Sie, wie man ein Handelssystem mit Gator Oscillator entwickelt
Ein neuer Artikel in unserer Serie über die Entwicklung eines Handelssystems auf der Grundlage beliebter technischer Indikatoren wird sich mit dem technischen Indikator Gator Oscillator und der Erstellung eines Handelssystems durch einfache Strategien befassen.