FX NOOKAI CODE LAB
Loading

How to Fix OrderSend Error 130: Invalid Stops in MT4

By FXNook Lab team

Trader checking order details and calculations at a desk

MT4 error 130, ERR_INVALID_STOPS, means the trade server rejected a stop-loss or take-profit price as invalid. The usual fixes are to check that each stop is on the correct side of the market, measure its distance from the correct quote in points, and normalize its price before calling OrderSend. A preflight check helps catch obvious errors, but it cannot always predict a broker’s server-side rules.

Error 130 most often appears when an SL or TP is too close to the market, is on the wrong side of the order, or was calculated from the wrong quote. It can also happen when the broker applies a floating stop restriction that is not visible in the terminal’s published stop level. This guide focuses on MT4 market orders; pending orders have their own entry-price and stop-distance checks.

What OrderSend error 130 means

In MQL4, OrderSend submits a trade request to the server. If the server rejects a stop price, the function returns -1, and GetLastError() reports error 130. This is a trade-request rejection, not a compiler error. The relevant MQL4 reference is OrderSend.

For a market buy, the order opens at Ask, but the position is normally closed against Bid. That is why the SL and TP for a buy are checked relative to Bid. For a market sell, the order opens at Bid and is closed against Ask, so its stop levels are checked relative to Ask. Using the entry quote for every calculation can make a stop appear far enough away in your code while it is still too close from the server’s perspective.

Direction matters as much as distance. A buy SL belongs below the market and its TP above; a sell SL belongs above the market and its TP below. A price on the wrong side is invalid even if the number of points between it and the quote is large.

Check stop distance for your order type

MODE_STOPLEVEL is the broker-reported minimum stop distance, expressed in points. In MQL4, read it with MarketInfo(Symbol(), MODE_STOPLEVEL). Convert points to a price distance by multiplying the value by the symbol’s point size, which you can read with MarketInfo(Symbol(), MODE_POINT).

For example, if the stop level is 25 points and the symbol’s point size is 0.00001, the reported minimum price distance is 0.00025. Do not assume that one point is one pip: on many five-digit FX quotes, a pip is ten points. The symbol’s quote precision determines the point size.

Market orderStopValid sideReference quote and minimum distance
BuySLBelow BidBid minus SL must be at least MODE_STOPLEVEL points
BuyTPAbove BidTP minus Bid must be at least MODE_STOPLEVEL points
SellSLAbove AskSL minus Ask must be at least MODE_STOPLEVEL points
SellTPBelow AskAsk minus TP must be at least MODE_STOPLEVEL points

The distance check applies when a stop is supplied. A stop value of zero means that stop is omitted; it is not a price to validate. Removing a stop may avoid error 130, but it does not correct an invalid stop calculation and changes how the order is managed. Treat that as a separate trading decision, not as an error-handling shortcut.

Common causes and fixes

CauseWhy it triggers error 130Fix
Confusing pips with pointsThe EA applies a distance ten times smaller or larger than intended on a fractional-pip quote.Read MODE_POINT and MODE_DIGITS for the symbol. Define whether each input is in pips, points, or price units, then convert it explicitly.
Measuring from the wrong quoteA buy stop is checked against Ask, or a sell stop against Bid, even though the close-side quote is the relevant reference for market-order stops.Use Bid for buy SL/TP checks and Ask for sell SL/TP checks. Refresh the rates before preparing the request.
Stop on the wrong sideA buy SL is at or above the market, a buy TP is at or below it, or the sell levels are reversed.Check direction separately from distance. A sufficiently distant price can still be on the invalid side.
Ignoring the minimum distanceThe stop is on the correct side but closer than the published stop level.Compare the price gap with MODE_STOPLEVEL * MODE_POINT before sending.
Unnormalized stop priceThe price has more decimal places than the symbol allows, or rounding moves it onto the wrong side or inside the minimum distance.Normalize the final SL, TP, and market price with NormalizeDouble(price, digits), then validate the rounded values.
Quote changes between check and sendThe market moves after preflight, reducing the distance before the server checks the request.Refresh rates and check immediately before OrderSend. Log the values used, and handle a rejection by diagnosing it rather than blindly resending.

Price normalization is not a substitute for correct stop logic. It rounds a number to the symbol’s displayed decimal precision; it does not make a price valid for the broker. A stop normalized from a value near the minimum can still end up too close after rounding, so run the distance check after normalization. See the MQL4 reference for NormalizeDouble.

Validate prices before calling OrderSend

