English 日本語
preview
Entwicklung eines Toolkit zur Analyse von Preisaktionen (Teil 13): RSI-Sentinel-Tool

Entwicklung eines Toolkit zur Analyse von Preisaktionen (Teil 13): RSI-Sentinel-Tool

MetaTrader 5Handelssysteme |
119 1
Christian Benjamin
Christian Benjamin

Inhalt


Einführung

Divergenz ist ein Konzept in der technischen Analyse, bei dem die Bewegung eines Indikators, z. B. des Momentums oder der Oszillatoren, von der Preisbewegung abweicht. Wenn der Kurs neue Höchst- oder Tiefststände bildet, die vom Indikator nicht widergespiegelt werden, kann dies ein Zeichen für einen sich abschwächenden Trend sein und möglicherweise eine Trendumkehr oder eine Änderung der Dynamik vorhersagen. Die RSI-Divergenz ist ein einfaches Mittel, um potenzielle Marktumkehrungen zu erkennen. Wenn sich der Kurs in eine Richtung bewegt, während der RSI in eine andere Richtung geht, könnte dies ein Zeichen für einen Trendwechsel sein. Das manuelle Scannen der Charts nach diesen Signalen kann jedoch langsam und fehleranfällig sein. Hier kommt die Automatisierung ins Spiel.

In diesem Artikel werden wir einen MQL5 Expert Advisor erstellen, der automatisch RSI-Divergenzsignale erkennt. Der EA markiert diese Signale mit klaren Pfeilen auf Ihrem Chart und liefert eine kurze Zusammenfassung, damit Sie schnell erkennen können, was passiert. Egal, ob Sie Anfänger oder erfahrener Händler sind, dieses Instrument hilft Ihnen, Handelschancen zu erkennen, die Sie vor der Ausführung von Geschäften validieren können, ohne stundenlang manuelle Analysen durchführen zu müssen. Lassen Sie uns eintauchen und sehen, wie dieser RSI Divergence EA Ihren Handelsprozess vereinfachen kann.


Überblick über die Strategie

Die RSI-Divergenz verstehen

Eine RSI-Divergenz tritt auf, wenn sich der Relative-Stärke-Index (RSI) in eine andere Richtung als der Kurs des Vermögenswerts bewegt, was auf eine potenzielle Verschiebung der Kursdynamik hinweist. Dieser Kontrast zwischen dem RSI und der Preisentwicklung ist ein Schlüsselindikator, den Händler nutzen, um Marktumkehrungen oder Trendfortsetzungen zu antizipieren. In der Regel folgt der RSI der Kursdynamik und bestätigt damit die vorherrschenden Trends. Wenn jedoch eine Divergenz auftritt, offenbart sie eine Diskrepanz, die häufig einer bedeutenden Kursbewegung vorausgeht. Das frühzeitige Erkennen dieser Signale kann für das Timing von Marktein- und -austritten entscheidend sein.

Im Zusammenhang mit der RSI-Divergenz gibt es zwei Haupttypen

1. Reguläre RSI-Divergenz

Eine Reguläre RSI-Divergenz wird im Allgemeinen als Umkehrsignal angesehen. Es deutet darauf hin, dass der aktuelle Trend an Stärke verliert und möglicherweise kurz vor einer Umkehr steht.

  • Reguläre Aufwärts-Divergenz des RSI

Tritt auf, wenn der Kurs ein tieferes Tief bildet, während der RSI ein höheres Tief bildet. Dies deutet darauf hin, dass der Kurs zwar rückläufig ist, die Dynamik jedoch beginnt, sich nach oben zu verlagern, was auf eine mögliche Umkehr zu einem Aufwärtstrend hindeutet.

Aufwärts-Divergenz

Abb. 1. Aufwärts-Divergenz

  • Reguläre Abwärts-Divergenz des RSI

Tritt auf, wenn der Kurs ein höheres Hoch bildet, während der RSI ein niedrigeres Hoch bildet. Trotz des steigenden Kurses deutet das nachlassende Momentum (wie der RSI zeigt) darauf hin, dass ein Abschwung bevorstehen könnte.

Verkaufssignal

Abb. 2. Abwärts-Divergenz

2. Verborgene RSI-Divergenz

Eine verborgene RSI-Divergenz wird als Signal für eine Trendfortsetzung und nicht für eine bevorstehende Umkehr interpretiert. Sie bestätigt, dass der aktuelle Trend immer noch stark ist, selbst wenn der RSI und der Preis vorübergehend auseinander laufen.

  • Verborgene Aufwärts-Divergenz des RSI: Wenn der Kurs in einem Aufwärtstrend ein höheres Tief bildet, während der RSI ein niedrigeres Tief bildet, deutet dies darauf hin, dass die Korrektur nur vorübergehend ist und der Aufwärtstrend wahrscheinlich fortgesetzt wird.

Verborgene Aufwärts-Divergenz

Abb. 3. Verborgene Aufwärts-Divergenz des RSI

  • Verborgene Abwärts-Divergenz des RSI: Wenn in einem Abwärtstrend der Kurs ein niedrigeres Hoch bildet, während der RSI ein höheres Hoch bildet, bestätigt dies die Stärke des Abwärtstrends und deutet darauf hin, dass die Abwärtsbewegung wahrscheinlich anhalten wird.

Abwärts-Divergenz

Abb. 4. Verborgene Abwärts-Divergenz

Nachfolgend finden Sie eine zusammenfassende Tabelle, in der die wichtigsten Unterschiede zwischen den verschiedenen Arten von RSI-Divergenzen zusammengefasst sind:

RSI-Divergenztyp Preisaktion RSI-Aktion Signalart Erwartung
Regulär Aufwärts Tiefes Tief (LL) Höheres Tief (HL) Umkehr nach oben Abwärtstrend zu Aufwärtstrend
Regulär Abwärts Hohes Hoch (HH)  Tieferes Hoch (LH) Umkehr nach unten Aufwärtstrend bis Abwärtstrend
Verborgen Aufwärts Hohes Tief(HL) Tiefes Tief (LL) Fortsetzung nach oben Aufwärtstrend setzt sich fort
Verborgen Abwärts Tiefes Hoch (LH) Hohes Hoch (HH) Fortsetzung nach unten Abwärtstrend setzt sich fort

Zusammenfassend lässt sich sagen, dass dieser EA sowohl die Kurs- als auch die RSI-Daten über einen definierten Rückblickzeitraum kontinuierlich überprüft, um Diskrepanzen zwischen ihren Bewegungen zu erkennen, was wir RSI-Divergenz nennen.

Im Folgenden wird beschrieben, was sie tut:

1. Datenerhebung und -aufbereitung

Der EA sammelt die RSI-Werte zusammen mit den entsprechenden Kursdaten (Tiefst-, Höchst- und Schlusskurse sowie die Uhrzeit) der letzten Bars. Dadurch wird sichergestellt, dass die Analyse immer auf den neuesten und vollständigen Informationen beruht.

2. Identifizierung von Wendepunkten

