English Русский 中文 Español 日本語 Português 한국어 Français Italiano Türkçe
preview
Einen handelnden Expert Advisor von Grund auf neu entwickeln (Teil 20): Neues Auftragssystem (III)

Einen handelnden Expert Advisor von Grund auf neu entwickeln (Teil 20): Neues Auftragssystem (III)

MetaTrader 5Handelssysteme | 2 September 2022, 12:59
361 0
Daniel Jose
Daniel Jose

Einführung

Im vorangegangenen Artikel, Entwicklung eines Expert Advisors für den Handel von Grund auf (Teil 19), konzentrierten wir uns auf Codeänderungen, die implementiert wurden, um den Betrieb des neuen Auftragssystems zu ermöglichen. Seit diese Änderungen vorgenommen wurden, kann ich mich zu 100 % auf das eigentliche Problem konzentrieren. Es geht darum, das Auftragssystem zu implementieren, das zu 100 % visuell und für diejenigen verständlich ist, die handeln, ohne den Tickwert (tick value) zu kennen oder zu wissen, wo man eine Order platzieren muss, um X zu verdienen, oder wo man den Stop-Loss setzen muss, um Y nicht zu verlieren.

Die Erstellung eines solchen Systems erfordert eine gute Beherrschung von MQL5 sowie ein Verständnis dafür, wie die MetaTrader 5-Plattform tatsächlich funktioniert und welche Ressourcen sie bietet.


1.0. Planung

1.0.1. Gestalten von Indikatoren

Die Idee hier, also nicht nur die Idee, sondern das, was ich tatsächlich tun werde, ist zu zeigen, wie man das System in diesem Artikel implementiert. Wir werden etwas Ähnliches wie in der folgenden Abbildung erstellen:

Es ist auch ohne meine Erklärung sehr leicht zu verstehen. Es gibt eine Schaltfläche zum Schließen, einen Wert und einen Punkt, um das Ziehen und Platzieren von Aufträgen zu erleichtern. Aber das ist noch nicht alles. Wenn aus dem Stop-Loss ein Stop-Gain wird, geht das System wie folgt vor:

So können wir leicht feststellen, wann, wie viel, wo und ob es sich lohnt, eine bestimmte Position zu halten oder nicht.

Die obigen Zahlen zeigen nur die Objekte der OCO-Order oder der OCO-Positionslimits, aber ich habe den Teil, der sich auf den Eröffnungskurs bezieht, nicht vergessen, da dieser ebenso wichtig ist.

Für schwebende Aufträge (pending orders) sieht dies wie folgt aus:

Bei einer Position sieht das etwas anders aus:

Allerdings sind die Proportionen nicht sehr ermutigend... Aber das ist die Idee, die umgesetzt werden soll. Was die Farben angeht, so werde ich die hier gezeigten verwenden. Aber Sie können die auswählen, die Ihnen gefallen.

Wenn wir mit der Planung fortfahren, können wir feststellen, dass wir grundsätzlich 5 Objekte in jedem Indikator haben werden. Das bedeutet, dass MetaTrader 5 für jeden Indikator 5 Objekte gleichzeitig verarbeiten muss. Im Falle eines OCO-Auftrags muss MetaTrader 5 mit 15 Objekten umgehen, genau wie bei einer OCO-Position, bei der MetaTrader 5 15 Objekte verarbeiten muss, was einem Auftrag oder einer Position entspricht. Das heißt, wenn Sie 4 ausstehende OCO-Aufträge und eine offene OCO-Position haben, muss MetaTrader 5 25 Objekte verarbeiten, abgesehen von den anderen, die sich ebenfalls auf dem Chart befinden. Und das nur, wenn Sie nur eine Anlage auf der Plattform verwenden.

