MQL-Programmiererin Anja Vogel

MQL5 ForexSetEA

Mehrere Währungspaare

mit einem EA gleichzeitig handeln

MQL5 Studienprojekt: Forex-Set-EA

Es handelt sich bei diesem MQL5 Expert Advisor um ein Trendfolge-System. Der Trend wird durch den Swap des Brokers vorgegeben oder könnte in der Funktion CheckTrendSwap() auch händisch festgelegt werden. Derzeit gibt es dort einen Backup-Bereich, falls keine Daten geliefert werden. Es ist eine Idee von mir, wie sich das in Realität verhält, weiß ich jedoch nicht. Aktuell habe ich die Demo-Phase mit allen 18 Währungspaaren gestartet (22.11.2022).

Er wurde von mir als Studienprojekt erstellt um

  • mich mit MQL5 zu beschäftigen
  • einen EA zu bauen, der mehrere Währungspaare handeln kann
  • mich mit dem Thema Stabilität auseinander zu setzen
  • übliche Einstiege mit Zufalls-Einstiegen zu vergleichen

Langfristig über zwei Jahre

Hätte man den EA nach 2 Jahren beendet, wäre das Ergebnis bei dem Ende der blauen Linie rechts unten bei 9.067,10 gewesen und das bedeutet bei dem Startkapital von 5.000,- knapp 35% im Jahr. Dabei wären pro Monat im Schnitt 150 Trades gemacht worden und pro Jahr im Schnitt 1.800 Trades. 35% pro Jahr bedeutet übrigens nur ca. 2,5% pro Monat durch den Zinseszinseffekt.
Meine Backtests sind auf einem Demo-Konto mit Hebel 1:100 entstanden.

AV_ForexSetEA V1.02: 2 Jahre backtest mit USDJPY und GBPUSD, Balance und Equity Verlauf
ForexSetEA (USDJPY/GBPUSD) vom 11.11.2020 - 11.11.2022, 2 Jahre
AV_ForexSetEA V1.02: 2 Jahre backtest mit USDJPY und GBPUSD, Kennzahlen
ForexSetEA (USDJPY/GBPUSD) vom 11.11.2020 - 11.11.2022, 2 Jahre

Einstieg

Für einen Einstieg muss der Trend des Tages nach 3:00 Uhr Serverzeit, sowie der Trend des Monats in gleicher Richtung verlaufen, wie vom Swap vorgegeben. Darüber hinaus ist der erste Einstieg zufällig und die folgenden werden eröffnet, wenn der offene Profit in dieser Richtung positiv ist. Der EA eröffnet dadurch in der Regel sofort Positionen und handelt fast ohne Pause.

Benutze StoplossPunkte um einen Stoploss zu setzen. Im Original arbeitet er ohne StopLoss, ganz einfach weil ich keine Einstellung dafür gefunden habe, die Sinn ergibt. Unten dazu mehr. Statt dessen wird eine Position nur geöffnet, wenn mindestens reservedEquity Moneten pro Position noch auf dem Konto verfügbar sind. Diese muss dann reichen, einen egal wie tiefen, Drawdown auszuhalten.

Bedingungen

In jedem OnTick() also jeder Preisänderung wird für jedes Symbol einmal entschieden:

  • Sind noch Positionen für dieses Symbol und sind noch Positionen nach reservedEquity erlaubt?
  • Läßt der ATR-Filter Positionen zu, also ist die Volatilität auf dem Symbol nicht zu hoch?
  • 1) LONG:
    • Ist der Profit in Buy-Richtung positiv? Oder gibt es noch gar keine Positionen?
    • Ist es nach 3:00 Uhr?
    • Geht der Trend heute hoch und geht er für diesen Monat hoch?
    • Ist der Long-Swap positiv und wenn das auch für den Short-Swap gilt, ist es der höhere Wert?
    • Was entscheidet der Zufall? Long, short oder gar nicht einsteigen?
    • Bis hier her gekommen? Dann gehe mit einer Position 0.01 Lot long!
  • 2) SHORT:
    • Ist der Profit in Short-Richtung positiv? Oder gibt es noch gar keine Positionen?
    • Ist es nach 3:00 Uhr?
    • Geht der Trend heute runter und geht er für diesen Monat runter?
    • Ist der Short-Swap positiv und wenn das auch für den Long-Swap gilt, ist es der höhere Wert?
    • Was entscheidet der Zufall? Long, short oder gar nicht einsteigen?
    • Bis hier her gekommen? Dann gehe mit einer Position 0.01 Lot short!
  • 3) Mache mit dem nächsten Symbol aus dem Forex-Set weiter
