MQL-Programmiererin Anja Vogel

MQL5 Indikator

Balance und EA Monitor

Performance auf einen Blick

Balance und EA Monitor als Indikator für MT5

Chart mit AV_Grid und AV_Monitor Indikatoren
Chart mit den Indikatoren AV_Grid oben und AV_Monitor unten

Ich vermisse meinen "Equity Chart", den ich im MT4 im Einsatz hatte s.u. Zuerst wollte ich diesen von MQL4 auf MQL5 übertragen, was mir zwar grundsätzlich auch gelungen ist, jedoch zeigt er keine Kurven an. Nach langer Suche, habe ich beschlossen, meinen eigenen Monitor zu schreiben, schließlich lerne ich gerade MQL5.

Während ich die Balance Kurve schnell hatte, ist die Equity Kurve schwieriger, als gedacht, weshalb ich diese erst mal weg lasse. Den Indikator habe ich für mich selbst geschrieben, wie auch den MQL Grid Indikator. Ich gehe aber davon aus, dass sich der Monitor noch mehr ändern wird, da er noch jünger ist.

Inzwischen gibt es eine Legende und einen Screenshot zur Verdeutlichung der Nutzung mit Filterung nach EA mit und ohne Magic auf dem selben Symbol und rechts daneben DAX mit Balance und Equity Linien des gesamten Accounts. Die Prozentwerte in der Legende beziehen sich beide auf die Start-Balance. Die unteren Fenster lassen sich in der Höhe verändern, aber das kennt ihr ja sicher. Die von den EA-Auswertungen würde ich verkleinern und die Gesamtauswertung vergrößern:

Chart mit AV_Grid und AV_Monitor Indikatoren
AV_Monitor inkl. Legende mit Filterung nach Magic und Symbol und rechts ohne Filterung

Einstellung

Wie üblich bei Indikatoren, sind die Farben und Linienarten einstellbar. Daneben kann man sich entscheiden, ob man die Short- und Longpositionen getrennt in je einer Kurve anzeigen lassen möchte oder nicht, sowie die Balance-Kurve zusammen mit zwei Linien für die aktuelle Equity und die aktuelle Balance sehen möchte oder nicht.

Möchte man lieber eine gleichbleibende Höhe im Chart, kann man die Null-Linie anzeigen lassen. Wie im Original gibt es ein Startdatum, wobei ich auch noch einen Startwert für die Balance benutze. Die Linie sieht dadurch schöner aus und ich starte von dort aus auch die Short- und Longkurven.

Weitere nützliche Filter sind eine Magic und ein Symbol bzw. ein Instrument. Beide können gleichzeitig benutzt werden und die "0" als Magic filtert EAs ohne Nutzung der Magic und händische Trades. Bleibt dieses Feld leer, werden alle angezeigt.

Damit kann man seine laufenden EAs bestens mit den Augen bewerten, was ich sehr mag. Auf einem separaten Chart kann man z.B. unter den DAX mit dem Indikator ohne Filter dann die gesamte Entwicklung des Kontos betrachten (siehe Screenshot oben).

Chart mit AV_Grid und AV_Monitor Indikatoren
AV_Monitor mit Filterung nur nach Symbol "GBPUSD", noch ohne Legende

AV_Monitor.mq5

Download Indikator AV_Monitor.mq5 für MT5. Hier folgt der Quellcode:

//+------------------------------------------------------------------+
//|                                                   AV_Monitor.mq5 |
//|                                       Copyright 2022, Anja Vogel |
//|                                        https://www.anjavogel.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2022, Anja Vogel"
#property link      "https://www.anjavogel.com"
#define VERSION "1.05"
#property version VERSION
string indicatorName = "AV Monitor V" + VERSION;
string indicatorShortname = "Monitor V" + VERSION;
#property description "Ansicht:"
#property description "Die Balance Linie startet rechts im Chart bei der aktuellen Balance."
#property description "Die Long- und Shortpositionen starten dagegen links im Chart bei der Start-Balance."
#property description "Die Prozentwerte in der Legende beziehen sich ebenso auf die Start-Balance."
#property description " "
#property description "Filterung:"
#property description "Benutze 0 im Feld Magic zur Filterung von EAs ohne Magic und von händischen Trades."
#property description "Lasse das Feld leer um alle anzuzeigen."