Ich sage dies, weil es wichtig ist, den möglichen Speicher- und Verarbeitungsbedarf für jeden Auftrag zu kennen, den Sie für das Handelsinstrument erteilen werden. Das ist bei modernen Computern kein Problem, aber man muss wissen, was genau man von der Hardware verlangt. Zuvor gab es für jeden Punkt des Auftrags nur ein Objekt auf dem Bildschirm. Jetzt gibt es 5 Objekte in jedem der Punkte, und sie müssen irgendwie miteinander verbunden bleiben. Diese Verbindung wird von der Plattform implementiert, während wir nur angeben, wie sie verbunden werden sollen und was passieren soll, wenn jedes der Objekte ausgelöst wird.


1.0.2. Auswahl der Objekte

Die nächste Frage betrifft die Auswahl der Objekte, die wir verwenden werden. Diese Frage mag einfach erscheinen, ist aber sehr wichtig, denn sie bestimmt, wie die Umsetzung tatsächlich ablaufen wird. Die erste Wahl basiert auf der Art und Weise, wie Objekte auf dem Bildschirm positioniert werden.

Wir haben zwei Möglichkeiten, dies umzusetzen. Glücklicherweise deckt MetaTrader 5 beides ab: die erste Möglichkeit ist die Positionierung über Zeit- und Preiskoordinaten, die zweite die über kartesische X- und Y-Koordinaten.

Bevor wir uns jedoch mit einem von ihnen im Detail befassen, werde ich das Modell, das Zeit- und Preiskoordinaten verwendet, sofort verwerfen. Obwohl es auf den ersten Blick ideal erscheint, ist es nicht sehr nützlich, wenn wir mit so vielen Gegenständen zu tun haben, die miteinander verbunden sind und zusammen bleiben müssen. Daher müssen wir das kartesische System verwenden.

In einem der vorangegangenen Artikel haben wir uns bereits mit diesem System befasst und besprochen, wie man Objekte auswählt. Einzelheiten finden Sie unter Mehrere Indikatoren in einem Chart (Teil 05).

Die Planung ist abgeschlossen, und nun können wir endlich zur eigentlichen Programmierung übergehen: Es ist an der Zeit, die Dinge in die Praxis umzusetzen.


2.0. Umsetzung

Mein Ziel ist es nicht, das System einfach nur zu implementieren, sondern zu erklären, was genau darin vor sich geht, sodass auch Sie Ihr eigenes System auf der Grundlage des betrachteten Systems erstellen können, wobei ich die Einzelheiten nach und nach erläutern werde. Dies wird Ihnen helfen zu verstehen, wie sie erstellt wird. Vergessen Sie nicht, dass alle Funktionen, die mit schwebenden Aufträgen zusammenhängen, auch für Positionen funktionieren, da das System denselben Prinzipien folgt und einen gemeinsamen Code hat.


2.0.1. Schaffung einer Schnittstellenstruktur

Das Ergebnis dieses ersten Schrittes ist unten zu sehen. Dies ist meine Art, die Vorteile zu präsentieren, damit Sie genauso begeistert sind, wie ich es war, als ich diese Codes entwickelt und beschlossen habe, sie mit Ihnen allen zu teilen. Ich hoffe, dass dies als Motivation für diejenigen dient, die das Programmieren lernen oder ihr Wissen zu diesem Thema vertiefen wollen.


Wenn Sie das obige Bild betrachten, werden Sie vielleicht denken, dass die Funktionalität auf herkömmliche Weise erstellt wurde, ohne den gesamten bisher erstellten Code. Aber nein, wir werden genau das verwenden, was bisher gebaut worden ist.

Wir verwenden also den Code, der im vorherigen Artikel vorgestellt wurde, und nehmen einige Änderungen daran vor. Konzentrieren wir uns also auf das, was im Code neu ist. Zunächst werden wir drei neue Klassen hinzufügen.


2.0.1.1. C_Object_Base Klasse

Wir beginnen mit der Erstellung einer neuen Klasse - C_Object_Base. Dies ist die unterste Klasse in unserem System. Die ersten Codes der Klasse sind unten aufgeführt:

class C_Object_Base
{
        public  :
//+------------------------------------------------------------------+
void Create(string szObjectName, ENUM_OBJECT typeObj)
{
        ObjectCreate(Terminal.Get_ID(), szObjectName, typeObj, 0, 0, 0);
        ObjectSetInteger(Terminal.Get_ID(), szObjectName, OBJPROP_SELECTABLE, false);
        ObjectSetInteger(Terminal.Get_ID(), szObjectName, OBJPROP_SELECTED, false);
        ObjectSetInteger(Terminal.Get_ID(), szObjectName, OBJPROP_BACK, true);
        ObjectSetString(Terminal.Get_ID(), szObjectName, OBJPROP_TOOLTIP, "\n");
        ObjectSetInteger(Terminal.Get_ID(), szObjectName, OBJPROP_BACK, false);
        ObjectSetString(Terminal.Get_ID(), szObjectName, OBJPROP_TOOLTIP, "\n");
        PositionAxleY(szObjectName, 9999);
};

// ... The rest of the class code

Beachten Sie, dass wir einen allgemeinen Code haben, der uns das Leben sehr erleichtern wird. In derselben Klasse gibt es standardmäßige X- und Y-Positionierungscodes.

void PositionAxleX(string szObjectName, int X)
{
        ObjectSetInteger(Terminal.Get_ID(), szObjectName, OBJPROP_XDISTANCE, X);
};
//+------------------------------------------------------------------+
virtual void PositionAxleY(string szObjectName, int Y)
{
        ObjectSetInteger(Terminal.Get_ID(), szObjectName, OBJPROP_YDISTANCE, Y);
};

Der Code für die Y-Positionierung hängt von einem bestimmten Objekt ab, aber selbst wenn das Objekt keinen spezifischen Code hat, bietet die Klasse einen allgemeinen Code. Es gibt eine allgemeine Möglichkeit, die Farbe des unten gezeigten Objekts festzulegen.

virtual void SetColor(string szObjectName, color cor)
{
        ObjectSetInteger(Terminal.Get_ID(), szObjectName, OBJPROP_COLOR, cor);
}

Und hier ist die Art und Weise, wie die Abmessungen der Objekte definiert werden.

void Size(string szObjectName, int Width, int Height)
{
        ObjectSetInteger(Terminal.Get_ID(), szObjectName, OBJPROP_XSIZE, Width);
        ObjectSetInteger(Terminal.Get_ID(), szObjectName, OBJPROP_YSIZE, Height);
};

Dies ist alles für den Moment mit der Klasse C_Object_Base, aber wir werden später darauf zurückkommen.


2.0.1.2. Die Klasse C_Object_BackGround

Lassen Sie uns nun zwei weitere Klassen erstellen, um unsere beiden grafischen Objekte zu unterstützen. Der erste von ihnen ist C_Object_BackGround. Es wird eine Hintergrundbox erstellt, die andere Elemente aufnimmt. Der Code ist ziemlich einfach. Sie können ihn unten in voller Länge sehen:

#property copyright "Daniel Jose"
//+------------------------------------------------------------------+
#include "C_Object_Base.mqh"
//+------------------------------------------------------------------+
class C_Object_BackGround : public C_Object_Base
{
        public:
//+------------------------------------------------------------------+
		void Create(string szObjectName, color cor)
                        {
                                C_Object_Base::Create(szObjectName, OBJ_RECTANGLE_LABEL);
                                ObjectSetInteger(Terminal.Get_ID(), szObjectName, OBJPROP_BORDER_TYPE, BORDER_FLAT);
                                ObjectSetInteger(Terminal.Get_ID(), szObjectName, OBJPROP_CORNER, CORNER_LEFT_UPPER);
                                ObjectSetInteger(Terminal.Get_ID(), szObjectName, OBJPROP_COLOR, clrNONE);
                                ObjectSetInteger(Terminal.Get_ID(), szObjectName, OBJPROP_BGCOLOR, cor);
                        }
//+------------------------------------------------------------------+
virtual void PositionAxleY(string szObjectName, int Y)
                        {
                                int desl = (int)(ObjectGetInteger(Terminal.Get_ID(), szObjectName, OBJPROP_YSIZE) / 2);
                                ObjectSetInteger(Terminal.Get_ID(), szObjectName, OBJPROP_YDISTANCE, Y - desl);
                        }
//+------------------------------------------------------------------+
};

Beachten Sie, dass wir die Vererbung nutzen, um das Objekt mit minimalem Code zusammenzusetzen. Auf diese Weise bringen wir die Klasse dazu, sich selbst zu modifizieren und zu modellieren, sodass wir diese Anpassungen später nicht mehr vornehmen müssen. Dies ist in dem hervorgehobenen Code zu sehen, in dem sich die Klasse automatisch an der richtigen Stelle positioniert, indem sie einfach den Wert der Y-Achse kennt — sie prüft die Größe und positioniert sich so, dass sie sich in der Mitte der Achse befindet, die wir ihr übergeben.


2.0.1.3. Die Klasse C_Object_TradeLine

Die Klasse C_Object_TradeLine ist dafür verantwortlich, die horizontale Linie zu ersetzen, die zuvor verwendet wurde, um anzuzeigen, wo sich die Preislinie des Auftrags befand. Diese Klasse ist sehr interessant, also schauen Sie sich ihren Code an: Sie hat eine private statische Variable, wie Sie im folgenden Code sehen können.

#property copyright "Daniel Jose"
#include "C_Object_BackGround.mqh"
//+------------------------------------------------------------------+
class C_Object_TradeLine : public C_Object_BackGround
{
        private :
                static string m_MemNameObj;
        public  :
//+------------------------------------------------------------------+

// ... Internal class code

//+------------------------------------------------------------------+
};
//+------------------------------------------------------------------+
string C_Object_TradeLine::m_MemNameObj = NULL;
//+------------------------------------------------------------------+

Sie wird hervorgehoben, um zu zeigen, wie man sie deklariert und wie man sie richtig initialisiert. Nun, wir könnten eine globale Variable erstellen, um die statische Variable zu ersetzen, aber ich möchte die Kontrolle über die Dinge behalten: Auf diese Weise hat jedes Objekt alles, was es braucht, und die Informationen sind in ihnen gespeichert. Und wenn wir ein Objekt durch ein anderes ersetzen wollen, können wir das problemlos tun.

Als Nächstes ist der Code für die Objekterstellung zu beachten.

void Create(string szObjectName, color cor)
{
        C_Object_BackGround::Create(szObjectName, cor);
        ObjectSetInteger(Terminal.Get_ID(), szObjectName, OBJPROP_XSIZE, TerminalInfoInteger(TERMINAL_SCREEN_WIDTH));
        SpotLight(szObjectName);
};

Um dies korrekt zu implementieren, verwenden wir die Klasse C_Object_BackGround, in der wir eine Box erstellen, die als Linie dient. Das liegt daran, dass wir bei Verwendung eines anderen Objekttyps nicht dasselbe Verhalten wie jetzt haben würden, und das einzige Objekt, das die von uns benötigten Funktionen erfüllt, ist das der Klasse C_Object_Background. Wir werden sie also ändern, um sie an unsere Bedürfnisse anzupassen, und so eine Linie schaffen.

Als Nächstes sehen wir uns den Code an, der für die Hervorhebung einer Linie verantwortlich ist.

void SpotLight(string szObjectName = NULL)
{
        if (szObjectName != NULL) ObjectSetInteger(Terminal.Get_ID(), szObjectName, OBJPROP_YSIZE, (szObjectName != NULL ? 4 : 3));
        if (m_MemNameObj != NULL) ObjectSetInteger(Terminal.Get_ID(), m_MemNameObj, OBJPROP_YSIZE, 3);
        m_MemNameObj = szObjectName;
};

Dieser Code ist recht interessant, denn wenn wir eine Linie hervorheben, brauchen wir nicht zu wissen, welche Linie hervorgehoben wurde, da das Objekt selbst dies für uns erledigt. Und wenn eine neue Linie hervorgehoben werden soll, verliert die hervorgehobene Linie automatisch diesen Status und die neue Linie nimmt ihren Platz ein. Wenn nun keine Linie hervorgehoben werden soll, rufen wir einfach die Funktion auf, und sie sorgt dafür, dass die Hervorhebung von jeder Linie entfernt wird.

Der obige Code und der folgende Code lassen den alten Auswahlcode verschwinden. Auf diese Weise wird MetaTrader 5 uns mitteilen, welchen Indikator wir manipulieren.

string GetObjectSelected(void) const { return m_MemNameObj; }

Es gibt noch eine weitere Funktion, auf die man achten sollte. Sie positioniert die Linie entlang der Y-Achse. Wir werden es weiter unten sehen.

virtual void PositionAxleY(string szObjectName, int Y)
{
        int desly = (m_MemNameObj == szObjectName ? 2 : 1);
        ObjectSetInteger(Terminal.Get_ID(), szObjectName, OBJPROP_YDISTANCE, Y - desly);
};

Wie die Funktion im BackGround-Objekt passt sich auch diese Funktion dem richtigen Punkt an, je nachdem, ob die Linie hervorgehoben ist oder nicht.

Wir haben zwei Objekte vollständig fertiggestellt. Aber bevor wir sie tatsächlich auf dem Bildschirm sehen können (wie oben gezeigt), müssen wir einige Dinge in der Klasse C_ObjectsTrade tun.


2.0.2. Modifikation der Klasse C_ObjectsTrade

Die durchzuführenden Änderungen sind auf den ersten Blick nicht sehr kompliziert, aber die Anzahl der Wiederholungen desselben Codes kann manchmal etwas entmutigend sein, daher habe ich versucht, einen Weg zu finden, dies zu umgehen. Das erste, was wir tun werden, ist eine Enumeration der Ereignisse zu erstellen. Dies geschieht mit Hilfe von Makros, aber wenn Sie es verwirrend finden, einem Code voller Makros zu folgen, können Sie gerne von Makros zu Funktionen oder Prozeduren wechseln und in extremen Fällen die Makros durch richtigen internen Code ersetzen. Ich ziehe es vor, Makros zu verwenden, wie ich dies schon seit vielen Jahren tue.

Zunächst erstellen wir eine Enumeration für Ereignisse.

enum eEventType {EV_GROUND = 65, EV_LINE};

Wenn Objekte erstellt werden, müssen wir hier neue Ereignisse hinzufügen, und diese müssen wichtig sein. Jedes Objekt hat jedoch nur eine Art von Ereignis, und dieses Ereignis wird von MetaTrader 5 erzeugt. Oder der Code stellt nur sicher, dass das Ereignis korrekt behandelt wird.

Danach werden wir Variablen erstellen, die den Zugang zu den einzelnen Objekten ermöglichen.

C_Object_BackGround     m_BackGround;
C_Object_TradeLine      m_TradeLine;

Sie befinden sich im globalen Bereich der Klasse, sind aber privat. Wir könnten sie in jeder Funktion deklarieren, die sie verwendet, aber das macht nicht viel Sinn, da sich die gesamte Klasse um die Objekte kümmert.

Wir nehmen also die entsprechende Änderung im Code des vorherigen Artikels vor.

inline string MountName(ulong ticket, eIndicatorTrade it, eEventType ev)
{
        return StringFormat("%s%c%c%c%d%c%c", def_NameObjectsTrade, def_SeparatorInfo, (char)it, def_SeparatorInfo, ticket, def_SeparatorInfo, (char)ev);
}

Die hervorgehobenen Teile gab es in der Vorgängerversion nicht, aber jetzt helfen sie dem MetaTrader 5, uns über das Geschehen zu informieren.

Außerdem haben wir eine neue Funktion.

void SetPositionMinimalAxleX(void)
{
        m_PositionMinimalAlxeX = (int)(ChartGetInteger(ChartID(), CHART_WIDTH_IN_PIXELS) * 0.2);
}

Es wird ein Startpunkt entlang der X-Achse für die Objekte erstellt. Jedes der Objekte wird einen bestimmten Punkt haben, aber wir geben hier einen ersten Hinweis. Sie können die Ausgangsposition ändern, indem Sie einfach den Punkt in diesem Code oben ändern.

Die Auswahlfunktion hat sich stark verändert, aber sie wird sich später noch ein wenig verändern. Zur Zeit sieht es folgendermaßen aus.

inline void Select(const string &sparam)
{
        ulong tick;
        double price;
        eIndicatorTrade it;
        eEventType ev;
        string sz = sparam;
                                
        if (!GetInfosOrder(sparam, tick, price, it, ev)) sz = NULL;
        m_TradeLine.SpotLight(sz);
}

Eine weitere Funktion, die sich ebenfalls geändert hat, ist diejenige, die den Indikator erstellt.

inline void CreateIndicatorTrade(ulong ticket, double price, eIndicatorTrade it, bool select)
{
        if (price <= 0) RemoveIndicatorTrade(ticket, it); else
        {
                CreateIndicatorTrade(ticket, it, select);
                PositionAxlePrice(price, ticket, it, -1, -1, 0, false);
        }
}

Aber der obige Code ist nicht so wichtig. Wo wirklich gearbeitet wird, zeigt der folgende Code.

inline void CreateIndicatorTrade(ulong ticket, eIndicatorTrade it)
{
        color cor1, cor2;
        string sz0;
                                
        switch (it)
        {
                case IT_TAKE    :
                        cor1 = clrPaleGreen;
                        cor2 = clrDarkGreen;
                        break;
                case IT_STOP    :
                        cor1 = clrCoral;
                        cor2 = clrMaroon;
                        break;
                case IT_PENDING:
                default:
                        cor1 = clrGold;
                        cor2 = clrDarkGoldenrod;
                        break;
        }                               
        m_TradeLine.Create(MountName(ticket, it, EV_LINE), cor2);
        if (ticket == def_IndicatorTicket0) m_TradeLine.SpotLight(MountName(ticket, IT_PENDING, EV_LINE));
        m_BackGround.Create(sz0 = MountName(ticket, it, EV_GROUND), cor1);
        switch (it)
        {
                case IT_TAKE:
                case IT_STOP:
                        m_BackGround.Size(sz0, 92, 22);
                        break;
                case IT_PENDING:
                        m_BackGround.Size(sz0, 110, 22);
                        break;
        }
}

In dieser Funktion legen wir die Farben und die Reihenfolge der Erstellung von Objekten fest und bestimmen ihre Größe. Jedes Objekt, das dem Indikator hinzugefügt wird, sollte mit dieser Funktion platziert werden, damit alles zentriert ist und immer überprüft wird. Wenn Sie damit beginnen, eine Funktion zur Erstellung von Indikatoren zu erstellen, werden Sie mit einer Art von Code enden, der schwer zu pflegen ist und dem es möglicherweise an geeigneten Prüfungen mangelt. Sie denken vielleicht, dass alles in Ordnung ist, dass es funktioniert, und Sie setzen es auf ein echtes Konto - erst dann wird es tatsächlich überprüft, und Sie werden plötzlich feststellen, dass einige Dinge nicht richtig funktionieren. Hier ein Ratschlag: Versuchen Sie immer, Funktionen in Dinge einzubauen, die dieselbe Aufgabe erfüllen; auch wenn es zunächst sinnlos erscheint, wird es mit der Zeit sinnvoll sein, da Sie immer wieder Dinge überprüfen werden, die sich ändern.

Im Folgenden finden Sie die nächste Funktion, die sich geändert hat.

#define macroDelete(A)  {                                                               \
                ObjectDelete(Terminal.Get_ID(), MountName(ticket, A, EV_GROUND));       \
                ObjectDelete(Terminal.Get_ID(), MountName(ticket, A, EV_LINE));         \
                        }
                                        
