Edgoro
Platforms

MQL5 OrderSend errors explained: 10016 invalid stops, 10030 filling mode, 10019 no money and more

Why an MT5 Expert Advisor's order fails: trade server return codes 10004 to 10046 and error 4756, with MQL5 checks for stops, filling mode, volume and margin.

An Expert Advisor that trades happily in the Strategy Tester and then refuses to open a single position on a demo or live account almost always says why. The reason is in the Experts tab of the terminal's Toolbox, as a number: 10016, 10030, 10019. This guide explains what those numbers mean, which ones you'll actually see, and how to prevent the common ones in code.

The MQL5 code below comes as excerpts: functions you add to your own EA, not complete programs.

Two kinds of numbers

When an order fails, MQL5 gives you two different codes, and mixing them up sends people in the wrong direction:

  • Trade server return codes ("retcodes"), from 10004 to about 10046. They're in the retcode field of MqlTradeResult, or trade.ResultRetcode() if you use the CTrade class. This is the reason the order failed.
  • Runtime errors from GetLastError(), in the 4000s and 5000s. The one you'll see most is 4756, "trade request sending failed". It only says that OrderSend() returned false; the retcode says why.

Success has codes too: 10009 (request completed) for market orders, 10008 (order placed) for pending orders, and 10010 when only part of the volume was filled.

Reading both after a raw OrderSend():

// Excerpt: send a market buy and report both codes if it fails.
bool SendBuy(const double volume)
  {
   MqlTradeRequest req = {};
   MqlTradeResult  res = {};
   req.action    = TRADE_ACTION_DEAL;
   req.symbol    = _Symbol;
   req.volume    = volume;
   req.type      = ORDER_TYPE_BUY;
   req.price     = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
   req.deviation = 10;
   req.magic     = 4505;
   if(!OrderSend(req, res))
     {
      PrintFormat("OrderSend failed: error %d, retcode %u (%s)", GetLastError(), res.retcode, res.comment);
      return false;
     }
   return res.retcode == TRADE_RETCODE_DONE || res.retcode == TRADE_RETCODE_PLACED;
  }

Quick reference

CodeConstantMeaningUsual cause
10004TRADE_RETCODE_REQUOTERequotePrice moved before the order was accepted
10006TRADE_RETCODE_REJECTRequest rejectedBroker-side rejection; check the journal for detail
10013TRADE_RETCODE_INVALIDInvalid requestA field that doesn't fit the order type, or a missing one
10014TRADE_RETCODE_INVALID_VOLUMEInvalid volumeBelow the minimum, above the maximum, or off the volume step
10015TRADE_RETCODE_INVALID_PRICEInvalid priceStale price, or a pending order on the wrong side of the market
10016TRADE_RETCODE_INVALID_STOPSInvalid stopsStop-loss or take-profit too close, or on the wrong side
10017TRADE_RETCODE_TRADE_DISABLEDTrade disabledTrading switched off for the symbol or account
10018TRADE_RETCODE_MARKET_CLOSEDMarket closedOutside the symbol's trading session
10019TRADE_RETCODE_NO_MONEYNot enough moneyFree margin too low for the volume
10020TRADE_RETCODE_PRICE_CHANGEDPrices changedFast market; the requested price is gone
10021TRADE_RETCODE_PRICE_OFFNo quotesNo price available to process the request
10024TRADE_RETCODE_TOO_MANY_REQUESTSToo frequent requestsOrder calls in a tight loop
10027TRADE_RETCODE_CLIENT_DISABLES_ATAutotrading disabled by client terminalThe Algo Trading button or the EA's permission is off
10030TRADE_RETCODE_INVALID_FILLUnsupported filling typeThe order's filling mode isn't allowed for this symbol
10034TRADE_RETCODE_LIMIT_VOLUMEVolume limit reachedTotal volume on the symbol would exceed its limit
10044TRADE_RETCODE_CLOSE_ONLYClose onlyThe symbol only allows closing existing positions
10045TRADE_RETCODE_FIFO_CLOSEFIFO ruleThe account must close the oldest position first
10046TRADE_RETCODE_HEDGE_PROHIBITEDHedging prohibitedOpposite positions on one symbol aren't allowed

The full list is in the MQL5 reference linked at the end.

10016: invalid stops

The most common one. The server rejects a stop-loss or take-profit when it's:

  • Too close to the current price. Each symbol has a minimum distance in points, SYMBOL_TRADE_STOPS_LEVEL. A value of 0 means no fixed minimum, but on floating spreads the server can still reject stops inside the spread.
  • Measured from the wrong price. A buy position closes at the bid, so its stop and target are checked against the bid. A sell closes at the ask, so its levels are checked against the ask. Using the ask for a buy's stop leaves you a spread short of the minimum.
  • On the wrong side, such as a buy's stop above the price, usually from a sign error.
  • Not on a valid price step. Gold, indices and some crypto CFDs move in steps larger than one point.

