Die Verwendung von ORDER_MAGIC für den Handel mit unterschiedlichen Expert Advisors mit einem Instrument

Mykola Demko | 9 März, 2016

Einleitung

In MQL5 haben wir die Möglichkeit, jeder Pending Order eine magische Zahl zuzuweisen, um diese Information zur Erkennung der Order zu nutzen. Dies eröffnet zahlreiche Interaktionsmöglichkeiten zwischen verschiedenen Expert Advisors und die Entwicklung noch komplexerer Systeme. In diesem Beitrag möchte ich die Leser über die unterschätzten Möglichkeiten der magischen Zahl informieren.

Doch bevor wir zum eigentlichen Thema des Beitrags übergehen, müssen wir zuerst verstehen, was die magische Zahl darstellt. Was könnte eine magische Zahl sein, die bestimmt, von welchem Expert Advisor sie festgelegt wurde? Die "Wunder" beginnen mit den Möglichkeiten, die die Entwickler im Typ ulong hinterlegen, der durch die magische Zahl deklariert wird.

Der Typ ulong ist der längste

Wenn wir den ganzzahligen Typ long im Detail betrachten, sehen wir, dass der Maximalwert dieses Typen einfach phänomenal ist:

Typ

Größe in Bytes

Minimalwert

Maximalwert

Gegenstück in C++

long

8

-9 223 372 036 854 775 808

9 223 372 036 854 775 807

__int64

ulong

8

0

18 446 744 073 709 551 615

unsigned __int64

Tabelle 1. Eigenschaften der Datentypen long und ulong

Doch der Typ ulong übertrifft ihn, indem er die positive und negative Mantisse kombiniert.

Die angegebene Länge ist also riesig, doch wie wurde sie vorher genutzt?

Bei meiner Arbeit mit MQL4 habe ich oft die Sinnlosigkeit der Kodierung der magischen Zahl durch viele Entwickler bemerkt. Die magische Zahl wurde sinnvoll verwendet, doch ihre Kodierung erschien albern. Zur Individualität der magischen Zahl 12345: Diese magische Zahl wird fast von der Hälfte der Entwickler verwendet. Die andere Hälfte nutzt die magischen Zahlen 55555, 33333 und 77777, und das ist so ziemlich alles. Ich möchte die Aufmerksamkeit des Lesers auf die Tatsache lenken, dass sein Computer über mehr als 1.000 Expert Advisors verfügt. Also wird die Zahl 1000 reichen, um den individuellen Namen all Ihrer Expert Advisors zu kodieren.

1000 – das sind nur 3 vollständige Kategorien. Was machen wir also mit den restlichen 15 Kategorien, die im Typ ulong verfügbar sind? Die Antwort ist einfach: Wir kodieren sie.

Was hat Wikipedia über das Wort Code zu sagen?

Ein Code ist eine Regel (Algorithmus), der Vergleich jeder einzelnen Meldung einer bestimmten Kombination aus Symbolen (Zeichen) (oder Signalen).

Deshalb bestimmen wir die Regeln. Ich empfehle, im Code der magischen Zahl nicht nur die ID des Expert Advisors festzulegen, sondern auch des Instruments, auf dem er ausgeführt wird. Dass ein Expert Advisor ausgeführt wird, beispielsweise auf EURUSD, bedeutet nicht zwangsläufig, dass der Expert Advisor nur Order für dieses Instrument anzeigen wird. Ich denke, es wäre auch hilfreich, den Interaktionscode des Expert Advisors zu schreiben – etwas wie "meins/fremd", damit der Expert Advisor beim Prüfen der Positionen verstehen kann, dass die aktuelle Order durch einen freundlichen Expert Advisor aufgebaut wird. Ich glaube, dass das ausreichen sollte, um ein sehr komplexes System zu erschaffen.

Fassen wir also zusammen, was wir bisher haben: Welche Gelegenheiten hinterlegen wir im System:

  1. Die Möglichkeit, dass zwei oder mehr Expert Advisors mit einem einzelnen Instrument arbeiten können, ohne in Konflikt zu geraten.
  2. Die Möglichkeit, dass zwei oder mehr Expert Advisors mit verschiedenen Instrumenten arbeiten und sich gegenseitig ergänzen.
  3. Die Möglichkeit, die Order anhand des Instruments zu bestimmen, mit dem der Expert Advisor arbeitet.

Nun ist die Aufgabe gestellt. Fangen wir mit ihrer Umsetzung an.

Einfacher Expert Advisor

Entwerfen wir den Code des simplen Expert Advisors, beispielsweise zum Halten der Position in der Richtung des Moving. Ich schätze, dass der Leser, der beschlossen hat, die magische Zahl zu analysieren, den Beitrag Schritt-für-Schritt-Anleitung zum Schreiben eines Expert Advisors in MQL5 für Anfänger bereits gelesen hat. Falls nicht, kann ich nur empfehlen, den Beitrag zu lesen, da ich die Erstellung eines Expert Advisors hier nicht im Detail beschreiben werde. Grundsätzlich öffnet der Expert Advisor die Position einmal und kehrt sie alle weiteren Male um. Deshalb benötigen wir die Funktion zum Öffnen der Position, d. h. zum Platzieren der Handelsanfrage (Handelsorder).

Erstellen Sie eine Hilfsklasse, die für uns die Parameter zum Befüllen der Felder der Handelsanfragestruktur berechnet.

//+------------------------------------------------------------------+
//| The class provides auxiliary trading calculations                |
//+------------------------------------------------------------------+
class CProvision
  {
protected:
   MqlTradeRequest   trades;                 // pointer to the request structure of OrderSend
public:
   int               TYPE(const double &v[]);  // determines the type, in respect to the  readings of the moving
   double            pricetype(int type);     // calculates the level of the opening, in respect to the type 
   double            SLtype(int type);        // calculates the level of the stop-loss in respect to the type
   double            TPtype(int type);        // calculates the level of the take-profit, in respect to the type
   long              spread();               // returns the spread of the current instrument
   int               SendOrder(ENUM_ORDER_TYPE type,double volume);
  };
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
int CProvision::SendOrder(ENUM_ORDER_TYPE type,double volume)
  {
   trades.action          =TRADE_ACTION_DEAL;       // Type of the implemented actions
   trades.magic           =magic;                 // Stamp of the Expert Advisor (identifier of the magic number)
   trades.symbol          =_Symbol;                // Name of the trading instrument
   trades.volume          =volume;                // Request the volume of the trade in lots
   trades.price           =pricetype((int)type);  // Price       
   trades.sl              =SLtype((int)type);     // Level of Stop Loss order
   trades.tp              =TPtype((int)type);     // Level of Take Profit order         
   trades.deviation=(int)spread();                // Maximum acceptable deviation from the requested price
   trades.type=type;                              // Order type
   trades.type_filling=ORDER_FILLING_FOK;
   if(OrderSend(trades,res)){return(res.retcode);}
   return(-1);
  }