inline void RemoveIndicatorTrade(ulong ticket, eIndicatorTrade it = IT_NULL)
                        {
                                ChartSetInteger(Terminal.Get_ID(), CHART_EVENT_OBJECT_DELETE, false);
                                if ((it != NULL) && (it != IT_PENDING) && (it != IT_RESULT)) macroDelete(it)
                                else
                                {
                                        macroDelete(IT_PENDING);
                                        macroDelete(IT_RESULT);
                                        macroDelete(IT_TAKE);
                                        macroDelete(IT_STOP);
                                }
                                ChartSetInteger(Terminal.Get_ID(), CHART_EVENT_OBJECT_DELETE, true);
                        }
#undef macroDelete

Wie die Nächste ist sie ein bisschen langweilig. Die Funktion sollte so funktionieren, dass Sie derjenige sind, der jedes der erstellten Objekte einzeln auswählt. Dies sollte für jeden der Indikatoren gelten. Überlegen Sie es sich. Wenn wir kein Makro verwenden, das die Aufgabe erleichtert, würde dies ein Alptraum werden. Es wäre äußerst mühsam, diese Funktion zu kodieren, da jeder Indikator am Ende des Codes 5 Objekte enthält. Da jeder Satz in einem OCO-Auftrag 3 Indikatoren hat, müssten wir 15 Objekte verwenden, wobei die Wahrscheinlichkeit eines Fehlers (weil der Unterschied zwischen den Objekten nur im Namen besteht) sehr groß wäre. Mit Hilfe eines Makros wird der Code also auf das reduziert, was im Code hervorgehoben ist: Wir werden am Ende nur 5 Objekte codieren. Dies ist jedoch nur die erste Phase, um das oben gezeigte Ergebnis zu erzielen.