The following complete MQL4 script is a preflight example for a market buy or sell on the chart symbol. It accepts absolute SL and TP prices as inputs, normalizes them, checks direction and the reported stop distance, then sends the request only if the checks pass. Set the stop inputs to zero if you intentionally want to omit that particular stop; do not use zero merely to make a rejected request pass.

#property strict

input int OrderType = OP_BUY;
input double Lots = 0.10;
input double StopLoss = 0.0;
input double TakeProfit = 0.0;
input int Slippage =  slippage;
input int MagicNumber = 12345;

bool PrepareMarketOrder(int cmd, double &sl, double &tp,
                        double &price, int &stopLevel)
{
   if(cmd != OP_BUY && cmd != OP_SELL)
   {
      Print("Preflight rejected: only OP_BUY and OP_SELL are supported.");
      return false;
   }

   RefreshRates();
   double bid = Bid;
   double ask = Ask;
   int digits = (int)MarketInfo(Symbol(), MODE_DIGITS);
   double point = MarketInfo(Symbol(), MODE_POINT);
   stopLevel = (int)MarketInfo(Symbol(), MODE_STOPLEVEL);

   if(point <= 0.0 || bid <= 0.0 || ask <= 0.0)
   {
      Print("Preflight rejected: invalid quote or point size for ", Symbol());
      return false;
   }

   price = NormalizeDouble(cmd == OP_BUY ? ask : bid, digits);
   sl = NormalizeDouble(sl, digits);
   tp = NormalizeDouble(tp, digits);
   double minDistance = stopLevel * point;
   double referencePrice = cmd == OP_BUY ? bid : ask;

   if(sl > 0.0)
   {
      bool wrongSide = cmd == OP_BUY ? sl >= referencePrice
                                     : sl <= referencePrice;
      bool tooClose = cmd == OP_BUY ? referencePrice - sl < minDistance
                                    : sl - referencePrice < minDistance;
      if(wrongSide || tooClose)
      {
         Print("Preflight rejected SL: symbol=", Symbol(), " type=", cmd,
               " Bid=", DoubleToString(bid, digits),
               " Ask=", DoubleToString(ask, digits),
               " SL=", DoubleToString(sl, digits),
               " TP=", DoubleToString(tp, digits),
               " stopLevel(points)=", stopLevel);
         return false;
      }
   }

   if(tp > 0.0)
   {
      bool wrongSide = cmd == OP_BUY ? tp <= referencePrice
                                     : tp >= referencePrice;
      bool tooClose = cmd == OP_BUY ? tp - referencePrice < minDistance
                                    : referencePrice - tp < minDistance;
      if(wrongSide || tooClose)
      {
         Print("Preflight rejected TP: symbol=", Symbol(), " type=", cmd,
               " Bid=", DoubleToString(bid, digits),
               " Ask=", DoubleToString(ask, digits),
               " SL=", DoubleToString(sl, digits),
               " TP=", DoubleToString(tp, digits),
               " stopLevel(points)=", stopLevel);
         return false;
      }
   }

   return true;
}

void OnStart()
{
   double sl = StopLoss;
   double tp = TakeProfit;
   double price = 0.0;
   int stopLevel = 0;

   if(!PrepareMarketOrder(OrderType, sl, tp, price, stopLevel))
      return;

   ResetLastError();
   int ticket = OrderSend(Symbol(), OrderType, Lots, price, Slippage,
                          sl, tp, "Stop preflight example",
                          MagicNumber, 0, clrNONE);

   if(ticket < 0)
   {
      int err = GetLastError();
      RefreshRates();
      Print("OrderSend failed: error=", err,
            " symbol=", Symbol(), " type=", OrderType,
            " Bid=", DoubleToString(Bid, Digits),
            " Ask=", DoubleToString(Ask, Digits),
            " SL=", DoubleToString(sl, Digits),
            " TP=", DoubleToString(tp, Digits),
            " stopLevel(points)=", stopLevel);
      return;
   }

   Print("Order opened: ticket=", ticket, " symbol=", Symbol());
}

Before compiling: replace slippage in the input declaration with an integer that suits your EA’s existing order logic, for example 3. The line is shown as input int Slippage = slippage; only as a named placeholder; MQL4 requires a numeric input default, so the compilable declaration is input int Slippage = 3;. Use your program’s own slippage setting rather than treating that example value as a universal setting.

The checks use the same point and digit values as the symbol, and compare the normalized stop prices with the relevant Bid or Ask. When the reported stop level is zero, minDistance is zero: direction is still checked, but the code cannot know whether the server is applying a floating restriction. The preflight is therefore a useful local check, not a guarantee that the server will accept the request.

