English 日本語
preview
Aufbau des Kerzenmodells Trend Constraint (Teil 9): Expert Advisor für mehrere Strategien (III)

Aufbau des Kerzenmodells Trend Constraint (Teil 9): Expert Advisor für mehrere Strategien (III)

MetaTrader 5Beispiele | 15 April 2025, 07:30
127 0
Clemence Benjamin
Clemence Benjamin

Einführung

Beim algorithmischen Handel stellt die Ermittlung optimaler Einstiegspunkte innerhalb eines vorherrschenden Trends nach wie vor eine große Herausforderung dar, da viele Strategien Schwierigkeiten haben, den richtigen Zeitpunkt zu erwischen oder häufig falsche Signale erzeugen, was zu einer suboptimalen Performance führt. Dieses Problem ist besonders ausgeprägt bei täglichen Trends, bei denen geringe Schwankungen die Ausführungsgenauigkeit stören können.

Die Divergenz bietet eine robuste Lösung, indem sie als Filter fungiert, um potenzielle Umkehrungen oder Fortsetzungen durch Diskrepanzen zwischen Kursbewegungen und Momentum-Indikatoren zu erkennen. Durch die Integration der Divergenzerfassung in den Trend Constraint Expert Advisor können Händler ihre Präzision bei der Ermittlung von Einstiegsniveaus erheblich verbessern.

Dieser Ansatz verbessert die Handelsgenauigkeit und gewährleistet in Verbindung mit den fortschrittlichen Funktionen von MQL5 einen konsistenten und effizienten Handel. In diesem Artikel befassen wir uns mit den Grundlagen der Divergenz, den Schritten zu ihrer Integration in MQL5 Expert Advisors, den Verbesserungen des Trend Constraint Expert Advisors mit neuen Handelsausführungsbedingungen und den Erkenntnissen aus Backtests, um ihre praktische Anwendung zu demonstrieren.

Hauptinhalt:

  1. Die Grundlagen der Divergenz
  2. Schritte zur Integration der Divergenzerkennung
  3. Erweiterungen des Trend Constraint Expert Advisor: Implementierung neuer Bedingungen für die Handelsausführung zur Nutzung von Divergenzen.
  4. Backtest-Ergebnisse und praktische Anwendung
  5. Schlussfolgerung


Die Grundlagen der Divergenz

Divergenz ist ein Schlüsselkonzept in der technischen Analyse, das Händlern durch den Vergleich von Kursbewegungen mit der Richtung eines Indikators Einblicke in potenzielle Kursumkehrungen oder -fortsetzungen gewährt. 

Bedeutung der Divergenz:

Eine Divergenz liegt vor, wenn sich der Kurs eines Vermögenswerts entgegengesetzt zu einem technischen Indikator bewegt, was häufig auf eine Abschwächung des Trends oder eine bevorstehende Umkehrung hinweist. Dieses Konzept ist besonders nützlich, wenn es darum geht, zu erkennen, wann ein Trend für eine Korrektur oder eine vollständige Trendwende fällig ist.

Arten von Divergenzen:

  1. Eine Aufwärts-Divergenz wird festgestellt, wenn der Kurs des Vermögenswerts niedrigere Tiefststände erreicht, der Indikator, wie der RSI, jedoch höhere Tiefststände anzeigt, was auf eine nachlassende Abwärtsdynamik hindeutet. Dies könnte bedeuten, dass der Verkaufsdruck nachlässt und eine Aufwärtsbewegung des Preises in Gang kommen könnte.
  2. Eine Abwärts-Divergenz wird beobachtet, wenn der Kurs höhere Höchststände erreicht, der Indikator jedoch niedrigere Höchststände anzeigt, was darauf hindeutet, dass die Aufwärtsdynamik abnimmt. Dies könnte ein Vorbote für einen Preisrückgang sein.

Hintergrund-Check:

Divergenz ist ein zentrales Konzept in der technischen Analyse, das das Marktverhalten und die Strategien der Händler beeinflusst. Bart und Masse (1981) betonen in „Divergence of Opinion and Risk", wie Diskrepanzen in der Marktmeinung das Risiko und die Preisvolatilität erhöhen können, was die Rolle der Divergenz in der technischen Analyse widerspiegelt.

Empirische Belege von Tilehnouei and Shivaraj (2013) deuten darauf hin, dass Instrumente wie der MACD den RSI in bestimmten Kontexten übertreffen können und durch Divergenzsignale wertvolle Einblicke in die Marktdynamik bieten. Aus dieser Untersuchung können wir schließen, dass die Integration der Divergenz mit anderen Indikatoren, wie z.B. dem Zusammenspiel von RSI, MACD und Kursbewegung, ihren Nutzen in einem umfassenden Handelsansatz verstärkt, was von verschiedenen Branchenquellen bestätigt wird.

Im nächsten Abschnitt werden wir einen praktischen Schritt unternehmen, um die Idee der Divergenz in unserer EA-Entwicklung umzusetzen.


Schritte zur Integration der Divergenzerkennung

Um die Erkennung von Divergenzen in einen MQL5 Expert Advisor (EA) zu integrieren, berechnen wir zunächst die Werte des Relative Strength Index (RSI) mit Funktionen wie iRSI() und vergleichen sie mit der Preisentwicklung. Signifikante Preisextreme werden mit iHigh() und iLow() über einen bestimmten Zeitraum ermittelt. Für dieses Projekt werde ich zwei Arten von Divergenzen unterscheiden: reguläre (Umkehrung) und versteckte.

Reguläre Divergenz:

Reguläre Divergenzen signalisieren potenzielle Trendumkehrungen, wobei ein Aufwärts-Setup entsteht, wenn der Kurs ein niedrigeres Tief erreicht, während der Indikator ein höheres Tief bildet, und ein Abwärts-Setup entsteht, wenn der Kurs ein höheres Hoch erreicht, der Indikator aber ein niedrigeres Hoch anzeigt.

