Expert Advisor Not Placing Trades in MT5: A Diagnostic Checklist

When an Expert Advisor is not placing trades in MetaTrader 5, the cause sits in one of four layers: the EA is not running (a log line ending in failed with code 1 means OnInit() returned INIT_FAILED and the terminal unloaded it), it is not allowed to trade, the trade server rejects the request, or the EA's own entry logic never fires. Check the layers in that order, because MetaTrader 5 reports most rejections as a numbered return code: 10027 for automated trading disabled in the terminal, 10030 for an invalid filling type, 10016 for invalid stops. This checklist follows the MQL5 reference as of November 2026 and links each check to its source.

Why is my MT5 Expert Advisor not placing trades?

An EA can fail quietly in three ways: unloaded during initialization, attached without permission to trade, or running and sending requests that the trade server rejects. In each case no trade appears, and the only trace is a line in a log. So start with the logs, not the code.

The Toolbox window has two logs. The Experts tab shows messages from running EAs and indicators, including everything your code writes with Print() or PrintFormat(). The Journal tab shows platform events, including trade operations. Both tabs show only recent entries; the daily files YYYYMMDD.LOG are in MQL5\Logs (Experts) and Logs (platform), and the log viewer in the context menu can filter to errors only.

Each step below names the MQL5 function or property that reports the state, so the EA can log its own diagnosis. If you would rather have an existing EA checked or rebuilt, see Expert Advisor development and review for MetaTrader 5.

The diagnostic checklist in eight steps

Status: MQL5 reference and MetaTrader 5 help, checked November 3, 2026; the newest platform build in the release notes is 6230 of September 24, 2026. Work from top to bottom: a failure in an early step makes the later ones meaningless. Each row links to its reference page.

StepCheckHow MQL5 reports itCodes when it fails
1EA loaded and initializedExperts log, return value of OnInit()“failed with code N”, REASON_INITFAILED
2Permissions: terminal, EA, account, serverTERMINAL_TRADE_ALLOWED, MQL_TRADE_ALLOWED, ACCOUNT_TRADE_ALLOWED, ACCOUNT_TRADE_EXPERT10027, 10026, runtime error 4752
3Symbol and account rulessymbol name, SymbolSelect(), SYMBOL_TRADE_MODE, ACCOUNT_MARGIN_MODEerrors 4301, 4302; 10017, 10042 to 10046
4Trading hours and quotesSymbolInfoSessionTrade(), TimeCurrent()10018, 10021
5Volume, limits and marginSYMBOL_VOLUME_MIN, _MAX, _STEP, _LIMIT, ACCOUNT_LIMIT_ORDERS, OrderCalcMargin()10014, 10019, 10033, 10034, 10040
6Filling modeSYMBOL_FILLING_MODE, SYMBOL_TRADE_EXEMODE10030
7Prices and stop distancesSYMBOL_TRADE_STOPS_LEVEL, SYMBOL_TRADE_FREEZE_LEVEL, SYMBOL_TRADE_TICK_SIZE10015, 10016, 10025, 10029
8Entry logic and datathe EA's own conditions, indicator handles, CopyBuffer()no request at all; CopyBuffer() returns -1

What does “failed with code 1” mean in MT5?

The message reports the value OnInit() returned. According to the MQL5 reference, zero means success; any non-zero value means failed initialization with deinitialization reason REASON_INITFAILED, and an EA that returns INIT_FAILED is forcibly unloaded from the chart. The official MQL5 book gives INIT_FAILED the value 1. The EA never receives a tick, so it seems to do nothing. The line printed just before the message usually says why:

  • An indicator handle could not be created. The reference's own sample returns INIT_FAILED when iCustom() returns INVALID_HANDLE, for example because the custom indicator was never compiled or its .ex5 file is not in MQL5\Indicators; iCustom() then reports error 4802 (ERR_INDICATOR_CANNOT_CREATE).
  • Inputs failed validation. For invalid inputs the reference recommends INIT_PARAMETERS_INCORRECT, which the Strategy Tester treats differently during optimization.
  • Checks that run too early. The MQL5 book warns that OnInit() often runs before the terminal has connected to the trade server, so symbol and account properties can still be unknown; even the value of a pip can come back as zero. An EA that rejects such values can fail after a terminal restart and still work when attached by hand. Move these checks to the first tick or timer event.