//+------------------------------------------------------------------+
//| Determines the type, in respect to the reading of the moving     |
//+------------------------------------------------------------------+
int CProvision::TYPE(const double &v[])
  {
   double t=v[0]-v[1];
   if(t==0.0)t=1.0;
   return((int)(0.5*t/fabs(t)+0.5));
  }
//+------------------------------------------------------------------+
//| Calculates the level of opening in respect to the type           |
//+------------------------------------------------------------------+
double CProvision::pricetype(int type)
  {
   if(SymbolInfoTick(_Symbol,tick))
     {
      if(type==0)return(tick.ask);
      if(type==1)return(tick.bid);
     }
   return(-1);
  }
//+------------------------------------------------------------------+
//| Calculates the level of stop-loss in respect to the type         |
//+------------------------------------------------------------------+
double CProvision::SLtype(int type)
  {
   if(SymbolInfoTick(_Symbol,tick))
     {
      if(type==0)return(tick.bid-SL*SymbolInfoDouble(Symbol(),SYMBOL_POINT));
      if(type==1)return(tick.ask+SL*SymbolInfoDouble(Symbol(),SYMBOL_POINT));
     }
   return(0);
  }
//+------------------------------------------------------------------+
//| Calculates the level of timeframe in respect to the type         |
//+------------------------------------------------------------------+
double CProvision::TPtype(int type)
  {
   if(SymbolInfoTick(_Symbol,tick))
     {
      if(type==0)return(tick.bid+TP*SymbolInfoDouble(Symbol(),SYMBOL_POINT));
      if(type==1)return(tick.ask-TP*SymbolInfoDouble(Symbol(),SYMBOL_POINT));
     }
   return(0);
  }
//+------------------------------------------------------------------+
//| Returns the spread                                               |
//+------------------------------------------------------------------+
long CProvision::spread() 
  {
   return(SymbolInfoInteger(_Symbol,SYMBOL_SPREAD));
  }

Mit einer solchen Klasse können wir problemlos einen Code für einen einfachen Expert Advisor schreiben: 

//+------------------------------------------------------------------+
//| Code of the Expert Advisor                                       |
//+------------------------------------------------------------------+

//--- Input parameters
input ulong              magic       =1;           // magic
input int                SL          =300;         // Stop Loss
input int                TP          =1000;        // Take Profit
input int                MA_Period   =25;         // MA period
input double             lot         =0.1;         // Volume of position
input int                MA_shift    =0;          // Shift of indicator
input ENUM_MA_METHOD     MA_smooth   =MODE_SMA;     // Smoothing type
input ENUM_APPLIED_PRICE price       =PRICE_OPEN;    // Price type 

//--- We will store the indicator's handle
int
MA_handle,     // Handle of the indicator
type_MA,       // Type that specify the direction of MA
rezult;        // The variable takes the value of the result of the OrderSend operation
double v[2];    // Buffer for receiving values of MA

MqlTradeResult   res;   // Pointer to the structure of responding by OrderSend
MqlTick         tick;  // Pointer to the structure of last market information
CProvision      prov;
//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
//--- Create the indicator's handle
   MA_handle=iMA(Symbol(),0,MA_Period,MA_shift,MA_smooth,price);
   return(0);
  }
//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
  {
   if(CopyBuffer(MA_handle,0,0,2,v)<=0)
     {Print("#",magic,"Error of copying");return;}
   type_MA=prov.TYPE(v); // Determine type depending on MA indication

   if(PositionSelect(_Symbol))// If there is an open position 
     {
      if(PositionGetInteger(POSITION_TYPE)!=type_MA)// Check if its time to close
        {
         Print("#",magic,"Position by magic number has volume ",PositionGetDouble(POSITION_VOLUME),
               " reverse position of type ",PositionGetInteger(POSITION_TYPE)," by ",type_MA);
         rezult=prov.SendOrder((ENUM_ORDER_TYPE)type_MA,PositionGetDouble(POSITION_VOLUME)+lot);
         // reverse the position
         if(rezult!=-1)Print("#",magic," Code of the operation result ",rezult," volume ",res.volume);
         else{Print("#",magic,"Error",GetLastError()); return;}
        }
     }
   else // If there is no open position then open
     {
      Print("#",magic,"Position by magic number has volume ",PositionGetDouble(POSITION_VOLUME),
            " open position of type ",type_MA);
      rezult=prov.SendOrder((ENUM_ORDER_TYPE)type_MA,lot);
      // open position 
      if(rezult!=-1)Print("#",magic," Code of operation result ",rezult," volume ",res.volume);
      else{Print("#",magic,"Error",GetLastError()); return;}
     }
  } 

Führen Sie ihn aus und stellen Sie sicher, dass der Expert Advisor sich in seiner Wirtschaftlichkeit nicht unterscheidet, aber gemäß der vorgeschriebenen Logik handelt, wie wir es von ihm erwarten.

Abbildung 1. Die Arbeit eines Expert Advisors mit einem einzelnen Instrument

Abbildung 1. Die Arbeit eines Expert Advisors mit einem einzelnen Instrument

Nun führen wir diesen EA mit verschiedenen Timeframes eines Instruments aus (für die Versuche haben wir ein beliebiges Instrument ausgesucht, zufälligerweise EURUSD).

Abbildung 2. Der Konflikt zwischen zwei Expert Advisors mit dem gleichen Instrument unter verschiedenen Timeframes

Abbildung 2. Der Konflikt zwischen zwei Expert Advisors mit dem gleichen Instrument unter verschiedenen Timeframes

Da beide Expert Advisors auf dem gleichen Instrument ausgeführt werden und der Code die gemeinsame Nutzung von Positionen nicht festlegt, versuchen beide Expert Advisors, die Handelsposition abhängig von den Anzeigen ihrer Indikatoren zu korrigieren, woraus der Konflikt entsteht. Der Expert Advisor, der auf M1 ausgeführt wird, versucht, die Position in Sell umzukehren, während sein Rivale dies verhindern will. Offenbar müssen wir die Positionen separat berechnen, was wir jetzt tun werden.

Position oder virtuelle Position?

Da die Entwickler in MetaTrader 5 den Fokus von Ordern auf Positionen umgelegt haben, ist es sinnvoll, die Funktionen im Zusammenhang mit der Aufzeichnung von Positionen zu betrachten.

// Returns the number of open positions.
int     PositionsTotal();

// Returns the symbol of the open position by the number in the list of positions.
string  PositionGetSymbol(int  index);