A check to run before sending, and a rounding helper:

// Excerpt: true if SL/TP respect the broker's minimum distance for a new market order.
bool StopsAreValid(const ENUM_ORDER_TYPE type, const double sl, const double tp)
  {
   MqlTick tick;
   if(!SymbolInfoTick(_Symbol, tick))
      return false;
   double minDist = (double)SymbolInfoInteger(_Symbol, SYMBOL_TRADE_STOPS_LEVEL) * _Point;
   if(type == ORDER_TYPE_BUY)
      return (sl == 0.0 || tick.bid - sl >= minDist) && (tp == 0.0 || tp - tick.bid >= minDist);
   if(type == ORDER_TYPE_SELL)
      return (sl == 0.0 || sl - tick.ask >= minDist) && (tp == 0.0 || tick.ask - tp >= minDist);
   return false;
  }

// Excerpt: round a price to the symbol's tick size.
double NormalizeToTick(const double price)
  {
   double tickSize = SymbolInfoDouble(_Symbol, SYMBOL_TRADE_TICK_SIZE);
   if(tickSize <= 0.0)
      return NormalizeDouble(price, _Digits);
   return NormalizeDouble(MathRound(price / tickSize) * tickSize, _Digits);
  }

Modifying a position's stops has one more constraint: inside the freeze level (SYMBOL_TRADE_FREEZE_LEVEL) near the current price, a position can't be modified at all. Trailing-stop code should check it; our MQL5 trailing stop guide does.

10030: unsupported filling type

Every symbol allows certain filling modes: Fill or Kill (FOK), Immediate or Cancel (IOC), and Return, depending on the broker and execution type. MqlTradeRequest and CTrade default to FOK. If the broker doesn't allow FOK on that symbol, every order fails with 10030, which is why EAs that work on one broker can fail on another.

With CTrade, one line when the EA starts (in its OnInit handler) fixes it:

// Excerpt: pick a filling mode the symbol allows; call it once at startup.
void ConfigureTrade(CTrade &t, const ulong magic)
  {
   t.SetExpertMagicNumber(magic);
   t.SetDeviationInPoints(10);
   t.SetTypeFillingBySymbol(_Symbol);
  }

If you build MqlTradeRequest yourself, read SymbolInfoInteger(_Symbol, SYMBOL_FILLING_MODE), check its flags (SYMBOL_FILLING_FOK, SYMBOL_FILLING_IOC) and set type_filling to an allowed value.

10019: not enough money

Free margin can't cover the new position. Causes, roughly in order of frequency: a lot size computed too large, lower leverage than expected (common on prop-firm and EU retail accounts, and for gold and indices), and margin already used by other positions. Check before sending:

// Excerpt: true if free margin covers a new position of this volume.
bool HasMarginFor(const ENUM_ORDER_TYPE type, const double volume)
  {
   double price  = (type == ORDER_TYPE_BUY) ? SymbolInfoDouble(_Symbol, SYMBOL_ASK) : SymbolInfoDouble(_Symbol, SYMBOL_BID);
   double margin = 0.0;
   if(!OrderCalcMargin(type, _Symbol, volume, price, margin))
      return false;
   return margin < AccountInfoDouble(ACCOUNT_MARGIN_FREE);
  }

Margin isn't risk, though. A trade can pass this check and still risk far more than you intended. Size from the stop distance first, as in MQL5 lot size from risk percent, then check margin.

10014: invalid volume

Volume must be between SYMBOL_VOLUME_MIN and SYMBOL_VOLUME_MAX and a multiple of SYMBOL_VOLUME_STEP. A lot size from a risk formula, such as 0.137, fails on a symbol with a 0.01 step unless you round it:

// Excerpt: round a volume down to the symbol's step; 0 means "too small to trade".
double NormalizeVolume(const double volume)
  {
   double minVol = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MIN);
   double maxVol = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MAX);
   double step   = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_STEP);
   if(step <= 0.0)
      return 0.0;
   double v = MathFloor(volume / step + 1e-9) * step;
   if(v < minVol)
      return 0.0;            // skip the trade rather than round the risk up
   return MathMin(v, maxVol);
  }

Rounding down keeps the risk at or below your target. Returning 0 when the result is under the minimum is deliberate: rounding up to the minimum lot would risk more than you asked for.

10004, 10015, 10020 and 10021: price problems