// Regular divergence conditions in code
bool CheckRegularBearishDivergence(int period = 14, ENUM_TIMEFRAMES timeframe = PERIOD_H1)
{
    double priceHigh1 = iHigh(_Symbol, timeframe, 2);
    double priceHigh2 = iHigh(_Symbol, timeframe, 8);
    double rsiHigh1 = iRSI(_Symbol, timeframe, period, PRICE_CLOSE, 2);
    double rsiHigh2 = iRSI(_Symbol, timeframe, period, PRICE_CLOSE, 8);
    
    if(priceHigh1 > priceHigh2 && rsiHigh1 < rsiHigh2) return true;
    return false;
}

Zur Veranschaulichung dieser regelmäßigen Divergenzen sind hier zwei Bilder zu sehen, die eine Aufwärts- und eine Abwärts-Divergenz darstellen:

Boom 300 IndexH4 - Aufwärts-Divergenz

Boom 300 IndexH4 - Aufwärts-Divergenz (Ein tieferes Tief, B, und ein höheres Tief des RSI, D) 


Boom 300 IndexH4 - Abwärts-Divergenz

Boom 300 IndexH4 - Abwärts-Divergenz: (Ein höheres Hoch, B, und ein niedrigeres Hoch des RSI, D)

Versteckte Divergenz:

Versteckte Divergenzen hingegen deuten auf eine Trendfortsetzung hin. Eine versteckte Aufwärts-Divergenz tritt in einem Aufwärtstrend auf, wenn der Kurs ein höheres Tief erreicht, während der Indikator ein niedrigeres Tief bildet, während eine versteckte Abwärts-Divergenz in einem Abwärtstrend auftritt, wenn der Kurs ein niedrigeres Hoch erreicht und der Indikator ein höheres Hoch bildet.

//RSI and Price Levels declaration and hidden divergence condition
bool CheckHiddenBullishDivergence(int period = 14, ENUM_TIMEFRAMES timeframe = PERIOD_H1)
{
    double priceLow1 = iLow(_Symbol, timeframe, 2);
    double priceLow2 = iLow(_Symbol, timeframe, 8);
    double rsiLow1 = iRSI(_Symbol, timeframe, period, PRICE_CLOSE, 2);
    double rsiLow2 = iRSI(_Symbol, timeframe, period, PRICE_CLOSE, 8);
    
    if(priceLow1 > priceLow2 && rsiLow1 < rsiLow2) return true;
    return false;
}

Die folgenden Abbildungen zeigen die versteckte Aufwärts-Divergenz und die versteckte Abwärts-Divergenz. Vergleichen Sie diese mit den Beschreibungen, die Sie zuvor erhalten haben, und üben Sie, ähnliche Muster in Ihren eigenen Charts zu erkennen.

Boom 300 IndexH4 versteckte Aufwärts-Divergenz

Boom 300 IndexH4: Versteckte Aufwärts-Divergenz

Boom 300 IndexH4 versteckte Abwärts-Divergenz

Boom 300 IndexH4: Versteckte Abwärts-Divergenz

Die Integration dieser Divergenztypen in den EA erfordert die Codierung von Bedingungen, um sie bei jedem Tick oder Balkenschluss zu erkennen, wobei Tools wie iRSI() oder iMACD() für die Indikatorberechnung verwendet werden. Sobald die Divergenzsignale erkannt sind, werden sie mit den Trendbegrenzungen abgeglichen, die anhand der täglichen Marktstimmung ermittelt werden.


Erweiterungen des Trend Constraint Expert Advisor: Einführung neuer Handelsausführungsbedingungen zur Nutzung von Divergenzen

Zum obigen Abschnitt: Wenn eine Divergenz festgestellt wird, benötigen wir einen zusätzlichen Indikator, um die Auftragsausführung zu bestätigen und zu signalisieren. Hierfür gibt es viele Möglichkeiten, aber wir werden den Einsatz von MACD und RSI in Betracht ziehen. Hier finden Sie eine Liste mit weiteren optionalen Bestätigungsindikatoren: 

  1. Bollinger-Bänder
  2. Stochastik Oszillator
  3. On Balance Volume (OBV) und volumengewichteter Durchschnittspreis (VWAP)
  4. Durchschnittlicher dynamischer Index (ADX)

Moving Average Convergence Divergence (MACD):

Warum er? Der MACD kann Momentumveränderungen bestätigen. Wenn eine Divergenz zum RSI festgestellt wird, kann der MACD eine sekundäre Bestätigung der Trendstärke oder -schwäche liefern. 

So funktioniert es:

Der EA sucht nach MACD-Linienüberkreuzungen oder Histogrammänderungen, die mit den Divergenzergebnissen übereinstimmen. Eine Abwärts-Divergenz könnte beispielsweise bestätigt werden, wenn die MACD-Linie unter die Signallinie kreuzt oder das Histogramm zu sinken beginnt.

Die wichtigsten Merkmale des MACD-Indikators:

Um eine Vorschau auf den eingebauten Indikator wie den MACD zu erhalten, können wir auf den Ordner "Examples" unter dem Ordner "Indicators" in der MetaEditor 5 Software zugreifen. Schauen Sie sich das folgende Bild an.

Zugriff auf den Quell-Code des MACD in MetaEditor 5

MetaEditor 5: Zugriff auf die MACD-Quelldatei

Der Grund für den Zugriff auf den Code ist, dass wir leicht herausfinden können, wie die Puffer aufgebaut sind, sodass wir sie in unserem Expert Advisor leicht anpassen können. Nachfolgend finden Sie einen Codeabschnitt für die gepufferten Deklarationen innerhalb des Indikators, an dem wir interessiert sind.

//--- indicator buffers
double ExtMacdBuffer[];
double ExtSignalBuffer[];
double ExtFastMaBuffer[];
double ExtSlowMaBuffer[];

int    ExtFastMaHandle;
int    ExtSlowMaHandle;

