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

Erstellen eines EA, der automatisch funktioniert (Teil 03): Neue Funktionen

MetaTrader 5Handel | 28 Februar 2023, 08:46
736 0
Daniel Jose
Daniel Jose

Einführung

Im vorangegangenen Artikel „Erstellen eines EA, der automatisch funktioniert (Teil 02): Erste Schritte mit dem Code“ haben wir begonnen, ein Auftragssystem zu entwickeln, das wir in unserem automatisierten EA verwenden werden. Wir haben jedoch nur eine der benötigten Funktionen geschaffen.

In der Regel benötigt das Auftragssystem einige zusätzliche Funktionen, um vollautomatisch arbeiten zu können. Darüber hinaus ziehen es manche Leute vor, mehrere EAs zu verwenden, die mit unterschiedlichen Einstellungen, aber mit demselben Vermögenswert arbeiten.

Dies wird für Konten mit dem Buchhaltungssystem NETTING nicht empfohlen. Der Grund dafür ist, dass der Handelsserver den so genannten durchschnittlichen Positionspreis erstellt, was dazu führen kann, dass ein EA versucht zu verkaufen und ein andere versucht zu kaufen. Dies führt natürlich dazu, dass beide EAs ‚sich bekämpfen‘ und ihre Einlage in kurzer Zeit verloren geht. Dies ist bei HEDGING-Konten nicht der Fall. Bei solchen Konten kann ein EA verkaufen, während der andere kaufen kann. Ein Auftrag hebt den anderen nicht auf.

Manche Leute handeln sogar mit demselben Vermögenswert wie der EA — aber das muss auf einem HEDGING-Konto geschehen. Lassen Sie im Zweifelsfall niemals zwei EAs mit demselben Vermögenswert handeln (zumindest nicht bei demselben Broker), und handeln Sie niemals mit demselben Vermögenswert, während ein automatisierter EA läuft.

Nun, da Sie gewarnt wurden, können wir dem EA weitere notwendige Funktionen hinzufügen, die mehr als 90 % der Fälle abdecken werden.


Warum brauchen wir neue Funktionen?

Wenn ein automatisierter EA einen Handel tätigt, eröffnet und schließt er mit Markt-Orders, d. h. der EA stellt nur selten einen schwebenden Auftrag in das Orderbuch. Es gibt jedoch Situationen, in denen es notwendig ist, einen Auftrag im Orderbuch zu platzieren. Das Verfahren zur Durchführung einer solchen Platzierung ist eines der schwierigsten. Aus diesem Grund war der vorherige Artikel ausschließlich der Implementierung einer solchen Funktion gewidmet. Aber das ist nicht genug für den automatisierten EA. Denn in den meisten Fällen handelt es sich um Markteintritte und -austritte. Daher benötigen wir mindestens zwei zusätzliche Funktionen:

  • Einer von ihnen wird Kauf- oder Verkaufsaufträge zum Marktpreis erteilen;
  • Mit der anderen können wir den Eröffnungspreis eines Auftrags ändern.

Nur diese beiden Funktionen, zusammen mit der im vorherigen Artikel besprochenen, sind das, was Sie wirklich in einem automatisierten EA brauchen. Jetzt wollen wir sehen, warum wir nur diese beiden Funktionen hinzufügen müssen und nichts weiter. Wenn der EA also eine Position eröffnet, sei es eine Verkaufs- oder eine Kaufposition, wird er dies oft zum Marktpreis mit einem vom Händler vorgegebenen Volumen tun.

Wenn Sie eine Position schließen müssen, können Sie dies auf zwei Arten tun: Die erste ist zum Marktpreis, indem Sie eine Position in die entgegengesetzte Richtung und mit demselben Volumen tätigen. Dies führt jedoch nicht oft zum Schließen der Position. Eigentlich funktioniert das nur mit einem NETTING-Konto. Bei einem HEDGING-Konto wird eine Position durch diese Operation nicht geschlossen. In diesem Fall benötigen wir einen klaren Auftrag zur Schließung der bestehenden Position.

Obwohl Sie durch die Eröffnung eines Handels in die entgegengesetzte Richtung mit demselben Volumen im HEDGING-Konto den Preis festschreiben können, bedeutet dies nicht, dass die Position tatsächlich geschlossen wird. Die Preisfestsetzung bringt weder Gewinn noch Verlust. Aus diesem Grund werden wir die Möglichkeit hinzufügen, dass der EA die Position schließt, was die Implementierung der dritten Funktion erfordert. Hinweis: Bei einem NETTING-Konto können Sie einfach einen Marktauftrag mit demselben Volumen, aber in die entgegengesetzte Richtung senden, und die Position wird geschlossen.

Jetzt brauchen wir eine Position, um den Auftragspreis ändern zu können. In einigen operativen Modellen funktioniert der EA wie folgt: Er eröffnet eine Marktposition und sendet sofort eine ausstehende STOP-LOSS-Order. Dieser Auftrag wird in das Orderbuch aufgenommen und bleibt dort so lange, bis die Position geschlossen wird. In diesem Fall sendet der EA weder einen Schließauftrag noch eine andere Art von Anfrage. Es wird einfach die Reihenfolge im Buch so verwalten, dass die Abschlussreihenfolge immer aktiv ist.

Dies funktioniert gut für NETTING-Konten, aber für HEDGING-Konten wird dieses System nicht wie erwartet funktionieren: In diesem Fall wird der Auftrag im Auftragsbuch einfach das tun, was ich bereits oben über die Art und Weise erklärt habe, wie der Handel geschlossen wird. Aber zurück zum Thema. Dasselbe Verfahren, mit dem der Auftrag im Auftragsbuch verwaltet wird, kann auch verwendet werden, um die Take-Profit- und Stop-Loss-Preise in einem OCO-Auftrag zu verschieben („one cancels the other“ oder „einer löscht den anderen (Auftrag)“).

Normalerweise verwendet ein automatisierter EA keine OCO-Aufträge, er arbeitet einfach mit einem Marktauftrag und mit dem Auftrag im Orderbuch. Bei HEDGING-Konten kann dieser Mechanismus jedoch mit einem OCO-Auftrag durchgeführt werden, wobei lediglich ein Stop-Loss-Preis festgelegt wird. Wenn der Programmierer möchte, kann er auch einfach eine Market-Order absenden und den EA den Markt auf irgendeine Weise beobachten lassen. Sobald ein bestimmter Punkt oder ein bestimmtes Preisniveau erreicht ist, sendet der EA einen Schließauftrag.