Anschließend werden sowohl in den Kurs- als auch in den RSI-Daten lokale Höchst- und Tiefststände ermittelt. Diese Swing-Punkte dienen als Referenzmarker für unsere Divergenzanalyse.
3. Reguläre Divergenz erkennen

  • Reguläre Aufwärts-Divergenz: Der EA sucht nach Situationen, in denen der Kurs ein tieferes Tief erreicht, während der RSI ein höheres Tief bildet, was darauf hindeutet, dass ein Abwärtstrend an Schwung verliert und sich nach oben umkehren könnte.
  • Reguläre Abwärts-Divergenz: Außerdem wird geprüft, ob der Kurs ein höheres Hoch erreicht, während der RSI ein niedrigeres Hoch bildet, was darauf hindeutet, dass sich ein Aufwärtstrend seinem Ende nähert, da die Dynamik nachlässt.

4. Verborgene Divergenz aufspüren
  • Verborgene Aufwärts-Divergenz: Wenn der Kurs in einem Aufwärtstrend ein höheres Tief bildet, der RSI jedoch ein niedrigeres Tief verzeichnet, erkennt der EA dies als Zeichen dafür, dass der allgemeine Aufwärtstrend trotz eines vorübergehenden Rückschlags weiterhin stark ist.
  • Verborgene Abwärts-Divergenz: Umgekehrt gilt: Wenn der Kurs während eines Abwärtstrends ein niedrigeres Hoch erreicht, während der RSI ein höheres Hoch anzeigt, bestätigt dies, dass der Abwärtstrend wahrscheinlich anhalten wird.

5. Visuelle und protokollierte Signalerzeugung

Sobald eine Divergenz erkannt wird, egal ob sie regelmäßig oder versteckt ist, markiert der EA das Ereignis visuell auf dem Chart (mit Pfeilen und Text) und protokolliert die Details des Signals für weitere Analysen oder Backtests. Wie die oben genannten Vorgänge ablaufen, erfahren Sie im Abschnitt Codeaufschlüsselung weiter unten.


MQL5 Code

//+--------------------------------------------------------------------+
//|                                                RSI Divergence.mql5 |
//|                                 Copyright 2025, Christian Benjamin |
//|                                               https://www.mql5.com |
//+--------------------------------------------------------------------+
#property copyright "2025, MetaQuotes Software Corp."
#property link      "https://www.mql5.com/en/users/lynnchris"
#property version   "1.0"
#property strict

//---- Input parameters
input int    InpRSIPeriod             = 14;    // RSI period
input int    InpSwingLeft             = 1;     // Bars to the left for swing detection (relaxed)
input int    InpSwingRight            = 1;     // Bars to the right for swing detection (relaxed)
input int    InpLookback              = 100;   // Number of bars to scan for divergence
input int    InpEvalBars              = 5;     // Bars after which to evaluate a signal
input int    InpMinBarsBetweenSignals = 1;     // Minimum bars between same-type signals (allows frequent re-entry)
input double InpArrowOffset           = 3.0;   // Arrow offset (in points) for display
input double InpMinSwingDiffPct       = 0.05;  // Lower minimum % difference to qualify as a swing
input double InpMinRSIDiff            = 1.0;   // Lower minimum difference in RSI between swing points

// Optional RSI threshold filter for bullish divergence (disabled by default)
input bool   InpUseRSIThreshold       = false; // If true, require earlier RSI swing to be oversold for bullish divergence
input double InpRSIOversold           = 30;    // RSI oversold level
input double InpRSIOverbought         = 70;    // RSI overbought level (if needed for bearish)

//---- Global variables
int      rsiHandle;         // Handle for the RSI indicator
double   rsiBuffer[];       // Buffer for RSI values
double   lowBuffer[];       // Buffer for low prices
double   highBuffer[];      // Buffer for high prices
double   closeBuffer[];     // Buffer for close prices
datetime timeBuffer[];       // Buffer for bar times
int      g_totalBars = 0;   // Number of bars in our copied arrays
datetime lastBarTime = 0;   // Time of last closed bar

//---- Structure to hold signal information
struct SignalInfo
  {
   string            type;         // e.g. "RegBearish Divergence", "HiddenBullish Divergence"
   int               barIndex;     // Bar index where the signal was generated
   datetime          signalTime;   // Time of the signal bar
   double            signalPrice;  // Price used for the signal (swing high for bearish, swing low for bullish)
  };

SignalInfo signals[];  // Global array to store signals

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
   rsiHandle = iRSI(_Symbol, _Period, InpRSIPeriod, PRICE_CLOSE);
   if(rsiHandle == INVALID_HANDLE)
     {
      Print("Error creating RSI handle");
      return(INIT_FAILED);
     }
   return(INIT_SUCCEEDED);
  }

//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
   if(rsiHandle != INVALID_HANDLE)
      IndicatorRelease(rsiHandle);
   EvaluateSignalsAndPrint();
  }

//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
  {
// Process once per new closed candle (using bar1's time)
   datetime currentBarTime = iTime(_Symbol, _Period, 1);
   if(currentBarTime == lastBarTime)
      return;
   lastBarTime = currentBarTime;

//--- Copy RSI data
   ArrayResize(rsiBuffer, InpLookback);
   ArraySetAsSeries(rsiBuffer, true);
   if(CopyBuffer(rsiHandle, 0, 0, InpLookback, rsiBuffer) <= 0)
     {
      Print("Error copying RSI data");
      return;
     }

//--- Copy price and time data
   ArrayResize(lowBuffer, InpLookback);
   ArrayResize(highBuffer, InpLookback);
   ArrayResize(closeBuffer, InpLookback);
   ArraySetAsSeries(lowBuffer, true);
   ArraySetAsSeries(highBuffer, true);
   ArraySetAsSeries(closeBuffer, true);
   if(CopyLow(_Symbol, _Period, 0, InpLookback, lowBuffer) <= 0 ||
      CopyHigh(_Symbol, _Period, 0, InpLookback, highBuffer) <= 0 ||
      CopyClose(_Symbol, _Period, 0, InpLookback, closeBuffer) <= 0)
     {
      Print("Error copying price data");
      return;
     }

   ArrayResize(timeBuffer, InpLookback);
   ArraySetAsSeries(timeBuffer, true);
   if(CopyTime(_Symbol, _Period, 0, InpLookback, timeBuffer) <= 0)
     {
      Print("Error copying time data");
      return;
     }

   g_totalBars = InpLookback;

//--- Identify swing lows and swing highs
   int swingLows[];
   int swingHighs[];
   int startIndex = InpSwingLeft;
   int endIndex   = g_totalBars - InpSwingRight;
   for(int i = startIndex; i < endIndex; i++)
     {
      if(IsSignificantSwingLow(i, InpSwingLeft, InpSwingRight))
        {
         ArrayResize(swingLows, ArraySize(swingLows) + 1);
         swingLows[ArraySize(swingLows) - 1] = i;
        }
      if(IsSignificantSwingHigh(i, InpSwingLeft, InpSwingRight))
        {
         ArrayResize(swingHighs, ArraySize(swingHighs) + 1);
         swingHighs[ArraySize(swingHighs) - 1] = i;
        }
     }

//--- Bearish Divergence (using swing highs)
   if(ArraySize(swingHighs) >= 2)
     {
      ArraySort(swingHighs); // ascending order: index 0 is most recent
      int recent   = swingHighs[0];
      int previous = swingHighs[1];

      // Regular Bearish Divergence: Price makes a higher high while RSI makes a lower high
      if(highBuffer[recent] > highBuffer[previous] &&
         rsiBuffer[recent] < rsiBuffer[previous] &&
         (rsiBuffer[previous] - rsiBuffer[recent]) >= InpMinRSIDiff)
        {
         Print("Regular Bearish Divergence detected at bar ", recent);
         DisplaySignal("RegBearish Divergence", recent);
        }
      // Hidden Bearish Divergence: Price makes a lower high while RSI makes a higher high
      else
         if(highBuffer[recent] < highBuffer[previous] &&
            rsiBuffer[recent] > rsiBuffer[previous] &&
            (rsiBuffer[recent] - rsiBuffer[previous]) >= InpMinRSIDiff)
           {
            Print("Hidden Bearish Divergence detected at bar ", recent);
            DisplaySignal("HiddenBearish Divergence", recent);
           }
     }

//--- Bullish Divergence (using swing lows)
   if(ArraySize(swingLows) >= 2)
     {
      ArraySort(swingLows); // ascending order: index 0 is most recent
      int recent   = swingLows[0];
      int previous = swingLows[1];

      // Regular Bullish Divergence: Price makes a lower low while RSI makes a higher low
      if(lowBuffer[recent] < lowBuffer[previous] &&
         rsiBuffer[recent] > rsiBuffer[previous] &&
         (rsiBuffer[recent] - rsiBuffer[previous]) >= InpMinRSIDiff)
        {
         // Optionally require the earlier swing's RSI be oversold
         if(!InpUseRSIThreshold || rsiBuffer[previous] <= InpRSIOversold)
           {
            Print("Regular Bullish Divergence detected at bar ", recent);
            DisplaySignal("RegBullish Divergence", recent);
           }
        }
      // Hidden Bullish Divergence: Price makes a higher low while RSI makes a lower low
      else
         if(lowBuffer[recent] > lowBuffer[previous] &&
            rsiBuffer[recent] < rsiBuffer[previous] &&
            (rsiBuffer[previous] - rsiBuffer[recent]) >= InpMinRSIDiff)
           {
            Print("Hidden Bullish Divergence detected at bar ", recent);
            DisplaySignal("HiddenBullish Divergence", recent);
           }
     }
  }

