English Русский 中文 Español Português
preview
Risikomanagement (Teil 5): Integration des Risikomanagementsystems in einen Expert Advisor

Risikomanagement (Teil 5): Integration des Risikomanagementsystems in einen Expert Advisor

MetaTrader 5Beispiele |
45 0
Niquel Mendoza
Niquel Mendoza


Einführung

Willkommen zum letzten Teil unserer Serie über Risikomanagement. In diesem Artikel werden wir untersuchen, wie die Verwendung des Risikomanagementsystems die Ergebnisse verändert: ob es überhaupt einen Unterschied macht, die Vor- und Nachteile eines dynamischen Ansatzes für das Risikomanagement und wann er am sinnvollsten ist.

Wir werden diese Fragen beantworten, indem wir einen einfachen Expert Advisor erstellen, der den in früheren Artikeln zum Risikomanagement beschriebenen Order-Block-Indikator verwendet.

Beginnen wir mit der Optimierung dieses Indikators, die schnellere und effizientere Backtests ermöglicht und die Optimierung des Expert Advisors erleichtert.

Außerdem gehen wir Schritt für Schritt die Erstellung eines Expert Advisors, die Definition seiner Parameter und die Einbindung zentraler Ereignisse wie OnTradeTransaction durch.

Schließlich werden wir die in diesem Artikel aufgeworfenen Fragen beantworten, indem wir vier Tests des Expert Advisors mit verschiedenen Parameterkombinationen durchführen. Auf diese Weise werden wir den Handel mit und ohne Verlust- und Gewinnlimits vergleichen und beurteilen, wann ein dynamisches Risikomanagement am effektivsten ist.


Verbesserungen des Indikators der „Order Blocks“

Wir beginnen mit der Optimierung des Order-Block-Indikators. Seine Leistung ließ früher sehr zu wünschen übrig. Nach der Analyse stellte ich fest, dass sich die Leistung durch die Optimierung der Verarbeitung von Kerzendaten und das Entfernen unnötiger Schleifen verbessern lässt.

1. Optimierung der Schleife zur Erkennung von Order Blocks

In der ersten Schleife des Indikators reduzieren wir die Anzahl der Iterationen, da wir nicht bei jeder Aktualisierung über alle Kerzen iterieren müssen. Wenn der Indikator geladen wird, ist der Parameter prev_calculated von OnCalculate gleich 0. Dieser Parameter gibt im Wesentlichen an, wie viele Kerzen bereits berechnet wurden. Da beim ersten Durchlauf noch keine Kerzen berechnet wurden, ist der Wert prev_calculated gleich 0. Daher durchläuft die Schleife bei der ersten Berechnung die Kerzen von Rango_universal_busqueda bis zur Kerze 6; wenn prev_calculated größer als 0 ist, wird nur Kerze 6 geprüft.

int inicio = prev_calculated == 0 ? Rango_universal_busqueda : 6;

    for(int i = inicio  ; i  > 5  ; i--)
     {
      //----

     }

2. Suche nach Mitigierung in den Order Blocks

Um festzustellen, ob ein Order Block abgeschwächt bzw. mitigiert wurde (d. h. ob der Preis das Niveau des Order Blocks erreicht oder überschritten hat), haben wir zwei Funktionen verbessert: eine für bullische und eine für bärische Blöcke. Beide Funktionen wurden vereinfacht, indem nur die Höchst- und Tiefstwerte der Kerzen verwendet wurden.

Mitigierung eines bullischen Order Blocks

datetime  mitigados_alcsitas(double price, const double &lowArray[], const  datetime &Time[], datetime start, datetime end)
 {
  int startIndex = iBarShift(_Symbol, PERIOD_CURRENT, start);
  int endIndex = iBarShift(_Symbol, PERIOD_CURRENT, end);

  NormalizeDouble(price, _Digits);
  for(int i = startIndex - 2 ; i >= endIndex + 1 ; i--)
   {
    if(price > lowArray[i])
     {
      return Time[i]; //si encuentra que si hubo retorna el tiempo de la vela donde hubo la mitigacion
      Print("el orderblock tuvo mitigaciones", TimeToString(end));
     }
   }

  return 0; //En caso no se haya encontrado  niguna mitigacion retorna 0
 }

Mitigierung eines bärischen Order Blocks

//+------------------------------------------------------------------+
datetime  mitigado_bajista(double price, const  double &highArray[], const datetime &Time[], datetime start, datetime end)
 {
  int startIndex = iBarShift(_Symbol, PERIOD_CURRENT, start);
  int endIndex = iBarShift(_Symbol, PERIOD_CURRENT, end);
  NormalizeDouble(price, _Digits);
  for(int i = startIndex - 2 ; i >= endIndex + 1 ; i--)
   {
    if(highArray[i] > price)
     {
      return Time[i]; //retorna el time de la vela encontrada
     }
   }
  return 0; // no se mitigo hasta el momento
 }

In beiden Funktionen verwenden wir direkt die Hochs und Tiefs der Kerzen, um die Anzahl der Parameter zu reduzieren und die Schleifenausführungszeit leicht zu verringern.

Außerdem haben wir diese Änderungen an den Funktionen esOb_mitigado_array_... vorgenommen, die früher iOpen, iClose und ähnliche Funktionen aufriefen. Dies war nicht erforderlich, da z. B. eine Kerze, die unter dem Niveau des Order Blocks schließt, auch ihr Tief unter diesem Niveau liegt. Daher können wir die Prüfung einer Mitigierung vereinfachen, indem wir uns je nach Blocktyp nur auf den niedrigen oder den hohen Wert verlassen.

Bullischer Order Block

//+------------------------------------------------------------------+
datetime esOb_mitigado_array_alcista(OrderBlocks &newblock, datetime end)
 {
  int endIndex = iBarShift(_Symbol, PERIOD_CURRENT, end);
  NormalizeDouble(newblock.price2, _Digits);
  for(int i = 0 ; i <  endIndex - 2  ; i++)
   {
    double low = iLow(_Symbol, PERIOD_CURRENT, i);
    if(newblock.price2 >= low)
     {
      newblock.mitigated = true;
      newblock.time2 = iTime(_Symbol, _Period, i);
      return newblock.time2; //retorna el time de la vela encontrada
     }
   }
  return 0; // no se mitigo hasta el momento
 }

Bärischer Order Block

//+------------------------------------------------------------------+
datetime esOb_mitigado_array_bajista(OrderBlocks &newblock, datetime end)
 {
  int endIndex = iBarShift(_Symbol, PERIOD_CURRENT, end);
  NormalizeDouble(newblock.price2, _Digits);
  for(int i = 0 ; i <  endIndex - 2  ; i++)
   {
    double high = iHigh(_Symbol, PERIOD_CURRENT, i);
    if(high >= newblock.price2)
     {
      newblock.mitigated = true;
      newblock.time2 = iTime(_Symbol, _Period, i);
      return newblock.time2;
     }
   }
  return 0; // no se mitigo hasta el momento
 }

3. Vorlagenfunktionen zum Hinzufügen von Elementen zu einem Array

Wir haben mehrere Vorlagenfunktionen hinzugefügt, die das Einfügen von Elementen in ein Array effizienter machen. Zuvor haben wir spezielle Funktionen erstellt, die nur einen bestimmten Typ akzeptieren. Um zum Beispiel eine Order_Blocks-Struktur hinzuzufügen, mussten wir eine eigene Funktion schreiben.

Template-Funktionen ermöglichen es, denselben Vorgang mit verschiedenen Typen auszuführen. In unserem Fall haben wir eine Funktion zum Hinzufügen eines Elements zu einem Array erstellt.

template <typename S>
bool AddArray(S &Array[], const S &Value)
 {
  for(int i = 0 ; i < ArraySize(Array) ; i++)
   {
    if(Array[i].name == Value.name)
      return false;
   }
  ArrayResize(Array, Array.Size() + 1);
  Array[Array.Size() - 1] = Value;
  return true;
 }

template <typename X>
void AddArrayNoVerification(X &array[], const X &value)
 {
  ArrayResize(array, array.Size() + 1);
  array[array.Size() - 1] = value;
 }

4. Schablonenfunktion zum Entfernen eines Elements nach Namen

Lassen Sie uns nun eine Vorlagenfunktion zum Entfernen eines Elements nach Namen erstellen. Das bedeutet, dass das Array, mit dem wir arbeiten, eine Struktur oder Klasse mit einem öffentlichen Mitglied „name“ sein muss. Diese Funktion entfernt ein Element auf der Grundlage des Parameters targetName, was nützlich ist, um nicht benötigte Elemente aus dem Array der mitigierten Order Blocks zu entfernen.

