English Русский 中文 Español 日本語 Português 한국어 Français Italiano Türkçe
preview
Mehrere Indikatoren in einem Chart (Teil 05): Umwandlung des MetaTrader 5 in ein RAD-System (I)

Mehrere Indikatoren in einem Chart (Teil 05): Umwandlung des MetaTrader 5 in ein RAD-System (I)

MetaTrader 5Handel | 16 Mai 2022, 09:02
464 0
Daniel Jose
Daniel Jose

Einführung

Es gibt viele Menschen, die keine Ahnung vom Programmieren haben, aber sehr kreativ sind und tolle Ideen haben. Der Mangel an Programmierkenntnissen hindert sie jedoch daran, diese Ideen umzusetzen. Heute werden wir unsere eigene Chart Trade-Schnittstelle erstellen, um Market Orders zu senden oder Parameter für Pending Orders festzulegen. Wir werden dies ohne Programmierung tun, indem wir einfach die Funktionen verwenden, die im Expert Advisor bereits vorhanden sind. Wir sind neugierig, also sehen wir uns an, wie es auf unseren Monitoren aussehen wird:


Sie denken vielleicht: "Aber wie macht man das? Ich habe keine Ahnung vom Programmieren, oder das, was ich weiß, reicht nicht aus, um es zu tun." Das Handelspanel, das Sie in der obigen Abbildung sehen, wurde in der MetaTrader 5 Plattform selbst erstellt und wurde, wie in der Abbildung unten dargestellt, gestaltet:


Jetzt, wo wir wissen, worum es in diesem Artikel geht, sollten wir voller Enthusiasmus und Ideen für die Erstellung unseres eigenen Charts sein. Damit das Ganze funktioniert, müssen wir jedoch noch einige Schritte ausführen. Sobald der Hilfscode eingerichtet ist, ist unsere Kreativität die einzige Grenze für die Gestaltung unserer eigenen Chart Trade IDE. Dieser Artikel ist eine Fortsetzung der vorangegangenen. Um ein vollständiges und umfassendes Verständnis zu erlangen, empfehle ich die Lektüre der vorangegangenen Artikel dieser Serie.

Machen wir uns also an die Arbeit.


Planung

Zu Beginn sollten wir die Eigenschaften des Charts bearbeiten, den wir als IDE verwenden werden. Dies geschieht, um mögliche Nebenwirkungen zu reduzieren. Wenn wir das Chart sauber lassen, ist es einfacher, die Chart Trade IDE zu erstellen und zu gestalten. Öffnen wir also die Chart-Eigenschaften und stellen Sie die Eigenschaften wie in der Abbildung unten gezeigt ein.

     

So wird der Bildschirm absolut sauber und frei von allem, was die Entwicklung unserer IDE stören könnte. Beachten Sie nun die folgende Erklärung. Unsere IDE wird als Einstellungsdatei, d.h. als TEMPLATE, gespeichert, so dass wir alle von MetaTrader 5 zur Verfügung gestellten Objekte verwenden können, aber aus praktischen Gründen werden wir nur einige von ihnen verwenden. Alle verfügbaren Objekte finden Sie unter Objekttypen in MetaTrader 5.

Objekt Typ der Koordinaten, die für die Positionierung verwendet werden Interessant für IDE Koordinatentypen zur Positionsbestimmung Wichtig für die IDE 
Text Zeit und Preis  NEIN
Label X- und Y-Ort  JA
Taste  X- und Y-Ort  JA
Diagramm  X- und Y-Ort  JA
Bitmap  Zeit und Preis  NEIN
Bitmap Label  X- und Y-Ort  JA
Anpassen  X- und Y-Ort  JA
Ereignis  Nur die Zeit wird verwendet  NEIN
Rechteck-Label X- und Y-Ort  JA

Wir werden ein System verwenden, das in jedem Bereich des Bildschirms platziert werden kann. Deshalb wäre es nicht sinnvoll, ein Objekt zu verwenden, das nicht das X- und Y-Koordinatensystem für die Positionierung verwendet, da solche Objekte die IDE völlig anders aussehen lassen können. Daher werden wir das System auf sechs Objekte beschränken, was mehr als genug ist, um eine Schnittstelle zu schaffen.

Die Idee ist, die Objekte in einer logischen Reihenfolge anzuordnen, ähnlich wie man etwas auf dem Bildschirm zeichnet. Wir beginnen zunächst mit der Erstellung des Hintergrunds und legen dann die Objekte übereinander, wobei wir die Objekte im Laufe der Entwicklung der Schnittstelle platzieren und anpassen. Das geht folgendermaßen:

    

    