//+------------------------------------------------------------------------+
//| IsSignificantSwingLow: Determines if the bar at 'index' is a swing low |
//+------------------------------------------------------------------------+
bool IsSignificantSwingLow(int index, int left, int right)
  {
   double currentLow = lowBuffer[index];
// Check left side for a local minimum condition
   for(int i = index - left; i < index; i++)
     {
      if(i < 0)
         continue;
      double pctDiff = MathAbs((lowBuffer[i] - currentLow) / currentLow) * 100.0;
      if(lowBuffer[i] < currentLow && pctDiff > InpMinSwingDiffPct)
         return false;
     }
// Check right side for a local minimum condition
   for(int i = index + 1; i <= index + right; i++)
     {
      if(i >= g_totalBars)
         break;
      double pctDiff = MathAbs((lowBuffer[i] - currentLow) / currentLow) * 100.0;
      if(lowBuffer[i] < currentLow && pctDiff > InpMinSwingDiffPct)
         return false;
     }
   return true;
  }

//+--------------------------------------------------------------------------+
//| IsSignificantSwingHigh: Determines if the bar at 'index' is a swing high |
//+--------------------------------------------------------------------------+
bool IsSignificantSwingHigh(int index, int left, int right)
  {
   double currentHigh = highBuffer[index];
// Check left side for a local maximum condition
   for(int i = index - left; i < index; i++)
     {
      if(i < 0)
         continue;
      double pctDiff = MathAbs((currentHigh - highBuffer[i]) / currentHigh) * 100.0;
      if(highBuffer[i] > currentHigh && pctDiff > InpMinSwingDiffPct)
         return false;
     }
// Check right side for a local maximum condition
   for(int i = index + 1; i <= index + right; i++)
     {
      if(i >= g_totalBars)
         break;
      double pctDiff = MathAbs((currentHigh - highBuffer[i]) / currentHigh) * 100.0;
      if(highBuffer[i] > currentHigh && pctDiff > InpMinSwingDiffPct)
         return false;
     }
   return true;
  }

//+------------------------------------------------------------------+
//| DisplaySignal: Draws an arrow on the chart and records the signal|
//+------------------------------------------------------------------+
void DisplaySignal(string signalText, int barIndex)
  {
// Prevent duplicate signals on the same bar (or too close)
   for(int i = 0; i < ArraySize(signals); i++)
     {
      if(StringFind(signals[i].type, signalText) != -1)
         if(MathAbs(signals[i].barIndex - barIndex) < InpMinBarsBetweenSignals)
            return;
     }

// Update a "LatestSignal" label for regular signals.
   if(StringFind(signalText, "Reg") != -1)
     {
      string labelName = "LatestSignal";
      if(ObjectFind(0, labelName) == -1)
        {
         if(!ObjectCreate(0, labelName, OBJ_LABEL, 0, 0, 0))
           {
            Print("Failed to create LatestSignal label");
            return;
           }
         ObjectSetInteger(0, labelName, OBJPROP_CORNER, 0);
         ObjectSetInteger(0, labelName, OBJPROP_XDISTANCE, 10);
         ObjectSetInteger(0, labelName, OBJPROP_YDISTANCE, 20);
         ObjectSetInteger(0, labelName, OBJPROP_COLOR, clrWhite);
        }
      ObjectSetString(0, labelName, OBJPROP_TEXT, signalText);
     }

// Create an arrow object for the signal.
   string arrowName = "Arrow_" + signalText + "_" + IntegerToString(barIndex);
   if(ObjectFind(0, arrowName) < 0)
     {
      int arrowCode = 0;
      double arrowPrice = 0.0;
      color arrowColor = clrWhite;
      double point = SymbolInfoDouble(_Symbol, SYMBOL_POINT);

      if(StringFind(signalText, "Bullish") != -1)
        {
         arrowCode  = 233; // Wingdings up arrow
         arrowColor = clrLime;
         arrowPrice = lowBuffer[barIndex] - (InpArrowOffset * point);
        }
      else
         if(StringFind(signalText, "Bearish") != -1)
           {
            arrowCode  = 234; // Wingdings down arrow
            arrowColor = clrRed;
            arrowPrice = highBuffer[barIndex] + (InpArrowOffset * point);
           }

      if(!ObjectCreate(0, arrowName, OBJ_ARROW, 0, timeBuffer[barIndex], arrowPrice))
        {
         Print("Failed to create arrow object ", arrowName);
         return;
        }
      ObjectSetInteger(0, arrowName, OBJPROP_COLOR, arrowColor);
      ObjectSetInteger(0, arrowName, OBJPROP_ARROWCODE, arrowCode);
     }

// Record the signal for evaluation.
   SignalInfo sig;
   sig.type = signalText;
   sig.barIndex = barIndex;
   sig.signalTime = timeBuffer[barIndex];
   if(StringFind(signalText, "Bullish") != -1)
      sig.signalPrice = lowBuffer[barIndex];
   else
      sig.signalPrice = highBuffer[barIndex];
   ArrayResize(signals, ArraySize(signals) + 1);
   signals[ArraySize(signals) - 1] = sig;

   UpdateSignalCountLabel();
  }