template<typename T>
bool DeleteArrayBiName(T &array[], const string targetName)
 {
  int size = ArraySize(array);
  int index = -1;

// Buscar el índice y desplazar elementos en un solo bucle
  for(int i = 0; i < size; i++)
   {
    if(array[i].name == targetName)
     {
      index = i;
     }
    if(index != -1 && i < size - 1)
     {
      array[i] = array[i + 1]; // Desplaza los elementos
     }
   }

  if(index == -1)
    return false;

  if(size > 1)
    ArrayResize(array, size - 1);
  else
    ArrayFree(array); // Si el array tenía solo un elemento, se libera completamente

  return true;
 }

5. Neue Funktionalität zum „Entfernen“ von Order Blocks

In früheren Versionen wurde bei der Mitigierung eines Order Blocks dessen Name in einem Hilfsarray gespeichert, während der Block selbst im Hauptarray verblieb. Durch diese Verbesserung wird nur der Name des mitigierten Order Blocks im Hilfsarray gespeichert, während der Block selbst vollständig aus dem Hauptarray entfernt wird.  

Bärische Order Blocks

    static bool buscar_obb = true;
    static datetime time_b = 0;
    string curr_elimiandor_obb[];

    for(int i = 0; i < ArraySize(ob_bajistas); i++)
     {
      datetime mitigadoTime = esOb_mitigado_array_bajista(ob_bajistas[i], ob_bajistas[i].time1);

      if(ob_bajistas[i].mitigated == false)
       {
        if(ObjectFind(ChartID(), ob_bajistas[i].name) < 0)
         {
          RectangleCreate(ChartID(), ob_bajistas[i].name, 0, ob_bajistas[i].time1, ob_bajistas[i].price1,
                          time[0], ob_bajistas[i].price2, Color_Order_Block_Bajista, Witdth_order_block, Fill_order_block, Back_order_block, STYLE_SOLID);
          sellOrderBlockBuffer[iBarShift(_Symbol, _Period, ob_bajistas[i].time1)] = ob_bajistas[i].price2;
         }
        else
          ObjectMove(ChartID(), ob_bajistas[i].name, 1, time[0], ob_bajistas[i].price2);
       }
      else
       {
        Alert("El order block bajista esta siendo mitigado: ", TimeToString(ob_bajistas[i].time1));
        AddArrayNoVerification(pricetwo_eliminados_obb, ob_bajistas[i].name);
        AddArrayNoVerification(curr_elimiandor_obb, ob_bajistas[i].name); 

        if(buscar_obb == true)
         {
          time_b = iTime(_Symbol, _Period, 0);
          buscar_obb = false;
         }
       }
     }

    for(int i = 0; i < ArraySize(curr_elimiandor_obb) ; i++)
     {
      DeleteArrayBiName(ob_bajistas, curr_elimiandor_obb[i]);
     }

Bullische Order Blocks

    static bool buscar_oba = true;
    static datetime time_a = 0;
    string curr_elimiandor_oba[];

    for(int i = 0; i < ArraySize(ob_alcistas); i++)
     {
      datetime mitigadoTime = esOb_mitigado_array_alcista(ob_alcistas[i], ob_alcistas[i].time1);

      if(ob_alcistas[i].mitigated == false)
       {
        if(ObjectFind(ChartID(), ob_alcistas[i].name) < 0)
         {
          RectangleCreate(ChartID(), ob_alcistas[i].name, 0, ob_alcistas[i].time1, ob_alcistas[i].price1,
                          time[0], ob_alcistas[i].price2, Color_Order_Block_Alcista, Witdth_order_block, Fill_order_block, Back_order_block, STYLE_SOLID);
          buyOrderBlockBuffer[iBarShift(_Symbol, _Period, ob_alcistas[i].time1)] = ob_alcistas[i].price2;
         }
        else
          ObjectMove(ChartID(), ob_alcistas[i].name, 1, time[0], ob_alcistas[i].price2); //por el contrario si si existe el objeto lo unico que haremos es actulizarlo al tiempo actual usando el punto de anclaje 1
       }
      else
       {
        Alert("El order block alcista esta siendo mitigado: ", TimeToString(ob_alcistas[i].time1));
        AddArrayNoVerification(pricetwo_eliminados_oba, ob_alcistas[i].name);
        AddArrayNoVerification(curr_elimiandor_oba, ob_alcistas[i].name);

        if(buscar_oba == true)
         {
          buscar_oba = false;
          time_a = iTime(_Symbol, _Period, 1);
         }
       }
     }

    for(int i = 0; i < ArraySize(curr_elimiandor_oba) ; i++)
     {
      DeleteArrayBiName(ob_alcistas, curr_elimiandor_oba[i]);
     }

6. Funktion zum Löschen von Objekten

Beim Löschen von Objekten aus dem Chart sind einige Änderungen erforderlich. Früher haben wir die Namen der mitigierten Order Blocks in einem Array gespeichert, um eine erneute Verarbeitung zu vermeiden. Wir haben nun eine neue Funktion implementiert, die diese Order Blocks aus dem Hauptfeld entfernt. Aus diesem Grund müssen die Namen der mitigierten Order Blocks in Hilfsarrays gespeichert werden, damit sie später aus dem Chart entfernt werden können.

Nachfolgend finden Sie die Funktion Delete_Objects, die so geändert wurde, dass sie die Hilfsarrays durchläuft und deren Inhalt ebenfalls löscht:

void Eliminar_Objetos()
 {
  ObjectsDeleteAll(0, "ENTRY", -1, -1);
  for(int i = 0; i < ArraySize(pricetwo_eliminados_oba) ; i++) ObjectDelete(0,pricetwo_eliminados_oba[i]);
  for(int i = 0; i < ArraySize(pricetwo_eliminados_obb) ; i++) ObjectDelete(0,pricetwo_eliminados_obb[i]); 
  for(int i = 0; i < ArraySize(ob_alcistas) ; i++) ObjectDelete(0,ob_alcistas[i].name);
  for(int i = 0; i < ArraySize(ob_bajistas) ; i++) ObjectDelete(0,ob_bajistas[i].name);
  
  ArrayFree(pricetwo_eliminados_oba);
  ArrayFree(pricetwo_eliminados_obb);
 }

7. Vordefinierte Arrays verwenden

Die Logik wurde durch die Verwendung vordefinierter Arrays optimiert, die MQL in OnCalculate bereitstellt. Nun werden wir diese Arrays verwenden, um Order Blocks zu berechnen. Die wichtigsten Kerzendaten sind „open“, „close“, „high“, „low“ und „tick_volume“.

Dieser Ansatz erleichtert die Verwaltung und Interpretation der Daten bei der Arbeit mit Order Blocks.

    ArraySetAsSeries(open, true);
    ArraySetAsSeries(close, true);
    ArraySetAsSeries(high, true);
    ArraySetAsSeries(low, true);
    ArraySetAsSeries(time, true);
    ArraySetAsSeries(tick_volume, true);
    ArraySetAsSeries(atr, true);


Erstellen eines Expert Advisors und Definieren seiner Parameter

Beginnen wir mit der Erstellung eines Expert Advisors:

1. Erstellen eines Expert Advisors anhand einer Vorlage:

Teil 1

2. Angabe des Namens und des Autors:

Teil 2

3. Auswahl nur des Ereignisses OnTradeTransaction:

Teil 3

4. Fertigstellen:

Teil 4

Als Nächstes erstellen wir die Parameter, die erforderlich sind, damit der Expert Advisor korrekt funktioniert. Beginnen wir mit den allgemeinen Parametern des Expert Advisors, wie z. B. der magischen Zahl und den Standardeinstellungen, die bereits im Order-Block-Indikator verwendet werden.

1. Allgemeine Parameter

Bevor wir die allgemeinen Parameter festlegen, definieren wir ein Enum mit den vom Indikator verwendeten Take-Profit- und Stop-Loss-Typen, die mit denen des Indikators übereinstimmen.

enum ENUM_TP_SL_STYLE
 {
  ATR = 0,
  POINT = 1
 };