Mit unseren Puffern im Hinterkopf gehen wir nun Schritt für Schritt durch den Entwicklungsprozess, der im Folgenden beschrieben wird.

Entwicklung der Strategie der Divergenz: 

Schritt 1: Deklarationen und Eingabeparameter

 Wir beginnen mit der Deklaration von Eingabeparametern für die Divergenzstrategie, MACD-Puffern und der Initialisierung der Klasse CTrade.

#include <Trade\Trade.mqh>
CTrade trade;

input bool UseDivergenceStrategy = true;        // Enable/Disable Divergence Strategy
input int DivergenceMACDPeriod = 12;            // MACD Fast EMA period
input int DivergenceSignalPeriod = 9;           // MACD Signal period
input double DivergenceLots = 1.0;              // Lot size for Divergence trades
input double DivergenceStopLoss = 300;          // Stop Loss in points for Divergence
input double DivergenceTakeProfit = 500;        // Take Profit in points for Divergence
input int DivergenceMagicNumber = 87654321;     // Magic number for Divergence Strategy
input int DivergenceLookBack = 8;               // Number of periods to look back for divergence

double ExtMacdBuffer[]; // MACD values
double ExtSignalBuffer[]; // Signal line values
int macd_handle; // MACD indicator handle

Schritt 2: MACD-Indikator initialisieren

Initialisierung des MACD-Handles und Zuweisung von Pufferspeicher in OnInit().

int OnInit()
{
    macd_handle = iMACD(_Symbol, PERIOD_CURRENT, DivergenceMACDPeriod, 26, DivergenceSignalPeriod, PRICE_CLOSE);
    if (macd_handle == INVALID_HANDLE)
    {
        Print("Failed to initialize MACD. Error: ", GetLastError());
        return INIT_FAILED;
    }
    ArrayResize(ExtMacdBuffer, DivergenceLookBack);
    ArrayResize(ExtSignalBuffer, DivergenceLookBack);
    return INIT_SUCCEEDED;
}

Schritt 3:  Erkennung von Divergenzen

Eine Divergenz tritt auf, wenn die Kursentwicklung eines Vermögenswerts nicht mit einem Indikator übereinstimmt, in diesem Fall mit dem MACD. Diese Strategie erkennt vier Arten von Divergenzen: „bullish regular“, „bullish hidden“, „bearish regular“ und „bearish hidden“ (reguläre und versteckte Auf- und Abwärts-Divergenzen). Jeder Typ hat spezifische Bedingungen, wobei die Höchst- oder Tiefstwerte der Kurse mit den entsprechenden Höchst- oder Tiefstwerten des MACD verglichen werden, um das Vorhandensein einer Divergenz festzustellen.

bool CheckBullishRegularDivergence()
{
    double priceLow1 = iLow(_Symbol, PERIOD_CURRENT, 2);
    double priceLow2 = iLow(_Symbol, PERIOD_CURRENT, DivergenceLookBack);
    double macdLow1 = ExtMacdBuffer[2];
    double macdLow2 = ExtMacdBuffer[DivergenceLookBack - 1];
    return (priceLow1 < priceLow2 && macdLow1 > macdLow2);
}

bool CheckBearishHiddenDivergence()
{
    double priceHigh1 = iHigh(_Symbol, PERIOD_CURRENT, 2);
    double priceHigh2 = iHigh(_Symbol, PERIOD_CURRENT, DivergenceLookBack);
    double macdHigh1 = ExtMacdBuffer[2];
    double macdHigh2 = ExtMacdBuffer[DivergenceLookBack - 1];
    return (priceHigh1 < priceHigh2 && macdHigh1 > macdHigh2);
}

Schritt 4: Handelslogik

Erstens stellt die Strategie sicher, dass der Divergenzhandel aktiviert ist und nicht mehr als 3 auf Divergenz basierende Positionen offen sind. Er ruft MACD-Pufferdaten ab, versucht es bei Fehlschlägen erneut und führt nur Handelsgeschäfte auf vollständigen Bars aus. Darüber hinaus richtet es den Handel an den täglichen Kerzen-Trends aus und sorgt dafür, dass nur an Aufwärtstagen gekauft und an Abwärtstagen verkauft wird.

void CheckDivergenceTrading()
{
    if (!UseDivergenceStrategy) return;

    int openDivergencePositions = CountOrdersByMagic(DivergenceMagicNumber);
    if (openDivergencePositions == 0 || openDivergencePositions < 3)
    {
        if (CopyBuffer(macd_handle, 0, 0, DivergenceLookBack, ExtMacdBuffer) > 0 &&
            CopyBuffer(macd_handle, 1, 0, DivergenceLookBack, ExtSignalBuffer) > 0)
        {
            double dailyClose = iClose(_Symbol, PERIOD_D1, 0);
            double dailyOpen = iOpen(_Symbol, PERIOD_D1, 0);
            bool isDailyBullish = dailyClose > dailyOpen;
            bool isDailyBearish = dailyClose < dailyOpen;

            if (isDailyBullish && 
                (CheckBullishRegularDivergence() && ExtMacdBuffer[0] > ExtSignalBuffer[0]) || 
                CheckBullishHiddenDivergence())
            {
                ExecuteDivergenceOrder(true);
            }

            if (isDailyBearish && 
                (CheckBearishRegularDivergence() && ExtMacdBuffer[0] < ExtSignalBuffer[0]) || 
                CheckBearishHiddenDivergence())
            {
                ExecuteDivergenceOrder(false);
            }
        }
    }
}

Schritt 5: Auftragsausführung

Sobald eine Divergenz erkannt wird, führt die Strategie Trades mit vordefinierten Parametern wie Lotgröße, Stop Loss und Take Profit aus. Die Funktion ExecuteDivergenceOrder berechnet auf der Grundlage der Handelsrichtung die entsprechenden Niveaus und verwendet das Handelsobjekt, um Kauf- oder Verkaufsaufträge zu erteilen.

