English
preview
Erstellen eines täglichen Drawdown-Limits EA in MQL5

Erstellen eines täglichen Drawdown-Limits EA in MQL5

MetaTrader 5Handel | 28 August 2024, 10:33
33 0
Kikkih25
Kikkih25

Einführung 

In diesem Artikel erstellen wir in der MetaQoutes Language (MQL5) für MetaTrader 5 einen Expert Advisor (EA) für den Forexhandel, der den täglichen Drawdown begrenzt. Das Ziel dieses EA ist es, eine tägliche Abhebungsgrenze für Handelskonten festzulegen. Der EA analysiert Faktoren wie die Gesamtzahl der Balken, den Startsaldo, die Tageszeit und den Tagessaldo und prüft, ob der Handel unter bestimmten Bedingungen stattfindet. Es sammelt auch Informationen wie Saldo, Kapital und Fonds von der MetaTrader-Plattform. Dieser EA wurde speziell für die MetaTrader-Handelsplattform entwickelt und erfordert ein Handelskonto, um ordnungsgemäß zu funktionieren.

Diese Reise wird die folgenden Themen behandeln:

  1. Erklärung des Drawdown-Limiters
  2. Erstellen des EAs in MQL5
  3. Schlussfolgerung


Erklärung des Drawdown-Limiters

Ein Drawdown-Limiter, Begrenzer der Inanspruchnahmen, ist ein Instrument, das beim Handel und bei Investitionen zur Risikosteuerung eingesetzt wird, indem potenzielle Verluste während einer Drawdown-Periode begrenzt werden. Ein Drawdown tritt ein, wenn der Wert eines Vermögenswerts oder eines Portfolios aufgrund von Marktvolatilität oder wirtschaftlichen Bedingungen sinkt. Während dieses Zeitraums schützt der Drawdown-Limiter die Anleger vor erheblichen Verlusten, indem er automatisch die gesamte Anlage oder einen Teil davon verkauft, wenn der Wert unter ein bestimmtes Niveau fällt. Dieses Instrument zielt darauf ab, mögliche Verluste zu verringern und das Kapital des Anlegers zu schützen. Managed-Futures-Konten und andere Anlagevehikel verwenden in der Regel Drawdown-Limiter, um das Risiko zu kontrollieren und sich vor erheblichen Marktrückgängen zu schützen. 

Beim Handel mit Guthabenkonten haben die Händler Probleme, ihren Drawdown zu kontrollieren. Für sie ist ein täglicher Drawdown-Limiter vorgesehen. Die Prop-Firmen legen in der Regel eine Regel namens „Trader Daily Drawdown“ fest, und wenn diese nicht eingehalten wird, wird der Händler disqualifiziert. Der Drawdown-Limiter hilft Händlern dabei:

  1. Kontrolle des Drawdowns
  2. Warnung des Händlers, wenn er oder sie risikoreiche Handelsgeschäfte tätigt
  3. Kontrolle des täglichen Drawdownd des Händlers
  4. Es verhindert, dass der Händler zu viel handelt, indem er offene Positionen begrenzt. 


Erstellen des EAs in MQL5 

Die Hauptfunktion des Expert Advisors besteht darin, alle Aktivitäten auf dem Konto zu überwachen, unabhängig davon, ob es sich um einen manuellen oder automatisierten Handel durch einen anderen Expert Advisor handelt. Der Händler muss nur ein Chart hinzufügen, und schon übernimmt er die Kontrolle. Die Gewerbetreibenden werden auf ihrem Bildschirm „Ampeln“ sehen. Die „Ampel“-Funktion informiert die Händler auf einfache grafische Weise über die vier oben genannten Schlüssel. Die EA-Kommentare können ausgeblendet und an die bevorzugten Farben und Schriftarten angepasst werden. Mit einem Klick kann die Detailseite einfach ausgeblendet werden, um Platz auf den Charts zu gewinnen. Die Position und der Stil der Ampeln lassen sich hervorragend an den Stil der Karte anpassen.

Wir müssen Handelspositionen eröffnen. Der einfachste Weg, Positionen zu eröffnen, ist die Einbeziehung einer Handelsinstanz, was in der Regel durch die Einbeziehung einer anderen Datei erreicht wird, die für offene Positionen bestimmt ist. Wir verwenden die Include-Direktive, um die Handelsbibliothek einzubinden, die Funktionen für Handelsoperationen enthält. Wir verwenden spitze Klammern, um anzuzeigen, dass die Datei, die wir einbinden wollen, im Include-Ordner enthalten ist gefolgt vom Ordner „Trade“ und einem normalen Schrägstrich und dann dem Namen der Zieldatei, in diesem Fall „Trade.mqh“. „cTrade“ ist eine Klasse für die Handhabung von Handelsoperationen, und obj-trade ist eine Instanz für diese Klasse, typischerweise ein Zeigerobjekt, das von der Klasse cTrade erstellt wird, um den Zugriff auf die Mitgliedsvariablen der Klasse zu ermöglichen.

#include <Trade/Trade.mqh>
CTrade obj-Trade;

