
Einführung in MQL5 (Teil 10): Eine Anleitung für Anfänger zur Arbeit mit den integrierten Indikatoren in MQL5
Einführung
Willkommen zurück zu unserer MQL5-Serie! Ich freue mich, Sie zu Teil zehn begrüßen zu dürfen, in dem wir einen weiteren wichtigen Aspekt des algorithmischen Handels untersuchen werden: die Arbeit mit integrierten Indikatoren. Wie immer verspricht dieser Teil sowohl fesselnd als auch praktisch zu werden, da wir mit unserem projektbasierten Ansatz fortfahren, um sicherzustellen, dass Sie das Gelernte direkt auf Ihre Handelsstrategien anwenden können.
In diesem Artikel werden wir einen Relative Strength Index (RSI) EA entwickeln. Einer der am häufigsten verwendeten technischen Indikatoren beim Handel ist der RSI. Wir werden ein Tool entwickeln, das die Marktbedingungen verfolgt und automatisch Handelsgeschäfte macht, indem wir diesen Indikator in unseren EA integrieren. Obwohl der Relative Strength Index (RSI) das Thema dieses Artikels ist, gelten die Ideen, die wir besprechen, für die meisten integrierten Indikatoren, da sie alle nach ähnlichen Prinzipien funktionieren. Da diese Serie hauptsächlich für Anfänger gedacht ist, werde ich mich bemühen, die Erklärungen und den Code so einfach wie möglich zu halten. Ich weiß, dass es für einen Anfänger entscheidend ist, jede Phase des Prozesses vollständig zu verstehen, einschließlich der Frage, warum ein bestimmter Code geschrieben wird, was die einzelnen Komponenten bewirken und wie die verschiedenen Teile zusammenarbeiten.
Prägnanter und speichereffizienter Code wird in der professionellen MQL5-Entwicklung häufig sehr geschätzt. Diese Methode eignet sich zwar hervorragend zur Optimierung der Effizienz, kann aber gelegentlich das Verständnis des Codes erschweren, insbesondere für Personen, die mit der Programmierung nicht vertraut sind. Aus diesem Grund habe ich in dieser Serie bewusst einen umfassenderen, methodischen Ansatz gewählt, damit Sie sich während des gesamten Prozesses wohl fühlen.
In diesem Artikel erfahren Sie mehr darüber:
- Wie man die integrierten Indikatoren verwendet.
- Verwendung von Indikator-Handles zur Interaktion mit Indikatoren.
- Zugriff auf Indikatorpuffer zum Abrufen von berechneten Indikatorwerten.
- Abrufen von RSI-Werten und den entsprechenden Kerzendaten aus dem Chart.
- Identifizierung von RSI-Hochs und -Tiefs zur Umsetzung von Liquiditätsdurchbruch-Konzepten.
- Schritt-für-Schritt-Erstellung eines EA auf der Grundlage von RSI-Werten und Kerzendaten.
- Erstellen von Objekten zur Markierung signifikanter RSI-Hochs und -Tiefs direkt im Chart zur besseren Analyse.
- Festlegen eines prozentualen Risikos pro Handel, um inkonsistente Kerzengrößen bei der Verwendung integrierter Indikatoren zu berücksichtigen.
Am Ende des Artikels werden Sie ein umfassendes Verständnis für die Integration integrierter Indikatoren in Ihre Handelsstrategien haben, wobei der Schwerpunkt auf Risikomanagement, Risikomodifikation und praktischen Erkenntnissen für die Erstellung und Verfeinerung eines RSI-basierten EA liegt.
1. Integrierte Indikatoren in MQL5 verstehen
1.1. Was sind integrierte Indikatoren?
Die in MetaTrader 5 integrierten Indikatoren sind nützliche Schnellhilfen für die Marktanalyse. Sie liefern Ihnen sofortige Informationen über die Marktbedingungen, die Dynamik und die Kursbewegungen. Bollinger-Bänder zeigen beispielsweise den Umfang der Marktbewegung an, gleitende Durchschnitte helfen bei der Erkennung von Trends, und der RSI kann anzeigen, ob ein Markt überkauft oder überverkauft ist. Diese Tools vereinfachen den Handel erheblich und sparen Ihnen Zeit.
1.2. Die Indikator-Handles
In MQL5 sind Indikator-Handles eindeutige Bezeichner, die Indikatoren zugewiesen werden, wenn sie erstellt oder initialisiert werden. Diese Handles dienen als Verweise auf die Indikatoren, sodass Sie mit ihnen interagieren und auf ihre Daten zugreifen können. Wenn Sie einen Indikator zu einem Chart hinzufügen, müssen Sie bestimmte Eigenschaften wie den Zeitraum, den Preistyp und andere Einstellungen eingeben, die das Verhalten des Indikators bestimmen.
Im Code spielt das Handle eine ähnliche Rolle: Es ermöglicht Ihrem Programm, zu „wissen“, mit welchem Indikator es arbeitet und auf dessen Eigenschaften zuzugreifen. Das Indikator-Handle dient im Wesentlichen dazu, die Einstellungen für den Indikator in Ihrem Programm einzugeben und auszulösen, sodass Sie effektiv mit ihm in Ihren Handelsstrategien arbeiten können.
Sobald ein Indikator-Handle mit Funktionen wie iRSI oder iBands erstellt wurde, wird Ihr Code an diesen speziellen Indikator „gebunden“, sodass Sie seine Daten leicht abrufen und bearbeiten können. Ohne das Handle wäre Ihr Programm nicht in der Lage, zwischen verschiedenen Indikatoren zu unterscheiden und auf die berechneten Werte in den Puffern des Indikators zuzugreifen. Wenn Sie beispielsweise die Einstellungen eines Indikators in Ihren Code eingeben möchten, verwenden Sie eine Funktion wie iRSI, um die erforderlichen Parameter (wie den Zeitraum, den angewandten Preis und die Verschiebung) festzulegen, die dann den Griff für den RSI-Indikator erstellen.
Syntax:
iRSI(symbol, period, rsi_period, applied_price);
Erläuterung:
- symbol: Dieser Parameter gibt das Symbol (Währungspaar, Aktie oder Vermögenswert) an, für das der RSI berechnet werden soll.
- period: Dies ist der Zeitraum (oder Zeitrahmen), für den der RSI berechnet wird. Er legt fest, wie weit zurück der RSI Datenpunkte berücksichtigen soll.
- rsi_period: Dies ist die Anzahl der Perioden, die für die Berechnung des RSI verwendet werden. Der RSI wird in der Regel mit 14 Perioden berechnet, kann aber an die jeweilige Strategie angepasst werden.
- applied_price: Dieser Parameter legt fest, welcher Preistyp für die Berechnung des RSI verwendet werden soll. Der RSI kann auf verschiedenen Kurswerten beruhen, z. B. auf dem Schlusskurs, dem Eröffnungskurs oder dem Höchst-/Tiefstkurs.
int rsi_handle = iRSI(_Symbol,PERIOD_CURRENT,14,PRICE_CLOSE);
Erläuterung:
int rsi_handle:
- Deklariert eine Integer-Variable rsi_handle zum Speichern des RSI-Indikator-Handles (eine eindeutige ID für den Indikator).
iRSI(...):
- Die Funktion, die zur Berechnung des Relative Strength Index (RSI) für das angegebene Symbol, den Zeitrahmen und die Einstellungen verwendet wird.
_Symbol:
- Bezieht sich auf das aktuelle Handelssymbol (z.B. EUR/USD) im Chart. Es wird automatisch das Symbol verwendet, mit dem Sie gerade arbeiten.
PERIOD_CURRENT:
- Bezieht sich auf den Zeitrahmen des Charts (z. B. 1 Stunde, 1 Tag). Sie sorgt dafür, dass der RSI auf der Grundlage des aktiven Zeitrahmens des Charts berechnet wird.
14:
- Die Periodenlänge des RSI gibt die Anzahl der Balken/Kerzen an, die für die Berechnung verwendet werden sollen (in der Regel 14 Balken).
PRICE_CLOSE:
- Legt fest, dass der RSI anhand der Schlusskurse jedes Balkens oder jeder Kerze berechnet wird.
Auf diese Weise geben Sie die Details der Indikatoren direkt in Ihren Code ein, ähnlich wie Sie sie in einem MetaTrader 5-Chart einrichten würden. Bei der Verwendung von Funktionen wie iRSI legen Sie das Symbol, den Zeitrahmen, den Zeitraum und den Preistyp fest, so wie Sie es auch bei der Anwendung eines Indikators auf dem Chart tun würden. Dadurch kann Ihr Code auf die Daten des Indikators zugreifen und mit ihnen arbeiten, um sicherzustellen, dass Ihre Handelsstrategie mit den angegebenen Einstellungen wie erwartet funktioniert.
Die gleichen Prinzipien können auf andere integrierte Indikatoren im MetaTrader 5 angewendet werden. Sie können zum Beispiel verschiedene Funktionen für verschiedene Indikatoren verwenden, die jeweils spezifische Parameter erfordern. Hier sind fünf häufig verwendete Funktionen:
- iBands - Für Bollinger Bänder können Sie das Symbol, den Zeitrahmen, den Zeitraum, die Abweichung und den angewandten Preis festlegen.
- iMA - Für den gleitenden Durchschnitt werden das Symbol, der Zeitrahmen, der Zeitraum, die Verschiebung, die Methode und der angewandte Preis angegeben.
- iMACD - Für den MACD definieren Sie das Symbol, den Zeitrahmen, die schnellen und langsamen EMAs, die Signalperiode und den angewandten Preis.
- iADX - Für Average Directional Index, wird das Symbol, der Zeitrahmen und die Periode angegeben.
Mit einer Reihe von Instrumenten für die technische Analyse, die Ihre Handelstechniken verbessern, bietet MQL5 viel mehr integrierte Indikatorfunktionen. Die Anwendung der gleichen Ideen auf andere Indikatoren ist einfach, wenn man weiß, wie man mit einem Indikator arbeitet. Die Indikatoren, die am besten zu Ihren Handelsanforderungen passen, finden Sie, wenn Sie sich eingehender mit der MQL5-Dokumentation beschäftigen.
1.2. Indikator Puffer
Nach der Definition eines Indikators mit Hilfe eines Handles besteht der nächste Schritt darin, seine Daten abzurufen. Dies geschieht über Indikatorpuffer (buffer), in denen die berechneten Werte eines Indikators für jeden Preispunkt im Chart gespeichert werden. Jeder Indikator verfügt über eine bestimmte Anzahl von Puffern, je nach Art der von ihm erzeugten Daten:
Moving Average (MA)
Dieser Indikator hat 1 Puffer, der die berechneten gleitenden Durchschnittswerte bei jeder Kerze speichert.
Relative Strength Index (RSI)
In ähnlicher Weise hat der RSI-Indikator 1 Puffer zum Speichern von RSI-Werten
Bollinger Bands
Dieser Indikator verwendet 3 Puffer zum Speichern von Daten.
- Das mittlere Band (Index 0) ist die Haupttrendlinie.
- Das obere Band (Index 1) stellt ein potenziell überkauftes Niveau dar.
- Das untere Band (Index 2) stellt ein potenziell überverkauftes Niveau dar.
Auf diese Puffer kann programmatisch mit der Funktion CopyBuffer() zugegriffen werden.
Die Funktion CopyBuffer() in MQL5 wird verwendet, um Daten aus dem Puffer eines Indikators in ein Array zur weiteren Analyse oder Entscheidungsfindung abzurufen. Sobald Sie ein Indikator-Handle mit Funktionen wie iRSI oder iBands erstellt haben, verwenden Sie CopyBuffer(), um auf die berechneten Indikatorwerte zuzugreifen.
Syntax:int CopyBuffer(indicator_handle, buffer_number, start_position, count, buffer);
Parameter:
- indicator_handle: Der eindeutige Bezeichner (Handle) des zuvor erstellten Indikators, z. B. von iRSI oder iBands.
- buffer_number: Der Index des Puffers, aus dem Daten abgerufen werden sollen. Zum Beispiel Bollinger Bands, 0 für das mittlere Band, 1 für das obere Band, 2 für das untere Band. Bei RSI oder gleitendem Durchschnitt nur 0, da sie nur einen Puffer haben.
- start_pos: Die Ausgangsposition im Chart (0 = jüngste Kerze).
- count: Die Anzahl der Werte, die aus dem Puffer abgerufen werden sollen.
- buffer[]: Das Array, in dem die Daten aus dem Puffer des Indikators gespeichert werden sollen.
int band_handle; // Bollinger Bands handle double upper_band[], mid_band[], lower_band[]; // Buffers for the bands void OnStart() { // Create the Bollinger Bands indicator band_handle = iBands(_Symbol, PERIOD_CURRENT, 20, 0, 2.0, PRICE_CLOSE); // Ensure arrays are series for correct indexing ArraySetAsSeries(upper_band, true); ArraySetAsSeries(mid_band, true); ArraySetAsSeries(lower_band, true); // Copy data from buffers (Index 0 = Middle, 1 = Upper, 2 = Lower) CopyBuffer(band_handle, 0, 0, 10, mid_band); // Middle band data CopyBuffer(band_handle, 1, 0, 10, upper_band); // Upper band data CopyBuffer(band_handle, 2, 0, 10, lower_band); // Lower band data // Print the most recent values Print("Middle Band: ", mid_band[0]); Print("Upper Band: ", upper_band[0]); Print("Lower Band: ", lower_band[0]); }
Erläuterung:
In diesem Beispiel wird gezeigt, wie die Funktion CopyBuffer() verwendet wird, um Daten aus dem Bollinger Bands Indikator abzurufen. Zunächst erstellt die Funktion iBands ein Indikator-Handle mit spezifischen Parametern wie dem Symbol (_Symbol), dem Zeitrahmen (PERIOD_CURRENT), der Periode (20), der Verschiebung (0), der Abweichung (2,0) und dem angewandten Preis (PRICE_CLOSE). Dieses Handle, das in band_handle gespeichert ist, dient als Verbindung zwischen unserer Anwendung und dem Bollinger-Band-Indikator und ermöglicht uns den Zugriff auf die berechneten Zahlen. Die Daten für das obere, mittlere und untere Band werden dann in drei Arrays gespeichert, die wir deklarieren: upper_band, mid_band und lower_band. Der jüngste Wert steht bei Index 0, wenn diese Arrays mit ArraySetAsSeries als Serie gesetzt werden. Das Einstellen dieser Arrays als Zeitreihe mit ArraySetAsSeries stellt sicher, dass der jüngste Wert bei Index 0 steht.
Jeder der drei Puffer des Bollinger-Band-Indikators — Puffer 0 für das mittlere Band, Puffer 1 für das obere Band und Puffer 2 für das untere Band — hat einen eigenen Aufruf der Funktion CopyBuffer(). Jeder Aufruf speichert die letzten elf Werte im entsprechenden Array, nachdem er sie aus dem entsprechenden Puffer abgerufen hat. Schließlich druckt das Programm die letzten Werte des mittleren, oberen und unteren Bandes mit Print() aus. Die Verbindung zwischen dem Preis und den Bollinger Bändern kann dann genutzt werden, um mögliche Ausbrüche oder Trendumkehrungen zu identifizieren, neben anderen Verwendungen dieser Daten für zusätzliche Analysen oder Entscheidungen innerhalb des Handelssystems.
2. Entwicklung des RSI-basierten Expert Advisors (EA)
2.1. Wie der EA funktioniert:
Der EA prüft überkaufte und überverkaufte Niveaus als entscheidende Marker für eine mögliche Trendwende.
2.1.1. Logik für einen Kauf
- Prüfung, ob der RSI-Wert unter 30 liegt.
- Feststellung, ob der RSI ein Tief gebildet hat.
- Der EA identifiziert das Tief der entsprechenden Kerze auf dem normalen Chart.
- Warten auf den Liquiditätsdurchbruch des Kurses, wenn er unter das Tief der angegebenen Kerze fällt.
- Wenn die erste Aufwärtskerze über das Tief schließt, nachdem der Kurs das Tief durchbrochen hat, leitet der EA einen Kauf in Erwartung einer Aufwärtsbewegung ein.
2.1.2. Logik für einen Verkauf
- Prüfung, ob der RSI-Wert über 70 liegt.
- Feststellung, ob der RSI ein Hoch gebildet hat.
- Der EA identifiziert das Hoch der entsprechenden Kerze auf dem normalen Chart.
- Er beobachtet dann die Kursentwicklung und wartet darauf, dass der Kurs über das Hoch der identifizierten Kerze ausbricht und die Liquidität mitnimmt.
- Nachdem der Kurs das Hoch durchbrochen hat, platziert der EA ein Verkaufsgeschäft, sobald die erste Abwärtskerze unter dem Hoch schließt, da er eine Abwärtsbewegung erwartet.
2.2. Hinzufügen der Handelsbibliothek
Der erste Schritt bei der Erstellung eines Expert Advisors (EA), der Positionen eröffnet, schließt oder ändert, ist das Einbinden der Handelsbibliothek. Diese Bibliothek bietet wesentliche Funktionen für die programmatische Ausführung und Verwaltung von Geschäften.
Beispiel:#include <Trade/Trade.mqh> // Include the trade library for trading functions // Create an instance of the CTrade class for trading operations CTrade trade; //magic number input int MagicNumber = 1111; //+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+ int OnInit() { // Set the magic number for the EA's trades trade.SetExpertMagicNumber(MagicNumber); // Return initialization success return(INIT_SUCCEEDED); }
Erläuterung:
Einbinden der Handelsbibliothek
- Damit die Klasse CTrade verwendet werden kann, muss die Handelsbibliothek mit der Deklaration #include <Trade/Trade.mqh> eingebunden werden. Diese Klasse vereinfacht die Handelsprozesse, indem sie Funktionen zur Verwaltung von Take-Profit, Stop-Loss, Marktaufträgen, schwebenden Aufträgen und anderen handelsbezogenen Aufgaben bietet.
Erstellen einer Instanz von CTrade
- Die Zeile „CTrade trade;“ erstellt eine Instanz der Klasse CTrade, die im gesamten EA zur Ausführung und Verwaltung von Handelsgeschäfte verwendet wird.
- Die MagicNumber wird mit dem Schlüsselwort Input als Eingangsvariable deklariert. Damit kann der Nutzer des EA eine eindeutige Kennung für die vom EA getätigten Geschäfte festlegen.
Warum ist das wichtig?
- Die magische Zahl stellt sicher, dass die vom EA platzierten Handelsgeschäfte getrennt von anderen Handelsgeschäften identifiziert und verwaltet werden können.
- Sie ermöglicht es dem EA, seine Handelsgeschäfte von manuellen Handelsgeschäften oder Handelsgeschäften anderer EAs zu unterscheiden.
- Durch Ändern der magischen Zahl, der „MagicNumber“, kann der Nutzer den EA auf verschiedenen Vermögenswerten oder sogar auf demselben Vermögenswert unter verschiedenen Marktbedingungen oder Zeitrahmen laufen lassen.
int OnInit() { // Set the magic number for the EA's trades trade.SetExpertMagicNumber(MagicNumber); // Return initialization success return(INIT_SUCCEEDED); }
Initialisierungsfunktion:
Die Funktion OnInit() wird ausgeführt, wenn der EA startet. Hier weist die Methode SetExpertMagicNumber() dem EA die angegebene magische Zahl zu, wodurch sichergestellt wird, dass alle von dieser Instanz des EA eröffneten Handelsgeschäfte diese Kennung tragen. Dieser Ansatz bietet Flexibilität, da die Nutzer die magische Zahl für verschiedene Handelsszenarien anpassen können, was es einfacher macht, mehrere Instanzen des EA für verschiedene Instrumente oder Strategien zu verwalten.
2.3. Abrufen von RSI-Werten und der Kerzendaten
Das Abrufen genauer Marktdaten ist für den EA von grundlegender Bedeutung, um effektive Handelsoperationen durchzuführen. Die Logik dieses EA dreht sich um das Verständnis von Kerzen-Mustern und des RSI-Indikators, weshalb es entscheidend ist, diese Werte zuverlässig zu erfassen. Anhand dieser Daten kann der EA analysieren, ob RSI-Hochs und -Tiefs mit bestimmten Kerzen übereinstimmen, was für die Bestimmung potenzieller Einstiegs- und Ausstiegspunkte entscheidend ist. Durch die Kombination von RSI-Werten mit den Kerzendaten stellt der EA sicher, dass er die Marktbedingungen ganzheitlich auswertet und überkaufte oder überverkaufte Bedingungen im Zusammenhang mit der Preisbewegung identifiziert. Dieser Ansatz stärkt die Fähigkeit des EAs, logische und präzise Handelsentscheidungen zu treffen.
2.3.1. Abrufen von RSI-Werten
Wie bereits erwähnt, ist die Ermittlung der RSI-Werte für den EA ein wesentlicher Schritt zur Bewertung der Marktbedingungen. Dies geschieht in zwei Teilen: Einrichten der Eigenschaften des RSI-Indikators mit dem iRSI-Handle und Abrufen der tatsächlichen RSI-Werte mit der Funktion CopyBuffer.
Beispiel:
#include <Trade/Trade.mqh> CTrade trade; // Magic number input int MagicNumber = 1111; //RSI handle int rsi_handle; double rsi_buffer[]; //+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+ int OnInit() { // Configure RSI buffer as a series for easier indexing ArraySetAsSeries(rsi_buffer, true); // Initialize RSI handle for the current symbol, timeframe, and parameters rsi_handle = iRSI(_Symbol, PERIOD_CURRENT, 14, PRICE_CLOSE); // Set the magic number (for trading, not relevant to RSI retrieval here) trade.SetExpertMagicNumber(MagicNumber); return (INIT_SUCCEEDED); // Indicate successful initialization } //+------------------------------------------------------------------+ //| Expert tick function | //+------------------------------------------------------------------+ void OnTick() { // Copy RSI values from the indicator into the buffer CopyBuffer(rsi_handle, 0, 1, 100, rsi_buffer); // Display the most recent RSI value for verification Comment("rsi_buffer[0]: ", rsi_buffer[0]); }
Erläuterung:
Anordnen des RSI-Puffers
- In der Funktion OnInit wird die Funktion ArraySetAsSeries verwendet, um den rsi_buffer als Serien-Array zu konfigurieren, was bedeutet, dass die neuesten Daten bei Index 0 stehen. Dies vereinfacht den Zugriff auf die neuesten RSI-Werte.
Erstellen des RSI-Handles
- Innerhalb der Funktion OnTick wird die Funktion iRSI aufgerufen, um den RSI-Handle (rsi_handle) zu initialisieren. Diese Funktion legt die Eigenschaften des RSI-Indikators fest, wie das Symbol (_Symbol), den Zeitrahmen (PERIOD_CURRENT), die Periodenlänge (14) und den angewandten Preis (PRICE_CLOSE).
Parameter:
- _Symbol: Das Handelsinstrument, für das der RSI berechnet wird.
- PERIOD_CURRENT: Der aktuelle Zeitrahmen des Charts.
- 14: Die RSI-Periode (ein häufig verwendeter Standardwert).
- PRICE_CLOSE: Der RSI wird auf der Grundlage der Schlusskurse berechnet.
Kopieren von RSI-Werten
- Die Funktion CopyBuffer ruft die RSI-Werte für den Indikator ab und füllt das Array rsi_buffer. Es beginnt mit dem Kopieren des letzten Wertes (Offset 1) und ruft bis zu 100 Werte ab.
Parameter:
- 0: Gibt den Pufferindex des RSI-Indikators an (0 für die Hauptlinie).
- 1: Beginnen Sie mit dem Kopieren des zweitjüngsten RSI-Werts, da der jüngste RSI-Wert noch in der Entstehung begriffen ist und seine Werte instabil sind.
- 100: Abruf von bis zu 100 Werten.
Dieser Ansatz stellt sicher, dass der EA Zugang zu aktuellen RSI-Werten hat, die für die Analyse von überkauften und überverkauften Bedingungen und für Handelsentscheidungen entscheidend sind.
Anzeige des RSI-Wertes
- Der Code validiert das Datenabrufverfahren, indem er den letzten RSI-Wert (rsi_buffer[0]) im Chart mit der Funktion Comment anzeigt.
- Dieser Ansatz stellt sicher, dass der EA Zugang zu aktuellen RSI-Werten hat, die für die Analyse von überkauften und überverkauften Bedingungen und für Handelsentscheidungen entscheidend sind.
Ausgabe:
2.3.2. Abrufen der Kerzendaten
Um die Preisaktivität zu analysieren und sie mit den RSI-Werten abzugleichen, ist es unerlässlich, Kerzendaten abzurufen. Um wichtige Kerzen-Informationen wie Eröffnungs-, Schluss-, Höchst- und Tiefstkurse sowie die dazugehörigen Zeitstempel abzurufen, verwendet der EA bestimmte Funktionen. Um die Marktbedingungen zu beurteilen und kluge Handelsentscheidungen zu treffen, sind diese Daten unerlässlich.
Beispiel:
#include <Trade/Trade.mqh> CTrade trade; // Magic number input int MagicNumber = 1111; int rsi_handle; double rsi_buffer[]; double open[]; double close[]; double high[]; double low[]; datetime time[]; //+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+ int OnInit() { // Configure RSI buffer as a series for easier indexing ArraySetAsSeries(rsi_buffer, true); // Initialize RSI handle for the current symbol, timeframe, and parameters rsi_handle = iRSI(_Symbol, PERIOD_CURRENT, 14, PRICE_CLOSE); // Configure candlestick arrays as series ArraySetAsSeries(open, true); ArraySetAsSeries(close, true); ArraySetAsSeries(high, true); ArraySetAsSeries(low, true); ArraySetAsSeries(time, true); // Set the magic number trade.SetExpertMagicNumber(MagicNumber); return (INIT_SUCCEEDED); // Indicate successful initialization } //+------------------------------------------------------------------+ //| Expert tick function | //+------------------------------------------------------------------+ void OnTick() { // Copy RSI values from the indicator into the buffer CopyBuffer(rsi_handle, 0, 1, 100, rsi_buffer); // Copy candlestick data CopyOpen(_Symbol, PERIOD_CURRENT, 1, 100, open); CopyClose(_Symbol, PERIOD_CURRENT, 1, 100, close); CopyHigh(_Symbol, PERIOD_CURRENT, 1, 100, high); CopyLow(_Symbol, PERIOD_CURRENT, 1, 100, low); CopyTime(_Symbol, PERIOD_CURRENT, 1, 100, time); // Display the most recent candlestick data for verification Comment("open[0]: ", open[0], "\nclose[0]: ", close[0], "\nhigh[0]: ", high[0], "\nlow[0]: ", low[0], "\ntime[0]: ", time[0]); }
Erläuterung:
Anordnen der Kerzen-Arrays
- In der Funktion OnInit wird die Funktion ArraySetAsSeries verwendet, um die Arrays Open, Close, High, Low und Time als Zeitreihen zu konfigurieren.
- Dadurch wird sichergestellt, dass die neuesten Daten bei Index 0 erscheinen, was das Abrufen und Analysieren der letzten abgeschlossenen Kerzendaten vereinfacht.
Kopieren der Kerzendaten
- In der Funktion OnTick werden die Funktionen CopyOpen, CopyClose, CopyHigh, CopyLow und CopyTime verwendet, um die entsprechenden Daten für Kerzens abzurufen.
Parameter:
- _Symbol: Das aktuelle Handelsinstrument.
- PERIOD_CURRENT: Der aktuelle Chart-Zeitrahmen (z. B. M1, H1).
- 1: Beginnen Sie mit dem Kopieren ab der zweitjüngsten Kerze, da sich die jüngste Kerze (Index 0) noch bildet und ihre Werte instabil sind.
- 100: Abrufen von 100 Kerzen-Werte für die Analyse.
Jede Funktion füllt ihr jeweiliges Array auf:
- open[]: Enthält die Eröffnungskurse der angegebenen Kerzen.
- close[]: Enthält die Schlusskurse.
- high[]: Enthält die Hochs.
- low[]: Enthält die Tiefs.
- time[]: Speichert die Eröffnungszeiten für die Synchronisierung mit der Preisaktion.
Anzeige der Kerzendaten
Die Funktion Comment wird verwendet, um die letzten abgeschlossenen Kerzendaten (Index 0) zu Validierungszwecken im Chart anzuzeigen:
- open[0]: Der Eröffnungspreis.
- close[0]: Der Schlusskurs.
- high[0]: Das Hoch.
- low[0]: Das Tief
- time[0]: Der Zeitstempel, der angibt, wann die Kerze geöffnet wurde.
2.3.3. Bestimmung der Hochs und Tiefs des RSI
Der EA muss RSI-Tiefs erkennen, wenn der RSI unter ein überkauftes Niveau (30) fällt, und RSI-Hochs, wenn er über ein überkauftes Niveau (70) steigt. Um mögliche interessante Bereiche zu identifizieren, werden diese RSI-Punkte dann mit bestimmten Kerzen auf dem Chart verbunden. Diese Phase ist die Grundlage für den Betrieb des EA, da diese Marker für die Konfiguration der Logik für Liquiditätsdurchbrüche unerlässlich sind.
Die Bestimmung der Hochs und Tiefs des RSI ist ziemlich einfach:
RSI-Tief:RSI-Hoch:
Beispiel:
// Magic number input int MagicNumber = 1111; // RSI handle and buffer int rsi_handle; double rsi_buffer[]; // Candlestick data arrays double open[]; double close[]; double high[]; double low[]; datetime time[]; // Variables to store high and low levels double max_high = 0; // Maximum high for the candlesticks datetime min_time1 = 0; // Time of the maximum high candlestick double min_low = 0; // Minimum low for the candlesticks datetime min_time2 = 0; // Time of the minimum low candlestick // Variables to store RSI highs and lows datetime time_low = 0; // Time of the RSI low datetime times_high = 0; // Time of the RSI high //+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+ int OnInit() { // Configure RSI buffer as a series for easier indexing ArraySetAsSeries(rsi_buffer, true); // Initialize RSI handle for the current symbol, timeframe, and parameters rsi_handle = iRSI(_Symbol, PERIOD_CURRENT, 14, PRICE_CLOSE); // Configure candlestick arrays as series ArraySetAsSeries(open, true); ArraySetAsSeries(close, true); ArraySetAsSeries(high, true); ArraySetAsSeries(low, true); ArraySetAsSeries(time, true); // Set the magic number for the EA trade.SetExpertMagicNumber(MagicNumber); return (INIT_SUCCEEDED); // Indicate successful initialization } //+------------------------------------------------------------------+ //| Expert tick function | | //+------------------------------------------------------------------+ void OnTick() { // Copy RSI values from the indicator into the buffer CopyBuffer(rsi_handle, 0, 1, 100, rsi_buffer); // Copy candlestick data (open, close, high, low, time) CopyOpen(_Symbol, PERIOD_CURRENT, 1, 100, open); CopyClose(_Symbol, PERIOD_CURRENT, 1, 100, close); CopyHigh(_Symbol, PERIOD_CURRENT, 1, 100, high); CopyLow(_Symbol, PERIOD_CURRENT, 1, 100, low); CopyTime(_Symbol, PERIOD_CURRENT, 1, 100, time); // Loop to find the maximum high from a bullish candlestick pattern for(int i = 0; i < 12; i++) { // Check for a bullish pattern: current close < open and previous close > open if(close[i] < open[i] && close[i+1] > open[i+1]) { // Calculate the maximum high between the two candlesticks max_high = MathMax(high[i], high[i+1]); // Record the time of the corresponding candlestick min_time1 = MathMin(time[i], time[i+1]); break; } } // Loop to find the minimum low from a bearish candlestick pattern for(int i = 0; i < 12; i++) { // Check for a bearish pattern: current close > open and previous close < open if(close[i] > open[i] && close[i+1] < open[i+1]) { // Calculate the minimum low between the two candlesticks min_low = MathMin(low[i], low[i+1]); // Record the time of the corresponding candlestick min_time2 = MathMin(time[i], time[i+1]); break; } } // Loop to find the RSI low point for(int i = 0; i < 12; i++) { // Check if the RSI is oversold and forms a low point if(rsi_buffer[i+1] < 30 && rsi_buffer[i] > rsi_buffer[i+1]) { // Record the time of the RSI low time_low = time[i+1]; break; } } // Loop to find the RSI high point for(int i = 0; i < 12; i++) { // Check if the RSI is overbought and forms a high point if(rsi_buffer[i+1] > 70 && rsi_buffer[i] < rsi_buffer[i+1]) { // Record the time of the RSI high times_high = time[i+1]; break; } } }
Erläuterung:
RSI-Tiefs (überverkaufte Bedingungen)
- Die Schleife sucht nach Punkten, an denen der RSI unter 30 liegt (rsi_buffer[i+1] < 30) und zu steigen beginnt (rsi_buffer[i] > rsi_buffer[i+1]).
- Diese Bedingungen deuten darauf hin, dass der RSI einen Tiefstand erreicht hat und eine Richtungsumkehr vollzieht.
- Der Zeitstempel des entsprechenden Kerzens wird in time_low gespeichert.
RSI-Hochs (überkaufte Bedingungen)
- Die Schleife sucht nach Punkten, an denen der RSI über 70 liegt (rsi_buffer[i+1] > 70) und zu fallen beginnt (rsi_buffer[i] < rsi_buffer[i+1]).
- Diese Bedingungen deuten darauf hin, dass der RSI einen Höchststand erreicht hat und eine Richtungsumkehr vollzieht.
- Der Zeitstempel des entsprechenden Kerzens wird in times_high gespeichert.
2.3.4. Markieren der Tiefs und Hochs im Chart
2.3.4.1. Markieren der Tiefs im Chart
Sobald die Logik zur Identifizierung des RSI-Tiefs erfüllt ist, wertet das Programm die Kerzendaten aus, um das entsprechende Minimum-Tief auf dem Chart zu ermitteln. Da zwei RSI-Werte erforderlich sind, um ein Tief zu bilden, verwendet der EA die Tiefs der beiden übereinstimmenden Kerzen, um das genaue Niveau zu bestimmen.
Dieser Mindestbetrag ist entscheidend, da er den Bereich festlegt, in dem das Programm auf einen potenziellen Liquiditätsabfluss wartet.
Logik:
- Der RSI bildet ein Tief, wenn:
- rsi_buffer[i+1] < 30: Der RSI sinkt in den überverkauften Bereich.
- rsi_buffer[i] > rsi_buffer[i+1]: Der RSI beginnt zu steigen, nachdem er seinen tiefsten Punkt erreicht hat.
- Sobald das RSI-Tief bestätigt ist, ermittelt das Programm das tiefste Tief anhand der Tiefstwerte der beiden entsprechenden Kerzen auf dem Chart.
- Dieses tiefste Tief legt das Niveau fest, bei dem der EA auf einen potenziellen Liquiditätsdurchbruch wartet.
- Sobald der RSI-Tiefpunkt identifiziert ist, ruft das Programm die Tiefstpreise der beiden entsprechenden Kerzen aus dem Array der Tiefs ab.
- Mit Hilfe der Funktion MathMin() wird der kleinste Wert zwischen diesen beiden Tiefstständen berechnet und damit der Pegel für die Überwachung der Liquiditätsabrufe markiert.
- Dieses tiefste Tief dient als der Punkt, an dem der EA eine potenzielle Umkehr oder einen Liquiditätsschwung erwartet, ein kritischer Faktor bei Handelsentscheidungen.
// Loop to find RSI and candlestick lows for(int i = 0; i < 12; i++) { // Check if the RSI is oversold and forms a low point if(rsi_buffer[i+1] < 30 && rsi_buffer[i] > rsi_buffer[i+1]) { // Record the time of the RSI low time_low = time[i+1]; // Find the minimum low from the two corresponding candlesticks min_low = (double)MathMin(low[i], low[i+1]); // Break the loop once the low is found break; } }
2.3.4.2.Markierung des Hochs auf dem Chart
Wenn die Logik zur Identifizierung des RSI-Hochs erfüllt ist, wertet das Programm die entsprechenden Kerzendaten aus, um das höchste Hoch im Chart zu bestimmen. Da zwei RSI-Werte erforderlich sind, um ein Hoch zu bilden, verwendet der EA die Hochs der beiden übereinstimmenden Kerzen, um das genaue Niveau zu bestimmen.
Dieses Hoch dient als Referenz für die Festlegung des Bereichs, in dem der EA auf einen potenziellen Liquiditätsdurchbruch wartet. Der Ansatz ist die Umkehrung der Logik für die Tiefs.
Logik:
- Der RSI bildet ein Hoch, wenn:
- rsi_buffer[i+1] > 70: Der RSI steigt in den überkauften Bereich.
- rsi_buffer[i] < rsi_buffer[i+1]: Der RSI beginnt zu fallen, nachdem er seinen höchsten Punkt erreicht hat.
- Sobald der RSI-Höchststand bestätigt ist, ermittelt das Programm das höchste Hoch anhand des Arrays der Hochs der beiden entsprechenden Kerzen auf dem Chart.
- Dieses Hoch legt das Niveau fest, bei dem der EA auf einen potenziellen Liquiditätsdurchbruch wartet.
- Sobald das RSI-Hoch identifiziert ist, ruft das Programm die Hochs der beiden entsprechenden Kerzen aus dem Hoch-Array ab.
- Mithilfe der Funktion MathMax() wird der größere Wert zwischen diesen beiden Hochs berechnet, der den Pegel für die Überwachung von Liquiditätsdurchbrüche markiert.
// Loop to find RSI and candlestick highs for(int i = 0; i < 12; i++) { // Check if the RSI is overbought and forms a high point if(rsi_buffer[i+1] > 70 && rsi_buffer[i] < rsi_buffer[i+1]) { // Record the time of the RSI high times_high = time[i+1]; // Find the maximum high from the two corresponding candlesticks max_high = (double)MathMax(high[i], high[i+1]); // Break the loop once the high is found break; } }
2.3.5. Kontrolle der RSI-Hochs und -Tiefs mit einer 12-Kerzen-Verzögerung
Eine der Schwierigkeiten bei der Verwendung des RSI zur Identifizierung von Hochs und Tiefs besteht darin, dass innerhalb eines kurzen Zeitraums mehrere RSI-Hochs oder -Tiefs auftreten können, insbesondere wenn der RSI in der überkauften oder überverkauften Zone bleibt. Infolgedessen können verschiedene Ebenen für Liquiditätsdurchbrüche bestimmt werden. Die Logik fügt eine 12-Kerzen-Wartezeit hinzu, um dieses Problem zu lösen, und stellt sicher, dass nach der Bestimmung eines Hochs oder Tiefs keine Aktualisierung erfolgt, bis sich 12 Kerzen gebildet haben.
Beispiel:
// Magic number input int MagicNumber = 1111; int rsi_handle; double rsi_buffer[]; double open[]; double close[]; double high[]; double low[]; datetime time[]; double max_high = 0; datetime min_time1 = 0; double min_low = 0; datetime min_time2 = 0; datetime time_low = 0; datetime times_high = 0; //+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+ int OnInit() { // Configure RSI buffer as a series for easier indexing ArraySetAsSeries(rsi_buffer, true); // Initialize RSI handle for the current symbol, timeframe, and parameters rsi_handle = iRSI(_Symbol, PERIOD_CURRENT, 14, PRICE_CLOSE); // Configure candlestick arrays as series ArraySetAsSeries(open, true); ArraySetAsSeries(close, true); ArraySetAsSeries(high, true); ArraySetAsSeries(low, true); ArraySetAsSeries(time, true); // Set the magic number trade.SetExpertMagicNumber(MagicNumber); return (INIT_SUCCEEDED); // Indicate successful initialization } //+------------------------------------------------------------------+ //| Expert tick function | //+------------------------------------------------------------------+ void OnTick() { // Copy RSI values from the indicator into the buffer CopyBuffer(rsi_handle, 0, 1, 100, rsi_buffer); // Copy candlestick data CopyOpen(_Symbol, PERIOD_CURRENT, 1, 100, open); CopyClose(_Symbol, PERIOD_CURRENT, 1, 100, close); CopyHigh(_Symbol, PERIOD_CURRENT, 1, 100, high); CopyLow(_Symbol, PERIOD_CURRENT, 1, 100, low); CopyTime(_Symbol, PERIOD_CURRENT, 1, 100, time); static double max_high_static = max_high; static datetime min_time1_static = min_time1; static double min_low_static = min_low; static datetime min_time2_static = min_time2; int total_bar_high = Bars(_Symbol,PERIOD_CURRENT,min_time1_static,TimeCurrent()); for(int i = 0; i < 12; i++) { if(close[i] < open[i] && close[i+1] > open[i+1]) { max_high = (double)MathMax(high[i],high[i+1]); min_time1 = (datetime)MathMin(time[i],time[i+1]); break; } } int total_bar_low = Bars(_Symbol,PERIOD_CURRENT,min_time2_static,TimeCurrent()); for(int i = 0; i < 12; i++) { if(close[i] > open[i] && close[i+1] < open[i+1]) { min_low = (double)MathMin(low[i],low[i+1]); min_time2 = (datetime)MathMin(time[i],time[i+1]); break; } } for(int i = 0; i < 12; i++) { if(rsi_buffer[i+1] < 30 && rsi_buffer[i] > rsi_buffer[i+1]) { time_low = time[i+1]; break; } } for(int i = 0; i < 12; i++) { if(rsi_buffer[i+1] > 70 && rsi_buffer[i] < rsi_buffer[i+1]) { times_high = time[i+1]; break; } } if((total_bar_high == 0 || total_bar_high > 12) && (min_time1 == times_high)) { max_high_static = max_high; min_time1_static = min_time1; } else if(min_time1 != times_high && total_bar_high > 13) { max_high_static = 0; min_time1_static = 0; } if((total_bar_low == 0 || total_bar_low > 12) && (min_time2 == time_low)) { min_low_static = min_low; min_time2_static = min_time2; } else if(min_time2 != time_low && total_bar_low > 13) { min_low_static = 0; min_time2_static = 0; } }
Erläuterung:
RSI und die Identifikation der Hoch-/Tief-Kerzen:
- Die Aktivität des Indikators in Bezug auf überkaufte oder überverkaufte Niveaus wird von der RSI-Logik verwendet, um zu beurteilen, ob sich ein Hoch oder ein Tief bildet.
- Ist dies der Fall, ermittelt das Programm das höchste Hoch bzw. das tiefste Tief aus den zugehörigen Kerzen.
- Mit Bars() berechnet das Programm, wie viele Kerzen sich seit dem letzten Hoch oder Tief gebildet haben.
- Die Hochs oder Tiefs werden erst aktualisiert, wenn mindestens 12 Kerzen verstrichen sind, um häufige Änderungen zu vermeiden.
- Wenn sich mehr als 13 Kerzen bilden und die RSI-Bedingung für das vorherige Hoch oder Tief nicht mehr gültig ist, werden die gespeicherten Werte gelöscht.
- Dadurch wird sichergestellt, dass sich der EA bei seinen Handelsentscheidungen nicht auf veraltete Informationen stützt.
In diesem Abschnitt erweitern wir den EA, um die identifizierten RSI-Hochs und -Tiefs visuell darzustellen, indem wir Linien direkt auf dem Chart einzeichnen. Dies ermöglicht es dem EA nicht nur, diese Linienobjekte zu verwenden, um die Hochs und Tiefs programmatisch zu identifizieren, sondern ermöglicht es den Händlern auch, die kritischen Niveaus manuell zu überwachen, um bessere Entscheidungen zu treffen.
#include <Trade/Trade.mqh> CTrade trade; // Magic number input int MagicNumber = 1111; int rsi_handle; double rsi_buffer[]; double open[]; double close[]; double high[]; double low[]; datetime time[]; double max_high = 0; datetime min_time1 = 0; double min_low = 0; datetime min_time2 = 0; datetime time_low = 0; datetime times_high = 0; string high_obj_name = "High_Line"; string low_obj_name = "Low_Line"; long chart_id; //+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+ int OnInit() { // Configure RSI buffer as a series for easier indexing ArraySetAsSeries(rsi_buffer, true); // Initialize RSI handle for the current symbol, timeframe, and parameters rsi_handle = iRSI(_Symbol, PERIOD_CURRENT, 14, PRICE_CLOSE); // Configure candlestick arrays as series ArraySetAsSeries(open, true); ArraySetAsSeries(close, true); ArraySetAsSeries(high, true); ArraySetAsSeries(low, true); ArraySetAsSeries(time, true); // Set the magic number trade.SetExpertMagicNumber(MagicNumber); return (INIT_SUCCEEDED); // Indicate successful initialization } //+------------------------------------------------------------------+ //| Expert tick function | //+------------------------------------------------------------------+ void OnTick() { // Copy RSI values from the indicator into the buffer CopyBuffer(rsi_handle, 0, 1, 100, rsi_buffer); // Copy candlestick data CopyOpen(_Symbol, PERIOD_CURRENT, 1, 100, open); CopyClose(_Symbol, PERIOD_CURRENT, 1, 100, close); CopyHigh(_Symbol, PERIOD_CURRENT, 1, 100, high); CopyLow(_Symbol, PERIOD_CURRENT, 1, 100, low); CopyTime(_Symbol, PERIOD_CURRENT, 1, 100, time); static double max_high_static = max_high; static datetime min_time1_static = min_time1; static double min_low_static = min_low; static datetime min_time2_static = min_time2; //CHART ID chart_id = ChartID(); int total_bar_high = Bars(_Symbol,PERIOD_CURRENT,min_time1_static,TimeCurrent()); for(int i = 0; i < 12; i++) { if(close[i] < open[i] && close[i+1] > open[i+1]) { max_high = (double)MathMax(high[i],high[i+1]); min_time1 = (datetime)MathMin(time[i],time[i+1]); break; } } int total_bar_low = Bars(_Symbol,PERIOD_CURRENT,min_time2_static,TimeCurrent()); for(int i = 0; i < 12; i++) { if(close[i] > open[i] && close[i+1] < open[i+1]) { min_low = (double)MathMin(low[i],low[i+1]); min_time2 = (datetime)MathMin(time[i],time[i+1]); break; } } for(int i = 0; i < 12; i++) { if(rsi_buffer[i+1] < 30 && rsi_buffer[i] > rsi_buffer[i+1]) { time_low = time[i+1]; break; } } for(int i = 0; i < 12; i++) { if(rsi_buffer[i+1] > 70 && rsi_buffer[i] < rsi_buffer[i+1]) { times_high = time[i+1]; break; } } if((total_bar_high == 0 || total_bar_high > 12) && (min_time1 == times_high)) { max_high_static = max_high; min_time1_static = min_time1; } else if(min_time1 != times_high && total_bar_high > 13) { max_high_static = 0; min_time1_static = 0; } if((total_bar_low == 0 || total_bar_low > 12) && (min_time2 == time_low)) { min_low_static = min_low; min_time2_static = min_time2; } else if(min_time2 != time_low && total_bar_low > 13) { min_low_static = 0; min_time2_static = 0; } ObjectCreate(ChartID(),high_obj_name,OBJ_TREND,0,min_time1_static,max_high_static,TimeCurrent(),max_high_static); ObjectSetInteger(chart_id,high_obj_name,OBJPROP_COLOR,clrGreen); ObjectSetInteger(chart_id,high_obj_name,OBJPROP_WIDTH,3); ObjectCreate(ChartID(),low_obj_name,OBJ_TREND,0,min_time2_static,min_low_static,TimeCurrent(),min_low_static); ObjectSetInteger(chart_id,low_obj_name,OBJPROP_COLOR,clrRed); ObjectSetInteger(chart_id,low_obj_name,OBJPROP_WIDTH,3); }
Erläuterung:
Der Code erstellt mit der Funktion ObjectCreate() zwei Trendlinien im Chart: high_obj_name, die das Hoch markiert, und low_obj_name, die das Tief markiert. Diese Trendlinien erstrecken sich bis zum aktuellen Zeitpunkt (TimeCurrent()) und werden aus den berechneten Höchst- und Tiefstkursen (max_high_static und min_low_static) für die entsprechenden Zeitpunkte (min_time1_static und min_time2_static) abgeleitet. Dadurch kann der Händler die Hochs und Tiefs des Charts visuell überwachen.
Sie können das Aussehen dieser Trendlinien mit der Funktion ObjectSetInteger() verändern. Sie ist für die obere Trendlinie grün und für die untere Trendlinie rot eingestellt. Damit beide Linien im Chart gut lesbar sind, wird ihre Breite auf 3 gesetzt. Sowohl der Händler als auch der EA können mit Hilfe dieses visuellen Tools die wichtigsten Kursniveaus leichter im Auge behalten und mögliche Marktbewegungen wie Liquiditätsschwankungen besser einschätzen.
2.3.7. Festlegung von Kauf- und Verkaufsbedingungen für Liquiditätsdurchbrüche
Die Handelsbedingungen müssen präzise sein und sich an bestimmte Marktszenarien halten, um sich für einen Liquiditätsdurchbruch aus den ermittelten Hochs und Tiefs zu qualifizieren. In diesem Abschnitt wird die Logik für die Ausführung von Kauf- und Verkaufstransaktionen um diese kritischen Niveaus herum dargelegt.
// Magic number input int MagicNumber = 1111; int rsi_handle; double rsi_buffer[]; double open[]; double close[]; double high[]; double low[]; datetime time[]; double max_high = 0; datetime min_time1 = 0; double min_low = 0; datetime min_time2 = 0; datetime time_low = 0; datetime times_high = 0; string high_obj_name = "High_Line"; string low_obj_name = "Low_Line"; long chart_id; double take_profit; double ask_price = 0; //+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+ int OnInit() { // Configure RSI buffer as a series for easier indexing ArraySetAsSeries(rsi_buffer, true); // Initialize RSI handle for the current symbol, timeframe, and parameters rsi_handle = iRSI(_Symbol, PERIOD_CURRENT, 14, PRICE_CLOSE); // Configure candlestick arrays as series ArraySetAsSeries(open, true); ArraySetAsSeries(close, true); ArraySetAsSeries(high, true); ArraySetAsSeries(low, true); ArraySetAsSeries(time, true); // Set the magic number trade.SetExpertMagicNumber(MagicNumber); return (INIT_SUCCEEDED); // Indicate successful initialization } //+------------------------------------------------------------------+ //| Expert tick function | //+------------------------------------------------------------------+ void OnTick() { // Copy RSI values from the indicator into the buffer CopyBuffer(rsi_handle, 0, 1, 100, rsi_buffer); // Copy candlestick data CopyOpen(_Symbol, PERIOD_CURRENT, 1, 100, open); CopyClose(_Symbol, PERIOD_CURRENT, 1, 100, close); CopyHigh(_Symbol, PERIOD_CURRENT, 1, 100, high); CopyLow(_Symbol, PERIOD_CURRENT, 1, 100, low); CopyTime(_Symbol, PERIOD_CURRENT, 1, 100, time); static double max_high_static = max_high; static datetime min_time1_static = min_time1; static double min_low_static = min_low; static datetime min_time2_static = min_time2; //GETTING TOTAL POSITIONS int totalPositions = 0; for(int i = 0; i < PositionsTotal(); i++) { ulong ticket = PositionGetTicket(i); if(PositionSelectByTicket(ticket)) { if(PositionGetInteger(POSITION_MAGIC) == MagicNumber && PositionGetString(POSITION_SYMBOL) == ChartSymbol(chart_id)) { totalPositions++; } } } ask_price = SymbolInfoDouble(_Symbol,SYMBOL_ASK); if(totalPositions < 1) { if(((low[0] < min_low_static && close[0] > min_low_static && close[0] > open[0]) || (low[1] < min_low_static && close[0] > min_low_static && close[0] > open[0]) || (low[2] < min_low_static && close[0] > min_low_static && close[0] > open[0] && close[1] < open[1]))) { take_profit = (close[0] - low[0]) * 3 + close[0]; trade.Buy(0.5,_Symbol,ask_price, low[0], take_profit); } else if(((high[0] > max_high_static && close[0] < max_high_static && close[0] < open[0]) || (high[1] > max_high_static && close[0] < max_high_static && close[0] < open[0]) || (high[2] > max_high_static && close[0] < max_high_static && close[0] < open[0] && close[1] > open[1]))) { take_profit = MathAbs((high[0] - close[0]) * 3 - close[0]); // Adjusted take-profit calculation trade.Sell(0.5,_Symbol,ask_price, high[0], take_profit); } } //CHART ID chart_id = ChartID(); int total_bar_high = Bars(_Symbol,PERIOD_CURRENT,min_time1_static,TimeCurrent()); for(int i = 0; i < 12; i++) { if(close[i] < open[i] && close[i+1] > open[i+1]) { max_high = (double)MathMax(high[i],high[i+1]); min_time1 = (datetime)MathMin(time[i],time[i+1]); break; } } int total_bar_low = Bars(_Symbol,PERIOD_CURRENT,min_time2_static,TimeCurrent()); for(int i = 0; i < 12; i++) { if(close[i] > open[i] && close[i+1] < open[i+1]) { min_low = (double)MathMin(low[i],low[i+1]); min_time2 = (datetime)MathMin(time[i],time[i+1]); break; } } for(int i = 0; i < 12; i++) { if(rsi_buffer[i+1] < 30 && rsi_buffer[i] > rsi_buffer[i+1]) { time_low = time[i+1]; break; } } for(int i = 0; i < 12; i++) { if(rsi_buffer[i+1] > 70 && rsi_buffer[i] < rsi_buffer[i+1]) { times_high = time[i+1]; break; } } if((total_bar_high == 0 || total_bar_high > 12) && (min_time1 == times_high)) { max_high_static = max_high; min_time1_static = min_time1; } else if(min_time1 != times_high && total_bar_high > 13) { max_high_static = 0; min_time1_static = 0; } if((total_bar_low == 0 || total_bar_low > 12) && (min_time2 == time_low)) { min_low_static = min_low; min_time2_static = min_time2; } else if(min_time2 != time_low && total_bar_low > 13) { min_low_static = 0; min_time2_static = 0; } ObjectCreate(ChartID(),high_obj_name,OBJ_TREND,0,min_time1_static,max_high_static,TimeCurrent(),max_high_static); ObjectSetInteger(chart_id,high_obj_name,OBJPROP_COLOR,clrGreen); ObjectSetInteger(chart_id,high_obj_name,OBJPROP_WIDTH,3); ObjectCreate(ChartID(),low_obj_name,OBJ_TREND,0,min_time2_static,min_low_static,TimeCurrent(),min_low_static); ObjectSetInteger(chart_id,low_obj_name,OBJPROP_COLOR,clrRed); ObjectSetInteger(chart_id,low_obj_name,OBJPROP_WIDTH,3); }
Erläuterung:
Verhindern von Mehrfachpositionen mit totalPositions
Die Variable totalPositions stellt sicher, dass der Expert Advisor (EA) jeweils nur eine aktive Position unterhält. Dieser Mechanismus verhindert ein übermäßiges Risiko, indem er alle offenen Positionen überprüft und feststellt, ob die magische Zahl (Kennung der Handelsgeschäfte des EA) und das Symbol mit dem aktuellen Chart übereinstimmen. Wenn keine passende Position existiert (totalPositions < 1), wertet der EA die Bedingungen aus, um einen neuen Handel zu platzieren.
Dieser Sicherheitsmechanismus gewährleistet eine disziplinierte Handelsausführung und vermeidet überlappende Positionen, wodurch die Strategie einfacher zu verwalten und weniger anfällig für sich summierende Risiken ist.
Bedingungen für Käufe (Liquiditätsdurchbruch des Tiefs)
Die Logik des Kaufs ist darauf ausgerichtet, einen Liquiditätsdurchbruch bei einem zuvor identifizierten niedrigen Niveau (min_low_static) zu erkennen und eine Bestätigung der Aufwärtsbewegung sicherzustellen.
Bedingungen
Durchbruch unter das Tief:
- Jede der drei jüngsten Kerzen (low[0], low[1] oder low[2]) muss unter das ermittelte Mindesttief fallen, was darauf hindeutet, dass die Liquidität unter das Schlüsselniveau gesenkt wurde.
Aufwärts gerichtete Erholung:
- Der aktuelle Schlusskurs (close[0]) muss sich über dem tiefsten Tief (min_low_static) erholen, um nach dem Durchbruch eine Aufwärtsabsicht zu signalisieren.
- Außerdem muss es eine Aufwärtskerze sein (close[0] > open[0]), was ein Aufwärtsmomentum widerspiegelt.
- Wenn zwei Kerzen vor der aktuellen Kerze (close[1] < open[1]) gefallen sind, kommt eine Umkehrkomponente hinzu, die die Legitimität des Durchbruchs verstärkt.
Ausführung des Handels
Sobald die Bedingungen erfüllt sind, wird ein Kauf ausgeführt, mit:- Take Profit: Das Dreifache der Spanne zwischen dem Schlusskurs und dem Tief (take_profit = (close[0] - low[0]) * 3 + close[0]).
- Stop Loss: Das ermittelte Tief (low[0]).
Bedingungen für Verkäufe (Liquiditätsdurchbruch des Hochs)
Die Verkaufslogik spiegelt die Kauflogik wider, konzentriert sich aber auf die Identifizierung eines Liquiditätsdurchbrüche auf einem zuvor festgelegten hohen Niveau (max_high_static) mit abwärts gerichteter Bestätigung.
Bedingungen
Über das Hoch hinausgehen:
- Eine der drei jüngsten Kerzen (high[0], high[1], or high[2]) muss das ermittelte höchste Hoch überschreiten, was auf einen Liquiditätsausbruch über diesem Schlüsselwert hindeutet.
Abwärts gerichtete Umkehr:
- Der aktuelle Schlusskurs (close[0]) muss unter das höchste Hoch (max_high_static) fallen, was darauf hindeutet, dass der Ausbruch nicht nachhaltig ist.
- Außerdem muss es eine Abwärtskerze sein (close[0] < open[0]), was auf ein Abwärtsmomentum hindeutet.
- Wenn es zwei Aufwärtskerzen vor der aktuellen (close[1] > open[1]) gab, weist dies auf eine mögliche Richtungsumkehr hin.
Ausführen des Handels
Sobald die Bedingungen erfüllt sind, wird ein Verkaufsauftrag erteilt:
- Take Profit: Das Dreifache der Spanne zwischen dem Hoch und dem Schlusskurs (take_profit = MathAbs((high[0] - close[0]) * 3 - close[0])).
- Stop Loss: Das ermittelte Hoch (high[0]).
Zusammenfassung
Durch die Kombination des Mechanismus von TotalPositions mit genau definierten Kauf- und Verkaufsbedingungen gewährleistet diese Strategie Präzision und begrenzt das Risiko bei Handelsentscheidungen:
- Käufe werden ausgelöst, wenn ein wichtiges Tief unterschritten wird und eine Erholung nach oben einsetzt.
- Verkäufe werden nach dem Überschreiten eines wichtigen Hochs mit einer abwärts gerichteten Umkehr ausgelöst.
Dieser strukturierte Ansatz nutzt Liquiditätsdurchbrüche als Kernkonzept und stellt gleichzeitig sicher, dass Handelsgeschäfte nur zu günstigen Bedingungen ausgeführt werden. Eine Einschränkung dieser Methode besteht darin, dass der Stop-Loss dynamisch auf der Grundlage des Low[0] oder High[0] der Kerze gesetzt wird. Das bedeutet, dass das Risiko pro Handel je nach Größe der Kerze variiert, was zu einem uneinheitlichen Engagement führt. Daher sollte die Strategie die Möglichkeit bieten, einen festen Prozentsatz des Kontosaldos pro Handel zu riskieren (z. B. 2 %). Dies gewährleistet ein konsistentes Risikomanagement, indem die Positionsgröße auf der Grundlage des Abstands zwischen dem Einstiegskurs und dem Stop-Loss berechnet und mit dem festgelegten Risikoprozentsatz abgeglichen wird.
2.3.8. Risikomanagement und Änderungen des Break-Even
Bei der Arbeit mit integrierten Indikatoren ist ein wirksames Risikomanagement unerlässlich, um eine gleichbleibende Leistung zu gewährleisten. Die Größe der Kerzen kann sehr unterschiedlich sein, sodass es wichtig ist, den Prozentsatz Ihres Kontos festzulegen, den Sie pro Handel riskieren möchten. Diese Beständigkeit ermöglicht es Ihrem Risiko-Ertrags-Verhältnis (RRR), Verluste bei erfolgreichen Geschäften auszugleichen. Darüber hinaus stellen Break-Even-Änderungen sicher, dass Gewinne gesichert sind, wenn sich der Handel zu Gunsten der Strategie entwickelt. Die Einbeziehung von Break-Even-Änderungen sichert Gewinne, wenn sich die Handelsgeschäfte zu Ihren Gunsten entwickeln, und erhöht die allgemeine Robustheit der Strategie.
Beispiel:
//+------------------------------------------------------------------+ //| MQL5INDICATORS_PROJECT4.mq5 | //| Copyright 2024, MetaQuotes Ltd. | //| https://www.mql5.com | //+------------------------------------------------------------------+ #property copyright "ForexYMN" #property link "crownsoyin@gmail.com" #property version "1.00" #include <Trade/Trade.mqh> CTrade trade; // Magic number input int MagicNumber = 1111; input double account_balance = 1000; // Account Balance input double percentage_risk = 2.0; // How many percent of the account do you want to risk per trade? input bool allow_modify = false; // Do you allow break even modifications? input int rrr = 3; // Choose Risk Reward Ratio int rsi_handle; double rsi_buffer[]; double open[]; double close[]; double high[]; double low[]; datetime time[]; double max_high = 0; datetime min_time1 = 0; double min_low = 0; datetime min_time2 = 0; datetime time_low = 0; datetime times_high = 0; string high_obj_name = "High_Line"; string low_obj_name = "Low_Line"; long chart_id; double take_profit; double ask_price = 0; double lot_size; double risk_Amount; double points_risk; // Risk modification double positionProfit = 0; double positionopen = 0; double positionTP = 0; double positionSL = 0; double modifyLevel = 0.0; //+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+ int OnInit() { // Configure RSI buffer as a series for easier indexing ArraySetAsSeries(rsi_buffer, true); // Initialize RSI handle for the current symbol, timeframe, and parameters rsi_handle = iRSI(_Symbol, PERIOD_CURRENT, 14, PRICE_CLOSE); // Configure candlestick arrays as series ArraySetAsSeries(open, true); ArraySetAsSeries(close, true); ArraySetAsSeries(high, true); ArraySetAsSeries(low, true); ArraySetAsSeries(time, true); // Set the magic number trade.SetExpertMagicNumber(MagicNumber); return (INIT_SUCCEEDED); // Indicate successful initialization } //+------------------------------------------------------------------+ //| Expert tick function | //+------------------------------------------------------------------+ void OnTick() { int currBars = iBars(_Symbol,_Period); static int prevBars = currBars; if(prevBars == currBars) return; prevBars = currBars; // Copy RSI values from the indicator into the buffer CopyBuffer(rsi_handle, 0, 1, 100, rsi_buffer); // Copy candlestick data CopyOpen(_Symbol, PERIOD_CURRENT, 1, 100, open); CopyClose(_Symbol, PERIOD_CURRENT, 1, 100, close); CopyHigh(_Symbol, PERIOD_CURRENT, 1, 100, high); CopyLow(_Symbol, PERIOD_CURRENT, 1, 100, low); CopyTime(_Symbol, PERIOD_CURRENT, 1, 100, time); static double max_high_static = max_high; static datetime min_time1_static = min_time1; static double min_low_static = min_low; static datetime min_time2_static = min_time2; //GETTING TOTAL POSITIONS int totalPositions = 0; for(int i = 0; i < PositionsTotal(); i++) { ulong ticket = PositionGetTicket(i); if(PositionSelectByTicket(ticket)) { if(PositionGetInteger(POSITION_MAGIC) == MagicNumber && PositionGetString(POSITION_SYMBOL) == ChartSymbol(chart_id)) { totalPositions++; } } } ask_price = SymbolInfoDouble(_Symbol,SYMBOL_ASK); if(totalPositions < 1) { if(((low[0] < min_low_static && close[0] > min_low_static && close[0] > open[0]) || (low[1] < min_low_static && close[0] > min_low_static && close[0] > open[0]) || (low[2] < min_low_static && close[0] > min_low_static && close[0] > open[0] && close[1] < open[1]))) { take_profit = (close[0] - low[0]) * rrr + close[0]; points_risk = close[0] - low[0]; double riskAmount = account_balance * (percentage_risk / 100.0); double minus = NormalizeDouble(close[0] - low[0],5); lot_size = CalculateLotSize(_Symbol, riskAmount, minus); trade.Buy(lot_size,_Symbol,ask_price, low[0], take_profit); } else if(((high[0] > max_high_static && close[0] < max_high_static && close[0] < open[0]) || (high[1] > max_high_static && close[0] < max_high_static && close[0] < open[0]) || (high[2] > max_high_static && close[0] < max_high_static && close[0] < open[0] && close[1] > open[1]))) { take_profit = MathAbs((high[0] - close[0]) * rrr - close[0]); // Adjusted take-profit calculation points_risk = MathAbs(high[0] - close[0]); double riskAmount = account_balance * (percentage_risk / 100.0); double minus = NormalizeDouble(high[0] - close[0],5); lot_size = CalculateLotSize(_Symbol, riskAmount, minus); trade.Sell(lot_size,_Symbol,ask_price, high[0], take_profit); } } //CHART ID chart_id = ChartID(); int total_bar_high = Bars(_Symbol,PERIOD_CURRENT,min_time1_static,TimeCurrent()); for(int i = 0; i < 12; i++) { if(close[i] < open[i] && close[i+1] > open[i+1]) { max_high = (double)MathMax(high[i],high[i+1]); min_time1 = (datetime)MathMin(time[i],time[i+1]); break; } } int total_bar_low = Bars(_Symbol,PERIOD_CURRENT,min_time2_static,TimeCurrent()); for(int i = 0; i < 12; i++) { if(close[i] > open[i] && close[i+1] < open[i+1]) { min_low = (double)MathMin(low[i],low[i+1]); min_time2 = (datetime)MathMin(time[i],time[i+1]); break; } } for(int i = 0; i < 12; i++) { if(rsi_buffer[i+1] < 30 && rsi_buffer[i] > rsi_buffer[i+1]) { time_low = time[i+1]; break; } } for(int i = 0; i < 12; i++) { if(rsi_buffer[i+1] > 70 && rsi_buffer[i] < rsi_buffer[i+1]) { times_high = time[i+1]; break; } } if((total_bar_high == 0 || total_bar_high > 12) && (min_time1 == times_high)) { max_high_static = max_high; min_time1_static = min_time1; } else if(min_time1 != times_high && total_bar_high > 13) { max_high_static = 0; min_time1_static = 0; } if((total_bar_low == 0 || total_bar_low > 12) && (min_time2 == time_low)) { min_low_static = min_low; min_time2_static = min_time2; } else if(min_time2 != time_low && total_bar_low > 13) { min_low_static = 0; min_time2_static = 0; } ObjectCreate(ChartID(),high_obj_name,OBJ_TREND,0,min_time1_static,max_high_static,TimeCurrent(),max_high_static); ObjectSetInteger(chart_id,high_obj_name,OBJPROP_COLOR,clrGreen); ObjectSetInteger(chart_id,high_obj_name,OBJPROP_WIDTH,3); ObjectCreate(ChartID(),low_obj_name,OBJ_TREND,0,min_time2_static,min_low_static,TimeCurrent(),min_low_static); ObjectSetInteger(chart_id,low_obj_name,OBJPROP_COLOR,clrRed); ObjectSetInteger(chart_id,low_obj_name,OBJPROP_WIDTH,3); if(allow_modify) { for(int i = 0; i < PositionsTotal(); i++) { ulong ticket = PositionGetTicket(i); if(PositionSelectByTicket(ticket)) { positionopen = PositionGetDouble(POSITION_PRICE_OPEN); positionTP = PositionGetDouble(POSITION_TP); positionSL = PositionGetDouble(POSITION_SL); positionProfit = PositionGetDouble(POSITION_PROFIT); if(PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_SELL && PositionGetInteger(POSITION_MAGIC) == MagicNumber && PositionGetString(POSITION_SYMBOL) == ChartSymbol(chart_id)) { modifyLevel = MathAbs(NormalizeDouble((positionSL - positionopen) - positionopen,4)); if(ask_price <= modifyLevel) { trade.PositionModify(ticket, positionopen, positionTP); } } if(PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_BUY && PositionGetInteger(POSITION_MAGIC) == MagicNumber && PositionGetString(POSITION_SYMBOL) == ChartSymbol(chart_id)) { modifyLevel = MathAbs(NormalizeDouble((positionopen - positionSL) + positionopen,4)); if(ask_price >= modifyLevel) { trade.PositionModify(ticket, positionopen, positionTP); } } } } } } //+------------------------------------------------------------------+ //| Function to calculate the lot size based on risk amount and stop loss //+------------------------------------------------------------------+ double CalculateLotSize(string symbol, double riskAmount, double stopLossPips) { // Get symbol information double point = SymbolInfoDouble(symbol, SYMBOL_POINT); double tickValue = SymbolInfoDouble(symbol, SYMBOL_TRADE_TICK_VALUE); // Calculate pip value per lot double pipValuePerLot = tickValue / point; // Calculate the stop loss value in currency double stopLossValue = stopLossPips * pipValuePerLot; // Calculate the lot size double lotSize = riskAmount / stopLossValue; // Round the lot size to the nearest acceptable lot step double lotStep = SymbolInfoDouble(symbol, SYMBOL_VOLUME_STEP); lotSize = MathFloor(lotSize / lotStep) * lotStep; // Ensure the lot size is within the allowed range double minLot = SymbolInfoDouble(symbol, SYMBOL_VOLUME_MIN); double maxLot = SymbolInfoDouble(symbol, SYMBOL_VOLUME_MAX); if(lotSize < minLot) lotSize = minLot; if(lotSize > maxLot) lotSize = maxLot; return lotSize; }
Erläuterung:
input double account_balance = 1000; // Account Balance input double percentage_risk = 2.0; // How many percent of the account do you want to risk per trade? input bool allow_modify = false; // Do you allow break even modifications? input int rrr = 3; // Choose Risk Reward Ratio
Die Eingaben definieren Schlüsselparameter für das Risikomanagement in der Handelsstrategie. account_balance gibt einen festen Kontosaldo an, der zur Berechnung des Risikos pro Handel verwendet wird, um Konsistenz zu gewährleisten. percentage_risk legt den Prozentsatz des Kontostands fest, der bei jedem Handel riskiert werden soll, um das Risiko zu kontrollieren und ein stabiles Risikoniveau aufrechtzuerhalten. rrr (Risk-Reward Ratio) legt die gewünschte Belohnung im Verhältnis zum Risiko fest, um sicherzustellen, dass die Strategie auf größere Gewinne als Verluste abzielt. Die Eingabe allow_modify steuert, ob der Stop-Loss verschoben werden soll, um die Gewinnschwelle zu erreichen, sobald sich ein Handel zugunsten der Strategie entwickelt. Wenn es aktiviert ist, sichert das die Gewinne und verringert das Risiko von Handelsgeschäften mit Gewinn. Zusammen sorgen diese Faktoren für einen disziplinierten Handelsansatz, indem sie ein gleichbleibendes Risiko pro Handel gewährleisten und die Gewinne bei erfolgreichen Geschäften schützen.
take_profit = (close[0] - low[0]) * rrr + close[0]; points_risk = close[0] - low[0]; double riskAmount = account_balance * (percentage_risk / 100.0); double minus = NormalizeDouble(close[0] - low[0],5); lot_size = CalculateLotSize(_Symbol, riskAmount, minus); trade.Buy(lot_size,_Symbol,ask_price, low[0], take_profit);
- take_profit = (close[0] - low[0]) * rrr + close[0]; berechnet Take-Profit durch Multiplikation des Abstands zwischen dem aktuellen Schlusskurs und dem Tief der Kerze mit dem Risiko-Ertrags-Verhältnis (rrr). Das Ergebnis wird zum Schlusskurs addiert, um ein Gewinnziel oberhalb des Einstiegspunktes festzulegen.
- points_risk = close[0] - low[0]; berechnet den Risikoabstand zwischen dem Einstiegskurs (close) und dem Stop-Loss (low) der Kerze.
- riskAmount = account_balance * (percentage_risk / 100.0); berechnet den Risikobetrag pro Handel auf der Grundlage des Kontostands und des Risikoprozentsatzes.
- lot_size = CalculateLotSize(_Symbol, riskAmount, minus); berechnet die Losgröße auf der Grundlage des Risikobetrags und des Stop-Loss-Abstands (minus) und stellt sicher, dass der Handel mit dem festgelegten Risiko übereinstimmt.
Für Verkaufsgeschäfte:
- take_profit = MathAbs((high[0] - close[0]) * rrr - close[0]); passt die Take-Profit-Berechnung an, indem der Abstand zwischen dem aktuellen Schlusskurs und dem Höchststand der Kerze verwendet wird, wiederum unter Berücksichtigung des Risiko-Ertrags-Verhältnisses.
- points_risk = MathAbs(high[0] - close[0]); berechnet die Risikodistanz für einen Verkauf vom Einstiegspunkt bis zum Stop-Loss (Hoch der Kerze).
- Die übrige Logik (Berechnung des Risikobetrags und der Losgröße) ist die gleiche wie bei Käufen, sodass ein einheitliches Risikomanagement für beide Handelsarten gewährleistet ist.
if(allow_modify) { for(int i = 0; i < PositionsTotal(); i++) { ulong ticket = PositionGetTicket(i); if(PositionSelectByTicket(ticket)) { positionopen = PositionGetDouble(POSITION_PRICE_OPEN); positionTP = PositionGetDouble(POSITION_TP); positionSL = PositionGetDouble(POSITION_SL); positionProfit = PositionGetDouble(POSITION_PROFIT); if(PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_SELL && PositionGetInteger(POSITION_MAGIC) == MagicNumber && PositionGetString(POSITION_SYMBOL) == ChartSymbol(chart_id)) { modifyLevel = MathAbs(NormalizeDouble((positionSL - positionopen) - positionopen,4)); if(ask_price <= modifyLevel) { trade.PositionModify(ticket, positionopen, positionTP); } } if(PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_BUY && PositionGetInteger(POSITION_MAGIC) == MagicNumber && PositionGetString(POSITION_SYMBOL) == ChartSymbol(chart_id)) { modifyLevel = MathAbs(NormalizeDouble((positionopen - positionSL) + positionopen,4)); if(ask_price >= modifyLevel) { trade.PositionModify(ticket, positionopen, positionTP); } } } } }
Der zur Verfügung gestellte Code behandelt die Logik zur Änderung aktiver Positionen auf der Grundlage bestimmter Bedingungen, wobei der Schwerpunkt auf der Implementierung von Anpassungen des Break-Even für Kauf- und Verkaufspositionen liegt. Hier ist eine Aufschlüsselung, was der Code bewirkt:
Prüfung auf Änderungsberechtigung (allow_modify):
- Der Block „if(allow_modify)“ stellt sicher, dass die Modifikationslogik nur dann ausgeführt wird, wenn die vom Nutzer definierten Änderungen des Break-Even erlaubt sind.
- Die for-Schleife iteriert durch alle derzeit offenen Positionen (PositionsTotal()). Jede Position wird daraufhin überprüft, ob sie die Bedingungen für eine Änderung erfüllt.
Auswählen der Position:
- Der Code ruft das Ticket für jede Position ab (PositionGetTicket(i)) und verwendet es zur Auswahl der Position (PositionSelectByTicket(ticket)).
Details zur abgerufenen Position:
Für jede ausgewählte Position werden die folgenden Details abgerufen:
- positionopen: Der Preis, zu dem die Position eröffnet wurde.
- positionTP: Take-Profit der Position.
- positionSL: Stop-Loss der Position.
- positionProfit: Der aktuelle Gewinn der Position.
Änderung der Verkaufsposition:
- Der Code prüft, ob die Position eine Verkaufsposition ist (POSITION_TYPE_SELL).
- Er berechnet modifyLevel, die absolute Differenz zwischen dem Stop-Loss (positionSL) und dem Eröffnungspreis (positionopen) und prüft dann, ob der aktuelle Briefkurs (ask_price) dieses Level erreicht oder überschritten hat.
- Wenn die Bedingung erfüllt ist, wird der Stop-Loss auf den Eröffnungskurs (Break-Even) geändert, wobei der Take-Profit unverändert bleibt (trade.PositionModify(ticket, positionopen, positionTP)).
- Ähnlich wird bei einer Kaufposition (POSITION_TYPE_BUY) das modifyLevel als absolute Differenz zwischen dem Eröffnungskurs und dem Stop-Loss berechnet und überprüft, ob der Briefkurs dieses Niveau günstig überschritten hat.
- Wenn die Bedingung erfüllt ist, wird der Stop-Loss auf den Eröffnungskurs (Break-Even) gesetzt und der Take-Profit unverändert beibehalten.
Schlussfolgerung
In diesem Artikel haben wir die Verwendung integrierter Indikatoren in MQL5 anhand eines praktischen, projektbezogenen Ansatzes untersucht. Wir haben einen Expert Advisor (EA) für den automatischen Handel am Beispiel des RSI entwickelt, der sich auf Handelsein- und -ausstiege konzentriert, die durch überkaufte und überverkaufte Signale ausgelöst werden. Wir haben wichtige Punkte in diesem Prozess hervorgehoben, wie z. B. die Verwendung eines Risiko-Ertrags-Verhältnisses (RRR), um Gewinnziele mit kalkulierten Risiken abzustimmen, und die Festlegung eines konstanten prozentualen Risikos für jeden Handel, um unterschiedliche Kerzengrößen zu verwalten. Um die Strategie zuverlässiger und erfolgreicher zu machen, haben wir uns auch mit Liquiditätsschwankungen befasst und die Anpassung des Break-Even hinzugefügt, um die Gewinne zu sichern, wenn sich die Geschäfte entwickeln. Dies ermöglichte es dem EA, sich an abrupte Marktveränderungen anzupassen.
Übersetzt aus dem Englischen von MetaQuotes Ltd.
Originalartikel: https://www.mql5.com/en/articles/16514





- 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.