void ExecuteDivergenceOrder(bool isBuy)
{
    trade.SetExpertMagicNumber(DivergenceMagicNumber);

    double currentPrice = isBuy ? SymbolInfoDouble(_Symbol, SYMBOL_ASK) : SymbolInfoDouble(_Symbol, SYMBOL_BID);
    double stopLossPrice = isBuy ? currentPrice - DivergenceStopLoss * _Point : currentPrice + DivergenceStopLoss * _Point;
    double takeProfitPrice = isBuy ? currentPrice + DivergenceTakeProfit * _Point : currentPrice - DivergenceTakeProfit * _Point;

    if (isBuy)
    {
        if (trade.Buy(DivergenceLots, _Symbol, 0, stopLossPrice, takeProfitPrice, "Divergence Buy"))
            Print("Divergence Buy order placed.");
    }
    else
    {
        if (trade.Sell(DivergenceLots, _Symbol, 0, stopLossPrice, takeProfitPrice, "Divergence Sell"))
            Print("Divergence Sell order placed.");
    }
}

Schritt 6: Auftragsverwaltung

Um Overtrading zu vermeiden, verwendet die Strategie die Funktion CountOrdersByMagic, um alle offenen Positionen mit der angegebenen magischen Zahl zu zählen. Dies gewährleistet die Einhaltung des maximalen Positionslimits für divergenzbasierte Trades.

int CountOrdersByMagic(int magic)
{
    int count = 0;
    for (int i = 0; i < PositionsTotal(); i++)
    {
        ulong ticket = PositionGetTicket(i);
        if (PositionSelectByTicket(ticket))
        {
            if (PositionGetInteger(POSITION_MAGIC) == magic)
            {
                count++;
            }
        }
    }
    return count;
}

Magische Zahl: 

Ein weiterer wichtiger Aspekt ist, dass wir unseren Positionen eine Identität geben. In diesem Fall haben wir den durch die Divergenzstrategie gesteuerten Geschäften eine eindeutige magische Zahl zugewiesen.

    // Ensure the magic number is set for the trade
    trade.SetExpertMagicNumber(DivergenceMagicNumber);

Schritt 7: Schutz des Gewinns

Wir haben eine Gewinnsicherungsfunktion eingeführt, die eine Gewinnsicherungslogik für unseren Trend Constraint Expert Advisor enthält, um Gewinne aus offenen Geschäften dynamisch zu sichern. Die Funktion LockProfits scannt alle aktiven Positionen und identifiziert diejenigen, deren Gewinne einen Schwellenwert von 100 Punkten überschreiten. Für jede qualifizierte Position wird auf der Grundlage des Parameters profitLockerPoints ein neues Stop-Loss-Niveau berechnet (z. B. 20 Punkte vom Einstiegskurs).

Durch diese Anpassung wird sichergestellt, dass der Stop-Loss näher an den aktuellen Kurs heranrückt, wodurch Gewinne effektiv gesichert werden. Bei Kaufpositionen wird der Stop-Loss über den Einstiegskurs verschoben, während er bei Verkaufspositionen nach unten verschoben wird. Die Funktion aktualisiert den Stop-Loss nur dann, wenn das neue Niveau einen besseren Schutz bietet, um ein optimales Risikomanagement zu gewährleisten. Erfolgreiche Änderungen werden mit einer Meldung zur Nachverfolgung protokolliert. Diese Funktion trägt dazu bei, die Gewinne zu sichern und gleichzeitig dem Handel das Potenzial für weiteres Wachstum zu geben.

Hier ist die Funktion LockProfits():

//+------------------------------------------------------------------+
//| Profit Locking Logic                                             |
//+------------------------------------------------------------------+
void LockProfits()
{
    for (int i = PositionsTotal() - 1; i >= 0; i--)
    {
        ulong ticket = PositionGetTicket(i);
        if (PositionSelectByTicket(ticket))
        {
            double entryPrice = PositionGetDouble(POSITION_PRICE_OPEN);
            double currentProfit = PositionGetDouble(POSITION_PROFIT);
            double currentPrice = PositionGetDouble(POSITION_PRICE_CURRENT);
            
            // Convert profit to points
            double profitPoints = MathAbs(currentProfit / _Point);

            // Check if profit has exceeded 100 points
            if (profitPoints >= 100)
            {
                double newStopLoss;
                
                if (PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_BUY)
                {
                    newStopLoss = entryPrice + profitLockerPoints * _Point; // 20 points above entry for buys
                }
                else if (PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_SELL)
                {
                    newStopLoss = entryPrice - profitLockerPoints * _Point; // 20 points below entry for sells
                }
                else
                {
                    continue; // Skip if not a buy or sell position
                }

                // Modify stop loss only if the new stop loss is more protective
                double currentStopLoss = PositionGetDouble(POSITION_SL);
                if (PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_BUY)
                {
                    if (currentStopLoss < newStopLoss || currentStopLoss == 0)
                    {
                        if (trade.PositionModify(ticket, newStopLoss, PositionGetDouble(POSITION_TP)))
                        {
                            Print("Profit locking for buy position: Stop Loss moved to ", newStopLoss);
                        }
                    }
                }
                else // POSITION_TYPE_SELL
                {
                    if (currentStopLoss > newStopLoss || currentStopLoss == 0)
                    {
                        if (trade.PositionModify(ticket, newStopLoss, PositionGetDouble(POSITION_TP)))
                        {
                            Print("Profit locking for sell position: Stop Loss moved to ", newStopLoss);
                        }
                    }
                }
            }
        }
    }
}

Schritt 8: Integrieren in OnTick()

Wir rufen die Handelslogik der Divergenz innerhalb der EA-Hauptschleife auf.

void OnTick()
{
    CheckDivergenceTrading();
}

Schritt 9: Beenden

void OnDeinit(const int reason)
{
    IndicatorRelease(macd_handle);
}