Es wird also pro OnTick()-Durchlauf und pro Symbol max. eine Long- oder eine Short-Positon geöffnet.

Curve fitting

Der Parameter strategietester hat keinerlei Auswirkung und wird von mir genutzt, um mehrfach Durchläufe zu generieren, um dem gefürchteten "Curve fitting" etwas entgegen zu setzen, siehe Screenshot mit 10 Durchläufen:

AV_ForexSetEA V1.02: 2 Jahre backtest mit USDJPY und GBPUSD, 10 Durchläufe
ForexSetEA (USDJPY/GBPUSD) vom 11.11.2020 - 11.11.2022, 2 Jahre in 10 Durchläufen

Drawdowns

Gegenmaßnahmen gegen heftige Drawdowns sind, dass

  • mehrere Währungspaare gehandelt werden können (Diversifikation)
  • jedes Währungspaar nur "seinen" Anteil am Kontovermögen nutzen darf
  • ein ATR-Filter dafür sorgt, nicht an den Extremen einzusteigen
  • min. 1.000 Trade bewerten werden, im Screenshot jeweils über 3.000 Trades

Volatilität durch News ist für dieses System kein Problem. Wenn eine Position bereits geöffnet ist, wird durch die Ausschläge nach oben und unten der TP erreicht und durch den ATR Filter wird nicht eingestiegen, bis es wieder ruhiger geworden ist.

Wichtig ist, dass dieser EA davon ausgeht, das gesamte Konto zur Verfügung zu haben. Laufen also noch andere EAs oder händische Trades, sollte man die reservedEquity höher einstellen, damit weniger Trades geöffnet werden.

Wichtig ist auch, dass dieser EA so neu ist, dass er noch nicht mal auf Demo gelaufen ist! Er dient daher zu Studienzwecken, z.B. zum MQL-Programmieren-Lernen. In der Funktion CheckTrend() kannst du deinen eigenen Einstieg programmieren, wobei die Rückgabewerte folgendes bedeuten:

  • return +1 = kaufe
  • return 0 = handle nicht
  • return -1 = verkaufe

Problematisch in diesem System ist, falls der Kurs für die nächsten Monate / Jahre dreht und nie wieder zurück kommt. Dafür habe ich bisher keine Lösung!

Forex-Set Beispiele

string forex[] = { "AUDCHF", "AUDJPY", "AUDUSD", "CADCHF", "CHFJPY", "EURAUD",
                   "EURCAD", "EURCHF", "EURGBP", "EURJPY", "EURNZD", "EURUSD",
                   "GBPCHF", "GBPJPY", "GBPUSD", "USDCAD", "USDCHF", "USDJPY" };

string forex[] = { "USDJPY", "GBPUSD", "EURUSD", "EURJPY", "AUDUSD" };

string forex[] = { _Symbol, "USDJPY", "GBPUSD", "-" };

string forex[] = { _Symbol }; // Einzeltests oder Market scanner

Relativ weit oben im EA kann man die Zeile //string forex[] = { _Symbol }; auskommtieren, um zum Beispiel Einzeltests oder Market scanner Test zu machen. Nur eine von diesen Beispiel-Zeilen darf auskommentiert sein, vor den anderen muss // oder /* */ drum herum stehen.

Das Array { ... } bzw. das Set kann erweitert oder gekürzt werden und mit _Symbol kann das aktuelle Währungspaar eingefügt werden, welches ansonsten ignoriert wird und nur die Währungspaare im Set gehandelt werden.

In der OnInit-Funktion habe ich mich für die Einschränkung auf Kombinationen mit den Hauptwährungen entschieden. Falls nichts passiert könnte es daher an dem aktuellen Symbol liegen. Des Weiteren versteht das Array "-" als gestrichenen Wert, der ignoriert wird, bzw. alles was nicht 6 Zeichen lang ist.

Kurzfristig für 1 Monat