#property indicator_separate_window

// Diese Level werden überschrieben:
#property indicator_level1 0 // Null-Linie
#property indicator_level2 0 // StopOutLevel
#property indicator_level3 0 // MarginCall
#property indicator_level4 0 // Start-Balance

#property indicator_buffers 6
#property indicator_plots   6
// 1. Farbchema: clrYellow, clrYellow, clrDodgerBlue, clrMediumSeaGreen, clrRed, clrChocolate
// 2. Farbchema: clrKhaki, clrKhaki, clrLightSkyBlue, clrDarkSeaGreen, clrLightCoral, clrRosyBrown
#property indicator_label1  "Balance"
#property indicator_type1   DRAW_LINE
#property indicator_color1  clrKhaki
#property indicator_style1  STYLE_SOLID
#property indicator_width1  3
#property indicator_label2  "Aktuelle Balance"
#property indicator_type2   DRAW_LINE
#property indicator_color2  clrKhaki
#property indicator_style2  STYLE_DASH
#property indicator_width2  1
#property indicator_label3  "Aktuelle Equity"
#property indicator_type3   DRAW_LINE
#property indicator_color3  clrLightSkyBlue
#property indicator_style3  STYLE_DASH
#property indicator_width3  1
#property indicator_label4  "Long-Positionen"
#property indicator_type4   DRAW_LINE
#property indicator_color4  clrDarkSeaGreen
#property indicator_style4  STYLE_DOT
#property indicator_width4  2
#property indicator_label5  "Short-Positionen"
#property indicator_type5   DRAW_LINE
#property indicator_color5  clrLightCoral
#property indicator_style5  STYLE_DOT
#property indicator_width5  2
#property indicator_label6  "Aktuelle Margin"
#property indicator_type6   DRAW_LINE
#property indicator_color6  clrRosyBrown
#property indicator_style6  STYLE_DASH
#property indicator_width6  1

input group    "Ansichts-Einstellungen"
input double   Begin_Balance = 10000.0;      // Start-Balance
input bool     Show_BalanceAndEquity = true; // Balance & Equity
input bool     Show_BuyAndSell = true;       // Short- & Longpositionen
input bool     With_Swap = true;             // Kurven inkl. Swap
input bool     Show_Zero = false;            // Null-Linie

input group    " "
input group    "Legende"
input color    Text_Neutral = clrDarkGray;               // Schriftfarbe
input int      Text_Size = 11;                           // Schriftgröße
input ENUM_BASE_CORNER Text_Corner = CORNER_RIGHT_LOWER; // Positionierung
input int      Text_Position = 250;                      // Abstand (15 links, 250 rechts)

input group    " "
input group    "Filter-Einstellungen"
input datetime Draw_Begin = D'2022.10.13 00:00'; // Start-Datum
input string   Only_Symbol = "";                 // Nur folgendes Symbol
input string   Only_Magic = "";                  // Nur folgende Magic

input group    " "
input group    "Entwicklung / Logging"
input bool develop = false; // Entwickler

double Balance[];
double currentBalance[];
double currentEquity[];
double buyBalance[];
double sellBalance[];
double currentMargin[];

double lastChartMax = 0;
double lastChartMin = 0;