Es ist alles sehr einfach, man braucht nur ein wenig Übung, um diese Art der Gestaltung und Erstellung einer eigenen IDE zu beherrschen. Die Idee, die hier verfolgt wird, ist derjenigen sehr ähnlich, die in RAD-Programmen verwendet wird, die dazu dienen, Programmierschnittstellen zu erstellen, wenn die Entwicklung der Nutzeroberfläche durch Code sehr komplex sein kann. Es ist nicht so, dass wir eine Schnittstelle nicht direkt durch Code erstellen können. Aber die Verwendung dieser Methode macht weitere Änderungen viel schneller und einfacher, was ideal für diejenigen ist, die eine Schnittstelle mit ihrem eigenen Stil wollen.

Wenn wir fertig sind, haben wir vielleicht eine Schnittstelle wie die unten abgebildete oder sogar eine noch coolere. Aber ich habe versucht, so viele Objekte wie möglich zu verwenden, damit Sie sie ausprobieren können. Sie können Ihre eigene bevorzugte Schnittstelle erstellen.

Dies ist der erste Schritt bei der Erstellung unserer IDE. Jetzt müssen wir Code erstellen, der diese Schnittstelle tatsächlich unterstützt und sie funktionsfähig macht. Obwohl die einfache Tatsache, dass Sie Ihre eigene Benutzeroberfläche erstellen können, auch die Quelle der Motivation sein sollte, und diese Motivation wird im Code verkörpert werden.

Der nächste Schritt besteht darin, diese Schnittstelle als Einstellungsdatei zu speichern. Jetzt können wir sie speichern und den Code der vorherigen Version verwenden, um sie als Zeiger anzuzeigen. Das bedeutet, dass wir keine wesentlichen Änderungen am Quellcode vornehmen müssen. Wenn wir jedoch die Möglichkeit testen wollten, Ereignisse zu empfangen oder an unsere IDE zu senden, würden wir feststellen, dass dies nicht möglich ist. Aber wenn die Schnittstelle mit Objekten aus MetaTrader 5 erstellt wurde, warum ist es dann nicht möglich, Ereignisse von diesen Objekten zu senden und zu empfangen? Die Antwort auf diese Frage ist einfacher zu zeigen als zu erklären. Wir können es überprüfen, indem wir den folgenden Code zur Originalversion des EA hinzufügen.

void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam)
{
        switch (id)
        {
                case CHARTEVENT_OBJECT_CLICK:
                        Print(sparam);
                        break;
// .... The rest of the code...
        }
}

Dieser Code meldet den Namen des Objekts, das den Klick empfängt und das Ereignis erzeugt. In diesem Fall ist das Ereignis CHARTEVENT_OBJECT_CLICK. Die gedruckte Nachricht ist jedoch der Name des vom EA erstellten Objekts, nicht der Name der Objekte in der IDE. Dies scheint ein großes Problem zu sein, das die Verwendung unserer IDE unmöglich macht, aber es gibt eine sehr einfache Lösung: Wir lesen die Einstellungsdatei und erstellen dann die Objekte wie in dieser Datei angegeben sind. Dadurch wird unsere IDE direkt auf dem Chart erstellt. Durch die Analyse der Einstellungsdatei (TPL) können wir also die Daten finden, die wir verwenden müssen.

Schlüssel Beschreibung
<chart> Beginn der Einstellungsdatei
</chart> Ende der Einstellungsdatei
<window> Beginn der Struktur der auf dem Chart dargestellten Elemente.
</window> Ende der Struktur der auf dem Chart dargestellten Elemente.
<indicator> Beginn der Struktur, die die Daten einiger Indikatoren bereitstellt.
</indicator> Ende der Struktur, die die Daten einiger Indikatoren bereitstellt.
<object> Beginn der Struktur, die die Daten einiger Objekte bereitstellt.
</object> Ende der Struktur, die Objektdaten bereitstellt.

Diese Struktur sieht in der TPL-Datei wie folgt aus.

<chart>

.... DATEN

<window>

... DATEN

<indicator>

... DATEN

</indicator>

<object>

... DATEN

</object>

</window>
</chart>

Der Teil, der uns interessiert, liegt zwischen <object> und </object>. Es kann mehrere solcher Strukturen geben, von denen jede ein eindeutiges Objekt angibt. Zunächst müssen wir also den Speicherort der Datei ändern — wir sollten sie an einem Ort hinzufügen, von dem aus sie gelesen werden kann. Dies ist das Verzeichnis FILES. Sie können den Ort ändern, aber in jedem Fall muss sich die Datei innerhalb des FILE-Baums befinden.

Ein wichtiges Detail: Obwohl das System eine Änderung erhalten hat, die es erlaubt, das Chart zu löschen, wenn Sie die IDE-Konfigurationsdatei verwenden, sollten Sie idealerweise auch eine saubere Datei mit demselben Namen im Verzeichnis Profiles\Templates haben. Dies minimiert etwaige Überbleibsel, die in der Standardvorlage vorhanden sein können, wie wir in früheren Artikeln gesehen haben. Die wichtigsten Änderungen werden im Folgenden hervorgehoben:

#include <Auxiliar\Chart IDE\C_Chart_IDE.mqh>
//+------------------------------------------------------------------+
class C_TemplateChart : public C_Chart_IDE
{

 .... Other parts from code ....

//+------------------------------------------------------------------+
void AddTemplate(const eTypeChart type, const string szTemplate, int scale, int iSize)
{
        if (m_Counter >= def_MaxTemplates) return;
        if (type == SYMBOL) SymbolSelect(szTemplate, true);
        SetBase(szTemplate, (type == INDICATOR ? _Symbol : szTemplate), scale, iSize);
        if (!ChartApplyTemplate(m_handle, szTemplate + ".tpl")) if (type == SYMBOL) ChartApplyTemplate(m_handle, "Default.tpl");
        if (szTemplate == "IDE") C_Chart_IDE::Create(m_IdSubWin);
        ChartRedraw(m_handle);
}
//+------------------------------------------------------------------+
void Resize(void)
{
#define macro_SetInteger(A, B) ObjectSetInteger(Terminal.Get_ID(), m_Info[c0].szObjName, A, B)
        int x0 = 0, x1, y = (int)(ChartGetInteger(Terminal.Get_ID(), CHART_HEIGHT_IN_PIXELS, m_IdSubWin));
        x1 = (int)((ChartGetInteger(Terminal.Get_ID(), CHART_WIDTH_IN_PIXELS, m_IdSubWin) - m_Aggregate) / (m_Counter > 0 ? (m_CPre == m_Counter ? m_Counter : (m_Counter - m_CPre)) : 1));
        for (char c0 = 0; c0 < m_Counter; x0 += (m_Info[c0].width > 0 ? m_Info[c0].width : x1), c0++)
        {
                macro_SetInteger(OBJPROP_XDISTANCE, x0);
                macro_SetInteger(OBJPROP_XSIZE, (m_Info[c0].width > 0 ? m_Info[c0].width : x1));
                macro_SetInteger(OBJPROP_YSIZE, y);
                if (m_Info[c0].szTemplate == "IDE") C_Chart_IDE::Resize(x0);
        }
        ChartRedraw();
#undef macro_SetInteger
}
//+------------------------------------------------------------------+

... The rest of the code

}

Beachten Sie, dass wir die IDE-Schnittstelle als neue Klasse hinzufügen, die von unserer ursprünglichen Klasse geerbt wird. Das bedeutet, dass die Funktionsweise der Originalklasse erweitert wird und keine Seiteneffekte im Originalcode verursacht.

Bis jetzt war das der einfache Teil. Jetzt müssen wir etwas Komplizierteres tun, das unsere IDE unterstützen wird. Zunächst erstellen wir ein Nachrichtenprotokoll, das das System verwenden wird. Dieses Protokoll wird es dem System ermöglichen, wie unten gezeigt zu arbeiten:


Beachten Sie, dass wir die Systemdaten ändern können, was derzeit nicht möglich ist, aber durch Hinzufügen eines Nachrichtenprotokolls wird es möglich sein, unsere IDE funktionsfähig zu machen. Lassen Sie uns also ein paar Dinge definieren:

Nachricht Zweck
MSG_BUY_MARKET Sendet eine Markt-KAUF-Order
MSG_SELL_MARKET Sendet eine Markt-VERKAUFS-Order
MSG_LEVERAGE_VALUE Leverage-Daten
MSG_TAKE_VALUE Daten des Take-Profit für den Handel
MSG_STOP_VALUE Stop-Loss-Daten für den Handel
MSG_RESULT Daten zum aktuellen Ergebnis der offenen Position
MSG_DAY_TRADE Informiert darüber, ob der Handel am Ende des Tages geschlossen wird oder nicht

Dieses Protokoll ist ein sehr wichtiger Schritt. Nachdem wir es definiert haben, nehmen wir Änderungen in der Einstellungsdatei vor. Wenn Sie die Liste der Objekte öffnen, müssen wir sie so ändern, dass sie wie folgt aussieht:

Die Schnittstelle, die ich zeige, wird eine Liste von Objekten wie im Bild haben. Bitte beachten Sie die folgende Tatsache. Der NAME der Objekte entspricht jeder der Nachrichten, die wir verwenden werden. Die Namen anderer Objekte spielen keine Rolle, da sie verwendet werden, um bei der Modellierung der IDE zu helfen, aber die Objekte mit den Namen der Nachrichten empfangen und senden Nachrichten. Wenn Sie mehr Nachrichten oder einen anderen Nachrichtentyp verwenden möchten, nehmen Sie einfach die erforderlichen Änderungen am Klassencode vor, und MetaTrader 5 selbst stellt die Mittel zum Austausch von Nachrichten zwischen der IDE und dem EA-Code bereit.