Not to be confused: in MQL4, “error 1” is ERR_NO_RESULT, which MetaQuotes' article on the checks for Market products names for a modify request that changes nothing; MQL5 reports that case as TRADE_RETCODE_NO_CHANGES (10025).

Permissions: terminal, EA, account and server

All four permissions must be true at the same time. The MetaTrader 5 help calls the toolbar button AutoTrading; in recent builds the same button may be labeled Algo Trading.

  1. Terminal: the toolbar button and the matching option under Tools, Options, Expert Advisors. When it is off, no EA can trade, whatever its own settings say. In code: TerminalInfoInteger(TERMINAL_TRADE_ALLOWED). In the reference's OrderCheck() example, this case returns retcode 10027 with runtime error 4752 (ERR_TRADE_DISABLED).
  2. The EA itself: the permission option in the EA's properties, also switchable in the Navigator. In code: MQLInfoInteger(MQL_TRADE_ALLOWED). The icon next to the EA's name in the upper right corner of the chart shows whether it may trade.
  3. The account: ACCOUNT_TRADE_ALLOWED is false without a connection, for an archived read-only account, when trading is disabled on the server, or after logging in with the investor password. Manual trading is then blocked as well.
  4. The server's permission for EAs: ACCOUNT_TRADE_EXPERT. If the broker disabled automated trading for the account, EA requests are not executed (retcode 10026). Only the broker can change this.

Three options on the same settings tab switch EA trading off automatically when you switch accounts, profiles, or the chart's symbol or period; check them first if an EA stopped after such a change. Indicators cannot trade at all; trade functions work only in EAs and scripts.

A diagnostic routine that prints these permissions and the symbol settings of steps 3 to 7. Call it in OnInit() and again on the first tick, when the connection exists:

//--- Illustrative, not compiled: check against the MQL5 reference.
void PrintTradeEnvironment(const string symbol)
  {
   PrintFormat("DIAG terminal connected=%d trade_allowed=%d build=%d program_trade_allowed=%d",
               (int)TerminalInfoInteger(TERMINAL_CONNECTED),
               (int)TerminalInfoInteger(TERMINAL_TRADE_ALLOWED),
               (int)TerminalInfoInteger(TERMINAL_BUILD),
               (int)MQLInfoInteger(MQL_TRADE_ALLOWED));
   PrintFormat("DIAG account trade_allowed=%d trade_expert=%d limit_orders=%d margin_mode=%s",
               (int)AccountInfoInteger(ACCOUNT_TRADE_ALLOWED),
               (int)AccountInfoInteger(ACCOUNT_TRADE_EXPERT),
               (int)AccountInfoInteger(ACCOUNT_LIMIT_ORDERS),
               EnumToString((ENUM_ACCOUNT_MARGIN_MODE)AccountInfoInteger(ACCOUNT_MARGIN_MODE)));
   PrintFormat("DIAG %s trade_mode=%s exemode=%s filling_flags=%d stops_level=%d freeze_level=%d",
               symbol,
               EnumToString((ENUM_SYMBOL_TRADE_MODE)SymbolInfoInteger(symbol, SYMBOL_TRADE_MODE)),
               EnumToString((ENUM_SYMBOL_TRADE_EXECUTION)SymbolInfoInteger(symbol, SYMBOL_TRADE_EXEMODE)),
               (int)SymbolInfoInteger(symbol, SYMBOL_FILLING_MODE),
               (int)SymbolInfoInteger(symbol, SYMBOL_TRADE_STOPS_LEVEL),
               (int)SymbolInfoInteger(symbol, SYMBOL_TRADE_FREEZE_LEVEL));
   PrintFormat("DIAG %s volume min=%g max=%g step=%g limit=%g",
               symbol,
               SymbolInfoDouble(symbol, SYMBOL_VOLUME_MIN),
               SymbolInfoDouble(symbol, SYMBOL_VOLUME_MAX),
               SymbolInfoDouble(symbol, SYMBOL_VOLUME_STEP),
               SymbolInfoDouble(symbol, SYMBOL_VOLUME_LIMIT));
  }

