Download MetaTrader 5

Creating a Multi-Currency Multi-System Expert Advisor

5 December 2013, 11:07
Maxim Khrolenko
9
12 942

Introduction

I believe there are quite a few traders who trade more than one trading symbol and use multiple strategies. This approach does not only allow you to potentially increase your profit but to also minimize the risk of substantial drawdown upon efficient money management. When creating an Expert Advisor, the first natural step in checking the efficiency of the program strategy is optimization in order to determine the best input parameters.

With parameter values identified, Expert Advisors would technically be ready for trading. However that would leave one important question unanswered. What would testing results be like if a trader could put all his strategies together in a single Expert Advisor? The realization that drawdown on several symbols or strategies might at some point overlap and result in a ghastly total drawdown or even a margin call may sometimes come as a nasty surprise.

This article introduces a concept of creating a multi-currency multi-system Expert Advisor that will allow us to find an answer to this important question.


1. Structure of the Expert Advisor

In general terms, the structure of the Expert Advisor is as follows:

Fig. 1. Structure of the multi-currency multi-system Expert Advisor

Fig. 1. Structure of the multi-currency multi-system Expert Advisor

As you can see, the program is based on a for loop. Each strategy is arranged in a loop where each iteration is responsible for trading each symbol separately. Here, you can arrange in loops unlimited number of strategies. Important is for your computer to have sufficient resources to "process" such a program.

You should keep in mind that there may only be one position for each traded symbol in MetaTrader 5. Such position represents the sum of lots of previously executed Buys and Sells. Therefore, the result of multi-strategy testing for one symbol will not be identical to the sum of separate testing results of the same strategies for the same symbol.

For a closer consideration of the structure of the Expert Advisor we will take 2 strategies each of which trades two symbols:

Strategy A:

  • Buy: Ask price reaches the lower band of the Bollinger Bands indicator calculated based on Low price.
    Closing: Bid price reaches the lower band of the Bollinger Bands indicator calculated based on High price.
  • Sell: Bid price reaches the upper band of the Bollinger Bands indicator calculated based on High price.
    Closing: Ask price reaches the upper band of the Bollinger Bands indicator calculated based on Low price.
  • Restriction: only one deal can be executed on any given bar.

Strategy В:

  • Buy: the previous bar is bearish (close < open) and Ask price reaches the previous bar's high.
    Closing: by Stop Loss or Take Profit.
  • Sell: the previous bar is bullish (close > open) and Bid price reaches the previous bar's low.
    Closing: by Stop Loss or Take Profit.
  • Restriction: only one deal can be executed on any given bar.

To be independent from the new ticks for a symbol on which the Expert Advisor will be tested or which it will trade, it is advisable to use the OnTimer() function for trading in multi-currency mode.

For this purpose, when initializing the Expert Advisor we specify the frequency of generating an event for program calculation call using the EventSetTimer() function, and upon deinitialization we use the EventKillTimer() function to tell the terminal to stop generation of events:

// Include standard libraries
// Create external parameters
// Create arrays, variables, indicator handles, etc.

//--- Initialization of the Expert Advisor
int OnInit()
  {
   //--- Set event generation frequency
   EventSetTimer(1); // 1 second
   // ...
   return(0);
  }
void OnTimer()
  {
   // ...
  }
//--- Deinitialization of the Expert Advisor
void OnDeinit(const int reason)
  {
   //--- Stop event generation
   EventKillTimer();
   // ...
  }

Instead of EventSetTimer(), you can also use EventSetMillisecondTimer(), where frequency is set accurate to millisecond but you should not misuse it by too frequent program calculation calls.

For access to account, position and symbol settings, as well as trading functions, we will use CAccountInfo, CPositionInfo, CSymbolInfo and CTrade classes, respectively. Let's include them in the Expert Advisor:

