On this page
Back to tutorials

Advanced MQL5 EA for MT5: Multi-Timeframe + Market Filters

Level up your EA: add multi-timeframe analysis, London/NY session filters, time windows, and ATR/spread filters. Fewer false signals, better performance.

📖 34 min read

📝 6,800 words

🏷️ MQL5 & Expert Advisors

Share this article:

Want to build a no-code strategy right now?

Create your free account in seconds and start building immediately.


Advanced EA: Multi-Timeframe & Filters — Fewer False Signals, Better Performance

Your EA has entry logic and risk management, but it still takes trades in quiet Asian sessions, during news spikes, or against the higher-timeframe trend. Filters reduce noise: you only trade when conditions align across multiple timeframes and market states. According to the MQL5 Timeseries documentation, you can read any symbol-period combination with CopyRates, iTime, and iBarShift. This guide teaches you multi-timeframe analysis, session filters, time windows, ATR and spread filters — so your EA trades when it matters most.

Related guides: MQL5 Programming Reference · EA Risk Management · Scalping EA in MT5 · Backtest EA in MT5 Strategy Tester


Why MTF & Filters Matter

What is the problem? A simple MA crossover EA trades on every signal. Many of those signals occur when:

  • The higher timeframe trend is against you (counter-trend scalp)
  • The market is thin (Asian session — wide spreads, choppy price)
  • Volatility is extreme (news — false breakouts, slippage)
  • Spread is high (cost eats your profit)

What do filters do? Filters block trades when conditions are bad. They don't change your entry logic — they add a simple yes/no check: "Is it London or NY?" "Is H1 trend in our favor?" "Is spread below 30 points?" If any check fails, you skip the trade. Result: fewer trades, but higher quality.

FilterPurpose
Multi-timeframe (MTF)Trade only when higher TF (H1/H4) agrees with entry direction
Session (London/NY)Trade only during high-volume hours — less spread, clearer trends
Time windowRestrict trading to specific hours (e.g. 9:00–17:00 server)
ATR filterSkip when volatility too high (news) or too low (dead market)
Spread filterSkip when spread exceeds your limit — protects profit targets

Step 1: Multi-Timeframe Analysis

What is this? Multi-timeframe analysis means checking a higher timeframe (e.g. H1 or H4) before taking a signal on the chart timeframe (e.g. M15). Example: "Only buy on M15 if the H1 trend is bullish." You use the higher timeframe as a filter — if H1 says down, you don't buy on M15 even if M15 gives a buy signal.

Why use it? Trading with the higher timeframe trend increases the chance that your M15 signal leads to a sustained move. Counter-trend trades often fail when the bigger picture is against you.

How it works: Read higher timeframe OHLC with CopyRates. The function copies MqlRates (time, open, high, low, close, volume). Use iBarShift to align the current bar time with the higher timeframe — it returns the bar index on the higher TF for a given time.

Example — H1 trend filter (only buy if H1 close is above H1 MA):

// In OnInit: create H1 MA handle
int g_maH1Handle = iMA(_Symbol, PERIOD_H1, 50, 0, MODE_EMA, PRICE_CLOSE);

bool IsH1TrendBullish()
{
   datetime barTime = iTime(_Symbol, PERIOD_CURRENT, 0);  // Current bar on entry TF
   int h1Bar = iBarShift(_Symbol, PERIOD_H1, barTime, false);
   if(h1Bar < 0) return false;

   double maBuffer[];
   ArraySetAsSeries(maBuffer, true);
   if(CopyBuffer(g_maH1Handle, 0, h1Bar, 1, maBuffer) < 1) return false;

   double closeH1 = iClose(_Symbol, PERIOD_H1, h1Bar);
   return (closeH1 > maBuffer[0]);  // Price above MA = bullish
}

Use before opening a Buy: if(!IsH1TrendBullish()) return;


Step 2: Session Filter (London & New York)

What is this? London (approx. 8:00–16:00 GMT) and New York (13:00–21:00 GMT) are the most liquid sessions. Spreads are usually tighter, trends clearer. The overlap (13:00–16:00 GMT) is often the best time for Forex. A session filter blocks trades outside these hours.