Aber wir müssen noch die TPL-Datei studieren, um zu lernen, wie wir unsere Objektklasse erstellen. Lassen Sie uns nun herausfinden, wie Objekte in der TPL-Datei deklariert werden. Es ist wahr, dass wir weniger Zugriff auf die Objekteigenschaften in der TPL-Datei haben als durch Programmierung, da die Terminalschnittstelle selbst weniger Zugriff auf Objekteigenschaften gewährt. Aber selbst der Zugriff, den wir haben, wird ausreichen, damit unsere IDE funktioniert.

In der TPL-Datei gibt es also die Struktur, die wir brauchen: von <object> bis </object>. Basierend auf den Daten innerhalb der Struktur mag es unklar erscheinen, wie man herausfinden kann, um welche Art von Objekt es sich handelt. Aber wenn Sie genauer hinschauen, sehen Sie, dass der Objekttyp durch die Variable type bestimmt wird. Sie nimmt für jedes der Objekte unterschiedliche Werte an. Die Tabelle zeigt die Objekte, die wir verwenden wollen:

Der Wert der Variablen TYPE Referenziertes Objekt
102 OBJ_LABEL
103 OBJ_BUTTON
106 OBJ_BITMAP_LABEL
107  OBJ_EDIT
110  OBJ_RECTANGLE_LABEL

Unsere Klasse nimmt bereits Gestalt an. Hier ist der erste Funktionscode:

bool Create(int nSub)
{
        m_CountObject = 0;
        if ((m_fp = FileOpen("Chart Trade\\IDE.tpl", FILE_BIN | FILE_READ)) == INVALID_HANDLE) return false;
        FileReadInteger(m_fp, SHORT_VALUE);
                                
        for (m_CountObject = eRESULT; m_CountObject <= eEDIT_STOP; m_CountObject++) m_ArrObject[m_CountObject].szName = "";
        m_SubWindow = nSub;
        m_szLine = "";
        while (m_szLine != "</chart>")
        {
                if (!FileReadLine()) return false;
                if (m_szLine == "<object>")
                {
                        if (!FileReadLine()) return false;
                        if (m_szLine == "type")
                        {
                                if (m_szValue == "102") if (!LoopCreating(OBJ_LABEL)) return false;
                                if (m_szValue == "103") if (!LoopCreating(OBJ_BUTTON)) return false;
                                if (m_szValue == "106") if (!LoopCreating(OBJ_BITMAP_LABEL)) return false;
                                if (m_szValue == "107") if (!LoopCreating(OBJ_EDIT)) return false;
                                if (m_szValue == "110") if (!LoopCreating(OBJ_RECTANGLE_LABEL)) return false;
                        }
                }
        }
        FileClose(m_fp);
        return true;
}

Bitte beachten Sie, dass Sie die Datei zunächst im Lesemodus und als Binärdatei öffnen müssen. Dies geschieht, um nichts zu übersehen. Wenn Sie den HEXA-Editor verwenden, sieht die TPL-Datei wie folgt aus. Beachten Sie, dass sie mit einem sehr interessanten Wert beginnt.

Klingt verwirrend? Ist es eigentlich nicht. Die Datei verwendet die Kodierung UTF-16. Wir wissen, dass die Daten zeilenweise organisiert sind, also erstellen wir eine Funktion, die die gesamte Zeile auf einmal liest. Zu diesem Zweck schreiben wir den folgenden Code:

bool FileReadLine(void)
{
        int utf_16 = 0;
        bool b0 = false;
        m_szLine = m_szValue = "";
        for (int c0 = 0; c0 < 500; c0++)
        {
                utf_16 = FileReadInteger(m_fp, SHORT_VALUE);
                if (utf_16 == 0x000D) { FileReadInteger(m_fp, SHORT_VALUE); return true; } else
                if (utf_16 == 0x003D) b0 = true; else
                if (b0) m_szValue = StringFormat("%s%c", m_szValue, (char)utf_16); else m_szLine = StringFormat("%s%c", m_szLine, (char)utf_16);
                if (FileIsEnding(m_fp)) break;
        }
        return (utf_16 == 0x003E);
}

Beim Lesen wird versucht, so effizient wie möglich zu sein. Wenn wir also auf ein Gleichheitszeichen ( = ) treffen, trennen wir bereits während des Lesens, um dies später nicht zu tun. Die Schleife begrenzt die Zeichenfolge auf maximal 500 Zeichen, aber dieser Wert ist willkürlich und kann bei Bedarf geändert werden. Bei jeder neu gefundenen Zeichenkette liefert die Funktion den Inhalt der Zeichenkette zurück, so dass wir mit der entsprechenden Analyse fortfahren können.