// Selects the open position for further working with it.
bool    PositionSelect(string  symbol, uint timeout=0);

// Function returns the requested property of the open position.
double  PositionGetDouble(ENUM_POSITION_PROPERTY  property_id);

// The function returns the requested property of the open position.
long    PositionGetInteger(ENUM_POSITION_PROPERTY  property_id);

// The function returns the requested property of the open position.
string  PositionGetString(ENUM_POSITION_PROPERTY  property_id);

Die Identifikatoren der Aufzählungen für die Funktionen zum Abrufen der entsprechenden Positionseigenschaften PositionGetDouble, PositionGetInteger und PositionGetString sind in den Tabellen 2-4 aufgeführt.

Bezeichner

Beschreibung

Typ

POSITION_VOLUME

Volumen der Position

double

POSITION_PRICE_OPEN

Preis der Position

double

POSITION_SL

Stop-Loss-Niveau für die offene Position

double

POSITION_TP

Take-Profit-Niveau für die offene Position

double

POSITION_PRICE_CURRENT

Aktueller Preis nach Symbol

double

POSITION_COMMISSION

Provision

double

POSITION_SWAP

Angesammelter Swap

double

POSITION_PROFIT

Aktueller Gewinn

double

Tabelle 2. Werte der Aufzählung ENUM_POSITION_PROPERTY_DOUBLE

Bezeichner

Beschreibung

Typ

POSITION_TIME

Öffnungszeit der Positionen

datetime

POSITION_TYPE

Art der Position

ENUM_POSITION_TYPE

POSITION_MAGIC

Magische Zahl für die Position (siehe ORDER_MAGIC)

long

POSITION_IDENTIFIER

Identifikator der Position. Es handelt sich um eine eindeutige Zahl, die automatisch jeder wiedereröffneten Position zugewiesen wird und sich im gesamten Lebenszyklus nicht ändert. Die Umkehrung einer Position ändert nicht ihre ID.

long

Tabelle 3. Werte der Aufzählung ENUM_POSITION_PROPERTY_INTEGER

Bezeichner

Beschreibung

Typ

POSITION_SYMBOL

Symbol, für das die Position geöffnet wird

string

POSITION_COMMENT

Kommentar zur Position

string

Tabelle 4. Werte der Aufzählung ENUM_POSITION_PROPERTY_STRING

Anhand der Funktionen können wir klar erkennen, dass die Sprache keine Aufteilung von Positionen auf Basis des Prinzips "Wer hat die Order erstellt" enthält, solche Aufzeichnungen aber möglich sind, da ORDER_MAGIC, POSITION_MAGIC und DEAL_MAGIC genau die gleiche Zahl sind und der vom Benutzer festgelegten magischen Zahl entnommen werden. POSITION_MAGIC wird DEAL MAGIC entnommen, die die Position öffnet. DEAL MAGIC wird wiederum der ORDER_MAGIC der platzierten Order entnommen.

Eine Order, Transaktion oder Position kann problemlos identifiziert werden, doch eine Position durch eine bestimmte magische Zahl zu bestimmen, ist nicht möglich. Dieses Manko versuchen wir nun zu beheben. Erstellen wir Gegenstücke der eingebauten Funktionen, allerdings mit Identifikation durch die magische Zahl. Deklarieren Sie eine Klasse zum Arbeiten mit einer virtuellen Position auf der magischen Zahl.

Da wir die Möglichkeit haben, mit der OOP zu arbeiten, deklarieren wir auch unsere eigene Struktur (und üben damit das Schreiben von Objekten).

//+------------------------------------------------------------------+
//| Structure of the CPositionVirtualMagic class                     |
//+------------------------------------------------------------------+
struct SPositionVirtualMagic
  {
   double            volume; // volume of virt. position
   ENUM_POSITION_TYPE type;  // type of virt. position
  };
//+--------------------------------------------------------------------------------+
//| The class calculates the virtual position of an Expert Advisor by magic number |
//+--------------------------------------------------------------------------------+
class CPositionVirtualMagic
  {
protected:
   SPositionVirtualMagic pvm;
public:
   double               cVOLUME(){return(pvm.volume);}
   // Returns the volume of virtual position of an Expert Advisor
   ENUM_POSITION_TYPE   cTYPE(){return(pvm.type);}
   // Returns the type of virtual position of an Expert Advisor
   bool              PositionVirtualMagic(ulong Magic,
                                          string symbol,
                                          datetime CurrentTime
                                          );
   // the method of calculation virt. position returns the presence or absence of virt. position
private:
   void              prHistory_Deals(ulong &buf[],int HTD);
   // Fills the array of tickets
  };
//+-------------------------------------------------------------------------------------+
//| Method of calculation of virt. position, returns true if there is a virt. position  |
//+-------------------------------------------------------------------------------------+
bool  CPositionVirtualMagic::PositionVirtualMagic(ulong Magic,
                                                  string symbol,
                                                  datetime CurrentTime
                                                  )
  {
   int DIGITS=(int)-log10(SymbolInfoDouble(symbol,SYMBOL_VOLUME_STEP));
   if(DIGITS<0)DIGITS=0;
   ulong Dticket=0;
   int History_Total_Deals=-1;
   double volume=0,volume_BUY=0,volume_SELL=0;
   ulong DTicketbuf[];

   do
     {
      if(HistorySelect(0,TimeCurrent()))
        {
         History_Total_Deals=HistoryDealsTotal();
         prHistory_Deals(DTicketbuf,History_Total_Deals);
        }
      HistorySelect(0,TimeCurrent());
     }
   while(History_Total_Deals!=HistoryDealsTotal());

   for(int t=0;t<History_Total_Deals;t++)
     {
      Dticket=DTicketbuf[t];
      if(HistoryDealSelect(Dticket))
        {
         if(HistoryDealGetInteger(Dticket,DEAL_TIME)>=CurrentTime)
           {
            if(HistoryDealGetInteger(Dticket,DEAL_MAGIC)==Magic)
              {
               if(HistoryDealGetInteger(Dticket,DEAL_TYPE)==DEAL_TYPE_BUY)
                 {
                  volume_BUY+=HistoryDealGetDouble(Dticket,DEAL_VOLUME);
                 }
               else
                 {
                  if(HistoryDealGetInteger(Dticket,DEAL_TYPE)==DEAL_TYPE_SELL)
                    {
                     volume_SELL+=HistoryDealGetDouble(Dticket,DEAL_VOLUME);
                    }
                 }
              }
           }
        }
      else{HistorySelect(0,TimeCurrent());t--;}
      // if there is a fault, load history data and pass the step again
     }
   volume=NormalizeDouble(volume_BUY-volume_SELL,DIGITS);
   if(volume<0)pvm.type=POSITION_TYPE_SELL;
   else
     {
      if(volume>0)pvm.type=POSITION_TYPE_BUY;
     }
   pvm.volume=fabs(volume);
   if(pvm.volume==0)return(false);
   else return(true);
  }