Reading the return code: OrderSend, OrderCheck and TRADE_RETCODE

OrderSend() returning true only means that the request passed a basic check and was accepted by the trade server. The outcome is in MqlTradeResult.retcode, listed in the return codes of the trade server; retcode_external carries a broker-specific code from an external system. 10008 (order placed), 10009 (request completed) and 10010 (only part completed) report that the request went through; everything else needs a look.

OrderCheck() tests the request first, including whether funds suffice, and fills MqlTradeCheckResult with a retcode, margin, free margin and a comment. It does not see everything: in the reference's own example output, it succeeds on a closed market, and the following OrderSend() fails with runtime error 4756 (ERR_TRADE_SEND_FAILED) and retcode 10018. Log both stages and call ResetLastError() before each, because GetLastError() does not reset itself.

RetcodeConstant (TRADE_RETCODE_…)Where to look
10014INVALID_VOLUMEstep 5: minimum, maximum, volume step
10015INVALID_PRICEstep 7: price field, rounding to tick size
10016INVALID_STOPSstep 7: stops level, side of SL and TP
10017TRADE_DISABLEDstep 3: symbol trade mode
10018MARKET_CLOSEDstep 4: sessions, server time
10019NO_MONEYstep 5: margin
10021PRICE_OFFstep 4: no quotes
10026, 10027SERVER_DISABLES_AT, CLIENT_DISABLES_ATstep 2: broker or terminal permission
10029FROZENstep 7: freeze level
10030INVALID_FILLstep 6: filling mode
10033, 10034, 10040LIMIT_ORDERS, LIMIT_VOLUME, LIMIT_POSITIONSstep 5: limits
10042 to 10046LONG_ONLY, SHORT_ONLY, CLOSE_ONLY, FIFO_CLOSE, HEDGE_PROHIBITEDstep 3: symbol and account rules

A wrapper that writes both stages to the Experts log in one fixed format, which the Python helper further down reads. With the standard library's CTrade class, the same values come from ResultRetcode(), ResultRetcodeDescription() and CheckResultRetcode():

//--- Illustrative, not compiled: check against the MQL5 reference.
ENUM_ORDER_TYPE_FILLING FillingFor(const string symbol)
  {
   long flags = SymbolInfoInteger(symbol, SYMBOL_FILLING_MODE);
   if((flags & SYMBOL_FILLING_FOK) == SYMBOL_FILLING_FOK) return ORDER_FILLING_FOK;
   if((flags & SYMBOL_FILLING_IOC) == SYMBOL_FILLING_IOC) return ORDER_FILLING_IOC;
   return ORDER_FILLING_RETURN;   // not allowed under Market Execution
  }

bool SendChecked(MqlTradeRequest &req)
  {
   MqlTradeCheckResult chk = {};
   MqlTradeResult      res = {};
   if(req.action == TRADE_ACTION_DEAL)
      req.type_filling = FillingFor(req.symbol);
   else if(req.action == TRADE_ACTION_PENDING)
      req.type_filling = ORDER_FILLING_RETURN;     // pending orders: always Return
   ResetLastError();
   if(!OrderCheck(req, chk))
     {
      PrintFormat("TRADE_FAIL stage=check retcode=%u err=%d comment=%s",
                  chk.retcode, GetLastError(), chk.comment);
      return false;
     }
   ResetLastError();
   bool sent = OrderSend(req, res);
   uint rc   = res.retcode;
   bool ok   = (rc == TRADE_RETCODE_DONE || rc == TRADE_RETCODE_PLACED ||
                rc == TRADE_RETCODE_DONE_PARTIAL);
   if(!sent || !ok)
     {
      PrintFormat("TRADE_FAIL stage=send retcode=%u ext=%d err=%d comment=%s",
                  rc, res.retcode_external, GetLastError(), res.comment);
      return false;
     }
   PrintFormat("TRADE_OK retcode=%u deal=%I64u order=%I64u", rc, res.deal, res.order);
   return true;
  }