//--- Include standard libraries
#include <Trade\AccountInfo.mqh>
#include <Trade\PositionInfo.mqh>
#include <Trade\SymbolInfo.mqh>
#include <Trade\Trade.mqh>

Since the Expert Advisor is based on for loops, we will need to create arrays for its external parameters. Let's first create constants equal to the number of symbols for each strategy:

//--- Number of traded symbols for each strategy
#define Strategy_A 2
#define Strategy_B 2

We then create external parameters. Using constants, we determine sizes of arrays to which they will be copied. Further, we create indicator handles and other global variables.

An example for one symbol of strategy А is provided below:

//------------------- External parameters of strategy A
input string          Data_for_Strategy_A="Strategy A -----------------------";
//--- Symbol 0
input string          Symbol_A0      = "EURUSD";   // Symbol
input bool            IsTrade_A0     = true;       // Permission for trading
//--- Bollinger Bands (BB) parameters
input ENUM_TIMEFRAMES Period_A0      = PERIOD_H1;  // ВВ period
input uint            BBPeriod_A0    = 20;         // Period for calculation of the moving average of BB
input int             BBShift_A0     = 0;          // Horizontal shift of ВВ
input double          BBDeviation_A0 = 2.0;        // Number of standard deviations of BB
//...
//--- General parameters of strategy A
input double          DealOfFreeMargin_A = 1.0;    // Percent of free margin for a deal
input uint            MagicNumber_A      = 555;    // Magic number
input uint            Slippage_A         = 100;    // Permissible slippage for a deal
//...
//------------- Set variables of strategy A -----
//--- Arrays for external parameters
string          Symbol_A[Strategy_A];
bool            IsTrade_A[Strategy_A];
ENUM_TIMEFRAMES Period_A[Strategy_A];
int             BBPeriod_A[Strategy_A];
int             BBShift_A[Strategy_A];
double          BBDeviation_A[Strategy_A];
//--- Arrays for global variables
double          MinLot_A[Strategy_A],MaxLot_A[Strategy_A];
double          Point_A[Strategy_A],ContractSize_A[Strategy_A];
uint            DealNumber_A[Strategy_A];
datetime        Locked_bar_time_A[Strategy_A],time_arr_A[];
//--- Indicator handles
int             BB_handle_high_A[Strategy_A];
int             BB_handle_low_A[Strategy_A];
//--- Arrays for indicator values
double          BB_upper_band_high[],BB_lower_band_high[];
double          BB_upper_band_low[],BB_lower_band_low[];
//--- Class
CTrade          Trade_A;
//...
//--- Set global variables for all strategies
long            Leverage;
//--- Classes
CAccountInfo    AccountInfo;
CPositionInfo   PositionInfo;
CSymbolInfo     SymbolInfo;

To have the possibility to disable trading for a certain symbol, we have created a Boolean variable IsTrade_A0 that will be placed at the very beginning of for loops.


2. Initialization of the Expert Advisor

First, let's get the values required for all strategies, e.g. leverage. Since leverage is applied to the trading account and has nothing to do with a strategy or a symbol, there is no need to copy its value to the arrays:

//--- Get the leverage for the account
   Leverage=AccountInfo.Leverage();

We then copy external variables to arrays.

//--- Copy external variables to arrays
   Symbol_A[0]     =Symbol_A0;
   IsTrade_A[0]    =IsTrade_A0;
   Period_A[0]     =Period_A0;
   BBPeriod_A[0]   =(int)BBPeriod_A0;
   BBShift_A[0]    =BBShift_A0;
   BBDeviation_A[0]=BBDeviation_A0;

If any external parameter is defined by the type that will require conversion to another one, this can be done in a more convenient way when copying to arrays.

In this case, we can see that BBPeriod_A0 was created as uint to prevent the user from setting a negative value. Here, we convert it to int and copy it to the array which was also created as int. Otherwise, the compiler will give a warning if you try to insert uint type parameter in the indicator handle.