Ich habe dies alles nur erklärt, um zu zeigen, dass es verschiedene Möglichkeiten gibt, die gleiche Sache zu tun - in unserem Fall eine Position zu schließen. Die Eröffnung einer Position ist der einfachste Teil des Prozesses. Das Schließen ist ein kniffliger Teil, da Sie Folgendes berücksichtigen müssen:

  • Mögliche Momente hoher Volatilität, in denen Aufträge „springen“ können (im Falle von OCO-Aufträgen). Die Aufträge im Auftragsbuch mit Ausnahme der Aufträge STOP LIMIT werden niemals springen, sie können außerhalb des gewünschten Punktes ausgelöst werden, aber sie springen niemals.
  • Verbindungsprobleme, wenn der EA eine Zeit lang nicht auf den Server zugreifen kann;
  • Probleme im Zusammenhang mit der Liquidität, wenn ein Auftrag zwar auf seine Ausführung wartet, das Volumen aber nicht ausreicht, um ihn tatsächlich auszuführen;
  • Und das Schlimmste: Der EA kann anfangen, Aufträge nach dem Zufallsprinzip auszuführen.

All diese Punkte müssen bei der Erstellung eines automatisierten EA berücksichtigt und beachtet werden. Es gibt auch andere Punkte, zum Beispiel fügen einige Programmierer EA-Tätigkeitszeiten hinzu. Was dies betrifft, so will ich ehrlich sein. Dies ist völlig nutzlos. Obwohl in einem anderen Artikel gezeigt wird, wie es geht, empfehle ich dies nicht, und hier ist der Grund dafür.

Stellen Sie sich Folgendes vor: Sie wissen nicht, wie man handelt, Sie haben einen automatisierten EA, der dies tut, Sie legen einen bestimmten Zeitplan fest. Jetzt denken Sie, Sie können sich entspannen und etwas anderes tun... Falsch. Lassen Sie niemals, ich wiederhole, niemals einen EA, selbst einen automatisierten, unbeaufsichtigt. NIEMALS. Während er läuft, sollten Sie oder eine Person Ihres Vertrauens in der Nähe sein und beobachten, wie er funktioniert.

Wenn Sie den EA-Lauf unbeaufsichtigt lassen, öffnen Sie die Tür für Probleme. Das Hinzufügen von Scheduling-Methoden oder Triggern zum Starten und Beenden des EA ist das Dümmste, was man bei der Verwendung eines automatisierten EA tun kann. Bitte tun Sie das nicht. Wenn Sie wollen, dass der EA für Sie arbeitet, dann schalten Sie ihn ein und beobachten Sie ihn. Wenn Sie gehen müssen, schalten Sie es aus und tun Sie, was Sie tun müssen. Lassen Sie einen EA niemals unbeaufsichtigt und auf eigene Faust handeln. Tun Sie dies nicht, können die Ergebnisse sehr enttäuschend sein.

Implementierung der erforderlichen Funktionen 

Da das Verfahren für die Ausführung eines Marktauftrags dem Verfahren für die Übermittlung eines schwebenden Auftrags sehr ähnlich ist, können wir ein allgemeines Verfahren erstellen, sodass alle Felder, die denselben Fülltyp haben, ausgefüllt werden. Nur vorgangsspezifische Felder werden lokal ausgefüllt. Schauen wir uns also an, wie diese Funktion des gemeinsamen Ausfüllens funktioniert, sie ist unten zu sehen:

inline void CommonData(const ENUM_ORDER_TYPE type, const double Price, const double FinanceStop, const double FinanceTake, const uint Leverage, const bool IsDayTrade)
                        {
                                double Desloc;
                                
                                ZeroMemory(m_TradeRequest);
				m_TradeRequest.magic		= m_Infos.MagicNumber;
                                m_TradeRequest.symbol           = _Symbol;
                                m_TradeRequest.volume           = NormalizeDouble(m_Infos.VolMinimal + (m_Infos.VolStep * (Leverage - 1)), m_Infos.nDigits);
                                m_TradeRequest.price            = NormalizeDouble(Price, m_Infos.nDigits);
                                Desloc = FinanceToPoints(FinanceStop, Leverage);
                                m_TradeRequest.sl               = NormalizeDouble(Desloc == 0 ? 0 : Price + (Desloc * (type == ORDER_TYPE_BUY ? -1 : 1)), m_Infos.nDigits);
                                Desloc = FinanceToPoints(FinanceTake, Leverage);
                                m_TradeRequest.tp               = NormalizeDouble(Desloc == 0 ? 0 : Price + (Desloc * (type == ORDER_TYPE_BUY ? 1 : -1)), m_Infos.nDigits);
                                m_TradeRequest.type_time        = (IsDayTrade ? ORDER_TIME_DAY : ORDER_TIME_GTC);
                                m_TradeRequest.stoplimit        = 0;
                                m_TradeRequest.expiration       = 0;
                                m_TradeRequest.type_filling     = ORDER_FILLING_RETURN;
                                m_TradeRequest.deviation        = 1000;
                                m_TradeRequest.comment          = "Order Generated by Experts Advisor.";
                        }

Beachten Sie, dass alles, was wir in dieser gemeinsamen Funktion haben, auch in der Funktion zur Erstellung ausstehender Aufträge vorhanden war, die wir im vorherigen Artikel betrachtet haben. Aber ich habe auch ein kleines Extra hinzugefügt, das es vorher nicht gab, das aber sehr nützlich sein kann, wenn Sie mit einem HEDGING-Konto arbeiten oder einen EA erstellen wollen, der nur die von ihm erstellten Aufträge beobachtet. Dies ist die so genannte „magic number“ oder magische Zahl. Normalerweise verwende ich diese Nummer nicht, aber wenn Sie dies tun, haben Sie bereits die Möglichkeit, sie zu unterstützen.

