English Русский 中文 Español 日本語 Português
preview
Erstellen eines EA, der automatisch funktioniert (Teil 09): Automatisierung (I)

Erstellen eines EA, der automatisch funktioniert (Teil 09): Automatisierung (I)

MetaTrader 5Handel | 4 Mai 2023, 12:51
370 0
Daniel Jose
Daniel Jose

Einführung

Im vorangegangenen Artikel „Erstellen eines EA, der automatisch funktioniert (Teil 08): OnTradeTransaction“, habe ich erklärt, wie wir die Vorteile der MetaTrader 5-Plattform nutzen können, indem wir eine interessante Funktion zur Ereignisbehandlung verwenden. Wir beginnen nun mit dem Aufbau der ersten Automatisierungsebene in unserem EA.

Im Gegensatz zu vielen bestehenden Mechanismen werden wir hier einen Mechanismus in Betracht ziehen, der den EA oder die Plattform nicht überlastet. Dieser Mechanismus kann für HEDGING-Konten verwendet werden, obwohl er hauptsächlich für NETTING-Konten gedacht ist.

Wir werden mit einem einfachen System beginnen, das OCO Aufträge verwendet. Später werden wir die Funktionsweise erweitern, um ein noch zuverlässigeres und interessanteres System anzubieten, insbesondere für diejenigen, die gerne in sehr volatilen Märkten handeln, wo ein hohes Risiko von verpassten Aufträgen besteht.


Erstellen von Breakeven und Trailing-Stop für OCO-Aufträge

Wenn Sie damit nicht vertraut sind, lassen Sie mich das erklären. Das OCO-Auftragssystem (One-Cancels-the-Other oder Einer löscht den Anderen) ist ein System, bei dem der Take-Profit und der Stop-Loss im Auftrag oder in der Position selbst festgelegt werden. Wenn der Auftrag storniert oder die Position geschlossen wird, werden diese Take-Profit- oder Stop-Loss-Aufträge ebenfalls storniert.

Take-Profit- und Stop-Loss-Aufträge können jederzeit entfernt oder hinzugefügt werden. Aus praktischen Gründen und um den Code nicht zu verkomplizieren, gehen wir davon aus, dass sie immer in dem Moment erstellt werden, in dem der EA einen Auftrag an den Handelsserver sendet, und dass sie aufhören zu existieren, wenn eines der Limits erreicht und die Position geschlossen wird.

Um einen Auslösemechanismus zu erstellen, werden wir mit der Klasse C_Manager arbeiten. Hier haben wir fast alles vorbereitet, um ein Trigger-System für Breakeven und Trailing-Stop zu erhalten. Fügen wir zunächst eine Funktion hinzu, die ein Breakeven-Level für eine Position generiert. Der vollständige Code der Funktion ist unten dargestellt:

inline void TriggerBreakeven(void)
                        {
                                if (PositionSelectByTicket(m_Position.Ticket))
                                        if (PositionGetDouble(POSITION_PROFIT) >= m_Trigger)
                                                m_Position.EnableBreakEven = (ModifyPricePoints(m_Position.Ticket, m_Position.PriceOpen, m_Position.PriceOpen, m_Position.TP) ? false : true);
                        }

Wahrscheinlich haben Sie eine viel komplexere Funktion als die hier gezeigte erwartet, aber glauben Sie mir, diese einfache Funktion ist in der Lage, den Breakeven-Level einer Position auszulösen. Wenn Sie nicht verstehen, wie dies möglich ist, lassen Sie uns analysieren, wie dies geschieht, damit Sie glauben, dass wir außer dieser einfachen Funktion nichts weiter benötigen.

Als erstes rufen wir die Funktion PositionSelectByTicket auf. Diese Funktion lädt alle aktualisierten Informationen über eine offene Position herunter. Wir verwenden dann die Funktion PositionGetDouble mit dem Argument POSITION_PROFIT, um den letzten Finanzwert zu erhalten, der durch einen Aufruf von PositionSelectByTicket geladen wurde. Wir vergleichen diesen Wert mit dem Wert, der bei der Initialisierung der Klasse im Konstruktoraufruf eingegeben wurde.

Wenn der Wert größer oder gleich diesem Wert ist (dies ist der Auslöser), deutet dies darauf hin, dass wir die Gewinnschwelle erreichen können. Dann senden wir den Kurswert, zu dem die Position eröffnet wurde, an die Funktion in der Klasse C_Orders. Wenn diese Interaktion erfolgreich ist, zeigt die Klasse an, dass die Gewinnschwelle erreicht wurde.

Diese Funktion ist ziemlich einfach, nicht wahr? Seine Definition ist jedoch im privaten Teil des Codes enthalten, auf den der EA zugreifen wird. Es handelt sich um die unten dargestellte Trailing-Stop-Funktion:

inline void TriggerTrailingStop(void)
                        {
                                double price;
                                
                                if ((m_Position.Ticket == 0) || (m_Position.SL == 0)) return;
                                if (m_Position.EnableBreakEven) TriggerBreakeven(); else
                                {
                                        price = SymbolInfoDouble(_Symbol, (GetTerminalInfos().ChartMode == SYMBOL_CHART_MODE_LAST ? SYMBOL_LAST : (m_Position.IsBuy ? SYMBOL_ASK : SYMBOL_BID)));
                                        if (MathAbs(price - m_Position.SL) >= (m_Position.Gap * 2))
                                                ModifyPricePoints(m_Position.Ticket, m_Position.PriceOpen, (m_Position.SL + (m_Position.Gap * (m_Position.IsBuy ? 1 : -1))), m_Position.TP);
                                }
                        }

Nachdem der Breakeven ausgelöst und ausgeführt wurde, prüfen wir beim nächsten Aufruf der Funktion TriggerTrailingStop durch den EA die Möglichkeit, den Stop-Loss zu verschieben (Trailing).

Achten Sie aber vorher darauf, wo die Breakeven-Funktion tatsächlich aufgerufen wird. Die Trailing-Stop-Bewegung hat einen etwas komplexeren Auslösemechanismus als die Breakeven-Bewegung. In diesem Fall werden wir es ein wenig anders machen. Im Gegensatz zum Breakeven, der nicht vom Vermögenswert, vom Chartmodus und vom Markttyp abhängt und bei dem nur das Gewinnniveau der Position wichtig ist, müssen wir beim Trailing-Stop zwei Dinge wissen: den Charttyp und die Art der Position.

Einige EA-Entwickler kümmern sich manchmal nicht um die Chart-Darstellungsmethode, die bei der Erstellung eines Trailing-Stop-Triggers verwendet wird. In manchen Fällen ist dies richtig. Wenn der Vermögenswert eine relativ hohe Varianz des Spread in der Historie aufweist (Differenz zwischen dem besten Verkäufer und dem besten Käufer), können wir selbst bei der auf dem letzten Preis basierenden Grafik den Darstellungsmodus ignorieren. Denn wenn die Bewegung innerhalb der Spanne liegt, ist es sehr wahrscheinlich, dass wir Probleme bekommen, weil der Stop-Loss-Auftrag am falschen Punkt liegt.

Deshalb ist es so wichtig, den Vermögenswert gut zu studieren und zu verstehen, um diese Art von Auslöser richtig zu entwickeln. Es geht darum, den Preis des Vermögenswertes zu erfassen, egal wie wir es tun. Aber wir brauchen diesen Wert wirklich. Sobald dies geschehen ist, ziehen wir diesen Preis von dem Punkt ab, an dem sich der Stop-Loss befindet. In diesem Fall spielt es keine Rolle, ob es sich um einen Verkauf oder einen Kauf handelt — der Wert wird automatisch in einen Wert in Punkten statt in Geld umgerechnet. Dieser Wert sollte mindestens doppelt so groß sein wie die Anzahl der Punkte, die wir immer bei jeder Aktualisierung auf dem Handelsserver berechnen. Wenn dies gelingt, verschieben wir die Stop-Loss-Position um die Anzahl der Punkte dieser Lücke. Das Intervall bleibt also immer gleich und der Zyklus beginnt von vorne.

Dieser Auslösemechanismus funktioniert in jeder Situation einwandfrei. Es gibt ein wichtiges Detail: Es ist unglaublich leicht und sehr schnell, was sehr wichtig ist. Denken Sie an diese Auslöser wie an eine Mausefalle. Wenn der Mechanismus zu kompliziert ist oder eine lange Ausführungszeit hat, schnappt sich die Maus schließlich den Käse und läuft weg, bevor die Falle ausgelöst wird.

Es gibt noch eine weitere Sache zu verstehen: Was sollte die Funktion oder die Ereignisbehandlung sein, die wir verwenden müssen, um die oben genannten Funktionen aufzurufen? Viele denken vielleicht, dass dies die Ereignisbehandlung der Funktion OnTick sein sollte. Richtig? Falsch. Um dies zu erklären, gehen wir zum nächsten Thema über.


Warum sollten wir OnTick nicht als Aufrufer für den Auslöser verwenden?

Ich weiß, dass es sehr verlockend sein kann, OnTick als Möglichkeit zum Aufruf einer anderen Funktion zu verwenden. Aber das ist zweifellos der größte Fehler, den man machen kann. Der richtige Weg ist die Verwendung des Ereignisses OnTime, wie unten gezeigt:

//+------------------------------------------------------------------+
int OnInit()
{
        manager = new C_Manager(def_MAGIC_NUMBER, user03, user02, user01, user04, user08);
        mouse = new C_Mouse(user05, user06, user07, user03, user02, user01);
        (*manager).CheckToleranceLevel();
        EventSetMillisecondTimer(100);

        return INIT_SUCCEEDED;
}
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
        delete manager;
        delete mouse;
        EventKillTimer();
}
//+------------------------------------------------------------------+
void OnTick() { }
//+------------------------------------------------------------------+
void OnTimer()
{
        (*manager).TriggerTrailingStop();
}
//+------------------------------------------------------------------+

