Introduction: Why Multi-Timeframe Analysis Matters for Your EAs
Single-timeframe EAs are like driving with only the rearview mirror. They see the immediate price action but miss the broader market context. Multi-timeframe (MTF) analysis lets your Expert Advisor check the daily trend before taking a 15-minute entry, or confirm a 1-hour signal with the 4-hour momentum. This dramatically reduces false signals and aligns your trades with the dominant market direction.
In this guide, you will learn how to programmatically access higher timeframe data inside your MQL4 or MQL5 EA or custom indicator. We will cover the core functions, handle the platform-specific differences, and walk through a complete working example. By the end, you will be able to add MTF logic to any automated strategy.
Prerequisites
- MetaTrader 4 or 5 installed on your computer (any version from build 600+ for MT4, build 1000+ for MT5).
- MetaEditor (comes with the platform).
- Basic familiarity with MQL syntax: variables, functions,
ifstatements, and loops. - A demo or live trading account to test your EA.
- A text editor or IDE (optional) – MetaEditor is sufficient.
Step-by-Step Instructions: Building an MTF EA in MQL4/MQL5
1. Understanding Timeframe Constants
MetaTrader uses predefined constants for timeframes. In MQL4, they are PERIOD_M1, PERIOD_M5, PERIOD_H1, PERIOD_H4, PERIOD_D1, etc. In MQL5, the same constants exist but are defined as ENUM_TIMEFRAMES enumeration. Always use these constants instead of numeric values for readability and compatibility.
| Constant | Value (MT4/MT5) | Timeframe |
|---|---|---|
| PERIOD_M1 | 1 | 1 minute |
| PERIOD_H1 | 60 | 1 hour |
| PERIOD_D1 | 1440 | Daily |
2. Accessing Higher Timeframe Data: The Core Functions
The key is using iClose(), iOpen(), iHigh(), iLow(), or any i***() indicator function with a different timeframe parameter. In MQL4, the syntax is:
double closeH4 = iClose(Symbol(), PERIOD_H4, 0); // Current H4 close
double rsiH4 = iRSI(Symbol(), PERIOD_H4, 14, PRICE_CLOSE, 1); // H4 RSI value
In MQL5, the approach is similar but uses handles for indicators. For price data, use CopyClose():
double closeH4[];
CopyClose(Symbol(), PERIOD_H4, 0, 1, closeH4);
double currentH4Close = closeH4[0];
For indicators in MQL5, you must create a handle first (e.g., iRSI() returns a handle), then use CopyBuffer() to retrieve values. This is a critical difference from MQL4.
3. Complete MTF Logic Example (MQL4)
Below is a simplified EA that only trades when the H4 trend is bullish (price above 200 EMA) and the M15 RSI is oversold. This demonstrates multi-timeframe confirmation.
//+------------------------------------------------------------------+
//| MTF_Example.mq4 |
//+------------------------------------------------------------------+
#property strict
input double LotSize = 0.1;
input int H4_EMA_Period = 200;
input int RSI_Period = 14;
input int RSI_Oversold_Level = 30;
//+------------------------------------------------------------------+
//| Expert initialization function |
//+------------------------------------------------------------------+
int OnInit()
{
return(INIT_SUCCEEDED);
}
//+------------------------------------------------------------------+
//| Expert tick function |
//+------------------------------------------------------------------+
void OnTick()
{
// 1. Get H4 EMA value (higher timeframe trend filter)
double h4EMA = iMA(Symbol(), PERIOD_H4, H4_EMA_Period, 0, MODE_EMA, PRICE_CLOSE, 0);
double h4Close = iClose(Symbol(), PERIOD_H4, 0);
// 2. Get M15 RSI value (entry signal timeframe)
double rsiM15 = iRSI(Symbol(), PERIOD_M15, RSI_Period, PRICE_CLOSE, 0);
// 3. Trade logic: only buy if H4 trend is up AND M15 RSI is oversold
if(h4Close > h4EMA && rsiM15 < RSI_Oversold_Level)
{
// Check for existing positions to avoid repeated entries
if(CountPositions() == 0)
{
OrderSend(Symbol(), OP_BUY, LotSize, Ask, 3, 0, 0, "MTF Buy", 0, 0, Green);
}
}
}
//+------------------------------------------------------------------+
//| Count open positions |
//+------------------------------------------------------------------+
int CountPositions()
{
int count = 0;
for(int i = OrdersTotal() - 1; i >= 0; i--)
{
if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES))
{
if(OrderSymbol() == Symbol() && OrderMagicNumber() == 0)
{
count++;
}
}
}
return count;
}
//+------------------------------------------------------------------+
4. Adapting for MQL5
MQL5 requires handle-based indicators. Here is the same logic in MQL5:
//+------------------------------------------------------------------+
//| MTF_Example.mq5 |
//+------------------------------------------------------------------+
#property strict
input double LotSize = 0.1;
input int H4_EMA_Period = 200;
input int RSI_Period = 14;
input int RSI_Oversold_Level = 30;
int h4EMA_Handle;
int rsiM15_Handle;
//+------------------------------------------------------------------+
//| Expert initialization function |
//+------------------------------------------------------------------+
int OnInit()
{
h4EMA_Handle = iMA(Symbol(), PERIOD_H4, H4_EMA_Period, 0, MODE_EMA, PRICE_CLOSE);
rsiM15_Handle = iRSI(Symbol(), PERIOD_M15, RSI_Period, PRICE_CLOSE);
if(h4EMA_Handle == INVALID_HANDLE || rsiM15_Handle == INVALID_HANDLE)
{
Print("Handle creation failed!");
return(INIT_FAILED);
}
return(INIT_SUCCEEDED);
}
//+------------------------------------------------------------------+
//| Expert tick function |
//+------------------------------------------------------------------+
void OnTick()
{
double h4EMA[], h4Close[], rsiM15[];
ArraySetAsSeries(h4EMA, true);
ArraySetAsSeries(h4Close, true);
ArraySetAsSeries(rsiM15, true);
// Copy H4 data
if(CopyBuffer(h4EMA_Handle, 0, 0, 1, h4EMA) < 1) return;
if(CopyClose(Symbol(), PERIOD_H4, 0, 1, h4Close) < 1) return;
// Copy M15 RSI
if(CopyBuffer(rsiM15_Handle, 0, 0, 1, rsiM15) < 1) return;
// Trade logic
if(h4Close[0] > h4EMA[0] && rsiM15[0] < RSI_Oversold_Level)
{
// Place trade using CTrade class (simplified)
// ... (trade execution omitted for brevity)
}
}
//+------------------------------------------------------------------+
Tips and Best Practices from Experience
- Always handle handle creation errors in MQL5. Check for
INVALID_HANDLEinOnInit()and returnINIT_FAILEDto prevent crashes. - Use
ArraySetAsSeries()in MQL5 to ensure index 0 is the most recent bar. Without it, you will get historical data in reverse order. - Cache higher timeframe data to avoid redundant calculations. Store the last known H4 close and only update when a new H4 bar forms.
- Be mindful of bar synchronization. Higher timeframe bars close at different times than the current chart. Use
TimeCurrent()andiTime()to check if a new bar has formed on the higher timeframe. - Test on multiple symbols. An MTF EA that works on EURUSD may behave differently on JPY pairs due to volatility. Always backtest across different asset classes.
Common Mistakes and Troubleshooting
Mistake 1: Using the Current Chart Timeframe Instead of Higher Timeframe
New developers often accidentally use iClose(Symbol(), 0, 0) inside an MTF EA. The 0 parameter means "current chart timeframe," which defeats the purpose. Always explicitly specify the desired timeframe constant.
Mistake 2: MQL5 Indicator Handle Not Created or Invalid
If your MQL5 EA compiles but does nothing, check the Experts tab. A common error is "Handle creation failed" because the indicator does not exist or the symbol is not visible. Ensure the symbol is in Market Watch.
Mistake 3: Off-By-One Index Errors
When copying data from higher timeframes, the index 0 always refers to the current (still-open) bar. For confirmed signals, use index 1 (the last completed bar). This avoids repainting and ensures your backtest matches live trading.
Mistake 4: Not Checking for New Bars
Without a new-bar check, your EA will fire multiple signals on every tick during the same bar. Use a static datetime variable to track the last processed bar time:
static datetime lastBarTime = 0;
if(Time[0] == lastBarTime) return; // Already processed this bar
lastBarTime = Time[0];
Mistake 5: Not Handling Symbol Visibility in MQL5
In MQL5, if a symbol is not visible in Market Watch, CopyClose() or CopyBuffer() may return zero bytes. Add this check before copying data:
if(!SymbolInfoInteger(Symbol(), SYMBOL_SELECT))
SymbolSelect(Symbol(), true);
Mistake 6: Mixing MQL4 and MQL5 Syntax
Common confusion: In MQL4, iRSI() returns the RSI value directly. In MQL5, iRSI() returns a handle. Trying to use the MQL4 pattern in MQL5 will cause compilation errors. Always check which platform you are coding for.
Advanced Techniques: Using Custom Indicators Across Timeframes
To call a custom indicator from a higher timeframe, use iCustom() in MQL4 or the handle-based approach in MQL5. For example, to access a ZigZag indicator on H1 from an M15 chart in MQL4:
double zigzagH1 = iCustom(Symbol(), PERIOD_H1, "ZigZag", Depth, Deviation, Backstep, 0, 0);
In MQL5, you create a handle for the custom indicator with the higher timeframe and then copy its buffer:
int zigzagHandle = iCustom(Symbol(), PERIOD_H1, "ZigZag", Depth, Deviation, Backstep);
double zigzagValues[];
ArraySetAsSeries(zigzagValues, true);
CopyBuffer(zigzagHandle, 0, 0, 1, zigzagValues);
double currentZigzag = zigzagValues[0];
Performance Optimization: Caching Higher Timeframe Data
Calling iClose() or CopyClose() on every tick for higher timeframes is inefficient. Cache the values and update only when a new bar forms on that timeframe. Here is a pattern for MQL4:
static double cachedH4Close = 0;
static datetime lastH4Time = 0;
datetime currentH4Time = iTime(Symbol(), PERIOD_H4, 0);
if(currentH4Time != lastH4Time)
{
cachedH4Close = iClose(Symbol(), PERIOD_H4, 0);
lastH4Time = currentH4Time;
}
For MQL5, use SeriesInfoInteger() to check for new bars:
static datetime lastH4Time = 0;
datetime currentH4Time;
SeriesInfoInteger(Symbol(), PERIOD_H4, SERIES_LASTBAR_DATE, currentH4Time);
if(currentH4Time != lastH4Time)
{
// Update cached values
lastH4Time = currentH4Time;
}
Summary and Next Steps
You now have a solid foundation for implementing multi-timeframe analysis in your MQL4 and MQL5 EAs. The core principle is simple: use iClose()/CopyClose() and indicator functions with the higher timeframe constant. The devil is
Frequently Asked Questions
Can I use the same MQL4 MTF code in MQL5?
Not directly. MQL5 uses handle-based indicators and requires CopyBuffer() to retrieve values. The price functions like iClose() also differ. You must rewrite the indicator calls for MQL5, but the logic structure remains the same.
Why does my MTF EA give different results in backtest vs live trading?
The most common cause is repainting. Ensure you use confirmed bars (index 1 or higher) for higher timeframe data, not the current (still-forming) bar. Also verify that your backtest uses "Every tick" mode for accurate bar formation.
Do I need special permissions to access higher timeframe data?
No, MetaTrader provides all timeframes for free as long as the symbol is available in Market Watch. No additional data subscriptions are required for standard timeframes like H1, H4, D1. Some brokers may restrict M1/M5 for certain symbols.






