Multi-symbol Expert some code for sharing - MT5

Hello MT5 programmers.

I’ve been putting together a multi-symbol EA and am sharing the part of the expert that may be of some interest to some of you.

Please note that I am no programmer and have only started using MT5 in the last 2 weeks, so the code is by no means elegant nor probably very efficient. If someone can improve upon it and share that, great.

The basis of the multi-symbol operation is as follows:

  1. Upon initialisation, synchronise history of all required symbols (28 in my case) and download sufficient bars for each. I have used the work of Andrey Khatimlianskii to do this.

  2. Look for a new bar in the current time frame and symbol (EURUSD, M1 in my case). This is based on the approach provided by Andrew R Young in his book in MT4 programming.

  3. Wait for all symbols to open a new bar that matches the time stamp of the chart to which the EA is attached. This is the while loop commencing at line 415 in the EA.

  4. When a new bar is open on all symbols, download Closing price history for each symbol, sufficient to run the EA.

This seems to work fine in “live” mode as well as when used as part of an Indicator. However there is a problem that I can not solve. When running in Strategy Tester, the CopyTime loop exits on Time Expiry (line 455) as it is unable to find a new bar for all 28 symbols. If anyone can suggest a fix for this, that would be great as well.

cheers

Files:

MultiCurrency_EA_-Testing_Concept-TimeSeries-_Rev_1.1.mq5 32 kb

Hi,

You’re off to a good start for only two weeks. If you’re investing time in learning MQL5 I would advise picking up a good c++ tutorial (there aren’t many good modern-MQL tutorials) so you can get a good foundation in OOP.

One thing I want to point out that will kill your EA is the fact that you hard coded all your symbols, and as soon as you use a broker with appended symbols (eg. EURUSDi) your entire system will fail to work.

Here is an example of using OOP and the standard library to accomplish the task (and more) while reducing a lot of redundant code. Feel free to take this EA and set a break-point at the OnInit() and step through the code… it will help you learn by watching what’s happening at every step. Good luck on your learning journey! :slight_smile:

//+------------------------------------------------------------------+
//|                                                MultiCurrency.mq5 |
//|                                                      nicholishen |
//|                                   www.reddit.com/u/nicholishenFX |
//+------------------------------------------------------------------+
#property copyright "nicholishen"
#property link      "www.reddit.com/u/nicholishenFX"
#property version   "1.00"
#include <Trade\SymbolInfo.mqh>
#include <Arrays\ArrayObj.mqh>

class MySymbol : public CSymbolInfo
{
protected:
   ENUM_TIMEFRAMES   m_period;
   MqlRates          m_rates[];
   datetime          m_bartime;
public:
   MySymbol(const string symbol,ENUM_TIMEFRAMES period):m_period(period)
   {
      m_name = symbol;
      ArraySetAsSeries(m_rates,true);
   }
   int BarsTotal() const { return ArraySize(m_rates);}
   bool RefreshRates()
   {
      datetime btime = (datetime)SeriesInfoInteger(m_name,m_period,SERIES_LASTBAR_DATE);
      if(m_bartime != btime || BarsTotal() < 300)
      {
         if(CopyRates(m_name,m_period,0,Bars(m_name,m_period),m_rates)<300)
            return false;
         m_bartime = btime;
      }
      return CSymbolInfo::RefreshRates();
   }
   double   Open  (const int i) const { return m_rates[i].open; }
   double   High  (const int i) const { return m_rates[i].high; }
   double   Low   (const int i) const { return m_rates[i].low ; }
   double   Close (const int i) const { return m_rates[i].close;}
   datetime Time  (const int i) const { return m_rates[i].time; }
};

class MySymbolCollection : public CArrayObj
{
public:
   MySymbol* operator[](const int index)const{return(MySymbol*)At(index);}
   bool  Init(string &syms[])
   {
      Clear();
      for(int i=0;i<ArraySize(syms);i++)
      {
         for(int j=0;j<SymbolsTotal(false);j++)
         {
            if(StringFind(SymbolName(j,false),syms[i])>=0)
            {
               Add(new MySymbol(SymbolName(j,false),Period()));
               break;
            }
         }
      }
      EventSetTimer(1);
      return true;
   }
   bool RefreshRates()
   {
      bool res = true;
      for(int i=0;i<Total();i++)
         if(!this[i].RefreshRates())  
            res = false;
      return res;
   }
};

string sym[]={    "AUDCAD","AUDCHF","AUDJPY","AUDNZD","AUDUSD","EURAUD","GBPAUD",
                  "CADCHF","CADJPY","EURCAD","GBPCAD","NZDCAD","USDCAD","CHFJPY",
                  "EURCHF","GBPCHF","NZDCHF","USDCHF","EURGBP","EURJPY","EURNZD",
                  "EURUSD","GBPJPY","GBPNZD","GBPUSD","NZDJPY","USDJPY","NZDUSD"};
MySymbolCollection symbols;
//+------------------------------------------------------------------+
int OnInit()
  {
//---
   symbols.Init(sym);
   symbols.RefreshRates();
//---
   return(INIT_SUCCEEDED);
  }

void OnTick()
{
//---
   if(!symbols.RefreshRates())
   {
      Print(__FUNCTION__+" Waiting for data");
      return;
   }
   else
   {
      for(int i=0;i<symbols.Total();i++)
         Print("My Symbol is ",symbols[i].Name()," and I have copied ",symbols[i].BarsTotal()," bars to rates.");
      ExpertRemove();
   }
   // --- stuff with symbol data....
}
//+------------------------------------------------------------------+
void OnTimer()
{
   symbols.RefreshRates();
   EventKillTimer();
}

Files:

MultiCurrency.mq5 4 kb

Thanks for the advice hedinboroughv

I’ll study your code.

Just to be clear (for newbies) there is absolutely no need to know or even use OOP with mql5. It’s optional.

Thanks . Reassuring comment.

By the way, have you any suggestions as to why the CopyTime loop works fine in live mode but not in strategy tester?

I’m not sure there’s too much to be gained in an all or nothing refresh strategy… Why not just update each symbol as a new bar comes in for that symbol? Here is the example in the code I posted…

 bool RefreshRates()
   {
      datetime btime = (datetime)SeriesInfoInteger(m_name,m_period,SERIES_LASTBAR_DATE);
      if(m_bartime != btime || BarsTotal() < 300)
      {
         if(CopyRates(m_name,m_period,0,Bars(m_name,m_period),m_rates)<300)
            return false;
         m_bartime = btime;
      }
      return CSymbolInfo::RefreshRates();
   }

If you’re generating signals down the line that are contingent upon all charts being on the same bar you would add a boolean method to check your symbol-collection to see if they are all on the same bar. In this example I add the following method to the MySymbolCollection class.

 bool IsAllSameBarTime()
   {
      for(int i=0;i<Total()-1;i++)
         if(this[i].Time(0) != this[i+1].Time(0))  
            return false;
      return Total() >= 2;
   }

And then implemented into your EA as

if(symbols.IsAllSameBarTime())
{
   //do stuff
}

Finally, while OOP is not “required”, your development capabilities will be greatly hampered if you’re not familiar with OOP while developing for MQL5 because you won’t be able to work with the standard library, most 3rd party libraries, and/or alter any of the EAs generated by the wizard. It is a steeper learning curve, but one that will pay off when it comes to development time and your ability to implement complex strategies, like the one you’re working on.

Thanks. So much more to think about!