//+------------------------------------------------------------------+
//| UpdateSignalCountLabel: Updates a label showing signal counts    |
//+------------------------------------------------------------------+
void UpdateSignalCountLabel()
  {
   int regCount = 0, hidCount = 0;
   for(int i = 0; i < ArraySize(signals); i++)
     {
      if(StringFind(signals[i].type, "Reg") != -1)
         regCount++;
      else
         if(StringFind(signals[i].type, "Hidden") != -1)
            hidCount++;
     }
   string countText = "Regular Signals: " + IntegerToString(regCount) +
                      "\nHidden Signals: " + IntegerToString(hidCount);
   string countLabel = "SignalCount";
   if(ObjectFind(0, countLabel) == -1)
     {
      if(!ObjectCreate(0, countLabel, OBJ_LABEL, 0, 0, 0))
        {
         Print("Failed to create SignalCount label");
         return;
        }
      ObjectSetInteger(0, countLabel, OBJPROP_CORNER, 0);
      ObjectSetInteger(0, countLabel, OBJPROP_XDISTANCE, 10);
      ObjectSetInteger(0, countLabel, OBJPROP_YDISTANCE, 40);
      ObjectSetInteger(0, countLabel, OBJPROP_COLOR, clrYellow);
     }
   ObjectSetString(0, countLabel, OBJPROP_TEXT, countText);
  }

//+--------------------------------------------------------------------+
//| EvaluateSignalsAndPrint: After backtesting, prints signal accuracy |
//+--------------------------------------------------------------------+
void EvaluateSignalsAndPrint()
  {
   double closeAll[];
   int totalBars = CopyClose(_Symbol, _Period, 0, WHOLE_ARRAY, closeAll);
   if(totalBars <= 0)
     {
      Print("Error copying complete close data for evaluation");
      return;
     }
   ArraySetAsSeries(closeAll, true);

   int totalEvaluated = 0, regTotal = 0, hidTotal = 0;
   int regEval = 0, hidEval = 0;
   int regCorrect = 0, hidCorrect = 0;

   for(int i = 0; i < ArraySize(signals); i++)
     {
      int evalIndex = signals[i].barIndex - InpEvalBars;
      if(evalIndex < 0)
         continue;
      double evalClose = closeAll[evalIndex];

      if(StringFind(signals[i].type, "Bullish") != -1)
        {
         if(StringFind(signals[i].type, "Reg") != -1)
           {
            regTotal++;
            regEval++;
            if(evalClose > signals[i].signalPrice)
               regCorrect++;
           }
         else
            if(StringFind(signals[i].type, "Hidden") != -1)
              {
               hidTotal++;
               hidEval++;
               if(evalClose > signals[i].signalPrice)
                  hidCorrect++;
              }
         totalEvaluated++;
        }
      else
         if(StringFind(signals[i].type, "Bearish") != -1)
           {
            if(StringFind(signals[i].type, "Reg") != -1)
              {
               regTotal++;
               regEval++;
               if(evalClose < signals[i].signalPrice)
                  regCorrect++;
              }
            else
               if(StringFind(signals[i].type, "Hidden") != -1)
                 {
                  hidTotal++;
                  hidEval++;
                  if(evalClose < signals[i].signalPrice)
                     hidCorrect++;
                 }
            totalEvaluated++;
           }
     }

   double overallAccuracy = (totalEvaluated > 0) ? (double)(regCorrect + hidCorrect) / totalEvaluated * 100.0 : 0.0;
   double regAccuracy = (regEval > 0) ? (double)regCorrect / regEval * 100.0 : 0.0;
   double hidAccuracy = (hidEval > 0) ? (double)hidCorrect / hidEval * 100.0 : 0.0;

   Print("----- Backtest Signal Evaluation -----");
   Print("Total Signals Generated: ", ArraySize(signals));
   Print("Signals Evaluated: ", totalEvaluated);
   Print("Overall Accuracy: ", DoubleToString(overallAccuracy, 2), "%");
   Print("Regular Signals: ", regTotal, " | Evaluated: ", regEval, " | Accuracy: ", DoubleToString(regAccuracy, 2), "%");
   Print("Hidden Signals:  ", hidTotal, " | Evaluated: ", hidEval, " | Accuracy: ", DoubleToString(hidAccuracy, 2), "%");
  }
//+------------------------------------------------------------------+


Code-Aufschlüsselung

1. Kopfzeileninformationen und Eingabeparameter

Ganz oben in unserem Skript befindet sich eine klar definierte Kopfzeile, die wichtige Informationen über den Code enthält.

Informationen zu Datei und Autor


Die Kopfzeile enthält den Dateinamen (RSI Divergence.mql5), den Copyright-Vermerk und einen Link zum Profil des Autors. Dies gewährleistet eine ordnungsgemäße Zuordnung und bietet den Nutzern einen Bezugspunkt, wenn sie nach Aktualisierungen oder zusätzlicher Dokumentation suchen müssen.

Versionierung und Kompilierungsrichtlinien

Die Direktive #property legt wichtige Eigenschaften wie die Versionsnummer und die Verwendung strenger Kompilierungsregeln (#property strict) fest. Dies trägt dazu bei, die Konsistenz zu wahren und mögliche Fehler während der Entwicklung und Bereitstellung zu reduzieren. Weiter geht es mit dem Abschnitt Eingabeparameter, der für die Anpassung von wesentlicher Bedeutung ist. Mit diesen Parametern können Sie oder jeder andere Nutzer das Verhalten der Logik zur Erkennung von Divergenzen feinabstimmen, ohne den Kerncode zu ändern. Nachfolgend sind einige Highlights aufgeführt:

RSI- und Swing-Erkennungsparameter

  • InpRSIPeriode: Legt den Zeitraum für den RSI-Indikator fest.
  • InpSwingLeft und InpSwingRight: Legen Sie fest, wie viele Balken auf jeder Seite bei der Erkennung von Schwungpunkten berücksichtigt werden. Durch die Anpassung dieser Werte wird die Schwungerkennung entweder lockerer oder strenger.

Einstellungen für Divergenz und Signalauswertung

  • InpLookback: Legt fest, wie viele Balken in der Vergangenheit das Skript nach Divergenzen durchsuchen soll.
  • InpEvalBars: Gibt die Anzahl der Balken an, die gewartet wird, bevor ausgewertet wird, ob ein Signal erfolgreich war.
  • InpMinBarsBetweenSignals: Hilft dabei, doppelte Signale zu vermeiden, indem ein Mindestabstand zwischen ähnlichen Signalen erzwungen wird.

Anpassungen anzeigen

  • InpArrowOffset: Legt den Abstand (in Punkten) fest, um den die Pfeile vom Schwungpunkt versetzt sind, um die visuelle Klarheit auf dem Chart zu verbessern.

Optionaler RSI-Schwellenwertfilter

  • InpUseRSIThreshold bietet zusammen mit InpRSIOversold und InpRSIOverbought eine zusätzliche Filterungsebene. Dadurch wird sichergestellt, dass bei einer Aufwärtsdivergenz der frühere RSI-Schwung im überverkauften Bereich liegt - sofern der Nutzer diesen Filter aktiviert hat.

//+------------------------------------------------------------------+
//|                                           RSI Divergence.mql5    |
//|                               Copyright 2025, Christian Benjamin |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "2025, MetaQuotes Software Corp."
#property link      "https://www.mql5.com/en/users/lynnchris"
#property version   "1.0"
#property strict