Nachfolgend finden Sie eine kurze Erläuterung zu jedem hinzugefügten Parameter:

  • Magic: Magic-Nummer, die die Trades des Expert Advisors identifiziert und es Ihnen ermöglicht, sie von anderen zu unterscheiden.

  • timeframe_order_block: Legt den Zeitrahmen für die Erkennung von Order Blocks fest.

  • Rango_universal_busqueda: Anzahl der Kerzen, auf die der Expert Advisor bei der Suche nach potenziellen Orderblöcken zurückblickt.

  • Witdth_order_block: Legt die Liniendicke (Rand) von Rechtecken fest, die Order Blocks darstellen.

  • Back_order_block und Fill_order_block: Grafische Parameter zum Zeichnen des Hintergrunds und der Füllung des Rechtecks, das jeden Order Block darstellt.

  • Color_Order_Block_Bassist und Color_Order_Block_Bullish: Farben für bärische bzw. bullische Orderblöcke.

  • tp_sl_style: Methode zur Berechnung von Take-Profit und Stop-Loss (ATR oder Punkte).

  • Atr_Multiplier_1 und Atr_Multiplier_2: Multiplikatoren, die bei der ATR-Methode verwendet werden.

  • TP_POINT und SL_POINT: Take-Profit- und Stop-Loss-Werte bei Verwendung der POINT-Methode.

sinput group "--- Order Block EA settings ---"
input ulong Magic = 545244; //Magic number
input ENUM_TIMEFRAMES timeframe_order_block = PERIOD_M5; //Order block timeframe

sinput group "-- Order Block --"
input int  Rango_universal_busqueda = 500; //search range of order blocks
input int  Witdth_order_block = 1; //Width order block

input bool Back_order_block = true; //Back order block?
input bool Fill_order_block = true; //Fill order block?

input color Color_Order_Block_Bajista = clrRed; //Bearish order block color
input color Color_Order_Block_Alcista = clrGreen; //Bullish order block color

sinput group "-- Strategy --"
input ENUM_TP_SL_STYLE tp_sl_style = POINT;//tp and sl style

sinput group "- ATR "
input double Atr_Multiplier_1 = 1.5;//Atr multiplier 1
input double Atr_Multiplier_2 = 2.0;//Atr multiplier 2

sinput group "- POINT "
input int TP_POINT = 1000; //TL in Points
input int SL_POINT = 1000; //SL in Points

2. Parameter für das Risikomanagement

Fahren wir mit der Liste der Expert Advisor-Parameter fort und definieren wir die Parameter, die für das Risikomanagement verwendet werden. Allgemeine Parameter:

  • Lote_Type: Legt fest, ob ein dynamisches Los (basierend auf dem Risikomanagement pro Trade) oder ein festes Los verwendet werden soll.

  • lot: Losgröße, die verwendet wird, wenn Lote_Type auf „Fixed“ gesetzt ist.

  • risk_mode: Hier können Sie wählen, ob es sich um ein persönliches Konto oder ein PropFirm-Konto handelt (z. B. propfirm_ftmo).

  • get_mode: Legt fest, wie die Losgröße berechnet wird. Dabei wird der Stop-Loss-Wert verwendet, um die Positionsgröße anhand des Risikos pro Trade anzupassen.

  • prop_firm_balance: Wenn ein FTMO-Konto (oder eine andere Prop-Firma mit ähnlichen Regeln) verwendet wird, wird der anfängliche Kontostand bestimmt. Wie in früheren Artikeln erläutert, wird dieser Parameter zur Berechnung des maximalen Verlusts pro Handel und des maximalen Tagesverlusts verwendet.

input ENUM_LOTE_TYPE Lote_Type = Dinamico; //Lote Type
input double lote = 0.1; //lot size (only for fixed lot)
input ENUM_MODE_RISK_MANAGEMENT risk_mode = personal_account;//type of risk management mode
input ENUM_GET_LOT get_mode = GET_LOT_BY_STOPLOSS_AND_RISK_PER_OPERATION; //How to get the lot
input double prop_firm_balance = 1000; //If risk mode is Prop Firm FTMO, then put your ftmo account balance

3. Parameter für den maximalen Verlust (täglich, wöchentlich und insgesamt)

Definieren wir nun Parameter für die Kontrolle des maximalen Verlusts (ML), des maximalen wöchentlichen Verlusts (MWL) und des maximalen täglichen Verlusts (MDL). Jeder dieser Grenzwerte basiert auf drei Variablen, die bestimmen, wie die Verlustgrenze berechnet und angewendet wird:

  • percentage_or_money_..._input: Stellt einen Prozentsatz oder einen Geldbetrag dar, je nach dem gewählten Berechnungsmodus. Wenn der Wert auf 0 gesetzt wird, wird die Verlustgrenze nicht verwendet.

  • mode_calculation_...: Gibt an, ob der Parameter als Prozentsatz oder als Geldbetrag ausgewertet wird.

  • applied_percentages_...: Bestimmt die Basis, auf die der Prozentsatz angewandt wird (z. B. Saldo, Kontoprofit usw.).

sinput group "- ML/Maxium loss/Maxima perdida -"
input double percentage_or_money_ml_input = 0; //percentage or money (0 => not used ML)
input ENUM_RISK_CALCULATION_MODE mode_calculation_ml = percentage; //Mode calculation Max Loss
input ENUM_APPLIED_PERCENTAGES applied_percentages_ml = Balance; //ML percentage applies to:

sinput group "- MWL/Maximum weekly loss/Perdida maxima semanal -"
input double percentage_or_money_mwl_input  = 0; //percentage or money (0 => not used MWL)
input ENUM_RISK_CALCULATION_MODE mode_calculation_mwl = percentage; //Mode calculation Max weekly Loss
input ENUM_APPLIED_PERCENTAGES applied_percentages_mwl = Balance;//MWL percentage applies to:

sinput group "- MDL/Maximum  daily loss/Perdida maxima diaria -"
input double percentage_or_money_mdl_input  = 0; //percentage or money (0 => not used MDL)
input ENUM_RISK_CALCULATION_MODE mode_calculation_mdl = percentage; //Mode calculation Max daily loss
input ENUM_APPLIED_PERCENTAGES applied_percentages_mdl = Balance;//MDL percentage applies to:

4. GMLPO: maximales Risiko pro Trade

Fahren wir mit der Einrichtung der Expert Advisor-Parameter fort und konfigurieren wir den maximalen Verlust pro Handel. Dieser Abschnitt ist komplexer, da er mehr als drei Parameter umfasst; wie bereits erwähnt, wird hier auch das dynamische Risiko eingeführt.

4.1 Allgemeine GMLPO-Parameter

Definieren wir zunächst die fünf wichtigsten Parameter für das dynamische Risiko. Dazu gehören drei Parameter, die bereits für den maximalen Tages-, Wochen- und Gesamtverlust verwendet werden, sowie zwei weitere Parameter. Letztere ermöglichen es Ihnen zu konfigurieren, wie oft das System prüft, ob das Risiko pro Trade angepasst werden muss, und welche Art der dynamischen Risikokonfiguration verwendet werden soll. Zusätzlich verwendete Enums:

//--- Enumeration of the types of dynamic operational risk
enum ENUM_OF_DYNAMIC_MODES_OF_GMLPO
 {
  DYNAMIC_GMLPO_FULL_CUSTOM, //Customisable dynamic risk per operation
  DYNAMIC_GMLPO_FIXED_PARAMETERS,//Risk per operation with fixed parameters
  NO_DYNAMIC_GMLPO //No dynamic risk for risk per operation
 };
//--- Enumeration to determine when to review a decrease in the initial balance to modify the risk per operation
enum ENUM_REVISION_TYPE
 {
  REVISION_ON_CLOSE_POSITION, //Check GMLPO only when closing positions
  REVISION_ON_TICK //Check GMLPO on all ticks
 };
sinput group "- GMLPO/Gross maximum loss per operation/Porcentaje a arriesgar por operacion -"
input ENUM_OF_DYNAMIC_MODES_OF_GMLPO mode_gmlpo = DYNAMIC_GMLPO_FULL_CUSTOM; //Select GMLPO mode:
input ENUM_REVISION_TYPE revision_type_gmlpo = REVISION_ON_CLOSE_POSITION; //Type revision
input double percentage_or_money_gmlpo_input  = 1.0; //percentage or money (0 => not used GMLPO)
input ENUM_RISK_CALCULATION_MODE mode_calculation_gmlpo = percentage; //Mode calculation Max Loss per operation
input ENUM_APPLIED_PERCENTAGES applied_percentages_gmlpo = Balance;//GMPLO percentage applies to:
  • mode_gmlpo: Bestimmt, ob das Risiko pro Trade dynamisch ist. Wählen Sie einen der dynamischen Risikokonfigurationsmodi. Wenn das dynamische Risiko nicht verwendet wird, wählen Sie NO_DYNAMIC_GMLPO.
  • revision_type_gmlpo: Gibt an, wann das Eigenkapital des Kontos überprüft wird, um das Risiko pro Handel anzupassen: wenn eine Position geschlossen wird oder bei jedem Markttick.
  • percentage_or_money_gmlpo_input: Prozentsatz oder Geldbetrag, der als Grundlage für die Bestimmung des Risikos pro Trade verwendet wird. Ist der Wert auf 0 gesetzt, ist die GMLPO-Funktion nicht aktiviert.
  • mode_calculation_gmlpo: Legt fest, ob percentage_or_money_gmlpo_input als Prozentsatz oder als fester Geldbetrag interpretiert wird.
  • applied_percentages_gmlpo: Legt die Basis (z. B. Saldo oder Kapital) fest, die zur Berechnung des Prozentsatzes verwendet wird, wenn dieser Berechnungsmodus ausgewählt ist.