Da man im langfristigen Chart von 2 Jahren am Ende einen starken Drawdown sieht, habe ich hier nochmal einen Screenshot für einen Monat gemacht. Man sieht hier, selbst wenn man ihn nach einem Monat beendet hätte, würde ein realisierbarer Profit von 4,2% anfallen. 4% im Monat sind übrigens 60% im Jahr, wenn das Kapital auf dem Konto bleibt, was man Zinseszins nennt. 12 x 4% = 48% kann man nur rechnen, wenn die Gewinne monatlich abgeschöpft werden.

AV_ForexSetEA V1.02: 1 Monat backtest mit USDJPY und GBPUSD, Balance und Equity Verlauf
ForexSetEA (USDJPY/GBPUSD) 11.10.2022 - 11.11.2022, 1 Monat
AV_ForexSetEA V1.02: 1 Monat backtest mit USDJPY und GBPUSD, Kennzahlen
ForexSetEA (USDJPY/GBPUSD) 11.10.2022 - 11.11.2022, 1 Monat

Swap

Da ich neulich keine Swap-Daten bekommen habe und dies bedeuten würde, das der EA weder handelt, noch der Strategietester laufen würde, gibt es einen Bereich in der CheckTrendSwap() Funktion, den man nutzen kann, um die Richtung für das jeweilige Paar festzulegen. Gibt es Swap-Werte vom Broker, werden diese ausgewertet, wird nur 0.0 vom Broker geliefert, wird der im Quellcode festgelegte Wert verwendet:

  • return +1; = nur Long-Positionen
  • return 0; = handle nicht
  • return -1; = nur Short-Positionen
  • return (MathRand() % 3) - 1; = keine Trendvorgabe

Periodenwechsel

Ich teste den EA in M1 und schreibe die verwendete Periode auch fest in den Quellcode, damit es nicht bei Wechsel der Chartansicht zu anderen Ergebnissen kommt. Diese Fehlerquelle möchte ich ausschließen.

Stoploss

Lange Zeit habe ich die Ansicht vertreten, dass es gefährlich ist ohne SL in den Markt zu gehen. Hier werden jedoch vergleichsweise kleine Positionen geöffnet. Vorgesehen von mir ist ein Konto von 1.000,-, welches maximal 1.000 / 200 = 5 Positionen à 0,01 Lot eröffnen und das Risiko auf bis zu 5 Währungspaare verteilen würde. Geht man damit wirklich mehr Risiko ein, als auf einem Konto mit 100.000,- auf dem ein 1% Trade geöffnet wird? So oder so muss der EA sich zuerst noch auf dem Demokonto bewähren.

Stoploss Durchläufe mit SL = 0 bis 1.000 in 100er Schritten:

AV_ForexSetEA V1.02: 1 Monat backtest mit USDJPY und GBPUSD, Stoploss
ForexSetEA (USDJPY/GBPUSD) 11.10.2022 - 11.11.2022, 1 Monat mit Stoploss von 0 bis 1.000
AV_ForexSetEA V1.02: 2 Jahre backtest mit USDJPY und GBPUSD, Stoploss
ForexSetEA (USDJPY/GBPUSD) 11.11.2020 - 11.11.2022, 2 Jahre mit Stoploss von 0 bis 1.000

Risiko

Das Risiko ist, dass auch trendige Währungspaare irgendwann einmal ihre Richtung ändern können. Ob der Swap dies rechtzeitig abfangen kann, weiß ich nicht. Es ist derzeit nur eine Hoffnung.

Bezüglich des Stoploss sieht man im kurzfristigen Screenshot oben, dass ein SL bei 300 bis 700 Punkten das Ergebnis erheblich verbessert hätte. Ein Unterschied von bis zu 911,- zwischen SL bei 500 und keinem Stoploss. Schaut man auf den langfristigen Screenshot darunter, ist jedoch der SL bei 500, neben den Vollkatastrophen bei 100 und 200, das schlechteste Ergebnis von den positiven Resultaten. Auch bitte der Spalte "Expected payoff" Beachtung schenken!

In einem Test von 20 Durchläufen mit einem SL von 900, welcher mir am sinnvollsten erscheint, habe ich ausschließlich positive Ergebnisse für 2 Jahre erhalten von ca. +500,- bis ca. +3.200,- Gewinn. Im Gegentest von 20 Durchläufen ohne SL erhalte ich ebenso ausschließlich Gewinne in einer Spanne von ca. +3.600,- bis ca. +7.000,-.