Achten Sie darauf, dass wir in OnInit einen Wert vom Typ short definieren, damit die Ereignisbehandlung von OnTime() erzeugt wird. Wir tun dies mit der Funktion EventSetMillisecondTimer. In den meisten Fällen können Sie jeden beliebigen Wert ab 50 Millisekunden einstellen. Sie müssen aber auch daran denken, die Funktion EventKillTimer innerhalb des Ereignisses OnDeInit aufzurufen. Auf diese Weise wird der EA das OnTime-Ereignis auslösen und nicht dafür sorgen, dass die MetaTrader 5-Plattform das Ereignis weiterhin erzeugt. Selbst wenn es der Plattform gelingt, dieses Problem zu lösen und das Ereignis nicht mehr zu erzeugen, ist es eine gute Praxis, das System freizugeben. In diesem Fall werden wir also alle 100 Millisekunden, also etwa 10 Mal pro Sekunde, die im obigen Thema gezeigte Überprüfung unseres Auslösers haben.

Aber das ist keine Antwort auf die Frage: Warum sollten wir nicht die die Ereignisbehandlung OnTick als Trigger-Aufrufer verwenden? Die obige Erklärung beantwortet diese Frage nämlich nicht. Aber lassen Sie uns das genauer betrachten, um zu verstehen, warum wir das nicht tun sollten.

OnTick wird bei jedem Tick, den der Server schickt, aufgerufen. Wenn Sie sich das Balkendiagramm ansehen, selbst wenn es sich um ein 1-Minuten-Diagramm handelt, können Sie sich kein wirkliches Bild davon machen, was passiert. Dazu wäre es notwendig, sich auf die Ebene des HFT (Hochfrequenzhandel) zu begeben, besser bekannt als Feld für institutionelle Roboter.

Vielen ist nicht klar, dass dies bei einem EA, der auf einer Plattform läuft, die auf einem Computer installiert ist, der Hunderte von Kilometern vom Handelsserver entfernt ist, nicht möglich ist. Sie haben nicht die geringste Chance, mit einem HFT verglichen zu werden, der auf einem dedizierten Server ein paar Meter vom Server entfernt läuft. Der Grund dafür ist die so genannte Latenzzeit, d. h. die Zeit, die die Daten benötigen, um den Server zu erreichen. Diejenigen, die Online-Spiele spielen, werden verstehen, wovon ich spreche, aber für diejenigen, die damit nicht vertraut sind, möchte ich sagen, dass die Latenzzeit viel höher ist als die Häufigkeit der von einem Handelsserver durchgeführten Operationen. Mit anderen Worten, es ist unmöglich, eine mit HFT vergleichbare Leistung zu erreichen.

Um zum Thema OnTick zurückzukehren, können wir sagen, dass der Server in nur einer Millisekunde mehr als 10 solcher Ereignisse auslösen kann. Wenn so viele Ereignisse die Plattform erreichen, dass sie alle Ereignisse auslösen, die der Server ausgelöst hat, dann wird die Plattform abstürzen, sobald Ihr EA zu arbeiten beginnt. Das liegt daran, dass er mit der Bearbeitung von viel mehr Ereignissen beschäftigt sein wird, als sein Prozessor verarbeiten kann. Das bedeutet, dass, egal wie alles organisiert ist, er nicht in der Lage sein wird, wegen der großen Anzahl von Aufrufen die Funktion auszuführen, die der Auslöser für den Trailing-Stop ist.

Es kann vorkommen, dass dies eine Zeit lang nicht der Fall ist und die Plattform weiterhin reibungslos funktioniert. Sobald das System jedoch erkennt, dass es eine Position gibt, und es mit der Überprüfung des Auslösers beginnt, kann es zu ernsthaften Problemen kommen, da die Wahrscheinlichkeit eines Absturzes der Plattform sehr hoch ist.

Diese Wahrscheinlichkeit kann noch größer sein, wenn die Volatilität eines Vermögenswerts rasch zunimmt. Dies ist besonders bei Vermögenswerten wie Index-Futures üblich. Versuchen Sie also nicht, OnTick als Mittel zum Auslösen von Triggern zu verwenden. Vergessen Sie diese EA-Ereignishandlung. Sie ist nicht für uns Normalsterbliche gedacht, sondern für die HFTs, die den Handelsserver überwachen können.

Der beste Weg, das Triggersystem zu überprüfen, ist also die Verwendung des OnTime-Ereignisses. Es ist so konfiguriert, dass die Prüfungen in einer ausreichend kurzen Zeitspanne durchgeführt werden, sodass es keine Qualitätseinbußen gibt, und gleichzeitig die Möglichkeit besteht, alle Auslöser zu prüfen. Auf diese Weise erhalten wir einen sicheren, robusten, ruhigen und zuverlässigen EA.