Anmerkung: Jede hier verwendete Enumeration, die bereits in früheren Artikeln beschrieben wurde, liefert weitere Einzelheiten darüber, wie das Risiko intern berechnet wird. 

4.2 Dynamische GMLPO-Einstellungen

Wie bereits erwähnt, gibt es zwei Modi zur Konfiguration des dynamischen Risikos: einen vollständig anpassbaren Modus und einen Modus mit festen Parametern. Im vorigen Artikel habe ich erklärt, warum wir diese Entscheidung getroffen haben. Kurz gesagt, das Problem bei der Verwendung von Zeichenketten als Parameter besteht darin, dass sie nicht optimiert werden können, was es schwieriger macht, die besten dynamischen Risikoparameter zu finden. Deshalb haben wir eine Enumeration eingeführt, um dieses Problem zu lösen.

4.2.1 Konfigurierbare dynamische GMLPO

In diesem Modus gibt der Nutzer an, bei welchem prozentualen Rückgang des Saldos sich das Risiko ändern muss und wie hoch der neue Risikowert pro Trade sein wird. Dieser Modus bietet ein Maximum an Flexibilität, obwohl er auf Zeichenketten basiert, was eine Optimierung verhindert.

sinput group "-- Optional GMLPO settings, Dynamic GMLPO"
sinput group "--- Full customizable dynamic GMLPO"
input string note1 = "subtracted from your total balance to establish a threshold.";  //This parameter determines a specific percentage that will be
input string str_percentages_to_be_reviewed = "2,5,7,9"; //percentages separated by commas.
input string note2 = "a new risk level will be triggered on your future trades: "; //When the current balance (equity) falls below this threshold
input string str_percentages_to_apply = "1,0.7,0.5,0.33"; //percentages separated by commas.
input string note3 = "0 in both parameters => do not use dynamic risk in gmlpo"; //Note:

  • str_percentages_to_be_reviewed: Enthält eine Liste von Prozentsätzen, die in aufsteigender Reihenfolge sortiert sind und den Schwellenwert festlegen, bei dem sich das Risiko pro Trade ändert. Standardmäßig sind die Werte „2,5,7,9“ eingestellt. Das bedeutet, dass das Risiko auf den entsprechenden Wert angepasst und der erste Wert in str_percentages_to_apply (1) angewandt wird, wenn der in Prozent ausgedrückte Gewinn des Kontos unter 2 % fällt.

  • str_percentages_to_apply Enthält die neuen Risikoprozentsätze, an die das Risiko pro Trade angepasst wird.

4.2.2 Dynamische GMLPO mit festen Parametern

Wenn Sie es vorziehen, das dynamische Risiko durch feste Parameter zu konfigurieren, gibt es einen separaten Abschnitt in den Einstellungen des Expert Advisors. Er verhält sich ähnlich wie der konfigurierbare Modus, aber in diesem Fall können Sie bis zu vier Parameter einstellen. Wenn Sie nicht alle vier, sondern nur drei Felder benötigen, geben Sie einfach zwei Nullen in die Felder ein, die nicht verwendet werden. Wenn zum Beispiel im Abschnitt „-- 4 --“ nur drei Modifikatoren verwendet werden, müssen die beiden verbleibenden Parameter auf 0 gesetzt werden.

sinput group "--- Fixed dynamic GMLPO with parameters"
sinput group "- 1 -"
input string note1_1 = "subtracted from your total balance to establish a threshold.";  //This parameter determines a specific percentage that will be
input double inp_balance_percentage_to_activate_the_risk_1 = 2.0; //percentage 1 that will be exceeded to modify the risk separated by commas
input string note2_1 = "a new risk level will be triggered on your future trades: "; //When the current balance (equity) falls below this threshold
input double inp_percentage_to_be_modified_1 = 1.0;//new percentage 1 to which the gmlpo is modified
sinput group "- 2 -"
input double inp_balance_percentage_to_activate_the_risk_2 = 5.0;//percentage 2 that will be exceeded to modify the risk separated by commas
input double inp_percentage_to_be_modified_2 = 0.7;//new percentage 2 to which the gmlpo is modified
sinput group "- 3 -"
input double inp_balance_percentage_to_activate_the_risk_3 = 7.0;//percentage 3 that will be exceeded to modify the risk separated by commas
input double inp_percentage_to_be_modified_3 = 0.5;//new percentage 3 to which the gmlpo is modified
sinput group "- 4 -"
input double inp_balance_percentage_to_activate_the_risk_4 = 9.0;//percentage 4 that will be exceeded to modify the risk separated by commas
input double inp_percentage_to_be_modified_4 = 0.33;//new percentage 4  1 to which the gmlpo is modified

5. Maximaler Tagesgewinn (MDP)

Um die Liste der Risikokontrollparameter zu vervollständigen, wird der Parameter Maximum Daily Profit (MDP) hinzugefügt. Sie enthält drei gemeinsame Parameter für alle Verlust-/Gewinnobergrenzen sowie einen neuen Parameter mdp_is_strict. Dieser Parameter gibt an, ob der Kontrollmodus „maximaler Gewinn pro Handel“ aktiviert ist.

Wenn mdp_is_strict falsch ist, bedeutet dies, dass es unabhängig von etwaigen Verlusten während des Tages ausreicht, den MDP-Wert zu überschreiten. Wenn das Gewinnziel beispielsweise 10 USD beträgt und im Laufe des Tages 4 USD verloren gehen, dann aber 5 USD verdient werden, wird dies als Überschreitung des maximalen Tagesgewinns betrachtet. Was passiert jedoch, wenn der Wert von mdp_is_strict true ist? Das bedeutet, dass Sie nicht nur das Gewinnziel erreichen, sondern auch alle täglichen Verluste aufholen müssen, bevor der Gewinn als Überschreitung des MDP gilt. Wenn das Ziel beispielsweise 10 USD beträgt und 5 USD verloren gegangen sind, müssen Sie zuerst diese 5 USD zurückgewinnen und dann weitere 10 USD verdienen.

sinput group "- MDP/Maximum daily profit/Maxima ganancia diaria -"
input bool mdp_is_strict = true; //MDP is strict?
input double percentage_or_money_mdp_input = 0; //percentage or money (0 => not used MDP)
input ENUM_RISK_CALCULATION_MODE mode_calculation_mdp = percentage; //Mode calculation Max Daily Profit
input ENUM_APPLIED_PERCENTAGES applied_percentages_mdp = Balance;//MDP percentage applies to:

6. Handelszeitfenster

Um zu vermeiden, dass in Zeiten geringer Volatilität oder zu unpassenden Zeiten gehandelt wird, wurde eine Option zur Definition eines Handelszeitfensters hinzugefügt.

Wenn die aktuelle Zeit innerhalb des angegebenen Bereichs liegt, darf der Expert Advisor handeln; andernfalls findet kein Handel statt.

sinput group "--- Session ---"
input         char hora_inicio = 16;//start hour to operate (0-23)
input         char min_inicio = 30;//start minute to operate (0-59)
input         char hora_fin = 18;//end hour to operate (1-23)
input         char min_fin =0;//end minute to operate (0-59)

Vollständige Liste der Parameter:

sinput group "--- Order Block EA settings ---"
input ulong Magic = 545244; //Magic number
input ENUM_TIMEFRAMES timeframe_order_block = PERIOD_M5; //Order block timeframe

sinput group "-- Order Block --"
input int  Rango_universal_busqueda = 500; //search range of order blocks
input int  Witdth_order_block = 1; //Width order block

input bool Back_order_block = true; //Back order block?
input bool Fill_order_block = true; //Fill order block?

input color Color_Order_Block_Bajista = clrRed; //Bearish order block color
input color Color_Order_Block_Alcista = clrGreen; //Bullish order block color

sinput group "-- Strategy --"
input ENUM_TP_SL_STYLE tp_sl_style = POINT;//tp and sl style

sinput group "- ATR "
input double Atr_Multiplier_1 = 1.5;//Atr multiplier 1
input double Atr_Multiplier_2 = 2.0;//Atr multiplier 2

sinput group "- POINT "
input int TP_POINT = 1000; //TL in Points
input int SL_POINT = 1000; //SL in Points