Let's further see whether the traded symbol is available in the Market Watch and whether it has been used more than once within one strategy:

//--- Check for the symbol in the Market Watch
   for(int i=0; i<Strategy_A; i++)
     {
      if(IsTrade_A[i]==false) continue;
      if(IsSymbolInMarketWatch(Symbol_A[i])==false)
        {
         Print(Symbol_A[i]," could not be found on the server!");
         ExpertRemove();
        }
     }

//--- Check whether the symbol is used more than once
   if(Strategy_A>1)
     {
      for(int i=0; i<Strategy_A-1; i++)
        {
         if(IsTrade_A[i]==false) continue;
         for(int j=i+1; j<Strategy_A; j++)
           {
            if(IsTrade_A[j]==false) continue;
            if(Symbol_A[i]==Symbol_A[j])
              {
               Print(Symbol_A[i]," is used more than once!");
               ExpertRemove();
              }
           }
        }
     }
//--- The IsSymbolInMarketWatch() function
bool IsSymbolInMarketWatch(string f_Symbol)
  {
   for(int s=0; s<SymbolsTotal(false); s++)
     {
      if(f_Symbol==SymbolName(s,false))
         return(true);
     }
   return(false);
  }

If the symbols were selected correctly, check for errors in input parameters for each of them, create indicator handles, get the data required for the lot calculation and, if necessary, do other things as defined by the given strategy.

We will implement the above mentioned actions inside a for loop.

//--- General actions
   for(int i=0; i<Strategy_A; i++)
     {
      if(IsTrade_A[i]==false) continue;
      //--- Check for errors in input parameters
      //...
      //--- Set indicator handles
      BB_handle_high_A[i]=iBands(Symbol_A[i],Period_A[i],BBPeriod_A[i],BBShift_A[i],BBDeviation_A[i],
                                 PRICE_HIGH);
      if(BB_handle_high_A[i]<0)
        {
         Print("Failed to create a handle for Bollinger Bands based on High prices for ",Symbol_A[i]," . Handle=",INVALID_HANDLE,
               "\n Error=",GetLastError());
         ExpertRemove();
        }
      //...
      //--- Calculate data for the Lot
      //--- set the name of the symbol for which the information will be obtained
      SymbolInfo.Name(Symbol_A[i]);
      //--- minimum and maximum volume size in trading operations
      MinLot_A[i]=SymbolInfo.LotsMin();
      MaxLot_A[i]=SymbolInfo.LotsMax();
      //--- point value
      Point_A[i]=SymbolInfo.Point();
      //--- contract size
      ContractSize_A[i]=SymbolInfo.ContractSize();

      //--- Set some additional parameters
     }

Then, we set the parameters for trading operations of strategy A using the Trade_A object of the CTrade class.

//--- Set parameters for trading operations
//--- set the magic number
   Trade_A.SetExpertMagicNumber(MagicNumber_A);
//--- set the permissible slippage in points upon deal execution
   Trade_A.SetDeviationInPoints(Slippage_A);
//--- order filling mode, use the mode that is allowed by the server
   Trade_A.SetTypeFilling(ORDER_FILLING_RETURN);
//--- logging mode, it is advisable not to call this method as the class will set the optimal mode by itself
   Trade_A.LogLevel(1);
//--- the function to be used for trading: true - OrderSendAsync(), false - OrderSend().
   Trade_A.SetAsyncMode(true);

The same procedure is repeated for each strategy, i.e.

  1. Copy external variables to arrays;
  2. Check whether symbols are selected correctly;
  3. Check errors, set indicator handles, calculate data for the lot and for everything that is required for a given strategy;
  4. Set parameters for trading operations.

Finally, it would be good to check if one and the same symbol is used in several strategies (an example for two strategies is provided below):

