Quick Answer
Trade return code 10018 (TRADE_RETCODE_MARKET_CLOSED) means that the market of the symbol is closed for trading at the time of your order. Every symbol has trade sessions: the hours of each weekday in which the broker accepts orders. Outside those hours the server rejects the order. This is common on weekends, at the daily rollover, on holidays, and for symbols such as indexes, stocks and some metals that do not trade around the clock.
An EA can avoid it in two steps. First, check that trading is allowed for the symbol (SYMBOL_TRADE_MODE). Second, check that the server time is inside one of the symbol's sessions, using SymbolInfoSessionTrade. If the check fails, skip the order and try again on the next tick or the next bar, and do not retry in a fast loop.
From the author: I build and convert Expert Advisors, and 10018 is one of the errors that appears only in live trading. A backtest on historical data can place a trade at a time when the real market was closed, and a live EA then meets the closed market. This guide shows how to read the sessions and add a safe check.
Key Facts
| What you see | Usual cause | Fix |
|---|---|---|
| 10018 on Saturday or Sunday | The weekend | Skip orders when the market is closed |
| 10018 around midnight server time | Daily rollover break | Check the session of the symbol |
| 10018 on an index or stock CFD | The symbol has short sessions | Read the sessions, not a fixed hour |
| 10018 on one symbol, fine on another | Sessions differ between symbols | Check each symbol on its own |
| The chart shows quotes, but orders fail | Quotes can exist outside the trade session | Use the trade session, not the quote session |
| 10018 right after a holiday | A holiday closes the market | Skip and wait |
What the Error Looks Like
With CTrade, a rejected order makes Buy or Sell return false, and the details are in the result:
if(!trade.Buy(lots, _Symbol, 0.0, sl, tp, "bot"))
{
PrintFormat("Order failed. Retcode %u: %s",
trade.ResultRetcode(), trade.ResultRetcodeDescription());
}
The full list of return codes is in MQL5 trade return codes. For the stops, see MQL5 invalid stops error 10016.
Quote Sessions and Trade Sessions
A symbol has two kinds of sessions, and they are not always the same:
- Quote sessions are the hours when the broker sends prices. Read them with
SymbolInfoSessionQuote. - Trade sessions are the hours when orders are accepted. Read them with
SymbolInfoSessionTrade.
You can see price changes in a window where you still cannot trade, so the check must use the trade session. The sessions are also shown in the symbol specification in the Market Watch of the terminal. They can be different for each broker.
Step 1: Is Trading Allowed for the Symbol?
SYMBOL_TRADE_MODE tells what kind of orders the broker accepts for the symbol:
| Value | Meaning |
|---|---|
| SYMBOL_TRADE_MODE_DISABLED | Trading is disabled |
| SYMBOL_TRADE_MODE_LONGONLY | Only buy positions |
| SYMBOL_TRADE_MODE_SHORTONLY | Only sell positions |
| SYMBOL_TRADE_MODE_CLOSEONLY | Only closing positions |
| SYMBOL_TRADE_MODE_FULL | No restrictions |
bool TradingAllowed(const string symbol)
{
long mode = SymbolInfoInteger(symbol, SYMBOL_TRADE_MODE);
return (mode == SYMBOL_TRADE_MODE_FULL);
}
This simple version accepts only the full mode. An EA that trades in one direction can also accept the matching one-sided mode, and an EA that only closes positions can accept the close-only mode.
Step 2: Is the Server Time Inside a Trade Session?
The sessions are stored per weekday and numbered from 0. For each session, SymbolInfoSessionTrade gives the start and the end as the number of seconds since 00:00 of that day, in server time:
// Returns true when the server time is inside a trade session of the symbol.
bool IsInTradeSession(const string symbol)
{
MqlDateTime now;
TimeToStruct(TimeTradeServer(), now);
int secondsToday = now.hour * 3600 + now.min * 60 + now.sec;
for(uint index = 0; index < 10; index++)
{
datetime from = 0, to = 0;
if(!SymbolInfoSessionTrade(symbol, (ENUM_DAY_OF_WEEK)now.day_of_week, index, from, to))
break; // no more sessions for this day
// from and to are seconds since 00:00; "to" can be 86400 for the end of the day
if(secondsToday >= (long)from && secondsToday < (long)to)
return true;
}
return false;
}
Three details matter here. The time must be the server time (TimeTradeServer()), because the sessions are in server time and not in your computer time. A day can have more than one session (a break in the middle of the day), so the loop goes through the indexes until the function returns false. And a day with no session at all, such as a weekend for forex, returns false at the first index, so the check is false.
The upper limit of 10 sessions per day is a safe guard for the loop, and most symbols have far fewer. If a day ends at midnight, the end value can be 86400, which the comparison above handles.
Combine the Checks
bool CanTradeNow(const string symbol)
{
if(!TradingAllowed(symbol))
return false;
if(!IsInTradeSession(symbol))
return false;
return true;
}
Call it before every order and let the EA decide what to do when it returns false. Most EAs just return from OnTick and wait for the next tick.
Using It With CTrade
#include <Trade\Trade.mqh>
CTrade trade;
void TryBuy(double lots, double sl, double tp)
{
if(!CanTradeNow(_Symbol))
{
Print("Skipping the order: the market of ", _Symbol, " is closed or trading is not allowed");
return;
}
if(!trade.Buy(lots, _Symbol, 0.0, sl, tp, "bot"))
PrintFormat("Buy failed. Retcode %u: %s",
trade.ResultRetcode(), trade.ResultRetcodeDescription());
}
A closed-market check also helps the other direction: do not place a pending order or modify a stop at a moment when the server will reject it.
Weekend, Rollover and Gaps
An EA that trades on a new bar can meet the first bar after the weekend, when the price has jumped (a gap). The market is open at that moment, so there is no 10018, but the spread can be wide and the stops can be too close. Two usual protections are a spread check before the order and a short wait after the session opens. The pre-live rules for the spread are in what to check in a trading bot before going live.
The daily rollover is a short break of many forex symbols at the end of the server day, and the spread is often wide around it. The session check above handles a closed break, and a spread filter handles a wide spread.
A Full EA That Only Prints
This EA does not trade. It prints the trade mode and the trade sessions of the current symbol for each weekday, and then it prints the result of the check on every new minute, so you can compare the numbers with the symbol specification of your broker:
datetime g_lastMinute = 0;
bool TradingAllowed(const string symbol)
{
return (SymbolInfoInteger(symbol, SYMBOL_TRADE_MODE) == SYMBOL_TRADE_MODE_FULL);
}
bool IsInTradeSession(const string symbol)
{
MqlDateTime now;
TimeToStruct(TimeTradeServer(), now);
int secondsToday = now.hour * 3600 + now.min * 60 + now.sec;
for(uint index = 0; index < 10; index++)
{
datetime from = 0, to = 0;
if(!SymbolInfoSessionTrade(symbol, (ENUM_DAY_OF_WEEK)now.day_of_week, index, from, to))
break;
if(secondsToday >= (long)from && secondsToday < (long)to)
return true;
}
return false;
}
int OnInit()
{
PrintFormat("%s trade mode: %d", _Symbol, (int)SymbolInfoInteger(_Symbol, SYMBOL_TRADE_MODE));
for(int day = 0; day < 7; day++)
{
for(uint index = 0; index < 10; index++)
{
datetime from = 0, to = 0;
if(!SymbolInfoSessionTrade(_Symbol, (ENUM_DAY_OF_WEEK)day, index, from, to))
break;
PrintFormat("Day %d, session %u: %s - %s", day, index,
TimeToString(from, TIME_MINUTES), TimeToString(to, TIME_MINUTES));
}
}
return INIT_SUCCEEDED;
}
void OnTick()
{
datetime minute = TimeTradeServer() / 60 * 60;
if(minute == g_lastMinute)
return;
g_lastMinute = minute;
PrintFormat("%s server time %s: trading allowed %s, inside a trade session %s",
_Symbol, TimeToString(TimeTradeServer(), TIME_DATE | TIME_MINUTES),
TradingAllowed(_Symbol) ? "yes" : "no",
IsInTradeSession(_Symbol) ? "yes" : "no");
}
In the Strategy Tester the server time follows the test data, so you can also use it to see which of your test trades would have fallen outside a trade session in real trading.
Common Causes Checklist
- A weekend or a holiday.
- The daily break of the symbol.
- Short sessions of an index, a stock or a metal.
- The wrong clock: computer time instead of server time.
- Quote session instead of trade session.
- Trade mode is close-only or one-sided.
- A backtest that trades at times when the real market is closed.
- A retry loop that sends the same order again and again.
Common Mistakes
- Hard-coding trading hours for all symbols.
- Using
TimeCurrent()or the local time instead ofTimeTradeServer(). - Retrying instantly after 10018.
- Testing only on a symbol that trades 24 hours on weekdays.
- Ignoring the return value of
trade.Buyandtrade.Sell.
FAQ
Related Guides
- MQL5 trade return codes: what each return code means and how to react.
- MQL5 invalid stops error 10016: the matching error for stop loss and take profit.
- MQL5 unsupported filling mode error: another order rejection with a clear fix.
- MT5 every tick vs real ticks: what the tester models and what it does not.
- MQL5 daily loss limit: stop trading when the day goes wrong.
- Pine Script barstate.isnew in MQL5: run logic once per bar.
- What to check in a trading bot before going live: a pre-live checklist for stops, lot size, spread and trade results.
- MQL5 invalid volume error 10014: a lot size that does not match the symbol rules.
- MQL5 not enough money error 10019: not enough free margin for the order.
- MQL5 invalid price error 10015: a price that is stale, on the wrong side or not rounded to the tick size.
Fix Compile Errors Without Guessing
If an Expert Advisor does not compile, or you want a second look at its order and risk logic, paste the code into the CodeFlowOS Error Fixer. It suggests a minimal fix, explains each error and shows what changed. A fix uses 1 credit, with up to 3 attempts included, and the compiler check is optional. Outputs are not guaranteed, and code that compiles can still have wrong trading logic, so always review the result. The Code Review is free during the early-bird period and checks an EA for common mistakes such as a fixed lot size or a missing stop loss. CodeFlowOS also converts strategies between Pine Script, MQL5 and cTrader C#: try the converter page.
This article is educational. It does not describe trading performance and is not financial advice. Always compile and test code in MetaEditor and the Strategy Tester before using it on a live account.