Ist es sicher, dass es zukünftig so passiert? Nein, natürlich nicht. Meine Auswahl der zwei günstigsten Währungspaare für die letzten 2 Jahre kann bereits das Problem sein!

AV_ForexSetEA V1.02: 2 Jahre backtest mit USDJPY und GBPUSD, Stoploss 900
ForexSetEA (USDJPY/GBPUSD) 11.11.2020 - 11.11.2022, 2 Jahre mit Stoploss 900
AV_ForexSetEA V1.02: 2 Jahre backtest mit USDJPY und GBPUSD, ohne Stoploss
ForexSetEA (USDJPY/GBPUSD) 11.11.2020 - 11.11.2022, 2 Jahre ohne Stoploss

Variante mit Stopploss

Der Stoploss in einer Entfernung von 900 bei einem TP von 200 ist als Sicherheitsstop anzusehen, der möglichst selten greifen sollte. Ich habe ihn so gewählt, weil er in der langfristigen Tabelle oben das niedrigste Drawdown aufweist. Hier noch zwei Screenshots zu der Stoploss = 900 Variante, um zu zeigen, dass der EA mit jedem Lauf die Einstiege zufällig anders setzt und sich das auch erheblich und mehr auswirkt, als ich selbst erwartet hatte, was der Stabilität zugutekommt:

AV_ForexSetEA V1.02: 2 Jahre backtest mit USDJPY und GBPUSD, Stoploss 900
ForexSetEA (USDJPY/GBPUSD) 11.11.2020 - 11.11.2022, 2 Jahre mit Stoploss 900
AV_ForexSetEA V1.02: 2 Jahre backtest mit USDJPY und GBPUSD, Stoploss 900
ForexSetEA (USDJPY/GBPUSD) 11.11.2020 - 11.11.2022, 2 Jahre mit Stoploss 900

Der Start eines EAs kann ungünstig ausfallen. Hätte man ihn vor einem Monat aufgesetzt, wäre es ganz sicher erst mal erheblich nach unten gegangen:

AV_ForexSetEA V1.02: 1 Monat mit USDJPY und GBPUSD
ForexSetEA (USDJPY/GBPUSD) 11.11.2022 - 11.11.2022, 1 Monat in 10 Durchläufen

Download

MT5 Download AV_ForexSetEA.mq5 Hier folgt der Quellcode:

//+------------------------------------------------------------------+
//|                                                AV_ForexSetEA.mq5 |
//|                                       Copyright 2022, Anja Vogel |
//|                                        https://www.anjavogel.com |
//+------------------------------------------------------------------+
#property version "1.03"
#define MAGIC 20221110
//---
#define ForEachPosition(ticket,i)                                    \
   HistorySelect(0,TimeCurrent());                                   \
   ulong ticket=PositionGetTicket(0);                                \
   int po_total=PositionsTotal();                                    \
   for(int i=1;i<=po_total;i++,ticket=PositionGetTicket(i-1))
//---
#include <Trade\Trade.mqh>
#include <Trade\OrderInfo.mqh>
#include <Trade\PositionInfo.mqh>
//---
CTrade        trade;
COrderInfo    order;
CPositionInfo position;
double Ask = 0;
double Bid = 0;
//+------------------------------------------------------------------+
// Einstellungen:
//+------------------------------------------------------------------+
input int ProfitPunkte = 200;
input int StoplossPunkte = 0;
input int reservedEquity = 200;

// Im Strategietester z.B 1-10 Durchläufe stabilisiert Ergebnisse durch Zufalls-Einstieg:
input int strategietester = 1;

//+------------------------------------------------------------------+
// OnInit()
//+------------------------------------------------------------------+
int OnInit()
{
   // Hauptwährungen
   if (StringFind(_Symbol, "USD", 0) != -1 || StringFind(_Symbol, "EUR", 0) != -1
    || StringFind(_Symbol, "JPY", 0) != -1 || StringFind(_Symbol, "GBP", 0) != -1
    || StringFind(_Symbol, "CHF", 0) != -1)
   {
      return(INIT_SUCCEEDED);
   }
   return(INIT_FAILED);
}