Zur Unterstützung des Nachrichtenprotokolls werden bestimmte Variablen benötigt. Sie sind im folgenden Code dargestellt:

class C_Chart_IDE
{
        protected:
                enum eObjectsIDE {eRESULT, eBTN_BUY, eBTN_SELL, eCHECK_DAYTRADE, eBTN_CANCEL, eEDIT_LEVERAGE, eEDIT_TAKE, eEDIT_STOP};
//+------------------------------------------------------------------+
#define def_HeaderMSG "IDE_"
#define def_MaxObject eEDIT_STOP + 32
//+------------------------------------------------------------------+
        private :
                int             m_fp,
                                m_SubWindow,
                                m_CountObject;
                string          m_szLine,
                                m_szValue;
                bool            m_IsDayTrade;
                struct st0
                        {
                                string  szName;
                                int     iPosX;
                        }m_ArrObject[def_MaxObject];

// ... Der Rest des Codes der Klasse....

Die Definition def_MaxObject gibt die maximale Anzahl der Objekte an, die wir behalten können. Diese Zahl ergibt sich aus der Anzahl der Nachrichten plus einer zusätzlichen Anzahl von Objekten, die wir verwenden werden. In unserem Fall haben wir die maximale Anzahl von 40 Objekten, aber sie kann bei Bedarf geändert werden. Die ersten 8 Objekte werden verwendet, um Nachrichten zwischen der IDE und MetaTrader 5 zu senden. Der Alias dieser Nachrichten kann in der Enumeration eObjectsIDE gesehen werden. Es ist wichtig, dies im Hinterkopf zu behalten, falls Sie das System erweitern oder für etwas anderes anpassen wollen.

Dies ist nur der erste Teil des Unterstützungssystems. Es gibt noch einen weiteren Punkt, auf den man achten sollte: die Konstante, die sich mit dem Nachrichtensystem befasst. Die Art und Weise, wie MQL5 mit Konstanten umgeht, kann für diejenigen, die in C / C++ programmieren, etwas verwirrend sein. In C/C++ wird eine Konstante in der Variablendeklaration selbst deklariert. In MQL5 kann die Art und Weise, wie sie erstellt wird, den Code ein wenig komplizierter machen. Sie können jedoch damit leben, da Konstanten recht selten verwendet werden. Im Folgenden ist fett dargestellt, wie Sie dies tun können.

        public  :
                static const string szMsgIDE[];

// ... The rest of the class code....

};
//+------------------------------------------------------------------+
static const string C_Chart_IDE::szMsgIDE[] = {
                                                "MSG_RESULT",
                                                "MSG_BUY_MARKET",
                                                "MSG_SELL_MARKET",
                                                "MSG_DAY_TRADE",
                                                "MSG_CLOSE_POSITION",
                                                "MSG_LEVERAGE_VALUE",
                                                "MSG_TAKE_VALUE",
                                                "MSG_STOP_VALUE"
                                             };
//+------------------------------------------------------------------+

Die definierten Konstanten sind genau die gleichen Werte, die in den Objektnamen der Schnittstelle verwendet werden. Das System wurde so konzipiert, dass es Groß- und Kleinschreibung nicht berücksichtigt. Sie können dieses Verhalten ändern, wenn Sie wollen, aber ich empfehle nicht, dies zu tun.

Nachdem Sie alle diese Schritte durchgeführt haben, ist es an der Zeit, mit dem nächsten Schritt fortzufahren. Gehen wir also zurück zur TPL-Datei. Sehen wir uns das folgende Dateifragment an:


Nachdem wir den Typ des zu verwendenden Objekts definiert haben, haben wir eine Reihe von Daten, die die Eigenschaften des Objekts angeben, wie Name, Position, Farbe, Schriftart usw. Diese Eigenschaften sollten an interne Objekte weitergegeben werden. Da es sich um eine sich wiederholende Sache handelt, können wir dafür eine allgemeine Funktion erstellen. Sie wird wie folgt aussehen:

bool LoopCreating(ENUM_OBJECT type)
{
#define macro_SetInteger(A, B) ObjectSetInteger(Terminal.Get_ID(), m_ArrObject[c0].szName, A, B)
#define macro_SetString(A, B) ObjectSetString(Terminal.Get_ID(), m_ArrObject[c0].szName, A, B)
        int c0;
        bool b0;
        string sz0 = m_szValue;
        while (m_szLine != "</object>") if (!FileReadLine()) return false; else
        {
                if (m_szLine == "name")
                {
                        b0 = false;
                        StringToUpper(m_szValue);
                        for(c0 = eRESULT; (c0 <= eEDIT_STOP) && (!(b0 = (m_szValue == szMsgIDE[c0]))); c0++);
                        c0 = (b0 ? c0 : m_CountObject);
                        m_ArrObject[c0].szName = StringFormat("%s%04s>%s", def_HeaderMSG, sz0, m_szValue);
                        ObjectDelete(Terminal.Get_ID(), m_ArrObject[c0].szName);
                        ObjectCreate(Terminal.Get_ID(), m_ArrObject[c0].szName, type, m_SubWindow, 0, 0);
                }
                if (m_szLine == "pos_x"                 ) m_ArrObject[c0].iPosX = (int) StringToInteger(m_szValue);
                if (m_szLine == "pos_y"                 ) macro_SetInteger(OBJPROP_YDISTANCE    , StringToInteger(m_szValue));
                if (m_szLine == "size_x"                ) macro_SetInteger(OBJPROP_XSIZE        , StringToInteger(m_szValue));
                if (m_szLine == "size_y"                ) macro_SetInteger(OBJPROP_YSIZE        , StringToInteger(m_szValue));
                if (m_szLine == "offset_x"              ) macro_SetInteger(OBJPROP_XOFFSET      , StringToInteger(m_szValue));
                if (m_szLine == "offset_y"              ) macro_SetInteger(OBJPROP_YOFFSET      , StringToInteger(m_szValue));
                if (m_szLine == "bgcolor"               ) macro_SetInteger(OBJPROP_BGCOLOR      , StringToInteger(m_szValue));
                if (m_szLine == "color"                 ) macro_SetInteger(OBJPROP_COLOR        , StringToInteger(m_szValue));
                if (m_szLine == "bmpfile_on"            ) ObjectSetString(Terminal.Get_ID()     , m_ArrObject[c0].szName, OBJPROP_BMPFILE, 0, m_szValue);
                if (m_szLine == "bmpfile_off"           ) ObjectSetString(Terminal.Get_ID()     , m_ArrObject[c0].szName, OBJPROP_BMPFILE, 1, m_szValue);
                if (m_szLine == "fontsz"                ) macro_SetInteger(OBJPROP_FONTSIZE     , StringToInteger(m_szValue));
                if (m_szLine == "fontnm"                ) macro_SetString(OBJPROP_FONT          , m_szValue);
                if (m_szLine == "descr"                 ) macro_SetString(OBJPROP_TEXT          , m_szValue);
                if (m_szLine == "readonly"              ) macro_SetInteger(OBJPROP_READONLY     , StringToInteger(m_szValue) == 1);
                if (m_szLine == "state"                 ) macro_SetInteger(OBJPROP_STATE        , StringToInteger(m_szValue) == 1);
                if (m_szLine == "border_type"           ) macro_SetInteger(OBJPROP_BORDER_TYPE  , StringToInteger(m_szValue));
        }
        m_CountObject += (b0 ? 0 : (m_CountObject < def_MaxObject ? 1 : 0));
        return true;
                        
#undef macro_SetString
#undef macro_SetInteger
}

Jedes Objekt erhält einen Namen und wird an der entsprechenden Stelle gespeichert, aber die hervorgehobene Zeile zeigt etwas anderes. Wenn wir eine IDE erstellen, muss sie an der oberen linken Ecke des Charts beginnen, aber diese X-Position ist nicht unbedingt die obere linke Ecke des Unterfensters. Diese Position muss der oberen linken Ecke des OBJ_CHART-Objekts entsprechen, an das die IDE gebunden wird. Dieses Objekt wird beim Laden der IDE-Vorlage angegeben und kann sich daher an einer beliebigen Stelle innerhalb des Unterfensters befinden. Wenn dies nicht korrigiert wird, wird die IDE nicht an der richtigen Stelle angezeigt. Speichern Sie daher den X-Wert und verwenden Sie ihn später, um das Objekt an der richtigen Stelle anzuzeigen. Die Funktion, die die IDE korrekt anzeigt, ist unten dargestellt.

Die grundlegenden Informationen, die in den Objekten verwendet werden, sind bereits definiert. Wenn Sie jedoch weitere Informationen hinzufügen möchten, fügen Sie diese einfach dem Befehlssatz hinzu und ändern Sie die Eigenschaft mit dem entsprechenden Wert.

void Resize(int x)
{
        for (int c0 = 0; c0 < m_CountObject; c0++)
                ObjectSetInteger(Terminal.Get_ID(), m_ArrObject[c0].szName, OBJPROP_XDISTANCE, x + m_ArrObject[c0].iPosX);
};