Integration der Strategie in den Haupttrend Constrant Expert.

Im letzten Artikel dieser Serie haben wir einen auf dem Donchian-Kanal basierenden Expert Advisor entwickelt, der zwei Strategien in einem einzigen Experten markiert. Heute beziehen wir die 3. Strategie mit ein und verwenden boolesche Daten, um die Strategien ein- und auszuschalten.

// Input parameters for controlling strategies
input bool UseTrendFollowingStrategy = false;   // Enable/Disable Trend Constraint Strategy
input bool UseBreakoutStrategy = false;         // Enable/Disable Breakout Strategy
input bool UseDivergenceStrategy = true;        // Enable/Disable Divergence Strategy

Wir setzen die Strategie Divergence auf true, damit wir beim Strategy Tester nicht durcheinander kommen.

Schließlich integrieren wir unsere Strategie sorgfältig in den Hauptcode:

//+------------------------------------------------------------------+
//|                                      Trend Constraint Expert.mq5 |
//|                                Copyright 2024, Clemence Benjamin |
//|             https://www.mql5.com/en/users/billionaire2024/seller |
//+------------------------------------------------------------------+
#property copyright "Copyright 2024, Clemence Benjamin"
#property link      "https://www.mql5.com/en/users/billionaire2024/seller"
#property version   "1.02"

#include <Trade\Trade.mqh>
CTrade trade;

// Input parameters for controlling strategies
input bool UseTrendFollowingStrategy = false;   // Enable/Disable Trend Following Strategy
input bool UseBreakoutStrategy = false;         // Enable/Disable Breakout Strategy
input bool UseDivergenceStrategy = true;        // Enable/Disable Divergence Strategy

// Input parameters for Trend Constraint Strategy
input int    RSI_Period = 14;           // RSI period
input double RSI_Overbought = 70.0;     // RSI overbought level
input double RSI_Oversold = 30.0;       // RSI oversold level
input double Lots = 0.1;                // Lot size
input double StopLoss = 100;            // Stop Loss in points
input double TakeProfit = 200;          // Take Profit in points
input double TrailingStop = 50;         // Trailing Stop in points
input int    MagicNumber = 12345678;    // Magic number for the Trend Constraint EA
input int    OrderLifetime = 43200;     // Order lifetime in seconds (12 hours)

// Input parameters for Breakout Strategy
input int InpDonchianPeriod = 20;       // Period for Donchian Channel
input double RiskRewardRatio = 1.5;     // Risk-to-reward ratio
input double LotSize = 0.1;             // Default lot size for trading
input double pipsToStopLoss = 15;       // Stop loss in pips for Breakout
input double pipsToTakeProfit = 30;     // Take profit in pips for Breakout

// Input parameters for Divergence Strategy
input int DivergenceMACDPeriod = 12;    // MACD Fast EMA period
input int DivergenceSignalPeriod = 9;   // MACD Signal period
input double DivergenceLots = 1.0;      // Lot size for Divergence trades
input double DivergenceStopLoss = 300;   // Stop Loss in points for Divergence
input double DivergenceTakeProfit = 500; // Take Profit in points for Divergence
input int DivergenceMagicNumber = 87654321;     // Magic number for Divergence Strategy
input int DivergenceLookBack = 8;       // Number of periods to look back for divergence
input double profitLockerPoints  = 20;  // Number of profit points to lock 

// Indicator handle storage
int rsi_handle;                         
int handle;                             // Handle for Donchian Channel
int macd_handle;


double ExtUpBuffer[];                   // Upper Donchian buffer
double ExtDnBuffer[];                   // Lower Donchian buffer
double ExtMacdBuffer[];                 // MACD buffer
double ExtSignalBuffer[];               // Signal buffer
int globalMagicNumber;

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
{
    // Initialize RSI handle
    rsi_handle = iRSI(_Symbol, PERIOD_CURRENT, RSI_Period, PRICE_CLOSE);
    if (rsi_handle == INVALID_HANDLE)
    {
        Print("Failed to create RSI indicator handle. Error: ", GetLastError());
        return INIT_FAILED;
    }

    // Create a handle for the Donchian Channel
    handle = iCustom(_Symbol, PERIOD_CURRENT, "Free Indicators\\Donchian Channel", InpDonchianPeriod);
    if (handle == INVALID_HANDLE)
    {
        Print("Failed to load the Donchian Channel indicator. Error: ", GetLastError());
        return INIT_FAILED;
    }

    // Initialize MACD handle for divergence
    globalMagicNumber = DivergenceMagicNumber;
    macd_handle = iMACD(_Symbol, PERIOD_CURRENT, DivergenceMACDPeriod, 26, DivergenceSignalPeriod, PRICE_CLOSE);
    if (macd_handle == INVALID_HANDLE)
    {
        Print("Failed to create MACD indicator handle for divergence strategy. Error: ", GetLastError());
        return INIT_FAILED;
    }

    // Resize arrays for MACD buffers
    ArrayResize(ExtMacdBuffer, DivergenceLookBack);
    ArrayResize(ExtSignalBuffer, DivergenceLookBack);

    return INIT_SUCCEEDED;
}

//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
    IndicatorRelease(rsi_handle);
    IndicatorRelease(handle);
    IndicatorRelease(macd_handle);
}