//---- Input parameters
input int    InpRSIPeriod             = 14;    // RSI period
input int    InpSwingLeft             = 1;     // Bars to the left for swing detection (relaxed)
input int    InpSwingRight            = 1;     // Bars to the right for swing detection (relaxed)
input int    InpLookback              = 100;   // Number of bars to scan for divergence
input int    InpEvalBars              = 5;     // Bars after which to evaluate a signal
input int    InpMinBarsBetweenSignals = 1;     // Minimum bars between same-type signals (allows frequent re-entry)
input double InpArrowOffset           = 3.0;   // Arrow offset (in points) for display
input double InpMinSwingDiffPct       = 0.05;  // Lower minimum % difference to qualify as a swing
input double InpMinRSIDiff            = 1.0;   // Lower minimum difference in RSI between swing points

// Optional RSI threshold filter for bullish divergence (disabled by default)
input bool   InpUseRSIThreshold       = false; // If true, require earlier RSI swing to be oversold for bullish divergence
input double InpRSIOversold           = 30;    // RSI oversold level
input double InpRSIOverbought         = 70;    // RSI overbought level (if needed for bearish)

2. Initialisierung des Indikators

In diesem Teil initialisieren wir unseren RSI-Indikator. Die Funktion OnInit() erstellt ein Handle für den RSI-Indikator unter Verwendung von Parametern wie Symbol, Zeitrahmen und der vom Nutzer angegebenen RSI-Periode. Dieser Schritt ist von entscheidender Bedeutung, da jede nachfolgende Operation davon abhängt, dass ein gültiges RSI-Handle zum Abrufen der Indikatordaten vorhanden ist.

  • Die Funktion iRSI wird mit den erforderlichen Parametern aufgerufen.
  • Es ist eine Fehlerbehandlung implementiert, um jeden Fehler bei der Erstellung des Handles abzufangen.
  • Die Initialisierung stellt sicher, dass unser Indikator für die Datenerfassung und -analyse bereit ist.
int OnInit()
{
   // Create the RSI indicator handle with the specified period
   rsiHandle = iRSI(_Symbol, _Period, InpRSIPeriod, PRICE_CLOSE);
   if(rsiHandle == INVALID_HANDLE)
   {
      Print("Error creating RSI handle");
      return(INIT_FAILED);
   }
   return(INIT_SUCCEEDED);
}

3. Datenerfassung bei jeder neuen Kerze

In der Funktion OnTick() wird vor der Verarbeitung geprüft, ob eine neue Kerze geschlossen wurde. Dadurch wird sichergestellt, dass unsere Analyse stets mit vollständigen Daten arbeitet. Wir kopieren dann Arrays von RSI-Werten, Tiefst- und Höchstständen, Schlusskursen und Zeitdaten über einen konfigurierbaren Rückblickzeitraum. Die Einstellung der Arrays als Serie stellt sicher, dass die Daten mit dem jüngsten Balken bei Index 0 geordnet sind.

  • Der Code wartet auf die nächste geschlossene Kerze, um die Verarbeitung unvollständiger Daten zu vermeiden.
  • RSI- und Kursdaten werden mit Funktionen wie CopyBuffer und CopyLow/High/Close/Time abgerufen.
  • Bei der Verwendung von ArraySetAsSeries bleibt die richtige Reihenfolge für die Zeitreihenanalyse erhalten.

void OnTick()
{
   // Process only once per new closed candle by comparing bar times
   datetime currentBarTime = iTime(_Symbol, _Period, 1);
   if(currentBarTime == lastBarTime)
      return;
   lastBarTime = currentBarTime;
   
   // Copy RSI data for a given lookback period
   ArrayResize(rsiBuffer, InpLookback);
   ArraySetAsSeries(rsiBuffer, true);
   if(CopyBuffer(rsiHandle, 0, 0, InpLookback, rsiBuffer) <= 0)
   {
      Print("Error copying RSI data");
      return;
   }
   
   // Copy price data (lows, highs, closes) and time data for analysis
   ArrayResize(lowBuffer, InpLookback);
   ArrayResize(highBuffer, InpLookback);
   ArrayResize(closeBuffer, InpLookback);
   ArraySetAsSeries(lowBuffer, true);
   ArraySetAsSeries(highBuffer, true);
   ArraySetAsSeries(closeBuffer, true);
   if(CopyLow(_Symbol, _Period, 0, InpLookback, lowBuffer) <= 0 ||
      CopyHigh(_Symbol, _Period, 0, InpLookback, highBuffer) <= 0 ||
      CopyClose(_Symbol, _Period, 0, InpLookback, closeBuffer) <= 0)
   {
      Print("Error copying price data");
      return;
   }
   
   ArrayResize(timeBuffer, InpLookback);
   ArraySetAsSeries(timeBuffer, true);
   if(CopyTime(_Symbol, _Period, 0, InpLookback, timeBuffer) <= 0)
   {
      Print("Error copying time data");
      return;
   }
   
   g_totalBars = InpLookback;
   
   // (Further processing follows here...)
}

4. Swing Detection (Erkennung von Swing Lows und Swing Highs)

Bevor wir Divergenzen erkennen können, müssen wir zunächst wichtige Umkehrpunkte ausfindig machen. Zwei Hilfsfunktionen, IsSignificantSwingLow und IsSignificantSwingHigh, werden verwendet, um lokale Minima und Maxima zu identifizieren. Dazu wird der Tiefst- oder Höchststand eines Balkens mit den benachbarten Balken innerhalb eines bestimmten Fensters verglichen und geprüft, ob die prozentuale Differenz einen festgelegten Schwellenwert erreicht.
  • Die Funktionen prüfen sowohl links als auch rechts des aktuellen Balkens.
  • Sie berechnen die prozentuale Differenz, um sicherzustellen, dass nur signifikante Schwankungen markiert werden.
  • Diese Filterung reduziert das Rauschen und gewährleistet, dass sich unsere Divergenzanalyse auf aussagekräftige Marktbewegungen konzentriert.
bool IsSignificantSwingLow(int index, int left, int right)
{
   double currentLow = lowBuffer[index];
   // Check left side for local minimum condition
   for(int i = index - left; i < index; i++)
   {
      if(i < 0) continue;
      double pctDiff = MathAbs((lowBuffer[i] - currentLow) / currentLow) * 100.0;
      if(lowBuffer[i] < currentLow && pctDiff > InpMinSwingDiffPct)
         return false;
   }
   // Check right side for local minimum condition
   for(int i = index + 1; i <= index + right; i++)
   {
      if(i >= g_totalBars) break;
      double pctDiff = MathAbs((lowBuffer[i] - currentLow) / currentLow) * 100.0;
      if(lowBuffer[i] < currentLow && pctDiff > InpMinSwingDiffPct)
         return false;
   }
   return true;
}

