On this page
Back to tutorials

MT5 Error 10016 Invalid Stops: Fix in MQL5 (2026)

Fix TRADE_RETCODE_INVALID_STOPS (10016): SYMBOL_TRADE_STOPS_LEVEL, Buy/Sell SL placement, NormalizeDouble, OrderCheck before OrderSend — with official MQL5 references.

📖 16 min read

📝 3,100 words

🏷️ MQL5 & Expert Advisors

Share this article:

Want to build a no-code strategy right now?

Create your free account in seconds and start building immediately.


MT5 Error 10016 (Invalid Stops): Causes and Fixes in MQL5 OrderSend

Short answer: Error 10016 in MetaTrader 5 is TRADE_RETCODE_INVALID_STOPS — the broker rejected your Stop Loss or Take Profit because they violate server rules (distance, direction, or precision). Fix it by reading SYMBOL_TRADE_STOPS_LEVEL, placing SL/TP on the correct side of price for Buy/Sell, using NormalizeDouble, and calling OrderCheck() before OrderSend().

If you searched MT5 error 10016, invalid stops MQL5, or OrderSend failed 10016, this page is the fix map — aligned with the official trade return codes, not forum guesses.

Related guides: Common MQL5 EA Errors · Can ChatGPT Write an MT5 EA? · EA Risk Management · Build Your First EA


What Is MT5 Error 10016?

After OrderSend(), check MqlTradeResult retcode. When it equals 10016, the constant is TRADE_RETCODE_INVALID_STOPS.

Official wording from MetaQuotes: "Invalid stops in the request."

That means the prices you sent for SL and/or TP are not acceptable to the trade server for this symbol at this moment — not that your EA logic is "wrong," but that the request broke broker constraints.

Symptoms:

  • Journal line: failed market buy … [invalid stops] or retcode 10016
  • OrderSend returns true but retcode != TRADE_RETCODE_DONE (always read retcode)
  • Works on one broker symbol suffix, fails on another (EURUSD vs EURUSD.pro)

Error 10016 vs Old Error 130 (MT4)

MT4 habitMT5 correct check
Typical number130 (ERR_INVALID_STOPS) in older examples10016 in MqlTradeResult.retcode
Where to readGetLastError() after OrderSendresult.retcode after OrderSend
AI riskChatGPT still outputs MT4 patternsDemand "MQL5 only, check result.retcode"

When auditing code, search for 130 and replace with handling of 10016 on MqlTradeResult. Our ChatGPT MQL5 guide covers this mix-up.


SYMBOL_TRADE_STOPS_LEVEL Explained

Brokers require a minimum distance (in points) between the current price and any pending SL/TP attached to a market order.

int stopLevel = (int)SymbolInfoInteger(_Symbol, SYMBOL_TRADE_STOPS_LEVEL);
double point  = SymbolInfoDouble(_Symbol, SYMBOL_POINT);
double minDist = stopLevel * point;  // price distance, not pips

Per SymbolInfoInteger documentation, SYMBOL_TRADE_STOPS_LEVEL is the minimum distance for stop orders.

Also check freeze level: SYMBOL_TRADE_FREEZE_LEVEL — while price is within freeze distance, modifying SL/TP can fail. Relevant for EAs that trail stops every tick.

Practical tip: Add 1–3 points buffer above stopLevel on live accounts; spreads widen at news.


Correct SL and TP Placement (Buy and Sell)

Wrong side is the #2 cause after distance.

ActionStop lossTake profit
Buy (ASK)Below entryAbove entry
Sell (BID)Above entryBelow entry

For Buy, use ASK as reference when setting SL/TP on market buy; for Sell, BID. Mixing Bid/Ask causes subtle 10016 on some servers.

Digits matter: NormalizeDouble(price, (int)SymbolInfoInteger(sym, SYMBOL_DIGITS)).


Fix Invalid Stops in MQL5 Code

Adjust SL/TP to minimum distance

