The Use of ORDER_MAGIC for Trading with Different Expert Advisors on a Single Instrument

Mykola Demko | 20 July, 2010


Introduction

In MQL5 we have a capability to assign a magic number to each pending order, in order to use this information to identify the order. This opens up the vast possibilities of interaction between different Expert Advisors, and the development of even more complex systems. In this article, I would like to inform the public about the undervalued opportunities of the Magic number.

But before we proceed to the essence of this article's subject, we need to gain a better understanding of what constitutes the Magic number. What could be magical in a number, which determines which Expert Advisor set it? The "miracles" begin with the opportunities, which developers set lay down into the type ulong, which is declared by the Magic number.

Type ulong is the longest

If we observe in detail the integer type long, we see that the maximum value of this type is simply phenomenal:

Type

Size in bytes

Minimum value

Maximum value

Analogue in the language 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

Table 1. Properties of data types long and ulong

but the type ulong outdid him by combining the positive and negative mantissa .

So, the specified length is huge, but how was it used before?

In my experience of working in mql 4, I often noticed the senselessness of coding the Magic number by many developers. The Magic number was used sensibly, but its coding seemed quiet silly. What can be said about the individuality of the Magic number 12345, such Magic is uses by almost half of the developing fraternity. The second half uses the Magic number 55555, 33333 and 77777, and this is pretty much the complete set. I want to draw the reader's attention to the fact that it is unlikely that his computer has more than 1,000 Expert Advisors, so the number 1000 will be sufficient to encode the individual name of all your Expert Advisors.

1000 - is only 3 full categories, so what should we do with the remaining 15 full categories, which are available in the type ulong? The answer is simple: encode them.

What does Wikipidia say about the word code:

The Code - rule (algorithm), the comparison for each individual message of a strictly particular combination of symbols (characters) (or signals).

Therefore, we will make the rules. I propose to prescribe in the code of the Magic number, not only the ID of the Expert Advisor, but also the instrument, on which it is running. The fact that the Expert Advisor is running, for example, on EURUSD, does not mean that the Expert Advisor will display an order only on that instrument. Also, I think it will be useful to write the code of interaction of Expert Advisors, something like "yours / foreign", so that the Expert Advisor, when checking the positions, could understand that the current order is constructed by a friendly Expert Advisor. I think this will be enough to create a very complex system.

And so let us sum up what we have: what opportunities are we laying down into the system:

  1. The possibility of two or more Expert Advisors to work on a single instrument and not interfere.
  2. The possibility of two or more Expert Advisors to work on different instruments and complement each other.
  3. The ability to identify the order by the instrument, working with the Expert Advisor.

And so, the task is set, let us now begin its implementation.

Simple Expert Advisor

Draft the code of the simple Expert Advisor- for example, hold the position in the direction of the Moving. I think that the reader, who decided to parse the Magic number, has already read the article Step-By-Step Guide to Writing an Expert Advisor in MQL5 for Beginners, if not, I highly recommend to do so, since I will not go into details into the creation of the Expert Advisor. Basically, the Expert Advisor will be opening the position one time, and turn it for all other times. Therefore, we will need the function for opening the position, i.e. for placing the trading request (trading order).

Create an auxiliary class, which will calculate the parameters for us for filling the fields of the trading request structure.

//+------------------------------------------------------------------+
//| 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));
  }

Having such a class, we can write a code for a simple Expert Advisor without a problem: 

//+------------------------------------------------------------------+
//| 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;}
     }
  } 

Run it and make sure that the Expert Advisor does not differ in profitability, but trades exactly in accordance to the specified logic, which is precisely what we need from it.

Figure 1. The work of one Expert Advisor on a single instrument

Figure 1. The work of one Expert Advisor on a single instrument

Now we will try to run this EA, but on different time frames of one instrument (for the experiments we have chosen a random instrument, which is EURUSD: o)

Figure 2. The conflict of two Expert Advisors on the same instrument on different timeframes