Sehen wir uns also diese neue Funktion an, die für das Senden eines schwebende Auftrags zuständig ist:

                ulong CreateOrder(const ENUM_ORDER_TYPE type, const double Price, const double FinanceStop, const double FinanceTake, const uint Leverage, const bool IsDayTrade)
                        {
                                double  bid, ask, Desloc;                               
                                
                                Price = AdjustPrice(Price);
                                bid = SymbolInfoDouble(_Symbol, (m_Infos.PlotLast ? SYMBOL_LAST : SYMBOL_BID));
                                ask = (m_Infos.PlotLast ? bid : SymbolInfoDouble(_Symbol, SYMBOL_ASK));
                                CommonData(type, AdjustPrice(Price), FinanceStop, FinanceTake, Leverage, IsDayTrade);
                                m_TradeRequest.action   = TRADE_ACTION_PENDING;
                                m_TradeRequest.type     = (type == ORDER_TYPE_BUY ? (ask >= Price ? ORDER_TYPE_BUY_LIMIT : ORDER_TYPE_BUY_STOP) : 
                                                                                    (bid < Price ? ORDER_TYPE_SELL_LIMIT : ORDER_TYPE_SELL_STOP));                              
                                ZeroMemory(m_TradeRequest);
                                m_TradeRequest.action           = TRADE_ACTION_PENDING;
                                m_TradeRequest.symbol           = _Symbol;
                                m_TradeRequest.volume           = NormalizeDouble(m_Infos.VolMinimal + (m_Infos.VolStep * (Leverage - 1)), m_Infos.nDigits);
                                m_TradeRequest.type             = (type == ORDER_TYPE_BUY ? (ask >= Price ? ORDER_TYPE_BUY_LIMIT : ORDER_TYPE_BUY_STOP) : 
                                                                                            (bid < Price ? ORDER_TYPE_SELL_LIMIT : ORDER_TYPE_SELL_STOP));
                                m_TradeRequest.price            = NormalizeDouble(Price, m_Infos.nDigits);
                                Desloc = FinanceToPoints(FinanceStop, Leverage);
                                m_TradeRequest.sl               = NormalizeDouble(Desloc == 0 ? 0 : Price + (Desloc * (type == ORDER_TYPE_BUY ? -1 : 1)), m_Infos.nDigits);
                                Desloc = FinanceToPoints(FinanceTake, Leverage);
                                m_TradeRequest.tp               = NormalizeDouble(Desloc == 0 ? 0 : Price + (Desloc * (type == ORDER_TYPE_BUY ? 1 : -1)), m_Infos.nDigits);
                                m_TradeRequest.type_time        = (IsDayTrade ? ORDER_TIME_DAY : ORDER_TIME_GTC);
                                m_TradeRequest.type_filling     = ORDER_FILLING_RETURN;
                                m_TradeRequest.deviation        = 1000;
                                m_TradeRequest.comment          = "Order Generated by Experts Advisor.";
                                
                                return (((type == ORDER_TYPE_BUY) || (type == ORDER_TYPE_SELL)) ? ToServer() : 0);
                        };

Alle durchgestrichenen Teile sind aus dem Code entfernt worden, da diese Felder von der gemeinsamen Funktion ausgefüllt werden. Was wir wirklich tun müssen, ist diese beiden Werte anzupassen, und das Auftragssystem wird einen schwebenden Auftrag erstellen, wie im vorherigen Artikel gezeigt.

Sehen wir uns nun an, was wir tatsächlich programmieren müssen, um ein Auftragssystem zu schaffen, das in der Lage ist, Ausführungsaufträge zum Marktpreis zu senden. Der erforderliche Code ist nachstehend aufgeführt:

                ulong ToMarket(const ENUM_ORDER_TYPE type, const double FinanceStop, const double FinanceTake, const uint Leverage, const bool IsDayTrade)
                        {
                                CommonData(type, SymbolInfoDouble(_Symbol, (type == ORDER_TYPE_BUY ? SYMBOL_ASK : SYMBOL_BID)), FinanceStop, FinanceTake, Leverage, IsDayTrade);
                                m_TradeRequest.action   = TRADE_ACTION_DEAL;
                                m_TradeRequest.type     = type;

                                return (((type == ORDER_TYPE_BUY) || (type == ORDER_TYPE_SELL)) ? ToServer() : 0);
                        };

Sehen Sie, wie einfach es ist: Alles, was Sie im Vergleich zum schwebenden Auftrag ändern müssen, sind nur diese beiden Dinge. Auf diese Weise ist gewährleistet, dass der Server immer kompatible Daten erhält, da sich nur die Art der Anfrage ändert.

Somit ist die Funktionsweise, sowohl was die Analyse der Rendite als auch die Art und Weise des Aufrufs der Prozeduren für die Platzierung eines schwebenden Auftrags oder die Ausführung einer Markttransaktion betrifft, nahezu identisch. Der einzige wirkliche Unterschied für diejenigen, die das Verfahren aufrufen, besteht darin, dass Sie bei der Ausführung eines Marktauftrags keinen Preis angeben müssen, da die Klasse den Wert korrekt ausfüllt, während bei einem schwebenden Auftrag ein Preis angegeben werden muss. Abgesehen davon werden alle Vorgänge unterschiedlich sein.

Nun wollen wir sehen, was sich im System geändert hat. Da wir einen Wert hinzugefügt haben, der als magische Zahl verwendet werden soll, müssen wir eine Klasse erstellen, die diesen Wert erhält. Dies sollte im Konstruktor der Klasse geschehen. So sieht der Konstruktor jetzt aus:

                C_Orders(const ulong magic = 0)
                        {
                                m_Infos.MagicNumber     = magic;
                                m_Infos.nDigits         = (int)SymbolInfoInteger(_Symbol, SYMBOL_DIGITS);
                                m_Infos.VolMinimal      = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MIN);
                                m_Infos.VolStep         = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_STEP);
                                m_Infos.PointPerTick    = SymbolInfoDouble(_Symbol, SYMBOL_TRADE_TICK_SIZE);
                                m_Infos.ValuePerPoint   = SymbolInfoDouble(_Symbol, SYMBOL_TRADE_TICK_VALUE);
                                m_Infos.AdjustToTrade   = m_Infos.PointPerTick / m_Infos.ValuePerPoint;
                                m_Infos.PlotLast        = (SymbolInfoInteger(_Symbol, SYMBOL_CHART_MODE) == SYMBOL_CHART_MODE_LAST);
                        };

Schauen wir uns an, was im obigen Code passiert. Wenn wir einen Standardwert deklarieren, wie hier geschehen, müssen wir zum Zeitpunkt der Erstellung der Klasse nicht darüber informieren, also machen wir diesen Konstruktor so, als ob er standardmäßig wäre (in dem kein Typ von Argument empfangen wird).

Wenn Sie jedoch den Nutzer der Klasse (d.h. den Programmierer) zwingen wollen, während der Phase, in der die Klasse erstellt wird, mitzuteilen, welche Werte verwendet werden sollen, entfernen Sie den Wert des Parameters. Wenn der Compiler also versucht, den Code zu generieren, wird er feststellen, dass etwas fehlt, und er wird fragen, welche Werte er verwenden soll. Das funktioniert aber nur, wenn die Klasse keinen anderen Konstruktor enthält. Dies ist ein kleines Detail, das viele Leute nicht verstehen können: warum wir manchmal Werte angeben müssen und manchmal nicht.