//+------------------------------------------------------------------+
//| Check and execute Trend Following EA trading logic               |
//+------------------------------------------------------------------+
void CheckTrendFollowing()
{
    if (PositionsTotal() >= 2) return; // Ensure no more than 2 orders from this strategy

    double rsi_value;
    double rsi_values[];
    if (CopyBuffer(rsi_handle, 0, 0, 1, rsi_values) <= 0)
    {
        Print("Failed to get RSI value. Error: ", GetLastError());
        return;
    }
    rsi_value = rsi_values[0];

    double ma_short = iMA(_Symbol, PERIOD_CURRENT, 50, 0, MODE_EMA, PRICE_CLOSE);
    double ma_long = iMA(_Symbol, PERIOD_CURRENT, 200, 0, MODE_EMA, PRICE_CLOSE);

    bool is_uptrend = ma_short > ma_long;
    bool is_downtrend = ma_short < ma_long;

    if (is_uptrend && rsi_value < RSI_Oversold)
    {
        double currentPrice = SymbolInfoDouble(_Symbol, SYMBOL_BID);
        double stopLossPrice = currentPrice - StopLoss * _Point;
        double takeProfitPrice = currentPrice + TakeProfit * _Point;

        // Corrected Buy method call with 6 parameters
        if (trade.Buy(Lots, _Symbol, 0, stopLossPrice, takeProfitPrice, "Trend Following Buy"))
        {
            Print("Trend Following Buy order placed.");
        }
    }
    else if (is_downtrend && rsi_value > RSI_Overbought)
    {
        double currentPrice = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
        double stopLossPrice = currentPrice + StopLoss * _Point;
        double takeProfitPrice = currentPrice - TakeProfit * _Point;

        // Corrected Sell method call with 6 parameters
        if (trade.Sell(Lots, _Symbol, 0, stopLossPrice, takeProfitPrice, "Trend Following Sell"))
        {
            Print("Trend Following Sell order placed.");
        }
    }
}

//+------------------------------------------------------------------+
//| Check and execute Breakout EA trading logic                      |
//+------------------------------------------------------------------+
void CheckBreakoutTrading()
{
    if (PositionsTotal() >= 2) return; // Ensure no more than 2 orders from this strategy

    ArrayResize(ExtUpBuffer, 2);
    ArrayResize(ExtDnBuffer, 2);

    if (CopyBuffer(handle, 0, 0, 2, ExtUpBuffer) <= 0 || CopyBuffer(handle, 2, 0, 2, ExtDnBuffer) <= 0)
    {
        Print("Error reading Donchian Channel buffer. Error: ", GetLastError());
        return;
    }

    double closePrice = iClose(_Symbol, PERIOD_CURRENT, 0);
    double lastOpen = iOpen(_Symbol, PERIOD_D1, 1);
    double lastClose = iClose(_Symbol, PERIOD_D1, 1);

    bool isBullishDay = lastClose > lastOpen;
    bool isBearishDay = lastClose < lastOpen;

    if (isBullishDay && closePrice > ExtUpBuffer[1])
    {
        double stopLoss = closePrice - pipsToStopLoss * _Point;
        double takeProfit = closePrice + pipsToTakeProfit * _Point;
        if (trade.Buy(LotSize, _Symbol, 0, stopLoss, takeProfit, "Breakout Buy") > 0)
        {
            Print("Breakout Buy order placed.");
        }
    }
    else if (isBearishDay && closePrice < ExtDnBuffer[1])
    {
        double stopLoss = closePrice + pipsToStopLoss * _Point;
        double takeProfit = closePrice - pipsToTakeProfit * _Point;
        if (trade.Sell(LotSize, _Symbol, 0, stopLoss, takeProfit, "Breakout Sell") > 0)
        {
            Print("Breakout Sell order placed.");
        }
    }
}

//+------------------------------------------------------------------+
//| DIVERGENCE TRADING STRATEGY                                      |
//+------------------------------------------------------------------+

bool CheckBullishRegularDivergence()
{
    double priceLow1 = iLow(_Symbol, PERIOD_CURRENT, 2);
    double priceLow2 = iLow(_Symbol, PERIOD_CURRENT, DivergenceLookBack);
    double macdLow1 = ExtMacdBuffer[2];
    double macdLow2 = ExtMacdBuffer[DivergenceLookBack - 1];

    return (priceLow1 < priceLow2 && macdLow1 > macdLow2);
}

bool CheckBullishHiddenDivergence()
{
    double priceLow1 = iLow(_Symbol, PERIOD_CURRENT, 2);
    double priceLow2 = iLow(_Symbol, PERIOD_CURRENT, DivergenceLookBack);
    double macdLow1 = ExtMacdBuffer[2];
    double macdLow2 = ExtMacdBuffer[DivergenceLookBack - 1];

    return (priceLow1 > priceLow2 && macdLow1 < macdLow2);
}

bool CheckBearishRegularDivergence()
{
    double priceHigh1 = iHigh(_Symbol, PERIOD_CURRENT, 2);
    double priceHigh2 = iHigh(_Symbol, PERIOD_CURRENT, DivergenceLookBack);
    double macdHigh1 = ExtMacdBuffer[2];
    double macdHigh2 = ExtMacdBuffer[DivergenceLookBack - 1];

    return (priceHigh1 > priceHigh2 && macdHigh1 < macdHigh2);
}

bool CheckBearishHiddenDivergence()
{
    double priceHigh1 = iHigh(_Symbol, PERIOD_CURRENT, 2);
    double priceHigh2 = iHigh(_Symbol, PERIOD_CURRENT, DivergenceLookBack);
    double macdHigh1 = ExtMacdBuffer[2]; 
    double macdHigh2 = ExtMacdBuffer[DivergenceLookBack - 1];

    return (priceHigh1 < priceHigh2 && macdHigh1 > macdHigh2);
}