//--- Check whether one and the same symbol is used in several strategies
   for(int i=0; i<Strategy_A; i++)
     {
      if(IsTrade_A[i]==false) continue;
      for(int j=0; j<Strategy_B; j++)
        {
         if(IsTrade_B[j]==false) continue;
         if(Symbol_A[i]==Symbol_B[j])
           {
            Print(Symbol_A[i]," is used in several strategies!");
            ExpertRemove();
           }
        }
     }

3. Trading "For" Loops

The framework of for loops inside the OnTimer() function is as follows:

void OnTimer()
  {
//--- Check if the terminal is connected to the trade server
   if(TerminalInfoInteger(TERMINAL_CONNECTED)==false) return;

//--- Section A: Main loop of the FOR operator for strategy A -----------
   for(int A=0; A<Strategy_A; A++)
     {
      //--- A.1: Check whether the symbol is allowed to be traded
      if(IsTrade_A[A]==false)
         continue; // terminate the current FOR iteration

     }

//--- Section В: Main loop of the FOR operator for strategy В -----------
   for(int B=0; B<Strategy_B; B++)
     {
      //--- B.1: Check whether the symbol is allowed to be traded
      if(IsTrade_B[B]==false)
         continue; // terminate the current FOR iteration

     }
  }

If a single-symbol Expert Advisor based on a single strategy has a condition whereby all subsequent calculations need to be ceased, we use the return operator. In our case, we just need to terminate the current iteration and proceed to the next symbol iteration. For this purpose, it is best to use the continue operator.

If you want to enhance your multi-strategy Expert Advisor by adding a strategy with a for loop that contains a condition for termination of all subsequent calculations, you can use the following pattern:

//--- Section N: Main loop of the FOR operator for strategy N -----------
for(int N=0; N<Strategy_N; N++)
  {

   //...
   bool IsInterrupt=false;
   for(int i=0; i<Number; i++)
     {
      if(...) // terminate all calculations
        {
         IsInterrupt=true;
         break;
        }
     }
   if(IsInterrupt=true)
      continue; // terminate the current FOR iteration
   //...

  }

After creating the framework of the for loops, we simply insert in it codes from other EAs and then replace some variables with array elements.

For example, we change the predefined variable _Symbol to Symbol_A[i] or _Point to Point_A[i]. Values of these variables are typical of the given symbol and were therefore copied to arrays upon initialization.

For instance, let's find the indicator value:

 //--- A.3: Lower band of BB calculated based on High prices
 if(CopyBuffer(BB_handle_high_A[A],LOWER_BAND,BBShift_A[A],1,BB_lower_band_high)<=0)
    continue; // terminate the current FOR iteration
 ArraySetAsSeries(BB_lower_band_high,true);

To implement closing of a buy position, we will write the following code:

 //--- A.7.1: Calculate the current Ask and Bid prices
 SymbolInfo.Name(Symbol_A[A]);
 SymbolInfo.RefreshRates();
 double Ask_price=SymbolInfo.Ask();
 double Bid_price=SymbolInfo.Bid();

 if(PositionSelect(Symbol_A[A]))
   {
    //--- A.7.2: Closing a BUY position
    if(PositionInfo.PositionType()==POSITION_TYPE_BUY)
      {
       if(Bid_price>=BB_lower_band_high[0] || DealNumber_A[A]==0)
         {
          if(!Trade_A.PositionClose(Symbol_A[A]))
            {
             Print("Failed to close the Buy ",Symbol_A[A]," position. Code=",Trade_A.ResultRetcode(),
                   " (",Trade_A.ResultRetcodeDescription(),")");
             continue; // terminate the current FOR iteration
            }
          else
            {
             Print("The Buy ",Symbol_A[A]," position closed successfully. Code=",Trade_A.ResultRetcode(),
                   " (",Trade_A.ResultRetcodeDescription(),")");
             continue; // terminate the current FOR iteration
            }
         }
      }

    //...
   }