bool IsSignificantSwingHigh(int index, int left, int right)
{
   double currentHigh = highBuffer[index];
   // Check left side for local maximum condition
   for(int i = index - left; i < index; i++)
   {
      if(i < 0) continue;
      double pctDiff = MathAbs((currentHigh - highBuffer[i]) / currentHigh) * 100.0;
      if(highBuffer[i] > currentHigh && pctDiff > InpMinSwingDiffPct)
         return false;
   }
   // Check right side for local maximum condition
   for(int i = index + 1; i <= index + right; i++)
   {
      if(i >= g_totalBars) break;
      double pctDiff = MathAbs((currentHigh - highBuffer[i]) / currentHigh) * 100.0;
      if(highBuffer[i] > currentHigh && pctDiff > InpMinSwingDiffPct)
         return false;
   }
   return true;
}

5. Erkennung von Divergenzen: Auf- und Abwärts-Divergenzen

Sobald die Schwankungspunkte identifiziert sind, vergleicht der Algorithmus die jüngsten Schwankungen, um Divergenzen zu erkennen. Bei Abwärts-Divergenz betrachtet der Code zwei Swing-Hochs und prüft, ob der Kurs ein höheres Hoch erreicht, während der RSI ein niedrigeres Hoch anzeigt (oder umgekehrt bei verborgenen Abwärts-Divergenz). Bei einer Aufwärts-Divergenz werden in ähnlicher Weise zwei Tiefststände verglichen. Ein optionaler RSI-Schwellenwert kann Aufwärtssignale weiter validieren, indem er sicherstellt, dass der frühere RSI-Wert im überverkauften Bereich liegt.

  • Für die Divergenzanalyse werden zwei aktuelle Swing-Punkte (entweder Höchst- oder Tiefstwerte) verwendet.
  • Die Bedingungen für reguläre und versteckte Abweichungen sind klar voneinander getrennt.
  • Optionale Parameter (wie die RSI-Oversold-Bedingung) ermöglichen eine zusätzliche Filterung der Signalstärke.

// --- Bearish Divergence (using swing highs)
if(ArraySize(swingHighs) >= 2)
{
   ArraySort(swingHighs); // Ensure ascending order: index 0 is most recent
   int recent   = swingHighs[0];
   int previous = swingHighs[1];
   
   // Regular Bearish Divergence: Price makes a higher high while RSI makes a lower high
   if(highBuffer[recent] > highBuffer[previous] &&
      rsiBuffer[recent] < rsiBuffer[previous] &&
      (rsiBuffer[previous] - rsiBuffer[recent]) >= InpMinRSIDiff)
   {
      Print("Regular Bearish Divergence detected at bar ", recent);
      DisplaySignal("RegBearish Divergence", recent);
   }
   // Hidden Bearish Divergence: Price makes a lower high while RSI makes a higher high
   else if(highBuffer[recent] < highBuffer[previous] &&
           rsiBuffer[recent] > rsiBuffer[previous] &&
           (rsiBuffer[recent] - rsiBuffer[previous]) >= InpMinRSIDiff)
   {
      Print("Hidden Bearish Divergence detected at bar ", recent);
      DisplaySignal("HiddenBearish Divergence", recent);
   }
}

// --- Bullish Divergence (using swing lows)
if(ArraySize(swingLows) >= 2)
{
   ArraySort(swingLows); // Ensure ascending order: index 0 is most recent
   int recent   = swingLows[0];
   int previous = swingLows[1];
   
   // Regular Bullish Divergence: Price makes a lower low while RSI makes a higher low
   if(lowBuffer[recent] < lowBuffer[previous] &&
      rsiBuffer[recent] > rsiBuffer[previous] &&
      (rsiBuffer[recent] - rsiBuffer[previous]) >= InpMinRSIDiff)
   {
      // Optionally require the earlier RSI swing to be oversold
      if(!InpUseRSIThreshold || rsiBuffer[previous] <= InpRSIOversold)
      {
         Print("Regular Bullish Divergence detected at bar ", recent);
         DisplaySignal("RegBullish Divergence", recent);
      }
   }
   // Hidden Bullish Divergence: Price makes a higher low while RSI makes a lower low
   else if(lowBuffer[recent] > lowBuffer[previous] &&
           rsiBuffer[recent] < rsiBuffer[previous] &&
           (rsiBuffer[previous] - rsiBuffer[recent]) >= InpMinRSIDiff)
   {
      Print("Hidden Bullish Divergence detected at bar ", recent);
      DisplaySignal("HiddenBullish Divergence", recent);
   }
}

6. Signalanzeige und -aufzeichnung

Wenn eine Divergenz entdeckt wird, ist es wichtig, das Signal visuell zu markieren und seine Details für eine spätere Auswertung aufzuzeichnen. Die Funktion DisplaySignal() erstellt nicht nur einen Pfeil auf dem Chart (mit unterschiedlichen Pfeilcodes und Farben für Auf- und Abwärtssignale), sondern aktualisiert auch eine Beschriftung für das neueste Signal und speichert die Metadaten des Signals in einem globalen Array. Diese systematische Aufzeichnung ermöglichen spätere Backtests der Strategie.

  • Doppelte Signale werden verhindert, indem geprüft wird, ob bereits ein Signal für einen ähnlichen Balken existiert.
  • Visuelle Anhaltspunkte wie Pfeile und Beschriftungen verbessern die Lesbarkeit des Charts.
  • Jedes Signal wird mit Details wie Typ, Bar-Index, Zeit und Preis gespeichert, was eine spätere Leistungsbewertung erleichtert.

void DisplaySignal(string signalText, int barIndex)
{
   // Prevent duplicate signals on the same or nearby bars
   for(int i = 0; i < ArraySize(signals); i++)
   {
      if(StringFind(signals[i].type, signalText) != -1)
         if(MathAbs(signals[i].barIndex - barIndex) < InpMinBarsBetweenSignals)
            return;
   }
     
   // Update a label for the latest regular signal
   if(StringFind(signalText, "Reg") != -1)
   {
      string labelName = "LatestSignal";
      if(ObjectFind(0, labelName) == -1)
      {
         if(!ObjectCreate(0, labelName, OBJ_LABEL, 0, 0, 0))
         {
            Print("Failed to create LatestSignal label");
            return;
         }
         ObjectSetInteger(0, labelName, OBJPROP_CORNER, 0);
         ObjectSetInteger(0, labelName, OBJPROP_XDISTANCE, 10);
         ObjectSetInteger(0, labelName, OBJPROP_YDISTANCE, 20);
         ObjectSetInteger(0, labelName, OBJPROP_COLOR, clrWhite);
      }
      ObjectSetString(0, labelName, OBJPROP_TEXT, signalText);
   }
     
   // Create an arrow object to mark the signal on the chart
   string arrowName = "Arrow_" + signalText + "_" + IntegerToString(barIndex);
   if(ObjectFind(0, arrowName) < 0)
   {
      int arrowCode = 0;
      double arrowPrice = 0.0;
      color arrowColor = clrWhite;
      double point = SymbolInfoDouble(_Symbol, SYMBOL_POINT);
          
      if(StringFind(signalText, "Bullish") != -1)
      {
         arrowCode  = 233; // Wingdings up arrow
         arrowColor = clrLime;
         arrowPrice = lowBuffer[barIndex] - (InpArrowOffset * point);
      }
      else if(StringFind(signalText, "Bearish") != -1)
      {
         arrowCode  = 234; // Wingdings down arrow
         arrowColor = clrRed;
         arrowPrice = highBuffer[barIndex] + (InpArrowOffset * point);
      }
          
      if(!ObjectCreate(0, arrowName, OBJ_ARROW, 0, timeBuffer[barIndex], arrowPrice))
      {
         Print("Failed to create arrow object ", arrowName);
         return;
      }
      ObjectSetInteger(0, arrowName, OBJPROP_COLOR, arrowColor);
      ObjectSetInteger(0, arrowName, OBJPROP_ARROWCODE, arrowCode);
   }
     
   // Record the signal details for later backtesting evaluation
   SignalInfo sig;
   sig.type = signalText;
   sig.barIndex = barIndex;
   sig.signalTime = timeBuffer[barIndex];
   if(StringFind(signalText, "Bullish") != -1)
      sig.signalPrice = lowBuffer[barIndex];
   else
      sig.signalPrice = highBuffer[barIndex];
   ArrayResize(signals, ArraySize(signals) + 1);
   signals[ArraySize(signals) - 1] = sig;
     
   UpdateSignalCountLabel();
}