Figure 2. The conflict of two Expert Advisors on the same instrument on different timeframes

Since both Expert Advisors are running on a single instrument, and the code doesn't specify the sharing of positions, then both of the Expert Advisors are trying to correct the trading position, depending on the readings of their indicators, and as a consequence of this, - a conflict arises. The Expert Advisor which is running on M1, is trying to turn the position in the Cell, while its rival seeks to stop it. It is obvious that we need a separate calculation of positions, which is what we will now do.


Position or virtual position?

Since in MetaTrader 5 developers have shifted from orders to the consideration of positions, it makes sense to consider in more detail the functions associated with recording positions.

// 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);

Identifiers of the enumerations for the functions of obtaining request of the compliant position properties PositionGetDouble, PositionGetInteger, and PositionGetString which are given in Tables 2-4.

Identifier

Description

Type

POSITION_VOLUME

Position volume

double

POSITION_PRICE_OPEN

Price of position

double

POSITION_SL

Level of Stop Loss for the open position

double

POSITION_TP

Level of Take Profit for the open position

double

POSITION_PRICE_CURRENT

Current price by the symbol

double

POSITION_COMMISSION

Commission

double

POSITION_SWAP

Accumulated swap

double

POSITION_PROFIT

Current profit

double

Table 2. Enumeration value ENUM_POSITION_PROPERTY_DOUBLE

Identifier

Description

Type

POSITION_TIME

Time of opening of positions

datetime

POSITION_TYPE

Type of position

ENUM_POSITION_TYPE

POSITION_MAGIC

Magic number for the position (see ORDER_MAGIC )

long

POSITION_IDENTIFIER

Identification of the position - this is a unique number that is assigned to each re-opened position and which does not change throughout its life cycle. The turnover of a position does not change its ID.

long

Table 3. Enumeration values ENUM_POSITION_PROPERTY_INTEGER

Identifier

Description

Type

POSITION_SYMBOL

Symbol, for which the position is opened

string

POSITION_COMMENT

Commentary to the position

string

Table 4. Enumeration values ENUM_POSITION_PROPERTY_STRING

From the functions, we can clearly see that the language doe not contain the division of positions, based on the principle of "who put out the order", but the possibility of such records is available since the ORDER_MAGIC, the POSITION_MAGIC and the DEAL_MAGIC are the same exact number, and are taken from the magic number, indicated by the user. The POSITION_MAGIC is taken from the DEAL_MAGIC, which opens the position, and the DEAL_MAGIC in turn, is taken from the ORDER_MAGIC, of the placed order.

Identifying an order, transaction, or position can be done without a problem, but it is impossible to lead out a position by a particular Magic number. Now we will try to eliminate this shortcoming . Let's create analogues of built-in functions, but with identification by the Magic number. Declare a class for working with a virtual position on the Magic number.

Since we have the opportunity to work with the OOP, let's also declare our own structure (gaining additional practice of writing objectively).

//+------------------------------------------------------------------+
//| 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);
  }

In the text above (where the code of the CProvision class is given), it was not explained where everything come from and where it further goes, since the development of the Expert Advisor is not the topic of this article.

But we will consider in details the class CPositionVirtualMagic.

The class is given the structure:

struct SPositionVirtualMagic

which is used for accepting the calculations results, such a global declaration within the class, thanks to pvm (variable of the structure), this structure will be available everywhere, in any method of the class.

Next follow the two methods of the class:

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

These methods are declared as public, and therefore, they will be available through the called class variable, at any place in the program, and are designed for output of the structure values in the requested location.

This is also the section where the following method is declared:

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

This is the main function of the class, and we shall further focus on its detailed analyses, and in the meantime, I will run ahead of myself and describe the function under the specifier of private access:

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

This method produces a ticket record of transactions into the array, which is basically a cycle, and could be described in the called function, but I wanted to reduce the size (in order to increase the readability of the code) of the function PositionVirtualMagic(), and so I moved this cycle beyond the function's limits, and demonstrated how to use the specifier of the private access.

