Ein Versuch, einen EA-Konstruktor zu entwickeln
Inhalt
- Einführung
- 1. EA-Funktionalität nach der Verwendung des Konstruktors
- 2. Der allgemeine Algorithmus des Konstruktors
- 3. Hinzufügen des Standardindikators — Behandlung der Datei Indicators Code.mq5
- 4. Hinzufügen eines nutzerdefinierten Indikators
- 5. Erfassen der Transaktion (vereinfachter Code)
- 6. Erstellen eines EA (Positionseröffnungssignale) mit Hilfe des Konstruktors
- 7. Erstellen eines EA (Signale für schwebende Aufträge (pending order)) mit dem Konstruktor
- Schlussfolgerung
Einführung
Von Anfang an war es mein Ziel, die Standardbibliothek zu nutzen. Meine erste Aufgabe bestand darin, die einfachste Funktionalität zu implementieren: die Handelsklasse CTrade einbinden und die Methode Buy oder Sell ausführen. Ich habe die Standardbibliothek gewählt, weil sie einen kurzen und prägnanten Code liefert. Der folgende kurze Code, der in Form eines Skripts ausgeführt wird, eröffnet eine Kaufposition mit einem Volumen von 1,0 Lot:
//+------------------------------------------------------------------+ //| Open Buy.mq5 | //| Copyright © 2018-2021, Vladimir Karputov | //+------------------------------------------------------------------+ #property copyright "Copyright © 2018-2021, Vladimir Karputov" #property version "1.001" //--- #include <Trade\Trade.mqh> CTrade m_trade; // trading object //+------------------------------------------------------------------+ //| Script program start function | //+------------------------------------------------------------------+ void OnStart() { //--- m_trade.Buy(1.0); // open Buy position, volume 1.0 lot }
Nach und nach wurden die Anforderungen immer komplizierter und ich stieß beim Schreiben eines neuen Expert Advisor (EA) fast jedes Mal auf Handelsfehler. So wurde mein Wunsch, korrekten Code zu schreiben, immer stärker. Schließlich erschien ein sehr wichtiger Artikel Welche Überprüfungen der Handelsroboter vor der Veröffentlichung in Market bestehen soll. Zu dem Zeitpunkt, als dieser Artikel veröffentlicht wurde, war mir bereits bewusst, dass ich zuverlässige Kontrollfunktionen für die Ausführung von Handelsaufträgen benötigte. Von da an begann ich, mir nach und nach bewährte Funktionen anzueignen, die sich mit Kopieren->Einfügen leicht in den EA einfügen lassen.
Da bei der Arbeit von EAs fast immer mit Indikatoren gearbeitet wird, habe ich begonnen, die Funktionen für die korrekte Erstellung von Indikator-Handles sowie für den Empfang von Indikator-Daten zu beschaffen.
NB: Der MQL5-Stil impliziert, dass das Indikator-Handle EINMAL erstellt wird. In der Regel wird dies in OnInit gemacht.
Seit etwa Version 2.XXX habe ich damit begonnen, zwei Entwicklungszweige zu pflegen — den normalen prozeduralen Code und den Code in Form einer Klasse (das Hauptziel der Klasse ist die Implementierung von Mehrwährungs-EAs).
Im Laufe meiner Arbeit erhielt der Konstruktor nach und nach die üblichen Einstellungen:
- Stop Loss und Take Profit,
- Trailing,
- Berechnung der Losgröße (lot) als Risikoprozentsatz oder als Konstante/Mindestlot,
- Kontrolle des Zeitintervalls, innerhalb dessen der Handel durchgeführt wird,
- Das Vorhandensein von nur einer Position auf dem Markt,
- Umkehrung der Handelssignale,
- Schließen von Positionen erzwingen, falls ein gegenteiliges Signal erscheint...
Jede Eingabe erforderte die Erstellung von Codeblöcken und neuen Funktionen.
Für den täglichen Gebrauch habe ich beschlossen, die üblichen Funktionen und alle Eingaben in dem EA Trading engine 3.mq5 zu sammeln. In der Tat ist dies ein fertiger EA, der uns von einer Menge Routinearbeit befreit. Alles, was wir tun müssen, ist, Funktionen hinzuzufügen/zu entfernen oder die Interaktion zwischen Codeblöcken in jedem einzelnen Fall zu ändern.
1. EA-Funktionalität nach dem Konstruktor
Der vom Konstruktor erstellte EA verfügt sofort über mehrere Einstellungen, die zu einzigartigen Strategien kombiniert werden können. In der Version 4.XXX gelten die folgenden Regeln:
- Das aktuelle Symbol wird verwendet (ein Symbol des Charts, auf dem der EA gestartet wird).
- Take Profit, Stop Loss und Trailing werden in Points (Punkten) in den Eingaben eingestellt. Points — aktuelle Symbolpunktgröße in der Kurswährung, z.B. für "EURSD" 1.00055-1.00045=10 Punkte.
Die "points" sind immer auf dem Symboldiagramm zu sehen, wenn Sie sich das Fadenkreuz anzeigen lassen:
Abb. 1. Points
Nachfolgend finden Sie die Eingaben des EA, der mit Hilfe des Konstruktors erstellt wird:
- Handelseinstellungen
- Arbeitszeitrahmen. Der Arbeitszeitrahmen (working timeframe) kann sich von dem Chart-Zeitrahmen unterscheiden, auf dem der EA gestartet wird. Dies ist der Zeitrahmen, auf dem der Indikator erstellt wird (wenn nicht explizit ein anderer Zeitrahmen im Indikator angegeben ist). Er wird auch verwendet, um den Zeitpunkt der Erstellung eines neuen Balkens zu verfolgen (falls ein Handelssignal nur dann erkannt werden soll, wenn ein neuer Balken erscheint oder wenn das Trailing nur dann gestartet werden soll, wenn ein neuer Balken erscheint).
- Stop Loss (0 — deaktiviert).
- Take Profit (0 — deaktiviert).
- Trailing on ... — Überprüfen des Trailing-Bedingung bei jedem Tick (Balken #0 (bei jedem Tick)) oder nur, wenn ein neuer Balken erscheint (Balken #1 (bei einem neuen Balken)).
- Search signals on ... — Suche nach einem Handelssignal bei jedem Tick (Balken #0 (bei jedem Tick)) oder nur wenn ein neuer Balken erscheint (Balken #1 (bei einem neuen Balken)).
- Trailing Stop (min distance from price to Stop Loss) — minimaler Abstand zwischen dem Preis und dem Stop Loss der Position. Das Trailing wird nur aktiviert, wenn die Position profitabel ist und sich der Preis um Trailing Stop + Trailing Step vom offenen Preis entfernt. Die Trailing-Operation wird in den TrailingStop Codebildern angezeigt.
- Trailing Step.
- Positionsgrößenverwaltung (Losgrößenberechnung).
- Money Management Lot: Lot OR Risk — Berechnungssystem der Losgröße. Die Losgröße (lot) kann konstant sein (Money management= konstante Losgröße, festgelegt wird die Losgröße durch "The value for "Money management" eingestellt) oder dynamisch - Risiko % pro Position (Money management= Risiko in Prozent für eine Position, der Prozentsatz des Risikos wird im The value for "Money management" eingestellt). Sie können auch eine konstante Losgröße einstellen, das dem Minimum entspricht (Money management= Lots Min).
- The value for "Money management"
- Handelsmodus
- Handelsmodus: Nur KAUF-Positionen erlaubt, Nur VERKAUF-Positionen erlaubt und KAUF- und VERKAUFS-Positionen erlaubt
- DEMA — Nutzerdefinierte Indikatorparameter. Hier stellen Sie schließlich Ihren Indikator und seine Parameter ein
- DEMA: averaging period (Glättungslänge)
- DEMA: horizontal shift (horizontale Verschiebung)
- DEMA: type of price (Art des Preises)
- Time control — Arbeitszeitraum. Die Zeitspanne, innerhalb derer nach Handelssignalen gesucht werden darf
- Use time control — Flag, zur Aktivierung/Deaktivierung der Zeitsteuerung
- Start Hour — Stunden des Beginns des Zeitintervalls
- Start Minute — Minute des Beginns des Zeitintervalls
- End Hour — Stunden des Endes des Zeitintervalls
- End Minute — Minute des Endes des Zeitintervalls
- Pending Order Parameters — Parameter für schwebende Aufträge (pending orders)
- Pending: Expiration, in minutes (0 -> OFF) — Lebensdauer des schwebenden Auftrags (0 — deaktiviert).
- Pending: Indent — Abstand des schwebenden Auftrags vom aktuellen Preis (wenn der Preis des schwebenden Auftrags nicht explizit festgelegt ist).
- Pending: Maximum spread (0 -> OFF) — Maximaler Spread (0 — deaktiviert). Wenn der aktuelle Spread den angegebenen Wert übersteigt, wird die Pending Order nicht gesetzt (der EA wartet auf eine Verringerung des Spreads).
- Pending: Only one pending — aktivieren/deaktivieren durch das Flag. Nur ein schwebender Auftrag ist auf dem Markt erlaubt.
- Pending: Reverse pending type — aktivieren/deaktivieren durch das Flag. Umkehren der Handelsaufträge.
- Pending: New pending -> delete previous ones — wenn ein schwebender Auftrag abgeschickt werden soll, dann werden alle anderen schwebenden Aufträge vorher gelöscht.
- Zusätzliche Eigenschaften
- Positions: Only one — aktivieren/deaktivieren durch das Flag. Es ist nur eine Position auf dem Markt erlaubt.
- Positions: Reverse — aktivieren/deaktivieren durch das Flag. Umkehren der Handelsaufträge.
- Positions: Close opposite — aktivieren/deaktivieren durch das Flag. Liegt ein Handelsauftrag vor, werden alle Positionen vorläufig geschlossen, damit der Auftrag ausgeführt werden kann.
- Print log — aktivieren/deaktivieren durch das Flag. Anzeige von erweiterten Informationen über Operationen und Fehler.
- Coefficient (if Freeze==0 Or StopsLevels==0) — Verhältnis unter Berücksichtigung des Stop Levels.
- Deviation — angegebener Schlupf (slippage).
- Magic number — die eindeutige ID des EAs.
2. Der allgemeine Algorithmus des Konstruktors
Das Array SPosition wird auf der globalen Programmebene (im EA-Header) deklariert. Es besteht aus der Struktur STRUCT_POSITION. Während des Starts hat das Array eine Größe von Null. Nach der Verarbeitung eines Handelssignals geht das Array ebenfalls auf Null zurück.
Ab OnTick wird die Funktion SearchTradingSignals aufgerufen. Wenn das Signal entstanden ist (der Markt hat keine offenen Positionen), erstellt die Funktion einen Handelsauftrag (eine einzelne Struktur STRUCT_POSITION wird für jeden Handelsauftrag im Array erstellt). Das Vorhandensein einer Handelsorder wird in OnTick überprüft — die Array-Größe von SPositionswird überprüft: wenn sie größer als Null ist, liegt eine Handelsorder vor, die zur Ausführung an OpenBuy oder OpenSell gesendet wird. Die Kontrolle über die Ausführung des Handelsauftrags erfolgt in OnTradeTransaction:
Abb. 2. Allgemeiner Algorithmus (vereinfacht)
Es wird immer davon ausgegangen, dass der EA mit dem aktuellen Symbol arbeitet — demjenigen, auf dessen Chart der EA platziert ist. Beispiel: Wenn der EA auf USDPLN platziert ist, arbeitet er mit USDPLN.
2.1. Die Struktur STRUCT_POSITION
Diese Struktur ist das Herzstück des EAs. Sie erfüllt gleich zwei Aufgaben: Die Struktur enthält die Felder, in denen der Handelsauftrag gesetzt wird (das Setzen erfolgt in SearchTradingSignals). Die Struktur enthält auch die Felder zur Verwaltung der Ausführung eines Handelsauftrags (die Steuerung erfolgt in OnTradeTransaction).
//+------------------------------------------------------------------+ //| Structure Positions | //+------------------------------------------------------------------+ struct STRUCT_POSITION { ENUM_POSITION_TYPE pos_type; // position type double volume; // position volume (if "0.0" -> the lot is "Money management") double lot_coefficient; // lot coefficient bool waiting_transaction; // waiting transaction, "true" -> it's forbidden to trade, we expect a transaction ulong waiting_order_ticket; // waiting order ticket, ticket of the expected order bool transaction_confirmed; // transaction confirmed, "true" -> transaction confirmed //--- Constructor STRUCT_POSITION() { pos_type = WRONG_VALUE; volume = 0.0; lot_coefficient = 0.0; waiting_transaction = false; waiting_order_ticket = 0; transaction_confirmed = false; } };
Einige Felder sind für den Handelsauftrag selbst zuständig, während andere die Ausführung übernehmen. Die Struktur enthält den Konstruktor — die Spezialfunktion STRUCT_POSITION(). Sie wird bei der Erstellung des Strukturobjekts aufgerufen und zur Initialisierung der Strukturelemente verwendet.
Felder eines Handelsauftrags:
- pos_type — zu eröffnende Positionsart (kann POSITION_TYPE_BUY oder POSITION_TYPE_SELL sein).
- volume — Positionsvolumen. Wenn das Volumen 0,0 ist, wird es aus der Gruppe der Eingaben 'Position size management (lot calculation)' übernommen.
- lot_coefficient — Ist das Verhältnis größer als 0,0, wird das Positionsvolumen mit dem Verhältnis multipliziert.
Felder der Steuerung der Ausführung von Handelsaufträgen
- waiting_transaction — das Flag, das anzeigt, dass ein Handelsauftrag erfolgreich ausgeführt wurde und auf die Bestätigung gewartet werden muss.
- waiting_order_ticket — Index des Auftrags, der bei der Ausführung eines Handelsauftrags erhalten wurde.
- Transaction_confirmed — Das Flag, das anzeigt, dass die Ausführung einer Handelsorder bestätigt wurde.
Nach der Aktivierung des Flags in transaction_confirmed wird ein Handelsauftrag aus der Struktur entfernt. Wenn also eine EA-Operation keinen Handelsauftrag beinhaltet, hat die Struktur eine Größe von Null. Weitere Informationen zu den Strukturfeldern und der Steuerung finden Sie im Bereich "Erfassen der Transaktion (vereinfachter Code)".
Warum genau verwende ich einen solchen Algorithmus?
Auf den ersten Blick mag es genügen, die Methode Buy auf true oder false zu prüfen und im Falle von true davon auszugehen, dass die Handelsorder ausgeführt wurde. In vielen Fällen funktioniert dieser Ansatz tatsächlich. Aber manchmal garantiert die Rückgabe von true das Ergebnis nicht. Dies wird in der Dokumentation zu den Methoden Buy und Sell ausgeführt:
Hinweis
Eine erfolgreiche Beendigung der Methode bedeutet nicht immer eine erfolgreiche Ausführung einer Handelsoperation. Es ist notwendig, das Ergebnis der Handelsanfrage (Rückgabecode des Handelsservers) mit ResultRetcode() und dem von ResultDeal() zurückgegebenen Wert zu überprüfen.
Das Vorhandensein des Eintrags in der Handelshistorie kann als endgültige und genaue Bestätigung der Ausführung der Handelsoperation dienen. Daher wurde der folgende Algorithmus gewählt: wenn die Methode erfolgreich ausgeführt wurde, wird ResultDeal (Handelsticket) geprüft, ResultRetcode (Ergebniscode der Anfrageausführung) geprüft und ResultOrder (Orderticket) gespeichert. Das Orderticket befindet sich in OnTradeTransaction.
3. Hinzufügen des Standardindikators — Handhabung der Datei Indicators Code.mq5
Für mehr Bequemlichkeit sind die vorgefertigten Codeblöcke (Deklaration der Variablen für die Speicherung von Handles, Inputs, Erstellung von Handles) in dem EA Indicators Code.mq5 gesammelt. Die Eingabeparameter des Indikators und die Variablen zum Speichern von Handles befinden sich im EA-"Header", die Handle-Erstellung wird in OnInit festgelegt. Beachten Sie, dass die Namen der Variablen für die Speicherung von Handles wie folgt gebildet werden: 'handle_' + 'indicator', zum Beispiel 'handle_iStdDev'. Die gesamte Handhabung von Indicators Code.mq5 läuft auf ein fortgesetztes Kopieren-Einfügen hinaus.
NB: Der MQL5-Stil impliziert, dass das Indikator-Handle EINMAL erstellt wird. In der Regel wird dies in OnInit gemacht.
3.1. Beispiel für das Hinzufügen des Indikators iRVI (Relative Vigor Index, RVI)
Erstellen wir den EA Add indicator.mq5. Wir starten im MetaEditor den MQL-Assistenten, z. B. durch Klicken auf und wählen Sie Expert Advisor (template)
Abb. 3. MQL-Assistent -> Expert Advisor (template)
Ich empfehle dringend, im nächsten Schritt mindestens eine Eingabe hinzuzufügen
Abb. 4. Expert Advisor (Vorlage) -> Parameter hinzufügen
Damit lassen sich die Zeilen für den Eingabeblock automatisch in den Code einfügen:
//--- input parameters input int Input1=9;
MQL Wizard hat einen leeren EA erstellt. Fügen wir nun den iRVI (Relative Vigor Index, RVI) Indikator hinzu. Suchen Sie in der Datei Indicators Code.mq5 nach handle_iRVI (über ctrl + F). Die Suche ergibt die Variable, in der der Handle gespeichert ist:
Abb. 5. RVI Handle
Kopiere wir die gefundene Zeilen und fügen sie in die EA-Kopf 'Add indicator' (Indikator hinzufügen) ein:
//--- input parameters input int Input1=9; //--- int handle_iRVI; // variable for storing the handle of the iRVI indicator //+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+ int OnInit()
Wir setzen die Suche fort und suchen den Block zur Erstellung des Handles:
Abb. 6. Handle des iRVI
Wir kopieren die gefundenen Zeilen und fügen sie in 'Add indicator' EA in OnInit ein:
//+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+ int OnInit() { //--- create handle of the indicator iRVI handle_iRVI=iRVI(m_symbol.Name(),Inp_RVI_period,Inp_RVI_ma_period); //--- if the handle is not created if(handle_iRVI==INVALID_HANDLE) { //--- tell about the failure and output the error code PrintFormat("Failed to create handle of the iRVI indicator for the symbol %s/%s, error code %d", m_symbol.Name(), EnumToString(Inp_RVI_period), GetLastError()); //--- the indicator is stopped early m_init_error=true; return(INIT_SUCCEEDED); } //--- return(INIT_SUCCEEDED); }
Fügen wir nun die Indikatoreingaben hinzu. Mit der mittleren Maustaste klicken wir in Indicators Code.mq5 auf Inp_RVI_period, um direkt zum Eingabeblock zu gelangen:
Abb. 7. Handle des iRVI
Kopieren wir die Zeilen und fügen sie zu den Eingabeparametern hinzu:
#property version "1.00" //--- input parameters input group "RVI" input ENUM_TIMEFRAMES Inp_RVI_period = PERIOD_D1; // RVI: timeframe input int Inp_RVI_ma_period = 15; // RVI: averaging period //--- int handle_iRVI; // variable for storing the handle of the iRVI indicator //+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+
Das Kompilieren erzeugt die folgenden Fehler: m_symbol und m_init_error. Dies ist ein erwartetes Verhalten, da diese Variablen in dem Code vorhanden sind, der nach der Konstruktoroperation erhalten wird, während der EA "Add indicator" EA nur erstellt wurde, um die Handhabung der Datei Indicators Code.mq5 zu demonstrieren.
4. Hinzufügen eines nutzerdefinierten Indikators
Lassen Sie uns einen MA on DeMarker Indikator hinzufügen. Erstens ist er ein nutzerdefinierter Indikator, zweitens verwendet er Gruppe. Erstellen wir wie im vorherigen Abschnitt den EA 'Add custom indicator'. Kopieren Sie anschließend die Eingaben aus dem nutzerdefinierten Indikator und fügen Sie sie in den EA ein:
#property version "1.00" //--- input parameters input group "DeMarker" input int Inp_DeM_ma_period = 14; // DeM: averaging period input double Inp_DeM_LevelUP = 0.7; // DeM: Level UP input double Inp_DeM_LevelDOWN = 0.3; // DeM: Level DOWN input group "MA" input int Inp_MA_ma_period = 6; // MA: averaging period input ENUM_MA_METHOD Inp_MA_ma_method = MODE_EMA; // MA: smoothing type //---
Wir suchen in Indicators Code.mq5 die Variable handle_iCustom zur Speicherung des Handles des nutzerdefinierten Indikators und fügen sie in den EA ein:
//+------------------------------------------------------------------+ //| Add custom indicator.mq5 | //| Copyright © 2021, Vladimir Karputov | //| https://www.mql5.com/en/users/barabashkakvn | //+------------------------------------------------------------------+ #property copyright "Copyright © 2021, Vladimir Karputov" #property link "https://www.mql5.com/en/users/barabashkakvn" #property version "1.00" //--- input parameters input group "DeMarker" input int Inp_DeM_ma_period = 14; // DeM: averaging period input double Inp_DeM_LevelUP = 0.7; // DeM: Level UP input double Inp_DeM_LevelDOWN = 0.3; // DeM: Level DOWN input group "MA" input int Inp_MA_ma_period = 6; // MA: averaging period input ENUM_MA_METHOD Inp_MA_ma_method = MODE_EMA; // MA: smoothing type //--- int handle_iCustom; // variable for storing the handle of the iCustom indicator //+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+ int OnInit()
Wir suchen den Block zur Erstellung des nutzerdefinierten Indikator-Handles in OnInit() aus Indicators Code.mq5 und kopieren ihn in den EA:
int handle_iCustom; // variable for storing the handle of the iCustom indicator //+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+ int OnInit() { //--- create handle of the indicator iCustom handle_iCustom=iCustom(m_symbol.Name(),Inp_DEMA_period,"Examples\\DEMA", Inp_DEMA_ma_period, Inp_DEMA_ma_shift, Inp_DEMA_applied_price); //--- if the handle is not created if(handle_iCustom==INVALID_HANDLE) { //--- tell about the failure and output the error code PrintFormat("Failed to create handle of the iCustom indicator for the symbol %s/%s, error code %d", m_symbol.Name(), EnumToString(Inp_DEMA_period), GetLastError()); //--- the indicator is stopped early m_init_error=true; return(INIT_SUCCEEDED); } //--- return(INIT_SUCCEEDED); } //+------------------------------------------------------------------+ //| Expert deinitialization function |
Hier haben wir einiges an Arbeit vor uns. Wir müssen den Zeitrahmen (InpWorkingPeriod), den Pfad zum Indikator (wir nehmen an, dass er im Stammverzeichnis des Ordners Indicators gespeichert ist) und die Eingaben festlegen:
//+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+ int OnInit() { //--- create handle of the indicator iCustom handle_iCustom=iCustom(m_symbol.Name(),InpWorkingPeriod,"MA on DeMarker", "DeMarker", Inp_DeM_ma_period, Inp_DeM_LevelUP, Inp_DeM_LevelDOWN, "MA", Inp_MA_ma_period, Inp_MA_ma_method); //--- if the handle is not created if(handle_iCustom==INVALID_HANDLE) { //--- tell about the failure and output the error code PrintFormat("Failed to create handle of the iCustom indicator for the symbol %s/%s, error code %d", m_symbol.Name(), EnumToString(InpWorkingPeriod), GetLastError()); //--- the indicator is stopped early m_init_error=true; return(INIT_SUCCEEDED); } //--- return(INIT_SUCCEEDED); }
5. Erfassen der Transaktion (vereinfachter Code)
NB: Dies ist eine vereinfachte Version des EA. Viele Funktionen haben einen kürzeren Code im Vergleich zu einem vollwertigen Konstruktor.
Wenn es keine Positionen auf dem Markt gibt, die von diesem EA eröffnet wurden, setzen wir die Handelsanfrage, um eine KAUF-Position zu eröffnen. Die Bestätigung der Positionseröffnung wird über OnTradeTransaction und OnTick ausgegeben. Suchen und Schreiben eines Handelsauftrags in der Funktion SearchTradingSignals:
//+------------------------------------------------------------------+ //| Search trading signals | //+------------------------------------------------------------------+ bool SearchTradingSignals(void) { if(IsPositionExists()) return(true); //--- int size_need_position=ArraySize(SPosition); if(size_need_position>0) return(true); ArrayResize(SPosition,size_need_position+1); SPosition[size_need_position].pos_type=POSITION_TYPE_BUY; if(InpPrintLog) Print(__FILE__," ",__FUNCTION__,", OK: ","Signal BUY"); return(true); }
Wenn der Markt keine vom EA eröffneten Positionen hat und die Array-Größe SPosition null ist, erhöhen wir die Array-Größe um eins. Auf diese Weise erstellen wir ein einzelnes STRUCT_POSITION-Strukturobjekt. Dieses wiederum ruft den Konstruktor STRUCT_POSITION() auf. Nach dem Aufruf des Konstruktors werden die Strukturelemente initialisiert (z.B. Volumen - das Positionsvolumen wird auf 0.0 gesetzt, es wird also aus der Gruppe 'Positionsgrößenverwaltung (Losberechnung)' übernommen). Jetzt bleibt nur noch die Handelsauftragsart in die Struktur zu setzen. In diesem Fall kann sie interpretiert werden als: "Eröffnen einer KAUF-Position".
Nach dem Setzen des Handelsauftrags besteht das Array SPosition aus einer einzigen Struktur und die Strukturelemente haben die folgenden Werte:
Element | Wert | Hinweis |
---|---|---|
pos_type | POSITION_TYPE_BUY | bestimmt in SearchTradingSignals |
volume | 0.0 | initialisiert im Konstruktors der Struktur |
lot_coefficient | 0.0 | initialisiert im Konstruktors der Struktur |
waiting_transaction | false | initialisiert im Konstruktors der Struktur |
waiting_order_ticket | 0 | initialisiert im Konstruktors der Struktur |
transaction_confirmed | false | initialisiert im Konstruktors der Struktur |
5.1. Wir gelangen zu OnTick bei einem neuen Tick
OnTick, allgemeines Prinzip:
//+------------------------------------------------------------------+ //| Expert tick function | //+------------------------------------------------------------------+ void OnTick() { //--- int size_need_position=ArraySize(SPosition); if(size_need_position>0) { for(int i=size_need_position-1; i>=0; i--) { if(SPosition[i].waiting_transaction) { if(!SPosition[i].transaction_confirmed) { if(InpPrintLog) Print(__FILE__," ",__FUNCTION__,", OK: ","transaction_confirmed: ",SPosition[i].transaction_confirmed); return; } else if(SPosition[i].transaction_confirmed) { ArrayRemove(SPosition,i,1); return; } } if(SPosition[i].pos_type==POSITION_TYPE_BUY) { SPosition[i].waiting_transaction=true; OpenPosition(i); return; } if(SPosition[i].pos_type==POSITION_TYPE_SELL) { SPosition[i].waiting_transaction=true; OpenPosition(i); return; } } } //--- search for trading signals only at the time of the birth of new bar if(!RefreshRates()) return; //--- search for trading signals if(!SearchTradingSignals()) return; //--- }
Zu Beginn von OnTick wird die Größe des Arrays SPosition überprüft (dies ist das Array der Struktur STRUCT_POSITION). Wenn die Array-Größe Null übersteigt, beginne mit dem Traversieren zu Null, wobei zwei Szenarien möglich sind:
- Wenn das Flag der Struktur waiting_transaction true ist (Handelsauftrag wurde vorbereitet, muss auf Bestätigung warten), wird das Flag transaction_confirmed geprüft.
- Wenn false, wurde die Transaktion noch nicht bestätigt (dies kann z.B. passieren, wenn der Handelsauftrag gesendet wurde, ein neues Tick eingetroffen ist, während OnTradeTransaction noch keine Bestätigung hat). Die entsprechende Meldung ausgeben und mit return beenden — auf einen neuen Tick warten, in der Hoffnung, dass die Informationen mit dem neuen Tick aktualisiert werden.
- Wenn true, wurde die Transaktion bestätigt. Es wird die Struktur aus dem Array entfernt und mit return verlassen.
-
Wenn das Strukturflag waiting_transaction false ist (der Handelsauftrag wurde gerade angegeben und noch nicht ausgeführt), wird das Flag waiting_transaction aktiviert und der Auftrag an OpenPosition umgeleitet. Der Codeblock kann vereinfacht werden zu
SPosition[i].waiting_transaction=true; OpenPosition(i); return;
aber ich habe es so belassen, damit die vollständige Form des EA-Konstruktors leichter zu verstehen ist:
if(SPosition[i].pos_type==POSITION_TYPE_BUY) { if(InpCloseOpposite) { if(count_sells>0) { ClosePositions(POSITION_TYPE_SELL); return; } } if(InpOnlyOne) { if(count_buys+count_sells==0) { SPosition[i].waiting_transaction=true; OpenPosition(i); return; } else { ArrayRemove(SPosition,i,1); return; } } SPosition[i].waiting_transaction=true; OpenPosition(i); return; } if(SPosition[i].pos_type==POSITION_TYPE_SELL) { if(InpCloseOpposite) { if(count_buys>0) { ClosePositions(POSITION_TYPE_BUY); return; } } if(InpOnlyOne) { if(count_buys+count_sells==0) { SPosition[i].waiting_transaction=true; OpenPosition(i); return; } else { ArrayRemove(SPosition,i,1); return; } } SPosition[i].waiting_transaction=true; OpenPosition(i); return; }
Lassen Sie uns die Verfolgung fortsetzen. Ich möchte Sie an den Codeblock erinnern:
if(SPosition[i].pos_type==POSITION_TYPE_BUY) { SPosition[i].waiting_transaction=true; OpenPosition(i); return; }
Der Handelsauftrag wurde gerade in die Struktur gesetzt (in der Funktion SearchTradingSignals) und das Flag waiting_transaction ist auf false gesetzt. Also setzen wir das Flag waiting_transaction auf true und übergeben den Parameter i an die Funktion OpenPosition. Dies ist die Seriennummer der Struktur im Array OpenPosition. Da die Handelsauftragsart POSITION_TYPE_BUY ist, übergeben wir die Struktur-Seriennummer an die Funktion OpenBuy.
5.2. Die Funktion OpenBuy
Das Ziel der Funktion ist es, die Vorprüfungen zu bestehen, eine Handelsanfrage zur Eröffnung von BUY zu senden und das Ergebnis sofort zu verfolgen.
Die erste Prüfung - SYMBOL_VOLUME_LIMIT
SYMBOL_VOLUME_LIMIT | Das maximal zulässige Gesamtvolumen der offenen Positionen und schwebender Aufträge in einer Richtung (entweder Kauf oder Verkauf) für dieses Symbol. Zum Beispiel können wir bei einem Volumen-Limit von 5 Lot eine offene Kaufposition von 5 Lot haben und eine Sell-Limit-Order von 5 Lot platzieren. In diesem Fall können wir jedoch weder eine schwebende Buy-Limit-Order platzieren (da das Gesamtvolumen in einer Richtung das Limit überschreitet) noch eine Sell-Limit-Order über 5 Lot platzieren. |
Wenn die Prüfung nicht bestanden wird, entfernen wir das Strukturelement aus dem Array SPosition.
Die zweite Prüfung - Marge
Wir ermitteln die nach einer Handelsoperation verbleibende freie Marge (FreeMarginCheck) und die für eine Handelsoperation notwendige Marge (MarginCheck). Danach sollte die Summe zur Sicherheit mindestens so hoch sein wie FreeMarginCheck:
if(free_margin_check>margin_check)
Wenn die Prüfung nicht bestanden wird, wird die Variable free_margin_check ausgedruckt und das Strukturelement aus dem SPosition-Array entfernt.
Die dritte Prüfung - boolsches Ergebnis der Operation Buy
Wenn die Methode Buy false zurückgibt, wird der Fehler ausgedruckt und waiting_transaction auf false gesetzt (der häufigste Grund ist Fehler 10004, requote). Damit wird ein Versuch unternommen, die KAUF-Position mit einem neuen Ticket wieder zu öffnen. Wenn das Ergebnis true ist (unten ist der Codeblock, in dem die Methode Buy Methode zurückgegeben hat true),
if(m_trade.ResultDeal()==0) { if(m_trade.ResultRetcode()==10009) // trade order went to the exchange { SPosition[index].waiting_transaction=true; SPosition[index].waiting_order_ticket=m_trade.ResultOrder(); } else { SPosition[index].waiting_transaction=false; if(InpPrintLog) Print(__FILE__," ",__FUNCTION__,", ERROR: ","#1 Buy -> false. Result Retcode: ",m_trade.ResultRetcode(), ", description of result: ",m_trade.ResultRetcodeDescription()); } if(InpPrintLog) PrintResultTrade(m_trade,m_symbol); } else { if(m_trade.ResultRetcode()==10009) { SPosition[index].waiting_transaction=true; SPosition[index].waiting_order_ticket=m_trade.ResultOrder(); } else { SPosition[index].waiting_transaction=false; if(InpPrintLog) Print(__FILE__," ",__FUNCTION__,", OK: ","#2 Buy -> true. Result Retcode: ",m_trade.ResultRetcode(), ", description of result: ",m_trade.ResultRetcodeDescription()); } if(InpPrintLog) PrintResultTrade(m_trade,m_symbol); }
dann wird ResultDeal geprüft (deal ticket).
Wenn das Deal-Ticket Null ist, prüfen wir ResultRetcode (Request execution reslut code). Der resultierende Returncode ist 10009 (z.B. die Handelsauftrag wurde an ein externes Handelssystem, wie eine Börse, gesendet, daher ist das Deal-Ticket Null). Wir setzen waiting_transaction auf true, während waiting_order_ticket ResultOrder (Auftragsticket) enthalten soll. Andernfalls (der Returncode ist nicht 10009), wird waiting_transaction auf false gesetzt und die Fehlermeldung wird ausgegeben.
Wenn das Deal-Ticket nicht Null ist (z. B. weil die Ausführung auf demselben Handelsserver erfolgt), werden die gleichen Rückgabeprüfungen durchgeführt und die Werte in Wartende_Transaktion und Wartende_Bestellungsticket geschrieben.
5.3. OnTradeTransaktion
Wenn der Handelsauftrag erfolgreich gesendet wurde, warten wir auf die Bestätigung, dass der Auftrag durchgeführt und in der Handelshistorie aufgezeichnet wurde. In OnTradeTransaction arbeiten wir mit der Variablen trans (die Struktur des MqlTradeTransaction-Typs). Die Struktur hat nur zwei Zeilen von Interesse — deal und type:
struct MqlTradeTransaction { ulong deal; // Deal ticket ulong order; // Order ticket string symbol; // Symbol name ENUM_TRADE_TRANSACTION_TYPE type; // Trading transaction type ENUM_ORDER_TYPE order_type; // Order type ENUM_ORDER_STATE order_state; // Order state ENUM_DEAL_TYPE deal_type; // Deal type ENUM_ORDER_TYPE_TIME time_type; // Order type by lifetime datetime time_expiration; // Order expiration time double price; // Price double price_trigger; // Stop Limit order trigger price double price_sl; // Stop Loss level double price_tp; // Take Profit level double volume; // Volume in lots ulong position; // Position ticket ulong position_by; // Opposite position ticket };
Sobald OnTradeTransaction TRADE_TRANSACTION_DEAL_ADD erhält (Hinzufügen eines Geschäfts zur Historie), wird eine Prüfung durchgeführt: Versuch, ein Deal in der Historie über HistoryDealSelect auszuwählen. Wenn die Auswahl fehlschlägt, wird der Fehler ausgeben. Wenn der Deal in der Historie existiert, iteriere über das SPosition-Array in einer Schleife. In der Schleife werden nur die Strukturen betrachtet, die waiting_transaction auf true gesetzt haben, während waiting_order_ticket gleich dem Ticket der Bestellung eines ausgewählten Deals ist. Wenn eine Übereinstimmung gefunden wird, setzen wir transaction_confirmed auf true. Das bedeutet, dass der Handelsauftrag ausgeführt und bestätigt wurde.
//+------------------------------------------------------------------+ //| TradeTransaction function | //+------------------------------------------------------------------+ void OnTradeTransaction(const MqlTradeTransaction &trans, const MqlTradeRequest &request, const MqlTradeResult &result) { //--- get transaction type as enumeration value ENUM_TRADE_TRANSACTION_TYPE type=trans.type; //--- if transaction is result of addition of the transaction in history if(type==TRADE_TRANSACTION_DEAL_ADD) { ResetLastError(); if(HistoryDealSelect(trans.deal)) m_deal.Ticket(trans.deal); else { Print(__FILE__," ",__FUNCTION__,", ERROR: ","HistoryDealSelect(",trans.deal,") error: ",GetLastError()); return; } if(m_deal.Symbol()==m_symbol.Name() && m_deal.Magic()==InpMagic) { if(m_deal.DealType()==DEAL_TYPE_BUY || m_deal.DealType()==DEAL_TYPE_SELL) { int size_need_position=ArraySize(SPosition); if(size_need_position>0) { for(int i=0; i<size_need_position; i++) { if(SPosition[i].waiting_transaction) if(SPosition[i].waiting_order_ticket==m_deal.Order()) { Print(__FUNCTION__," Transaction confirmed"); SPosition[i].transaction_confirmed=true; break; } } } } } } }
Bei einem neuen Tick starten wir wieder bei OnTick. Die Struktur mit true in transaction_confirmed wird aus dem SPosition-Array entfernt. Damit ist ein Handelsauftrag erteilt und verfolgt worden, bis er in der Handelshistorie erscheint.
6. Erstellen eines EA (Positionseröffnungssignale) mit Hilfe des Konstruktors
Bevor wir einen EA entwickeln, sollten wir über seine Handelsstrategie nachdenken. Betrachten wir eine einfache Strategie, die auf dem Indikator iDEMA (Double Exponential Moving Average, DEMA) basiert. Dies ist eine Standardstrategie, die im Konstruktor eingestellt ist. Ein Versuch, ein offenes Signal zu finden, wird nur unternommen, wenn ein neuer Balken erscheint, während das Handelssignal selbst ein Indikator ist, der sequentiell nach oben oder unten geht:
Abb. 8. DEMA-Strategie
Denken Sie daran, dass jede Strategie durch Anpassung der Parameter stark verändert werden kann. Wir können zum Beispiel Trailing deaktivieren, aber Take Profit und Stop Loss beibehalten. Oder umgekehrt: Take Profit und Stop Loss deaktivieren, Trailing aber beibehalten. Wir können auch die Handelsrichtungen einschränken, indem wir nur KAUFEN oder VERKAUFEN zulassen. Es ist auch möglich, die Zeitsteuerung zu aktivieren und den Handel in der Nacht einzuschränken oder, im Gegenteil, den Handel nur zur Nachtzeit einzurichten. Ein Handelssystem kann auch drastisch verändert werden, indem Parameter aus der Gruppe Zusätzliche Funktionen angepasst werden.
Im Allgemeinen wird das Rückgrat einer Handelsstrategie in der Funktion SearchTradingSignals gebildet, während alle anderen Parameter für das "Sondieren" des Marktes auf der Suche nach optimalen Ansätzen gedacht sind.
Wir erstellen also eine neue Datei, die ein leerer EA sein soll (wir führen die in den Abbildungen 3 und 4 angegebenen Schritte aus). In Schritt 4 geben wir einen eindeutigen Namen für Ihren EA an. Nehmen wir an, es ist iDEMA Full EA.mq5. Als Ergebnis erhalten wir die folgende Zwischenversion:
//+------------------------------------------------------------------+ //| iDEMA Full EA.mq5 | //| Copyright © 2021, Vladimir Karputov | //| https://www.mql5.com/en/users/barabashkakvn | //+------------------------------------------------------------------+ #property copyright "Copyright © 2021, Vladimir Karputov" #property link "https://www.mql5.com/en/users/barabashkakvn" #property version "1.00" //--- input parameters input int Input1=9; //+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+ int OnInit() { //--- //--- return(INIT_SUCCEEDED); } //+------------------------------------------------------------------+ //| Expert deinitialization function | //+------------------------------------------------------------------+ void OnDeinit(const int reason) { //--- } //+------------------------------------------------------------------+ //| Expert tick function | //+------------------------------------------------------------------+ void OnTick() { //--- } //+------------------------------------------------------------------+
Kopieren wir nun den gesamten Code aus Trading engine 3.mq5 und fügen ihn anstelle der Zeilen ein. Nun ist es an der Zeit, den "EA-Header" zu bearbeiten. Der resultierende "Header" sieht wie folgt aus:
//+------------------------------------------------------------------+ //| iDEMA Full EA.mq5 | //+------------------------------------------------------------------+ //| Trading engine 3.mq5 | //| Copyright © 2021, Vladimir Karputov | //| https://www.mql5.com/en/users/barabashkakvn | //+------------------------------------------------------------------+ #property copyright "Copyright © 2021, Vladimir Karputov" #property link "https://www.mql5.com/en/users/barabashkakvn" #property version "4.003" #property description "barabashkakvn Trading engine 4.003" #property description "Take Profit, Stop Loss and Trailing - in Points (1.00055-1.00045=10 points)" /* barabashkakvn Trading engine 4.003 */ #include <Trade\PositionInfo.mqh>
Der "Kopf" sollte wie folgt aussehen:
//+------------------------------------------------------------------+ //| iDEMA Full EA.mq5 | //| Copyright © 2021, Vladimir Karputov | //| https://www.mql5.com/en/users/barabashkakvn | //+------------------------------------------------------------------+ #property copyright "Copyright © 2021, Vladimir Karputov" #property link "https://www.mql5.com/en/users/barabashkakvn" #property version "1.001" #property description "iDEMA EA" #property description "Take Profit, Stop Loss and Trailing - in Points (1.00055-1.00045=10 points)" /* barabashkakvn Trading engine 4.003 */ #include <Trade\PositionInfo.mqh>
Wenn wir dies jetzt kompilieren, gibt es keine Fehler. Der erhaltene EA ist sogar in der Lage zu handeln.
6.1. Die Funktion SearchTradingSignals
Dies ist die Hauptfunktion, die für die Überprüfung der Verfügbarkeit von Handelsaufträgen verantwortlich ist. Betrachten wir diese Funktion Block für Block.
Nicht mehr als eine Position pro Balken:
if(iTime(m_symbol.Name(),InpWorkingPeriod,0)==m_last_deal_in) // on one bar - only one deal return(true);
Checking the trading time range:
if(!TimeControlHourMinute()) return(true);
Empfangen von Daten aus dem Indikator. Die Indikatordaten werden an das Array dema übergeben. Mit Hilfe von ArraySetAsSeries wird für dieses die umgekehrte Indizierungsreihenfolge festgelegt (das Array-Element [0] soll dem ganz rechten Balken im Chart entsprechen). Die Daten werden über die nutzerdefinierte Funktion iGetArray empfangen:
double dema[]; ArraySetAsSeries(dema,true); int start_pos=0,count=6; if(!iGetArray(handle_iCustom,0,start_pos,count,dema)) { return(false); } int size_need_position=ArraySize(SPosition); if(size_need_position>0) return(true);
Eröffnungssignal für eine KAUF-Position. Falls erforderlich (die Variable InpReverse speichert den Positions: Reverse-Eingangswert) kann ein Handelssignal umgekehrt werden. Wenn es eine Einschränkung bezüglich der Handelsrichtung gibt (die Variable InpTradeMode speichert den Handelsmodus: der Wert des Eingabeparameters) wird diese berücksichtigt:
//--- BUY Signal if(dema[m_bar_current]>dema[m_bar_current+1] && dema[m_bar_current+1]>dema[m_bar_current+3]) { if(!InpReverse) { if(InpTradeMode!=sell) { ArrayResize(SPosition,size_need_position+1); SPosition[size_need_position].pos_type=POSITION_TYPE_BUY; if(InpPrintLog) Print(__FILE__," ",__FUNCTION__,", OK: ","Signal BUY"); return(true); } } else { if(InpTradeMode!=buy) { ArrayResize(SPosition,size_need_position+1); SPosition[size_need_position].pos_type=POSITION_TYPE_SELL; if(InpPrintLog) Print(__FILE__," ",__FUNCTION__,", OK: ","Signal SELL"); return(true); } } }
Der Codeblock für ein VERKAUFS-Signal ist ähnlich.
7. Erstellen eines EA (Signale für schwebende Aufträge (pending order)) mit dem Konstruktor
Der Name des EA lautet iDEMA Full EA Pending.mq5. Wir öffnen iDEMA Full EA.mq5 und speichern ihn unter dem neuen Namen.
Eine Handelsstrategie wird immer zuerst entwickelt, dann folgt der Code. Lassen Sie uns die im Abschnitt 6 verwendete Strategie leicht abändern: Erstellen Sie den EA (Positionseröffnungssignale) mit dem Konstruktor. Das Positionseröffnungssignal KAUFEN wird durch den schwebenden Auftrag Buy-Stop ersetzt, während bei einem Positionseröffnungssignal VERKAUFEN durch den schwebenden Auftrag Sell-Stop ersetzt wird. Die folgenden Parameter werden für die schwebenden Aufträge verwendet:
- Pending: Expiration, in minutes (0 -> OFF) -> 600
- Pending: Indent -> 50 — Abstand des schwebenden Auftrags vom aktuellen Preis (wenn der Preis des schwebenden Auftrags nicht explizit festgelegt ist)
- Pending: Maximum spread (0 -> OFF). -> 12 — Wenn der aktuelle Spread den angegebenen Wert übersteigt, wird der schwebende Auftrag nicht erteilt (der EA wartet auf eine Verringerung des Spreads)
- Pending: Only one pending — aktivieren/deaktivieren durch das Flag. Only one pending order is allowed in the market -> true
- Pending: Reverse pending type — aktivieren/deaktivieren durch das Flag. Pending order reverse -> false
- Pending: New pending -> delete previous ones — Wenn ein schwebender Auftrag erteilt werden soll, dann werden alle anderen schwebenden Aufträge vorher gelöscht -> true
Die Funktion SearchTradingSignals sieht wie folgt aus:
//+------------------------------------------------------------------+ //| Search trading signals | //+------------------------------------------------------------------+ bool SearchTradingSignals(void) { if(iTime(m_symbol.Name(),InpWorkingPeriod,0)==m_last_deal_in) // on one bar - only one deal return(true); if(!TimeControlHourMinute()) return(true); double dema[]; ArraySetAsSeries(dema,true); int start_pos=0,count=6; if(!iGetArray(handle_iCustom,0,start_pos,count,dema)) { return(false); } int size_need_pending=ArraySize(SPending); if(size_need_pending>0) return(true); //--- if(InpPendingOnlyOne) if(IsPendingOrdersExists()) return(true); if(InpPendingClosePrevious) m_need_delete_all=true; //--- BUY Signal if(dema[m_bar_current]>dema[m_bar_current+1] && dema[m_bar_current+1]>dema[m_bar_current+3]) { if(!InpReverse) { if(InpTradeMode!=sell) { ArrayResize(SPending,size_need_pending+1); SPending[size_need_pending].pending_type=ORDER_TYPE_BUY_STOP; if(InpPrintLog) Print(__FILE__," ",__FUNCTION__,", OK: ","Signal BUY STOP"); return(true); } } else { if(InpTradeMode!=buy) { ArrayResize(SPending,size_need_pending+1); SPending[size_need_pending].pending_type=ORDER_TYPE_SELL_STOP; if(InpPrintLog) Print(__FILE__," ",__FUNCTION__,", OK: ","Signal SELL STOP"); return(true); } } } //--- SELL Signal if(dema[m_bar_current]<dema[m_bar_current+1] && dema[m_bar_current+1]<dema[m_bar_current+3]) { if(!InpReverse) { if(InpTradeMode!=buy) { ArrayResize(SPending,size_need_pending+1); SPending[size_need_pending].pending_type=ORDER_TYPE_SELL_STOP; if(InpPrintLog) Print(__FILE__," ",__FUNCTION__,", OK: ","Signal SELL STOP"); return(true); } } else { if(InpTradeMode!=sell) { ArrayResize(SPending,size_need_pending+1); SPending[size_need_pending].pending_type=ORDER_TYPE_BUY_STOP; if(InpPrintLog) Print(__FILE__," ",__FUNCTION__,", OK: ","Signal BUY STOP"); return(true); } } } //--- /*if(InpPendingOnlyOne) if(IsPendingOrdersExists()) return(true); if(InpPendingClosePrevious) m_need_delete_all=true; int size_need_pending=ArraySize(SPending); ArrayResize(SPending,size_need_pending+1); if(!InpPendingReverse) SPending[size_need_pending].pending_type=ORDER_TYPE_BUY_STOP; else SPending[size_need_pending].pending_type=ORDER_TYPE_SELL_STOP; SPending[size_need_pending].indent=m_pending_indent; if(InpPendingExpiration>0) SPending[size_need_pending].expiration=(long)(InpPendingExpiration*60); if(InpPrintLog) Print(__FILE__," ",__FUNCTION__,", OK: ","Signal BUY STOP");*/ //--- return(true); }
Beachten Sie, dass wir den Preis des schwebenden Auftrags nicht auf die SPending-Struktur setzen. Das bedeutet, dass der aktuelle Preis plus Abstand verwendet werden soll.
Wir haben ein Handelssignal erhalten, das erst dann ausgelöst wird (eine Pending Order wurde platziert), wenn der Spread unter den angegebene fällt:
Abb. 9. iDEMA Full EA Pending
Die dem Artikel beigefügten Dateien:
Name | Dateityp | Beschreibung |
---|---|---|
Indikatoren Code | EA | Variablen zum Speichern von Handles, Indikatoreingaben, Indikatorerstellungsblöcke |
Add indicator.mq5 | EA | Beispiel für die Arbeit mit Add indicator.mq5 — Hinzufügen eines Standardindikators |
Add custom indicator.mq5 | EA | Beispiel für das Hinzufügen eines nutzerdefinierten Indikators |
Trading engine 4.mq5 | EA | Konstruktor |
iDEMA Full EA.mq5 | EA | Erstellen eines EAs mit Hilfe des Konstruktors — Positionseröffnungssignale |
iDEMA Full EA Pending.mq5 | EA | EA erstellt mit Hilfe des Konstruktors — Pending Order Placement Signale |
Schlussfolgerung
Ich hoffe, dass dieser Satz von Handelsfunktionen Ihnen helfen wird, zuverlässigere Expert Advisors zu erstellen, die für die sich ständig ändernden Handelsbedingungen des Marktes bereit sind. Zögern Sie nicht, mit den Parametern zu experimentieren. Sie können Ihre Strategie drastisch verändern, indem Sie einige Parameter aktivieren, während Sie andere deaktivieren.
Übersetzt aus dem Russischen von MetaQuotes Ltd.
Originalartikel: https://www.mql5.com/ru/articles/9717
- Freie Handelsapplikationen
- Über 8.000 Signale zum Kopieren
- Wirtschaftsnachrichten für die Lage an den Finanzmärkte
Sie stimmen der Website-Richtlinie und den Nutzungsbedingungen zu.