Broker and symbol settings: filling mode, stop distances, volume

The filling mode decides what happens when the requested volume is not fully available. It is a symbol setting, so a hard-coded filling type can work on one account and fail with 10030 on another:

  • Market Execution: FOK and IOC only where the symbol's SYMBOL_FILLING_MODE flags allow them; Return is disabled.
  • Instant and Request Execution: FOK and IOC regardless of the symbol setting, Return always.
  • Exchange Execution: FOK and IOC according to the symbol flags, Return always.
  • Pending orders: ORDER_FILLING_RETURN regardless of the execution mode.

SYMBOL_FILLING_FOK (1), SYMBOL_FILLING_IOC (2) and SYMBOL_FILLING_BOC (4) are bit flags, not values of ENUM_ORDER_TYPE_FILLING; test them with a bitwise AND, as FillingFor() above does. CTrade::SetTypeFillingBySymbol() picks FOK when both FOK and IOC are allowed.

Stop distances: SYMBOL_TRADE_STOPS_LEVEL is the minimum distance in points between the current price and stop loss or take profit; zero means no minimum. MetaQuotes' article on Market checks names the reference price: a buy opens at Ask, but its SL and TP are measured from Bid; for a sell, from Ask. Within SYMBOL_TRADE_FREEZE_LEVEL, pending orders and positions cannot be modified, which matches retcode 10029.

Worked example: with a point of 0.00001 and a stops level of 30 points, a buy at Bid 1.08500 needs its stop loss at 1.08470 or lower. In a Python port of the check, run with Python 3.9.6, the naive comparison 1.08500 - 1.08470 >= 30 * 0.00001 evaluated to false, because the subtraction gives 0.00029999999999996696. MQL5's double uses the same IEEE 754 format, so compare whole points:

//--- Illustrative, not compiled: check against the MQL5 reference.
bool StopsDistanceOk(const ENUM_ORDER_TYPE type, const double sl, const double tp)
  {
   double point = SymbolInfoDouble(_Symbol, SYMBOL_POINT);
   long   level = SymbolInfoInteger(_Symbol, SYMBOL_TRADE_STOPS_LEVEL);
   double bid   = SymbolInfoDouble(_Symbol, SYMBOL_BID);
   double ask   = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
   if(type == ORDER_TYPE_BUY)    // SL and TP measured from Bid
      return (sl == 0 || MathRound((bid - sl) / point) >= level) &&
             (tp == 0 || MathRound((tp - bid) / point) >= level);
   if(type == ORDER_TYPE_SELL)   // SL and TP measured from Ask
      return (sl == 0 || MathRound((sl - ask) / point) >= level) &&
             (tp == 0 || MathRound((ask - tp) / point) >= level);
   return true;
  }

Volume: the lot size must lie between SYMBOL_VOLUME_MIN and SYMBOL_VOLUME_MAX and be a multiple of SYMBOL_VOLUME_STEP, otherwise the answer is 10014. SYMBOL_VOLUME_LIMIT caps position plus pending volume in one direction, ACCOUNT_LIMIT_ORDERS the number of pending orders (0 means no limit). Typical example: risk-based sizing computes 0.004 lots on a symbol with a 0.01 minimum, so every request is invalid. OrderCalcMargin() returns the margin a planned order needs; 10019 means the free margin is not there.

When the EA runs but never sends a request

If permissions are fine and the logs show no rejected request, the EA probably never calls OrderSend(). Log the decision inputs on every evaluation: signal values, filters and the reason it stayed out. Typical causes:

  • Time filters on the wrong clock. TimeCurrent() returns the server time of the last quote, TimeLocal() the computer's clock; a filter that mixes them can stay closed all day. SymbolInfoSessionTrade() returns sessions as seconds since midnight.
  • Position bookkeeping. On a netting account only one position per symbol exists, so a manual trade or a second EA on the same symbol can make the EA believe it is already in the market. Filter by magic number and symbol.
  • Indicator data not ready. CopyBuffer() returns -1 on an error, and right after a handle is created the indicator may not have calculated its data yet; BarsCalculated() then returns -1. An EA that treats -1, or fewer values than requested, as “no signal” stays flat.
  • Symbol names. A hard-coded symbol name fails where the broker spells it differently: runtime error 4301 (unknown symbol), or 4302 if it is not selected in Market Watch, which SymbolSelect() fixes.
  • Tester versus live. If the EA trades only in the Strategy Tester, run steps 1 to 7 on the live account: initialization at terminal start, permissions and the live account's symbol settings.