Wie Sie sehen, kann das Programmieren sehr interessant sein. In vielen Fällen geht es darum, eine Lösung mit möglichst geringem Aufwand zu erstellen, damit nicht so viele Dinge programmiert und getestet werden müssen. Aber wir haben die Klasse C_Orders noch nicht fertiggestellt. Es braucht noch eine erforderliche und eine optionale Funktion, die wir aufgrund des Unterschieds zwischen HEDGING- und NETTING-Konten noch erstellen werden. Weiter geht's:

                bool ModifyPricePoints(const ulong ticket, const double Price, const double PriceStop, const double PriceTake)
                        {
                                ZeroMemory(m_TradeRequest);
                                m_TradeRequest.symbol   = _Symbol;
                                if (OrderSelect(ticket))
                                {
                                        m_TradeRequest.action   = (Price > 0 ? TRADE_ACTION_MODIFY : TRADE_ACTION_REMOVE);
                                        m_TradeRequest.order    = ticket;
                                        if (Price > 0)
                                        {
                                                m_TradeRequest.price      = NormalizeDouble(AdjustPrice(Price), m_Infos.nDigits);
                                                m_TradeRequest.sl         = NormalizeDouble(AdjustPrice(PriceStop), m_Infos.nDigits);
                                                m_TradeRequest.tp         = NormalizeDouble(AdjustPrice(PriceTake), m_Infos.nDigits);
                                                m_TradeRequest.type_time  = (ENUM_ORDER_TYPE_TIME)OrderGetInteger(ORDER_TYPE_TIME) ;
                                                m_TradeRequest.expiration = 0;
                                        }
                                }else if (PositionSelectByTicket(ticket))
                                {
                                        m_TradeRequest.action   = TRADE_ACTION_SLTP;
                                        m_TradeRequest.position = ticket;
                                        m_TradeRequest.tp       = NormalizeDouble(AdjustPrice(PriceTake), m_Infos.nDigits);
                                        m_TradeRequest.sl       = NormalizeDouble(AdjustPrice(PriceStop), m_Infos.nDigits);
                                }else return false;
                                ToServer();
                                
                                return (_LastError == ERR_SUCCESS);
                        };

Das obige Verfahren ist äußerst wichtig, sogar noch wichtiger als das nächste, das wir noch sehen werden. Der Grund dafür ist, dass dieses Verfahren für die Manipulation von Preispositionen verantwortlich ist, unabhängig davon, ob es sich um Orderbuchpositionen im Falle eines schwebenden Auftrags oder um die Limits im Falle einer offenen Position handelt. Die obige Funktion ist so leistungsfähig, dass sie Order- oder Positionslimits erstellen oder aufheben kann. Um zu verstehen, wie das Ganze funktioniert, sollten wir den internen Code analysieren. Dies wird Ihnen helfen zu verstehen, wie Sie diese Funktion richtig nutzen können.

Um die Erklärung einfacher und verständlicher zu machen, werde ich alles in einzelne Abschnitte unterteilen, damit Sie sich nicht in der Erklärung verlieren.

Zunächst sollten wir Folgendes verstehen: Wenn Sie einen Auftrag übermitteln, sei es ein schwebender Auftrag für das Orderbuch oder ein Marktauftrag, erhalten Sie einen Rückgabewert. Wenn bei der Anfrage kein Fehler aufgetreten ist, ist dieser Wert ungleich Null. Dieser Wert sollte jedoch nicht ignoriert werden, da der von den Funktionen zur Erstellung von Aufträgen oder Positionen zurückgegebene Wert mit großer Sorgfalt gespeichert werden sollte, da er das Auftrags- oder Positionsticket darstellt.

Dieses Ticket, das als eine Art Passierschein dient, bietet Ihnen mehrere Möglichkeiten, einschließlich der Möglichkeit, Aufträge oder Positionen auf dem Handelsserver zu manipulieren. Der Wert, den Sie erhalten, wenn Sie einen Trade zum Marktpreis senden oder wenn Sie versuchen, eine Pending Order zu platzieren, und der von den Funktionen zurückgegeben wird, die diese Prozedur ausführen, dient Ihnen also im Wesentlichen (wenn er von Null verschieden ist) als Passierschein für den EA, um mit dem Server zu kommunizieren. Mit dem Ticket haben Sie die Möglichkeit, Preise zu manipulieren.

Jede Order bzw. jede Position hat ein eigenes Ticket, also achten Sie auf diese Nummer und versuchen Sie nicht, sie willkürlich zu erstellen. Es gibt Möglichkeiten, sie zu erhalten, wenn Sie den Wert nicht kennen oder sie verloren haben. Die Wege werden jedoch die Zeit des EA in Anspruch nehmen, also stellen Sie sicher, dass Sie diese Zahl nicht verlieren.

Gehen wir zunächst davon aus, dass wir einen Auftrag haben und die Grenzwerte (Take-Profit oder Stop-Loss) dieses Auftrags entfernen oder ändern wollen. Verwechseln Sie nicht eine Order mit einer Position. Wenn ich „Order“ sage, meine ich eine mögliche und zukünftige Position; normalerweise stehen die Orders im Orderbuch, während die Position dann vorliegt, wenn die Order tatsächlich ausgeführt wird.. In diesem Fall erhalten Sie das Auftragsticket und die neuen Preiswerte. Beachten Sie, dass es sich jetzt um Preiswerte handelt und Sie nicht mehr den finanziellen Wert (den mit dem Kauf und Verkauf verbundenen Geldwert) angeben. Was wir jetzt erwarten, ist der Nennwert, den Sie in der Tabelle sehen. Deshalb können Sie hier nicht wahllos arbeiten, sonst wird Ihr Auftrag abgelehnt.

Nun, da dies klar geworden ist, können Sie praktisch jeden Take-Profit- und Stop-Loss-Wert in einem Auftrag platzieren, aber in der Realität ist dies nicht ganz richtig. Bei einem Kaufauftrag kann der Stop-Loss-Wert nicht größer als der Eröffnungskurs der Position und der Take-Profit-Wert nicht kleiner als der Eröffnungskurs der Position sein. Wenn Sie dies versuchen, wird der Server einen Fehler zurückgeben.

Achten Sie nun genau darauf: Die Take-Profit- und Stop-Loss-Werte im Auftrag müssen diesen Kriterien entsprechen, aber im Falle einer Position kann der Stop-Loss-Wert einer Kaufposition höher sein als der Eröffnungskurs, in diesem Fall wird der Stop-Loss zu einem Stop-Profit, d.h. Sie haben bereits einen Gewinn, wenn der Stop-Loss-Auftrag erreicht wird. Aber darüber werden wir später mehr sprechen. Denken Sie daran, dass der Stop-Loss bei Kaufaufträgen unter dem Eröffnungskurs und bei Verkaufsaufträgen über dem Eröffnungskurs liegen sollte. Der Stop-Loss einer Position kann überall liegen.

Die obigen Ausführungen betreffen nur die Stop-Levels von Orders und Positionen. Aber wenn Sie sich die obige Funktion ansehen, werden Sie feststellen, dass wir auch den Eröffnungskurs der Position manipulieren können, solange es sich noch um eine Order bzw. schwebenden Auftrag handelt. Das wichtige Detail: Wenn Sie dies tun, müssen Sie den Stop-Loss und den Take-Profit zusammen verschieben. Wenn Sie dies nicht tun, wird der Server irgendwann Ihren Antrag auf Änderung des Eröffnungspreises ablehnen.

Um diesen Teil zu verstehen, wollen wir ein kleines EA-Programm erstellen, um diese Fälle zu überprüfen. Erstellen Sie eine neue EA-Datei und fügen Sie den folgenden Code in diese geöffnete Datei ein. Danach kompilieren Sie den EA und starten ihn in einem Chart. Dann werden wir die Erklärung sehen:

#property copyright "Daniel Jose"
#property description "This one is an automatic Expert Advisor"
#property description "for demonstration. To understand how to"
#property description "develop yours in order to use a particular"
#property description "operational, see the articles where there"
#property description "is an explanation of how to proceed."
#property version   "1.03"
#property link      "https://www.mql5.com/pt/articles/11226"
//+------------------------------------------------------------------+
#include <Generic Auto Trader\C_Orders.mqh>
//+------------------------------------------------------------------+
C_Orders *orders;
//+------------------------------------------------------------------+
input int       user01   = 1;           //Lot value
input int       user02   = 100;         //Take Profit
input int       user03   = 75;          //Stop Loss
input bool      user04   = true;        //Day Trade ?
input double    user05   = 84.00;       //Entry price...
//+------------------------------------------------------------------+
int OnInit()
{
        orders = new C_Orders(1234456789);
        
        return INIT_SUCCEEDED;
}
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
        delete orders;
}
//+------------------------------------------------------------------+
void OnTick()
{
}
//+------------------------------------------------------------------+
void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam)
{
#define KEY_UP                  38
#define KEY_DOWN                40
#define KEY_NUM_1               97
#define KEY_NUM_2               98
#define KEY_NUM_3               99
#define KEY_NUM_7               103
#define KEY_NUM_8               104
#define KEY_NUM_9               105

        static ulong sticket = 0;
        int key = (int)lparam;
        
        switch (id)
        {
                case CHARTEVENT_KEYDOWN:
                        switch (key)
                        {
                                case KEY_UP:
                                        if (sticket == 0)
                                                sticket = (*orders).CreateOrder(ORDER_TYPE_BUY, user05, user03, user02, user01, user04);
                                        break;
                                case KEY_DOWN:
                                        if (sticket == 0)
                                                sticket = (*orders).CreateOrder(ORDER_TYPE_SELL, user05, user03, user02, user01, user04);
                                        break;
                                case KEY_NUM_1:
                                case KEY_NUM_7:
                                        if (sticket > 0) ModifyStop(key == KEY_NUM_7, sticket);
                                        break;
                                case KEY_NUM_2:
                                case KEY_NUM_8:
                                        if (sticket > 0) ModifyPrice(key == KEY_NUM_8, sticket);
                                        break;
                                case KEY_NUM_3:
                                case KEY_NUM_9:
                                        if (sticket > 0) ModifyTake(key == KEY_NUM_9, sticket);
                                        break;
                        }
                        break;
        }
}
//+------------------------------------------------------------------+
void ModifyPrice(bool IsUp, const ulong ticket)
{
        double p, s, t;
        
        if (!OrderSelect(ticket)) return;
        p = OrderGetDouble(ORDER_PRICE_OPEN) + (SymbolInfoDouble(_Symbol, SYMBOL_TRADE_TICK_SIZE) * (IsUp ? 1 : -1));
        s = OrderGetDouble(ORDER_SL);
        t = OrderGetDouble(ORDER_TP);
        (*orders).ModifyPricePoints(ticket, p, s, t);
}
//+------------------------------------------------------------------+
void ModifyTake(bool IsUp, const ulong ticket)
{
        double p, s, t;
        
        if (!OrderSelect(ticket)) return;
        p = OrderGetDouble(ORDER_PRICE_OPEN);
        s = OrderGetDouble(ORDER_SL);
        t = OrderGetDouble(ORDER_TP) + (SymbolInfoDouble(_Symbol, SYMBOL_TRADE_TICK_SIZE) * (IsUp ? 1 : -1));
        (*orders).ModifyPricePoints(ticket, p, s, t);
}
//+------------------------------------------------------------------+
void ModifyStop(bool IsUp, const ulong ticket)
{
        double p, s, t;
        
        if (!OrderSelect(ticket)) return;
        p = OrderGetDouble(ORDER_PRICE_OPEN);
        s = OrderGetDouble(ORDER_SL) + (SymbolInfoDouble(_Symbol, SYMBOL_TRADE_TICK_SIZE) * (IsUp ? 1 : -1));
        t = OrderGetDouble(ORDER_TP);
        (*orders).ModifyPricePoints(ticket, p, s, t);
}
//+------------------------------------------------------------------+

Machen Sie sich keine Sorgen, wenn dieser Code auf den ersten Blick kompliziert erscheint. Es soll nur zeigen, was alles passieren kann. Sie sollten sie verstehen, um sie in Zukunft zu Ihrem Vorteil nutzen zu können.

Der Code selbst ist sehr ähnlich zu dem, was wir im vorigen Artikel gesehen haben, aber hier können wir noch etwas mehr tun: Wir können Aufträge manipulieren, indem wir den Wert des Take-Profits, des Stop-Loss und des Open Price ändern. Die einzige Unannehmlichkeit, die im Code noch vorhanden ist, besteht darin, dass eine einmal im Chart platzierte Order nicht einfach entfernt werden kann, um eine andere zu platzieren. Sie können die Order auf dem Chart belassen, das macht nichts, aber wenn Sie diesen EA verwenden, um eine schwebenden Auftrag zu erstellen (und im Moment funktioniert er nur für schwebende Orders), können Sie den Preispunkt ändern, der zeigt, wo die Order sein wird, indem Sie die numerische Tastatur verwenden, die im rechten Teil der physischen Tastatur.

Dies geschieht über diese Ereignisbehandlung. Bitte beachten Sie, dass jede der Tasten für eine bestimmte Aufgabe verwendet wird, z.B. um den Stop-Loss zu erhöhen oder den Auftragspreis zu senken. Tun Sie dies, während Sie das Ergebnis auf der Registerkarte „Handel“ in der Toolbox beobachten. Sie werden eine Menge lernen.

Wenn Sie sich nicht sicher sind, ob Sie diesen Code in die Plattform einfügen sollen (er ist absolut harmlos, aber Sie sollten trotzdem vorsichtig sein), sehen Sie sich bitte das folgende Video an, in dem gezeigt wird, was der Code tatsächlich tut.



Demonstration des obigen Codes.

Sie können sehen, dass wir bei der Bewegung von Stop-Loss und Take-Profit die richtige Bewegung haben, aber wenn wir den offenen Preis bewegen, bleiben Stop-Loss und Take-Profit stehen. Warum ist das so? Der Grund dafür ist, dass Sie bei einem Handelsserver einen Auftrag verschieben, der möglicherweise ein Stopp für einen anderen offenen Handel ist.

Erinnern Sie sich daran, dass ich zu Beginn erwähnt habe, dass eine Möglichkeit, eine offene Position zu schließen, darin besteht, eine Order im Orderbuch zu platzieren und diese langsam zu verschieben. Das ist genau das, was in dieser Phase geschehen sollte. Das heißt, für den Server ist der zu verschiebende Preis nur die Preisinformation, die zur Verfügung gestellt wird. Sie behandelt die OCO-Aufträge nicht als Ganzes. Eine OCO-Bestellung erscheint als unterschiedliche Preispunkte. Sobald eines der Limits erreicht ist, sendet der Server ein Ereignis, das den Preis, zu dem die Position offen ist, entfernt. Beide Aufträge, Take-Profit und Stop-Loss, werden nicht mehr existieren, da das entsprechende Ticket aus dem System entfernt wird.