//+------------------------------------------------------------------+
// OnInit()
//+------------------------------------------------------------------+
int OnInit()
{
   IndicatorSetString(INDICATOR_SHORTNAME, indicatorShortname);
   IndicatorSetInteger(INDICATOR_DIGITS, 2);

   // Buffer
   SetIndexBuffer(0, Balance, INDICATOR_CALCULATIONS);
   SetIndexBuffer(1, currentBalance, INDICATOR_CALCULATIONS);
   SetIndexBuffer(2, currentEquity, INDICATOR_CALCULATIONS);
   SetIndexBuffer(3, buyBalance, INDICATOR_CALCULATIONS);
   SetIndexBuffer(4, sellBalance, INDICATOR_CALCULATIONS);
   SetIndexBuffer(5, currentMargin, INDICATOR_CALCULATIONS);

   // Level
   IndicatorSetString(INDICATOR_LEVELTEXT, 0, "Zero");

   /*if (AccountInfoInteger(ACCOUNT_MARGIN_SO_MODE) == ACCOUNT_STOPOUT_MODE_MONEY) {
      IndicatorSetString(INDICATOR_LEVELTEXT, 1, "Stop Out: " + AccountInfoDouble(ACCOUNT_MARGIN_SO_SO));
      IndicatorSetDouble(INDICATOR_LEVELVALUE,1, AccountInfoDouble(ACCOUNT_MARGIN_SO_SO));
   }
   if (AccountInfoInteger(ACCOUNT_MARGIN_SO_MODE) == ACCOUNT_STOPOUT_MODE_PERCENT) {
      IndicatorSetString(INDICATOR_LEVELTEXT, 1, "Stop Out: " + AccountInfoDouble(ACCOUNT_MARGIN_SO_SO)*AccountInfoDouble(ACCOUNT_MARGIN)/100);
      IndicatorSetDouble(INDICATOR_LEVELVALUE,1, AccountInfoDouble(ACCOUNT_MARGIN_SO_SO)*AccountInfoDouble(ACCOUNT_MARGIN)/100);
   }

   IndicatorSetString(INDICATOR_LEVELTEXT, 2, "Margin Call: " + AccountInfoDouble(ACCOUNT_MARGIN_SO_CALL));
   IndicatorSetDouble(INDICATOR_LEVELVALUE,2, AccountInfoDouble(ACCOUNT_MARGIN_SO_CALL));
   */
   if (Begin_Balance > 0) {
      IndicatorSetString(INDICATOR_LEVELTEXT, 3, "Start: " + TimeToString(Draw_Begin,TIME_DATE));
      IndicatorSetDouble(INDICATOR_LEVELVALUE,3, Begin_Balance);
   }

   return(INIT_SUCCEEDED);
}

//+------------------------------------------------------------------+
// OnDeinit()
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
   ObjectDelete(0,"Balance");
   ObjectDelete(0,"currentBalance");
   ObjectDelete(0,"currentEquity");
   ObjectDelete(0,"buyBalance");
   ObjectDelete(0,"sellBalance");
   ObjectDelete(0,"currentMargin");
   ObjectDelete(0,"Begin_Balance");
   ObjectDelete(0,"Only_Symbol");
   ObjectDelete(0,"Only_Magic");
}