Im oberen Text (an der Stelle, an der der Code der CProvision-Klasse dargelegt wird) wurde nicht erklärt, wo das alles herkommt und wohin es geht, da die Entwicklung des Expert Advisors nicht das Thema dieses Beitrags ist.

Doch wir sehen die Klasse CPositionVirtualMagic im Detail an.

Die Klasse erhält die Struktur:

struct SPositionVirtualMagic

die zum Akzeptieren der Berechnungsergebnisse wie der globalen Deklarierung innerhalb der Klasse verwendet wird. Dank pvm (Variable der Struktur) wird diese Struktur überall in allen Methoden der Klasse verfügbar sein.

Als Nächstes folgen zwei Methoden der Klasse:

double               cVOLUME(){return(pvm.volume);} // Returns the volume of the virtual position of the EA
ENUM_POSITION_TYPE   cTYPE()  {return(pvm.type);}   // Returns the type of the virtual position of the EA

Diese Methoden sind als public deklariert und sind deshalb über die aufrufende Klassenvariable von jedem beliebigen Ort im Programm verfügbar. Ihre Bestimmung ist die Ausgabe der Strukturwerte am angefragten Ort.

In diesem Abschnitt wird auch die folgende Methode deklariert:

bool              PositionVirtualMagic(ulong Magic,string symbol,datetime CurrentTime);

Das ist die Hauptfunktion der Klasse, und wir werden sie im weiteren Verlauf detailliert analysieren. Jetzt möchte ich allerdings etwas vorgreifen und die Funktion des Spezifizierers der private-Adresse beschreiben:

void              prHistory_Deals(ulong &buf[],int HTD);

Diese Methode erfasst Tickets von Transaktionen in einem Array und ist im Prinzip ein Zyklus und kann in der aufgerufenen Funktion beschrieben werden. Ich wollte allerdings die Größe der Funktion PositionVirtualMagic() reduzieren (um die Lesbarkeit des Codes zu verbessern), also verschob ich diesen Zyklus hinter die Grenzen der Funktion, um zu demonstrieren, wie der Spezifizierer der private-Adresse genutzt wird.

Zurück zu PositionVirtualMagic(). An ihrem Anfang weist diese Funktion eine einzeilige Genauigkeitsberechnung auf, auf die Sie den double-Wert des Volumens der berechneten Position runden müssen.

int DIGITS=(int)-log10(SymbolInfoDouble(symbol,SYMBOL_VOLUME_STEP)); if(DIGITS<0)DIGITS=0;

Dies wird für den Vergleich mit Null benötigt, da ansonsten möglicherweise eine Unstimmigkeit bei der 8. Nachkommastelle uns daran hindert, den Wert an Null anzugleichen, was zu einem Ausführungsfehler führt.

Das Positionsvolumen wird auf den Minimalschritt gerundet. Wenn der Minimalschritt größer als 1 ist, wird zum ganzzahligen Teil gerundet. Als Nächstes folgt der Zyklus while, wird allerdings auf eine neue Art angewendet (anders als in MQL4), da die Verifizierung des wahrheitsgemäßen Ausdrucks am Ende des Zyklus anstatt an seinem Anfang stattfindet:

    do
     {
      if(HistorySelect(0,TimeCurrent()))
        {
         History_Total_Deals=HistoryDealsTotal();
         prHistory_Deals(DTicketbuf,History_Total_Deals);
        }
      HistorySelect(0,TimeCurrent());  
     }
   while(History_Total_Deals!=HistoryDealsTotal());

Diese Herangehensweise wird verwendet, da der Ausdruck der Wahrhaftigkeit innerhalb des Zyklus berechnet wird und bei seinem Start noch nicht für diese Verifizierung bereit ist.

Der Zyklus enthält den Upload der Historie. Ich möchte Ihre Aufmerksamkeit darauf lenken, dass dies eine Grundanforderung ist, um die Funktionsfähigkeit der eingebauten Funktionen für die Arbeit mit der Historie zu gewährleisten.

HistorySelect(0,TimeCurrent())

Ich denke, ich sollte mein System für die Auswahl der Variablennamen erklären.

Dem aufmerksamen Leser sollte aufgefallen sein, dass die Namen der Klassen durch den Anfangsbuchstaben "C" definiert sind. Das ist keine Bedingung der Syntax und es kann jeder beliebige Name gewählt werden, diese Vorgehensweise macht das Lesen allerdings viel einfacher. Wenn der Buchstabe "C" vor den Namen erscheint, wissen wir sofort, dass es sich um den Namen einer Klasse handelt. Beim Buchstaben "S" ist es eine Struktur. Wenn die Variable den Wert einer eingebauten Funktion annimmt, kann ich einfach die Komponenten des Namen der Funktion austauschen und erhalte den Variablennamen, zum Beispiel so:

CurrentTime = TimeCurrent();

Einfach und lesbar – man sieht sofort, was die Variable beinhaltet, insbesondere, weil MetaEditor eine Funktion zum Ziehen eines bestimmten Stücks Code an einen bestimmten Ort bietet.

Bei der weiteren Überprüfung des Codes sehen wir, dass auf den Upload der Historie der Aufruf der Funktion erfolgt:

History_Total_Deals=HistoryDealsTotal();

mit der Speicherung der Anzahl von Transaktionen in der Variable. Mit der gleichen Bedingung implementieren wir die Verifizierung zum Verlassen des Zyklus. Wofür benötigen wir diese Verifizierung? Und warum können wir nicht einfach die Historie hochladen und dann die Transaktionen daraus abrufen?

Das Problem ist, dass die Historie während der Arbeit der Expert Advisors von jedem EA separat abgefragt wird. Deshalb wird die Tiefe der Historie unterschiedlich ausfallen, wenn die Expert Advisors zu verschiedenen Zeiten ausgeführt werden. Das bedeutet wiederum, wenn ein Expert Advisor den Zyklus betritt und die Historie für seinen Zeitraum hochlädt, findet er möglicherweise vor dem Ende des Zyklus, dass diese Historie bereits durch die Anfrage eines anderen Expert Advisors hochgeladen wurde. Deshalb muss die Authentizität verifiziert werden.

Das mag möglicherweise nicht die beste Art von Verifizierung sein, doch sie funktioniert. Fahren wir also fort. In diesem Zyklus rufen wir die Klassenmethode auf, die die Ticketwerte der Transaktionen in einen speziell erstellten Puffer eingibt. Nach dem Aufruf der Funktion prHistory_Deals() laden wir die Historie erneut hoch.