void AdjustStopsForBroker(const string sym, ENUM_ORDER_TYPE type,
                          double price, double &sl, double &tp)
{
   int digits = (int)SymbolInfoInteger(sym, SYMBOL_DIGITS);
   int stopLevel = (int)SymbolInfoInteger(sym, SYMBOL_TRADE_STOPS_LEVEL);
   if(stopLevel <= 0) stopLevel = 10;
   double point = SymbolInfoDouble(sym, SYMBOL_POINT);
   double minDist = (stopLevel + 2) * point;  // +2 point buffer

   if(type == ORDER_TYPE_BUY)
   {
      if(sl > 0 && price - sl < minDist)
         sl = NormalizeDouble(price - minDist, digits);
      if(tp > 0 && tp - price < minDist)
         tp = NormalizeDouble(price + minDist, digits);
      if(sl > 0 && sl >= price) sl = NormalizeDouble(price - minDist, digits);
      if(tp > 0 && tp <= price) tp = NormalizeDouble(price + minDist, digits);
   }
   else if(type == ORDER_TYPE_SELL)
   {
      if(sl > 0 && sl - price < minDist)
         sl = NormalizeDouble(price + minDist, digits);
      if(tp > 0 && price - tp < minDist)
         tp = NormalizeDouble(price - minDist, digits);
      if(sl > 0 && sl <= price) sl = NormalizeDouble(price + minDist, digits);
      if(tp > 0 && tp >= price) tp = NormalizeDouble(price - minDist, digits);
   }
}

Call AdjustStopsForBroker immediately before building MqlTradeRequest.

Alternative: send without SL/TP, manage in code

Set request.sl = 0 and request.tp = 0 on OrderSend, then modify with OrderSend TRADE_ACTION_SLTP once price has moved, or exit with a market close. Used by some scalping EAs — see scalping EA guide.


Use OrderCheck() Before OrderSend()

MetaQuotes recommends validation first. From OrderSend: "It is recommended to check the request before sending it to a trade server… use the OrderCheck() function."

MqlTradeRequest request = {};
MqlTradeCheckResult check = {};
// ... fill request ...

if(!OrderCheck(request, check))
{
   Print("OrderCheck failed, retcode=", check.retcode,
         " balance=", check.balance, " margin=", check.margin);
   return;
}
if(check.retcode == TRADE_RETCODE_INVALID_STOPS)
   Print("Would fail with 10016 — fix SL/TP before sending");

if(!OrderSend(request, result))
   Print("OrderSend error ", GetLastError());
else if(result.retcode != TRADE_RETCODE_DONE)
   Print("Server retcode=", result.retcode, " ", result.comment);

Log result.comment — it often says invalid stops in plain language for the Journal.


Backtest vs Live: Why 10016 Only on Real

EnvironmentTypical behavior
Strategy TesterFixed spread; may not enforce stops level like live
Demo liveCloser to real server rules — use for SL/TP validation
LiveStrict SYMBOL_TRADE_STOPS_LEVEL, freeze, symbol suffix

Community tests of AI-generated EAs often pass backtest then hit 10016 on the first live order — because stops level was never coded.

Checklist before live:

  1. Same symbol name/suffix as production
  2. Print SYMBOL_TRADE_STOPS_LEVEL once in OnInit
  3. Demo trade with smallest lot
  4. Read deploy guide logging section

Next Steps

References: enum_trade_return_codes — TRADE_RETCODE_INVALID_STOPS, SymbolInfoInteger, OrderSend, OrderCheck, NormalizeDouble, MQL5 — ChatGPT EA test (2026).

Build your no-code trading strategy now — free

Create your account and start building a complete no-code strategy right now. Add indicators, filters, and risk rules, then export MQL5 in minutes.

Frequently Asked Questions

Retcode 10016 is TRADE_RETCODE_INVALID_STOPS: the trade server rejected your Stop Loss or Take Profit in the OrderSend request. The official MQL5 description is "Invalid stops in the request." Usually SL/TP are too close to the current price or on the wrong side of the market.

Read SYMBOL_TRADE_STOPS_LEVEL with SymbolInfoInteger, add a small buffer in points, normalize prices with NormalizeDouble, and verify Buy SL below price and Sell SL above price. Use OrderCheck before OrderSend. Full code patterns are in this guide.

Similar meaning (invalid stops) but different numbering. MT5 uses trade return codes in MqlTradeResult.retcode (10016). MT4 often reported 130 in GetLastError context. When migrating or using AI code, confirm you handle result.retcode in MQL5, not MT4 error IDs.

The tester may use fixed spread and looser stop enforcement depending on settings. Live brokers apply SYMBOL_TRADE_STOPS_LEVEL and freeze level strictly. Always test on Demo with the same symbol suffix and broker as live.

Often yes — AI drafts frequently omit stops-level checks. Add broker safety to your prompt or switch to a structured builder. See our ChatGPT MQL5 guide and this article fix section.

Build your no-code strategy now — free

Create Free Account