//+------------------------------------------------------------------+
// OnTick()
//+------------------------------------------------------------------+
void OnTick()
{
   //+------------------------------------------------------------------+
   // Forex-Set Möglichkeiten: 1) "Währungspaar", 2) "-", 3) _Symbol
   //+------------------------------------------------------------------+
   string forex[] = {"AUDCHF", "AUDJPY", "AUDUSD", "CADCHF", "CHFJPY",
                     "EURAUD", "EURCAD", "EURCHF", "EURGBP", "EURJPY",
                     "EURNZD", "EURUSD", "GBPCHF", "GBPJPY", "GBPUSD",
                     "USDCAD", "USDCHF", "USDJPY" };
   //+------------------------------------------------------------------+
   //string forex[] = { "USDJPY", "GBPUSD", "EURUSD", "EURJPY", "AUDUSD" };
   //+------------------------------------------------------------------+
   //string forex[] = { "USDJPY", "GBPUSD", "-" }; // , _Symbol
   //+------------------------------------------------------------------+

   //+------------------------------------------------------------------+
   // Verwende alternativ dies für Einzeltests oder den "Market scanner":
   //+------------------------------------------------------------------+
   //string forex[] = { _Symbol };
   //+------------------------------------------------------------------+


   // Die erlaubten Positionen "max" auf die Währungspaare "symbols" aufteilen
   int symbols = 0;
   for (int i=0; i<ArraySize(forex); i++) {
      if (StringLen(forex[i]) != 6) continue; // 6 Zeichen
      symbols++;
   }

   // Konto: max erlaubte Positionen / Equity Stop
   double Balance = AccountInfoDouble(ACCOUNT_BALANCE);
   double Equity  = AccountInfoDouble(ACCOUNT_EQUITY);

   int max = (int)MathFloor(MathMin(Balance, Equity) / reservedEquity);
   int maxOnEachSymbol = (int)MathMax(1, MathRound(max/symbols));

   string currentName = "";
   int currentPos = 0;

   // Infobox
   string forexInfo1 = "";
   string forexInfo2 = "";
   string forexInfo3 = "";

   for (int i=0; i<ArraySize(forex); i++) {
      if (StringLen(forex[i]) != 6) continue; // 6 Zeichen

      if (currentName != forex[i]) {
         currentName = forex[i];

         calculatePositions(currentName);
         currentPos = openPositions;

         // Infobox
         if (StringLen(forexInfo1) < 42) forexInfo1 += (string)currentName + " ";
         else if (StringLen(forexInfo2) < 42) forexInfo2 += (string)currentName + " ";
         else forexInfo3 += (string)currentName + " ";
      }

      // Sind mehr Positionen in dem Paar erlaubt?
      if (openPositions < maxOnEachSymbol && PositionsTotal() < max) {

         // ATR-Filter verhindert Einstieg an Extremen
         if (CheckATR(currentName) > 0) {

            // Wenn es Profit in Buy Richtung, keine offenen Positionen oder Loss bei Sells gibt
            if (openBuyProfit > 0 || currentPos == 0) // || openSellProfit < 0

            // Tag und Monat nach oben, Buy-Swap positiv und Zufall nach oben
            if (CheckTrend(currentName) > 0 && CheckTrendToday(currentName) > 0 && CheckTrendSwap(currentName) > 0)
            {
               OpenBuyOrder(currentName); // Kaufe eine Long-Position
               currentPos++;
            }

            // Wenn es Profit in Sell Richtung, keine offenen Positionen oder Loss bei Buys gibt
            if (openSellProfit > 0 || currentPos == 0) //  || openBuyProfit < 0

            // Tag und Monat nach unten, Sell-Swap positiv und Zufall nach unten
            if (CheckTrend(currentName) < 0 && CheckTrendToday(currentName) < 0 && CheckTrendSwap(currentName) < 0)
            {
               OpenSellOrder(currentName); // Kaufe eine Short-Position
               currentPos++;
            }
         }
      }
   }

   calculatePositions(); // alle Positionen

   string Text[] = {
      "  ",
      "  Balance:    " + (string)NormalizeDouble(Balance,1),
      "  Equity:     " + (string)NormalizeDouble(Equity,1),
      "  Open Buy:   " + (string)NormalizeDouble(openBuyProfit,1),
      "  Open Sell:  " + (string)NormalizeDouble(openSellProfit,1),
      "  Max Konto:  " + (string)max + " Pos.",
      "  Max Symbol: " + (string)maxOnEachSymbol + " Pos.",
      "  Open:       " + (string)openPositions + " Pos.",
      " ",
      " Forex-Set enthält " + (string)symbols + " Paare:",
      " " + forexInfo1,
      " " + forexInfo2,
      " " + forexInfo3
   };

   ChartWrite("Info", Text, 15, 0);
}


