In algorithmic trading, precise timing is paramount. Understanding not just when an order was opened or modified, but critically, when it was closed, provides invaluable data for performance analysis, logging, and subsequent trading decisions. For MQL5 developers building complex Expert Advisors or analytical tools, accessing the exact closure timestamp of a trade is a fundamental requirement.
This article delves into the methods available in MQL5 to accurately determine when an order was closed, focusing on accessing historical trade data.
Understanding Order Properties in MQL5
MQL5 provides a rich set of functions and enumerations to query various properties of trades, orders, and positions. These properties include things like ticket numbers, symbols, volumes, prices, and timestamps. For current open positions or pending orders, you’d typically use functions like PositionGetDouble(), PositionGetInteger(), etc., or OrderGetInteger() for pending orders.
However, properties related to the completion of a trade, such as the close price or close time, are only relevant for orders that have already been executed and closed, or for deals that have occurred.
Importance of Knowing Order Close Time
Knowing the exact close time of an order or deal is crucial for several reasons:
- Backtesting and Analysis: Accurate backtesting relies on precise entry and exit times. The close time helps evaluate the duration of a trade and analyze profitability over specific periods.
- Performance Logging: For detailed logging and auditing of an EA’s performance, recording the close time is essential.
- Strategy Logic: Some strategies might depend on the time a previous trade was closed (e.g., avoiding re-entry too soon after a loss, or timing entries based on market sessions relative to past exits).
- Compliance and Reporting: For regulatory or personal reporting, precise trade timestamps are often required.
Methods to Determine Order Close Time
Unlike MQL4, where OrderCloseTime() was directly available after selecting a historical order, MQL5 uses a more unified approach via history functions and property accessors.
Using HistoryOrderGetInteger() Function
The primary way to get integer properties of a historical order in MQL5 is using the HistoryOrderGetInteger() function. This function requires the ticket of the historical order and the specific property ID you want to retrieve.
Accessing ORDER_TIME_CLOSE Property
The specific property ID needed to get the order’s close time is ORDER_TIME_CLOSE. This property stores the timestamp of when the order state transitioned to a final state (like Filled, Canceled, Expired) that resulted in its closure or removal from the market. For market orders, this corresponds to the deal execution time if the order was filled.
To access this:
- Select the history range using
HistorySelect(). This loads historical orders and deals into memory for processing. - Iterate through the historical orders using
HistoryOrdersTotal()andHistoryOrderGetTicket(). Alternatively, iterate through deals usingHistoryDealsTotal()andHistoryDealGetTicket(), then link the deal back to the order if necessary (though the deal time often suffices and can be retrieved viaHistoryDealGetInteger(..., DEAL_TIME)orDEAL_TIME_MSC). For strict order close time,HistoryOrderGetInteger(..., ORDER_TIME_CLOSE)is the method. - Once you have a historical order ticket, call
HistoryOrderGetInteger(order_ticket, ORDER_TIME_CLOSE).
The function returns the timestamp as a datetime value, which is the number of seconds since January 1, 1970 (Unix epoch).
Checking Order History
Accessing the close time requires accessing historical data. Open or pending orders do not have a populated ORDER_TIME_CLOSE property; it would return 0. Therefore, you must use the history functions.
The typical workflow involves:
- Calling
HistorySelect(from_date, to_date)to load the relevant history. - Checking the return value of
HistorySelect()to ensure the data was loaded successfully. - Iterating through the orders or deals within the selected range.
- Using
HistoryOrderGetInteger()orHistoryDealGetInteger()with the appropriate time property (ORDER_TIME_CLOSE,DEAL_TIME,DEAL_TIME_MSC).
Practical Examples and Code Snippets
Let’s illustrate with some code examples.
Example 1: Retrieving Close Time of the Last Closed Order
This example finds the most recent historical order and prints its close time.
void OnStart()
{
// Select history for a recent period (e.g., last 7 days)
datetime from_time = TimeCurrent() - 7*24*60*60; // 7 days ago
datetime to_time = TimeCurrent();
if(HistorySelect(from_time, to_time))
{
int total_orders = HistoryOrdersTotal();
if(total_orders > 0)
{
// Get the ticket of the last historical order
ulong last_order_ticket = HistoryOrderGetTicket(total_orders - 1);
// Get its close time
long close_time = HistoryOrderGetInteger(last_order_ticket, ORDER_TIME_CLOSE);
if(close_time != 0)
{
Print("Last historical order ticket: ", last_order_ticket);
Print("Close Time: ", (datetime)close_time);
}
else
{
// This might happen if the last 'order' was not actually closed
// in a way that populates ORDER_TIME_CLOSE, or if there was an error.
// For executed trades, checking deals might be more robust.
Print("Last historical order (ticket ", last_order_ticket, ") does not have a valid close time.");
}
}
else
{
Print("No historical orders found in the selected range.");
}
}
else
{
Print("Failed to select history. Error: ", GetLastError());
}
}
Example 2: Getting Close Time Within a Loop
This example iterates through all historical orders in a range and prints details for those with a valid close time.
void OnStart()
{
// Select history for the last 30 days
datetime from_time = TimeCurrent() - 30*24*60*60; // 30 days ago
datetime to_time = TimeCurrent();
if(HistorySelect(from_time, to_time))
{
int total_orders = HistoryOrdersTotal();
Print("Selected history range: ", from_time, " - ", to_time);
Print("Total historical orders found: ", total_orders);
for(int i = 0; i < total_orders; i++)
{
ulong order_ticket = HistoryOrderGetTicket(i);
long close_time = HistoryOrderGetInteger(order_ticket, ORDER_TIME_CLOSE);
// ORDER_TIME_CLOSE is 0 for orders that were not filled/closed in a standard way
// (e.g., parent orders of deals, or specific order types).
// It's populated for orders that resulted in a deal or were cancelled/expired after being placed.
if(close_time != 0)
{
string symbol = HistoryOrderGetString(order_ticket, ORDER_SYMBOL);
ENUM_ORDER_TYPE type = (ENUM_ORDER_TYPE)HistoryOrderGetInteger(order_ticket, ORDER_TYPE);
PrintFormat("Order #%I64u, Symbol: %s, Type: %s, Close Time: %s",
order_ticket,
symbol,
EnumToString(type),
(datetime)close_time);
}
}
if(total_orders == 0)
{
Print("No historical orders found in the selected range.");
}
}
else
{
Print("Failed to select history. Error: ", GetLastError());
}
}
Example 3: Handling Errors and Invalid Orders
Always check the return values of MQL5 functions. HistorySelect() returns true on success. HistoryOrderGetInteger() can fail if the ticket is invalid or the property is not available for that specific order type/state. While ORDER_TIME_CLOSE for a valid historical order ticket should generally return a value (even 0 if not applicable), checking GetLastError() after crucial calls is good practice, though not strictly necessary for HistoryOrderGetInteger itself if the ticket is known to be valid from HistoryOrderGetTicket.
The check if (close_time != 0) in the examples serves as a way to filter out orders that didn’t result in a standard closure with a recorded timestamp.
Working with Different Time Zones
Understanding Server Time vs. Local Time
The datetime value returned by ORDER_TIME_CLOSE represents the time on the broker’s server when the order was closed. This server time is typically UTC or has a fixed offset from UTC, but it is not necessarily your local computer’s time.
All time-related properties obtained from the trading server (like TimeCurrent(), ORDER_TIME_OPEN, ORDER_TIME_CLOSE, DEAL_TIME) are in the server’s time zone.
Converting Order Close Time to a Specific Time Zone
If you need to display or process the close time in your local time zone or another specific time zone, you must perform a conversion. MQL5 provides functions like TimeGMT(), TimeLocal(), and TimeGMTOffset() to help with this.
A common conversion involves understanding the offset between server time (often UTC) and the target time zone. Since datetime is seconds since epoch, you can add or subtract the necessary number of seconds for the offset.
For example, to convert a server timestamp (server_time) to the terminal’s local time:
datetime server_time = ...; // Retrieved ORDER_TIME_CLOSE
long local_time_offset = TimeLocal() - TimeGMT(); // Difference between local time and GMT/UTC
datetime local_close_time = server_time + local_time_offset;
Note: This simple conversion assumes server time is GMT/UTC. Verify your broker’s server time zone. For brokers using a different fixed offset, you’d need to account for that offset relative to GMT.
Conclusion
Accurately determining order close time in MQL5 is fundamental for robust strategy development, analysis, and logging. While MQL4 offered a direct function, MQL5 utilizes a history-centric approach accessing properties via HistoryOrderGetInteger() or HistoryDealGetInteger().
Summary of Key Methods
- Use
HistorySelect(from_date, to_date)to load relevant trade history. - Iterate through historical orders using
HistoryOrdersTotal()andHistoryOrderGetTicket(). - Retrieve the close time using
HistoryOrderGetInteger(order_ticket, ORDER_TIME_CLOSE).DEAL_TIMEorDEAL_TIME_MSCviaHistoryDealGetInteger()is often more direct for executed trades. - Always check the success of
HistorySelect(). - Be aware that
ORDER_TIME_CLOSEis 0 for orders not resulting in a standard closure state that populates this field.
Best Practices for Handling Order Close Time in MQL5
- Always work with historical data when querying close times.
- Validate the result of
HistorySelect()and individual property retrieval functions. - Understand that the timestamp is in server time and perform necessary conversions if local or other time zones are required.
- Consider whether
DEAL_TIMEfrom the deal history might be more appropriate for executed trades, as it directly corresponds to the transaction time.
Mastering the MQL5 history functions is key to developing sophisticated and reliable trading applications that can accurately track and analyze past trading activity.