MQL5 Session Time Filter: Code EA Trade Hours Utility

Build a reusable MQL5 session time filter that handles DST automatically. Full code, configuration tips, and real-world pitfalls from a veteran EA developer.

mql5-session-time-filter-code-ea-trade-hours

Why Your EA Needs a Session Filter

I've been burned more times than I care to admit by EAs that trade during dead hours. You backtest a strategy on London open volatility, it looks rock-solid, then you run it live and watch it take a string of small losses at 3 AM because your code didn't know when to shut up. The fix is embarrassingly simple: a session time filter. But getting it right—especially across daylight saving time changes—is where most developers trip up.

This article walks you through building a reusable MQL5 session time filter utility class. You'll be able to drop it into any EA with minimal changes, configure your preferred trading sessions, and handle DST transitions without touching your code twice a year. I'll include the full implementation, common pitfalls, and honest notes on where this approach falls short.

Let me be blunt: if you're not using a session filter, you're gambling on when your EA decides to trade. I've seen developers spend weeks optimizing entry logic only to have their EA take a trade during the Sunday open when spreads are 5x normal. A session filter won't fix bad strategy logic, but it will keep your EA out of the garbage time that kills accounts.

Before we dive into code, let me share a quick war story. A few years back I was consulting on an EA that traded breakouts on EURUSD. The backtest looked incredible—2:1 profit factor over three years. Live performance? Terrible. The issue wasn't the breakout logic; it was that the EA was taking trades at 1:00 AM server time during Asian session when liquidity was thin and spreads were wide. A simple session filter limiting trades to London and New York overlap fixed it. Profit factor went from 0.8 to 1.6 in the next month. That's the kind of difference a good filter makes.

What a Session Time Filter Actually Does

At its core, a session filter is a yes/no gate. Your EA calls CanTrade() at the start of OnTick(), and the filter returns true only if the current server time falls within one of your defined windows. That's it. No magic, no predictive logic—just a hard check that prevents trades during known low-liquidity periods like the Asian afternoon or the hour after Friday close.

The trick is that "server time" in MetaTrader is typically UTC+2 or UTC+3 depending on DST. Your desired trading sessions—London open at 08:00 UTC, New York close at 22:00 UTC—don't shift with DST. So you can't hardcode times like 09:00 and expect them to work year-round. You need to convert your session definitions to server time dynamically, accounting for whether DST is currently active.

DST Handling: The Part Everyone Gets Wrong

Most MQL5 time filter examples I've seen online either ignore DST entirely or use a fixed offset. That works until March or October rolls around, then your EA starts trading an hour early or late. If you're scalping on M1, that one-hour offset can be the difference between catching the move and getting stopped out on noise.

MetaTrader 5 provides TimeCurrent() which returns the broker's server time. The server itself handles DST for its location. The problem is that your session definitions (London open, New York close) are in UTC or local market time, not server time. You need to know the server's UTC offset at any given moment. MQL5 doesn't expose this directly, but you can derive it by comparing server time with UTC obtained from the TimeGMT() function.

Here's the kicker: some brokers change their server time for DST, others don't. I've seen brokers that stay on UTC+2 year-round, and others that switch between UTC+2 and UTC+3. If you assume your broker follows a specific pattern, you'll get burned. The only reliable approach is to compute the offset dynamically on every tick or at least once per day.

Building the MQL5 Session Filter Utility Class

Let me show you the implementation I use in production. I'll keep it modular—one include file you can #include in any EA. The class stores session definitions as structs and provides a single boolean method to check if current server time falls within any active session.

//+------------------------------------------------------------------+
//|                                                  SessionFilter.mqh |
//|                                          TradingBotMaker.com Example |
//+------------------------------------------------------------------+
#property copyright "TradingBotMaker.com"
#property link      "https://tradingbotmaker.com"
#property version   "1.00"

//--- Session structure
struct SessionDef
{
   string            name;           // Session label (for logging)
   int               startHourUTC;   // Start hour in UTC (0-23)
   int               startMinuteUTC; // Start minute in UTC (0-59)
   int               endHourUTC;     // End hour in UTC (0-23)
   int               endMinuteUTC;   // End minute in UTC (0-59)
   bool              active;         // Enable/disable this session
};

//+------------------------------------------------------------------+
//| CSessionFilter class                                              |
//+------------------------------------------------------------------+
class CSessionFilter
{
private:
   SessionDef        m_sessions[];   // Dynamic array of sessions
   int               m_totalSessions;
   int               m_serverUTCOffset; // Cached offset in seconds

   int               GetServerUTCOffset();
   bool              IsTimeInRange(datetime serverTime, SessionDef &session);

public:
                     CSessionFilter();
                    ~CSessionFilter();
   void              AddSession(string name, int startH, int startM, int endH, int endM, bool active=true);
   bool              CanTrade();
   string            GetActiveSessionName();
   int               TotalSessions() { return m_totalSessions; }
};

//+------------------------------------------------------------------+
//| Constructor                                                       |
//+------------------------------------------------------------------+
CSessionFilter::CSessionFilter()
{
   m_totalSessions = 0;
   ArrayResize(m_sessions, 0);
   m_serverUTCOffset = GetServerUTCOffset();
}

//+------------------------------------------------------------------+
//| Destructor                                                        |
//+------------------------------------------------------------------+
CSessionFilter::~CSessionFilter()
{
   ArrayFree(m_sessions);
}

//+------------------------------------------------------------------+
//| Get server UTC offset in seconds                                  |
//+------------------------------------------------------------------+
int CSessionFilter::GetServerUTCOffset()
{
   datetime serverTime = TimeCurrent();
   datetime gmtTime = TimeGMT();
   return (int)(serverTime - gmtTime);
}