Auf diese Weise wird verifiziert, ob es während der Ausführung der Funktion prHistory_Deals() Änderungen in der Historie der Handelsaktivitäten gab. Wenn es keine Änderungen gab, ist die Variable History_Total_Deals gleich HistoryDealsTotal() und der Zyklus wird über einen Durchlauf verlassen. Gab es Änderungen, startet das System einen zweiten Zyklus und wiederholt ihn, bis die Tickethistorie fehlerfrei hochgeladen wurde (und vergessen Sie nicht, ";" an das Ende zu setzen):

while(History_Total_Deals!=HistoryDealsTotal());

Weiter im Zyklus for findet die Berechnung virtueller Positionen statt.

Wenn eine Transaktion es erfolgreich an einer Reihe von Filtern vorbei schafft (Zeit der Transaktion und magische Zahl der Transaktion), erhöht ihr Volumen den Anteil der virtuellen Position, zu dessen Typ die Transaktion gehört.

Ich möchte anmerken, dass ich die Berechnungen der virtuellen Positionen erst ab dem Start des Expert Advisors aufzeichne, obwohl andere Optionen möglich sind.

Hier sollte festgestellt werden, wie die Position genau berechnet wird. Nach dem Nachweisbuch, das wir seit langer Zeit nutzen, haben wir Ausgaben und Gewinne und die Bilanz ist die Differenz dieser Werte. Das gleiche Schema gilt auch für die Berechnung einer Position: Wenn Sie Posten eröffnen, 0,2 für Sell und 0,3 für Buy, bedeutet das, dass Sie eine Position von 0,1 für Buy halten. Öffnungszeit und Niveauunterschied sind Gewinnkategorien, doch Sie haben eine Position mit 0,1 Posten des Typen Buy.

Deshalb fassen wir alle vom Expert Advisor auf Buy und separat auf Sell durchgeführten Transaktionen zusammen, vergleichen sie anschließend und erhalten die allgemeine Position (das ist auch das weitere Thema der zu untersuchenden Funktion).

Berechnung des Volumens der Position:

volume=NormalizeDouble(volume_BUY-volume_SELL,DIGITS);

Erkennen des Typen der Position mit Ausgabe des Wertes in der Struktur:

   if(volume<0)pvm.type=POSITION_TYPE_SELL;
   else
     {
      if(volume>0)pvm.type=POSITION_TYPE_BUY;
     }

Ausgabe des Volumens in der Struktur:

pvm.volume=fabs(volume);

 Ausgabe des Wertes in der Funktion: Wenn das Volumen der Position 0 beträgt, ist sie false, andernfalls, wenn die Position existiert, ist sie true:

   if(pvm.volume==0)return(false);
   else return(true);

Mit der Funktion der virtuellen Position können wir nun mühelos den Code des Expert Advisors erstellen, der nicht mit seinen "Nachbarn" im Konflikt steht.

Um Platz zu sparen, stelle ich nur bestimmte Teile des Codes dar, die nicht weiter oben dargestellt wurden, anstatt des Codes in seiner Gesamtheit.

//+------------------------------------------------------------------+
//| Code of the Expert Advisor                                       |
//+------------------------------------------------------------------+

//--- input parameters
input ulong              magic       =1;           // magic
input int                SL          =300;         // Stop Loss
input int                TP          =1000;        // Take Profit
input int                MA_Period   =25;          // MA period
input double             lot         =0.1;         // Volume of position
input int                MA_shift    =0;          // Shift of indicator
input ENUM_MA_METHOD     MA_smooth   =MODE_SMA;     // Smoothing type
input ENUM_APPLIED_PRICE price       =PRICE_OPEN;    // Price type 
//--- we will store the indicator's handle
int MA_handle,type_MA,rezult;
double v[2];
datetime  CurrentTime;  // The variable stores the time of start of the Expert Advisor
MqlTradeResult    res;   // Pointer to the structure of responding by OrderSend
MqlTick          tick;  // Pointer to the structure of last market information
CPositionVirtualMagic cpvm;
CProvision prov;
//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
   CurrentTime=TimeCurrent();// The variable stores the time of start of the Expert Advisor
//--- Create the indicator's handle
   MA_handle=iMA(Symbol(),0,MA_Period,MA_shift,MA_smooth,price);
   return(0);
  }
//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
  {
   if(CopyBuffer(MA_handle,0,0,2,v)<=0)
     {Print("#",magic,"Error of copying");return;}
   type_MA=prov.TYPE(v); // Determine type depending on MA indication

   if(cpvm.PositionVirtualMagic(magic,_Symbol,CurrentTime))// If there is ab open position 
     {
      if((int)cpvm.cTYPE()!=type_MA)// Check if it is time to close
        {
         Print("#",magic,"Position by magic number has volume ",cpvm.cVOLUME(),
               " reverse position of type ",(int)cpvm.cTYPE()," by ",type_MA);
         //cpvm.cVOLUME() - volume of virtual position
         rezult=prov.SendOrder((ENUM_ORDER_TYPE)type_MA,cpvm.cVOLUME()+lot);// reverse the poistion
         if(rezult!=-1)Print("#",magic," Code of the operation result ",rezult," volume ",res.volume);
         else{Print("#",magic,"Error",GetLastError()); return;}
        }
     }
   else // If there is no open position then open
     {
      Print("#",magic,"Poistion by magic number has volume ",cpvm.cVOLUME()," open position of type ",type_MA);
      rezult=prov.SendOrder((ENUM_ORDER_TYPE)type_MA,lot);// Open position 
      if(rezult!=-1)Print("#",magic," Code of the operation result ",rezult," volume ",res.volume);
      else{Print("#",magic,"Error",GetLastError()); return;}
     }
  }

Führen Sie den Expert Advisor dreimal auf einem einzelnen Instrument, aber mit unterschiedlichen Timeframes aus und weisen Sie jedes Mal andere magische Zahlen zu:

Abbildung 3. Wir weisen zwei identischen Expert Advisors unterschiedliche magische Zahlen zu (ein Instrument, verschiedene Timeframes), Start des ersten Expert Advisors

Abbildung 3. Wir weisen zwei identischen Expert Advisors unterschiedliche magische Zahlen zu (ein Instrument, verschiedene Timeframes), Start des ersten Expert Advisors

Abbildung 4. Wir weisen zwei identischen Expert Advisors unterschiedliche magische Zahlen zu (ein Instrument, verschiedene Timeframes), Start des zweiten Expert Advisors

Abbildung 4. Wir weisen zwei identischen Expert Advisors unterschiedliche magische Zahlen zu (ein Instrument, verschiedene Timeframes), Start des zweiten Expert Advisors