sinput group "--- Risk Management ---"
input ENUM_LOTE_TYPE Lote_Type = Dinamico; //Lote Type
input double lote = 0.1; //lot size (only for fixed lot)
input ENUM_MODE_RISK_MANAGEMENT risk_mode = personal_account;//type of risk management mode
input ENUM_GET_LOT get_mode = GET_LOT_BY_STOPLOSS_AND_RISK_PER_OPERATION; //How to get the lot
input double prop_firm_balance = 1000; //If risk mode is Prop Firm FTMO, then put your ftmo account balance

sinput group "- ML/Maxium loss/Maxima perdida -"
input double percentage_or_money_ml_input = 0; //percentage or money (0 => not used ML)
input ENUM_RISK_CALCULATION_MODE mode_calculation_ml = percentage; //Mode calculation Max Loss
input ENUM_APPLIED_PERCENTAGES applied_percentages_ml = Balance; //ML percentage applies to:

sinput group "- MWL/Maximum weekly loss/Perdida maxima semanal -"
input double percentage_or_money_mwl_input  = 0; //percentage or money (0 => not used MWL)
input ENUM_RISK_CALCULATION_MODE mode_calculation_mwl = percentage; //Mode calculation Max weekly Loss
input ENUM_APPLIED_PERCENTAGES applied_percentages_mwl = Balance;//MWL percentage applies to:

sinput group "- MDL/Maximum  daily loss/Perdida maxima diaria -"
input double percentage_or_money_mdl_input  = 0; //percentage or money (0 => not used MDL)
input ENUM_RISK_CALCULATION_MODE mode_calculation_mdl = percentage; //Mode calculation Max daily loss
input ENUM_APPLIED_PERCENTAGES applied_percentages_mdl = Balance;//MDL percentage applies to:

sinput group "- GMLPO/Gross maximum loss per operation/Porcentaje a arriesgar por operacion -"
input ENUM_OF_DYNAMIC_MODES_OF_GMLPO mode_gmlpo = DYNAMIC_GMLPO_FULL_CUSTOM; //Select GMLPO mode:
input ENUM_REVISION_TYPE revision_type_gmlpo = REVISION_ON_CLOSE_POSITION; //Type revision
input double percentage_or_money_gmlpo_input  = 1.0; //percentage or money (0 => not used GMLPO)
input ENUM_RISK_CALCULATION_MODE mode_calculation_gmlpo = percentage; //Mode calculation Max Loss per operation
input ENUM_APPLIED_PERCENTAGES applied_percentages_gmlpo = Balance;//GMPLO percentage applies to:

sinput group "-- Optional GMLPO settings, Dynamic GMLPO"
sinput group "--- Full customizable dynamic GMLPO"
input string note1 = "subtracted from your total balance to establish a threshold.";  //This parameter determines a specific percentage that will be
input string str_percentages_to_be_reviewed = "2,5,7,9"; //percentages separated by commas.
input string note2 = "a new risk level will be triggered on your future trades: "; //When the current balance (equity) falls below this threshold
input string str_percentages_to_apply = "1,0.7,0.5,0.33"; //percentages separated by commas.
input string note3 = "0 in both parameters => do not use dynamic risk in gmlpo"; //Note:

sinput group "--- Fixed dynamic GMLPO with parameters"
sinput group "- 1 -"
input string note1_1 = "subtracted from your total balance to establish a threshold.";  //This parameter determines a specific percentage that will be
input double inp_balance_percentage_to_activate_the_risk_1 = 2.0; //percentage 1 that will be exceeded to modify the risk separated by commas
input string note2_1 = "a new risk level will be triggered on your future trades: "; //When the current balance (equity) falls below this threshold
input double inp_percentage_to_be_modified_1 = 1.0;//new percentage 1 to which the gmlpo is modified
sinput group "- 2 -"
input double inp_balance_percentage_to_activate_the_risk_2 = 5.0;//percentage 2 that will be exceeded to modify the risk separated by commas
input double inp_percentage_to_be_modified_2 = 0.7;//new percentage 2 to which the gmlpo is modified
sinput group "- 3 -"
input double inp_balance_percentage_to_activate_the_risk_3 = 7.0;//percentage 3 that will be exceeded to modify the risk separated by commas
input double inp_percentage_to_be_modified_3 = 0.5;//new percentage 3 to which the gmlpo is modified
sinput group "- 4 -"
input double inp_balance_percentage_to_activate_the_risk_4 = 9.0;//percentage 4 that will be exceeded to modify the risk separated by commas
input double inp_percentage_to_be_modified_4 = 0.33;//new percentage 4  1 to which the gmlpo is modified

sinput group "- MDP/Maximum daily profit/Maxima ganancia diaria -"
input bool mdp_is_strict = true; //MDP is strict?
input double percentage_or_money_mdp_input = 0; //percentage or money (0 => not used MDP)
input ENUM_RISK_CALCULATION_MODE mode_calculation_mdp = percentage; //Mode calculation Max Daily Profit
input ENUM_APPLIED_PERCENTAGES applied_percentages_mdp = Balance;//MDP percentage applies to:

sinput group "--- Session ---"
input         char hora_inicio = 16;//start hour to operate (0-23)
input         char min_inicio = 30;//start minute to operate (0-59)
input         char hora_fin = 18;//end hour to operate (1-23)
input         char min_fin =0;//end minute to operate (0-59)


Deklaration von globalen Variablen

In diesem Abschnitt werden verschiedene globale Variablen erstellt, um Risiken und die Handelsaktivitäten des Expert Advisors zu verwalten.

1. CTrade- und CRiskManagement-Objekte

Für Handelsoperationen binden wir unsere Bibliothek Risk_Management.mqh ein, die im Laufe dieser Artikelserie entwickelt wurde. Darüber hinaus wird ein Objekt vom Typ CTrade deklariert.

#include  <Risk_Management.mqh>
CTrade trade;

Dann wird eine Instanz der Klasse CRiskManagement erstellt und die vom Konstruktor benötigten Parameter werden gesetzt:

CRiskManagemet risk(mdp_is_strict, get_mode, Magic, risk_mode, prop_firm_balance);

2. Variablen für die Speicherung von Indikator-Deskriptoren

Um den Entscheidungsprozess des Expert Advisors zu visualisieren, werden wir zwei Variablen einführen, um Indikator-Deskriptoren zu speichern: eine für den Order Blocks-Indikator und die andere für den EMA.

//--- Handles
int order_block_indicator_handle;
int hanlde_ma;

3. Arrays zur Speicherung von TP- und SL-Werten

Um die aus den Indikatorpuffern der Order Blocks kopierten Werte zu speichern, werden vier Arrays erstellt:

double tp1[];
double tp2[];
double sl1[];
double sl2[];

4. Variablen für vorherige Abschlüsse (täglicher, wöchentlicher und Zeitrahmen der Order Blocks)

Der Expert Advisor muss den Zeitstempel speichern, d. h. die Schlusszeit der letzten Tages-, Wochen- oder spezifischen Kerze des Zeitrahmens, in dem ein Order Block erkannt wurde:

//---
datetime TiempoBarraApertua;
datetime TiempoBarraApertua_1;
datetime prev_vela;

5. Boolesche Variable zur Aktivierung/Deaktivierung der Handelsaktivität

Um zu steuern, ob der Expert Advisor nach dem Überschreiten bestimmter Gewinn- oder Verlustgrenzen arbeiten kann, wird eine boolesche Variable erstellt, die angibt, ob der Handel erlaubt ist (true) oder nicht (false):

//---
bool opera = true;

6. Variablen für die Speicherung des Beginns und des Endes des Handelszeitfensters

Schließlich werden Variablen definiert, die den Beginn und das Ende der Sitzung markieren, in der nach Kauf- oder Verkaufssignalen gesucht wird:

datetime start_sesion;
datetime end_sesion;


Die Funktion OnInit

In diesem Abschnitt werden wir alles einrichten, was für die korrekte Funktion des Expert Advisors erforderlich ist. Wir deklarieren und parametrisieren den Order-Blocks-Indikator, initialisieren seinen Deskriptor und den EMA und konfigurieren schließlich das Risikomanagementsystem.

1. Erstellen eines MqlParam-Arrays für den Order-Block-Indikator

Der erste Schritt besteht darin, eine Reihe von Parametern vorzubereiten, von denen jeder einer Indikatoreinstellung entspricht. Mithilfe der Struktur MqlParam können diese Parameter geordnet an die Funktion IndicatorCreate() übergeben werden:

//---
  MqlParam param[17];

  param[0].type = TYPE_STRING;
  param[0].string_value = "::Indicators\\Order_Block_Indicador_New_Part_2";

  param[1].type = TYPE_STRING;
  param[1].string_value = "--- Order Block Indicator settings ---";

  param[2].type = TYPE_STRING;
  param[2].string_value = "-- Order Block --";

  param[3].type = TYPE_INT;
  param[3].integer_value = Rango_universal_busqueda;

  param[4].type = TYPE_INT;
  param[4].integer_value = Witdth_order_block;

  param[5].type = TYPE_BOOL;
  param[5].integer_value = Back_order_block;

  param[6].type = TYPE_BOOL;
  param[6].integer_value = Fill_order_block;

  param[7].type = TYPE_COLOR;
  param[7].integer_value = Color_Order_Block_Bajista;

  param[8].type = TYPE_COLOR;
  param[8].integer_value = Color_Order_Block_Alcista;

  param[9].type = TYPE_STRING;
  param[9].string_value = "-- Strategy --";

  param[10].type = TYPE_INT;
  param[10].integer_value = tp_sl_style;

  param[11].type = TYPE_STRING;
  param[11].string_value = "- ATR";

  param[12].type = TYPE_DOUBLE;
  param[12].double_value = Atr_Multiplier_1;

  param[13].type = TYPE_DOUBLE;
  param[13].double_value = Atr_Multiplier_2;

  param[14].type = TYPE_STRING;
  param[14].string_value = "- POINT";

  param[15].type = TYPE_INT;
  param[15].integer_value = TP_POINT;

  param[16].type = TYPE_INT;
  param[16].integer_value = SL_POINT;

Die Indizes des param[]-Arrays entsprechen den einzelnen Parametern, die für die Funktion des Indikators „Order Blocks“ erforderlich sind.

2. Erstellung und Validierung von Indikator-Deskriptoren

Nachdem das Array gefüllt ist, wird die Funktion IndicatorCreate() verwendet, um den Deskriptor des Order-Blocks-Indikators zu erhalten. Darüber hinaus wird ein EMA-Deskriptor erstellt, der als zusätzlicher Leitfaden für die Ein- und Ausstiegsstrategie dient.

//---
  order_block_indicator_handle = IndicatorCreate(_Symbol, timeframe_order_block, IND_CUSTOM, ArraySize(param), param);
  hanlde_ma = iMA(_Symbol, timeframe_order_block, 30, 0, MODE_EMA, PRICE_CLOSE);
  trade.SetExpertMagicNumber(Magic);

  if(order_block_indicator_handle == INVALID_HANDLE)
   {
    Print("The order blocks indicator is not available last error: ", _LastError);
    return INIT_FAILED;
   }

  if(hanlde_ma == INVALID_HANDLE)
   {
    Print("The ema indicator is not available latest error: ", _LastError);
    return INIT_FAILED;
   }

3. Hinzufügen von Indikatoren zum Chart

Um das Debugging und die visuelle Überwachung zu vereinfachen, können Sie die Indikatoren direkt in das Chart einfügen. Dieser Schritt ist optional, aber wenn Sie die Objekte im Chart sehen, können Sie leichter überprüfen, ob die Parameter und die Visualisierung korrekt sind.

  ChartIndicatorAdd(0, 0, order_block_indicator_handle);
  ChartIndicatorAdd(0, 0, hanlde_ma);

4. Einrichtung des Risikomanagements

Konfigurieren Sie das CRiskManagement-Objekt, das für die Anwendung von Verlust- und Gewinngrenzen und die Berechnung der idealen Losgröße zuständig ist.

//---
  risk.SetPorcentages(percentage_or_money_mdl_input, percentage_or_money_mwl_input, percentage_or_money_gmlpo_input
                      , percentage_or_money_ml_input, percentage_or_money_mdp_input);
  risk.SetEnums(mode_calculation_mdl, mode_calculation_mwl, mode_calculation_gmlpo, mode_calculation_ml, mode_calculation_mdp);
  risk.SetApplieds(applied_percentages_mdl, applied_percentages_mwl, applied_percentages_gmlpo, applied_percentages_ml, applied_percentages_mdp);

Dynamische GMLPO-Konfiguration

Je nach gewähltem Konfigurationsmodus (fest oder vollständig anpassbar) werden die Risikowerte pro Trade festgelegt.

  if(mode_gmlpo == DYNAMIC_GMLPO_FIXED_PARAMETERS)
   {
    string percentages_to_activate, risks_to_be_applied;
    SetDynamicGMLPOUsingFixedParameters(inp_balance_percentage_to_activate_the_risk_1, inp_balance_percentage_to_activate_the_risk_2, inp_balance_percentage_to_activate_the_risk_3
                                        , inp_balance_percentage_to_activate_the_risk_4, inp_percentage_to_be_modified_1, inp_percentage_to_be_modified_2, inp_percentage_to_be_modified_3, inp_percentage_to_be_modified_4
                                        , percentages_to_activate, risks_to_be_applied);
    risk.SetDynamicGMLPO(percentages_to_activate, risks_to_be_applied, revision_type_gmlpo);
   }
  else
    if(mode_gmlpo == DYNAMIC_GMLPO_FULL_CUSTOM)
      risk.SetDynamicGMLPO(str_percentages_to_be_reviewed, str_percentages_to_apply, revision_type_gmlpo);

Die Funktion SetDynamicGMLPOUsingFixedParameters() wandelt feste Parameter (inp_balance_percentage_to_activate_the_risk_X und inp_percentage_to_be_modified_X) in Zeichenketten um. Diese Funktion ist einfach: Sie erstellt im Wesentlichen Zeichenketten durch Verkettung von in Zeichenketten umgewandelten Variablenwerten.

void SetDynamicGMLPOUsingFixedParameters(
  double _balance_percentage_to_activate_the_risk_1, double _balance_percentage_to_activate_the_risk_2, double _balance_percentage_to_activate_the_risk_3, double _balance_percentage_to_activate_the_risk_4,
  double _percentage_to_be_modified_1, double _percentage_to_be_modified_2, double _percentage_to_be_modified_3, double _percentage_to_be_modified_4,
  string &percentages_to_activate, string &risks_to_be_applied)
 {
  percentages_to_activate = DoubleToString(_balance_percentage_to_activate_the_risk_1) + "," + DoubleToString(_balance_percentage_to_activate_the_risk_2) + "," + DoubleToString(_balance_percentage_to_activate_the_risk_3)
                            + "," + DoubleToString(_balance_percentage_to_activate_the_risk_4);
  risks_to_be_applied = DoubleToString(_percentage_to_be_modified_1) + "," + DoubleToString(_percentage_to_be_modified_2) + "," + DoubleToString(_percentage_to_be_modified_3)
                        + "," + DoubleToString(_percentage_to_be_modified_4);
 }

 5. Einrichten des Arrays TP/SL für den Order-Block-Indikator

Schließlich sind die Arrays so konfiguriert, dass sie die Indikatordaten der Order Blocks sequentiell speichern:

//---
  ArraySetAsSeries(tp1, true);
  ArraySetAsSeries(tp2, true);
  ArraySetAsSeries(sl1, true);
  ArraySetAsSeries(sl2, true);


OnTick und OnTradeTransaction Funktionen

Die Funktionen OnTick und OnTradeTransaction sind für jedes Handelssystem von grundlegender Bedeutung. In unserem Fall wird OnTick verwendet, um zu prüfen, ob Verlustlimits überschritten werden, um neue Tages- und Wochenkerzen zu erkennen (usw.) und um Signale auf der Grundlage der Daten des Order-Block-Indikators auszuführen.

1. Überprüfung auf einen neuen Tag und eine neue Woche, Einrichtung der Sitzung

In OnTick wird zunächst festgestellt, ob ein neuer Tag oder eine neue Woche begonnen hat. Dies geschieht durch den Vergleich des Datums des letzten Balkens für den täglichen (PERIOD_D1) und wöchentlichen (PERIOD_W1) Zeitrahmen.

Während der täglichen Kerzenprüfung wird die Variable „opera“ auf true zurückgesetzt (was anzeigt, ob der Handel erlaubt ist). Darüber hinaus wird die Funktion OnNewDay des Objekts „Risiko“ aufgerufen und die Zeit des Handelszeitfensters für den aktuellen Tag berechnet.

//---
  if(TiempoBarraApertua != iTime(_Symbol, PERIOD_D1, 0))
   {
    opera = true;
    risk.OnNewDay();
    start_sesion = HoraYMinutoADatetime(hora_inicio,min_inicio);
    end_sesion = HoraYMinutoADatetime(hora_fin,min_fin);

    if(TiempoBarraApertua_1 != iTime(_Symbol, PERIOD_W1, 0))
     {
      risk.OnNewWeek();
      TiempoBarraApertua_1 = iTime(_Symbol, PERIOD_W1, 0);
     }

    TiempoBarraApertua = iTime(_Symbol, PERIOD_D1, 0);
   }