//+------------------------------------------------------------------+
//| Add a trading session definition                                  |
//+------------------------------------------------------------------+
void CSessionFilter::AddSession(string name, int startH, int startM, int endH, int endM, bool active=true)
{
   ArrayResize(m_sessions, m_totalSessions + 1);
   m_sessions[m_totalSessions].name = name;
   m_sessions[m_totalSessions].startHourUTC = startH;
   m_sessions[m_totalSessions].startMinuteUTC = startM;
   m_sessions[m_totalSessions].endHourUTC = endH;
   m_sessions[m_totalSessions].endMinuteUTC = endM;
   m_sessions[m_totalSessions].active = active;
   m_totalSessions++;
}

//+------------------------------------------------------------------+
//| Check if server time falls within a session (handles overnight)   |
//+------------------------------------------------------------------+
bool CSessionFilter::IsTimeInRange(datetime serverTime, SessionDef &session)
{
   MqlDateTime dt;
   TimeToStruct(serverTime, dt);
   
   // Convert session UTC times to server time by adding offset
   datetime sessionStartUTC = StringToTime(
      StringFormat("%04d.%02d.%02d %02d:%02d:00",
         dt.year, dt.mon, dt.day,
         session.startHourUTC, session.startMinuteUTC));
   datetime sessionEndUTC = StringToTime(
      StringFormat("%04d.%02d.%02d %02d:%02d:00",
         dt.year, dt.mon, dt.day,
         session.endHourUTC, session.endMinuteUTC));
   
   // Apply server UTC offset to get server-local session times
   datetime sessionStartServer = sessionStartUTC + m_serverUTCOffset;
   datetime sessionEndServer = sessionEndUTC + m_serverUTCOffset;
   
   // Handle sessions that cross midnight (e.g., 22:00 - 02:00 UTC)
   if(sessionEndUTC <= sessionStartUTC)
   {
      // Session ends next day
      if(serverTime >= sessionStartServer || serverTime < sessionEndServer + 86400)
         return true;
      return false;
   }
   
   // Normal same-day session
   if(serverTime >= sessionStartServer && serverTime < sessionEndServer)
      return true;
      
   return false;
}

//+------------------------------------------------------------------+
//| Main check: returns true if current time is within any active session |
//+------------------------------------------------------------------+
bool CSessionFilter::CanTrade()
{
   datetime serverTime = TimeCurrent();
   
   // Refresh offset periodically (in case DST changes during run)
   m_serverUTCOffset = GetServerUTCOffset();
   
   for(int i = 0; i < m_totalSessions; i++)
   {
      if(!m_sessions[i].active)
         continue;
      if(IsTimeInRange(serverTime, m_sessions[i]))
         return true;
   }
   return false;
}

//+------------------------------------------------------------------+
//| Get name of the first active session we're in (for logging)       |
//+------------------------------------------------------------------+
string CSessionFilter::GetActiveSessionName()
{
   datetime serverTime = TimeCurrent();
   for(int i = 0; i < m_totalSessions; i++)
   {
      if(!m_sessions[i].active)
         continue;
      if(IsTimeInRange(serverTime, m_sessions[i]))
         return m_sessions[i].name;
   }
   return "No active session";
}

One thing I want to highlight: the IsTimeInRange function handles sessions that cross midnight. This is critical for sessions like the Asian session that starts at 22:00 UTC and ends at 08:00 UTC the next day. Most simple time comparisons break on this edge case. My implementation uses a two-day window for overnight sessions, which is more reliable than trying to wrap times around midnight.

How to Use This in Your EA

Integration is straightforward. Create an instance of CSessionFilter in your EA's global scope, define your sessions in OnInit(), then call CanTrade() at the top of OnTick(). Here's a minimal example:

// Include the filter class
#include <SessionFilter.mqh>

// Global instance
CSessionFilter g_SessionFilter;

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
{
   // Define sessions in UTC
   // London open: 08:00 - 16:00 UTC
   g_SessionFilter.AddSession("London", 8, 0, 16, 0, true);
   // New York open: 13:00 - 22:00 UTC
   g_SessionFilter.AddSession("New York", 13, 0, 22, 0, true);
   // Asian session: 00:00 - 09:00 UTC
   g_SessionFilter.AddSession("Asia", 0, 0, 9, 0, false); // disabled
   
   Print("Server UTC offset: ", g_SessionFilter.GetServerUTCOffset() / 3600, " hours");
   Print("Total sessions defined: ", g_SessionFilter.TotalSessions());
   return(INIT_SUCCEEDED);
}

//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
{
   if(!g_SessionFilter.CanTrade())
   {
      // Optional: comment out in production to avoid log spam
      // Print("Outside trading session: ", g_SessionFilter.GetActiveSessionName());
      return;
   }
   
   // Your trading logic here
   // ...
}

Notice I'm defining sessions in UTC. This is critical—your session times stay constant regardless of server DST. The class handles the conversion internally using the dynamic server UTC offset.

I recommend adding a print statement in OnInit() to log the server UTC offset. This helps you verify that your broker's server time matches your expectations. If you see an offset of +3 hours when you expected +2, you'll know something's off before your EA starts trading at the wrong times.

Parameter Configuration via Inputs

Hardcoding session times works for testing, but you'll want external inputs for production. Here's how I typically expose session parameters:

InputTypeDefaultDescription
Session1_EnablebooltrueEnable Session 1 (London)
Session1_StartUTCstring"08:00"Session start time in UTC (HH:MM)
Session1_EndUTCstring"16:00"Session end time in UTC (HH:MM)
Session2_Enablebool

Want to build your own version?

Recreate similar entry logic, risk rules, and filters in TradingBotMaker—no MQL coding. Start free.

Community

Clap for the article and open comments only when you want to read them.

0 claps0 comments

Related articles