Backtest-Auswertung zur Deinitialisierung

Schließlich wird die Funktion EvaluateSignalsAndPrint() aufgerufen, wenn der Experte deinitialisiert wird. Diese Funktion wertet alle aufgezeichneten Signale rückwirkend aus, indem sie die Preisbewegung einige Balken nach dem Signal mit dem aufgezeichneten Preis des Signals vergleicht. Es berechnet die Genauigkeit sowohl für reguläre als auch für versteckte Signale und liefert so wertvolles Feedback über die Leistung unserer Divergenzstrategie.

  • Die Funktion ruft die vollständigen historischen Abschlussdaten ab.
  • Jedes Signal wird nach einer festen Anzahl von Balken ausgewertet (wie in InpEvalBars festgelegt).
  • Genauigkeitsmetriken werden sowohl für die Gesamtsignale als auch getrennt für reguläre und versteckte Signale berechnet, was die Leistungsvalidierung erleichtert.

void EvaluateSignalsAndPrint()
{
   double closeAll[];
   int totalBars = CopyClose(_Symbol, _Period, 0, WHOLE_ARRAY, closeAll);
   if(totalBars <= 0)
   {
      Print("Error copying complete close data for evaluation");
      return;
   }
   ArraySetAsSeries(closeAll, true);
   
   int totalEvaluated = 0, regTotal = 0, hidTotal = 0;
   int regEval = 0, hidEval = 0;
   int regCorrect = 0, hidCorrect = 0;
   
   for(int i = 0; i < ArraySize(signals); i++)
   {
      int evalIndex = signals[i].barIndex - InpEvalBars;
      if(evalIndex < 0)
         continue;
      double evalClose = closeAll[evalIndex];
      
      if(StringFind(signals[i].type, "Bullish") != -1)
      {
         if(StringFind(signals[i].type, "Reg") != -1)
         {
            regTotal++;
            regEval++;
            if(evalClose > signals[i].signalPrice)
               regCorrect++;
         }
         else if(StringFind(signals[i].type, "Hidden") != -1)
         {
            hidTotal++;
            hidEval++;
            if(evalClose > signals[i].signalPrice)
               hidCorrect++;
         }
         totalEvaluated++;
      }
      else if(StringFind(signals[i].type, "Bearish") != -1)
      {
         if(StringFind(signals[i].type, "Reg") != -1)
         {
            regTotal++;
            regEval++;
            if(evalClose < signals[i].signalPrice)
               regCorrect++;
         }
         else if(StringFind(signals[i].type, "Hidden") != -1)
         {
            hidTotal++;
            hidEval++;
            if(evalClose < signals[i].signalPrice)
               hidCorrect++;
         }
         totalEvaluated++;
      }
   }
     
   double overallAccuracy = (totalEvaluated > 0) ? (double)(regCorrect + hidCorrect) / totalEvaluated * 100.0 : 0.0;
   double regAccuracy = (regEval > 0) ? (double)regCorrect / regEval * 100.0 : 0.0;
   double hidAccuracy = (hidEval > 0) ? (double)hidCorrect / hidEval * 100.0 : 0.0;
     
   Print("----- Backtest Signal Evaluation -----");
   Print("Total Signals Generated: ", ArraySize(signals));
   Print("Signals Evaluated: ", totalEvaluated);
   Print("Overall Accuracy: ", DoubleToString(overallAccuracy, 2), "%");
   Print("Regular Signals: ", regTotal, " | Evaluated: ", regEval, " | Accuracy: ", DoubleToString(regAccuracy, 2), "%");
   Print("Hidden Signals:  ", hidTotal, " | Evaluated: ", hidEval, " | Accuracy: ", DoubleToString(hidAccuracy, 2), "%");
}


Tests und Ergebnisse

Nachdem Sie Ihren EA mit MetaEditor erfolgreich kompiliert haben, ziehen Sie ihn zum Testen auf den Chart. Stellen Sie sicher, dass Sie ein Demokonto verwenden, um kein echtes Geld zu riskieren. Sie können auch den RSI-Indikator zu Ihrem Chart hinzufügen, um beim Testen Ihres EA eine einfache Signalbestätigung zu erhalten. Gehen Sie dazu auf die Registerkarte „Indikatoren“, wählen Sie den RSI-Indikator im Ordner „Panels“ aus und stellen Sie die gewünschten Parameter ein, die mit denen Ihres EA übereinstimmen müssen. Im folgenden GIF sehen Sie, wie Sie das RSI-Indikator-Fenster zu einem MetaTrader 5-Chart hinzufügen können. Sie können auch das bestätigte Signal sehen, eine reguläre Aufwärts-Divergenz auf einem einminütigen Zeitrahmen.

Aufwärts-Konvergenz

Abb. 5. Einstellung Anzeige und Testergebnis 1

Nachfolgend sehen Sie einen weiteren Test, den wir für Boom 500 durchgeführt haben. Sowohl die Kursentwicklung als auch der RSI-Indikator bestätigen ein Verkaufssignal.

Tests

Abb. 6. Testergebnis 2

Ein weiterer Test wurde mittels Backtests auf dem unten stehenden GIF durchgeführt, das mehrere positive Verschiebungen zeigt. Wenn Sie genau hinsehen, werden Sie sowohl versteckte Fortsetzungssignale als auch reguläre Signale erkennen. Einige Signale müssen jedoch trotz ihrer positiven Auswirkungen herausgefiltert werden, da sie nicht bestätigt wurden.

Backtests

Abb. 7. Testergebnis 3


Schlussfolgerung

Dieses Instrument hat sich als extrem preisorientiert erwiesen, was das Hauptziel unserer Serie ist, so viele Preisaktionsanalyse-Tools wie möglich zu entwickeln. Ich habe es wirklich zu schätzen gewusst, wie effektiv der RSI-Indikator mit der Preisbewegung interagiert, indem er positive Signale aus Divergenzen extrahiert. Die von uns durchgeführten Tests haben vielversprechende Ergebnisse und einen positiven Trend gezeigt.

Ich bin jedoch der Meinung, dass es an der Zeit ist, eine weitere Verbesserung einzuführen, die externe Bibliotheken zur präzisen und genauen Identifizierung von Schwüngen nutzt und damit die Signalgenauigkeit verbessert. Ich empfehle Ihnen, das Instrument gründlich zu testen und seine Parameter an Ihren Handelsstil anzupassen. Denken Sie daran, dass jedes generierte Signal vor dem Einstieg gegengeprüft werden sollte, da das Instrument Ihnen helfen soll, den Markt zu beobachten und Ihre Gesamtstrategie zu bestätigen.