Why use it? Asian session (00:00–08:00 GMT) often has wider spreads and choppy price. News at session opens can spike volatility. Restricting to London/NY improves execution and trend quality.

How it works: Use TimeToStruct(TimeCurrent(), dt) to get server hour (dt.hour) and day (dt.day_of_week). Check if the hour falls within London (8–16) or NY (13–21). For overlap, allow 8–21. Exclude weekends (dt.day_of_week == SUNDAY || dt.day_of_week == SATURDAY).

Example — Allow trading 8:00–21:00 GMT (London + NY):

bool IsLondonOrNySession()
{
   MqlDateTime dt;
   TimeToStruct(TimeCurrent(), dt);
   if(dt.day_of_week == SUNDAY || dt.day_of_week == SATURDAY) return false;
   return (dt.hour >= 8 && dt.hour < 21);
}

Note: TimeCurrent() returns broker server time. If your broker uses GMT+2, adjust your hour checks. SymbolInfoSessionTrade returns broker-defined session times if you prefer that over fixed hours.


Step 3: Time Window Filter

What is this? A time window restricts trading to a specific range of hours (e.g. 9:00–17:00 server time). Unlike the session filter (London/NY), this is fully customizable — you set start and end hours as inputs.

Why use it? You might want to avoid the first hour after London open (news spikes) or the last hour before NY close. A time window gives you precise control.

How it works: Same as session filter — TimeToStruct(TimeCurrent(), dt), then check dt.hour >= InpStartHour && dt.hour < InpEndHour.

Example — Trade only 10:00–18:00 server:

input int InpStartHour = 10;   // Start hour (server)
input int InpEndHour   = 18;   // End hour (server)

bool IsWithinTimeWindow()
{
   MqlDateTime dt;
   TimeToStruct(TimeCurrent(), dt);
   if(dt.day_of_week == SUNDAY || dt.day_of_week == SATURDAY) return false;
   if(InpStartHour <= InpEndHour)
      return (dt.hour >= InpStartHour && dt.hour < InpEndHour);
   return (dt.hour >= InpStartHour || dt.hour < InpEndHour);  // Overnight window
}

Step 4: ATR Filter

What is this? ATR (Average True Range) measures volatility. An ATR filter skips trades when volatility is too high (news, panic) or too low (dead market, ranging). Example: don't trade if current ATR > 1.5× the 20-period average ATR (too volatile) or < 0.5× (too quiet).

Why use it? During news, price can spike and hit your SL quickly — or whipsaw. During very quiet periods, your strategy might generate false signals. The ATR filter avoids both extremes.

How it works: Get current ATR and average ATR (e.g. 20-period SMA of ATR). Compare: if atrNow > atrAvg * 1.5 → too volatile, skip. If atrNow < atrAvg * 0.5 → too quiet, skip.

Example — Skip when ATR > 1.5× or < 0.5× 20-bar average:

int g_atrHandle = iATR(_Symbol, PERIOD_CURRENT, 14);

bool IsAtrAcceptable()
{
   double atrBuffer[];
   ArraySetAsSeries(atrBuffer, true);
   if(CopyBuffer(g_atrHandle, 0, 0, 21, atrBuffer) < 21) return false;

   double atrNow = atrBuffer[0];
   double atrSum = 0;
   for(int i = 1; i <= 20; i++) atrSum += atrBuffer[i];
   double atrAvg = atrSum / 20.0;

   if(atrNow > atrAvg * 1.5) return false;  // Too volatile
   if(atrNow < atrAvg * 0.5) return false;  // Too quiet
   return true;
}

Step 5: Spread Filter

What is this? Spread is the difference between Ask and Bid. When spread is high (e.g. during news or low liquidity), your fill price is worse and the cost per round-trip increases. A spread filter skips opening when spread exceeds your limit (e.g. 30 points).

Why use it? If your target is 50 pips and spread is 20 pips, you need 70 pips of move to break even. High spread eats into small targets and scalping strategies.