Es gibt ein weiteres Problem im Zusammenhang mit der Auslösung von Trailing-Stop-Ereignissen. Einige EA-Systeme erreichen den Breakeven nicht, bevor der Trailing-Stop ausgelöst wird. Sie verwenden einen Trailing-Stop ab dem Zeitpunkt der Eröffnung einer Position. Das mag ein wenig seltsam erscheinen, aber tatsächlich geht es darum, dass das Betriebssystem oder die Konfiguration dieses Konzept bereits in sich trägt. In diesem Fall ist das System dem oben gezeigten sehr ähnlich, allerdings mit einem Unterschied in der verwendeten Abstand des Trailing-Stop. Das Konzept selbst bleibt jedoch gleich.

Neben diesem Modell gibt es noch ein weiteres, bei dem wir weder einen Trailing-Stop noch einen Breakeven haben. Wir haben eine andere Art von System, das einen Ausstieg garantiert, falls der Markt beginnt, sich gegen unsere Position zu entwickeln. Es handelt sich um eine andere Art von Auslöser, die wir in einem anderen Artikel behandeln werden.


Trailing-Stop und Breakeven mittels einer Pending Order

Das oben vorgestellte System funktioniert sehr gut, wenn Sie eine Position auf der Grundlage des OCO-Auftragssystems haben möchten. Es gibt jedoch ein Problem mit diesem OCO-Auftragssystem, nämlich die Volatilität. Wenn die Volatilität sehr hoch ist, können Aufträge übersprungen werden, was zu ungünstigen Situationen führen kann.

Es gibt eine „Lösung“ für dieses Problem, die darin besteht, eine Pending Order für einen garantierten Ausstieg zu verwenden. Diese „Lösung“ kann jedoch nicht in ein Hedging-System implementiert werden, d.h. sie kann nicht mit dem vorgestellten EA verwendet werden. Wenn wir versuchen, es auf einem Hedging-Konto zu verwenden, wird der EA aus dem Chart geworfen, weil er einen schweren Fehler für die C_Manager-Klasse macht. Wir können jedoch einige Anpassungen in der C_Manager-Klasse vornehmen, damit der EA zwei offene Positionen in entgegengesetzter Richtung auf dem Hedging-Konto haben kann. Es ist möglich, dasselbe zu tun, und ich zeige Ihnen, wie das geht, wenn Sie ein Netting-Konto verwenden.

Aber wenn Sie darüber nachdenken, macht es keinen Sinn, auf einem Netting-Konto eine Position in der Gegenrichtung zu eröffnen. Wenn der Auftragsserver den schwebenden Auftrag erfasst, ist es besser, dem Server eine Aufforderung zu senden, beide Positionen zu schließen. Dadurch verhält sich der EA auf einem Hedging-Konto wie auf einem Netting-Konto. Er schließt die offene Position mit einem schwebenden Auftrag.

Finden Sie diese Idee gut? Bevor Sie mit der Umsetzung beginnen, sind einige Details und Fragen zu beachten.

Erstens wird ein schwebender Auftrag nicht unbedingt zu dem von Ihnen angegebenen Zeitpunkt ausgeführt. Ein schwebender Auftrag wird auf den Preis reagieren, wo immer er sich befindet. Ein weiteres Problem besteht darin, dass bei einem schwerwiegenden Ausfall des EA und der Stornierung oder Schließung der Position oder des Auftrags die andere Seite möglicherweise ohne Gegenpartei dasteht.

Aber es gibt auch einen Vorteil: Wenn alles gut geht und die Verbindung zum Server unterbrochen wird, schließt ein schwebender Auftrag in der Gegenrichtung mit demselben Volumen die Position (Netting-Konten) oder hält den Preis fest (Hedging-Konten).

Wie Sie sehen, hat diese Methode ihre Vor- und Nachteile.

Um sie zu verwenden, müssen wir einige Änderungen an der Klasse C_Manager vornehmen. Diese Änderungen sind leicht zu verstehen und können rückgängig gemacht werden, wenn Sie sie nicht verwenden möchten. Allerdings gibt es hier ein wichtiges Detail: Wenn Sie diese Methode verwenden, achten Sie darauf, dass Sie keine Pending Orders löschen, die vom EA erteilt wurden. Wenn Sie dies tun, fehlt Ihnen der zweite Teil, das für den Abschluss zuständig ist.

Bei diesem System ist es nicht empfehlenswert, eine Änderung durch den Nutzer zuzulassen, sobald der Code kompiliert worden ist. Wenn er nicht geeignet ist, können Sie ihn vom EA abkoppeln. Sie sollte immer eingeschaltet oder ausgeschaltet bleiben. Zu diesem Zweck wird eine neue Definition im Code der Klasse C_Manager erstellt. Siehe unten:

//+------------------------------------------------------------------+
#define def_MAX_LEVERAGE                10
#define def_ORDER_FINISH                false
//+------------------------------------------------------------------+