//+------------------------------------------------------------------+
// OnTradeTransaction()
//+------------------------------------------------------------------+
void OnTradeTransaction(const MqlTradeTransaction &trans,
                        const MqlTradeRequest &request,
                        const MqlTradeResult &result)
{
   // 0,2,3,6,10    0,9,6,2,3
   //Print("OnTradeTransaction() --------------------- ", trans.type);
}
//+------------------------------------------------------------------+
// OnCalculate()
//+------------------------------------------------------------------+
int OnCalculate(const int rates_total,
                const int prev_calculated,
                const datetime &time[],
                const double &open[],
                const double &high[],
                const double &low[],
                const double &close[],
                const long &tick_volume[],
                const long &volume[],
                const int &spread[])
{
   ArraySetAsSeries(time, true);
   ArraySetAsSeries(open, true);
   ArraySetAsSeries(close, true);
   ArraySetAsSeries(Balance, true);
   ArraySetAsSeries(currentBalance, true);
   ArraySetAsSeries(currentEquity, true);
   ArraySetAsSeries(buyBalance, true);
   ArraySetAsSeries(sellBalance, true);
   ArraySetAsSeries(currentMargin, true);


   datetime start = TimeCurrent(); datetime end = TimeCurrent();
   double result = 0.0; double resultBuys = 0.0; double resultSells = 0.0;
   string symbol = NULL;
   int lastValue = 0; // Für Addition
   bool hasDeals = false;

   Balance[0]        = Show_BalanceAndEquity ? AccountInfoDouble(ACCOUNT_BALANCE) : Begin_Balance;
   currentBalance[0] = Show_BalanceAndEquity ? AccountInfoDouble(ACCOUNT_BALANCE) : Begin_Balance;
   currentEquity[0]  = Show_BalanceAndEquity ? AccountInfoDouble(ACCOUNT_EQUITY)  : Begin_Balance;
   currentMargin[0]  = Show_BalanceAndEquity ? AccountInfoDouble(ACCOUNT_MARGIN)  : Begin_Balance;

   double max = MathMax(Balance[0], currentEquity[0]);
   double min = MathMin(Balance[0], currentEquity[0]);
   if (Show_Zero) min = MathMin(min, 0.0);


   int periods = rates_total;

   // Berechnung der Balance anhand der Deals (von rechts nach links)
   for (int i=1; i < periods; i++) { // Perioden

      // Vorbelegung
      if (Show_BalanceAndEquity) {
         currentBalance[i] = AccountInfoDouble(ACCOUNT_BALANCE);
         currentEquity[i]  = AccountInfoDouble(ACCOUNT_EQUITY);
         currentMargin[i]  = AccountInfoDouble(ACCOUNT_MARGIN);
      }

      end = time[i-1]; // 0
      start = time[i]; // 1

      // Vorbelegung
      if (start < (int)Draw_Begin) {
         Balance[i] = Begin_Balance;
         // Min/Max für Fensteroptimierung
         min = MathMin(min, Balance[i]);
         max = MathMax(max, Balance[i]);
         continue;
      }

      HistorySelect(start,end); // Deals für Zeitraum in den Cache laden
      int historyDeals = HistoryDealsTotal();

      for (int j=0; j < historyDeals; j++) { // Deals in der Periode
         ulong ticket = HistoryDealGetTicket(j);

         if (ticket > 0) {
            if (!FilterDeal(ticket)) continue;

            // Bugfix:
            if (HistoryDealGetInteger(ticket, DEAL_TIME) >= start && HistoryDealGetInteger(ticket, DEAL_TIME) < end) {
               hasDeals = true;

               result += HistoryDealGetDouble(ticket, DEAL_PROFIT);
               if (With_Swap) result += HistoryDealGetDouble(ticket, DEAL_SWAP);

               if (HistoryDealGetInteger(ticket, DEAL_ENTRY) == DEAL_ENTRY_OUT) {

                  // bei DEAL_ENTRY_OUT alles umgekehrt:
                  if (HistoryDealGetInteger(ticket, DEAL_TYPE) == DEAL_TYPE_SELL) {
                     resultBuys += HistoryDealGetDouble(ticket, DEAL_PROFIT);
                     if (With_Swap) resultBuys += HistoryDealGetDouble(ticket, DEAL_SWAP);
                  }
                  else if (HistoryDealGetInteger(ticket, DEAL_TYPE) == DEAL_TYPE_BUY) {
                     resultSells += HistoryDealGetDouble(ticket, DEAL_PROFIT);
                     if (With_Swap) resultSells += HistoryDealGetDouble(ticket, DEAL_SWAP);
                  }
               }
            }
         }
      }

      // den letzten Wert merken für die Addition unten
      if (result != 0.0) lastValue = i;

      // Liniendaten befüllen
      Balance[i]     = Balance[i-1] - result;
      buyBalance[i]  = NormalizeDouble(resultBuys,2);
      sellBalance[i] = NormalizeDouble(resultSells,2);

      // Min/Max für Fensteroptimierung
      if (Show_BalanceAndEquity) {
         min = MathMin(min, Balance[i]);
         max = MathMax(max, Balance[i]);
      }

      // Werte zurück setzen
      result = 0.0;
      resultBuys = 0.0;
      resultSells = 0.0;
   }


   // Addition der reinen Sells und Buys (von links nach rechts)
   if (Show_BuyAndSell) {

      // Linie ab lastValue in Vergangenheit zeichnen
      for (int l=lastValue; l < periods; l++) {
         buyBalance[l]  = Begin_Balance;
         sellBalance[l] = Begin_Balance;
      }

      // Linie ab lastValue in Zukunft zeichnen
      for (int k = lastValue; k > 1; k--) {
         buyBalance[k-1]  += NormalizeDouble(buyBalance[k],2);
         sellBalance[k-1] += NormalizeDouble(sellBalance[k],2);

         // Min/Max für Fensteroptimierung
         min = MathMin(min, buyBalance[k-1]);
         max = MathMax(max, buyBalance[k-1]);
         min = MathMin(min, sellBalance[k-1]);
         max = MathMax(max, sellBalance[k-1]);
      }
      // aktueller Wert bleibt auf dem letztem Wert (und nicht auf Balance zurück)
      buyBalance[0]  = NormalizeDouble(buyBalance[1],2);
      sellBalance[0] = NormalizeDouble(sellBalance[1],2);
   }


   // Fensteroptimierung
   if (min == max) {
      max *= 1.1;
      min *= 0.9;
   }
   if (hasDeals) {
      if (lastChartMax != (int)MathCeil(max + 0.16*(max-min))) {
         lastChartMax = (int)MathCeil(max + 0.16*(max-min));
         IndicatorSetDouble(INDICATOR_MAXIMUM, lastChartMax);
      }
      if (lastChartMin != (int)MathFloor(min - 0.16*(max-min))) {
         lastChartMin = (int)MathFloor(Show_Zero ? 0 : min - 0.16*(max-min));
         IndicatorSetDouble(INDICATOR_MINIMUM, lastChartMin);
      }
   } else {
      if (lastChartMax != (int)MathCeil(Begin_Balance*1.5)) {
         lastChartMax = (int)MathCeil(Begin_Balance*1.5);
         IndicatorSetDouble(INDICATOR_MAXIMUM, lastChartMax);
      }

      if (lastChartMin != (int)MathFloor(Show_Zero ? Show_Zero : Begin_Balance/2)) {
         lastChartMin = (int)MathFloor(Show_Zero ? 0 : Begin_Balance/2);
         IndicatorSetDouble(INDICATOR_MINIMUM, lastChartMin);
      }
   }


   // Legende
   string text = "";
   int x = Text_Position;
   int y = 0, add = 0;

   switch (Text_Corner) {
      case CORNER_RIGHT_UPPER:
      case CORNER_LEFT_UPPER:
         y = 10;
         add = 20;
         break;
      case CORNER_LEFT_LOWER:
      case CORNER_RIGHT_LOWER:
         y = 30;
         add = -20;
         y += Show_BalanceAndEquity ? 4*MathAbs(add) : 0;
         y += Show_BuyAndSell ? 2*MathAbs(add) : 0;
         y += Only_Magic != "" ? 1*MathAbs(add) : 0;
         y += Only_Symbol != "" ? 1*MathAbs(add) : 0;
         break;
   }

   if (Only_Symbol != "") {
      text = "Symbol " + Only_Symbol;
      DrawLabel("Only_Symbol",x,y+=add, text, develop ? clrOrange : Text_Neutral);
   }
   if (Only_Magic != "") {
      text = "Magic " + Only_Magic;
      DrawLabel("Only_Magic",x,y+=add, text, develop ? clrOrange : Text_Neutral);
   }

   if (Show_BalanceAndEquity) {
      text = indicator_label1 + " " + (string)NormalizeDouble(AccountInfoDouble(ACCOUNT_BALANCE),2);
      DrawLabel("Balance",x,y+=add,text, develop ? clrOrange : indicator_color1);

      text = indicator_label2 + " " + (string)NormalizeDouble(AccountInfoDouble(ACCOUNT_BALANCE)/Begin_Balance*100,1) + " %";
      DrawLabel("currentBalance",x,y+=add,text, develop ? clrOrange : indicator_color2);

      text = indicator_label3  + " " + (string)NormalizeDouble(AccountInfoDouble(ACCOUNT_EQUITY)/Begin_Balance*100,1) + " %";
      DrawLabel("currentEquity",x,y+=add,text, develop ? clrOrange : indicator_color3);

      text = indicator_label6  + " " + (string)NormalizeDouble(AccountInfoDouble(ACCOUNT_MARGIN),2);
      DrawLabel("currentMargin",x,y+=add,text, develop ? clrOrange : indicator_color6);
   } else {
      ObjectDelete(0,"Balance");
      ObjectDelete(0,"currentBalance");
      ObjectDelete(0,"currentEquity");
      ObjectDelete(0,"currentMargin");
   }
   if (Show_BuyAndSell) {
      text = indicator_label4 + " " + (string)NormalizeDouble(buyBalance[0], 2);
      DrawLabel("buyBalance",x,y+=add, text, develop ? clrOrange : indicator_color4);

      text = indicator_label5 + " " + (string)NormalizeDouble(sellBalance[0], 2);
      DrawLabel("sellBalance",x,y+=add, text, develop ? clrOrange : indicator_color5);
   } else {
      ObjectDelete(0,"buyBalance");
      ObjectDelete(0,"sellBalance");
   }


   // Daten entfernen, falls sie nicht angezeigt werden sollen
   if (!Show_BalanceAndEquity) {
      ArrayFill(Balance,0,ArraySize(Balance),EMPTY_VALUE);
      ArrayFill(currentBalance,0,ArraySize(currentBalance),EMPTY_VALUE);
      ArrayFill(currentEquity,0,ArraySize(currentEquity),EMPTY_VALUE);
      ArrayFill(currentMargin,0,ArraySize(currentMargin),EMPTY_VALUE);
   }
   if (!Show_BuyAndSell) {
      ArrayFill(buyBalance,0,ArraySize(buyBalance),EMPTY_VALUE);
      ArrayFill(sellBalance,0,ArraySize(sellBalance),EMPTY_VALUE);
   }

   return(rates_total);
}