So let's go back to the  PositionVirtualMagic() . This function, at its very beginning, has a one-line calculation of accuracy, to which you need to round the double-value of the volume of the calculated position.

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

This is needed for making the operation of comparison to zero, otherwise some balance in the 8th digits after the decimal point, will prevent us from equating the value to zero, and will cause an execution error.

The position volume is rounded to the minimum step. And if the minimum step is greater than 1, then the rounding is done by the integral part. Next is the cycle while, but it is used in a new way (different from the one in mql4), since the verification of the truthful expression is done at the end of the cycle, rather then at the beginning:

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

Such approach is used because the expression of truthfulness is computed within the cycle, and at its launch, it is not yet prepared for this verification.

The cycle contains the Uploading of the history , I want to draw the attention of the reader to the fact that this is a required condition in order to make ensure the work of the built-in functions of working with the history.

HistorySelect(0,TimeCurrent())

I think that I should explain my system of choosing the variable names.

The attentive reader should have noticed that the names of the classes are defined by the initial letter "C", this is not required by the syntax and any name can be given, but such a way makes the reading much easier. If the letter "C" appears before the name, we know right away that this is the name of the class, and if the letter is "S" - then it is a structure. If the variable takes the value of some built-in function, then I simply change the components of the function's name and obtain the variable name, for example like this:

CurrentTime = TimeCurrent();

Simple and readable, we can immediately see what the variable contains. Especially since MetaEditor contains the function for dragging a particular piece of code into a specified location.

Reviewing the code further, we see that after the uploading of the history, follows the call to the function:

History_Total_Deals=HistoryDealsTotal();

with the storage of the number of transactions into the variable. By the same condition, we will be implementing the verification for exiting the cycle. What do we need this verification for? And why can't we simply upload the history and then retrieve the transactions from it?

The issue lays in the fact that during the the work of the Expert Advisors, the history will be requested separately by each EA, and thus if the Expert Advisors are running at different times, then the depth of the history will be different. And this means that when one Expert Advisor enters the cycle and uploads the history for its period, then before reaching the end of the cycle, it may discover that this history has already been uploaded by the request of another Expert Advisor, and so a verification for authenticity needs to be made.

Incidentally, it may not be the best type of verification, but it works. And so, let's continue. In the cycle we call up the class method, which inputs the ticket values of the transactions into a specially prepared buffer. After calling the prHistory_Deals () function, we again produce the upload of the history.

This is how the verification is arranged of whether or not there has been any changes in the history of trades, over the course of the work of the prHistory_Deals () function. If there were no changes, then the variable of History_Total_Deals will be equal to the HistoryDealsTotal (), and an exits from the cycle for a single pass will occur. If there were changes, the system will launch a second cycle, and continue to repeat until the history of tickets is uploaded without any errors (and do not forget to put ";" at the end):

while(History_Total_Deals!=HistoryDealsTotal());

Further in the cycle for, the calculation of virtual positions takes place.

If the transaction has successfully passed a series of filters (the time of the transaction and the transaction Magic number), then its volume increases that portion of the virtual position, the type of which the transaction belongs to.

I want to note that I record the calculations of the virtual positions only from the launch of the Expert Advisor, although other options are possible.

Here it should be noted how exactly the position is calculated. By the record book, which we all have been using from immemorial time and to this day, we have expenditure and profit, and the counting of the balance is kept as the difference between these values, the same scheme applies to the calculation of a position: if you open lots, 0.2 to Sell and 0.3 to Buy, this means that you hold the position of 0.1 for Buy. Time of opening and the difference in levels are categories of profit, but the position you will be holding is 0.1 lots, the Buy type.

This is why we simply summarize all transactions, made by the Expert Advisor on Buy and separately, on Sell, then we compare them and get the general position (actually, this is what the rest of the examined function is engaged with).

Calculation the volume of positions:

volume=NormalizeDouble(volume_BUY-volume_SELL,DIGITS);