Bevor wir uns ansehen, wie Nachrichten verarbeitet werden, wollen wir zwei andere, ebenso wichtige Funktionen analysieren. Das System kann Werte vom EA empfangen, die während der Initialisierung eingegangen sind. Diese Werte müssen korrekt dargestellt und angepasst werden, damit bei der Verwendung von Chart Trade die Orders direkt darin konfiguriert werden können, um entweder eine Market Order oder eine Pending Order zu senden, ohne den EA aufrufen zu müssen. Beide Funktionen sind unten dargestellt:

void UpdateInfos(bool bSwap = false)
{
        int nContract, FinanceTake, FinanceStop;

        nContract       = (int) StringToInteger(ObjectGetString(Terminal.Get_ID(), m_ArrObject[eEDIT_LEVERAGE].szName, OBJPROP_TEXT));
        FinanceTake = (int) StringToInteger(ObjectGetString(Terminal.Get_ID(), m_ArrObject[eEDIT_TAKE].szName, OBJPROP_TEXT));
        FinanceStop = (int) StringToInteger(ObjectGetString(Terminal.Get_ID(), m_ArrObject[eEDIT_STOP].szName, OBJPROP_TEXT));
        m_IsDayTrade = (bSwap ? (m_IsDayTrade ? false : true) : m_IsDayTrade);
        ObjectSetInteger(Terminal.Get_ID(), m_ArrObject[eCHECK_DAYTRADE].szName, OBJPROP_STATE, m_IsDayTrade);
        NanoEA.Initilize(nContract, FinanceTake, FinanceStop, clrNONE, clrNONE, clrNONE, m_IsDayTrade);
}
//+------------------------------------------------------------------+
void InitilizeChartTrade(int nContracts, int FinanceTake, int FinanceStop, color cp, color ct, color cs, bool b1)
{
        NanoEA.Initilize(nContracts, FinanceTake, FinanceStop, cp, ct, cs, b1);
        if (m_CountObject < eEDIT_STOP) return;
        ObjectSetString(Terminal.Get_ID(), m_ArrObject[eEDIT_LEVERAGE].szName, OBJPROP_TEXT, IntegerToString(nContracts));
        ObjectSetString(Terminal.Get_ID(), m_ArrObject[eEDIT_TAKE].szName, OBJPROP_TEXT, IntegerToString(FinanceTake));
        ObjectSetString(Terminal.Get_ID(), m_ArrObject[eEDIT_STOP].szName, OBJPROP_TEXT, IntegerToString(FinanceStop));
        ObjectSetInteger(Terminal.Get_ID(), m_ArrObject[eCHECK_DAYTRADE].szName, OBJPROP_STATE, m_IsDayTrade = b1);
}

Bitte beachten Sie, dass die IDE mit dem Auftragssystem verknüpft ist, so dass Änderungen am System auch im Auftragssystem berücksichtigt werden. Auf diese Weise müssen wir keine Daten im EA ändern, wie wir es vorher getan haben. Jetzt können wir dies direkt in der IDE oder in unserem Chart Trade tun - dies geschieht mit den beiden oben genannten Funktionen, die mit dem Nachrichtensystem verbunden sind.

void DispatchMessage(int iMsg, string szArg, double dValue = 0.0)
{
        if (m_CountObject < eEDIT_STOP) return;
        switch (iMsg)
        {
                case CHARTEVENT_CHART_CHANGE:
                        if (szArg == szMsgIDE[eRESULT])
                        {
                                ObjectSetInteger(Terminal.Get_ID(), m_ArrObject[eRESULT].szName, OBJPROP_BGCOLOR, (dValue < 0 ? clrLightCoral : clrLightGreen));
                                ObjectSetString(Terminal.Get_ID(), m_ArrObject[eRESULT].szName, OBJPROP_TEXT, DoubleToString(dValue, 2));
                        }
                        break;
                case CHARTEVENT_OBJECT_CLICK:
                        if (StringSubstr(szArg, 0, StringLen(def_HeaderMSG)) != def_HeaderMSG) return;
                        szArg = StringSubstr(szArg, 9, StringLen(szArg));
                        StringToUpper(szArg);
                        if ((szArg == szMsgIDE[eBTN_SELL]) || (szArg == szMsgIDE[eBTN_BUY])) NanoEA.OrderMarket(szArg == szMsgIDE[eBTN_BUY]);
                        if (szArg == szMsgIDE[eBTN_CANCEL])
                        {
                                NanoEA.ClosePosition();
                                ObjectSetInteger(Terminal.Get_ID(), m_ArrObject[eBTN_CANCEL].szName, OBJPROP_STATE, false);
                        }
                        if (szArg == szMsgIDE[eCHECK_DAYTRADE]) UpdateInfos(true);
                        break;
                case CHARTEVENT_OBJECT_ENDEDIT:
                        UpdateInfos();
                        break;
        }
}