For longer logs, a small helper maps codes to checklist steps. It reads the format of SendChecked() plus initialization messages. The constructed sample log used for the test (typical example):

2026.11.03 10:00:01.120	MyEA (EURUSD,H1)	initializing of MyEA (EURUSD,H1) failed with code 1
2026.11.03 10:02:00.000	MyEA (EURUSD,H1)	TRADE_FAIL stage=check retcode=10027 err=4752 comment=AutoTrading disabled by client
2026.11.03 10:06:00.000	MyEA (EURUSD,H1)	TRADE_FAIL stage=send retcode=10030 ext=0 err=4756 comment=Invalid order filling type
2026.11.03 10:07:00.000	MyEA (EURUSD,H1)	TRADE_FAIL stage=send retcode=10030 ext=0 err=4756 comment=Invalid order filling type
2026.11.03 10:08:00.000	MyEA (EURUSD,H1)	TRADE_FAIL stage=check retcode=10016 err=0 comment=Invalid stops
2026.11.03 10:09:00.000	MyEA (EURUSD,H1)	TRADE_FAIL stage=send retcode=10006 ext=0 err=4756 comment=Request rejected
2026.11.03 10:10:00.000	MyEA (EURUSD,H1)	TRADE_OK retcode=10009 deal=123 order=456
#!/usr/bin/env python3
"""Map MetaTrader 5 log lines to a step of the diagnostic checklist.
Standard library only; reads a log file, sends nothing, places no orders."""
import re
import sys
from collections import Counter

STEPS = {  # TRADE_RETCODE_* values from the MQL5 reference
    10014: "5 volume", 10033: "5 volume (pending order limit)",
    10034: "5 volume (symbol volume limit)", 10019: "5 margin",
    10040: "5 volume (position limit)", 10017: "3 symbol trade mode",
    10042: "3 symbol trade mode", 10043: "3 symbol trade mode",
    10044: "3 symbol trade mode", 10045: "3 account rule (FIFO)",
    10046: "3 account rule (no hedging)",
    10018: "4 trading hours", 10021: "4 no quotes",
    10026: "2 server permission", 10027: "2 terminal permission",
    10030: "6 filling mode", 10015: "7 price", 10016: "7 stops level",
    10029: "7 freeze level", 10025: "7 modify without change",
}
SUCCESS = {10008, 10009, 10010}  # placed, done, partially done
ERRORS = {4752: "2 terminal or EA permission", 4301: "3 symbol name",
          4302: "3 Market Watch", 4806: "8 indicator data"}


def read_text(path):
    raw = open(path, "rb").read()
    if raw[:2] in (b"\xff\xfe", b"\xfe\xff"):  # UTF-16 with byte-order mark
        return raw.decode("utf-16")
    return raw.decode("utf-8", errors="replace")


def triage(lines):
    found = Counter()
    for line in lines:
        m = re.search(r"failed with code (\d+)", line)
        if m:
            found["1 OnInit returned %s (init failed)" % m.group(1)] += 1
        for code in map(int, re.findall(r"retcode=(\d+)", line)):
            if code not in SUCCESS:
                found[STEPS.get(code, "? retcode %d, see reference" % code)] += 1
        for code in map(int, re.findall(r"\berr=(\d+)", line)):
            if code in ERRORS:
                found[ERRORS[code]] += 1
    return found


if __name__ == "__main__":
    for step, n in sorted(triage(read_text(sys.argv[1]).splitlines()).items()):
        print("%-40s %d" % (step, n))
$ python3 mt5_log_triage.py sample.log
1 OnInit returned 1 (init failed)        1
2 terminal or EA permission              1
2 terminal permission                    1
6 filling mode                           2
7 stops level                            1
? retcode 10006, see reference           1