Danach benötigen wir eine Steuerlogik, um Signale zur Eröffnung von Positionen zu erzeugen. In unserem Fall prüft die Funktion OnTick(), ob die Variable isTradeAllowed wahr ist. Ist dies der Fall, wird die Funktion checkDailyProfit() aufgerufen, was darauf hindeutet, dass der Zweck der Funktion OnTick() darin besteht, den Tagesgewinn zu prüfen und auf der Grundlage dieser Prüfung möglicherweise Geschäfte zuzulassen oder zu verbieten. Die Balken verfolgen die Gesamtzahl der Balken auf dem Chart und stellen sicher, dass die Handelslogik nur einmal pro neuem Balken ausgeführt wird, um Mehrfachausführungen innerhalb eines einzelnen Balkens zu verhindern. Zusammen ermöglichen diese Variablen dem Expert Advisor die Generierung von Handelssignalen auf der Grundlage von Werten unter Beibehaltung des richtigen Ausführungszeitpunkts. Da die Funktion keine Parameter benötigt, können wir die Aktionen, die sie ausführt, wie folgt beschreiben:

  • Sie definiert die Variable total_day_Profit und initialisiert sie mit 0.  
  • Außerdem erhält sie die aktuelle Uhrzeit und wandelt sie mit der Funktion TimeToString, die in der Datumsvariablen gespeichert ist, in eine Zeichenkette um.
  • Ebenso wird die Anfangsstunde des Tages durch Addition von 1 zum Tagesanfang berechnet und in einer Variablen gespeichert.
  • Prüft, ob die „current time“ (tagsüber) kleiner ist als die „time“. Wenn ja, wird der Wert dayTime gesetzt und der aktuelle Saldo mit der Funktion Acc_B berechnet und in der Variablen dayBalance gespeichert.
  • Wählt die historischen Daten für den Tag mit der Funktion Historie auswählen aus, wobei die Anfangs- und Endzeit auf den Tagesanfang und das Tagesende eingestellt ist.
  • Berechnet die Gesamtzahl der Angebote für den Tag mit der Funktion HistoryDealsTotal und speichert sie in der Variablen TotalDeals.
  • Darüber hinaus wird jede Transaktion in der Historie daraufhin überprüft, ob der Transaktionstyp DEAL_ENTRY_OUT lautet, was bedeutet, dass es sich um eine abschließende Transaktion handelt. Wenn ja, wird der Handelsgewinn durch Addition der Werte DEAL_PROFIT, DEAL_COMMISSION und DEAL_SWAP berechnet und zur Variablen total_day_profit hinzugefügt.
  • Er berechnet den Eröffnungssaldo des Tages durch Subtraktion des total_day_Profit vom aktuellen Kontostand unter Verwendung der Funktion AccountInfoDouble mit dem Parameter ACCOUNT-BALANCE.

Die Funktion gibt den berechneten Eröffnungssaldo als Double-Wert zurück.

int totalBars = 0;
double initialBalance = 0;
datetime dayTime = 0;
double dayBalance = 0;
bool isTradeAllowed = true;

Als Nächstes gehen wir zur Definition von Funktionen über. Die Funktion scheint sich auf Kontoinformationen zu beziehen und gibt je nach Funktionsname unterschiedliche Werte zurück. Die Funktionen Acc_B(), Acc_E() und Acc_S() werden verwendet, um Informationen über den Kontostand, das Eigenkapital bzw. die Währung abzurufen. Diese Funktionen dienen der Überwachung des Finanzstatus des Kontos.

double Acc_B(){return AccountInfoDouble(ACCOUNT_BALANCE);}
double Acc_E(){return AccountInfoDouble(ACCOUNT_EQUITY);}
string Acc_S(){return AccountInfoString(ACCOUNT_CURRENCY);}

Der vollständige Code der offenen Stellen lautet wie folgt:

#include <Trade/Trade.mqh>
CTrade obj-Trade;

int totalBars = 0;
double initialBalance = 0;
datetime dayTime = 0;
double dayBalance = 0;
bool isTradeAllowed=true;

double Acc_B(){return AccountInfoDouble(ACCOUNT_BALANCE);}
double Acc_E(){return AccountInfoDouble(ACCOUNT_EQUITY);}
string Acc'S(){return AccountInfoString(ACCOUNT_CURRENCY);}

Onlnit wird immer dann aufgerufen, wenn der Expert Advisor initialisiert wird. Es ist die Instanz, die wir benötigen, um den Indikator zu initialisieren und Text zu erstellen, um die Daten des Anfangssaldos des Kontos für die weitere Analyse anzuzeigen. Um den Indikator zu initialisieren, verwenden wir die eingebaute Funktion, um seinen createText zurückzugeben, indem wir die richtigen Parameter angeben. Das Textobjekt wird an bestimmten Koordinaten positioniert und verwendet Farben mit einer bestimmten Schriftgröße. Hier ist eine Aufschlüsselung dessen, was wir mit dieser Funktion erreichen:

  1. Sie sucht den Anfangssaldo des Kontos mit der Funktion Acc_B()und speichert ihn in der Variablen „initialBalance“.
  2. Dadurch wird ein Textfeld mit dem Text „* PROP FIRM PROGRESS DASHBOARD *“ an der Position (30,30) auf dem Bildschirm erstellt, mit einer Schriftgröße von 13 und einer hellblauen Farbe (clrAqual).
  3. Dadurch werden mehrere Textfelder erstellt, in denen verschiedene Meldungen und Informationen für den Nutzer angezeigt werden. Diese Textfelder befinden sich an verschiedenen Stellen auf dem Bildschirm und haben unterschiedliche Schriftgrößen und -farben.