Anmerkung: HoraYMinutoADatetime(int hora, int minuto) wandelt die in den Eingabeparametern angegebenen Stunden und Minuten in eine Datetime-Variable um.

datetime HoraYMinutoADatetime(int hora, int minuto) {
  MqlDateTime tm;
  TimeCurrent(tm);
// Asigna la hora y el minuto deseado
  tm.hour = hora;
  tm.min = minuto;
  tm.sec = 0; // Puedes ajustar los segundos si es necesario
  return StructToTime(tm);;
}

2. Überprüfungen und Logik bei jeder neuen Kerze des Zeitrahmens der Order Blocks

Im nächsten Schritt wird geprüft, ob sich ein neuer Balken auf dem für den Order-Block-Indikator festgelegten Zeitrahmen gebildet hat. Nachdem eine neue Kerze erkannt wurde, werden die Indikatorpuffer kopiert, um TP- und SL-Werte zu erhalten. Anschließend wird SL für die Berechnung der Losgröße festgelegt, und ein Kauf- oder Verkaufsauftrag wird auf der Grundlage des Signals ausgeführt.

 if(prev_vela != iTime(_Symbol, timeframe_order_block, 0))
   {
    CopyBuffer(order_block_indicator_handle, 2, 0, 5, tp1);
    CopyBuffer(order_block_indicator_handle, 3, 0, 5, tp2);
    CopyBuffer(order_block_indicator_handle, 4, 0, 5, sl1);
    CopyBuffer(order_block_indicator_handle, 5, 0, 5, sl2);

    if(tp1[0] > 0 && tp2[0]  > 0 && sl1[0] > 0 &&  sl2[0] > 0)
     {
      if(tp2[0] > sl2[0] && risk.GetPositionsTotal() == 0)  //compras
       {
        double ASK = NormalizeDouble(SymbolInfoDouble(_Symbol, SYMBOL_ASK), _Digits);
        risk.SetStopLoss(ASK - sl1[0]);
        double lot = (Lote_Type == Dinamico ? risk.GetLote(ORDER_TYPE_BUY) : lote);

        if(lot > 0.0)
          trade.Buy(lot, _Symbol, ASK, sl1[0], tp1[0], "Order Block EA Buy");
       }
      else
        if(sl2[0] > tp2[0] && risk.GetPositionsTotal() == 0)  //venta
         {
          double BID = NormalizeDouble(SymbolInfoDouble(_Symbol, SYMBOL_BID), _Digits);
          risk.SetStopLoss(sl1[0] - BID);
          double lot = (Lote_Type == Dinamico ? risk.GetLote(ORDER_TYPE_SELL) : lote);

          if(lot > 0.0)
            trade.Sell(lot, _Symbol, BID, sl1[0], tp1[0], "Order Block EA Sell");
         }
     }

    prev_vela = iTime(_Symbol, timeframe_order_block, 0);
   }

Hinweis: risk.GetPositionsTotal() begrenzt die Anzahl der Trades, die gleichzeitig geöffnet sein können. In diesem Beispiel wird geprüft, ob es keine offenen Positionen gibt, bevor ein neuer Auftrag erteilt wird.

Es werden die Werte sl1 und tp1 verwendet, aber mit tp2 kann auch ein anderes Verhältnis eingestellt werden, z. B. 1:2, je nach den Parametereinstellungen.

3. Abschließende Überprüfung des Risikomanagements

Am Ende jedes OnTick prüft der Code, ob Verlust- oder Gewinngrenzen überschritten wurden. Werden die Grenzen überschritten, werden alle vom Expert Advisor eröffneten Positionen geschlossen und der Wert der Variable „opera“ wird auf false gesetzt.

 risk.OnTickEvent();

  if(risk.ML_IsSuperated(CLOSE_POSITION_AND_EQUITY)  == true)
   {
    if(risk_mode == propfirm_ftmo)
     {
      Print("The expert advisor lost the funding test");
      ExpertRemove();
     }
    else
     {
      risk.CloseAllPositions();
      Print("Maximum loss exceeded now");
      opera = false;
     }
   }

  if(risk.MDL_IsSuperated(CLOSE_POSITION_AND_EQUITY) == true)
   {
    risk.CloseAllPositions();
    Print("Maximum daily loss exceeded now");
    opera = false;
   }

  if(risk.MDP_IsSuperated(CLOSE_POSITION_AND_EQUITY) == true)
   {
    risk.CloseAllPositions();
    Print("Excellent Maximum daily profit achieved");
    opera = false;
   }

Anmerkung: Zur Überwachung anderer Verlustarten, wie z. B. des maximalen wöchentlichen Verlusts, können zusätzliche Prüfungen hinzugefügt werden.

 if(risk.MWL_IsSuperated(CLOSE_POSITION_AND_EQUITY) == true)
   {
    risk.CloseAllPositions();
    Print("The maximum weekly loss has been exceeded");
    opera = false;
    extra = false;
   }

4. Das Ereignis OnTradeTransaction

Für den Empfang von Positionseröffnungs-, -schließungs- oder -änderungsereignissen wird schließlich die Funktion OnTradeTransaction implementiert. Diese Funktion ruft die entsprechende Methode der Risikomanagementklasse auf, um diese Ereignisse zu behandeln.

//+------------------------------------------------------------------+
//| TradeTransaction function                                        |
//+------------------------------------------------------------------+
void OnTradeTransaction(const MqlTradeTransaction& trans,
                        const MqlTradeRequest& request,
                        const MqlTradeResult& result)
 {
  risk.OnTradeTransactionEvent(trans);
 }


OnDeinit-Funktion

Die Funktion OnDeinit wird aufgerufen, kurz bevor der Expert Advisor aus dem Chart entfernt oder von diesem getrennt wird. In diesem Stadium ist es ideal, alle Ressourcen freizugeben und alle während der Laufzeit hinzugefügten Indikatoren oder Objekte zu entfernen, um das Chart sauber zu halten und Speicherlecks zu vermeiden.

Im Folgenden finden Sie ein Beispiel für die Durchführung dieser Bereinigung.

//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
 {
//---
  ChartIndicatorDelete(0, 0, ChartIndicatorName(0, 0, GetMovingAverageIndex()));
  ChartIndicatorDelete(0, 0, "Order Block Indicator");

  if(hanlde_ma != INVALID_HANDLE)
    IndicatorRelease(hanlde_ma);

  if(order_block_indicator_handle != INVALID_HANDLE)
    IndicatorRelease(order_block_indicator_handle);

 }

Auffinden und Entfernen des Exponential Moving Average (EMA) aus dem Chart

Um einen gleitenden Durchschnittsindikator zu entfernen, müssen Sie zunächst seinen Namen finden, d. h. den Namen, der auf der Registerkarte Indikatoren des Charts angezeigt wird. Manchmal ist die einzige Möglichkeit, dies zu tun, die Iteration über Indikatornamen und die Suche nach dem vollständigen Namen oder einem Teil davon.

//+------------------------------------------------------------------+
//| Extra Functions                                                  |
//+------------------------------------------------------------------+
int GetMovingAverageIndex(long chart_id = 0)
 {
  int total_indicators = ChartIndicatorsTotal(chart_id, 0);
  for(int i = 0; i < total_indicators; i++)
   {
    string indicator_name = ChartIndicatorName(chart_id, 0, i);
    if(StringFind(indicator_name, "MA") >= 0)  return i;
   }
  return -1;
 }
//+------------------------------------------------------------------+

ChartIndicatorsTotal(chart_id, 0): Gibt die Gesamtzahl der Indikatoren zurück, die dem Hauptfenster des Charts zugeordnet sind.

ChartIndicatorName(chart_id, 0, i): Gibt den Namen jedes Indikators im Hauptfenster des Charts zurück.

StringFind(indicator_name, "MA"): Prüft, ob der Name „MA“ enthält (dies kann „EMA“, „MA“, usw. sein). Wenn eine Übereinstimmung gefunden wird, gibt die Funktion den Index zurück.

Sobald Sie den Index in der Indikatorliste haben, können Sie den Namen verwenden, um den Indikator mit ChartIndicatorDelete zu entfernen.

Freigabe der Indikator-Deskriptoren

Der Aufruf von IndicatorRelease() stellt sicher, dass der Indikator vollständig aus dem Speicher freigegeben wird, insbesondere bei nutzerdefinierten Indikatoren oder wenn auf deren Daten über Deskriptoren zugegriffen wird. Andernfalls können nach dem Schließen des Expert Advisors Datenreste im Speicher verbleiben.
  • handle_ma: Deskriptor des Exponential Moving Average (EMA).
  • order_block_indicator_handle: Deskriptor des Indikators „Order Blocks“.