How it works: Get current spread with SymbolInfoInteger(_Symbol, SYMBOL_SPREAD) (in points) or calculate (Ask - Bid) / Point. Compare with your max spread input.

Example — Skip when spread > 30 points:

input int InpMaxSpread = 30;   // Max spread (points)

bool IsSpreadAcceptable()
{
   long spread = SymbolInfoInteger(_Symbol, SYMBOL_SPREAD);
   return (spread <= InpMaxSpread);
}

Summary — Putting It All Together

You added: MTF trend filter (H1/H4 alignment), session filter (London/NY), time window (custom hours), ATR filter (volatility range), and spread filter (max spread). Apply all filters before your entry logic — if any fails, skip the trade. Your EA now trades only when conditions align.

Bonus Tip: Prefer to add MTF analysis and filters visually? Try AlfaTactix Strategy Builder free — configure multi-timeframe signals, London/NY session filters, time windows, ATR filters, and spread limits with a no-code interface. Export production-ready MQL5 with all filters built in. Fewer false signals, same result.


Full Code

// In OnInit
int g_maH1Handle = iMA(_Symbol, PERIOD_H1, 50, 0, MODE_EMA, PRICE_CLOSE);
int g_atrHandle  = iATR(_Symbol, PERIOD_CURRENT, 14);

bool PassesAllFilters()
{
   if(!IsH1TrendBullish()) return false;
   if(!IsLondonOrNySession()) return false;
   if(!IsWithinTimeWindow()) return false;
   if(!IsAtrAcceptable()) return false;
   if(!IsSpreadAcceptable()) return false;
   return true;
}

void OnTick()
{
   // ... your signal logic (e.g. MA crossover) ...
   if(!PassesAllFilters()) return;   // Apply filters before opening
   // ... open trade ...
}

Troubleshooting

SymptomCauseFix
No trades with MTF filterH1 trend never matches, or iBarShift returns -1Log H1 bar index; ensure history is loaded for PERIOD_H1. Check symbol in Market Watch.
Session filter blocks allServer timezone differs from GMTLog TimeCurrent() and dt.hour; adjust hour range for your broker (e.g. GMT+2 → use 10–22).
ATR filter too strict1.5× / 0.5× thresholds too tightWiden range (e.g. 2.0× and 0.3×) or use different ATR period.
CopyRates returns -1No history for symbol/periodEnsure symbol in Market Watch; wait for history download. Use Bars() to check available bars.
Spread filter never passesMax spread too low for symbolIncrease InpMaxSpread or log current spread to set a realistic value.

See MQL5 Series Functions and Market Info for full reference.

Build your no-code trading strategy now — free

Create your account and start building a complete no-code strategy right now. Add indicators, filters, and risk rules, then export MQL5 in minutes.

Frequently Asked Questions

Multi-timeframe (MTF) analysis means checking a higher timeframe (e.g. H1 or H4) for trend or structure before taking signals on a lower timeframe (e.g. M15). For example: only buy on M15 if H1 trend is bullish. Use CopyRates, iTime, or iBarShift to read higher timeframe data.

Use TimeToStruct(TimeCurrent(), dt) to get the current hour (dt.hour) in server time. London is typically 8–16 GMT, NY 13–21 GMT. Check if current hour falls within these ranges before opening trades. Alternatively use SymbolInfoSessionTrade for broker-defined sessions.

An ATR filter skips trades when volatility is too high or too low. Example: don't trade if current ATR > 1.5× average ATR (too volatile) or < 0.5× average (too quiet). Use iATR and CopyBuffer to get ATR values.

High spread increases cost per trade and can invalidate small targets. A spread filter skips opening when SymbolInfoInteger(symbol, SYMBOL_SPREAD) or (Ask-Bid)/Point exceeds a limit (e.g. 30 points).

iBarShift(symbol, higherTF, time, false) returns the bar index on the higher timeframe for a given time. Use the current bar time from the entry timeframe to find the corresponding H1/H4 bar. See the MQL5 CopyRates docs for examples.

Build your no-code strategy now — free

Create Free Account