Um die erste Phase abzuschließen, haben wir eine weitere, ebenso mühsame Funktion. Würden wir keine Makros verwenden, könnten wir anstelle von Makros auch Prozeduren einsetzen. Aber es wurde dieser Weg gewählt.

#define macroSetAxleY(A)        {                                               \
                m_BackGround.PositionAxleY(MountName(ticket, A, EV_GROUND), y); \
                m_TradeLine.PositionAxleY(MountName(ticket, A, EV_LINE), y);    \
                                }
                                                                        
#define macroSetAxleX(A, B)     {                                               \
                m_BackGround.PositionAxleX(MountName(ticket, A, EV_GROUND), B); \
                m_TradeLine.PositionAxleX(MountName(ticket, A, EV_LINE), B);    \
                                }

inline void PositionAxlePrice(double price, ulong ticket, eIndicatorTrade it, int FinanceTake, int FinanceStop, int Leverange, bool isBuy)
                        {
                                double ad;
                                int x, y;
                                
                                ChartTimePriceToXY(Terminal.Get_ID(), 0, 0, price, x, y);
                                macroSetAxleY(it);
                                macroSetAxleX(it, m_PositionMinimalAlxeX);
                                if (Leverange == 0) return;
                                if (it == IT_PENDING)
                                {
                                        ad = Terminal.GetAdjustToTrade() / (Leverange * Terminal.GetVolumeMinimal());
                                        ChartTimePriceToXY(Terminal.Get_ID(), 0, 0, price + Terminal.AdjustPrice(FinanceTake * (isBuy ? ad : (-ad))), x, y);
                                        macroSetAxleY(IT_TAKE);
                                        macroSetAxleX(IT_TAKE, m_PositionMinimalAlxeX + 120);
                                        ChartTimePriceToXY(Terminal.Get_ID(), 0, 0, price + Terminal.AdjustPrice(FinanceStop * (isBuy ? (-ad) : ad)), x, y);
                                        macroSetAxleY(IT_STOP);
                                        macroSetAxleX(IT_STOP, m_PositionMinimalAlxeX + 220);
                                }
                        }