Und es stellt sich die Frage: Ist das alles? Ja, es ist das Nachrichtensystem, das es der MetaTrader 5 Plattform ermöglicht, mit der IDE zu interagieren. Es ist sehr einfach, das muss ich zugeben, aber ohne diese Funktion würde die IDE nicht funktionieren, und es wäre nicht möglich, das System zu erstellen. Es mag ein wenig kompliziert erscheinen, wie man dies in einem EA umsetzen kann, aber dank OOP bleibt der EA-Code super einfach. Was ein wenig knifflig ist, ist die Aktualisierung des Ergebnisses, das in der IDE erscheinen wird. Die Werte werden in der Funktion OnTick aktualisiert, aber der Einfachheit halber habe ich die in MetaTrader 5 bereitgestellten Daten verwendet, so dass die Funktion wie folgt aussieht. Dieser Teil ist der wichtigste — diese Funktion ist die meistgefragte von allen, also sollte sie auch die schnellste sein.

void OnTick()
{
        SubWin.DispatchMessage(CHARTEVENT_CHART_CHANGE, C_Chart_IDE::szMsgIDE[C_Chart_IDE::eRESULT], NanoEA.CheckPosition());
}

Mit anderen Worten, bei jeder neuen Notierung wird eine Nachricht an die Klasse gesendet und der daraus resultierende Wert wird in der Operation aktualisiert. Vergessen Sie aber bitte nicht, dass diese Funktion gut optimiert sein muss, sonst können wir ernsthafte Probleme bekommen.


Schlussfolgerung

Manchmal scheint es unmöglich, einige Dinge zu tun, aber ich mag Herausforderungen. Und diese hier, die zeigt, wie man ein RAD-System auf einer Plattform realisiert, die ursprünglich nicht dafür entwickelt wurde, war sehr interessant. Ich hoffe, dass dieses System, das mit etwas Einfachem begann, Sie dazu motivieren kann, etwas Neues zu erforschen, was sich nur wenige Menschen trauen.

Bald werde ich etwas Neues zu diesem Expert Advisor hinzufügen, also bleiben Sie dran!



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

Beigefügte Dateien |
EA_1.04.zip (3275.47 KB)
Mehrere Indikatoren in einem Chart (Teil 06): Umwandlung des MetaTrader 5 in ein RAD-System (II) Mehrere Indikatoren in einem Chart (Teil 06): Umwandlung des MetaTrader 5 in ein RAD-System (II)
In meinem letzten Artikel habe ich Ihnen gezeigt, wie man einen Chart Trade mit MetaTrader 5 Objekten erstellt und so die Plattform in ein RAD-System verwandelt. Das System funktioniert sehr gut, und sicher haben viele der Leser über die Erstellung einer Bibliothek nachgedacht, die es ermöglichen würde, die Funktionsweise des vorgeschlagenen Systems zu erweitern. Auf dieser Grundlage wäre es möglich, einen intuitiveren Expert Advisor mit einer schöneren und einfacher zu bedienenden Oberfläche zu entwickeln.
Grafiken in der DoEasy-Bibliothek (Teil 99): Verschieben eines erweiterten grafischen Objekts mit einem einzigen Steuerpunkt Grafiken in der DoEasy-Bibliothek (Teil 99): Verschieben eines erweiterten grafischen Objekts mit einem einzigen Steuerpunkt
Im vorigen Artikel habe ich die Möglichkeit implementiert, Angelpunkte eines erweiterten grafischen Objekts mithilfe von Steuerformularen zu verschieben. Jetzt werde ich die Möglichkeit implementieren, ein zusammengesetztes grafisches Objekt mithilfe eines einzelnen grafischen Objektsteuerungspunkts (Formulars) zu verschieben.
Grafiken in der Bibliothek DoEasy (Teil 100): Verbesserungen im Umgang mit erweiterten grafischen Standardobjekten Grafiken in der Bibliothek DoEasy (Teil 100): Verbesserungen im Umgang mit erweiterten grafischen Standardobjekten
Im aktuellen Artikel werde ich offensichtliche Fehler bei der gleichzeitigen Behandlung von erweiterten (und Standard-) Grafikobjekten und Formularobjekten auf der Leinwand beseitigen sowie Fehler beheben, die bei dem im vorherigen Artikel durchgeführten Test entdeckt wurden. Der Artikel schließt diesen Teil der Bibliotheksbeschreibung ab.
Mehrere Indikatoren in einem Chart (Teil 04): Weiterentwicklung zum Expert Advisor Mehrere Indikatoren in einem Chart (Teil 04): Weiterentwicklung zum Expert Advisor
In meinen früheren Artikeln habe ich erklärt, wie man einen Indikator mit mehreren Unterfenstern erstellt, was bei der Verwendung von nutzerdefinierten Indikatoren interessant wird. Dieses Mal werden wir sehen, wie man mehrere Fenster einem Expert Advisor hinzufügen kann.