Datum Name des Werkzeugs  Beschreibung Version  Aktualisierungen  Hinweis
01/10/24 Chart Projector Skript zur Überlagerung der Kursentwicklung des Vortages mit Geistereffekt. 1.0 Erste Veröffentlichung Erstes Werkzeug in Lynnchris Tool Chest
18/11/24 Analytical Comment Er liefert Informationen zum Vortag in Tabellenform und nimmt die zukünftige Marktentwicklung vorweg. 1.0 Erste Veröffentlichung Zweites Werkzeug in Lynnchris Tool Chest
27/11/24 Analytics Master Reguläre Aktualisierung der Marktmetriken alle zwei Stunden.  1.01 Zweite Veröffentlichung Drittes Werkzeug in Lynnchris Tool Chest
02/12/24 Analytics Forecaster  Reguläre Aktualisierung der Marktmetriken alle zwei Stunden mit Telegram-Integration. 1.1 Dritte Auflage Werkzeug Nummer 4
09/12/24 Volatility Navigator Der EA analysiert die Marktbedingungen anhand der Indikatoren Bollinger Bands, RSI und ATR. 1.0 Erste Veröffentlichung Werkzeug Nummer 5
19/12/24 Mean Reversion Signal Reaper Analysiert den Markt anhand der Strategie „Umkehr zur Mitte“ und liefert Signale.  1.0  Erste Veröffentlichung  Werkzeug Nummer 6 
9/01/25  Signal-Impuls  Analysator für mehrere Zeitrahmen. 1.0  Erste Veröffentlichung  Werkzeug Nummer 7 
17/01/25  Metrics Board  Bedienfeld mit Taste für die Analyse.  1.0  Erste Veröffentlichung Werkzeug Nummer 8 
21/01/25 External Flow Analytik durch externe Bibliotheken. 1.0  Erste Veröffentlichung Werkzeug Nummer 9 
27/01/25 VWAP Volumengewichteter Durchschnittspreis   1.3  Erste Veröffentlichung  Werkzeug Nummer 10 
02/02/25  Heikin Ashi  Trendglättung und Identifizierung von Umkehrsignalen  1.0  Erste Veröffentlichung  Werkzeug Nummer 11
04/02/25  FibVWAP  Signalerzeugung durch Python-Analyse  1.0  Erste Veröffentlichung  Werkzeug Nummer 12
14/02/25  RSI DIVERGENCE  Kursentwicklung versus RSI-Divergenzen  1.0  Erste Veröffentlichung  Werkzeug Nummer 13 

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

Beigefügte Dateien |
RSI_DIVERGENCE.mq5 (33.61 KB)
Letzte Kommentare | Zur Diskussion im Händlerforum (1)
linfo2
linfo2 | 18 Feb. 2025 in 20:05
Danke, Chris, noch einmal für deine gut durchdachten und hilfreichen Artikel. Sehr geschätzt als Vorlage für mich zu ändern. Ich schätze auch Ihr gelehrtes Denken und praktische Implementierungen. Für mich hatte ich Probleme mit EvaluateSignalsAndPrint , es steht geschrieben die Funktion gibt Error Copying Compete close data for evaluation zurück . Der letzte Fehler ist '4003' . Ich bin nicht schlau genug, um herauszufinden, warum die Funktion bei Verwendung der WHOLE_ARRAY-Variable fehlschlägt. Wenn ich die Variable WHOLE_ARRAY in copyclose durch eine Zählung der geschlossenen Bars ersetze, erhalte ich ein 'Backtest Signal Evaluation' zurück. BTW ich bin in einer seltsamen Zeitzone GMT +13 und habe manchmal Probleme mit lokalen und Server Daten und Zeiten, aber vielleicht hilft dies jemandem
Robustheitstests für Expert Advisors Robustheitstests für Expert Advisors
Bei der Entwicklung von Strategien sind viele komplizierte Details zu berücksichtigen, von denen viele für Anfänger nicht besonders interessant sind. Infolgedessen mussten viele Händler, mich eingeschlossen, diese Lektionen auf die harte Tour lernen. Dieser Artikel basiert auf meinen Beobachtungen von häufigen Fallstricken, die den meisten Anfängern bei der Entwicklung von Strategien auf MQL5 begegnen. Es wird eine Reihe von Tipps, Tricks und Beispielen bieten, die dabei helfen, die Untauglichkeit eines EA zu erkennen und die Robustheit unserer eigenen EAs auf einfache Weise zu testen. Ziel ist es, die Leser aufzuklären und ihnen zu helfen, zukünftige Betrügereien beim Kauf von EAs zu vermeiden und Fehler bei der eigenen Strategieentwicklung zu verhindern.
MQL5-Assistenten-Techniken, die Sie kennen sollten (Teil 54): Verstärkungslernen mit hybriden SAC und Tensoren MQL5-Assistenten-Techniken, die Sie kennen sollten (Teil 54): Verstärkungslernen mit hybriden SAC und Tensoren
Soft Actor Critic ist ein Reinforcement Learning-Algorithmus, den wir bereits in einem früheren Artikel vorgestellt haben, in dem wir auch Python und ONNX als effiziente Ansätze für das Training von Netzwerken vorgestellt haben. Wir überarbeiten den Algorithmus mit dem Ziel, Tensoren, Berechnungsgraphen, die häufig in Python verwendet werden, zu nutzen.
Erstellen eines Handelsadministrator-Panels in MQL5 (Teil IX): Code Organisation (I) Erstellen eines Handelsadministrator-Panels in MQL5 (Teil IX): Code Organisation (I)
Diese Diskussion befasst sich mit den Herausforderungen, die bei der Arbeit mit großen Codebasen auftreten. Wir werden die besten Praktiken für die Codeorganisation in MQL5 untersuchen und einen praktischen Ansatz zur Verbesserung der Lesbarkeit und Skalierbarkeit des Quellcodes unseres Trading Administrator Panels implementieren. Darüber hinaus wollen wir wiederverwendbare Code-Komponenten entwickeln, von denen andere Entwickler bei der Entwicklung ihrer Algorithmen profitieren können. Lesen Sie weiter und beteiligen Sie sich an der Diskussion.
Trendvorhersage mit LSTM für Trendfolgestrategien Trendvorhersage mit LSTM für Trendfolgestrategien
Long Short-Term Memory (LSTM) ist eine Art rekurrentes neuronales Netz (RNN), das für die Modellierung sequenzieller Daten entwickelt wurde, indem es langfristige Abhängigkeiten effektiv erfasst und das Problem des verschwindenden Gradienten löst. In diesem Artikel werden wir untersuchen, wie LSTM zur Vorhersage zukünftiger Trends eingesetzt werden kann, um die Leistung von Trendfolgestrategien zu verbessern. Der Artikel behandelt die Einführung von Schlüsselkonzepten und die Motivation hinter der Entwicklung, das Abrufen von Daten aus dem MetaTrader 5, die Verwendung dieser Daten zum Trainieren des Modells in Python, die Integration des maschinellen Lernmodells in MQL5 und die Reflexion der Ergebnisse und zukünftigen Bestrebungen auf der Grundlage von statistischem Backtesting.