Recognition of the type of position with the output of the value in the structure:

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

Output of volume into the structure:

pvm.volume=fabs(volume);

 The output of value of the function: if the volume of position is 0, then it is false, otherwise, if the position exists, then it is true:

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

Now, having the function of the virtual position, we can easily draw up the code of the Expert Advisor, which will not conflict with its "neighbors".

To save up space, I will provide certain parts of the code, which were not laid out above, rather than the entire code itself.

//+------------------------------------------------------------------+
//| 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;}
     }
  }

Run the Expert Advisor three times on a single instrument, but with different timeframes, as well as assign different magic numbers each time:

Figure 3. Let's assign different magic numbers to two identical Expert Advisors, (one instrument, different timeframes) launch of the first Expert Advisor

Figure 3. Let's assign different magic numbers to two identical Expert Advisors, (one instrument, different timeframes) launch of the first Expert Advisor

Figure 4. Let's assign different magic numbers to two identical Expert Advisors (one instrument, different timeframes) launch of the second Expert Advisor

Figure 4. Let's assign different magic numbers to two identical Expert Advisors (one instrument, different timeframes) launch of the second Expert Advisor

Figure 5. The result is a conflict-free work of Expert Advisors on a single instrument, with various magic numbers

Figure 5. The result is a conflict-free work of Expert Advisors on a single instrument, with various magic numbers

The trial run has been passed successfully, the Expert Advisors obligingly yield way to each other, and no conflicting matters seem to be present.

The first point of the Technical Specifications has been implemented, but there is more to come.


Coding the magic

For the implementation of the following parts, we will need to develop a class of methods, which will encode / decode the information, as well as retrieve the values from the built-in functions, and transform them into a specified format.

To do this, repeat the terms of coding (so to speak, the technical specifications for development):

To begin with, let's select the name for the new class, - let it be magic (a generic name), assign our enumeration, to make the code more visually comprehendable.

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
  };

Enumeration works simply: you describe the names, separated by commas, and the compiler assigns them numbers by the sequence.

First of all, if you assign a variable of a different type (this is not applicable to numbers), then when specifying a parameter from the enumeration, you will receive an error during compilation, and secondly, you obtain clarity: you do not just simply assign 0 , but rather give the command to assign ENUM_DIGITAL_NAME .

As with the creation of a structure or a class, I chose a simple name for the enumeration. I simply added E to the generally selected name, and obtained Emagic , respectively, the corresponding structure will be Smagic , and the class Cmagic .

Again, pay attention that this matter is not mandatory, and you can call the enumeration Enumerator, the structure Structurer, and the class Classifier. But this will not provide a commonality in names, and reading this type of code will be uncomfortable.

Next, let's create a structure for storing our 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
  };

After this, declare the Cmagic class, in which we register all of the methods of encoding and decoding the Magic, including the methods from the previous Expert Advisor (simply declare them in the current class and rewrite the headers)

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);    
  };   

Now we'll developed the methods.

The first method in the class:

//+------------------------------------------------------------------+
//| 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);
  }

This method receives two values: a digital name of the Expert Advisor and the code of interaction.

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

And verifies their correctness immediately:

   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)");

But there are no reprisals against the user's actions, even in case of an error, it meekly continues to work.

Next comes the assignment into the structure of the input data, which the user specifies, however the instrument symbol is not specified, and is obtained from the private method:

int Cmagic::symbolexpert()

I will not provide its code, because it is very long and is given in the attached file. Let me just say that this method is basically just a table, which assigns every symbol from the "market view" window a corresponding number: for EURUSD, for example, it's 1, etc.

You can certainly obtain this data dynamically, by writing a code for a survey of what currencies are present in the "market view" window, but the solution must correspond to the complexity of the problem, and it makes no sense to deal with calling up the windows, thus we will do it the simple way, - comprise a list of currencies and assign each one of them an index.

And, finally, the most essential line of the whole method:

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