//+------------------------------------------------------------------+
// Filter für die Deals
//+------------------------------------------------------------------+
bool FilterDeal(ulong ticket)
{
   //if (!HistoryDealGetDouble(ticket, DEAL_PRICE)) return(false);
   if (!HistoryDealGetDouble(ticket, DEAL_PROFIT)) return(false);

   if (Only_Magic != "") if (StringFind(Only_Magic, (string)HistoryDealGetInteger(ticket, DEAL_MAGIC)) == -1) return(false);
   if (Only_Symbol != "") if (StringFind(Only_Symbol, HistoryDealGetString(ticket, DEAL_SYMBOL)) == -1) return(false);

   // aus Original übernommen - unsicher ob das stimmt:
   //if (HistoryDealGetInteger(ticket, DEAL_TYPE) > 5) return(true);  // ab 5, z.B. 7 = DEAL_TYPE_COMMISSION
   //if (HistoryDealGetInteger(ticket, DEAL_TYPE) == DEAL_TYPE_BALANCE) return(false); // 0 = DEAL_TYPE_BUY & DEAL_TYPE_SELL (2 = DEAL_TYPE_BALANCE)

   return(true);
}

//+------------------------------------------------------------------+
// Zeichne Label bzw. Legende
//+------------------------------------------------------------------+
bool DrawLabel(string name = "Label",
               int    x = 0,
               int    y = 0,
               string text = "Label",
               color  clr = clrGainsboro) // clrGray
{
   // Label bereits vorhanden?
   if (ObjectFind(0, name) < 0) {

      // erstellen
      if (!ObjectCreate(0, name, OBJ_LABEL, ChartWindowFind(), 0, 0)) {
         return(false);
      }
      ObjectSetInteger(0,name,OBJPROP_XDISTANCE,x);
      ObjectSetInteger(0,name,OBJPROP_YDISTANCE,y);
      ObjectSetInteger(0,name,OBJPROP_ANCHOR,ANCHOR_LEFT_UPPER);
      ObjectSetString(0,name,OBJPROP_FONT,"Lucida Console");
      ObjectSetInteger(0,name,OBJPROP_BACK,false);
      ObjectSetInteger(0,name,OBJPROP_SELECTABLE,true);
      ObjectSetInteger(0,name,OBJPROP_CORNER,Text_Corner);
      ObjectSetInteger(0,name,OBJPROP_FONTSIZE,Text_Size);
      ObjectSetInteger(0,name,OBJPROP_HIDDEN,!develop);
   }

   // aktualisieren
   ObjectSetString(0,name,OBJPROP_TEXT,text);
   ObjectSetInteger(0,name,OBJPROP_COLOR,clr);

   return(true);
}

//+------------------------------------------------------------------+
// Logge Information, nur wenn eingeschaltet (gut bei Nutzung in mehreren Charts)
//+------------------------------------------------------------------+
void out(string text)
{
   if (develop) Print(_Symbol, " ----------- ", text, " ----------- ");
}
//+------------------------------------------------------------------+

Externe Links

Bevor ich meinen eigenen MQL5 Indikator zur Beobachtung der Balance und der Profitabilität von laufenden EAs programmiert habe, war der folgende bei mir im MT4 im Einsatz:

MQL4 Equity Chart Indikator

Möchtest du MQL programmieren lernen? Dieser Kurs macht wirklich Spaß und kostet nur einen Euro. Mein Prädikat: ausgezeichnet und auch nicht langweilig mit Vorwissen:

MQL5 Einsteigerkurs
MQL4 Einsteigerkurs