Wenn diese Definition auf „true“ gesetzt ist, verwendet das System die Stop-Methode über eine Pending Order. Das bedeutet, dass Sie keinen Take-Profit-Punkt mehr haben werden, da dies die Dinge sehr verkompliziert und dazu führen kann, dass der Gegenauftrag nicht verfügbar ist. Wenn dieser Parameter auf false gesetzt ist, verwenden Sie die Breakeven- und die Trailing-Stop-Methode, die wir im ersten Teil dieses Artikels behandelt haben. Auf diese Weise verwenden Sie das OCO-Auftragssystem. Ich werde es standardmäßig auf false setzen. Wenn Sie die in diesem Thema beschriebene Methode verwenden möchten, ändern Sie diesen Wert von false auf true und kompilieren Sie den EA.

Nun müssen wir die Funktionen ändern, die Marktaufträge platzieren oder schwebende Aufträge erstellen. Sie werden so aussehen:

//+------------------------------------------------------------------+
                void CreateOrder(const ENUM_ORDER_TYPE type, const double Price)
                        {
                                if ((m_StaticLeverage >= def_MAX_LEVERAGE) || (m_TicketPending > 0) || (m_bAccountHedging && (m_Position.Ticket > 0))) return;
                                m_TicketPending = C_Orders::CreateOrder(type, Price, (def_ORDER_FINISH ? 0 : m_InfosManager.FinanceStop), (def_ORDER_FINISH ? 0 : m_InfosManager.FinanceTake), m_InfosManager.Leverage, m_InfosManager.IsDayTrade);
                        }
//+------------------------------------------------------------------+  
                void ToMarket(const ENUM_ORDER_TYPE type)
                        {
                                ulong tmp;
                                
                                if ((m_StaticLeverage >= def_MAX_LEVERAGE) || (m_bAccountHedging && (m_Position.Ticket > 0))) return;
                                tmp = C_Orders::ToMarket(type, (def_ORDER_FINISH ? 0 : m_InfosManager.FinanceStop), (def_ORDER_FINISH ? 0 : m_InfosManager.FinanceTake), m_InfosManager.Leverage, m_InfosManager.IsDayTrade);
                                m_Position.Ticket = (m_bAccountHedging ? tmp : (m_Position.Ticket > 0 ? m_Position.Ticket : tmp));
                        }
//+------------------------------------------------------------------+

Wir müssen diese Teile so ändern, dass die Take-Profit- und Stop-Loss-Kurse nicht tatsächlich erstellt werden. Wenn wir dies tun, wird der Server verstehen, dass diese Preise nicht erstellt werden und Sie werden einen Auftrag ohne Gewinn- und Verlustgrenzen haben. Sie haben vielleicht Angst, dass Sie einen Marktauftrag senden, der keinen Stop-Loss enthält, oder einen schwebenden Auftrag ohne Stop-Loss erteilen. Aber keine Sorge. Um zu verstehen, was wirklich vor sich geht, sollten Sie das System ausprobieren. Sobald dies geschehen ist, müssen wir die folgende Änderung vornehmen:

                void PendingToPosition(void)
                        {
                                ResetLastError();
                                if ((m_bAccountHedging) && (m_Position.Ticket > 0))
                                {
                                        if (def_ORDER_FINISH)
                                        {
                                                if (ClosePosition(m_Position.Ticket)) ZeroMemory(m_Position.Ticket);
                                                ClosePosition(m_TicketPending);                                         
                                        }else SetUserError(ERR_Unknown);
                                }else m_Position.Ticket = (m_Position.Ticket == 0 ? m_TicketPending : m_Position.Ticket);
                                m_TicketPending = 0;
                                if (_LastError != ERR_SUCCESS) UpdatePosition(m_Position.Ticket);
                                CheckToleranceLevel();
                        }

In diesem Fall, wenn der EA mitteilt, dass der schwebende Auftrag in eine Position umgewandelt wurde, kann es zu Problemen kommen, wenn wir ein Hedging-Konto führen und bereits eine offene Position haben. Wenn der Parameter in diesem Fall auf „false“ gesetzt wird, wird eine Fehlermeldung erzeugt. Wenn „true“ gesetzt ist, senden wir eine Anfrage, um die ursprüngliche Position zu schließen und ihren Speicherbereich zurückzusetzen. Wir werden auch die neu eröffnete Position schließen. Wenn die Verbindung zum Server gut ist, können wir beide Positionen erfolgreich schließen und so unser Engagement auf dem Markt auf Null reduzieren.

Nun muss das System einen schwebenden Auftrag erstellen, der als Auftrag zur Positionsschließung, d.h. als Stop-Loss, verwendet wird und im Auftragsbuch verbleibt. Dieser Punkt ist vielleicht der kritischste im gesamten System, denn wenn er nicht gut geplant ist, werden Sie ernsthafte Probleme mit dem Konto bekommen, das ohne eine Stop-Loss bleiben wird. Aus diesem Grund müssen Sie sich darüber im Klaren sein, was der EA tun wird, indem Sie Folgendes überprüfen:

Abbildung 01

Abbildung 01. Der Punkt, an dem wir schwebende Aufträge und offene Positionen sehen

Das in Abbildung 01 dargestellte Fenster muss immer geöffnet sein, damit Sie analysieren können, welche Aktionen der EA auf dem Server durchführt. Sie sollten einem EA nicht blind vertrauen, auch wenn er noch so zuverlässig erscheint.

Überwachen Sie stets die Absichten des EA. Damit der EA also einen Stop-Loss-Punkt hat, muss die Klasse C_Manager eine Pending Order erstellen. Sie wird wie folgt erstellt:

                void UpdatePosition(const ulong ticket)
                        {
                                int ret;
                                double price;
                                
                                if ((ticket == 0) || (ticket != m_Position.Ticket)) return;
                                if (PositionSelectByTicket(m_Position.Ticket))
                                {
                                        ret = SetInfoPositions();
                                        if (def_ORDER_FINISH)
                                        {
                                                price = m_Position.PriceOpen + (FinanceToPoints(m_InfosManager.FinanceStop, m_Position.Leverage) * (m_Position.IsBuy ? -1 : 1));
                                                if (m_TicketPending > 0) if (OrderSelect(m_TicketPending))
                                                {
                                                        price = OrderGetDouble(ORDER_PRICE_OPEN);
                                                        C_Orders::RemoveOrderPendent(m_TicketPending);
                                                }
                                                m_TicketPending = C_Orders::CreateOrder(m_Position.IsBuy ? ORDER_TYPE_SELL : ORDER_TYPE_BUY, price, 0, 0, m_Position.Leverage, m_InfosManager.IsDayTrade);
                                        }
                                        m_StaticLeverage += (ret > 0 ? ret : 0);
                                }else
				{
					ZeroMemory(m_Position);
                                	if (def_ORDER_FINISH)
					{
						RemoveOrderPendent(m_TicketPending);
						m_TicketPending = 0;
					}
				}
                                ResetLastError();
                        }

Zunächst wird geprüft, ob das Auftragssystem auf den Wert „true“ eingestellt ist. Wenn diese Bedingung erfüllt ist, berechnen wir den Preispunkt für den Beginn des Aufbaus und verwenden den schwebenden Auftrag als Positionsstopp. Als Nächstes prüfen wir, ob wir bereits eine Auftrag abgesendet haben. Wenn dies der Fall ist, halten wir den Preis dort fest, wo er steht, und entfernen den Auftrag aus dem Buch. Auf die eine oder andere Weise werden wir versuchen, einen neuen schwebenden Auftrag zu erstellen. Wenn alles korrekt ist, haben wir eine schwebende Order im Orderbuch, die als Stop-Loss-Order dient.

Achtung! Wenn die Position geschlossen wird, muss der schwebende Auftrag storniert werden. Dies kann mit den folgenden Codezeilen erreicht werden.

Jetzt müssen wir die Funktion SetInfoPosition ändern, um eine korrekte Angabe darüber zu erhalten, ob wir einen Breakeven machen müssen oder ob wir sofort mit einem Trailing-Stop beginnen können. Die neue Funktion lautet wie folgt:

inline int SetInfoPositions(void)
                        {
                                double v1, v2;
                                int tmp = m_Position.Leverage;
                                
                                m_Position.Leverage = (int)(PositionGetDouble(POSITION_VOLUME) / GetTerminalInfos().VolMinimal);
                                m_Position.IsBuy = ((ENUM_POSITION_TYPE) PositionGetInteger(POSITION_TYPE)) == POSITION_TYPE_BUY;
                                m_Position.TP = PositionGetDouble(POSITION_TP);
                                v1 = m_Position.SL = PositionGetDouble(POSITION_SL);
                                v2 = m_Position.PriceOpen = PositionGetDouble(POSITION_PRICE_OPEN);
                                if (def_ORDER_FINISH) if (m_TicketPending > 0) if (OrderSelect(m_TicketPending)) v1 = OrderGetDouble(ORDER_PRICE_OPEN);
                                m_Position.EnableBreakEven = (def_ORDER_FINISH ? m_TicketPending == 0 : m_Position.EnableBreakEven) || (m_Position.IsBuy ? (v1 < v2) : (v1 > v2));
                                m_Position.Gap = FinanceToPoints(m_Trigger, m_Position.Leverage);

                                return m_Position.Leverage - tmp;
                        }

Hier führen wir eine Reihe von Tests durch, um den Preis zu ermitteln, bei dem sich der schwebende Auftrag befindet. Wenn das Ticket für ausstehende Aufträge aus irgendeinem Grund noch nicht erstellt wurde, wird der Breakeven-Indikator für unsere Zwecke ordnungsgemäß gestartet.