Opening a Buy position:

 //--- A.9.1: for a Buy
 if(Ask_price<=BB_lower_band_low[0])
   {
    //...

    //--- A.9.1.3: Execute a deal
    if(!Trade_A.Buy(OrderLot,Symbol_A[A]))
      {
       Print("The Buy ",Symbol_A[A]," has been unsuccessful. Code=",Trade_A.ResultRetcode(),
             " (",Trade_A.ResultRetcodeDescription(),")");
       continue; // terminate the current FOR iteration
      }
    else
      {
       Print("The Buy ",Symbol_A[A]," has been successful. Code=",Trade_A.ResultRetcode(),
             " (",Trade_A.ResultRetcodeDescription(),")");
       continue; // terminate the current FOR iteration
      }
   }

Remember to terminate timer event generation and delete the indicator handles at deinitialization.


4. Test Results

When the Expert Advisor is ready, we test each strategy and each symbol separately and compare the test results with the ones obtained in the test mode when trading all strategies and symbols simultaneously.

It is assumed that the user has already identified the optimal values of input parameters.


Below are the settings of the Strategy Tester:

Fig. 2. Strategy Tester settings

Fig. 2. Strategy Tester settings

Results for strategy A, EURUSD:

Fig. 3. Test results for strategy A, EURUSD

Fig. 3. Test results for strategy A, EURUSD

Results for strategy A, GBPUSD:

Fig. 4. Test results for strategy A, GBPUSD

Fig. 4. Test results for strategy A, GBPUSD

Results for strategy B, AUDUSD:

Fig. 5. Test results for strategy В, AUDUSD

Fig. 5. Test results for strategy В, AUDUSD

Results for strategy B, EURJPY:

Fig. 6. Test results for strategy В, EURJPY

Fig. 6. Test results for strategy В, EURJPY

Test results for all strategies and symbols:

Fig. 7. Test results for all strategies and symbols

Fig. 7. Test results for all strategies and symbols


Conclusion

As a result, we have a convenient and simple structure of the multi-currency multi-system Expert Advisor in which you can place virtually any of your strategies.

Such an Expert Advisor allows you to better assess the efficiency of trading using all your strategies. It may also prove useful in case only one Expert Advisor is allowed to work on a given account. The source code of the Expert Advisor is attached to the article to facilitate studying the above information.


Translated from Russian by MetaQuotes Software Corp.
Original article: https://www.mql5.com/ru/articles/770

Attached files |
2multi_en.mq5 (30.27 KB)
Last comments | Go to discussion (9)
Rogerio Figurelli
Rogerio Figurelli | 22 Dec 2013 at 12:55
q.import:
You would have to have at a min. 23 accounts running at a min 46 bots buy and sell hedge and re-hedge exit and hedge more. It's a never ending cycle, very complex monitoring each and every position in a way as not to erase ones signal from profitability, not as easy as the article above, its a very nice proof of concept. I love it.

As I stated before, you have the same result, and easier, with a simple EA using several graphs and just one account, to trade more than one trading symbol and use multiple strategies.
The only condition is: your algorithm must check the symbols and do specific operations, not generic, just for the group symbol you define, and you must have input parameters to define each group of symbols.

For instance, imagine a simple Moving Average Crossing EA, you just need adapt the code to be as simple as code below. This will enable any EA to be multi-currency multi-system in one account, and you can have 12 symbols and 4 groups of independent backtesting adjustment (period_MA1 and period_MA2).

// Structure of the multi-currency multi-system Expert Advisor using 1 account and multiple graphs

input string symbol1="EURUSD"; // graph group symbol 1 
input string symbol2="EURJPY"; // graph group symbol 2  
input string symbol3="USDJPY"; // graph group symbol 3  
int period_MA1=14; // period of moving average 1
int period_MA2=9; // period of moving average 2

// algorithm symbol test (execute the strategy algorithm testing the name of the symbol)
if (...==symbol1 || ...==symbol2 || ...==symbol3) {
   ...
}
q.import
q.import | 24 Dec 2013 at 01:18
figurelli:

As I stated before, you have the same result, and easier, with a simple EA using several graphs and just one account, to trade more than one trading symbol and use multiple strategies.
The only condition is: your algorithm must check the symbols and do specific operations, not generic, just for the group symbol you define, and you must have input parameters to define each group of symbols.

For instance, imagine a simple Moving Average Crossing EA, you just need adapt the code to be as simple as code below. This will enable any EA to be multi-currency multi-system in one account, and you can have 12 symbols and 4 groups of independent backtesting adjustment (period_MA1 and period_MA2).

I agree its great have one EA for 12 symbols even more if your into exotics. Indicator data is not accurate. Example IBFX opens after FXCM and FXCM open after Dukascopy the moving average would be off, so would all the other indicators. Its a parlor trick, Real Economic data and Economic Indicators that don't rely on opening or closings but price alone. technical indicators are great psychological indications of public perception of price for speculation. The market place is so much bigger and correlated and to rely on only 1 ea with a couple of tricks to get by would only result in a bad EA later down the road, it may work for 2010 but not in 2012 nope thats not for me. Multiple EA's hedging against psychological perception vs hard economic data positions, responding to Commodities , Securities, Gov Debt. pushing hard numbers and seeing how the world's market places respond and adjusting my position accordingly with the release of the data against all market sectors. Its very complex. MQL5 Language has come alone way and I love to see progress such as the article I read. I see progress that allows people to only be limited by there imagination. I am building on the code and developing a framework like I said its a great proof of concept. I see amazing things coming on the road ahead for metaqoutes and the people who dedicate there time.
Vantages
Vantages | 2 Sep 2014 at 03:15

Hello.

This code of EA is exactly what I'm looking for.

However, I'm doing it semi-auto with my custom EA with 15 symbols

though, I'm only controlling 2-3 at most. Would there be any version for

MT4?

 

Thanks. 

Vantages
Vantages | 2 Sep 2014 at 03:18

Furthermore, I'm not using any indicators; my account equity is my indicator...

Kashif Javed
Kashif Javed | 13 Dec 2014 at 08:13
I tested strategy A for GBPUSD for same period with same settings in mt4 but my results are not same. Any idea why?
Step on New Rails: Custom Indicators in MQL5 Step on New Rails: Custom Indicators in MQL5

I will not list all of the new possibilities and features of the new terminal and language. They are numerous, and some novelties are worth the discussion in a separate article. Also there is no code here, written with object-oriented programming, it is a too serous topic to be simply mentioned in a context as additional advantages for developers. In this article we will consider the indicators, their structure, drawing, types and their programming details, as compared to MQL4. I hope that this article will be useful both for beginners and experienced developers, maybe some of them will find something new.

Here Comes the New MetaTrader 5 and MQL5 Here Comes the New MetaTrader 5 and MQL5

This is just a brief review of MetaTrader 5. I can't describe all the system's new features for such a short time period - the testing started on 2009.09.09. This is a symbolical date, and I am sure it will be a lucky number. A few days have passed since I got the beta version of the MetaTrader 5 terminal and MQL5. I haven't managed to try all its features, but I am already impressed.

False trigger protection for Trading Robot False trigger protection for Trading Robot

Profitability of trading systems is defined not only by logic and precision of analyzing the financial instrument dynamics, but also by the quality of the performance algorithm of this logic. False trigger is typical for low quality performance of the main logic of a trading robot. Ways of solving the specified problem are considered in this article.

Using text files for storing input parameters of Expert Advisors, indicators and scripts Using text files for storing input parameters of Expert Advisors, indicators and scripts

The article describes the application of text files for storing dynamic objects, arrays and other variables used as properties of Expert Advisors, indicators and scripts. The files serve as a convenient addition to the functionality of standard tools offered by MQL languages.