Abbildung 5. Das Ergebnis ist die konfliktfreie Ausführung von Expert Advisors auf einem einzelnen Instrument mit unterschiedlichen magischen Zahlen

Abbildung 5. Das Ergebnis ist die konfliktfreie Ausführung von Expert Advisors auf einem einzelnen Instrument mit unterschiedlichen magischen Zahlen

Der Testlauf ist erfolgreich abgeschlossen, die Expert Advisors machen sich gegenseitig Platz und es scheinen keine Konflikte vorzuliegen.

Der erste Punkt der technischen Aufgabe wurde umgesetzt, doch es folgt noch mehr.


Kodieren der magischen Zahl

Für die Implementierung der folgenden Teile müssen wir eine Klasse von Methoden entwickeln, die die Informationen kodieren/dekodieren, und die Werte der eingebauten Funktionen abrufen und sie in ein vorgegebenes Format umwandeln.

Wiederholen Sie dafür die Bedingungen der Kodierung (sozusagen die technische Aufgabe für die Entwicklung):

Für den Anfang wählen wir den Namen für die neue Klasse – dies kann magic sein (ein generischer Name) – und ordnen unsere Aufzählung zu, um den Code verständlicher zu machen.

enum Emagic
  {
   ENUM_DIGITAL_NAME,    // digital name if the Expert Advisor
   ENUM_CODE_INTERACTION,// code of interaction
   ENUM_EXPERT_SYMBOL    // symbol, on which the EA is launched
  };

Die Funktion der Aufzählung ist einfach: Sie beschreiben die Namen, getrennt durch Kommata, und der Compiler ordnet ihnen sequentiell Zahlen zu.

Erstens erhalten Sie einen Fehler während der Kompilierung, wenn Sie beim Festlegen eines Parameters aus der Aufzählung eine Variable eines anderen Typen zuweisen (dies gilt nicht für Zahlen), und zweitens schaffen Sie Klarheit: Sie weisen nicht einfach 0 zu, sondern geben stattdessen den Befehl zum Zuweisen von ENUM_DIGITAL_NAME.

Wie bei der Erstellung einer Struktur oder Klasse habe ich einen einfachen Namen für die Aufzählung gewählt. Ich habe zum allgemeinen gewählten Namen einfach ein E hinzugefügt und Emagic erhalten. Die entsprechende Struktur heißt Smagic und die Klasse Cmagic.

Ich weise nochmals darauf hin, dass das nicht verpflichtend ist. Sie können die Aufzählung ebenso Enumerator, die Struktur Structurer und die Klasse Classifier nennen. Doch damit erhalten Sie keine Gemeinsamkeit bei den Namen und das Lesen dieser Art von Code wird unpraktisch sein.

Erstellen wir als Nächstes eine Struktur zum Speichern unserer Codes.

struct Smagic
  {
   ulong             magicnumber;      // magic in an assembled form - how it is written in the order
   int               digital_name;     // digital name
   int               code_interaction; // code of interaction
   int               expert_symbol;    // symbol, on which the Expert Advisor is launched
  };

Deklarieren Sie daraufhin die Cmagic-Klasse, in der wir alle Methoden zum Kodieren und Dekodieren der magischen Zahl registrieren, einschließlich der Methoden des vorherigen Expert Advisors (deklarieren Sie sie einfach in der aktuellen Klasse und schreiben Sie die Kopfzeilen neu).

class Cmagic
  {
protected:
   Smagic            mag;
   SPositionVirtualMagic pvm;
public:
// the function returns the assembled magic, assembled from the incoming data
   ulong             SetMagic_request(int digital_name=0,int code_interaction=0);

// the function obtains the assembled magic and divides it according to the assembly logic
   ulong             SetMagic_result(ulong magicnumber);    

// the function obtains the return identification and returns the requested part of the assembled magic
   ulong             GetMagic_result(Emagic enum_); 

// the function obtains the return identification and returns the textual interpretation of the request part of the assembled magic
   string            sGetMagic_result(Emagic enum_);

// returns the voulme of the virtual position of the Expert Advisor
   double            cVOLUME(){return(pvm.volume);}
   
// returns the type of the virtual position of the Expert Advisor
   ENUM_POSITION_TYPE  cTYPE(){return(pvm.type);}
                                           
// method of calculating the virtual position, returns the presence of absence of the virtual position   
   bool              PositionVirtualMagic(Emagic enum_,
                                          string symbol,
                                          datetime CurrentTime);
private:
// function divides the magic into three parts  of three charges, and returns the part to which the category points to
   int               decodeMagic_result(int category); 

// interpretor of instrument symbols into the digital code                                                      
   int               symbolexpert();     
   
// interpretor of the digital code into the prescribed text (Expert Advisors)
   string            expertcode(int code);    
                                 
// interpretor of the digital code into the prescribed text (interaction)   
   string            codeinterdescript(int code);

// interpretor of the digital code into the instrument symbol                                         
   string            symbolexpert(int code);

// cycle of recording tickets into the buffer
   void              prHistory_Deals(ulong &buf[],int HTD);    
  };  

Nun entwickeln wir die Methoden.

Die erste Methode in der Klasse:

//+------------------------------------------------------------------------+
//| Function returns the assembled magic, assembled from the input data    |
//+------------------------------------------------------------------------+
ulong Cmagic::SetMagic_request(int digital_name=0,int code_interaction=0)
  {
   if(digital_name>=1000)Print("Incorrectly specified digital name of the Expert Advisor (more than 1000)");
   if(code_interaction>=1000)Print("Incorrectly specified the code of recognizing yours-foreign (more than 1000)");
   mag.digital_name     =digital_name;
   mag.code_interaction =code_interaction;
   mag.expert_symbol    =symbolexpert();
   mag.magicnumber      =mag.digital_name*(int)pow(1000,2)+
                         mag.code_interaction*(int)pow(1000,1)+
                         mag.expert_symbol;
   return(mag.magicnumber);
  }

Diese Methode erhält zwei Werte: einen digitalen Namen des Expert Advisors und den Code der Interaktion.

ulong Cmagic::SetMagic_request(int digital_name=0,int code_interaction=0)

Und verifiziert sofort ihre Korrektheit:

   if(digital_name>=1000)Print("Incorrectly specified the digital name of the Expert Advisor(more than 1000)");
   if(code_interaction>=1000)Print("Incorrectly specifies the code of recognizing yours-foreign (more than 1000)");

Doch es gibt keine Einschränkungen der Aktionen des Benutzers. Die Arbeit wird selbst bei Fehlern friedlich fortgesetzt.