Bisher können in dem System, in dem ein schwebender Auftrag als Stop-Punkt verwendet wird, weder Breakeven noch Trailing-Stop tatsächlich verwendet werden. Um dieses System zu implementieren, brauchen wir den Auslöser nicht wirklich, da er bereits konfiguriert ist: Wir müssen nur ein System zum Verschieben von ausstehenden Aufträgen implementieren. Für Breakeven, um die Bewegung zu realisieren, werden wir folgendes hinzufügen:

inline void TriggerBreakeven(void)
                        {
                                double price;
                                
                                if (PositionSelectByTicket(m_Position.Ticket))
                                        if (PositionGetDouble(POSITION_PROFIT) >= m_Trigger)
                                        {
                                                price = m_Position.PriceOpen + (GetTerminalInfos().PointPerTick * (m_Position.IsBuy ? 1 : -1));
                                                if (def_ORDER_FINISH)
                                                {
                                                        if (m_TicketPending > 0) m_Position.EnableBreakEven = !ModifyPricePoints(m_TicketPending, price, 0, 0);
                                                }else m_Position.EnableBreakEven = !ModifyPricePoints(m_Position.Ticket, m_Position.PriceOpen, price, m_Position.TP);
                                        }
                        }

In der vorherigen Version dieser Funktion, die für die Ausführung des Breakeven verantwortlich war, entsprach der Preis der Stop-Order genau dem Eröffnungskurs der Position. Viele Händler möchten immer einen, wenn auch kleinen, Gewinn mitnehmen. Daher habe ich beschlossen, die Funktion so zu ändern, dass der EA bei Auslösung des Breakeven eine um 1 Tick vom Eröffnungskurs verschobene Order platziert. Der Händler erhält also mindestens 1 Tick Gewinn.

Dieses System funktioniert für jeden Vermögenswert und jeden Markttyp, da wir Daten aus dem Vermögenswert selbst verwenden, um zu wissen, wo die Stopp-Linie liegen sollte.

Sobald dies geschehen ist, prüfen wir, ob wir das Stopp-Modell auf der Grundlage eines schwebenden Auftrags verwenden. Wenn dies der Fall ist, prüfen wir, ob die Variable „Pending Order“ einen Wert enthält. Liegt ein solcher Wert vor, senden wir eine Aufforderung, den Punkt, an dem der Auftrag erteilt wurde, auf eine neue Position zu ändern. Wenn wir ein System verwenden, das auf einem OCO-Auftrag basiert, werden wir die oben beschriebene Methode anwenden. Diese Prüfungen mögen sinnlos erscheinen, aber sie verhindern, dass wir ungültige Anfragen an den Server senden.

Schauen wir uns nun an, wie die Trailing-Stop-Funktion in diesem Fall funktioniert:

inline void TriggerTrailingStop(void)
                        {
                                double price, v1;
                                
                                if ((m_Position.Ticket == 0) || (def_ORDER_FINISH ? m_TicketPending == 0 : m_Position.SL == 0)) return;
                                if (m_Position.EnableBreakEven) TriggerBreakeven(); else
                                {
                                        price = SymbolInfoDouble(_Symbol, (GetTerminalInfos().ChartMode == SYMBOL_CHART_MODE_LAST ? SYMBOL_LAST : (m_Position.IsBuy ? SYMBOL_ASK : SYMBOL_BID)));
                                        v1 = m_Position.SL;
                                        if (def_ORDER_FINISH) if (OrderSelect(m_TicketPending)) v1 = OrderGetDouble(ORDER_PRICE_OPEN);
                                        if (v1 > 0) if (MathAbs(price - v1) >= (m_Position.Gap * 2)) 
                                        {
                                                price = v1 + (m_Position.Gap * (m_Position.IsBuy ? 1 : -1));
                                                if (def_ORDER_FINISH) ModifyPricePoints(m_TicketPending, price, 0, 0);
                                                else ModifyPricePoints(m_Position.Ticket, m_Position.PriceOpen, price, m_Position.TP);
                                        }
                                }
                        }

Diese Funktion mag auf den ersten Blick seltsam erscheinen, da ich Dinge verwendet habe, die für die meisten Menschen ungewöhnlich sind. Schauen wir mal, was hier los ist. Zuerst prüfen wir, ob wir eine offene Position haben, das ist der einfachste Teil. Aber das Seltsame hier ist der ternärer Operator, dessen Verwendung hier recht ungewöhnlich ist. Hier wird unterschieden, ob die Prüfung durch das Pending Ticket oder durch den Stop-Loss-Kurswert erfolgt. Dieser Vergleich wird in Verbindung mit dem Positionsvergleich verwendet. Wenn die Tests ergeben, dass nichts getan werden muss, kehren wir einfach zur aufrufenden Funktion zurück.

