Scanning 28 symbols in 5 timeframes - page 2

 
Marbo:

Mladen, do I understand you correct that you don't recommend using a timer? Do you think my code is the most efficient way to make sure that no candle is missing?

If I loop all symbols and all timeframes constantly with every tick I can be pretty sure that I don't miss any new candle.

For the job you want to do the best way is to use the timer (as @Marco vd Heijden has already told in his post)

Timer eliminates the need for a tick and makes sure that you check data for symbols different from current chart symbol even is there is not incoming tick

Marco vd Heijden
Marco vd Heijden
  • www.mql5.com
Published product MT4 to Telegram allows you to broadcast your trades to a Telegram Channel or Group. Can be used to build, support and expand your own network of traders. A Group can have 200.000 members, A Channel can have an unlimited number of subscribers. The messages are send to Telegram in HTML5 format, which is the preferred language...
 

Would this be the correct way to use the timer-event?

Now the OnTimer function is called every minute and the function itself does the work.

So now it's not executed with every tick anymore.

#property strict
#property indicator_chart_window

string sym[] =    {"AUDCAD", "AUDCHF", "AUDJPY", "AUDNZD", "AUDUSD",
                   "CADCHF", "CADJPY", "CHFJPY", "EURAUD", "EURCAD",
                   "EURCHF", "EURGBP", "EURJPY", "EURNZD", "EURUSD",
                   "GBPAUD", "GBPCAD", "GBPCHF", "GBPJPY", "GBPNZD",
                   "GBPUSD", "NZDCAD", "NZDCHF", "NZDJPY", "NZDUSD",
                   "USDCAD", "USDCHF", "USDJPY"};
int tf[]= {PERIOD_M5, PERIOD_M15, PERIOD_M30, PERIOD_H1, PERIOD_H4};
datetime times[28][5];

