Importance of Controlling Trading Hours
Effective algorithmic trading often hinges on precision, and this includes when your Expert Advisor (EA) operates. Controlling trading hours is paramount for several reasons:
- Aligning with Market Liquidity: Trading during peak liquidity hours (e.g., session overlaps) can reduce slippage and improve execution.
- Strategy Specificity: Many strategies are designed for specific market conditions prevalent during certain trading sessions (e.g., breakout strategies during London open, range trading during Asian session).
- Risk Management: Avoiding trading during highly volatile news releases or periods of low liquidity (like weekends or holidays) can protect capital.
- Resource Optimization: Prevents the EA from analyzing markets or attempting trades when conditions are unfavorable or trading is disabled by the broker.
Overview of MQL5 Functions for Time Management
MQL5 provides a robust set of functions for time management. Key functions include:
TimeCurrent(): Retrieves the current server time of the broker. This is the most crucial time for trading logic.TimeLocal(): Gets the local time of the computer where the terminal is running.TimeToStruct(datetime time, MqlDateTime& dt_struct): Converts adatetimevalue into aMqlDateTimestructure, making it easy to access individual components like hour, minute, day of the week, etc.StructToTime(MqlDateTime& dt_struct): Performs the reverse operation ofTimeToStruct.EventSetTimer() / EventKillTimer(): Manages timer events for scheduled tasks.
Using TimeCurrent() and TimeLocal() Functions
Understanding the Difference Between Server and Local Time
A common pitfall for developers is confusing server time with local time.
TimeCurrent()(Server Time): This function returns the last known server time, which is the time at the broker’s trade server. All trading operations, historical data timestamps, and server-side events are based on this time. For all trading logic,TimeCurrent()should be the primary source.TimeLocal()(Local Time): This function returns the time of the operating system on the computer where your MetaTrader 5 terminal is running. It can be useful for logging or UI elements specific to the user’s timezone, but it should not be used for making trading decisions due to potential discrepancies with server time and its irrelevance to trade execution timing.
The difference, TimeCurrent() - TimeLocal(), can be used to find the offset, but this offset can change (e.g., due to Daylight Saving Time changes on either the server or local machine) and is generally not needed if you consistently use TimeCurrent().
Retrieving Current Time Information
Both TimeCurrent() and TimeLocal() return a datetime value, which represents the number of seconds elapsed since 00:00 January 1, 1970. To work with this information in a more human-readable format, we use the MqlDateTime structure and the TimeToStruct() function.
void OnTick() {
// Retrieve server time
datetime serverTime = TimeCurrent();
MqlDateTime srvTimeStruct;
TimeToStruct(serverTime, srvTimeStruct);
PrintFormat("Server Time: %04u.%02u.%02u %02u:%02u:%02u (Day of Week: %d)",
srvTimeStruct.year, srvTimeStruct.mon, srvTimeStruct.day,
srvTimeStruct.hour, srvTimeStruct.min, srvTimeStruct.sec,
srvTimeStruct.day_of_week);
// Retrieve local time
datetime localTime = TimeLocal();
MqlDateTime locTimeStruct;
TimeToStruct(localTime, locTimeStruct);
PrintFormat("Local Time: %04u.%02u.%02u %02u:%02u:%02u (Day of Week: %d)",
locTimeStruct.year, locTimeStruct.mon, locTimeStruct.day,
locTimeStruct.hour, locTimeStruct.min, locTimeStruct.sec,
locTimeStruct.day_of_week);
}
Note: srvTimeStruct.day_of_week will be 0 for Sunday, 1 for Monday, …, 6 for Saturday.
Implementing Trading Hour Restrictions
Checking if the Current Time is Within Allowed Trading Hours
To restrict trading to specific hours, you need to extract the hour and minute from the current server time and compare them against your desired trading window.
// Input parameters for the EA
input int StartTradingHour = 9; // e.g., 9 AM
input int StartTradingMinute = 0;
input int EndTradingHour = 17; // e.g., 5 PM (trades will not be placed at 17:00 or later)
input int EndTradingMinute = 0;
bool IsWithinTradingHours() {
MqlDateTime currentTimeStruct;
TimeToStruct(TimeCurrent(), currentTimeStruct);
int currentTotalMinutes = currentTimeStruct.hour * 60 + currentTimeStruct.min;
int startTotalMinutes = StartTradingHour * 60 + StartTradingMinute;
int endTotalMinutes = EndTradingHour * 60 + EndTradingMinute;
// Case 1: Normal period (e.g., 09:00 - 17:00)
if (startTotalMinutes <= endTotalMinutes) {
return (currentTotalMinutes >= startTotalMinutes && currentTotalMinutes < endTotalMinutes);
}
// Case 2: Overnight period (e.g., 22:00 - 05:00)
else {
return (currentTotalMinutes >= startTotalMinutes || currentTotalMinutes < endTotalMinutes);
}
}
void OnTick() {
if (!IsWithinTradingHours()) {
Print("Outside of configured trading hours. No new trades.");
return;
}
// Proceed with trading logic
Print("Within trading hours. Evaluating trading signals...");
}
The condition currentTotalMinutes < endTotalMinutes ensures that trading stops before the EndTradingHour:EndTradingMinute. If you want to include trades exactly at the end minute, you might adjust logic or input expectations.
Creating Functions to Define Trading Sessions
For clarity and reusability, encapsulate session checking logic into functions. You might have multiple sessions, e.g., Asian, London, New York.
// Structure to define a trading session
struct TradingSession {
string name;
int startHour;
int startMinute;
int endHour;
int endMinute;
bool isActive; // To easily enable/disable sessions via inputs
bool IsTimeWithin(const MqlDateTime &dt) const {
int currentTotalMinutes = dt.hour * 60 + dt.min;
int startTotalMinutes = startHour * 60 + startMinute;
int endTotalMinutes = endHour * 60 + endMinute;
if (startTotalMinutes <= endTotalMinutes) {
return (currentTotalMinutes >= startTotalMinutes && currentTotalMinutes < endTotalMinutes);
} else {
return (currentTotalMinutes >= startTotalMinutes || currentTotalMinutes < endTotalMinutes);
}
}
};
// Example usage:
TradingSession londonSession = {"London", 10, 0, 18, 0, true}; // Assuming broker time +2 UTC, London opens 8 AM UTC (10:00 broker)
void OnTick() {
MqlDateTime currentTimeStruct;
TimeToStruct(TimeCurrent(), currentTimeStruct);
if (londonSession.isActive && londonSession.IsTimeWithin(currentTimeStruct)) {
Print(londonSession.name, " session is active.");
// London session trading logic
} else {
Print(londonSession.name, " session is inactive or outside hours.");
}
}
Handling Weekend Restrictions
Most markets are closed on weekends. It’s crucial to prevent your EA from attempting to trade during these times.
bool IsWeekend(datetime time) {
MqlDateTime dtStruct;
TimeToStruct(time, dtStruct);
return (dtStruct.day_of_week == SUNDAY || dtStruct.day_of_week == SATURDAY); // 0 for Sunday, 6 for Saturday
}
void OnTick() {
if (IsWeekend(TimeCurrent())) {
Print("Market closed (Weekend). No trading.");
return;
}
// ... rest of your trading logic
}
Advanced Techniques for Time-Based Trading
Utilizing EventSetTimer() for Scheduled Operations
Sometimes you need actions performed at specific intervals or times, independent of incoming ticks. EventSetTimer() creates a timer that will generate OnTimer() events.
// In OnInit()
int OnInit() {
// Set a timer to trigger every minute
EventSetTimer(60); // Parameter is in seconds
return(INIT_SUCCEEDED);
}
// In OnDeinit()
void OnDeinit(const int reason) {
EventKillTimer();
}
// Timer event handler
void OnTimer() {
// This code will execute every 60 seconds
MqlDateTime now;
TimeToStruct(TimeCurrent(), now);
// Example: Check if it's 5 minutes before market close to flatten positions
// This is a simplified example; precise closing logic might vary.
// if (now.hour == EndTradingHour && now.min == (EndTradingMinute - 5 + 60) % 60 ) {
// CloseAllPositions();
// Print("Approaching end of trading day, preparing to close positions.");
// }
// Another example: Perform a check at the start of every hour
if (now.min == 0 && now.sec < 5) { // Check within the first few seconds of the hour
PrintFormat("Hourly check at %02u:00", now.hour);
// Perform hourly tasks
}
}
EventSetTimer is powerful for tasks like periodic checks, data downloads, or time-based order modifications without relying on OnTick.
Calculating Time to Next Trading Session
Knowing when the next trading session begins can be useful for an EA to manage its state or provide information to the user. The general approach involves:
- Determining the target start time (
hour,minute) for the session. - Getting the current server time (
TimeCurrent()) and converting it to anMqlDateTimestructure. - Creating an
MqlDateTimestructure for today’s session start time by setting the hour and minute, keeping the current date. - Converting both current time and target session start time to
datetimetimestamps (seconds since epoch). - If the current time is already past today’s session start time, the next session will be on a subsequent day. You would then calculate the timestamp for the session start on the next valid trading day (e.g., adding
24*60*60seconds for the next day, and ensuring to skip weekends). - The difference between the next session start timestamp and the current timestamp gives the seconds remaining. This can be formatted into hours/minutes/seconds.
This calculation requires careful handling of date rollovers (day, month, year) and skipping non-trading days (weekends, holidays). For many EAs, a simpler approach is to periodically check (e.g., via OnTimer or OnTick) if the current time is within an allowed session and remain idle otherwise, rather than calculating precise countdowns.
Implementing Dynamic Trading Schedules
For sophisticated strategies, trading hours might not be static. They could depend on:
- Day of the week: Different hours for Monday vs. Friday.
- Volatility conditions: Only trade if ATR is above a certain level during specific general hours.
- News events: Pausing trading around major news releases. This typically requires an external news calendar integration or a manual input mechanism.
Implementing this involves adding more layers of conditional logic to your IsTradingAllowed() function or equivalent checks, potentially using arrays of TradingSession structs and selecting the active one based on day_of_week or other criteria.
// Example: Different closing hours for Friday
// (Assumes StartTradingHour, StartTradingMinute, EndTradingHour, EndTradingMinute are global or inputs)
input int FridayEndHour = 15;
input int FridayEndMinute = 0;
bool IsWithinTradingHoursDynamic() {
MqlDateTime currentTimeStruct;
TimeToStruct(TimeCurrent(), currentTimeStruct);
int effectiveEndHour = EndTradingHour; // Default end hour
int effectiveEndMinute = EndTradingMinute; // Default end minute
if (currentTimeStruct.day_of_week == FRIDAY) {
effectiveEndHour = FridayEndHour;
effectiveEndMinute = FridayEndMinute;
}
int currentTotalMinutes = currentTimeStruct.hour * 60 + currentTimeStruct.min;
int startTotalMinutes = StartTradingHour * 60 + StartTradingMinute;
int endTotalMinutes = effectiveEndHour * 60 + effectiveEndMinute;
if (startTotalMinutes <= endTotalMinutes) { // Normal period
return (currentTotalMinutes >= startTotalMinutes && currentTotalMinutes < endTotalMinutes);
} else { // Overnight period
return (currentTotalMinutes >= startTotalMinutes || currentTotalMinutes < endTotalMinutes);
}
}
Practical Examples and Considerations
Example: Trading only during London and New York sessions
This requires defining multiple time windows and checking if the current time falls into any of them. Broker server time is key. Assume your broker server is GMT+2 (this is an example, verify your broker’s time).
- London Actual Open: 8:00 AM GMT -> If broker is GMT+2, this is 10:00 AM Broker Time.
- New York Actual Open: 1:00 PM GMT (13:00) -> If broker is GMT+2, this is 3:00 PM Broker Time (15:00).
Set your EA inputs based on the broker’s server time.
// Inputs for London Session (Broker Time)
input int LonStartHour = 10; input int LonStartMin = 0; // Example: 10:00 broker time
input int LonEndHour = 18; input int LonEndMin = 0; // Example: 18:00 broker time
// Inputs for New York Session (Broker Time)
input int NYStartHour = 15; input int NYStartMin = 0; // Example: 15:00 broker time
input int NYEndHour = 23; input int NYEndMin = 0; // Example: 23:00 broker time
// Reusable function to check if current time is in a given session's hours
bool IsTimeInSession(const MqlDateTime &dt, int sH, int sM, int eH, int eM) {
int currentTotalMinutes = dt.hour * 60 + dt.min;
int startTotalMinutes = sH * 60 + sM;
int endTotalMinutes = eH * 60 + eM;
if (startTotalMinutes <= endTotalMinutes) {
return (currentTotalMinutes >= startTotalMinutes && currentTotalMinutes < endTotalMinutes);
} else { // Handles overnight sessions, though not typical for Lon/NY individually
return (currentTotalMinutes >= startTotalMinutes || currentTotalMinutes < endTotalMinutes);
}
}
void OnTick() {
if (IsWeekend(TimeCurrent())) {
Print("Market closed (Weekend).");
return;
}
MqlDateTime currentTimeStruct;
TimeToStruct(TimeCurrent(), currentTimeStruct);
bool isLondonActive = IsTimeInSession(currentTimeStruct, LonStartHour, LonStartMin, LonEndHour, LonEndMin);
bool isNYActive = IsTimeInSession(currentTimeStruct, NYStartHour, NYStartMin, NYEndHour, NYEndMin);
if (isLondonActive || isNYActive) {
string activeSessions = "";
if (isLondonActive) activeSessions += "London ";
if (isNYActive) activeSessions += "New York";
PrintFormat("%s session(s) active. Trade logic enabled.", StringTrim(activeSessions));
// --- Your core trading logic here ---
} else {
Print("Outside of specified London/NY trading sessions.");
}
}
Dealing with Broker Time Zone Differences
The fundamental rule is: always base your trading times on TimeCurrent() (broker’s server time). Your EA’s input parameters for start and end hours must be relative to this server time.
- DST (Daylight Saving Time): Most brokers adjust their server time for DST if their server’s locality observes it. This means
TimeCurrent()automatically reflects these changes. If your strategy targets specific UTC times (e.g., London open always at 8:00 UTC), you must be aware of your broker’s current UTC offset and how it changes with DST. Some brokers maintain a fixed offset (e.g., GMT+2 always), while others shift (e.g., GMT+2 in winter, GMT+3 in summer for European brokers).- To make an EA robust to broker DST changes when targeting absolute UTC times, you’d ideally need to know the broker’s current UTC offset. MQL5 doesn’t have a direct function like
BrokerGMTOffset(). You might compareTimeCurrent()withTimeGMT()(available inMqlDateTimefromTimeToStruct(TimeCurrent(), dt); dt.gmt_ofs;– but this is client’s interpretation of GMT offset forTimeCurrent()not necessarily broker’s base definition). A common, robust method is to use an input parameter for the broker’s current base UTC offset, which you update manually if necessary. - Often, it’s simpler to define session times in broker server hours in your EA’s inputs. You would then manually adjust these inputs if the broker’s DST changes shift their server time relative to the true market session times you are targeting (e.g., London open at 8 AM UTC).
- To make an EA robust to broker DST changes when targeting absolute UTC times, you’d ideally need to know the broker’s current UTC offset. MQL5 doesn’t have a direct function like
Best Practices for Reliable Time Management in MQL5
- Exclusively Use
TimeCurrent(): For all trading logic, time-based conditions, and historical data alignment, rely solely on the broker’s server time. MqlDateTimeis Your Friend: Convertdatetimevalues to theMqlDateTimestructure usingTimeToStruct()for easy and reliable access to hours, minutes, day of the week, etc.- Parameterize Trading Hours: Use
inputvariables for start/end hours and minutes. This makes the EA flexible, testable, and configurable without code modification. - Handle Overnight Sessions Correctly: If your defined trading window spans midnight (e.g., 22:00 server time to 05:00 server time), ensure your comparison logic
(currentTime >= startTime || currentTime < endTime)is correctly implemented using total minutes or consistent hour/minute comparisons. - Weekend and Holiday Handling: Always include logic to prevent trading on weekends (
day_of_week == SATURDAY || day_of_week == SUNDAY). For market holidays, MQL5 does not provide a built-in calendar; this requires a manual list, external data feed, or user input. - Thorough Testing in Strategy Tester: Test your time-based logic extensively, especially across date ranges that include DST transitions for your broker’s server time zone. Test with different brokers (demo accounts) if you plan to use the EA widely, as server time configurations can vary.
- Consider
OnTimerfor Precise Timed Actions: If an action must occur at a very specific time (e.g., closing all positions at 16:59:59 server time), usingEventSetTimer()and theOnTimer()event handler is more reliable thanOnTick, as ticks are not guaranteed to arrive at every second. - Keep it Server-Centric: Always think and code in terms of the broker’s server time. If your strategy says