Wenn etwas getan werden muss, prüfen wir den Wert von Breakeven. Wenn er bereits ausgeführt wurde, beginnen wir mit der Erfassung des aktuellen Vermögenspreises, um zu prüfen, ob wir den Trailing-Stop aktivieren können. Da wir den Preis benötigen, zu dem die Pending-Order platziert wird, und der Faktor derselbe sein wird, beginnen wir die temporäre Variable mit dem möglichen Wert, der als Stop-Loss-Linie verwendet werden könnte. Es kann aber auch vorkommen, dass wir einen schwebenden Auftrag als Stopp-Punkt verwenden. In diesem Fall muss er sich bewegen, damit wir den Preis dort erfassen, wo er ist. Wir führen das Factoring durch, um zu prüfen, ob wir die Stopp-Stufe verschieben sollten. Wenn dies möglich ist, passen wir den Preis an, zu dem sich der Auftrag bewegen wird, und verschieben den schwebenden Auftrag oder die Stop-Loss-Linie entsprechend. Was sich tatsächlich bewegt, hängt von dem System ab, das wir verwenden.

Das folgende Video zeigt dieses System in Betrieb. Diejenigen, die sich vorstellen, dass dies etwas anderes oder nicht funktionell ist, sollten sich das Video ansehen und ihre eigenen Schlüsse ziehen. Am besten ist es jedoch, den EA zu kompilieren und eigene Tests auf einem DEMO-Konto durchzuführen, um zu verstehen, was vor sich geht. Auf diese Weise wird das Verständnis des gesamten Systems solider und klarer.

Video 01. Demonstration des Systems „Stop-Loss“ mittels eines schwebenden Auftrags.


Schlussfolgerung

In diesem Artikel habe ich das einfachste Triggersystem behandelt, das in einen EA eingebaut werden kann, den viele Leute mögen oder in ihrem EA haben wollen. Dieses System ist jedoch nicht geeignet, wenn Sie den EA in einem Portfolio-Setup verwenden möchten. In diesem Fall würde ich zu einem manuellen System raten. Aber das ist nur mein Rat, denn ich weiß nicht genau, wie Sie dieses Wissen nutzen werden.

Der Anhang enthält den vollständigen Code, den Sie studieren können, um mehr über diese Art von Mechanismus zu erfahren. In diesem Code haben Sie zunächst einen EA, der eine Stopplinie verwendet. Um eine Pending Order für den Stop zu verwenden, müssen Sie den EA wie in diesem Artikel beschrieben modifizieren.

Jetzt haben wir das Minimum an Automatisierung, um die restlichen Schritte durchzuführen. Im nächsten Artikel werden wir uns ansehen, wie man einen 100% automatisierten EA erstellt. Viel Glück!

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

Beigefügte Dateien |
Erstellen eines EA, der automatisch funktioniert (Teil 10): Automatisierung (II) Erstellen eines EA, der automatisch funktioniert (Teil 10): Automatisierung (II)
Automatisierung bedeutet nichts, wenn Sie den Zeitplan nicht kontrollieren können. Kein Arbeitnehmer kann effizient sein, wenn er 24 Stunden am Tag arbeitet. Viele sind jedoch der Meinung, dass ein automatisiertes System 24 Stunden am Tag funktionieren sollte. Aber es ist immer gut, eine Möglichkeit zu haben, einen Arbeitsbereich für den EA festzulegen. In diesem Artikel geht es darum, wie man einen solchen Zeitbereich richtig festlegt.
Algorithmen zur Optimierung mit Populationen: Der Affen-Algorithmus (Monkey Algorithmus, MA) Algorithmen zur Optimierung mit Populationen: Der Affen-Algorithmus (Monkey Algorithmus, MA)
In diesem Artikel werde ich den Optimierungsalgorithmus Affen-Algorithmus (MA, Monkey Algorithmus) betrachten. Die Fähigkeit dieser Tiere, schwierige Hindernisse zu überwinden und die unzugänglichsten Baumkronen zu erreichen, bildete die Grundlage für die Idee des MA-Algorithmus.
Erstellen eines EA, der automatisch funktioniert (Teil 11): Automatisierung (III) Erstellen eines EA, der automatisch funktioniert (Teil 11): Automatisierung (III)
Ein automatisiertes System wird ohne angemessene Sicherheit nicht erfolgreich sein. Die Sicherheit wird jedoch nicht gewährleistet sein, wenn man bestimmte Dinge nicht richtig versteht. In diesem Artikel werden wir untersuchen, warum es so schwierig ist, ein Maximum an Sicherheit in automatisierten Systemen zu erreichen.
Neuronale Netze leicht gemacht (Teil 36): Relationales Verstärkungslernen Neuronale Netze leicht gemacht (Teil 36): Relationales Verstärkungslernen
In den Verstärkungslernmodellen, die wir im vorherigen Artikel besprochen haben, haben wir verschiedene Varianten von Faltungsnetzwerken verwendet, die in der Lage sind, verschiedene Objekte in den Originaldaten zu identifizieren. Der Hauptvorteil von Faltungsnetzen ist die Fähigkeit, Objekte unabhängig von ihrer Position zu erkennen. Gleichzeitig sind Faltungsnetzwerke nicht immer leistungsfähig, wenn es zu verschiedenen Verformungen von Objekten und Rauschen kommt. Dies sind die Probleme, die das relationale Modell lösen kann.