#undef macroSetAxleX
#undef macroSetAxleY

Wenn Sie denken, dass das vorherige Feature langweilig war, dann sehen Sie sich dieses an. Hier wäre die Arbeit doppelt so groß, aber dank der hervorgehobenen Codes wird die Sache akzeptabel.

Nun, es gibt noch andere kleine Änderungen, die vorgenommen werden mussten, aber sie sind nicht wirklich erwähnenswert. Wenn wir diesen Code ausführen, erhalten wir genau das, was wir erwarten, nämlich die Anzeige auf dem Bildschirm.


Schlussfolgerung

Es dauert noch eine ganze Weile, bis das System fertig ist und den Auftrag direkt auf dem Chart vollständig anzeigen kann. Aber jetzt müssen wir alles auf einmal machen, da es notwendig ist, sehr bedeutende Änderungen an anderen Stellen des Codes vorzunehmen.

Wir werden dies also für den nächsten Artikel aufheben, da die Änderungen sehr tiefgreifend sein werden. Und wenn etwas schief geht, müssen Sie einen Schritt zurückgehen und es erneut versuchen, bis Sie das System so ändern können, wie Sie es wollen. Auf diese Weise können Sie das System so anpassen, dass es Ihnen gefällt. Im nächsten Artikel werden wir das System wie unten dargestellt darstellen:


Ist doch einfach zu implementieren, nicht wahr? Aber glauben Sie mir, es gibt eine Menge Änderungen, die vorgenommen werden müssen. Wir sehen uns im nächsten Artikel.

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

Beigefügte Dateien |
Neuronale Netze leicht gemacht (Teil 18): Assoziationsregeln Neuronale Netze leicht gemacht (Teil 18): Assoziationsregeln
Als Fortsetzung dieser Artikelserie betrachten wir eine andere Art von Problemen innerhalb der Methoden des unüberwachten Lernens: die Ermittlung von Assoziationsregeln. Dieser Problemtyp wurde zuerst im Einzelhandel, insbesondere in Supermärkten, zur Analyse von Warenkörben eingesetzt. In diesem Artikel werden wir über die Anwendbarkeit solcher Algorithmen im Handel sprechen.
Lernen Sie, wie man ein Handelssystem mit Bulls Power entwirft Lernen Sie, wie man ein Handelssystem mit Bulls Power entwirft
Willkommen zu einem neuen Artikel in unserer Serie über das Lernen, wie man ein Handelssystem durch die beliebtesten technischen Indikator zu entwerfen, wie wir in diesem Artikel über einen neuen technischen Indikator lernen und wie wir ein Handelssystem durch sie zu entwerfen und dieser Indikator ist der Bulls Power-Indikator.
Datenwissenschaft und maschinelles Lernen — Neuronales Netzwerk (Teil 02): Entwurf von Feed Forward NN-Architekturen Datenwissenschaft und maschinelles Lernen — Neuronales Netzwerk (Teil 02): Entwurf von Feed Forward NN-Architekturen
Bevor wir fertig sind, müssen wir noch einige kleinere Dinge im Zusammenhang mit dem neuronalen Feed-Forward-Netz behandeln, unter anderem den Entwurf. Sehen wir uns an, wie wir ein flexibles neuronales Netz für unsere Eingaben, die Anzahl der verborgenen Schichten und die Knoten für jedes Netz aufbauen und gestalten können.
Lernen Sie, wie man ein Handelssystem mit Bears Power entwirft Lernen Sie, wie man ein Handelssystem mit Bears Power entwirft
Willkommen zu einem neuen Artikel in unserer Serie über das Lernen, wie man ein Handelssystem durch die beliebtesten technischen Indikator hier ist ein neuer Artikel über das Lernen, wie man ein Handelssystem von Bears Power technischen Indikator zu entwerfen.