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

Einen handelnden Expert Advisor von Grund auf neu entwickeln (Teil 19): Neues Auftragssystem (II)

MetaTrader 5Handel | 19 August 2022, 09:21
348 0
Daniel Jose
Daniel Jose

Einführung

Im vorangegangenen Artikel, Entwicklung eines Expert Advisors für den Handel von Grund auf (Teil 18), haben wir einige Korrekturen, Änderungen und Anpassungen im Auftragssystem vorgenommen, um ein System zu schaffen, das einen unterschiedlichen Handel auf NETTING- und HEDGING-Konten ermöglicht, da es Unterschiede bei den Kontobewegungen gibt. Beim Typ NETTING erzeugt das System einen Durchschnittspreis, und Sie haben nur eine offene Position für eine Anlage. Auf HEDGING-Konten können Sie mehrere offene Positionen haben, für die jeweils individuelle Limits gelten. Sie können die gleichen Vermögenswerte gleichzeitig kaufen und verkaufen. Dies ist nur bei HEDGING-Konten möglich. Dies ist die Grundlage, auf der der Optionshandel verstanden werden kann.

Aber jetzt ist es an der Zeit, das Auftragssystem endlich vollständig visuell zu gestalten, sodass wir das Mitteilungsfeld eliminieren und analysieren können, welche Werte sich in den einzelnen Positionen befinden, ohne es zu benötigen. Wir können dies einfach anhand des neuen Auftragssystems tun. Auf diese Weise können wir mehrere Dinge auf einmal einstellen. Außerdem werden wir in der Lage sein, die Gewinn- und Verlustlimits einer OCO-Position oder eines schwebenden (pending) OCO-Auftrags leicht zu erkennen, da der EA die entsprechenden Informationen in Echtzeit anzeigt und keine zusätzlichen Berechnungen erfordert.

Obwohl dies der erste Teil der Implementierung ist, fangen wir nicht bei Null an: Wir werden das bestehende System ändern, indem wir weitere Objekte und Ereignisse in den Chart des von uns gehandelten Vermögenswerts einfügen.


1.0. Planung

Die Planung des Systems, das wir hier verwenden, ist nicht besonders schwierig: Wir werden das bestehende System ändern, indem wir nur das System ändern, das die Aufträge auf dem Diagramm darstellt. Dies ist der Grundgedanke, der recht einfach erscheint. In der Praxis erfordert dies jedoch viel Kreativität, da wir die Daten so manipulieren und modellieren werden, dass die MetaTrader 5-Plattform die ganze Arbeit für uns erledigt.

Es gibt verschiedene Möglichkeiten, Daten zu modellieren, und jede hat ihre Vor- und Nachteile.

  • Die erste Möglichkeit ist die Verwendung einer Liste. Es kann ein zyklisches Einzel-, ein zyklisches Doppel- oder sogar ein Hashing-System sein. Der Vorteil jedes dieser Ansätze ist, dass das System einfach zu implementieren ist. Der Nachteil ist jedoch, dass dadurch eine Einfluss auf die Daten verhindert wird oder die Anzahl der Aufträge begrenzt wird. Außerdem müssten wir in diesem Fall die gesamte zusätzliche Logik erstellen, nur um die Liste zu speichern.
  • Die zweite Möglichkeit ist, ein Array von Klassen zu erstellen, wobei die Klasse alle neu erstellten Objekte enthält und verwaltet. In diesem Fall funktioniert das Array wie eine Liste, aber wir müssen weniger Code schreiben, weil MQL5 bereits einige Dinge unterstützt, die wir bei der Verwendung einer Liste programmieren müssten. Allerdings hätten wir dann andere Probleme, wie z.B. die Behandlung von Ereignissen, was in dieser Situation ziemlich schwierig wäre.
  • Der dritte Weg ist der, den wir verwenden werden. Wir werden den in MQL5 erstellten Code zwingen, dynamische Objekte zu unterstützen. Das scheint etwas Unwirkliches zu sein, aber wenn wir die zu verwendenden Daten richtig modellieren, können wir mit der Sprache MQL5 ein System schaffen, bei dem es keine Beschränkungen für die Anzahl der Objekte auf dem Bildschirm gibt. Außerdem werden alle Objekte in der Lage sein, Ereignisse zu erzeugen und zu empfangen. Und trotz ihrer Individualität sieht die Plattform sie alle verknüpft, als ob sie in einer Liste oder in einem Array-Index wären.