These mean the price in the request no longer matches the market: a requote, a price that changed, an invalid price or no quotes at all. Read the price immediately before sending, never from a variable saved earlier in the tick or on the previous bar, and set a sensible deviation (SetDeviationInPoints()). For pending orders, 10015 often means the order is on the wrong side: a buy limit must be below the ask, a buy stop above it, and both must respect the stops level. After a requote, wait for the next tick before retrying, and limit the retries.

10018, 10017 and 10044: the symbol isn't tradable right now

The market is closed (weekends, holidays, the daily break on some CFDs), trading is disabled for the symbol, or it only allows closing. A quick check before sending:

// Excerpt: can this EA open a new position on the chart's symbol right now?
bool CanOpenPositions()
  {
   long mode = SymbolInfoInteger(_Symbol, SYMBOL_TRADE_MODE);
   if(mode == SYMBOL_TRADE_MODE_DISABLED || mode == SYMBOL_TRADE_MODE_CLOSEONLY)
      return false;
   return TerminalInfoInteger(TERMINAL_TRADE_ALLOWED) && MQLInfoInteger(MQL_TRADE_ALLOWED);
  }

SYMBOL_TRADE_MODE_LONGONLY and SYMBOL_TRADE_MODE_SHORTONLY exist too; check them if your EA trades both directions. For session times, SymbolInfoSessionTrade() returns the trading sessions per weekday. The market hours tool is a quick visual check, but the broker's sessions in server time are what count.

10027 and error 4752: autotrading is off

The terminal or the EA isn't allowed to trade. Check that the Algo Trading button in the toolbar is on, and that "Allow Algo Trading" is ticked in the EA's properties when you attach it. The CanOpenPositions() check above covers both. Logging in with the account's investor (read-only) password also blocks trading. Step-by-step setup is in how to install an Expert Advisor on MT5.

10045 and 10046: FIFO and hedging rules

Some accounts, notably under US rules, forbid holding opposite positions on one symbol (10046) and require positions to be closed in the order they were opened (10045). An EA written for a hedging account needs a different design on these accounts: one position per symbol, closed or reduced rather than hedged.

Error 4806: requested data not found

Not a trade error, but a common neighbour. CopyBuffer() fails with 4806 when an indicator hasn't finished calculating, typically on the first ticks after the EA starts or after a timeframe change. Return and try again on the next tick, or check BarsCalculated(handle) first. Never trade on values from a copy that failed.

A logging helper

When an order fails, log everything you'd need to diagnose it later, in one line:

#include <Trade/Trade.mqh>

// Excerpt: one-line diagnosis after a failed CTrade call.
void LogTradeFailure(CTrade &t, const string what, const double sl, const double tp)
  {
   PrintFormat("%s failed: retcode %u (%s), error %d, bid %s, ask %s, sl %s, tp %s, stops level %d pts, volume step %s",
               what, t.ResultRetcode(), t.ResultRetcodeDescription(), GetLastError(),
               DoubleToString(SymbolInfoDouble(_Symbol, SYMBOL_BID), _Digits),
               DoubleToString(SymbolInfoDouble(_Symbol, SYMBOL_ASK), _Digits),
               DoubleToString(sl, _Digits), DoubleToString(tp, _Digits),
               (int)SymbolInfoInteger(_Symbol, SYMBOL_TRADE_STOPS_LEVEL),
               DoubleToString(SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_STEP), 2));
  }

Coming from MT4?

MQL4 used different numbers for the same problems:

MQL4 errorMQL5 retcode
129 Invalid price10015
130 Invalid stops10016
131 Invalid trade volume10014
132 Market is closed10018
133 Trade is disabled10017
134 Not enough money10019
135 Price changed10020
136 Off quotes10021
138 Requote10004
4109 Trade is not allowed10027, or runtime error 4752

The rest of an MT4-to-MT5 port is covered in how to convert MQL4 to MQL5.

Test where you'll trade

The Strategy Tester doesn't reproduce everything a live server checks: requotes, broker-specific filling rules, session breaks and account permissions can all differ. Before going live, run the EA on a demo account with the same broker and account type you'll use, and read the Experts tab daily for the first weeks.

Sources and further reading

Try it with Edgoro

The Expert Advisors Edgoro's MT5 EA generator writes already normalise prices and volumes, keep stops beyond the broker's stop level and print the return code when an order fails, and every file is compile-checked in MetaEditor; it's part of the trading bots option. Broker conditions still vary, so run any EA on a demo account with your broker before trusting it with real money.

Error handling makes an EA safer to run, not profitable. Trading involves substantial risk of loss; test on a demo account first.

Build it with Edgoro

Describe your strategy in plain English and get a TradingView indicator in Pine Script v6 or a MetaTrader 5 Expert Advisor in minutes. Every first purchase is covered by a 7-day money-back guarantee.

Build your indicator with Edgoro