Back to MetaTrader 5 MQL5 for CFD Traders
Part 4

Multi-Timeframe and Multi-Symbol

Read a higher timeframe for regime, scan other symbols, and avoid the repainting trap.

Advanced12 min readBeginner → AdvancedLesson 15 / 17
Try it in MetaEditor
Daily regime filter read from a lower timeframe

Save as MQL5/Scripts/Lesson15MTF.mq5. It reads the D1 EMA200 and the H1 EMA50 from any chart and prints the regime — note it uses shift 1 to avoid repainting.

//+------------------------------------------------------------------+
//| Lesson 15 - Multi-timeframe regime filter                        |
//+------------------------------------------------------------------+
#property copyright "Strategist Academy"
#property version   "1.00"

void OnStart()
{
   string sym = _Symbol;

   int maD1 = iMA(sym, PERIOD_D1, 200, 0, MODE_EMA, PRICE_CLOSE);
   int maH1 = iMA(sym, PERIOD_H1, 50,  0, MODE_EMA, PRICE_CLOSE);
   int maH4 = iMA(sym, PERIOD_H4, 50,  0, MODE_EMA, PRICE_CLOSE);

   if(maD1 == INVALID_HANDLE || maH1 == INVALID_HANDLE || maH4 == INVALID_HANDLE)
   {
      Print("Handle creation failed. Error: ", GetLastError());
      return;
   }

   double d1[], h1[], h4[];
   ArraySetAsSeries(d1, true);
   ArraySetAsSeries(h1, true);
   ArraySetAsSeries(h4, true);

   // Ask for 3 bars so index 1 (last CLOSED bar) is always available.
   bool okD1 = (CopyBuffer(maD1, 0, 0, 3, d1) >= 3);
   bool okH1 = (CopyBuffer(maH1, 0, 0, 3, h1) >= 3);
   bool okH4 = (CopyBuffer(maH4, 0, 0, 3, h4) >= 3);

   if(!okD1 || !okH1 || !okH4)
   {
      Print("Not enough higher-timeframe data yet. Error: ", GetLastError());
      IndicatorRelease(maD1); IndicatorRelease(maH1); IndicatorRelease(maH4);
      return;
   }

   double price = SymbolInfoDouble(sym, SYMBOL_BID);

   Print("=== Multi-timeframe view of ", sym, " ===");
   Print("Current chart timeframe: ", EnumToString((ENUM_TIMEFRAMES)_Period));
   Print("Bid:       ", DoubleToString(price, _Digits));
   Print("D1 EMA200: ", DoubleToString(d1[1], _Digits));
   Print("H4 EMA50:  ", DoubleToString(h4[1], _Digits));
   Print("H1 EMA50:  ", DoubleToString(h1[1], _Digits));
   Print("");
   Print("Daily regime:  ", (price > d1[1]) ? "ABOVE the daily trend - longs only"
                                            : "BELOW the daily trend - shorts only");
   Print("H4 alignment:  ", (price > h4[1]) ? "above" : "below");
   Print("H1 alignment:  ", (price > h1[1]) ? "above" : "below");
   Print("");
   Print("Note: all values use shift 1 (last CLOSED bar). Shift 0 is still");
   Print("forming and would make this repaint - the backtest would look better");
   Print("than the live result, every time.");

   // Raw bars from another timeframe, for reference.
   MqlRates dailyBars[];
   ArraySetAsSeries(dailyBars, true);
   if(CopyRates(sym, PERIOD_D1, 0, 5, dailyBars) == 5)
   {
      Print("");
      Print("Last 5 completed daily bars:");
      for(int i = 1; i < 5; i++)
         Print("  ", TimeToString(dailyBars[i].time, TIME_DATE),
               "  O=", DoubleToString(dailyBars[i].open,  _Digits),
               "  H=", DoubleToString(dailyBars[i].high,  _Digits),
               "  L=", DoubleToString(dailyBars[i].low,   _Digits),
               "  C=", DoubleToString(dailyBars[i].close, _Digits));
   }

   IndicatorRelease(maD1);
   IndicatorRelease(maH1);
   IndicatorRelease(maH4);
}
To run it
  1. 1. Open MetaEditor from MT5 (press F4), and pick the folder this program belongs in — Experts, Indicators, or Scripts.
  2. 2. Create a new file, paste this over it, then press F7 to compile. Fix anything the Errors tab reports.
  3. 3. Back in MT5, drag it onto a chart — or open the Strategy Tester if it is an Expert Advisor.

Just pass a different symbol or timeframe

Every indicator function and copy function takes a symbol and a timeframe. iMA(_Symbol, PERIOD_D1, 200, ...) gives you a daily average on an hourly chart. No special API, no request.security equivalent — just different arguments.

Why traders do this

A higher timeframe gives you regime: are we broadly trending up or down? Trade only in the direction of the higher timeframe and you filter out a large share of whipsaws. This is the single highest-value addition to most beginner systems.

The repainting trap, in practice

Read the higher timeframe at shift 0 and you are reading a bar that has not closed. On a daily filter read from an hourly chart, that daily value will keep changing all day. Your backtest — computed after the fact — will show the final value, while live you saw the evolving one. The backtest looks better than reality, always. Use shift 1.

CopyRates for raw bars

When you need open/high/low/close rather than an indicator value, use CopyRates(symbol, timeframe, 0, count, rates[]) into a MqlRates array. It works across symbols and timeframes identically.

Synchronising across symbols

When you scan several symbols, they will not have bars aligned — one may not have ticked recently. Either accept and handle missing data, or explicitly wait for the bar you need. Do not assume every symbol has a fresh bar just because the one on your chart does.

Performance

Reading ten symbols on every tick is expensive. Gate your multi-symbol work on a timer or on new bars, and cache results where you can. The Strategy Tester will thank you.

What you just did

Lesson 15 of 17 in MetaTrader 5 MQL5 for CFD Traders. When you have run the examples or read the section, tick it off and move to the next lesson.