Als Nächstes folgt die Zuordnung in die Struktur der Eingabedaten, die vom Benutzer festgelegt werden. Allerdings wird das Symbol des Instruments nicht festgelegt und wird aus der privaten Methode abgerufen:

int Cmagic::symbolexpert()

Ich gebe ihren Code nicht an, da er sehr lang ist und in der angehängten Datei bereitgestellt wird. Ich möchte dazu nur sagen, dass diese Methode im Grunde einfach eine Tabelle ist, die jedem Symbol aus dem Fenster "Market view" (Marktübersicht) eine entsprechende Zahl zuordnet: Für EURUSD ist es beispielsweise 1 usw.

Natürlich können Sie diese Daten dynamisch abrufen, indem Sie einen Code schreiben, der abfragt, welche Währungen im Fenster "Market view" verfügbar sind, doch eine Lösung muss der Komplexität des Problems entsprechen und es ist nicht sinnvoll, sich mit dem Aufruf der Fenster auseinanderzusetzen. Deshalb machen wir es einfach: Wir erstellen eine Liste von Währungen und weisen jeder Währung einen Index zu.

Und letztendlich die wichtigste Zeile der gesamten Methode:

mag.magicnumber      =mag.digital_name*(int)pow(1000,2)+
                      mag.code_interaction*(int)pow(1000,1)+
                      mag.expert_symbol;

zusammengesetzt aus den verschiedenen Teilen der gesamten magischen Zahl. Das ist die magische Zahl, die der Order unseres Expert Advisors zugewiesen wird.

Die nächste öffentliche Methode der Klasse:

//+------------------------------------------------------------------+
//| Function obtains the assembled magic                             |
//| and divides it according to the logic of the assembly            |
//+------------------------------------------------------------------+
ulong Cmagic::SetMagic_result(ulong magicnumber)
  {
   mag.magicnumber      =magicnumber;
   mag.expert_symbol    =decodeMagic_result(1);
   mag.code_interaction =decodeMagic_result(2);
   mag.digital_name     =decodeMagic_result(3);
   return(mag.magicnumber);
  }

Diese Methode dient im Grunde als Hülle, die die Ergebnisse von drei Aufrufen einer einzelnen private-Methode in der Struktur verteilt. Die Deklarierung unter einem solchen Spezifizierer ist gut, weil sie beim Aufruf einer Klassenvariable nicht in der Popup-Aufforderung erscheint, was zu dem Eindruck führt, dass die ganze Arbeit von einer public-Funktion erledigt wurde.

Aber zurück zu unseren privaten Funktionen:

//+------------------------------------------------------------------+
//| Function divides the magic into three parts of three charges     |
//| and returns the part, which the category points to               |
//+------------------------------------------------------------------+
int Cmagic::decodeMagic_result(int category)
  {
   string string_value=(string)mag.magicnumber;
   int rem=(int)MathMod(StringLen(string_value),3);
   if(rem!=0)
     {
      rem=3-rem;
      string srem="0";
      if(rem==2)srem="00";
      string_value=srem+string_value;
     }
   int start_pos=StringLen(string_value)-3*category;
   string value=StringSubstr(string_value,start_pos,3);
   return((int)StringToInteger(value));
  }

Visuell kann diese Methode als Lesen einer dreistelligen Zahl aus dem angegebenen Feld dargestellt werden. Haben wir beispielsweise die magische Zahl 123456789, können wir sie als |123|456|789| darstellen. Wenn das festgelegte Feld 1 ist, lautet das Ergebnis 789, weil die Felder von rechts nach links nummeriert sind.

Somit verteilen wir nach der Verwendung aller drei Felder in der aufgerufenen Methode alle erhaltenen Daten in der Struktur. Dazu wird die magische Zahl zum Typ string gebracht:

string string_value=(string)mag.magicnumber;

gefolgt von der Sortierung der einzelnen Komponenten der Zeile.

Es folgen zwei ähnliche Funktionen, die im Wesentlichen Umschalter des Typen switch sind und sich nur in ihrer Art der Ausgabe der Werte unterscheiden:

//+------------------------------------------------------------------+
//| Function obtains the identifier of the return                    |
//| and returns the requested part of the assembled magic            |
//+------------------------------------------------------------------+
ulong Cmagic::GetMagic_result(Emagic enum_)
  {
   switch(enum_)
     {
      case ENUM_DIGITAL_NAME     : return(mag.digital_name);     break;
      case ENUM_CODE_INTERACTION : return(mag.code_interaction); break;
      case ENUM_EXPERT_SYMBOL    : return(mag.expert_symbol);    break;
      default: return(mag.magicnumber); break;
     }
  }
//+------------------------------------------------------------------------------+
//| Function obtains the identifier of the return and returns                    |
//| a textual interpretation of the requested type of the assembled magic        |
//+------------------------------------------------------------------------------+
string Cmagic::sGetMagic_result(Emagic enum_)
  {
   switch(enum_)
     {
      case ENUM_DIGITAL_NAME     : return(expertcode(mag.digital_name));            break;
      case ENUM_CODE_INTERACTION : return(codeinterdescript(mag.code_interaction)); break;
      case ENUM_EXPERT_SYMBOL    : return(symbolexpert(mag.expert_symbol));         break;
      default: return((string)mag.magicnumber); break;
     }
  }

Die Funktionen geben den Anteil der magischen Zahl aus, der den Parameter des Typen Emagic ausgibt. Dabei gibt die erste das Ergebnis in ulong-Form aus, die in Berechnungen verwendet wird, und die zweite in string-Form, die zur Visualisierung genutzt werden kann.

In der Funktion GetMagic_result() ist alles einfach angeordnet. Sie verteilt die Werte der Struktur über die switch-Zweige, während sGetMagic_result() etwas komplizierter ist. Jeder case-Zweig ruft eine Tabellenfunktion auf, die den Wert der Struktur in eine visuelle Form überträgt. Wenn also der Wert mag.expert_symbol=1 ist, gibt die erste Funktion 1 aus, die zweite EURUSD.

Ich habe die Vorteile der Tabellenfunktionen beim Kodieren/Dekodieren der Informationen bereits beschrieben, deshalb erwähne ich an dieser Stelle nur, dass jeder Fall gesondert betrachtet werden muss, basierend auf der Komplexität der Umsetzung einer tabellenlosen Methode, und seine Vorteile gegen die benötigte Zeit zum Schreiben der Tabellen abgewogen werden müssen. Wenn es einfacher ist, eine Tabelle von Zuständen zu schreiben, müssen wir die Sache nicht unnötig verkomplizieren. Doch wenn das Schreiben der Tabelle viel Zeit in Anspruch nimmt, sollten die verfahrensorientierten Methoden angewandt werden. Um Platz zu sparen, stelle ich die Tabellen hier nicht zur Verfügung (Sie finden sie in den angehängten Dateien).