In diesem Fall müssen wir den Stop-Loss und den Take-Profit zusammen mit dem Auftragskurs bewegen. Um dies zu realisieren, nehmen wir die folgenden Änderungen am obigen Code vor:

void ModifyPrice(bool IsUp, const ulong ticket)
{
        double p, s, t;
        
        if (!OrderSelect(ticket)) return;
        p = OrderGetDouble(ORDER_PRICE_OPEN) + (SymbolInfoDouble(_Symbol, SYMBOL_TRADE_TICK_SIZE) * (IsUp ? 1 : -1));
        s = OrderGetDouble(ORDER_SL) + (SymbolInfoDouble(_Symbol, SYMBOL_TRADE_TICK_SIZE) * (IsUp ? 1 : -1));
        t = OrderGetDouble(ORDER_TP) + (SymbolInfoDouble(_Symbol, SYMBOL_TRADE_TICK_SIZE) * (IsUp ? 1 : -1));
        (*orders).ModifyPricePoints(ticket, p, s, t);
}

Sobald sich nun der Eröffnungskurs bewegt, bewegen sich auch der Take-Profit- und der Stop-Loss-Kurs, wobei der Abstand zum Eröffnungskurs immer gleich bleibt.

Bei der Position funktioniert es ähnlich, aber wir können den Eröffnungskurs der Position nicht verschieben. Wir werden die gleiche Funktion verwenden, um Take-Profit und Stop-Loss zu verschieben, mit einem kleinen Unterschied:

                bool ModifyPricePoints(const ulong ticket, const double Price, const double PriceStop, const double PriceTake)
                        {
                                ZeroMemory(m_TradeRequest);
                                m_TradeRequest.symbol   = _Symbol;
                                if (OrderSelect(ticket))
                                {
// ... Code to move orders...
                                }else if (PositionSelectByTicket(ticket))
                                {
                                        m_TradeRequest.action   = TRADE_ACTION_SLTP;
                                        m_TradeRequest.position = ticket;
                                        m_TradeRequest.tp       = NormalizeDouble(AdjustPrice(PriceTake), m_Infos.nDigits);
                                        m_TradeRequest.sl       = NormalizeDouble(AdjustPrice(PriceStop), m_Infos.nDigits);
                                }else return false;
                                ToServer();
                                
                                return (_LastError == ERR_SUCCESS);
                        };

Mit dem obigen Code implementieren wir den Breakeven- oder Trailing-Stop. Das Einzige, was wir tun müssen, ist, die Werte zu überprüfen, die als Auslöser für den Beginn der Bewegung dienen - dies ist der Break-Even-Wert. Sobald der Auslöser ausgelöst wird, legen wir den offenen Kurs der Position fest und setzen ihn als Stop-Loss-Kurs ein, dann rufen wir die Änderung des Positionskurses auf, und das Ergebnis ist Breakeven.

Ein Trailing-Stop funktioniert genauso, nur dass sich in diesem Fall das Niveau bewegt, wenn der Auslöser bei einem bestimmten Preisabstand oder etwas anderem ausgelöst wird. Wenn dies geschieht, nehmen wir den neuen Wert, der für den Stop-Loss verwendet werden soll, und rufen die obige Funktion auf. Das ist ganz einfach.

Wir werden die Breakeven- und Trailing-Stop-Trigger später betrachten, wenn ich zeige, wie man diese Trigger für einen automatisch arbeitenden EA entwickelt. Wenn Sie ein Enthusiast sind, haben Sie vielleicht schon einige Ideen zu diesen Ebenen. Wenn dies der Fall ist - Herzlichen Glückwunsch! Sie sind auf dem richtigen Weg.

Lassen Sie uns nun auf das Verfahren der Preisänderung zurückkommen, denn es gibt etwas, das ich noch nicht erwähnt habe. Es ist wichtig, dass Sie wissen, wie und warum es dort ist. Zur einfacheren Erklärung betrachten wir den folgenden Code:

                bool ModifyPricePoints(const ulong ticket, const double Price, const double PriceStop, const double PriceTake)
                        {
                                ZeroMemory(m_TradeRequest);
                                m_TradeRequest.symbol   = _Symbol;
                                if (OrderSelect(ticket))
                                {
                                        m_TradeRequest.action = (Price > 0 ? TRADE_ACTION_MODIFY : TRADE_ACTION_REMOVE);
                                        m_TradeRequest.order  = ticket;
                                        if (Price > 0)
                                        {
                                                m_TradeRequest.price      = NormalizeDouble(AdjustPrice(Price), m_Infos.nDigits);
                                                m_TradeRequest.sl         = NormalizeDouble(AdjustPrice(PriceStop), m_Infos.nDigits);
                                                m_TradeRequest.tp         = NormalizeDouble(AdjustPrice(PriceTake), m_Infos.nDigits);
                                                m_TradeRequest.type_time  = (ENUM_ORDER_TYPE_TIME)OrderGetInteger(ORDER_TYPE_TIME) ;
                                                m_TradeRequest.expiration = 0;
                                        }
                                }else if (PositionSelectByTicket(ticket))
                                {
// Code for working with positions ...
                                }else return false;
                                ToServer();
                                
                                return (_LastError == ERR_SUCCESS);
                        };

Es gibt Fälle, in denen eine Order aus dem Orderbuch entfernt werden muss, d. h. sie muss storniert oder gelöscht werden. Und es besteht die Gefahr, dass Sie während der Ausführung etwas tun, was dazu führt, dass der EA einen Preis von Null erzeugt. Glauben Sie mir, das kommt vor, und es ist etwas ganz Übliches, vor allem, wenn Sie einen automatisierten EA verwenden. Dann sendet der EA einen Auftrag, sodass der Auftragspreis geändert wird, aber aufgrund eines Fehlers wird dieser Preis als Null gesendet.

In diesen Fällen lehnt der Handelsserver den Auftrag ab, aber der EA kann dabei bleiben und wie verrückt darauf bestehen, dass der Eröffnungskurs Null sein muss. Wenn man nichts dagegen unternimmt, kann es zu einer Schleife kommen, was bei einem Live-Konto äußerst unangenehm ist. Um zu vermeiden, dass der EA dort verbleibt und auf etwas besteht, das vom Server nicht akzeptiert wird, habe ich die folgende Idee aufgenommen: Wenn der EA einen Positionseröffnungspreis von Null sendet, muss der Auftrag vom Server geschlossen werden. Das ist genau das, was dieser Code hier tut...