Run with Python 3.9.6 on the seven-line sample above, saved as UTF-8 and as UTF-16 with byte-order mark; both gave the output above. No terminal connection, no orders.

Limits of this checklist and what to do next

The checklist covers what the MQL5 reference documents. Broker-specific codes in retcode_external only the broker can explain, and errors in the strategy's own arithmetic need a code review. Practical rules:

  • Reproduce and fix on a demo account. A request that failed live can succeed after a fix and open a real position at once.
  • Do not silence a rejection by removing a check. 10014 or 10019 can mean that the sizing logic produced a volume the account cannot carry; fix the sizing.
  • Log symbol and account settings at start; they can differ between demo and live accounts of the same broker.
  • When asking a developer or the broker for help, send the Experts and Journal lines around the failed attempt, the PrintTradeEnvironment() output, the account type and the terminal build.
  • Before an EA built or repaired with an AI assistant goes live, run a pre-live checklist for AI-built trading bots; for prop firm accounts, see prop firm EA rules in code.

Frequently asked questions

Why is my expert advisor not placing trades in MT5?

Work through four layers in order. First, check the Experts log to see whether the EA initialized at all. Second, check the terminal, EA, account and server permissions for automated trading. Third, read the return code of rejected requests, which names the problem, such as 10030 for filling mode or 10016 for stops. If the logs show no request at all, look at the EA's own entry logic and data.

What does failed with code 1 mean in MetaTrader 5?

It means the EA's OnInit function returned 1, which is INIT_FAILED. Any non-zero return value counts as failed initialization, and the terminal unloads the EA from the chart, so it never receives ticks. Look at the line printed just before the message: typical reasons are an indicator handle that could not be created, for example a missing custom indicator, rejected inputs, or checks that ran before the terminal was connected.

How do I allow algo trading for an EA in MT5?

Two switches must both be on: the automated trading button on the toolbar, which matches the option under Tools, Options, Expert Advisors, and the permission option in the EA's own properties, also available in the Navigator. In addition, the account must allow trading, which fails with an investor password, and the broker must allow Expert Advisors on the account. In code, check TERMINAL_TRADE_ALLOWED, MQL_TRADE_ALLOWED, ACCOUNT_TRADE_ALLOWED and ACCOUNT_TRADE_EXPERT.

What does retcode 10030, invalid filling type, mean in MT5?

The request used a filling mode the symbol does not support. Under Market Execution, FOK and IOC are allowed only where the symbol's SYMBOL_FILLING_MODE flags permit them, and Return is never allowed. Read the flags at runtime with a bitwise AND and set the request's type_filling accordingly, or use the SetTypeFillingBySymbol method of the CTrade class. Pending orders use Return regardless of the execution mode.

Why do I get invalid stops (10016) in MT5?

Stop loss or take profit is too close to the current price or on the wrong side. SYMBOL_TRADE_STOPS_LEVEL gives the minimum distance in points. For a buy, measure from Bid; for a sell, measure from Ask. Compare the distance in whole points rather than raw price differences, because floating-point subtraction can make a stop that is exactly at the limit look slightly too close.

Why does my EA trade in the Strategy Tester but not on a live account?

Compare what differs between the two environments. On the live chart, all four permission switches of terminal, EA, account and server must be on. After a terminal restart, OnInit can run before the connection to the trade server exists. And filling mode, stop distances and volume limits come from the live account's symbol settings, which can differ from the account used for the test. Print these values in the live terminal and work through the checklist.

Does OrderSend returning true mean the trade was executed?

No. According to the MQL5 reference, true only means the request passed a basic check and was accepted by the trade server. The result is in the retcode field of MqlTradeResult: 10009 means the request was completed, 10008 that an order was placed, 10010 that it was partly completed. Any other value needs handling, and retcode_external can carry an additional broker-specific code.

Sources and status

As of November 2026. Third-party rules, prices and APIs may have changed since; the providers' current documentation and the official texts are authoritative.

Need an Expert Advisor diagnosed, fixed or rebuilt? Get in touch — start with the logs, the account and symbol settings and a reproduction on a demo account, before anything trades live. More: Trading bot development for Interactive Brokers and MetaTrader 5.