Wenn Sie denken, dass dies nicht einfach zu implementieren ist, sehen Sie sich den folgenden Code-Teil der Klasse C_HLineTrade an:

inline void SetLineOrder(ulong ticket, double price, eHLineTrade hl, bool select)
{
        string sz0 = def_NameHLineTrade + (string)hl + (string)ticket, sz1;
                                
        ObjectCreate(Terminal.Get_ID(), sz0, OBJ_HLINE, 0, 0, 0);

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

Der hervorgehobene Teil zeigt genau, dass wir so viele horizontale Linien erstellen können, wie wir wollen, und dass sie völlig unabhängig voneinander Ereignisse empfangen. Alles, was wir tun müssen, ist, Ereignisse zu implementieren, die auf dem Namen basieren, den jede der Zeilen haben wird, da die Namen eindeutig sein werden. Die MetaTrader 5-Plattform kümmert sich um den Rest. Das Ergebnis sieht dann etwa so aus:


Obwohl dies bereits als ideal erscheint, wird diese Modellierung nicht ausreichen, um das wirklich benötigte Ergebnis zu erzielen. Die Idee kann umgesetzt werden. Die derzeit im EA verfügbare Datenmodellierung ist jedoch nicht ideal, da wir nicht eine unbegrenzte Anzahl von Objekten auf der Grundlage eines Namens haben können. Wir müssen einige Änderungen vornehmen, die eine ziemlich tiefgreifende Codeänderung erfordern.

Wir werden nun damit beginnen, diese neue Datenmodellierungsmethode zu implementieren, aber wir werden nur das ändern, was dafür notwendig ist, während wir den gesamten Code stabil halten, da er weiterhin so stabil wie möglich funktionieren soll. Alle Arbeiten werden von der MetaTrader 5 Plattform durchgeführt, wir geben nur an, wie die Plattform unsere Modellierung verstehen soll.


2.0. Umsetzung

Die erste Änderung besteht darin, dass wir C_HLineTrade in die neue Klasse C_ObjectsTrade umwandeln. Diese neue Klasse wird das unterstützen können, was wir brauchen - eine Möglichkeit, eine unbegrenzte Anzahl von Objekten zu verknüpfen.

Schauen wir uns zunächst die ursprünglichen Definitionen im folgenden Code an.

class C_ObjectsTrade
{
//+------------------------------------------------------------------+
#define def_NameObjectsTrade 	"SMD_OT"
#define def_SeparatorInfo       '*'
#define def_IndicatorTicket0    1
//+------------------------------------------------------------------+
        protected:
                enum eIndicatorTrade {IT_NULL, IT_STOP= 65, IT_TAKE, IT_PRICE};
//+------------------------------------------------------------------+

// ... The rest of the class code

Hier haben wir die Ausgangsbasis, die wir umsetzen werden. Es wird in der Zukunft erweitert werden, aber im Moment möchte ich, dass das System stabil bleibt, obwohl es geändert wird und neue Datenmodellierung hat.

Auch innerhalb der „protected“ Deklaration gibt es die folgenden Funktionen:

inline double GetLimitsTake(void) const { return m_Limits.TakeProfit; }
//+------------------------------------------------------------------+
inline double GetLimitsStop(void) const { return m_Limits.StopLoss; }
//+------------------------------------------------------------------+
inline bool GetLimitsIsBuy(void) const { return m_Limits.IsBuy; }
//+------------------------------------------------------------------+
inline void SetLimits(double take, double stop, bool isbuy)
{
        m_Limits.IsBuy = isbuy;
        m_Limits.TakeProfit = (m_Limits.TakeProfit < 0 ? take : (isbuy ? (m_Limits.TakeProfit > take ? m_Limits.TakeProfit : take) : (take > m_Limits.TakeProfit ? m_Limits.TakeProfit : take)));
        m_Limits.StopLoss = (m_Limits.StopLoss < 0 ? stop : (isbuy ? (m_Limits.StopLoss < stop ? m_Limits.StopLoss : stop) : (stop < m_Limits.StopLoss ? m_Limits.StopLoss : stop)));
}
//+------------------------------------------------------------------+
inline int GetBaseFinanceLeveRange(void) const { return m_BaseFinance.Leverange; }
//+------------------------------------------------------------------+
inline int GetBaseFinanceIsDayTrade(void) const { return m_BaseFinance.IsDayTrade; }
//+------------------------------------------------------------------+
inline int GetBaseFinanceTakeProfit(void) const { return m_BaseFinance.FinanceTake; }
//+------------------------------------------------------------------+
inline int GetBaseFinanceStopLoss(void) const { return m_BaseFinance.FinanceStop; }


Derzeit dienen diese Funktionen nur als Sicherheitsmaßnahme für ein anderes System, das wir in Zukunft einführen werden. Auch wenn wir die Daten und das Parsing an anderer Stelle implementieren können, ist es gut, einige Dinge so weit unten in der Vererbungskette wie möglich zu belassen. Selbst wenn die Rückgabewerte nur von abgeleiteten Klassen verwendet werden, möchte ich dies nicht direkt zulassen: Ich möchte nicht, dass die abgeleitete Klasse auf die Werte zugreift, die sich in dieser C_ObjectsTrade-Objektklasse befinden, denn das würde die Idee der Objektklassenkapselung brechen und künftige Änderungen oder Fehlerbehebungen erschweren, wenn die abgeleitete Klasse den Wert der Basisklasse ändert, ohne die entsprechenden Änderungen durch einen Prozeduraufruf vorzunehmen.

Um die Überschneidung von Aufrufen so gering wie möglich zu halten, werden alle Funktionen inline deklariert: Dies erhöht die Größe der ausführbaren Datei geringfügig, führt aber zu einem sichereren System.

Nun kommen wir zu den „private“ Deklarationen.

//+------------------------------------------------------------------+
        private :
                string  m_SelectObj;
                struct st00
                {
                        double  TakeProfit,
                                StopLoss;
                        bool    IsBuy;
                }m_Limits;
                struct st01
                {
                        int     FinanceTake,
                                FinanceStop,
                                Leverange;
                        bool    IsDayTrade;
                }m_BaseFinance;
//+------------------------------------------------------------------+
                string MountName(ulong ticket, eIndicatorTrade it)
                {
                        return StringFormat("%s%c%c%c%d", def_NameObjectsTrade, def_SeparatorInfo, (char)it, def_SeparatorInfo, ticket);
                }
//+------------------------------------------------------------------+


Der wichtigste Teil ist das hervorgehobene Fragment, das die Namen der Objekte modellieren wird. Ich behalte die Grundlagen, die noch im System vorhanden sind. Das liegt daran, dass wir zunächst die Modellierung erstellen und ändern, um das System stabil zu halten. Dann fügen wir neue Objekte hinzu, und das geht ganz einfach und schnell. Außerdem werden wir die bereits erreichte Stabilität beibehalten.

Obwohl der Code viel mehr Änderungen erfahren hat, als hier gezeigt wird, werde ich mich nur auf die neuen Funktionen sowie auf die Änderungen konzentrieren, die im Vergleich zu früheren Codes erheblich waren.

Die erste Funktion ist unten dargestellt:

inline string CreateIndicatorTrade(ulong ticket, eIndicatorTrade it, bool select)
{
        string sz0 = MountName(ticket, it);
                                
        ObjectCreate(Terminal.Get_ID(), sz0, OBJ_HLINE, 0, 0, 0);
        ObjectSetInteger(Terminal.Get_ID(), sz0, OBJPROP_COLOR, (it == IT_PRICE ? clrBlue : (it == IT_STOP ? clrFireBrick : clrForestGreen)));
        ObjectSetInteger(Terminal.Get_ID(), sz0, OBJPROP_WIDTH, 1);
        ObjectSetInteger(Terminal.Get_ID(), sz0, OBJPROP_STYLE, STYLE_DASHDOT);
        ObjectSetInteger(Terminal.Get_ID(), sz0, OBJPROP_SELECTABLE, select);
        ObjectSetInteger(Terminal.Get_ID(), sz0, OBJPROP_SELECTED, false);
        ObjectSetInteger(Terminal.Get_ID(), sz0, OBJPROP_BACK, true);
        ObjectSetString(Terminal.Get_ID(), sz0, OBJPROP_TOOLTIP, (string)ticket + " "+StringSubstr(EnumToString(it), 3, 10));
                                
        return sz0;
}

Im Moment wird nur eine horizontale Linie erzeugt. Achten Sie auf den Code für die Namensgenerierung; beachten Sie auch, dass die Farben jetzt intern durch den Code und nicht durch den Nutzer definiert werden.

Dann überladen wir die gleiche Funktion, wie unten zu sehen ist.

inline string CreateIndicatorTrade(ulong ticket, double price, eIndicatorTrade it, bool select)
{
        if (price <= 0)
        {
                RemoveIndicatorTrade(ticket, it);
                return NULL;
        }
        string sz0 = CreateIndicatorTrade(ticket, it, select);
        ObjectMove(Terminal.Get_ID(), sz0, 0, 0, price);
                                
        return sz0;
}

Verwechseln Sie diese beiden Funktionen nicht, denn obwohl sie auf den ersten Blick gleich aussehen, sind sie in Wirklichkeit unterschiedlich. Überladung ist recht häufig: Wir erstellen eine einfache Funktion und fügen ihr dann neue Parameter hinzu, um eine bestimmte Art der Modellierung zu erreichen. Wenn wir sie nicht durch Überladung implementieren würden, müssten wir manchmal die gleiche Codesequenz wiederholen. Das ist gefährlich, denn wir können vergessen, etwas zu deklarieren. Außerdem ist dies nicht sehr praktisch, sodass wir die Funktion überladen, um einen Aufruf statt mehrerer zu machen.

Eine Sache, die hier erwähnt werden sollte, ist der Teil, der in dieser zweiten Version hervorgehoben ist. Es ist nicht nötig, sie hier zu erstellen, wir können sie auch an einem anderen Ort erstellen. Wenn wir jedoch versuchen, ein Objekt mit dem Preis Null zu schaffen, muss es in der Tat zerstört werden.

Um zu sehen, in welchem Moment dies geschieht, sehen Sie sich den folgenden Code an:

class C_Router : public C_ObjectsTrade
{

// ... Internal class code ....

                void UpdatePosition(int iAdjust = -1)
                        {

// ... Internal function code ...

                                for(int i0 = p; i0 >= 0; i0--) if(PositionGetSymbol(i0) == Terminal.GetSymbol())
                                {
                                        ul = PositionGetInteger(POSITION_TICKET);
                                        m_bContainsPosition = true;
                                        CreateIndicatorTrade(ul, PositionGetDouble(POSITION_PRICE_OPEN), IT_PRICE, false);
                                        CreateIndicatorTrade(ul, take = PositionGetDouble(POSITION_TP), IT_TAKE, true);
                                        CreateIndicatorTrade(ul, stop = PositionGetDouble(POSITION_SL), IT_STOP, true);

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


Jedes Mal, wenn der EA das OnTrade-Ereignis empfängt, führt er die obige Funktion aus und versucht, einen Indikator für die ausgewählten Punkte zu erstellen, aber wenn der Nutzer das Limit entfernt, wird es zu Null. Wenn er aufgerufen wird, wird der Indikator also tatsächlich aus dem Chart gelöscht, was uns unnötige Objekte im Speicher erspart. So haben wir an einigen Stellen einen Gewinn, da die Prüfung gleich bei der Erstellung erfolgt.

Aber wir haben immer noch ein Problem mit der Überladung, weil einige Leute nicht ganz verstehen, wie sie in echtem Code verwendet wird. Um dies zu verstehen, werfen Sie einen Blick auf die beiden folgenden Codeteile:

class C_OrderView : public C_Router
{
        private  :
//+------------------------------------------------------------------+
        public   :
//+------------------------------------------------------------------+
                void InitBaseFinance(int nContracts, int FinanceTake, int FinanceStop, bool b1)
                        {                       
                                SetBaseFinance(nContracts, FinanceTake, FinanceStop, b1);
                                CreateIndicatorTrade(def_IndicatorTicket0, IT_PRICE, false);
                                CreateIndicatorTrade(def_IndicatorTicket0, IT_TAKE, false);
                                CreateIndicatorTrade(def_IndicatorTicket0, IT_STOP, false);
                        }
//+------------------------------------------------------------------+

// ... Rest of the code...
class C_Router : public C_ObjectsTrade
{

// ... Class code ...

                void UpdatePosition(int iAdjust = -1)
                        {
// ... Function code ....
                                for(int i0 = p; i0 >= 0; i0--) if(PositionGetSymbol(i0) == Terminal.GetSymbol())
                                {
                                        ul = PositionGetInteger(POSITION_TICKET);
                                        m_bContainsPosition = true;
                                        CreateIndicatorTrade(ul, PositionGetDouble(POSITION_PRICE_OPEN), IT_PRICE, false);

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

Beachten Sie, dass in beiden Fällen derselbe Name der verwendeten Funktion verwendet wird. Außerdem sind beide Teil der gleichen Klasse C_ObjectsTrade. Aber selbst in diesem Fall kann der Compiler zwischen ihnen unterscheiden, was an der Anzahl der Parameter liegt. Wenn Sie genau hinsehen, werden Sie feststellen, dass der einzige Unterschied ein zusätzlicher „Preis“-Parameter ist, aber es kann auch noch andere geben. Wie Sie sehen, ist es viel einfacher, mit einem Aufruf den gesamten Code zu kopieren, der in einer der überladenen Versionen vorhanden ist, sodass wir am Ende einen saubereren Code haben, der leichter zu pflegen ist.

Lassen Sie uns nun zur Klasse C_ObjectsTrade zurückkehren. Die nächste Funktion, die wir verstehen müssen, sieht wie folgt aus:

bool GetInfosOrder(const string &sparam, ulong &ticket, double &price, eIndicatorTrade &it)
{
        string szRet[];
        char szInfo[];
                                
        if (StringSplit(sparam, def_SeparatorInfo, szRet) < 2) return false;
        if (szRet[0] != def_NameObjectsTrade) return false;
        StringToCharArray(szRet[1], szInfo);
        it = (eIndicatorTrade)szInfo[0];
        ticket = (ulong) StringToInteger(szRet[2]);
        price = ObjectGetDouble(Terminal.Get_ID(), sparam, OBJPROP_PRICE);
                                
        return true;
}


Sie ist das Herz, der Geist und der Körper des gesamten neuen Systems. Obwohl sie recht einfach erscheint, erfüllt sie eine Aufgabe, die für das Funktionieren des gesamten EA, wie es unser neues Modellierungssystem erfordert, unerlässlich ist.

Achten Sie genau auf den hervorgehobenen Code, insbesondere auf die Funktion StringSplit. Wenn es diese Funktion in MQL5 nicht gäbe, müssten wir sie programmieren. Glücklicherweise verfügt MQL5 darüber, sodass wir diese Funktion in vollem Umfang nutzen werden. Dabei wird der Name des Objekts in die erforderlichen Daten zerlegt. Wenn ein Objektname erstellt wird, wird er auf eine ganz bestimmte Weise modelliert, und deshalb können wir dieses Kodierungsmodell rückgängig machen, sodass StringSplit rückgängig macht, was die Funktion StringFormat tut.

Der Rest der Funktion erfasst die im Objektnamen enthaltenen Daten, damit wir sie testen und später verwenden können. Das heißt, MetaTrader 5 generiert die Daten für uns, wir zerlegen sie, um zu wissen, was passiert ist, und sagen MetaTrader 5 dann, welche Schritte er unternehmen soll. Unser Ziel ist es, MetaTrader 5 für uns arbeiten zu lassen. Ich erstelle kein Modell von Grund auf, sondern ich modelliere die Schnittstelle und den EA von Grund auf. Daher sollten wir die Unterstützung des MetaTrader 5 nutzen, anstatt nach einer externen Lösung zu suchen.

Im folgenden Code werden wir etwas sehr Ähnliches tun wie oben:

inline void RemoveAllsIndicatorTrade(bool bFull)
{
        string sz0, szRet[];
        int i0 = StringLen(def_NameObjectsTrade);
                                
        ChartSetInteger(Terminal.Get_ID(), CHART_EVENT_OBJECT_DELETE, false);
        for (int c0 = ObjectsTotal(Terminal.Get_ID(), -1, -1); c0 >= 0; c0--)
        {
                sz0 = ObjectName(Terminal.Get_ID(), c0, -1, -1);
                if (StringSubstr(sz0, 0, i0) == def_NameObjectsTrade)
                {
                        if (!bFull)
                        {
                                StringSplit(sz0, def_SeparatorInfo, szRet);
                                if (StringToInteger(szRet[2]) == def_IndicatorTicket0) continue;
                        }
                }else continue;                                         
                ObjectDelete(Terminal.Get_ID(), sz0);
        }
        ChartSetInteger(Terminal.Get_ID(), CHART_EVENT_OBJECT_DELETE, true);
}


Jedes Mal, wenn wir eine Linie aus dem Chart entfernen, egal ob es sich um eine Position handelt, die geschlossen wird, oder um ein Limit-Level, das entfernt wird, muss das entsprechende Objekt entfernt werden, genau wie beim Entfernen des EA aus dem Chart. Wir müssen die Objekte löschen, aber wir haben auch eine Reihe von Linien, die nicht gelöscht werden sollten, wenn es nicht absolut notwendig ist: dies ist Ticket0, es sollte nicht gelöscht werden, wenn es nicht absolut notwendig ist. Um die Entfernung zu vermeiden, verwenden wir den hervorgehobenen Code. Andernfalls müssten wir dieses Ticket0 jedes Mal neu erstellen, da dieses Ticket in einem anderen Codeteil, den wir später besprechen werden, sehr wichtig ist.

In allen anderen Fällen müssen wir etwas Bestimmtes löschen. Dazu verwenden wir eine andere Funktion für das Entfernung, die unten dargestellt ist.

inline void RemoveIndicatorTrade(ulong ticket, eIndicatorTrade it = IT_NULL)
{
        ChartSetInteger(Terminal.Get_ID(), CHART_EVENT_OBJECT_DELETE, false);
        if ((it != NULL) && (it != IT_PRICE))
                ObjectDelete(Terminal.Get_ID(), MountName(ticket, it));
        else
        {
                ObjectDelete(Terminal.Get_ID(), MountName(ticket, IT_PRICE));
                ObjectDelete(Terminal.Get_ID(), MountName(ticket, IT_TAKE));
                ObjectDelete(Terminal.Get_ID(), MountName(ticket, IT_STOP));
        }
        ChartSetInteger(Terminal.Get_ID(), CHART_EVENT_OBJECT_DELETE, true);
}

Die nächste neue Routine ist unten zu sehen:

inline void PositionAxlePrice(double price, ulong ticket, eIndicatorTrade it, int FinanceTake, int FinanceStop, int Leverange, bool isBuy)
{
        double ad = Terminal.GetAdjustToTrade() / (Leverange * Terminal.GetVolumeMinimal());
        ObjectMove(Terminal.Get_ID(), MountName(ticket, it), 0, 0, price);
        if (it == IT_PRICE)
        {
                ObjectMove(Terminal.Get_ID(), MountName(ticket, IT_TAKE), 0, 0, price + Terminal.AdjustPrice(FinanceTake * (isBuy ? ad : (-ad))));
                ObjectMove(Terminal.Get_ID(), MountName(ticket, IT_STOP), 0, 0, price + Terminal.AdjustPrice(FinanceStop * (isBuy ? (-ad) : ad)));
        }
}

Sie platziert Objekte auf der Preisachse. Gewöhnen Sie sich aber nicht zu sehr daran, denn sie wird aus verschiedenen Gründen bald nicht mehr existieren. Eine davon ist die, die wir in einem anderen Artikel dieser Serie besprochen haben: Mehrere Indikatoren auf einem Chart (Teil 05): Umwandlung von MetaTrader 5 in das RAD(I)-System. Dieser Artikel enthält eine Tabelle mit Objekten, die kartesische Koordinaten für die Positionierung verwenden können, und diese Koordinaten sind X und Y. Preis- und Zeitkoordinaten sind zwar in einigen Fällen nützlich, aber nicht immer bequem: Wenn wir Elemente positionieren wollen, die an bestimmten Punkten auf dem Bildschirm positioniert werden müssen, ist es zwar schneller, Dinge unter Verwendung von Preis- und Zeitkoordinaten zu entwickeln, aber es ist viel schwieriger, mit ihnen zu arbeiten als mit dem X- und Y-System.

Wir werden beim nächsten Mal Änderungen vornehmen, aber jetzt geht es uns darum, ein alternatives System zu dem bisher verwendeten zu schaffen.

Als Nächstes kommt die letzte wichtige Funktion in der Klasse C_ObjectsTrade. Dies wird in der folgenden Zeile dargestellt:

inline double GetDisplacement(const bool IsBuy, const double Vol, eIndicatorTrade it) const
{
        int i0 = (it == IT_TAKE ? m_BaseFinance.FinanceTake : m_BaseFinance.FinanceStop),
            i1 = (it == IT_TAKE ? (IsBuy ? 1 : -1) : (IsBuy ? -1 : 1));
        return (Terminal.AdjustPrice(i0 * (Vol / m_BaseFinance.Leverange) * Terminal.GetAdjustToTrade() / Vol) * i1);
}


Diese Funktion führt eine Umrechnung zwischen den im Chart Trader angegebenen Werten für eine zu platzierende Pending Order oder eine Position, die vom Markt eröffnet wird, durch.

All diese Änderungen wurden vorgenommen, um die Funktion C_HLineTrade in C_ObjectsTrade umzuwandeln. Diese Änderungen erforderten jedoch auch einige andere Änderungen. Die Klasse C_ViewOrder zum Beispiel hat sich ebenfalls stark verändert. Einige Teile dieser Klasse haben einfach aufgehört zu existieren, weil ihre Existenz sinnlos ist, während die übrigen Funktionen geändert wurden. Die Funktionen, die besondere Aufmerksamkeit verdienen, werden im Folgenden hervorgehoben.

Die erste ist die Funktion zur Initialisierung der vom Chart Trader kommenden Daten.

void InitBaseFinance(int nContracts, int FinanceTake, int FinanceStop, bool b1)
{                       
        SetBaseFinance(nContracts, FinanceTake, FinanceStop, b1);
        CreateIndicatorTrade(def_IndicatorTicket0, IT_PRICE, false);
        CreateIndicatorTrade(def_IndicatorTicket0, IT_TAKE, false);
        CreateIndicatorTrade(def_IndicatorTicket0, IT_STOP, false);
}

Die hervorgehobenen Teile sind die Stellen, an denen das Ticket0 tatsächlich erstellt wird. Dieses Ticket wird verwendet, um einen schwebenden Auftrag mit der Maus und der Tastatur zu platzieren: (SHIFT) zum Kaufen, (CTRL) zum Verkaufen. Zuvor wurden an dieser Stelle Linien erstellt, die dann dazu dienten, anzuzeigen, wo der Auftrag liegen würde. Jetzt sind die Dinge viel einfacher: Genauso wie wir einen zu erteilenden Auftrag sehen, sehen wir auch einen ausstehenden Auftrag oder eine offene Position. Das bedeutet, dass wir das System ständig überprüfen werden. Es ist, als ob Sie ein Fahrzeug zusammenbauen und die ganze Zeit die Bremsen überprüfen würden, damit Sie wissen, wie es sich verhält, wenn Sie es tatsächlich benutzen müssen.

Das große Problem bei einem langatmigen Code ist, dass wir bei der Erstellung einer Funktion nur wissen können, ob sie zu dem Zeitpunkt funktioniert, zu dem sie tatsächlich verwendet wird. Aber jetzt wird das System immer überprüft - selbst wenn wir nicht alle Funktionen verwenden, werden sie aufgrund der Wiederverwendung von Code an verschiedenen Stellen ständig überprüft.

Die letzte Routine, die ich in diesem Artikel erwähnen werde, ist unten dargestellt. Es wird ein schwebender Auftrag erteilt. Beachten Sie, dass sie im Vergleich zur gleichen Funktion in früheren Artikeln extrem kompakt geworden ist.

inline void MoveTo(uint Key)
{
        static double local = 0;
        datetime dt;
        bool    bEClick, bKeyBuy, bKeySell, bCheck;
        double  take = 0, stop = 0, price;
                                
        bEClick  = (Key & 0x01) == 0x01;    //Let mouse button click
        bKeyBuy  = (Key & 0x04) == 0x04;    //Pressed SHIFT
        bKeySell = (Key & 0x08) == 0x08;    //Pressed CTRL  
        Mouse.GetPositionDP(dt, price);
        if (bKeyBuy != bKeySell)
        {
                Mouse.Hide();
                bCheck = CheckLimits(price);
        } else Mouse.Show();
        PositionAxlePrice((bKeyBuy != bKeySell ? price : 0), def_IndicatorTicket0, IT_PRICE, (bCheck ? 0 : GetBaseFinanceTakeProfit()), (bCheck ? 0 : GetBaseFinanceStopLoss()), GetBaseFinanceLeveRange(), bKeyBuy);
        if((bEClick) && (bKeyBuy != bKeySell) && (local == 0)) CreateOrderPendent(bKeyBuy, local = price);
        local = (local != price ? 0 : local);
}


Der Grund dafür ist, dass es jetzt eine neue Regel im System geben wird, sodass die Funktion „etwas an Gewicht verloren“ hat und kompakter geworden ist.


Schlussfolgerung

Ich habe hier einige Änderungen vorgestellt, die im nächsten Artikel verwendet werden. Der Zweck all dessen ist es, sie zu vereinfachen und Dinge zu zeigen, die zu verschiedenen Zeiten unterschiedlich sein können. Meine Idee ist, dass jeder mitmacht und lernt, wie man einen EA programmiert, der einem bei der Arbeit hilft. Ich möchte zeigen, dass es Probleme gibt, die gelöst werden müssen, und den Weg aufzeigen, den ich eingeschlagen habe, um die Fragen und Probleme zu lösen, die während der Entwicklung auftreten. Ich hoffe, Sie verstehen diesen Punkt. Denn wenn es darum ginge, ein System zu schaffen und es in einer fertigen Form zu präsentieren, würde ich das besser tun und die Idee verkaufen, aber das ist nicht meine Absicht...

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

Beigefügte Dateien |
EA.zip (12023.87 KB)
Metamodelle für maschinelles Lernen und Handel: Ursprünglicher Zeitpunkt der Handelsaufträge Metamodelle für maschinelles Lernen und Handel: Ursprünglicher Zeitpunkt der Handelsaufträge
Metamodelle im maschinellen Lernen: Automatische Erstellung von Handelssystemen mit wenig oder gar keinem menschlichen Eingriff — Das Modell entscheidet selbständig, wann und wie es handelt.
Lernen Sie, wie man ein Handelssystem mit dem Force Index entwirft Lernen Sie, wie man ein Handelssystem mit dem Force Index entwirft
Hier ist ein neuer Artikel aus unserer Serie darüber, wie man ein Handelssystem basierend auf den beliebtesten technischen Indikatoren entwirft. In diesem Artikel lernen wir einen neuen technischen Indikator kennen und erfahren, wie man ein Handelssystem mit dem Force Index-Indikator erstellt.
Datenwissenschaft und maschinelles Lernen - Neuronales Netzwerk (Teil 01): Entmystifizierte Feed Forward Neurale Netzwerke Datenwissenschaft und maschinelles Lernen - Neuronales Netzwerk (Teil 01): Entmystifizierte Feed Forward Neurale Netzwerke
Viele Menschen lieben sie, aber nur wenige verstehen die gesamte Funktionsweise neuronaler Netze. In diesem Artikel werde ich versuchen, alles, was hinter den verschlossenen Türen einer mehrschichtigen Feed-Forward-Wahrnehmung vor sich geht, in einfacher Sprache zu erklären.
Komplexe Indikatoren mit Objekten vereinfachen Komplexe Indikatoren mit Objekten vereinfachen
In diesem Artikel wird eine Methode zur Erstellung komplexer Indikatoren vorgestellt, bei der gleichzeitig die Probleme vermieden werden, die bei der Arbeit mit mehreren Flächen, Puffern und/oder der Kombination von Daten aus mehreren Quellen auftreten.