Problem Description
Solution
To correctly handle time in MQL4, you must distinguish between the three specific time types provided by the API and understand how they behave differently in Live Trading versus the Strategy Tester.
Key Concepts
- Server Time (
TimeCurrent()): This is the time on your broker's server. It determines the open/close time of bars on your chart. This is the most critical time for trade execution. - Local Time (
TimeLocal()): This is the time on your computer (Windows OS time). It is generally not used for trading logic because it varies by user and is irrelevant to market hours. - GMT (
TimeGMT()): This is Greenwhich Mean Time (UTC). It is the standard anchor for aligning strategies across different brokers who may have different server time zones (e.g., GMT+2 vs GMT+3).
The Challenge: Backtesting vs. Live
- Live Trading:
TimeGMT()returns the actual GMT time calculated from your PC's clock and OS settings. - Strategy Tester:
TimeGMT()andTimeLocal()are simulated and usually return the same value asTimeCurrent()(Server Time). This breaks GMT-based logic in backtests unless you handle offsets manually.
The Solution
The following code implements a robust Time Manager structure. It allows you to define trading hours in GMT (e.g., London Open at 08:00 GMT), and it automatically calculates the correct Server Time to trade, regardless of your broker's time zone. It also includes a manual override for accurate backtesting.
MQL4 Implementation
//+------------------------------------------------------------------+
//| TimeManagementEA.mq4|
//| Copyright 2023, Quantitative Trading AI |
//| https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2023, Quantitative Trading AI"
#property link "https://www.mql5.com"
#property version "1.00"
#property strict
//--- INPUT PARAMETERS ---
input string Inp_TimeSettings = "=== Time Settings ==="; // Section Header
input int TradingStartHourGMT = 8; // Start Trading Hour (GMT)
input int TradingEndHourGMT = 17; // End Trading Hour (GMT)
input bool UseAutoGMTOffset = true; // Auto-detect GMT Offset (Live only)
input int ManualGMTOffset = 2; // Manual Broker Offset (Required for Backtesting)
//--- GLOBAL VARIABLES ---
int g_currentGMTOffset = 0;
//+------------------------------------------------------------------+
//| Expert initialization function |
//+------------------------------------------------------------------+
int OnInit()
{
// Validate inputs
if(TradingStartHourGMT < 0 || TradingStartHourGMT > 23 ||
TradingEndHourGMT < 0 || TradingEndHourGMT > 23)
{
Alert("Invalid Trading Hours. Please set between 0 and 23.");
return(INIT_FAILED);
}
// Calculate initial offset
CalculateGMTOffset();
return(INIT_SUCCEEDED);
}
//+------------------------------------------------------------------+
//| Expert deinitialization function |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
Comment(""); // Clear chart comments
}
//+------------------------------------------------------------------+
//| Expert tick function |
//+------------------------------------------------------------------+
void OnTick()
{
// 1. Update Offset (In case of DST change or live connection update)
CalculateGMTOffset();
// 2. Get Current Times
datetime serverTime = TimeCurrent();
datetime gmtTime = GetCurrentGMTTime();
// 3. Check if we are in the trading window
bool isTradeTime = IsTradingWindow(gmtTime);
// 4. Display Dashboard
string status = isTradeTime ? "OPEN" : "CLOSED";
color statusColor = isTradeTime ? clrLime : clrRed;
string comment = "=== Time Manager ===\n" +
"Server Time: " + TimeToString(serverTime, TIME_DATE|TIME_SECONDS) + "\n" +
"GMT Time: " + TimeToString(gmtTime, TIME_DATE|TIME_SECONDS) + "\n" +
"Broker Offset: GMT " + (g_currentGMTOffset >= 0 ? "+" : "") + IntegerToString(g_currentGMTOffset) + "\n" +
"--------------------\n" +
"Trading Window (GMT): " + IntegerToString(TradingStartHourGMT) + ":00 - " + IntegerToString(TradingEndHourGMT) + ":00\n" +
"Market Status: " + status;
Comment(comment);
// 5. Trading Logic Example
if(isTradeTime)
{
// Place your strategy logic here
// Example: if(OrdersTotal() == 0) ...
}
}
//+------------------------------------------------------------------+
//| Helper: Calculate Broker's Offset from GMT |
//+------------------------------------------------------------------+
void CalculateGMTOffset()
{
// In Strategy Tester, TimeGMT() == TimeCurrent(), so Auto detection fails.
// We must use Manual Offset during testing.
if(IsTesting() || !UseAutoGMTOffset)
{
g_currentGMTOffset = ManualGMTOffset;
}
else
{
// In Live Trading, we can calculate it dynamically
// TimeGMTOffset() returns seconds. We convert to hours.
// Note: TimeGMTOffset() = TimeGMT() - TimeLocal(). This is PC offset, not Broker offset.
// Correct way to find Broker Offset:
datetime gmt = TimeGMT();
datetime server = TimeCurrent();
// Calculate difference in seconds
long diff = server - gmt;
// Round to nearest hour (handle potential small drifts)
if(diff > 0)
g_currentGMTOffset = (int)((diff + 1800) / 3600);
else
g_currentGMTOffset = (int)((diff - 1800) / 3600);
}
}
//+------------------------------------------------------------------+
//| Helper: Get Calculated GMT Time |
//+------------------------------------------------------------------+
datetime GetCurrentGMTTime()
{
// We calculate GMT based on Server Time - Broker Offset
// This ensures consistency in both Live and Backtesting environments
return(TimeCurrent() - (g_currentGMTOffset * 3600));
}
//+------------------------------------------------------------------+
//| Helper: Check if within Trading Hours |
//+------------------------------------------------------------------+
bool IsTradingWindow(datetime currentGMT)
{
int currentHour = TimeHour(currentGMT);
// Case 1: Standard Day (e.g., 08:00 to 17:00)
if(TradingStartHourGMT < TradingEndHourGMT)
{
if(currentHour >= TradingStartHourGMT && currentHour < TradingEndHourGMT)
return(true);
}
// Case 2: Overnight Session (e.g., 22:00 to 02:00)
else
{
if(currentHour >= TradingStartHourGMT || currentHour < TradingEndHourGMT)
return(true);
}
return(false);
}
Explanation of the Code
-
CalculateGMTOffset():- Live Mode: It compares
TimeCurrent()(Broker) withTimeGMT()(Universal) to find the offset (e.g., +2 or +3). - Testing Mode: It forces the use of
ManualGMTOffset. This is crucial because the Strategy Tester simulates GMT as equal to Server time, which would result in an offset of 0 and incorrect trading hours if not manually overridden.
- Live Mode: It compares
-
GetCurrentGMTTime():- Instead of calling
TimeGMT()directly in your logic (which fails in backtests), this function derives GMT by subtracting the calculated offset from the Server Time. This ensures your logic works identically in backtests and live trading.
- Instead of calling
-
IsTradingWindow():- This function handles the logic for checking hours.
- It specifically handles Overnight Sessions. If you set Start=22 (10 PM) and End=2 (2 AM), standard logic (
current > start && current < end) would fail. The logicstart < endvsstart > endhandles the midnight crossover correctly.
How to Use
- Determine your Broker's Offset: Look at your Market Watch time. If it is 10:00 and GMT is 08:00, your offset is +2.
- Backtesting: Set
UseAutoGMTOffset = falseand setManualGMTOffsetto your broker's historical offset (usually 2 or 3 for Forex brokers). - Live Trading: Set
UseAutoGMTOffset = true. The EA will automatically align itself even if Daylight Savings Time changes.
Q&A
Q: Why does TimeGMT() not work in the Strategy Tester?
A: In the Strategy Tester, MetaTrader simulates time based on the historical data (.fxt files). It does not simulate the computer's local clock or external GMT. Therefore, TimeGMT() simply returns the simulated Server Time, making the offset zero.
Q: How do I handle Daylight Savings Time (DST)?
A: Most Forex brokers switch between GMT+2 (Winter) and GMT+3 (Summer/DST) to align with New York closing time (17:00 NY).
- Live: The
UseAutoGMTOffsetlogic in the code above recalculates the offset on every tick, so it will automatically adjust when the server time shifts. - Backtesting: You must know if your historical data includes DST shifts. If you backtest over multiple years, a fixed
ManualGMTOffsetmight be slightly inaccurate for a few weeks a year during DST transitions. For 99% of strategies, a fixed offset is acceptable, but for high-precision news trading, you would need complex DST schedule arrays.
Q: Can I use TimeLocal() for trading alerts?
A: Yes. TimeLocal() is perfect for Print() or Alert() messages to tell you (the human) when something happened in your local timezone, but you should never use it for OrderSend logic or determining market opens.