assembled from the disparate parts of the whole Magic. This is the Magic that will be assigned to the order of our Expert Advisor.

The next public method of the class:

//+------------------------------------------------------------------+
//| 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);
  }

Actually, this method serves as a shell, distributing through the structure the results of three calls of a single private-method. The declaration under such specifier is good in the fact that they are not shown in the pop-up prompt message, when you call up a class variable, creating the impression that all the work was done by a public-function.

But let us return to our private functions:

//+------------------------------------------------------------------+
//| 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));
  }

Visually, this method can be represented as a reading of a three-digit number from the specified field, for example, if we have a Magic 123456789 , we can represent it as | 123 | 456 | 789 | if the specified field is 1 , then the result will be 789 since the fields are numbered from right to left.

Thus, after using all three fields in the called method, we distribute to the structure of all the acquired data. This is done through a procedure of bringing the Magic to a minuscule type string:

string string_value=(string)mag.magicnumber;

followed by the sorting out of individual line components.

Next follow two similar functions, which are in their essence switches switch, and differ only in the type of the output values:

//+------------------------------------------------------------------+
//| 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;
     }
  }

Functions return the portion of the Magic, which specifies the parameter of type Emagic, with the first one giving the result in the form of ulong, which is used in calculations, and the second one giving the results of type string, which can be used for visualization.

In the function GetMagic_result () everything is organized simply, it distributes the values of the structure throughout the branches switch, whereas sGetMagic_result () is a little more complicated. Each branch case calls up a table function, which transfers the value of the structure into a visual form. Thus, if the value mag.expert_symbol = 1 , then the first function will give 1 , and the second EURUSD .

I have already described the advantages of the table functions in encoding / decoding the information, so I will only mention that each case should be considered separately, based on the complexity of implementation of a table-less method, and its advantages to the time needed to write the tables. If it is easier to write a table of states, then there is no need to complicate matters. But if the writing of the table will take up a lot of time, then, obviously, the preference should be given to the procedural methods. I am not providing the tables in order to save up space (they can be found in the attached files).

Basically this is it, our class is developed, however, there are still the four remaining functions that we used in the development of the previous Expert Advisor. 

I simply redeclared them in a new class, especially considering that they needed to be slightly modified.

Now the main method:

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

not only is declared as a method of class Cmagic but also has a different set of parameters.

Instead of the Magic, it now gets the identification by the field of the Magic, which the position was calculated. Besides, even though the symbol was present in the last option, it was only used for obtaining information about the step of the lot by the symbol. And now it is prescribed in the filter, and can participate, on an equal with others basis, in the filtration of position counting.

What does this give us? Now we can simultaneously filter the transactions, which were open on a different instrument, but by the same Expert Advisor. In doing so they will not be confused with other, similar Expert Advisors, running on a different instrument. To be honest, it is very difficult to describe all of the different ways of using this new system of calculations. And the reader can personally decide what he needs such a complicated system for. I only strongly advise you not to complicate instances, where you can write simply, and not to fear such complications when there is an obvious need for it.

Well, since the class has been designed, it is time to test it on a new Expert Advisor:

//+------------------------------------------------------------------+
//| 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;}
     }
  }

As mentioned earlier, this Expert Advisor is extremely simple and was created only to demonstrate the different capabilities, run it three times on a single instrument:

Figure 6. Installation of three Expert Advisors, with different magics on different graphs

Figure 6. Installation of three Expert Advisors, with different magics on different graphs

Figure 7. The result is conflict-free trading of three Expert Advisors with different magics

Figure 7. The result is conflict-free trading of three Expert Advisors with different magics

As can be seen from the printouts of the Expert Advisors' messages, all three participants were launched successfully and demonstrated no conflicts.


Conclusion

By providing the opportunity to assign magical orders to trading operations, the creators of MQL5, have greatly facilitated the life of Expert Advisor writers. But developers can only provide you with instruments - you need to be the one to actually obtain the diamonds.

Good luck and until we meet again.