//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
int OnInit()
  {
//--- indicator buffers mapping
   EventSetTimer(60);
//---
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+
//| Custom indicator iteration function                              |
//+------------------------------------------------------------------+
int OnCalculate(const int rates_total,
                const int prev_calculated,
                const datetime &time[],
                const double &open[],
                const double &high[],
                const double &low[],
                const double &close[],
                const long &tick_volume[],
                const long &volume[],
                const int &spread[])
  {
//---
   
//--- return value of prev_calculated for next call
   return(rates_total);
  }
//+------------------------------------------------------------------+
//| Timer function                                                   |
//+------------------------------------------------------------------+
void OnTimer()
  {
//---
   for (int s=0; s<ArraySize(sym); s++) {   
      for (int t=0; t<ArraySize(tf); t++) {
         if (times[s][t]!=iTime(sym[s], tf[t], 0)) {
            times[s][t]=iTime(sym[s], tf[t], 0);
            if (iClose(sym[s], tf[t], 1)>iHigh(sym[s], tf[t], 2)) {
               Alert(sym[s]+" "+(string)tf[t]);
            }
         }
      }
   }
  }
//+------------------------------------------------------------------+
 
Marbo:

Would this be the correct way to use the timer-event?

Now the OnTimer function is called every minute and the function itself does the work.

So now it's not executed with every tick anymore.

Yes, that is OK

Bare in mind though that with a timer set to 1 minute. you can have potential maximal lag of 59 seconds between the new bar forming for some time frame + symbol and testing that time frame + symbol. 

 

Yes and if you want it to be faster you can use:

EventSetMillisecondTimer(500);

Then it checks twice a second but make sure that the entire calculation fits otherwise it will start to cause problems due to calculations not being finished before the next timer cycle set's in it may become unstable.

So it depends on how fast you want / need it to be.

If you lowest time frame is M5 then a check every minute is fine.

 
Mladen Rakic:

Yes, that is OK

Bare in mind though that with a timer set to 1 minute. you can have potential maximal lag of 59 seconds between the new bar forming for some time frame + symbol and testing that time frame + symbol. 

Yes, 59 seconds is fine. I think I will adjust the timer interval to my needs. I am not sure which timeframes I need at the end.

 
Marco vd Heijden:

Yes and if you want it to be faster you can use:

Then it checks twice a second but make sure that the entire calculation fits otherwise it will start to cause problems due to calculations not being finished before the next timer cycle set's in it may become unstable.

So it depends on how fast you want / need it to be.

If you lowest time frame is M5 then a check every minute is fine.

Thanks a lot!

I think at the end I might use M30-H4. So I will use a higher interval and I will make sure that I start the MT4 at a full or half hour.

 
Marbo:

I am not really sure what you mean with working with TimeCurrent() in this case. In my opinion the time is not so important because I have to make sure that a new candle started and that is not always with XX:XX:00. More often that not the first tick of a new candle comes (in quiet times) 10 seconds late.

I'm saying that, since you asked about optimisations, it's possible to do far less work per tick/timer than processing all of 28x5 = 140 different combinations and calling iTime() on all of them.

But the following approach contains two relatively subtle assumptions about undocumented aspects of the way MT4 works internally:

  • Assumes that, if MT4 receives a tick dated 00:01:xx for one symbol, then it can't later receive a tick for another symbol relating to the previous minute
  • Assumes that MT4 will receive ticks as a batch, and process them all before updating indicators and EAs. To put that another way: assumes that, if TimeCurrent() has been updated, then all candle data for all symbols will be valid as-at TimeCurrent()


#property strict
#property indicator_chart_window

string sym[] =    {"AUDCAD", "AUDCHF", "AUDJPY", "AUDNZD", "AUDUSD",
                   "CADCHF", "CADJPY", "CHFJPY", "EURAUD", "EURCAD",
                   "EURCHF", "EURGBP", "EURJPY", "EURNZD", "EURUSD",
                   "GBPAUD", "GBPCAD", "GBPCHF", "GBPJPY", "GBPNZD",
                   "GBPUSD", "NZDCAD", "NZDCHF", "NZDJPY", "NZDUSD",
                   "USDCAD", "USDCHF", "USDJPY"};
int tf[]= {PERIOD_M5, PERIOD_M15, PERIOD_M30, PERIOD_H1, PERIOD_H4};

datetime glbLastTimeCurrent;
bool glbCreatedTimer = false;

int OnInit()
{
   CreateTimer(); // See note below
   glbLastTimeCurrent = 0;
   glbCreatedTimer = false;
   
   return(INIT_SUCCEEDED);
}

void OnDeinit(const int reason)
{
   EventKillTimer();
}

int OnCalculate(const int rates_total, const int prev_calculated, const datetime &time[], const double &open[], const double &high[], const double &low[], const double &close[], const long &tick_volume[], const long &volume[], const int &spread[])
{
   CreateTimer();  // See note below
   return(rates_total);
}

void OnTimer()
{
   if (!glbLastTimeCurrent) {
      // Ignore first call, and wait for first change of TimeCurrent()
   } else {
      for (int iTF = 0; iTF < ArraySize(tf); iTF++) {
         int thisTF = tf[iTF];
         int tfSeconds = PeriodSeconds(thisTF);
         
         if (TimeCurrent() / tfSeconds != glbLastTimeCurrent / tfSeconds) {
            // New bar has formed on timeframe 
            for (int iSym = 0; iSym < ArraySize(sym); iSym++) {
               string thisSym = sym[iSym];
               
               // This symbol may or may not have started a new candle... 
               MqlRates rates[];
               ArraySetAsSeries(rates, true);
               if (CopyRates(thisSym, thisTF, 0, 3, rates) < 3) {
                  // WTF? Too little data
               } else {
                  int barToCompare;
                  
                  if (rates[0].time / tfSeconds == TimeCurrent() / tfSeconds) {
                     // MT4 has received a new tick and started a new candle.
                     // Need to compare #1 against #2
                     barToCompare = 1;
                  } else {
                     // MT4 has not yet received a new tick, and not yet started a new candle.
                     // Need to compare #0 (which will now be complete) against #1
                     barToCompare = 0;
                  }
      
                  if (rates[barToCompare].close > rates[barToCompare + 1].high) {
                     Alert(thisSym + " " + (string)thisTF);
                  }
               }
            }         
         } else {
            // Same bar as previous check
            // If the list of timeframes is guaranteed to be in ascending order, with the second and subsequent
            // all being multiples of the first one, then we can safely do an early exit from the loop here.
            // But the speed gain from that would be entirely trivial.
         }
      }
   }
   
   glbLastTimeCurrent = TimeCurrent();
}

// Timer creation can fail in OnInit() if MT4 is started up with 
// an EA or indicator already attached to a chart (probably because the 
// MQL4 code is started before a Win32 window handle exists).
// To be safe, it's necessary to re-attempt the timer creation in 
// OnTick()/OnCalculate() if it failed in OnInit(). 
void CreateTimer()
{
   if (!glbCreatedTimer) glbCreatedTimer = EventSetMillisecondTimer(500);
}

 
JC:

I'm saying that, since you asked about optimisations [...]

... but it then becomes a question of what's a meaningful optimisation. On the average tick/timer/test when no new bar has formed, the code above is about 300 times faster than doing all the checks on iTime(). But the iTime-based version still only consumes about 1 millisecond per check. So, my version would only be a meaningful improvement if you wanted to use something like this in backtesting. It's not going to make any material difference in real-life use.

 
JC:

I'm saying that, since you asked about optimisations, it's possible to do far less work per tick/timer than processing all of 28x5 = 140 different combinations and calling iTime() on all of them.

But the following approach contains two relatively subtle assumptions about undocumented aspects of the way MT4 works internally:

  • Assumes that, if MT4 receives a tick dated 00:01:xx for one symbol, then it can't later receive a tick for another symbol relating to the previous minute
  • Assumes that MT4 will receive ticks as a batch, and process them all before updating indicators and EAs. To put that another way: assumes that, if TimeCurrent() has been updated, then all candle data for all symbols will be valid as-at TimeCurrent()


Very interesting approach using a very different coding-style compared to mine. It's definitely something I can learn a lot from. Thank you for that!