//+------------------------------------------------------------------+
// Einstieg zufällig: kaufen, verkaufen, nichts tun
//+------------------------------------------------------------------+
int CheckTrend(string currentSymbol)
{
   MathSrand(GetTickCount());

   return (MathRand() % 3) - 1;
}

//+------------------------------------------------------------------+
// Filter: Tag und Monat entscheiden erlaubte Trade-Richtung
// Zeitbeschränkung ist hier auch drin
//+------------------------------------------------------------------+
int startNotBefore = 3;

double CheckTrendToday(string currentSymbol)
{
   if (Hour() < startNotBefore) return 0.0; // Zeit begrenzen

   MqlRates PriceInfo[];
   ArraySetAsSeries(PriceInfo,true);

   int Data = CopyRates(currentSymbol,PERIOD_D1,0,3,PriceInfo); // Tag
   double startToday = PriceInfo[0].open;

   Data = CopyRates(currentSymbol,PERIOD_MN1,0,3,PriceInfo); // Monat
   double startMonth = PriceInfo[0].open;

   double currentPrice = PriceInfo[0].close;

   // Tag und Monat
   if (startMonth < currentPrice && startToday < currentPrice) return 1.0;
   if (startMonth > currentPrice && startToday > currentPrice) return -1.0;

   return 0.0;
}

//+------------------------------------------------------------------+
// ATR-Filter
//+------------------------------------------------------------------+
//input double atrJPY = 0.04;

double CheckATR(string currentSymbol)
{
   double PreisArray[];
   ArraySetAsSeries(PreisArray,true);

   int handle = iATR(currentSymbol,PERIOD_M1,14);
   Sleep(100);
   CopyBuffer(handle,0,0,3,PreisArray);

   double value = NormalizeDouble(PreisArray[0], 5);

   // Außnahme für JPY
   if (StringFind(currentSymbol, "JPY", 0) != -1) {

      if (value > 0.08) return 0.0; // nicht kaufen
      //if (value < atrJPY) return 0.0; // nicht kaufen

      return 1.0; // kaufen
   }

   // Standard
   if (value > 0.00024) return 0.0; // nicht kaufen
   if (value < 0.00014) return 0.0; // nicht kaufen

   return 1.0; // kaufen
}

//+------------------------------------------------------------------+
// Swap als Filter-Richtung nutzen, wegen länger hängender Trades
//+------------------------------------------------------------------+
double CheckTrendSwap(string currentSymbol)
{
   if (SymbolInfoDouble(currentSymbol,SYMBOL_SWAP_LONG) > 0
    && SymbolInfoDouble(currentSymbol,SYMBOL_SWAP_SHORT) > 0) {

      if (SymbolInfoDouble(currentSymbol,SYMBOL_SWAP_LONG) > SymbolInfoDouble(currentSymbol,SYMBOL_SWAP_SHORT))
         return SymbolInfoDouble(currentSymbol,SYMBOL_SWAP_LONG); // buy
      else
         return -1 * SymbolInfoDouble(currentSymbol,SYMBOL_SWAP_SHORT); // sell
   }

   if (SymbolInfoDouble(currentSymbol,SYMBOL_SWAP_LONG) > 0)
      return SymbolInfoDouble(currentSymbol,SYMBOL_SWAP_LONG); // buy

   if (SymbolInfoDouble(currentSymbol,SYMBOL_SWAP_SHORT) > 0)
      return -1 * SymbolInfoDouble(currentSymbol,SYMBOL_SWAP_SHORT); // sell


   // auf einmal bekomme ich keine Swap-Werte mehr, daher:
   if (SymbolInfoDouble(currentSymbol,SYMBOL_SWAP_LONG) == 0
    && SymbolInfoDouble(currentSymbol,SYMBOL_SWAP_SHORT) == 0) {
      // beide Richtungen ermöglichen:
      // if (currentSymbol == "") return (MathRand() % 3) - 1;

      // long:
      if (currentSymbol == "AUDCHF") return 1;
      if (currentSymbol == "AUDJPY") return 1;
      if (currentSymbol == "CADCHF") return 1;
      if (currentSymbol == "EURJPY") return 1;
      if (currentSymbol == "EURCHF") return 1;
      if (currentSymbol == "GBPJPY") return 1;
      if (currentSymbol == "GBPCHF") return 1;
      if (currentSymbol == "USDJPY") return 1;
      if (currentSymbol == "USDCHF") return 1;
      // short:
      if (currentSymbol == "AUDUSD") return -1;
      if (currentSymbol == "EURAUD") return -1;
      if (currentSymbol == "EURGBP") return -1;
      if (currentSymbol == "EURUSD") return -1;
      if (currentSymbol == "EURNZD") return -1;
      if (currentSymbol == "GBPUSD") return -1;
      // gar nicht, da beides negativ:
      if (currentSymbol == "CHFJPY") return 0;
      if (currentSymbol == "USDCAD") return 0;
   }
   return 0.0;
}