Hier besteht der Hauptzweck darin, eine Nutzeroberfläche zu schaffen, die verschiedene Informationen im Zusammenhang mit der Kontoführung und dem Handel anzeigt. Textfelder werden verwendet, um Kontoinformationen, Nachrichten und andere relevante Daten für den Nutzer anzuzeigen.

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit() {

   initialBalance = Acc_B();
   
   createText("0","***DAILY DRAWDOWN LIMITER ***",30,30,clrAqua,13);
   createText("00","______________________________________",30,30,clrAqua,13);
   createText("1","DrawDown Limiter is Active.",70,50,clrWhite,11);
   createText("2","Counters will be Reset on Next Day Start.",70,65,clrWhite,10);
   createText("3","From: ",70,80,clrWhite,10);
   createText("4",'Time Here',120,80,clrGray,10);
   createText("5","To: ",70,95,clrWhite,10);
   createText("6",'Time Here',120,95,clrGray,10);
   createText("7",'Current: ',70,110,clrWhite,10);
   createText("8",'Time Here',120,110,clrGray,10);

   createText("9",'ACCOUNT DRAWDOWN ============',70,130,clrPeru,11);
   createText("10",'Account Initial Balance: ',70,145,clrWhite,10);
   createText("11",DoubleToString(initialBalance,2)+" "+Acc_S(),250,145,clrWhite,10);
   createText("12",'Torelated DrawDown: ',70,160,clrWhite,10);
   createText("13","12.00 %",250,160,clrAqua,10);
   createText("14",'Current Account Equity: ',70,175,clrWhite,10);
   createText("15",DoubleToString(Acc_E(),2)+" "+Acc_S(),250,175,clrWhite,10);
   createText("16",'Current Balance Variation: ',70,190,clrWhite,10);
   createText("17",DoubleToString((Acc_E()-Acc_B())/Acc_B()*100,2)+" %",250,190,clrGray,10);

   createText("18",'DAILY DRAWDOWN ================',70,210,clrPeru,11);
   createText("19",'Starting Balance: ',70,225,clrWhite,10);
   createText("20",DoubleToString(Acc_B(),2)+" "+Acc_S(),270,225,clrWhite,10);
   createText("21",'DrawDowm Maximum Threshold: ',70,240,clrWhite,10);
   createText("22",'5.00 %"+" "+Acc_S(),270,240,clrAqua,10);
   createText("23",'DrawDown Maximum Amount: ',70,255,clrWhite,10);
   createText("24",'-"+DoubleToString(Acc_B()*5/100,2)+' "+Acc_S(),270,255,clrYellow,10);
   createText("25",'Current Closed Daily Profit: ',70,270,clrWhite,10);
   createText("26",'0.00"+" "+Acc_S(),270,270,clrGray,10);
   createText("27",'Current DrawDown Percent: ',70,285,clrWhite,10);
   createText("28",'0.00"+" %",270,285,clrGray,10);

   createText("29",'>>> Initializing The Program, Get Ready To Trade.",70,300,clrYellow,10);
   
   return(INIT_SUCCEEDED);
}

Hier wird die Funktion OnTick jedes Mal aufgerufen, wenn es einen neuen Tick für das Symbol gibt, an das der EA gebunden ist. Bei der Funktion checkDailyProfit ist darauf zu achten, dass sie korrekt implementiert wird. Die Variable isTradeAllowed ist eine boolesche Variable, die steuert, ob der Handel erlaubt ist. Wenn isTradeAllowed falsch ist, kehrt die Funktion sofort zurück, und es wird kein weiterer Code innerhalb der OnTick-Funktion ausgeführt.

//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick(){   
   
   checkDailyProfit();
   
   if (!isTradeAllowed) return;
    

Wir definieren einfach Instanzen des Breakdown-Welle. Dies muss bei jedem Tick geschehen, also tun wir es ohne Einschränkungen. Zunächst geben wir Ask und Bid an, die wir zur Eröffnung der Positionen verwenden, sobald die jeweiligen Bedingungen erfüllt sind. Beachten Sie, dass dies auch bei jedem Tick geschehen muss, damit wir die aktuellsten Preisnotierungen erhalten. Hier deklarieren wir die Variablen vom Typ „double“ für die Speicherung der aktuellen Preise und normalisieren sie auf die Ziffern der Symbolwährung, indem wir die Gleitkommazahl runden, um die Genauigkeit zu erhalten.

double ask = NormalizeDouble(SymbolInfoDouble(_Symbol,SYMBOL_ASK),_Digits); 
double bid = NormalizeDouble(SymbolInfoDouble(_Symbol,SYMBOL_BID),_Digits); 

Nach der Definition der Instanzen der Breakdown-Schaukel gehen wir nun zur Definition einer Funktion namens „iBars“ über, die in unserem Fall zwei Parameter benötigt: „_Symbol“ und „_Period“. Die Funktion „iBars“ gibt einen ganzzahligen Wert zurück, der als „bars“ bezeichnet wird. Anschließend wird geprüft, ob die Variable „totalBars“ gleich dem von der Funktion „iBars“ zurückgegebenen Wert ist. Wenn sie gleich sind, kehrt die Funktion zurück, ohne etwas anderes zu tun. Sind sie nicht gleich, wird der Wert von „totalBars“ auf den Wert von „bars“ gesetzt, der von der Funktion „iBars“ zurückgegeben wird.

   int bars = iBars(_Symbol,_Period);
   if (totalBars == bars) return;
   totalBars = bars;

Wir fahren nun mit der Definition der Funktion „iBars“ fort; hier prüfen wir, ob die Ergebnisse des Aufrufs der Funktion „positionsTotal()“ größer als 1 sind. Ist dies der Fall, kehrt die Funktion zurück, ohne etwas anderes zu tun. Ist dies nicht der Fall, geht der Code in die nächste Zeile. Die Zeile „int number = MathRand()%“ scheint unvollständig zu sein, da keine schließenden Klammern oder Semikolons vorhanden sind. Angenommen, das Ziel ist es, eine zufällige ganze Zahl zu generieren, würde die Zeile wie folgt vervollständigt werden: „int number = MathRand()% totalBars;“ Diese Zeile generiert eine Zufallszahl zwischen 0 und dem Wert von „totalBars“ (einschließlich) und weist sie der Variablen „number“ zu.

 if (PositionsTotal() > 1) return;
   int number = MathRand()%

Der vollständige Code der definierenden Funktionen ist unten aufgeführt:

//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick(){   
   
   checkDailyProfit();
   
   if (!isTradeAllowed) return;
   
   double ask = NormalizeDouble(SymbolInfoDouble(_Symbol,SYMBOL_ASK),_Digits);
   double bid = NormalizeDouble(SymbolInfoDouble(_Symbol,SYMBOL_BID),_Digits);

   int bars = iBars(_Symbol,_Period);
   if (totalBars == bars) return;
   totalBars = bars;
   
   if (PositionsTotal() > 1) return;
   int number = MathRand()%

Im Folgenden finden Sie eine Aufschlüsselung der wichtigsten Funktionen des Artikels, wie z. B. das Platzieren von Trades, das Erstellen von Textbeschriftungen auf dem Chart und die Überprüfung des täglichen Gewinns: 

1. Ausführung des Handelsgeschäfts 

In dieser Komponente untersuchen wir den Wert einer Variablen namens „number“ und führen auf der Grundlage ihres Wertes verschiedene Aktionen durch.

Wenn die „Zahl“ 0 ist, löst der Code eine Methode namens „Buy“ für ein Objekt mit der Bezeichnung „obj-Trade“ aus. Diese Methode erfordert fünf Parameter: der erste ist 0,1, der zweite ist die Variable „_Symbol“, der dritte ist die Variable „ask“, der vierte ist der „ask“-Wert minus das 70-fache der Variable „_Point“ und der fünfte ist der „ask“-Wert plus das 70-fache derselben Variable „_Point“. Dies zeigt an, dass der Code versucht, einen Vermögenswert zu einem Preis zu kaufen, der unter Berücksichtigung der Geld-Brief-Spanne etwas unter dem aktuellen Briefkurs liegt.

Wenn die „Zahl“ 1 ist, führt der Code eine Methode namens „Sell“ für dasselbe Objekt „obj-Trade“ aus. Auch diese Methode benötigt fünf Parameter: Der erste ist 0,1, der zweite ist die Variable „_Symbol“, der dritte ist die Variable „bid“, der vierte ist der „bid“-Wert plus das 70-fache der Variable „_Point“ und der fünfte ist der „bid“-Wert minus das 70-fache derselben Variable „_Point“. Dies bedeutet, dass der Code versucht, einen Vermögenswert zu einem Preis zu verkaufen, der leicht über dem aktuellen Geldkurs liegt, wobei auch die Geld-Brief-Spanne berücksichtigt wird. Diese wertet den Wert einer als „number“ bezeichneten Variablen aus und führt auf der Grundlage ihres Wertes verschiedene Aktionen durch.

if (number == 0){
      obj_Trade.Buy(0.1,_Symbol,ask,ask-70*_Point,ask+70*_Point);
   }
   else if (number == 1){
      obj_Trade.Sell(0.1,_Symbol,bid,bid+70*_Point,bid-70*_Point);
   }

2. Text-Erstellung

In unserem vorherigen Code gibt es eine Funktion namens createText, die für die Erstellung eines Textobjekts auf dem Chart verantwortlich ist. Zur Ausführung dieser Funktion werden bestimmte Parameter wie Objektname (objName), Textinhalt (text), x- und y-Koordinaten für die Platzierung des Etiketts (x und y), Textfarbe (clrTxt) und Schriftgröße (font size) benötigt. Anhand dieser Eingaben erstellt die Funktion ein Textobjekt auf dem Chart, passt dessen Eigenschaften an und aktualisiert das Chart. Außerdem gibt die Funktion einen booleschen Wert zurück, der angibt, ob die Erstellung des Etiketts erfolgreich war oder nicht.


bool createText(string objName,string text,int x, int y,color clrTxt,int fontSize){
   ResetLastError();
   if (!ObjectCreate(0,objName,OBJ_LABEL,0,0,0)){
      Print(__FUNCTION__,": failed to create the Label! Error Code = ",GetLastError());
      return (false);
   }
   ObjectSetInteger(0,objName,OBJPROP_XDISTANCE,x);
   ObjectSetInteger(0,objName,OBJPROP_YDISTANCE,y);
   ObjectSetInteger(0,objName,OBJPROP_CORNER,CORNER_LEFT_UPPER);
   ObjectSetString(0,objName,OBJPROP_TEXT,text);
   ObjectSetInteger(0,objName,OBJPROP_COLOR,clrTxt);
   ObjectSetInteger(0,objName,OBJPROP_FONTSIZE,fontSize);
   
   ChartRedraw(0);
   return (true);
}

3. Tägliche Gewinnüberprüfung

In dieser MQL-Funktion checkDailyProfit, die den Gesamtgewinn für einen bestimmten Tag berechnet. Die Funktion benötigt keine Parameter und führt die folgenden Schritte aus:

  • Definiert die Variable total_day_profit und initialisiert sie auf 0.
  • Ermittelt die aktuelle Uhrzeit und wandelt sie mit der Funktion TimeToString in eine Zeichenkette um, die in der Variablen date gespeichert wird.
  • Berechnet die Anfangsstunde des Tages, indem 1 zum Tagesanfang addieren und in einer Variablen speichern.
  • Prüft, ob die Tageszeit kleiner ist als die aktuelle Zeit. Wenn ja, setzen wir dayTime und berechnen den aktuellen Saldo mit der Funktion Acc_B, die in der Variablen dayBalance gespeichert ist.
  • Wählt die historischen Daten für den Tag mit der Funktion HistorySelect aus, wobei die Anfangs- und Endzeiten auf den Tagesanfang und das Tagesende gesetzt werden.
  • Berechnet die Gesamtzahl der Angebote für den Tag mit der Funktion HistoryDealsTotal und speichert sie in der Variablen TotalDeals.
  • Mit dem Durchgang durch die Transaktionen in der Historie wird geprüft, ob der Transaktionstyp DEAL_ENTRY_OUT lautet, d. h., es handelt sich um eine abschließende Transaktion. Wenn ja, wird der Handelsgewinn berechnet, indem die Werte DEAL_PROFIT, DEAL_COMMISSION und DEAL_SWAP addiert und zur Variablen total_day_profit hinzugefügt werden.
  • Berechnung des Eröffnungssaldos des Tages durch Subtraktion des total_day_profit vom aktuellen Kontostand unter Verwendung der Funktion AccountInfoDouble mit dem Parameter ACCOUNT_BALANCE.

Die Funktion gibt den berechneten Eröffnungssaldo als Double-Wert zurück.

void checkDailyProfit(){

   double total_day_Profit = 0;
   datetime end = TimeCurrent();
   string sdate = TimeToString(TimeCurrent(),TIME_DATE);
   datetime start = StringToTime(sdate);
   datetime to = start + (1*24*60*60);
   
   if (dayTime < to){
      dayTime = to;
      dayBalance = Acc_B();
   }
   
   HistorySelect(start,end);
   int TotalDeals = HistoryDealsTotal();
   for (int i=0; i<TotalDeals; i++){
      ulong Ticket = HistoryDealGetTicket(i);
      if (HistoryDealGetInteger(Ticket,DEAL_ENTRY)==DEAL_ENTRY_OUT){
         double Latest_Day_Profit = (HistoryDealGetDouble(Ticket,DEAL_PROFIT)
                               +HistoryDealGetDouble(Ticket,DEAL_COMMISSION)
                               +HistoryDealGetDouble(Ticket,DEAL_SWAP));
         total_day_Profit += Latest_Day_Profit;
      }
   }
   double startingBalance = 0;
   startingBalance = AccountInfoDouble(ACCOUNT_BALANCE) - total_day_Profit;
   double daily_profit_or_drawdown = NormalizeDouble((total_day_Profit*100/startingBalance),2);
   string daily_profit_in_Text_Format = "";
   daily_profit_in_Text_Format = DoubleToString(daily_profit_or_drawdown,2)+" %";
   
   //Print(total_day_Profit, " >>> ",daily_profit_in_Text_Format);
   
   createText("4",TimeToString(start),120,80,clrYellow,10);
   createText("6",TimeToString(to),120,95,clrYellow,10);
   createText("8",TimeToString(end),120,110,clrWhite,10);

   if (Acc_E() > initialBalance){
      createText("15",DoubleToString(Acc_E(),2)+" "+Acc_S(),250,175,clrLime,10);
      createText("17",DoubleToString((Acc_E()-initialBalance)/initialBalance*100,2)+" %",250,190,clrLime,10);
   }
   else if (Acc_E() < initialBalance){
      createText("15",DoubleToString(Acc_E(),2)+" "+Acc_S(),250,175,clrRed,10);
      createText("17",DoubleToString((Acc_E()-initialBalance)/initialBalance*100,2)+" %",250,190,clrRed,10);
   }
   if (Acc_E() == initialBalance){
      createText("15",DoubleToString(Acc_E(),2)+" "+Acc_S(),250,175,clrWhite,10);
      createText("17",DoubleToString((Acc_E()-initialBalance)/initialBalance*100,2)+" %",250,190,clrWhite,10);
   }
   
   createText("20",DoubleToString(dayBalance,2)+" "+Acc_S(),270,225,clrWhite,10);
   createText("24","-"+DoubleToString(dayBalance*5/100,2)+" "+Acc_S(),270,255,clrYellow,10);

   if (Acc_B() > dayBalance){
      createText("26",DoubleToString(total_day_Profit,2)+" "+Acc_S(),270,270,clrLime,10);
      createText("28",daily_profit_in_Text_Format,270,285,clrLime,10);
   }
   else if (Acc_B() < dayBalance){
      createText("26",DoubleToString(total_day_Profit,2)+" "+Acc_S(),270,270,clrRed,10);
      createText("28",daily_profit_in_Text_Format,270,285,clrRed,10);
   }
   else if (Acc_B() == dayBalance){
      createText("26",DoubleToString(total_day_Profit,2)+" "+Acc_S(),270,270,clrWhite,10);
      createText("28",daily_profit_in_Text_Format,270,285,clrWhite,10);
   }
   
   if (daily_profit_or_drawdown <= -5.00 || ((Acc_E()-initialBalance)/initialBalance*100) < -12.00){
      createText("29",">>> Maximum Threshold Hit, Can't Trade.",70,300,clrRed,10);
      isTradeAllowed = false;
   }
   else {
      createText("29",">>> Maximum Threshold Not Hit, Can Trade.",70,300,clrL…

Der vollständige Code der Schlüsselkomponenten der Artikelfunktionen ist nachstehend aufgeführt:

2;
   if (number == 0){
      obj_Trade.Buy(0.1,_Symbol,ask,ask-70*_Point,ask+70*_Point);
   }
   else if (number == 1){
      obj_Trade.Sell(0.1,_Symbol,bid,bid+70*_Point,bid-70*_Point);
   }
}
//+------------------------------------------------------------------+
bool createText(string objName,string text,int x, int y,color clrTxt,int fontSize){
   ResetLastError();
   if (!ObjectCreate(0,objName,OBJ-LABEL,0,0,0)){
      Print(__FUNCTION__,": failed to create the Label! Error Code = ",GetLastError());
      return (false);
   }
   ObjectSetInteger(0,objName,OBJPROP-XDISTANCE,x);
   ObjectSetInteger(0,objName,OBJPROP-YDISTANCE,y);
   ObjectSetInteger(0,objName,OBJPROP-CORNER,CORNER_LEFT_UPPER);
   ObjectSetString(0,objName,OBJPROP-TEXT,text);
   ObjectSetInteger(0,objName,OBJPROP-COLOR,clrTxt);
   ObjectSetInteger(0,objName,OBJPROP-FONTSIZE,fontSize);
   
   ChartRedraw(0);
   return (true);
}

void checkDailyProfit(){

   double total_day_Profit = 0;
   datetime end = TimeCurrent();
   string sdate = TimeToString(TimeCurrent(),TIME_DATE);
   datetime start = StringToTime(sdate);
   datetime to = start + (1*24*60*60);
   
   if (dayTime < to){
      dayTime = to;
      dayBalance = Acc_B();
   }
   
   HistorySelect(start,end);
   int TotalDeals = HistoryDealsTotal();
   for (int i=0; i<TotalDeals; i++){
      ulong Ticket = HistoryDealGetTicket(i);
      if (HistoryDealGetInteger(Ticket,DEAL_ENTRY)==DEAL_ENTRY_OUT){
         double Latest_Day_Profit = (HistoryDealGetDouble(Ticket,DEAL_PROFIT)
                               +HistoryDealGetDouble(Ticket,DEAL_COMMISSION)
                               +HistoryDealGetDouble(Ticket,DEAL_SWAP));
         total_day_Profit += Latest_Day_Profit;
      }
   }
   double startingBalance = 0;
   startingBalance = AccountInfoDouble(ACCOUNT-BALANCE) - total_day_Profit;
   double daily_profit_or_drawdown = NormalizeDouble((total_day_Profit*100/startingBalance),2);
   string daily_profit_in_Text_Format = "";
   daily_profit_in_Text_Format = DoubleToString(daily_profit_or_drawdown,2)+" %";
   
   //Print(total_day_Profit, " >>> ",daily_profit_in_Text_Format);
   
   createText("4",TimeToString(start),120,80,clrYellow,10);
   createText("6",TimeToString(to),120,95,clrYellow,10);
   createText("8",TimeToString(end),120,110,clrWhite,10);

   if (Acc_E() > initialBalance){
      createText("15",DoubleToString(Acc_E(),2)+" "+Acc_S(),250,175,clrLime,10);
      createText("17",DoubleToString((Acc_E()-initialBalance)/initialBalance*100,2)+" %",250,190,clrLime,10);
   }
   else if (Acc_E() < initialBalance){
      createText("15",DoubleToString(Acc_E(),2)+" "+Acc_S(),250,175,clrRed,10);
      createText("17",DoubleToString((Acc_E()-initialBalance)/initialBalance*100,2)+" %",250,190,clrRed,10);
   }
   if (Acc_E() == initialBalance){
      createText("15",DoubleToString(Acc_E(),2)+" "+Acc_S(),250,175,clrWhite,10);
      createText("17",DoubleToString((Acc_E()-initialBalance)/initialBalance*100,2)+" %",250,190,clrWhite,10);
   }
   
   createText("20",DoubleToString(dayBalance,2)+" "+Acc_S(),270,225,clrWhite,10);
   createText("24","-"+DoubleToString(dayBalance*5/100,2)+" "+Acc_S(),270,255,clrYellow,10);

   if (Acc_B() > dayBalance){
      createText("26",DoubleToString(total_day_Profit,2)+" "+Acc_S(),270,270,clrLime,10);
      createText("28",daily_profit_in_Text_Format,270,285,clrLime,10);
   }
   else if (Acc_B() < dayBalance){
      createText("26",DoubleToString(total_day_Profit,2)+" "+Acc_S(),270,270,clrRed,10);
      createText("28",daily_profit_in_Text_Format,270,285,clrRed,10);
   }
   else if (Acc_B() == dayBalance){
      createText("26",DoubleToString(total_day_Profit,2)+" "+Acc_S(),270,270,clrWhite,10);
      createText("28",daily_profit_in_Text_Format,270,285,clrWhite,10);
   }
   
   if (daily_profit_or_drawdown <= -5.00 || ((Acc_E()-initialBalance)/initialBalance*100) < -12.00){
      createText("29",">>> Maximum Threshold Hit, Can't Trade.",70,300,clrRed,10);
      isTradeAllowed = false;
   }
   else {
      createText("29",">>> Maximum Threshold Not Hit, Can Trade.",70,300,clrRed);

Das Ergebnis ist wie folgt:

Beispiel für eine Logik mit weitem Schwellenwert.

weiter Schwellenwert

Beispiel für die Logik eines engen Schwellenwerts.

naher Schwellenwert

Beispiel für einen ausgelösten (getroffenen) Schwellenwert:

getroffener Schwellenwert

Der vollständige Code für die Erstellung eines Drawdown-Limiters lautet wie folgt:

//+------------------------------------------------------------------+
//|                                       Daily Drawdown Limiter.mq5 |
//|                                  Copyright 2024, MetaQuotes Ltd. |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2024, MetaQuotes Ltd."
#property link      "https://www.mql5.com"
#property version   "1.00"

#include <Trade/Trade.mqh>
CTrade obj_Trade;

int totalBars = 0;
double initialBalance = 0;
double dayBalance = 0;
datetime dayTime = 0;
bool isTradeAllowed = true;

// Functions to get account balance, equity, and currency
double Acc_B() {return AccountInfoDouble(ACCOUNT_BALANCE);}
double Acc_E() {return AccountInfoDouble(ACCOUNT_EQUITY);}
string Acc_S() {return AccountInfoString(ACCOUNT_CURRENCY);}

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
   // Initialize initial balance
   initialBalance = Acc_B();
   
   // Create dashboard texts
   createText("0","*** Daily Drawdown Limiter ***",30,30,clrBlack,13);
   createText("00","______________________________________",30,30,clrBlack,13);
   createText("1","DrawDown Limiter is Active.",70,50,clrBlack,11);
   createText("2","Counters will be reset on Next Day Start.",70,65,clrBlack,10);
   createText("3","From: ",70,80,clrBlack,10);
   createText("4","Time Here",120,80,clrGray,10);
   createText("5","To: ",70,95,clrBlack,10);
   createText("6","Time Here",120,95,clrGray,10);
   createText("7","Current: ",70,110,clrBlack,10);
   createText("8","Time Here",120,110,clrGray,10);

   createText("9","ACCOUNT DRAWDOWN ============",70,130,clrPeru,11);
   createText("10","Account Initial Balance: ",70,145,clrBlack,10);
   createText("11",DoubleToString(initialBalance,2)+" "+Acc_S(),250,145,clrBlack,10);
   createText("12","Tolerated DrawDown: ",70,160,clrBlack,10);
   createText("13","12.00 %",250,160,clrBlack,10);
   createText("14","Current Account Equity: ",70,175,clrBlack,10);
   createText("15",DoubleToString(Acc_E(),2)+" "+Acc_S(),250,175,clrBlack,10);
   createText("16","Current Balance Variation: ",70,190,clrBlack,10);
   createText("17",DoubleToString((Acc_E()-Acc_B())/Acc_B()*100,2)+" %",250,190,clrGray,10);

   createText("18","DAILY DRAWDOWN ================",70,210,clrPeru,11);
   createText("19","Starting Balance: ",70,225,clrBlack,10);
   createText("20",DoubleToString(Acc_B(),2)+" "+Acc_S(),270,225,clrBlack,10);
   createText("21","DrawDown Maximum Threshold: ",70,240,clrBlack,10);
   createText("22","5.00 %",270,240,clrBlack,10);
   createText("23","DrawDown Maximum Amount: ",70,255,clrBlack,10);
   createText("24","-"+DoubleToString((Acc_B()*5/100),2)+" "+Acc_S(),270,255,clrBlue,10);
   createText("25","Current Closed Daily Profit: ",70,270,clrBlack,10);
   createText("26","0.00"+" "+Acc_S(),270,270,clrGray,10);
   createText("27","Current DrawDown Percent: ",70,285,clrBlack,10);
   createText("28","0.00 %",270,285,clrGray,10);
   createText("29",">>> Initializing The Program, Get Ready To Trade.",70,300,clrBlue,10);

   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
   // Deinitialization code here (if needed)
  }
//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
  {
   // Check daily profit and drawdown
   checkDailyProfit();
   
   // If trading is not allowed, exit function
   if (!isTradeAllowed) return;
   
   // Get current ask and bid prices
   double ask = NormalizeDouble(SymbolInfoDouble(_Symbol,SYMBOL_ASK),_Digits);
   double bid = NormalizeDouble(SymbolInfoDouble(_Symbol,SYMBOL_BID),_Digits);
   
   // Check for new bar
   int bars = iBars(_Symbol,_Period);
   if (totalBars == bars) return;
   totalBars = bars;
   
   // If more than one position, exit function
   if (PositionsTotal() > 1) return;
   
   // Random trade decision
   int number = MathRand()%2;
   Print(number);
   
   if (number == 0){
      obj_Trade.Buy(1,_Symbol,ask,ask-70*_Point,ask+70*_Point);
   }
   else if (number == 1){
      obj_Trade.Sell(1,_Symbol,bid,bid+70*_Point,bid-70*_Point);
   }
  }
//+------------------------------------------------------------------+
//| Check daily profit and drawdown                                  |
//+------------------------------------------------------------------+
void checkDailyProfit() {
   
   double total_day_Profit = 0.0;
   datetime end = TimeCurrent();
   string sdate = TimeToString (TimeCurrent(), TIME_DATE);
   datetime start = StringToTime(sdate);
   datetime to = start + (1*24*60*60);
   
   // Reset daily balance and time at start of new day
   if (dayTime < to){
      dayTime = to;
      dayBalance = Acc_B();
   }

   // Calculate total daily profit
   HistorySelect(start,end);
   int TotalDeals = HistoryDealsTotal();

   for(int i = 0; i < TotalDeals; i++){
      ulong Ticket = HistoryDealGetTicket(i);
      if(HistoryDealGetInteger(Ticket,DEAL_ENTRY) == DEAL_ENTRY_OUT){
         double Latest_Day_Profit = (HistoryDealGetDouble(Ticket,DEAL_PROFIT)
                                    + HistoryDealGetDouble(Ticket,DEAL_COMMISSION)
                                    + HistoryDealGetDouble(Ticket,DEAL_SWAP));
         total_day_Profit += Latest_Day_Profit;
      }
   }
   
   double startingBalance = 0.0;
   startingBalance = AccountInfoDouble(ACCOUNT_BALANCE) - total_day_Profit;
   string day_profit_in_TextFormat = "";
   double daily_Profit_or_Drawdown = NormalizeDouble(((total_day_Profit) * 100/startingBalance),2);
   day_profit_in_TextFormat = DoubleToString(daily_Profit_or_Drawdown,2) + " %";
      
   // Update dashboard texts with new data
   createText("4",TimeToString(start),120,80,clrBlue,10);
   createText("6",TimeToString(to),120,95,clrBlue,10);
   createText("8",TimeToString(end),120,110,clrBlack,10);

   createText("11",DoubleToString(initialBalance,2)+" "+Acc_S(),250,145,clrBlack,10);
   if (Acc_E() > initialBalance){
      createText("15",DoubleToString(Acc_E(),2)+" "+Acc_S(),250,175,clrMediumBlue,10);
      createText("17",DoubleToString(((Acc_E()-initialBalance)/initialBalance)*100,2)+" %",250,190,clrMediumBlue,10);
   }
   else if (Acc_E() < initialBalance){
      createText("15",DoubleToString(Acc_E(),2)+" "+Acc_S(),250,175,clrRed,10);
      createText("17",DoubleToString(((Acc_E()-initialBalance)/initialBalance)*100,2)+" %",250,190,clrRed,10);
   }
   else if (Acc_E() == initialBalance){
      createText("15",DoubleToString(Acc_E(),2)+" "+Acc_S(),250,175,clrBlack,10);
      createText("17",DoubleToString(((Acc_E()-initialBalance)/initialBalance)*100,2)+" %",250,190,clrBlack,10);
   }

   createText("20",DoubleToString(dayBalance,2)+" "+Acc_S(),270,225,clrBlack,10);
   createText("24","-"+DoubleToString((dayBalance*5/100),2)+" "+Acc_S(),270,255,clrBlue,10);
   if (Acc_B() > dayBalance){
      createText("26",DoubleToString(total_day_Profit,2)+" "+Acc_S(),270,270,clrMediumBlue,10);
      createText("28",day_profit_in_TextFormat,270,285,clrMediumBlue,10);
   }
   else if (Acc_B() < dayBalance){
      createText("26",DoubleToString(total_day_Profit,2)+" "+Acc_S(),270,270,clrRed,10);
      createText("28",day_profit_in_TextFormat,270,285,clrRed,10);
   }
   else if (Acc_B() == dayBalance){
      createText("26",DoubleToString(total_day_Profit,2)+" "+Acc_S(),270,270,clrBlack,10);
      createText("28",day_profit_in_TextFormat,270,285,clrBlack,10);
   }
   
   // Check if drawdown limits are hit and update trading permission
   if (daily_Profit_or_Drawdown <= -5.00 ||((Acc_E()-initialBalance)/initialBalance)*100 < -12.00){
      createText("29",">>> Max ThreshHold Hit, Can't Trade.",70,300,clrRed,10);
      isTradeAllowed = false;
   }
   else {
      createText("29",">>> Max ThresHold Not Hit, Can Trade.",70,300,clrMediumBlue,10);
      isTradeAllowed = true;
   }
}

//+------------------------------------------------------------------+
//| Create text label on the chart                                   |
//+------------------------------------------------------------------+
bool createText(string objName, string text, int x, int y, color clrTxt,int fontSize) {
 ResetLastError();
     if (!ObjectCreate(0,objName,OBJ_LABEL,0,0,0)){
        Print(__FUNCTION__,": failed to create the Label! Error code = ", GetLastError());
        return(false);
     }

   ObjectSetInteger(0,objName,OBJPROP_XDISTANCE, x);
   ObjectSetInteger(0,objName,OBJPROP_YDISTANCE, y);
   ObjectSetInteger(0,objName,OBJPROP_CORNER, CORNER_LEFT_UPPER);
   ObjectSetString(0,objName,OBJPROP_TEXT, text);
   ObjectSetInteger(0,objName,OBJPROP_FONTSIZE, fontSize);
   //ObjectSetString(0,objName,OBJPROP_FONT, "Calibri");
   ObjectSetInteger(0,objName,OBJPROP_COLOR, clrTxt);
   
   ChartRedraw(0);
   
   return(true); 
}

Prost auf uns! Jetzt haben wir ein tägliches Drawdown-Limit für Forex Trading Expert Advisor erstellt, das auf der Festlegung einer täglichen Abhebungsgrenze für Handelskonten basiert.


Schlussfolgerung

Dieser Artikel überwacht die Handelsaktivitäten und aktualisiert das Chart mit relevanten Textbeschriftungen, die den Handelsstatus, den Gewinn und die Erlaubnis zum Handel auf der Grundlage bestimmter Schwellenwerte anzeigen. Die Verwendung von Funktionen zur Platzierung von Vorgängen und zur Aktualisierung von Textbeschriftungen im Chart hilft, den Artikel zu organisieren und die Pflege zu vereinfachen. Wir haben uns die grundlegenden Schritte angesehen, die für die Automatisierung der berühmten täglichen Drawdown-Limiter Forex-Handelsstrategie in MQL5 durchgeführt werden müssen. Wir haben die grundlegende Definition und Beschreibung der Strategie geliefert und gezeigt, wie sie in MQL5 erstellt werden kann. Händler können das gezeigte Wissen nutzen, um ein komplexeres System zur Begrenzung des täglichen Drawdowns zu entwickeln, das später optimiert werden kann, um am Ende bessere Ergebnisse zu erzielen.

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

Beigefügte Dateien |
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.
Entwicklung eines Expertenberaters für mehrere Währungen (Teil 7): Auswahl einer Gruppe auf der Grundlage der Vorwärtsperiode Entwicklung eines Expertenberaters für mehrere Währungen (Teil 7): Auswahl einer Gruppe auf der Grundlage der Vorwärtsperiode
Zuvor haben wir die Auswahl einer Gruppe von Handelsstrategie-Instanzen mit dem Ziel, die Ergebnisse ihrer gemeinsamen Operation zu verbessern, nur für den gleichen Zeitraum bewertet, in dem die Optimierung der einzelnen Instanzen durchgeführt wurde. Mal sehen, was in der Vorwärtsperiode passiert.
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.
Algorithmen zur Optimierung mit Populationen: Vogelschwarm-Algorithmus (BSA) Algorithmen zur Optimierung mit Populationen: Vogelschwarm-Algorithmus (BSA)
Der Artikel befasst sich mit dem vogelschwarmbasierten Algorithmus (BSA), der von den kollektiven Schwarminteraktionen der Vögel in der Natur inspiriert ist. Die unterschiedlichen Suchstrategien der BSA-Individuen, einschließlich des Wechsels zwischen Flucht-, Wachsamkeits- und Futtersuchverhalten, machen diesen Algorithmus vielschichtig. Es nutzt die Prinzipien der Vogelschwärme, der Kommunikation, der Anpassungsfähigkeit, des Führens und Folgens, um effizient optimale Lösungen zu finden.