In a finished EA, keep stop inputs and calculations consistent with the strategy’s intended prices. For example, if an input is a distance in points, calculate a price using the symbol’s point size and round it to the symbol’s digits before validating. If it is a distance in pips, convert pips to points correctly for that symbol instead of passing the input directly as a price.

Capture error 130 without hiding the cause

Call GetLastError() immediately after OrderSend returns a negative ticket. Store the result before calling other functions, then log the symbol, order type, Bid, Ask, SL, TP, and the stop level used during preflight. This gives you enough context to compare the intended levels with the quote and broker-reported distance.

Remember that quotes may have changed by the time you print the failure log. The Bid and Ask printed after a failure may be newer than those passed to the request, so keep a record of the preflight snapshot too if you need to reproduce a timing-sensitive rejection. In the example, the stop level is the value read during preflight; it may also change or fail to describe a floating restriction.

Do not respond to every 130 by widening stops and resending without checking the calculation. That can hide a points-versus-pips error, a reversed buy/sell formula, or a quote mismatch. A retry should use a fresh quote, repeat the direction and distance checks, and follow rules you deliberately chose for the EA. It should not silently change the stop the strategy intended to place.

Why MODE_STOPLEVEL can be zero

A zero from MODE_STOPLEVEL can mean that the broker reports no fixed minimum distance for that symbol. It does not always mean that every stop price is acceptable. MT4’s documentation notes that a zero stop level can also indicate a floating restriction controlled by the trade server, which the terminal cannot express as a fixed value.

In that situation, your EA can pass a local check and still receive error 130 when the server evaluates the request. There is no reliable client-side calculation of a floating rule if the broker does not publish that rule. Recheck the stop level close to the send, log the actual request values, and consult the broker’s specifications for the target symbol and account.

Test the exact symbol and account type where the EA will run. Stop rules can differ across symbols and account conditions, and a result from one symbol is not proof that another will accept the same distance. If the broker’s rule is unclear, ask the broker for its stop-distance requirements rather than assuming that zero removes the restriction.

Verify the fix in Strategy Tester and logs

  1. Test both order directions. Confirm that the buy path uses Bid as its stop reference and the sell path uses Ask, with SL and TP on the correct sides.
  2. Test symbols with different digits. Include symbols quoted with different decimal precision and verify that the EA uses that symbol’s point size and digits, not hard-coded values from another chart.
  3. Test around the published minimum. Check stops comfortably beyond the reported distance, then test near the boundary. Small quote changes can make a stop that just meets the minimum invalid at send time.
  4. Check the target broker and account. Use the Strategy Tester to inspect calculation paths, then verify broker acceptance in the environment where you intend to run the EA. A tester may not reproduce every live server-side floating restriction.
  5. Read the logs after a rejection. Compare the logged Bid, Ask, SL, TP, order type, and stop level. Confirm whether the issue is direction, units, rounding, quote movement, or a broker-side rule.
  6. Confirm what the fix changed. The goal is for the intended, valid SL or TP to be accepted. Removing a stop or suppressing the error is not the same as fixing the price calculation.

Past or tested results do not guarantee future results, and a successful test does not guarantee that a broker will accept every later request. If an EA still fails to trade after you check the stops, see the practical checks in MT4 EA Not Opening Trades: Checks, Causes and Fixes. If the rejection is embedded in a larger MQL4 problem, FXNook Lab’s MQL4/MQL5 error-fixing page describes how its code checks and fixes are handled.

Practical next step

Log the exact normalized prices and quote used immediately before one failing request, then compare the calculated gaps with the symbol’s reported stop level. If the EA still receives error 130, check the broker’s rules for that symbol and account and validate again at send time rather than relying on a cached value.

Questions

What does error 130 mean in MT4?

Error 130 is ERR_INVALID_STOPS: the trade server rejected an SL or TP price in the order request. Check that the stop is on the correct side of the market and meets the required distance for the order type.

How do I check MODE_STOPLEVEL in MQL4?

Use `MarketInfo(Symbol(), MODE_STOPLEVEL)` to read the broker-reported minimum distance in points, and `MarketInfo(Symbol(), MODE_POINT)` to convert that distance into a price gap. The value can vary by symbol, and a zero reading does not rule out a floating server restriction.

Why does OrderSend return error 130 when MODE_STOPLEVEL is zero?

A zero can mean the server uses a floating stop restriction that is not reported as a fixed number to the terminal. The server can still reject a stop, so check the broker’s symbol rules and log the request’s prices and quotes at send time.

How do I set a valid stop loss for a buy or sell order in MT4?

For a market buy, put the SL below Bid and at least the required distance away; for a market sell, put it above Ask and at least the required distance away. Normalize the price to the symbol’s digits, then recheck its direction and distance just before OrderSend.