//+------------------------------------------------------------------+
// Offene Positionen: Profit pro Richtung berechnen
//+------------------------------------------------------------------+
int openPositions = 0;
int openBuyPositions = 0;
int openSellPositions = 0;
double openProfit = 0.0;
double openBuyProfit = 0.0;
double openSellProfit = 0.0;

void calculatePositions(string currentSymbol = "")
{
   // vor der Neuberechnung zurück setzen:
   openPositions = 0;
   openBuyPositions = 0;
   openSellPositions = 0;
   openProfit = 0.0;
   openBuyProfit = 0.0;
   openSellProfit = 0.0;

   ForEachPosition(positionid,index) {
      if (PositionGetInteger(POSITION_MAGIC) == MAGIC) {

         if (currentSymbol == "" || currentSymbol == PositionGetString(POSITION_SYMBOL)) {

            openProfit += PositionGetDouble(POSITION_PROFIT);
            openPositions++;

            if ((ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_BUY) {
               openBuyProfit += PositionGetDouble(POSITION_PROFIT);
               openBuyPositions++;
            }
            if ((ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_SELL) {
               openSellProfit += PositionGetDouble(POSITION_PROFIT);
               openSellPositions++;
            }
         }
      }
   }
}

//+------------------------------------------------------------------+
// InfoBox
//+------------------------------------------------------------------+
void ChartWrite(string  name,
                string& comments[],
                int     x_distance,
                int     y_distance,
                int     FontSize = 10,
                color   clr = clrYellow,
                int     diff = 23)
{
   for(int i=0; i<ArraySize(comments); i++) {
      ChartWrite(name+"-"+(string)i, comments[i], x_distance, y_distance+=diff, FontSize, clr);
   }
}

//+------------------------------------------------------------------+
// InfoBox
//+------------------------------------------------------------------+
void ChartWrite(string  name,
                string  comment,
                int     x_distance,
                int     y_distance,
                int     FontSize = 10,
                color   clr = clrWhite)
{
   string boxName = "BGCOLOR";

   // Hintergrundbox
   if (ObjectFind(0, boxName) < 0) {
      ObjectCreate(0, boxName, OBJ_RECTANGLE_LABEL, 0, 0, 0);
      ObjectSetInteger(0, boxName, OBJPROP_BGCOLOR, ChartGetInteger(0,CHART_COLOR_BACKGROUND,0));
      ObjectSetInteger(0, boxName, OBJPROP_BORDER_TYPE, BORDER_FLAT); // schwarzer Rand nicht änderbar
      ObjectSetInteger(0, boxName, OBJPROP_XDISTANCE, x_distance);
      ObjectSetInteger(0, boxName, OBJPROP_YDISTANCE, 20);
      ObjectSetInteger(0, boxName, OBJPROP_XSIZE, 200);
      ObjectSetInteger(0, boxName, OBJPROP_YSIZE, 200);
   }

   // Inhalte
   ObjectCreate(0,     name, OBJ_LABEL, 0, 0, 0);
   ObjectSetInteger(0, name, OBJPROP_CORNER, CORNER_LEFT_UPPER);
   ObjectSetInteger(0, name, OBJPROP_COLOR, clr);
   ObjectSetString(0,  name, OBJPROP_TEXT, comment);
   ObjectSetInteger(0, name, OBJPROP_FONTSIZE, FontSize);
   ObjectSetString(0,  name,  OBJPROP_FONT, "Lucida Console");
   ObjectSetInteger(0, name, OBJPROP_SELECTABLE, false);
   ObjectSetInteger(0, name, OBJPROP_XDISTANCE, x_distance);
   ObjectSetInteger(0, name, OBJPROP_YDISTANCE, y_distance);
   ObjectSetInteger(0, name, OBJPROP_BACK, false);
}

//+------------------------------------------------------------------+
// Kaufe 0.01 Lot
//+------------------------------------------------------------------+
void OpenBuyOrder(string currentSymbol)
{
   MqlTradeRequest myrequest;
   MqlTradeResult myresult;
   ZeroMemory(myrequest);

   // Ask und Bid Preis für wechselnde Symbole
   Ask = NormalizeDouble(SymbolInfoDouble(currentSymbol,SYMBOL_ASK),(int)SymbolInfoInteger(currentSymbol,SYMBOL_DIGITS));
   Bid = NormalizeDouble(SymbolInfoDouble(currentSymbol,SYMBOL_BID),(int)SymbolInfoInteger(currentSymbol,SYMBOL_DIGITS));

   myrequest.magic = MAGIC;
   myrequest.action = TRADE_ACTION_DEAL;
   myrequest.type = ORDER_TYPE_BUY;
   myrequest.symbol = currentSymbol;
   myrequest.volume = 0.01;
   myrequest.type_filling = ORDER_FILLING_FOK;
   myrequest.price = SymbolInfoDouble(currentSymbol,SYMBOL_ASK);
   myrequest.tp = Ask + (ProfitPunkte * SymbolInfoDouble(currentSymbol,SYMBOL_POINT));
   myrequest.sl = StoplossPunkte == 0 ? 0 : Bid - (StoplossPunkte * SymbolInfoDouble(currentSymbol,SYMBOL_POINT));
   myrequest.deviation = 50;
   bool err = OrderSend (myrequest,myresult);
}

//+------------------------------------------------------------------+
// Verkaufe 0.01 Lot
//+------------------------------------------------------------------+
void OpenSellOrder(string currentSymbol)
{
   MqlTradeRequest myrequest;
   MqlTradeResult myresult;
   ZeroMemory(myrequest);

   // Ask und Bid Preis für wechselnde Symbole
   Ask = NormalizeDouble(SymbolInfoDouble(currentSymbol,SYMBOL_ASK),(int)SymbolInfoInteger(currentSymbol,SYMBOL_DIGITS));
   Bid = NormalizeDouble(SymbolInfoDouble(currentSymbol,SYMBOL_BID),(int)SymbolInfoInteger(currentSymbol,SYMBOL_DIGITS));

   myrequest.magic = MAGIC;
   myrequest.action = TRADE_ACTION_DEAL;
   myrequest.type = ORDER_TYPE_SELL;
   myrequest.symbol = currentSymbol;
   myrequest.volume = 0.01;
   myrequest.type_filling = ORDER_FILLING_FOK;
   myrequest.price = SymbolInfoDouble(currentSymbol,SYMBOL_BID);
   myrequest.tp = Bid - (ProfitPunkte * SymbolInfoDouble(currentSymbol,SYMBOL_POINT));
   myrequest.sl = StoplossPunkte == 0 ? 0 : Ask + (StoplossPunkte * SymbolInfoDouble(currentSymbol,SYMBOL_POINT));
   myrequest.deviation =50;
   bool err = OrderSend (myrequest,myresult);
}

//+------------------------------------------------------------------+
// MQL4 Funktionen, die mir fehlen
//+------------------------------------------------------------------+
int Day() {
    MqlDateTime tm;
    TimeCurrent(tm);
    return(tm.day);
}
int DayOfWeek() {
    MqlDateTime tm;
    TimeCurrent(tm);
    return(tm.day_of_week);
}
int DayOfYear() {
    MqlDateTime tm;
    TimeCurrent(tm);
    return(tm.day_of_year);
}
int Hour() {
    MqlDateTime tm;
    TimeCurrent(tm);
    return(tm.hour);
}
int Minute() {
    MqlDateTime tm;
    TimeCurrent(tm);
    return(tm.min);
}
int Month() {
    MqlDateTime tm;
    TimeCurrent(tm);
    return(tm.mon);
}
int Seconds() {
    MqlDateTime tm;
    TimeCurrent(tm);
    return(tm.sec);
}
int Year() {
    MqlDateTime tm;
    TimeCurrent(tm);
    return(tm.year);
}
//+------------------------------------------------------------------+

Externe Links

MQL5 Reference

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