Tests

Schließlich werden wir diesen Expert Advisor testen und die Vor- und Nachteile der Verwendung des Risikomanagementsystems bewerten.

Unser erster Backtest wird Daten aus dem vergangenen Jahr verwenden.

Der Testzeitraum reicht vom 01.01.2024 bis zum 28.03.2025.

  • Symbol: Gold
  • Zeitrahmen: M5
  • Modell: Ausführung bei jedem Tick auf der Grundlage von echten Ticks
  • Hebel: 1:100

    Einstellungen:

    Chart:

                                                                                                           Backtest 1

    Anmerkung: Beachten Sie, dass dieser Backtest nicht optimiert wurde.

    Jetzt werden wir den Expert Advisor optimieren. Hier sind die Ergebnisse:

    • Neuer Zeitrahmen: M3
    • Verwendete Parameter: maximaler Tagesgewinn und maximaler Tagesverlust.

                                                                                                          Backtest 2       

    Wir haben die Handelszeit, den maximalen Verlust, den maximalen Gewinn, das Risiko pro Handel, den ATR-Multiplikator und den Zeitrahmen, idealerweise 3 Minuten, optimiert.

    Anmerkung: In beiden Backtests wurde die ATR zur Berechnung von Stop-Loss und Take-Profit verwendet.

    Um zu zeigen, dass sich das Risikomanagement auf die Backtest-Ergebnisse auswirken kann, werden wir die Parameter maximaler Tagesverlust und maximaler Gewinn entfernen, während wir die übrigen Parameter beibehalten.

    Hier ist das Chart:

                                                                                                      Backtest 3                                                   

    Wie Sie sehen können, zeigt das Backtest-3-Chart ein schnelleres anfängliches Wachstum, das jedoch später durch verschiedene Faktoren beeinflusst wird und zu sinken beginnt. Im Gegensatz dazu zeigt die Grafik von Backtest 2 ein gleichmäßiges, stetiges Wachstum ohne ausgeprägte Spitzen. Dies deutet darauf hin, dass die Festlegung eines Gewinnlimits exzessives Handeln verhindern und Sie vor Pechsträhnen und unerwarteten Situationen schützen kann. Gleichzeitig trägt die Verlustobergrenze dazu bei, mögliche Verluste zu begrenzen, die z. B. während einer Pechsträhne auftreten können. Die maximale Tagesverlustgrenze kann dazu beitragen, den Expert Advisor wieder in Richtung Breakeven zu bringen.

    Wir führen den letzten Test mit denselben Parametern wie in Backtest 3 durch, jedoch mit einem Unterschied: Das dynamische Risiko wird aktiviert, sobald das Konto negative Ergebnisse aufweist.

                                                                                                    Backtest 4  

    Sie sehen, dass das Risiko pro Handel stark abnimmt, wenn der prozentuale Gewinn auf dem Konto negativ ist, was die potenziellen Verluste begrenzt. Es gibt zwei wesentliche Unterschiede zwischen den Backtests 3 und 4:

    • Gewinnserie

    Während des Backtests 3 wurden nicht nur die Verluste während einer Gewinnsträhne aufgeholt, sondern das Konto stieg auch um 10 %. Bei einer Finanzierungsprüfung würde dieses Szenario zu Verlusten führen, da der Saldo auf etwa 8.600 $ fallen würde. Im Gegensatz dazu blieb das Konto in Backtest 4 trotz einer Gewinnserie in der Nähe des Ausgangsniveaus (10.000 $), ohne ein bedeutendes Wachstum zu erzielen.

    • Erholung nach Verlusten

    In Backtest 4 erfordert die Erholung von Verlusten in der Regel mehr Zeit und mehr Trades. Der Vorteil des dynamischen Risikos besteht darin, dass das Konto auch bei einer Finanzierungsprüfung besser geschützt ist und der Mindestsaldo bei etwa 9.086 $ gehalten wird.

    Diese Ergebnisse deuten darauf hin, dass das dynamische Risiko für Prop-Firmen vorzuziehen ist, da es die potenziellen Verluste, die der Expert Advisor generieren kann, deutlich begrenzt. Bei einem regulären Konto kann das dynamische Risiko jedoch die Erholungszeit verlängern, was für eine Prop-Firma, deren Ziel die Kontoführung und nicht schnelle Gewinne sind, kein Problem darstellt.


    Schlussfolgerung

    In diesem Artikel haben wir ein Risikomanagementsystem in einem Expert Advisor implementiert, das auf dem Order-Block-Indikator basiert. Wie Sie sehen, führt die Verwendung von Gewinn- und Verlustlimits – und die Verwendung des dynamischen Risikos – zu deutlich unterschiedlichen Ergebnissen. Im letzten Teil des Artikels haben wir die Funktionsweise des Systems erläutert und die Wirksamkeit der Schutzmechanismen in Aktion gezeigt.

    In diesem Artikel verwendete oder aktualisierte Dateien:

    Dateiname Typ Beschreibung 
    Risk_Management.mqh  .mqh (Include-Datei) Die Hauptdatei enthält allgemeine Funktionen und die Implementierung der Klasse CRiskManagement, die für das Risikomanagement im System zuständig ist. Diese Datei definiert und erweitert alle Funktionen im Zusammenhang mit der Gewinn- und Verlustverwaltung.
    Order_Block_Indicador_New_Part_2.mq5 .mq5 Datei mit dem Code für den Order-Block-Indikator.
    Order Block EA MT5.mq5  .mq5 Datei mit dem Code für den Order Blocks Expert Advisor.
    Set 1% Risk.set  .set Für Backtest 1 verwendete Einstellungen.
    Set Order Block EA.set .set In Backtest 2 verwendete Einstellungen, mit täglicher Verlust- und Gewinnobergrenze.
    Set Dynamic Risk.set .set Einstellungen für den dynamischen Risikotest (Backtest 3), ohne tägliche Verlust- und Gewinngrenzen.
    Set No Dynamic Risk.set .set Einstellungen ohne dynamisches Risiko (Backtest 4), auch ohne tägliche Verlust- und Gewinngrenzen.

    Übersetzt aus dem Spanischen von MetaQuotes Ltd.
    Originalartikel: https://www.mql5.com/es/articles/17640

    Beigefügte Dateien |
    MQL5.zip (36.61 KB)
    Die Übertragung der Trading-Signale in einem universalen Expert Advisor. Die Übertragung der Trading-Signale in einem universalen Expert Advisor.
    In diesem Artikel wurden die verschiedenen Möglichkeiten beschrieben, um die Trading-Signale von einem Signalmodul des universalen EAs zum Steuermodul der Positionen und Orders zu übertragen. Es wurden die seriellen und parallelen Interfaces betrachtet.
    Neuronale Netze im Handel: Integration der Chaostheorie in die Zeitreihenprognose (letzter Teil) Neuronale Netze im Handel: Integration der Chaostheorie in die Zeitreihenprognose (letzter Teil)
    Wir fahren fort, die von den Autoren des Attraos-Frameworks vorgeschlagenen Methoden in Handelsmodelle zu integrieren. Ich möchte Sie daran erinnern, dass dieses Framework Konzepte der Chaostheorie verwendet, um Probleme der Zeitreihenprognose zu lösen, indem es sie als Projektionen mehrdimensionaler chaotischer dynamischer Systeme interpretiert.
    Eine alternative Log-datei mit der Verwendung der HTML und CSS Eine alternative Log-datei mit der Verwendung der HTML und CSS
    In diesem Artikel werden wir eine sehr einfache, aber leistungsfähige Bibliothek zur Erstellung der HTML-Dateien schreiben, dabei lernen wir auch, wie man eine ihre Darstellung einstellen kann (nach seinem Geschmack) und sehen wir, wie man es leicht in seinem Expert Advisor oder Skript hinzufügen oder verwenden kann.
    Marktsimulation (Teil 15): Sockets (IX) Marktsimulation (Teil 15): Sockets (IX)
    In diesem Artikel besprechen wir eine der möglichen Lösungen für das, was wir versucht haben zu demonstrieren, nämlich wie man es einem Excel-Nutzer ermöglicht, eine Aktion in MetaTrader 5 auszuführen, ohne Aufträge zu senden oder Positionen zu öffnen oder zu schließen. Die Idee ist, dass der Nutzer Excel verwendet, um eine fundamentale Analyse eines bestimmten Symbols durchzuführen. Und allein mit Excel lässt sich ein in MetaTrader 5 laufender Expert Advisor anweisen, eine bestimmte Position zu eröffnen oder zu schließen.