void CheckDivergenceTrading()
{
    if (!UseDivergenceStrategy) return;

    // Check if no position is open or if less than 3 positions are open
    int openDivergencePositions = CountOrdersByMagic(DivergenceMagicNumber);
    if (openDivergencePositions == 0 || openDivergencePositions < 3)
    {
        int barsAvailable = Bars(_Symbol, PERIOD_CURRENT);
        if (barsAvailable < DivergenceLookBack * 2)
        {
            Print("Not enough data bars for MACD calculation.");
            return;
        }

        int attempt = 0;
        while(attempt < 6)
        {
            if (CopyBuffer(macd_handle, 0, 0, DivergenceLookBack, ExtMacdBuffer) > 0 &&
                CopyBuffer(macd_handle, 1, 0, DivergenceLookBack, ExtSignalBuffer) > 0)
                break; 
            
            Print("Failed to copy MACD buffer, retrying...");
            Sleep(1000);
            attempt++;
        }
        if(attempt == 6)
        {
            Print("Failed to copy MACD buffers after ", attempt, " attempts.");
            return;
        }

        if(TimeCurrent() == iTime(_Symbol, PERIOD_CURRENT, 0))
        {
            Print("Skipping trade due to incomplete bar data.");
            return;
        }

        double currentClose = iClose(_Symbol, PERIOD_CURRENT, 0);
        double dailyClose = iClose(_Symbol, PERIOD_D1, 0);
        double dailyOpen = iOpen(_Symbol, PERIOD_D1, 0);
        bool isDailyBullish = dailyClose > dailyOpen;
        bool isDailyBearish = dailyClose < dailyOpen;

        // Only proceed with buy orders if D1 is bullish
        if (isDailyBullish)
        {
            if ((CheckBullishRegularDivergence() && ExtMacdBuffer[0] > ExtSignalBuffer[0]) ||
                CheckBullishHiddenDivergence())
            {
                ExecuteDivergenceOrder(true);
            }
        }

        // Only proceed with sell orders if D1 is bearish
        if (isDailyBearish)
        {
            if ((CheckBearishRegularDivergence() && ExtMacdBuffer[0] < ExtSignalBuffer[0]) ||
                CheckBearishHiddenDivergence())
            {
                ExecuteDivergenceOrder(false);
            }
        }
    }
    else
    {
        Print("Divergence strategy: Maximum number of positions reached.");
    }
}

void ExecuteDivergenceOrder(bool isBuy)
{
    // Ensure the magic number is set for the trade
    trade.SetExpertMagicNumber(DivergenceMagicNumber);
    
    double currentPrice = isBuy ? SymbolInfoDouble(_Symbol, SYMBOL_ASK) : SymbolInfoDouble(_Symbol, SYMBOL_BID);
    double stopLossPrice = isBuy ? currentPrice - DivergenceStopLoss * _Point : currentPrice + DivergenceStopLoss * _Point;
    double takeProfitPrice = isBuy ? currentPrice + DivergenceTakeProfit * _Point : currentPrice - DivergenceTakeProfit * _Point;

    if (isBuy)
    {
        if (trade.Buy(DivergenceLots, _Symbol, 0, stopLossPrice, takeProfitPrice, "Divergence Buy"))
        {
            Print("Divergence Buy order placed.");
        }
    }
    else
    {
        if (trade.Sell(DivergenceLots, _Symbol, 0, stopLossPrice, takeProfitPrice, "Divergence Sell"))
        {
            Print("Divergence Sell order placed.");
        }
    }
}

int CountOrdersByMagic(int magic)
{
    int count = 0;
    for (int i = 0; i < PositionsTotal(); i++)
    {
        ulong ticket = PositionGetTicket(i);
        if (PositionSelectByTicket(ticket))
        {
            if (PositionGetInteger(POSITION_MAGIC) == magic)
            {
                count++;
            }
        }
    }
    return count;
}
//+------------------------------------------------------------------+
//| Profit Locking Logic                                             |
//+------------------------------------------------------------------+
void LockProfits()
{
    for (int i = PositionsTotal() - 1; i >= 0; i--)

    {
        ulong ticket = PositionGetTicket(i);
        if (PositionSelectByTicket(ticket))
        {
            double entryPrice = PositionGetDouble(POSITION_PRICE_OPEN);
            double currentProfit = PositionGetDouble(POSITION_PROFIT);
            double currentPrice = PositionGetDouble(POSITION_PRICE_CURRENT);
            
            // Convert profit to points
            double profitPoints = MathAbs(currentProfit / _Point);

            // Check if profit has exceeded 100 points
            if (profitPoints >= 100)
            {
                double newStopLoss;
                
                if (PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_BUY)
                {
                    newStopLoss = entryPrice + profitLockerPoints * _Point; // 20 points above entry for buys
                }
                else if (PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_SELL)
                {
                    newStopLoss = entryPrice - profitLockerPoints * _Point; // 20 points below entry for sells
                }
                else
                {
                    continue; // Skip if not a buy or sell position
                }

                // Modify stop loss only if the new stop loss is more protective
                double currentStopLoss = PositionGetDouble(POSITION_SL);
                if (PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_BUY)
                {
                    if (currentStopLoss < newStopLoss || currentStopLoss == 0)
                    {
                        if (trade.PositionModify(ticket, newStopLoss, PositionGetDouble(POSITION_TP)))
                        {
                            Print("Profit locking for buy position: Stop Loss moved to ", newStopLoss);
                        }
                    }
                }
                else // POSITION_TYPE_SELL
                {
                    if (currentStopLoss > newStopLoss || currentStopLoss == 0)
                    {
                        if (trade.PositionModify(ticket, newStopLoss, PositionGetDouble(POSITION_TP)))
                        {
                            Print("Profit locking for sell position: Stop Loss moved to ", newStopLoss);
                        }
                    }
                }
            }
        }
    }
}

//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
{
    if (UseTrendFollowingStrategy) CheckTrendFollowing();
    if (UseBreakoutStrategy) CheckBreakoutTrading();
    if (UseDivergenceStrategy) CheckDivergenceTrading();
    LockProfits(); // Call this function to check and lock profits
}


Backtest-Ergebnisse und praktische Anwendung

Um unseren Test durchzuführen, müssen wir den Trend Constraint Expert unter den Experten auf dem Terminal suchen. Stellen Sie sicher, dass Sie mit einem Demokonto arbeiten. Es öffnet sich ein Strategietester-Fenster, in dem die Eingabeeinstellungen für verschiedene Optimierungen angepasst werden können. Siehe die Bilder unten mit Standardeinstellungen:

Eingabe-Einstellungen

Der Trend Constraint Experte: Demo-Einstellungen 

Wir haben den Strategy Tester ausgeführt, und die Trades wurden erfolgreich ausgeführt. Das System beschränkte die maximale Anzahl von Positionen auf drei pro Sitzung, wodurch eine mehrfache unkontrollierte Auftragsausführung verhindert wurde. Das folgende Bild zeigt einen Ausschnitt aus dem Backtesting-Prozess in Aktion:

EA auf Tester

Der Trend Constraint Experte: Prüfung auf EURUSD M15

Die folgende Abbildung zeigt, dass unsere Positionsmanagementfunktionen wie erwartet funktionieren. Gemäß der implementierten Logik sind maximal drei Orders aktiv, die jeweils mit dem Kommentar Divergence Sell gekennzeichnet sind, wodurch sie leicht identifizierbar und auf die Strategie abgestimmt sind.


Der Trend Constraint Experte: Maximal 3 Positionen pro


Schlussfolgerung

Wir haben verschiedene Arten von Divergenzen untersucht und sie im Rahmen einer Confluence-Strategie in Code umgesetzt, die Indikatoren wie RSI und MACD kombiniert, um präzise Handelssignale zu generieren. Diese Signale wurden weiter verfeinert, indem tägliche Kerzen-Trendeinschränkungen einbezogen wurden, um sicherzustellen, dass sie mit breiteren Markttrends übereinstimmen und somit zuverlässiger sind. Unser Trend Constraint Expert Advisor (EA) verfügt jetzt über unterschiedliche, konfigurierbare Strategien, die es den Nutzern ermöglichen, den EA auf ihre Handelsvorlieben zuzuschneiden und an verschiedene Marktbedingungen anzupassen.

Um das Handelsmanagement zu verbessern, haben wir Funktionen wie eindeutige MAGIC-Nummern für jede Position eingeführt, die eine präzise Kontrolle der offenen Geschäfte und eine Begrenzung der Anzahl der Positionen pro Strategie ermöglichen. Darüber hinaus wurde eine nutzerdefinierte Gewinnsicherungsfunktion entwickelt, um Gewinne zu sichern, indem die Stop-Loss dynamisch angepasst werden, falls der Markt vor Erreichen des Take-Profit-Ziels umschlägt. Diese Funktionalität gewährleistet sowohl Risikomanagement als auch Flexibilität und macht den EA robust und anpassungsfähig. Im Anhang finden Sie die Quelldateien für das EA-Programm. Wir ermutigen Sie, sie zu erforschen, verschiedene Konfigurationen zu testen und Ihr Feedback im Kommentarbereich zu hinterlassen. Bitte beachten: Diese Beispiele sind nur für Lehrzwecke gedacht, und alle Tests sollten auf Demo-Konten durchgeführt werden.

Viel Spaß beim Entwickeln, liebe Händler!

Übersetzt aus dem Englischen von MetaQuotes Ltd.
Originalartikel: https://www.mql5.com/en/articles/16549

Beigefügte Dateien |
Handelseinblicke über das Volumen: Trendbestätigung Handelseinblicke über das Volumen: Trendbestätigung
Die Enhanced Trend Confirmation Technique kombiniert Preisaktionen, Volumenanalysen und maschinelles Lernen, um echte Marktbewegungen zu identifizieren. Für die Handelsvalidierung sind sowohl Preisausbrüche als auch Volumensprünge (50 % über dem Durchschnitt) erforderlich, während ein neuronales LSTM-Netzwerk für zusätzliche Bestätigung sorgt. Das System verwendet eine ATR-basierte Positionsgröße und ein dynamisches Risikomanagement, wodurch es an verschiedene Marktbedingungen angepasst werden kann und gleichzeitig falsche Signale herausfiltert.
MQL5 Handels-Toolkit (Teil 4): Entwicklung einer EX5-Bibliothek zur Verwaltung der Handelsgeschichte MQL5 Handels-Toolkit (Teil 4): Entwicklung einer EX5-Bibliothek zur Verwaltung der Handelsgeschichte
Lernen Sie, wie Sie geschlossene Positionen, Aufträge und Deals mit MQL5 abrufen, verarbeiten, klassifizieren, sortieren, analysieren und verwalten können, indem Sie in einer detaillierten Schritt-für-Schritt-Anleitung eine umfangreiche History Management EX5 Library erstellen.
Entwicklung eines Toolkit zur Analyse von Preisaktionen (Teil 4): Der Analytik Forecaster EA Entwicklung eines Toolkit zur Analyse von Preisaktionen (Teil 4): Der Analytik Forecaster EA
Wir gehen über die einfache Darstellung von analysierten Metriken in Charts hinaus und bieten eine breitere Perspektive, die auch die Integration von Telegram umfasst. Mit dieser Erweiterung können wichtige Ergebnisse über die Telegram-App direkt auf Ihr mobiles Gerät geliefert werden. Begleiten Sie uns in diesem Artikel auf dieser gemeinsamen Reise.
Klassische Strategien neu interpretieren (Teil 12): EURUSD Ausbruchsstrategie Klassische Strategien neu interpretieren (Teil 12): EURUSD Ausbruchsstrategie
Begleiten Sie uns heute, wenn wir uns der Herausforderung stellen, eine profitable Ausbruchs-Handelsstrategie in MQL5 zu entwickeln. Wir haben das Währungspaar EURUSD ausgewählt und versucht, Kursausbrüche auf dem stündlichen Zeitrahmen zu handeln. Unser System hatte Schwierigkeiten, zwischen falschen Ausbrüchen und dem Beginn eines echten Trends zu unterscheiden. Wir haben unser System mit Filtern überlagert, die unsere Verluste minimieren und gleichzeitig unsere Gewinne erhöhen sollen. Am Ende haben wir unser System erfolgreich profitabel und weniger anfällig für falsche Ausbrüche gemacht.