Risikomanagement (Teil 5): Integration des Risikomanagementsystems in einen Expert Advisor
- Einführung
- Verbesserungen des Indikators für die „Order Blocks“
- Erstellen eines Expert Advisors und Definieren seiner Parameter
- Deklarieren von globalen Variablen
- Die Funktion OnInit
- OnTick und OnTradeTransaction Funktionen
- OnDeinit-Funktion
- Tests
- Schlussfolgerung
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:

2. Angabe des Namens und des Autors:

3. Auswahl nur des Ereignisses OnTradeTransaction:

4. Fertigstellen:

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
Warnung: Alle Rechte sind von MetaQuotes Ltd. vorbehalten. Kopieren oder Vervielfältigen untersagt.
Dieser Artikel wurde von einem Nutzer der Website verfasst und gibt dessen persönliche Meinung wieder. MetaQuotes Ltd übernimmt keine Verantwortung für die Richtigkeit der dargestellten Informationen oder für Folgen, die sich aus der Anwendung der beschriebenen Lösungen, Strategien oder Empfehlungen ergeben.
Die Übertragung der Trading-Signale in einem universalen Expert Advisor.
Neuronale Netze im Handel: Integration der Chaostheorie in die Zeitreihenprognose (letzter Teil)
Eine alternative Log-datei mit der Verwendung der HTML und CSS
Marktsimulation (Teil 15): Sockets (IX)
- Freie Handelsapplikationen
- Über 8.000 Signale zum Kopieren
- Wirtschaftsnachrichten für die Lage an den Finanzmärkte
Sie stimmen der Website-Richtlinie und den Nutzungsbedingungen zu.