Im Großen und Ganzen war es das. Unsere Klasse ist fertig entwickelt, doch es bleiben noch die vier Funktionen, die wir bei der Entwicklung des vorherigen Expert Advisors verwendet haben. 

Ich habe sie einfach in einer neuen Klasse neu deklariert, mit besonderem Fokus darauf, dass sie leicht abgeändert werden mussten.

Nun ist die Hauptmethode:

bool  Cmagic::PositionVirtualMagic(Emagic enum_,
                                   string symbol,
                                   datetime CurrentTime)

nicht nur als eine Methode der Klasse Cmagic deklariert, sondern hat auch einen anderen Satz von Parametern.

Anstatt der magischen Zahl erhält sie nun den Identifikator nach dem Feld der magischen Zahl, für das die Position berechnet wurde. Außerdem: Auch wenn das Symbol in der letzten Option vorhanden war, wurde es nur zum Abrufen von Informationen über den Schritt des Postens nach Symbol verwendet. Jetzt ist es im Filter festgelegt und kann gleichgestellt mit anderen an der Filterung der Positionszählung teilnehmen.

Was haben wir davon? Jetzt können wir die Transaktionen, die auf einem anderen Instrument, aber mit demselben Expert Advisor eröffnet wurden, gleichzeitig filtern. Dabei werden sie nicht mit einem anderen, ähnlichen Expert Advisor, der auf einem anderen Instrument ausgeführt wird, verwechselt. Ehrlich gesagt, ist es sehr schwer, alle verschiedenen Möglichkeiten zur Nutzung dieses neuen Berechnungssystems zu beschreiben. Und der Leser kann selbst entscheiden, wofür er ein so kompliziertes System braucht. Ich möchte Ihnen nur wirklich ans Herz legen, Fälle, in denen Sie einfach schreiben können, nicht zu verkomplizieren und sich nicht vor solchen Verkomplizierungen zu fürchten, wo sie angemessen sind.

Nun, da die Klasse fertig ist, ist es an der Zeit, sie an einem neuen Expert Advisor zu testen:

//+------------------------------------------------------------------+
//| Code of the Expert Advisor                                       |
//+------------------------------------------------------------------+
//--- input parameters
input ulong              digital_name_       =4;           // Digital name of Expert Advisor
input ulong              code_interaction_   =1;           // Code of interaction
input Emagic             _enum               =0;           // Model of magic number  
input int                SL                  =300;         // Stop Loss
input int                TP                  =1000;        // Take Profit
input int                MA_Period           =25;          // MA period
input double             lot                 =0.4;         // Volume of position
input int                MA_shift            =0;           // Shift of indicator
input ENUM_MA_METHOD     MA_smooth           =MODE_SMA;      // Smoothing type
input ENUM_APPLIED_PRICE price               =PRICE_OPEN;    // Price type 

//--- we will store the indicator's handle
int MA_handle,type_MA,rezult;
static ulong magic;
double v[2];
datetime  CurrentTime;// The variable stores the time of start of the Expert Advisor

MqlTradeResult  res;   // Pointer to the structure of responding by OrderSend
MqlTick         tick;  // Pointer to the structure of last market information
CProvision prov;
Cmagic mg;
//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
   magic=mg.SetMagic_request(digital_name_,code_interaction_);
// Stamp of Expert Advisor (the magic number identifier) the magic variable is declared at the global scope
// used in int CProvision::SendOrder(ENUM_ORDER_TYPE type,double volume)
   CurrentTime=TimeCurrent();// The variable stores the time of start of the Expert Advisor
//--- Create the indicator's handle
   MA_handle=iMA(Symbol(),0,MA_Period,MA_shift,MA_smooth,price);
   return(0);
  }
//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
  {
   if(CopyBuffer(MA_handle,0,0,2,v)<=0)
     {Print("#",magic,"Error of copying");return;}
   type_MA=prov.TYPE(v); // Determine type depending on MA indication
   mg.SetMagic_result(magic);// put the information into the structure
   if(mg.PositionVirtualMagic(_enum,_Symbol,CurrentTime))// If three is an open position 
     {
      if((int)mg.cTYPE()!=type_MA)// Check if it is time to close
        {
         mg.SetMagic_result(magic);// put the information into the structure
         Print("#",mg.GetMagic_result(_enum),"Position by magic number has volume ",mg.cVOLUME(),
               " reverse position of type ",(int)mg.cTYPE()," by ",type_MA);
         //cpvm.cVOLUME() - volume of virtual position
         rezult=prov.SendOrder((ENUM_ORDER_TYPE)type_MA,mg.cVOLUME()+lot);// reverse position
         if(rezult!=-1)Print("№",magic," Code of the operation result ",rezult," volume ",res.volume);
         else{Print("№",magic,"Error",GetLastError()); return;}
        }
     }
   else // If there is no open position then open
     {
      Print("#",magic,"Position by magic number has volume  ",mg.cVOLUME()," open position of type",type_MA);
      rezult=prov.SendOrder((ENUM_ORDER_TYPE)type_MA,lot);// Open position 
      if(rezult!=-1)Print("#",magic," Code of the operation result ",rezult," volume ",res.volume);
      else{Print("#",magic,"Error",GetLastError()); return;}
     }
  }

Wie bereits erwähnt, ist dieser Expert Advisor ausgesprochen einfach und wurde nur erstellt, um die verschiedenen Möglichkeiten zu demonstrieren. Führen Sie ihn dreimal auf einem einzelnen Instrument aus:

Abbildung 6. Einrichtung von drei Expert Advisors mit unterschiedlichen magischen Zahlen auf verschiedenen Diagrammen

Abbildung 6. Einrichtung von drei Expert Advisors mit unterschiedlichen magischen Zahlen auf verschiedenen Diagrammen

Abbildung 7. Das Ergebnis ist ein konfliktfreier Handel der drei Expert Advisors mit unterschiedlichen magischen Zahlen

Abbildung 7. Das Ergebnis ist ein konfliktfreier Handel der drei Expert Advisors mit unterschiedlichen magischen Zahlen

Wie Sie in den Meldungen der Expert Advisors sehen, wurden alle drei Teilnehmer erfolgreich gestartet und standen nicht miteinander im Konflikt.


Fazit

Durch die Möglichkeit, Handelsvorgängen magische Order hinzuzufügen, haben die Schöpfer von MQL5 Schreibern von Expert Advisors das Leben wesentlich erleichtert. Doch Entwickler können Ihnen nur Instrumente zur Verfügung stellen. Die Schätze müssen Sie selber bergen.

Viel Glück und bis zum nächsten Mal.