Sie teilt dem Handelsserver mit, dass die Order geschlossen und aus dem Auftragsbuch entfernt werden muss. Wenn dies geschieht, ist die Order nicht mehr vorhanden, und Sie können darüber informiert werden. Aber ich füge hier keinen Code dafür ein, denn es gibt andere, ebenso nützliche Verwendungszwecke für diese Art von Dingen, die nicht nur verhindern, dass der EA auf etwas besteht. Sie kann verwendet werden, um einen Order einfach aus dem Orderbuch zu entfernen.

Aber wir haben den Artikel noch nicht fertiggestellt. Es gibt noch ein letztes Verfahren, das zwar fakultativ ist, aber in manchen Fällen nützlich sein kann. Wenn ich schon dabei bin, die Blackbox des Auftragssystems zu öffnen, sollten wir uns ein weiteres Verfahren ansehen, das im Folgenden dargestellt wird:

                bool ClosePosition(const ulong ticket, const uint partial = 0)
                        {
                                double v1 = partial * m_Infos.VolMinimal, Vol;
                                bool IsBuy;
                                
                                if (!PositionSelectByTicket(ticket)) return false;
                                IsBuy = PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_BUY;
                                Vol = PositionGetDouble(POSITION_VOLUME);
                                ZeroMemory(m_TradeRequest);
                                m_TradeRequest.action    = TRADE_ACTION_DEAL;
                                m_TradeRequest.type      = (IsBuy ? ORDER_TYPE_SELL : ORDER_TYPE_BUY);
                                m_TradeRequest.price     = SymbolInfoDouble(_Symbol, (IsBuy ? SYMBOL_BID : SYMBOL_ASK));
                                m_TradeRequest.position  = ticket;
                                m_TradeRequest.symbol    = _Symbol;
                                m_TradeRequest.volume    = ((v1 == 0) || (v1 > Vol) ? Vol : v1);
                                m_TradeRequest.deviation = 1000;
                                ToServer();
                                
                                return (_LastError == ERR_SUCCESS);
                        };

Das oben beschriebene Verfahren bringt viele Menschen dazu, große Träume zu haben, sich Dinge vorzustellen und Sterne zu sehen. Wenn Sie sich dieses Verfahren ansehen, werden Sie denken, dass es dazu dient, eine Position zu schließen. Warum sollte man davon träumen?

Beruhigen Sie sich, mein lieber Leser. Sie verstehen es immer noch nicht, Sie haben ein Vorurteil, wenn Sie den Namen des Verfahrens sehen. Aber lassen Sie uns ein wenig tiefer gehen, den Code analysieren und verstehen, warum viele von großen Dingen träumen. Für dieses Verfahren gibt es einige Berechnungen, aber warum? Dies geschieht, um eine Position teilweise zu schließen (partial close). Wir wollen verstehen, wie dies umgesetzt wird. Angenommen, Sie haben eine offene Position mit einem Volumen von 300. Wenn das handelbare Mindestvolumen 100 beträgt, können Sie mit 100, 200 oder 300 aussteigen.

Dafür müssen Sie aber einen Wert angeben, der standardmäßig Null ist, d.h. er sagt der Funktion, dass die Position vollständig geschlossen wird, aber das wird nur passieren, wenn Sie ihn als Standardwert beibehalten. Aber es gibt ein Detail: Belassen Sie es nicht als Standard. Sie geben den Volumenwert an, d. h. die Anzahl der zu schließenden Lose. Wenn das Mindestlosvolumen 100 beträgt und Sie 300 haben, bedeutet dies, dass das Volumen 3x ist. Zum teilweisen Schließen können Sie 1 oder 2 angeben. Wenn Sie 0, 3 oder einen Wert größer als 3 angeben, wird die Position vollständig geschlossen.

Es gibt jedoch einige Alternativen. Bei der B3 (Bolsa do Brasil) zum Beispiel werden die Aktien des Unternehmens in Losen von 100 Stück gehandelt, aber es gibt auch den Fraktionsmarkt, auf dem man ab 1 handeln kann. In diesem Fall, wenn Sie den EA im fraktionalen Modus laufen lassen, kann der Wert im gleichen Beispiel von 300 von 1 auf 299 gehen, und selbst dann wird die Position nicht vollständig geschlossen, sodass ein offener Rest übrig bleibt.

Ich hoffe, Sie verstehen jetzt. Das konkrete Verfahren hängt vom Markt und den Interessen des Händlers ab. Wenn Sie mit einem HEDGING-Konto arbeiten, benötigen Sie die oben genannte Funktion unbedingt. Andernfalls häufen sich die Positionen einfach an und nehmen dem EA Zeit und Ressourcen für die Analyse dessen, was bereits hätte geschlossen werden können.

Um diesen Artikel zu beenden und alle Fragen zum Auftragssystem zu beantworten, sehen wir uns an, wie der EA-Code aussehen sollte, um die folgende Einschränkung zu beseitigen: Sobald eine Order erstellt wurde, kann sie keine weitere Order mehr aufgeben und keine Daten manipulieren. Um diese Probleme zu beheben, müssen wir einige Änderungen am EA-Code vornehmen, aber dadurch werden Sie bereits jetzt in der Lage sein, Spaß zu haben und viel mehr Dinge zu tun. Vielleicht wird Sie das ein wenig mehr begeistern, was man mit einem relativ einfachen Code und wenig Programmierkenntnissen alles machen kann.

Um den EA teilweise zu korrigieren, müssen wir den Code ändern, der für die Ereignisbehandlung auf dem Chart verantwortlich ist. Der neue Code ist unten abgebildet:

void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam)
{
#define KEY_UP                  38
#define KEY_DOWN                40
#define KEY_NUM_1               97
#define KEY_NUM_2               98
#define KEY_NUM_3               99
#define KEY_NUM_7               103
#define KEY_NUM_8               104
#define KEY_NUM_9               105

        static ulong sticket = 0;
        ulong ul0;
        int key = (int)lparam;
        
        switch (id)
        {
                case CHARTEVENT_KEYDOWN:
                        switch (key)
                        {
                                case KEY_UP:
                                        if (sticket == 0)
                                                sticket = (*orders).CreateOrder(ORDER_TYPE_BUY, user05, user03, user02, user01, user04);
                                        ul0 = (*orders).CreateOrder(ORDER_TYPE_BUY, user05, user03, user02, user01, user04);
                                        sticket = (ul0 > 0 ? ul0 : sticket);
                                        break;
                                case KEY_DOWN:
                                        if (sticket == 0)
                                                sticket = (*orders).CreateOrder(ORDER_TYPE_SELL, user05, user03, user02, user01, user04);
                                        ul0 = (*orders).CreateOrder(ORDER_TYPE_SELL, user05, user03, user02, user01, user04);
                                        sticket = (ul0 > 0 ? ul0 : sticket);
                                        break;
                                case KEY_NUM_1:
                                case KEY_NUM_7:
                                        if (sticket > 0) ModifyStop(key == KEY_NUM_7, sticket);
                                        break;
                                case KEY_NUM_2:
                                case KEY_NUM_8:
                                        if (sticket > 0) ModifyPrice(key == KEY_NUM_8, sticket);
                                        break;
                                case KEY_NUM_3:
                                case KEY_NUM_9:
                                        if (sticket > 0) ModifyTake(key == KEY_NUM_9, sticket);
                                        break;
                        }
                        break;
        }
}

Wir haben die durchgestrichenen Teile gelöscht und eine neue Variable hinzugefügt, die den von der Auftragsklasse zurückgegebenen Ticketwert erhalten soll. Ist der Wert ungleich Null, wird das neue Ticket in der statischen Variablen gespeichert, die den Wert so lange speichert, bis ihr ein neuer Wert zugewiesen wird. Nun, damit können Sie bereits viel mehr Dinge verwalten, und wenn eine Order versehentlich ausgeführt wird und Sie den Wert beim Eröffnen einer neuen Order nicht überschrieben haben, können Sie immer noch die Orderlimits verwalten.

Als zusätzliche Aufgabe, um zu testen, ob Sie wirklich gelernt haben, wie man mit dem Ordersystem arbeitet (vor dem nächsten Artikel), versuchen Sie nun, das Ordersystem eine Marktposition eröffnen zu lassen und den Ticketwert zu verwenden, um die Limits der offenen Position verwalten zu können. Tun Sie es, ohne weitere Erklärungen zu lesen. Dies wird Ihnen helfen zu verstehen, ob Sie den Erklärungen folgen können.

Schauen wir uns nun den Code an, der den Preis ändert. Der Öffnungscode ist derselbe. Wir können den Fall auslassen, dass der Auftrag in eine Position umgewandelt wird. Betrachten wir Take-Profit:

void ModifyTake(bool IsUp, const ulong ticket)
{
        double p = 0, s, t;
        
        if (!OrderSelect(ticket)) return;
        if (OrderSelect(ticket))
        {
                p = OrderGetDouble(ORDER_PRICE_OPEN);
                s = OrderGetDouble(ORDER_SL);
                t = OrderGetDouble(ORDER_TP) + (SymbolInfoDouble(_Symbol, SYMBOL_TRADE_TICK_SIZE) * (IsUp ? 1 : -1));
        }else if (PositionSelectByTicket(ticket))
        {
                s = PositionGetDouble(POSITION_SL);
                t = PositionGetDouble(POSITION_TP) + (SymbolInfoDouble(_Symbol, SYMBOL_TRADE_TICK_SIZE) * (IsUp ? 1 : -1));
        }else return;
        (*orders).ModifyPricePoints(ticket, p, s, t);
}

Der durchgestrichene Code existiert nicht mehr, sodass wir den neuen Code verwenden können, um den Take-Profit-Wert einer Position zu verwalten. Das Gleiche gilt für den unten dargestellten Stop-Loss-Code:

void ModifyStop(bool IsUp, const ulong ticket)
{
        double p = 0, s, t;
        
        if (!OrderSelect(ticket)) return;
        if (OrderSelect(ticket))
        {
                p = OrderGetDouble(ORDER_PRICE_OPEN);
                s = OrderGetDouble(ORDER_SL) + (SymbolInfoDouble(_Symbol, SYMBOL_TRADE_TICK_SIZE) * (IsUp ? 1 : -1));
                t = OrderGetDouble(ORDER_TP);
        }else if (PositionSelectByTicket(ticket))
        {
                s = PositionGetDouble(POSITION_SL) + (SymbolInfoDouble(_Symbol, SYMBOL_TRADE_TICK_SIZE) * (IsUp ? 1 : -1));
                t = PositionGetDouble(POSITION_TP);
        }else return;
        (*orders).ModifyPricePoints(ticket, p, s, t);
}

Verwenden Sie diesen EA als Lernwerkzeug, vor allem auf Demo-Konten - machen Sie das Beste daraus; erkunden Sie in vollem Umfang, was in diesen ersten drei Artikeln ist, denn an diesem Punkt betrachte ich das Auftragssystem vollständig. Im nächsten Artikel werde ich zeigen, wie man einen EA initialisiert, um einige Informationen, Probleme und mögliche Lösungen im Zusammenhang mit dieser Initialisierung zu erfassen. Aber diese Fragen sind nicht Teil des Ordersystems - wir haben alles implementiert, was wir wirklich brauchen, um den EA automatisch zu machen.


Schlussfolgerung

Trotz dessen, was wir in diesen ersten drei Artikeln gesehen haben, sind wir noch weit davon entfernt, einen vollständig automatisierten EA zu erstellen. Viele ignorieren oder kennen die hier vorgestellten Details nicht, was gefährlich ist. Wir fangen gerade erst an, über automatisierte Systeme und die Gefahren zu sprechen, die damit verbunden sind, wenn man sie ohne entsprechende Kenntnisse einsetzt.

Der gesamte zuvor betrachtete Code ist im Anhang verfügbar. Studieren Sie es und lernen Sie, wie die Dinge funktionieren. Ich hoffe, wir sehen uns im nächsten Artikel wieder.

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

Beigefügte Dateien |
Erstellen eines EA, der automatisch funktioniert (Teil 04): Manuelle Auslöser (I) Erstellen eines EA, der automatisch funktioniert (Teil 04): Manuelle Auslöser (I)
Heute werden wir sehen, wie man einen Expert Advisor erstellt, der einfach und sicher im automatischen Modus arbeitet.
Erstellen eines EA, der automatisch funktioniert (Teil 02): Erste Schritte mit dem Code Erstellen eines EA, der automatisch funktioniert (Teil 02): Erste Schritte mit dem Code
Heute werden wir sehen, wie man einen Expert Advisor erstellt, der einfach und sicher im automatischen Modus arbeitet. Im vorigen Artikel haben wir die ersten Schritte besprochen, die jeder verstehen muss, bevor er einen Expert Advisor für den automatischen Handel erstellen kann. Wir haben uns Gedanken über die Konzepte und die Struktur gemacht.
Experimente mit neuronalen Netzen (Teil 3): Praktische Anwendung Experimente mit neuronalen Netzen (Teil 3): Praktische Anwendung
In dieser Artikelserie entwickle ich mit Hilfe von Experimenten und unkonventionellen Ansätzen ein profitables Handelssystem und prüfe, ob neuronale Netze für Trader eine Hilfe sein können. MetaTrader 5 ist als autarkes Werkzeug für den Einsatz neuronaler Netze im Handel konzipiert.
DoEasy. Steuerung (Teil 31): Scrollen des Inhalts des ScrollBar-Steuerelements DoEasy. Steuerung (Teil 31): Scrollen des Inhalts des ScrollBar-Steuerelements
In diesem Artikel werde ich die Funktionsweise des Scrollens des Inhalts des Containers mithilfe der Schaltflächen der horizontalen Bildlaufleiste implementieren.