Error 4756 Invalid Stops

 

All,

I've been slowly implementing all the things I can think of to prevent this error and I'm running out of ideas.  Here is all the functions that do something leading up to a Buy/Sell.  I check margins, I check Positions, I check for Trade Stop Levels (this symbol has none it seems).  See attached pic for the errors/logs.  The SL/TP calculation looks good to me, as in the price numbers chosen are accurate.


// FROM main EA

      double tp = rates[0].close - (RRValue * (sl - rates[0].close));
      double lots = FindLotSize(slPips, AccountRisk, AccountStartingBalance, MaxLots);
      
      lastTradeTime = Sell(tradeResult, lots, latestPrice.bid, sl, tp) ? rates[0].time : lastTradeTime;


// FROM Utils.mqh

// checks the margin levels to see if we have enough to make the trade
// @SEE https://www.mql5.com/en/articles/2555
bool CheckMoneyForTrade(string symb, double lots, double price, ENUM_ORDER_TYPE type)
{
   //--- values of the required and free margin
   double margin, free_margin = AccountInfoDouble(ACCOUNT_MARGIN_FREE);
   //--- call of the checking function
   if(!OrderCalcMargin(type, symb, lots, price, margin))
   {
      //--- something went wrong, report and return false
      string str = "";
      StringConcatenate(str, "Error trying to get margin checks code=", GetLastError());
      LOG(LOG_LEVEL_ERROR, str);
      return false;
   }
   //--- if there are insufficient funds to perform the operation
   if(margin > free_margin)
   {
      //--- report the error and return false
      LOG(LOG_LEVEL_WARN, 
         StringFormat("Not enough funds for trade %s %s %.3f lots, Avail Margin: %.2f Needed: %.2f",
            symb, EnumToString(type), lots, free_margin, margin));

      return false;
   }
   //--- checking successful
   LOG(LOG_LEVEL_INFO,
      StringFormat("Funds available for trade %s %s %.3f lots, Avail Margin: %.2f Needed: %.2f",
         symb, EnumToString(type), lots, free_margin, margin));
   
   return true;
}

// checks that the SL/TP are at least a minimum level away from Bid/Ask prices according to the symbol
// price should be either bid or ask to match the order type!
// @SEE https://www.mql5.com/en/articles/2555
bool CheckStopLoss_Takeprofit(ENUM_ORDER_TYPE type, double price, double SL, double TP)
  {
   //--- get the SYMBOL_TRADE_STOPS_LEVEL level
   int stops_level = (int)SymbolInfoInteger(_Symbol,SYMBOL_TRADE_STOPS_LEVEL);
   if(stops_level != 0)
   {
      LOG(LOG_LEVEL_INFO, StringFormat("SYMBOL_TRADE_STOPS_LEVEL=%d: StopLoss and TakeProfit must"+
                  " not be nearer than %d points from the closing price", stops_level, stops_level));
   }
   else
      LOG(LOG_LEVEL_INFO, "Symbol " + _Symbol + " has no trade stops level set");

   bool SL_check = false, TP_check = false;
   //--- check only two order types
   switch(type)
   {
      //--- Buy operation
      case  ORDER_TYPE_BUY:
      {
         //--- check the StopLoss
         SL_check = (price - SL > stops_level * _Point);
         if(!SL_check)
            LOG(LOG_LEVEL_WARN, StringFormat("For order %s StopLoss=%.5f must be less than %.5f"+
                        " (Bid=%.5f - SYMBOL_TRADE_STOPS_LEVEL=%d points)",
                        EnumToString(type), SL, price - stops_level * _Point, price, stops_level));
         //--- check the TakeProfit
         TP_check = (TP - price > stops_level * _Point);
         if(!TP_check)
            LOG(LOG_LEVEL_WARN, StringFormat("For order %s TakeProfit=%.5f must be greater than %.5f"+
                        " (Bid=%.5f + SYMBOL_TRADE_STOPS_LEVEL=%d points)",
                        EnumToString(type), TP, price + stops_level * _Point, price, stops_level));
         //--- return the result of checking
         return (SL_check && TP_check);
      }
      //--- Sell operation
      case  ORDER_TYPE_SELL:
      {
         //--- check the StopLoss
         SL_check = (SL - price > stops_level * _Point);
         if(!SL_check)
            LOG(LOG_LEVEL_WARN, StringFormat("For order %s StopLoss=%.5f must be greater than %.5f "+
                        " (Ask=%.5f + SYMBOL_TRADE_STOPS_LEVEL=%d points)",
                        EnumToString(type), SL, price + stops_level * _Point, price, stops_level));
         //--- check the TakeProfit
         TP_check=(price - TP > stops_level * _Point);
         if(!TP_check)
            LOG(LOG_LEVEL_WARN, StringFormat("For order %s TakeProfit=%.5f must be less than %.5f "+
                        " (Ask=%.5f - SYMBOL_TRADE_STOPS_LEVEL=%d points)",
                        EnumToString(type), TP, price - stops_level * _Point, price, stops_level));
         //--- return the result of checking
         return (TP_check && SL_check);
      }
      break;
   }
   //--- a slightly different function is required for pending orders
   return false;
}

bool Buy(MqlTradeResult& tradeResult, double lots, double price, double sl, double tp)
{
   return Trade(tradeResult, lots, price, sl, tp, ORDER_TYPE_BUY);
}

bool Sell(MqlTradeResult& tradeResult, double lots, double price, double sl, double tp)
{
   return Trade(tradeResult, lots, price, sl, tp, ORDER_TYPE_SELL);
}


// Returns true if the trade went through
bool Trade(MqlTradeResult& tradeResult, double lots, double price, double sl, double tp, ENUM_ORDER_TYPE type)
{
   string s = "";
   StringConcatenate(s, "Placing Trade type: ", EnumToString(type), " lots: ", lots, " price: ", price, " sl: ", sl, " tp: ", tp);
   LOG(LOG_LEVEL_INFO, s);
   // margin check
   if (!CheckMoneyForTrade(_Symbol, lots, price, type))
   {
      return false;
   }
   // SL/TP distance check
   if (!CheckStopLoss_Takeprofit(type, price, sl, tp))
   {
      return false;
   }

   MqlTradeRequest tradeRequest;
   ZeroMemory(tradeRequest);
   // take position
   tradeRequest.action = TRADE_ACTION_DEAL;
   tradeRequest.price = NormalizeDouble(price, _Digits);
   tradeRequest.sl = sl;
   tradeRequest.tp = NormalizeDouble(tp, _Digits);
   tradeRequest.symbol = _Symbol;
   tradeRequest.volume = lots;
   tradeRequest.magic = EA_MAGIC;
   tradeRequest.type = type;
   tradeRequest.type_filling = ORDER_FILLING_FOK; // fill or kill, all or nothin
   tradeRequest.deviation = 100; // deviation from current price in points (10 pips!)
   
   if(!OrderSend(tradeRequest, tradeResult))
   {
      string str = "";
      StringConcatenate(str, "Issue sending order in ", GetLastError(), " ", tradeResult.comment);
      LOG(LOG_LEVEL_ERROR, str);
      return false;
   }
   
   // 08 == placed, 09 == done, either is a success
   if (tradeResult.retcode == 10008 || tradeResult.retcode == 10009)
   {
      string str = "";
      StringConcatenate(str, "buy order placed, ticket# ", tradeResult.order);
      LOG(LOG_LEVEL_INFO, str);
      return true;
   }
   else
   {
      string str = "";
      StringConcatenate(str, "order failed", GetLastError());
      LOG(LOG_LEVEL_ERROR, str);
      ResetLastError();
      return false;
   }
}
Files:
EAError.png  29 kb
 

You need to correct the check function like this:

bool CheckStopLoss_Takeprofit(ENUM_ORDER_TYPE type, double SL, double TP)
  {
   double price = SymbolInfoDouble(_Symbol, type == ORDER_TYPE_BUY ? SYMBOL_BID : SYMBOL_ASK);
  
   //--- get the SYMBOL_TRADE_STOPS_LEVEL level
   int stops_level = (int)SymbolInfoInteger(_Symbol,SYMBOL_TRADE_STOPS_LEVEL);
   if(stops_level != 0)
   {
      LOG(LOG_LEVEL_INFO, StringFormat("SYMBOL_TRADE_STOPS_LEVEL=%d: StopLoss and TakeProfit must"+
                  " not be nearer than %d points from the closing price", stops_level, stops_level));
   }
   else
      LOG(LOG_LEVEL_INFO, "Symbol " + _Symbol + " has no trade stops level set");
The check for SL/TP is done against the exit price for the trade (NOT its entry price).
 

Thank you all for replying.  amrali, that's exactly what I pass into the function from the above code. Sell is called with the current bid value as the price.


I think I've narrowed it down to the spread and setting a SL that is under the current spread. So I'm working my code around to factor spread in now.

 
Garrett Barton #:

Thank you all for replying.  amrali, that's exactly what I pass into the function from the above code. Sell is called with the current bid value as the price.


I think I've narrowed it down to the spread and setting a SL that is under the current spread. So I'm working my code around to factor spread in now.

No wrong!
Sell should be called with ask, buy with bid.
A sell trade opens on the bid price, and it will always close on the Ask price.
So, the spread is always factored in the calculations. 
 

Thank you for the reply!

I went back to the source page and noticed what you were saying.  https://www.mql5.com/en/articles/2555


To me it still seems wrong, for a sell I feel like I should be checking the SL against the ask, but the TP against the current bid. I don't understand why I care to measure from the ask which is higher than the bid for the sell TP.  For now sell is all ask checks, and buy is all bid checks.
The checks a trading robot must pass before publication in the Market
The checks a trading robot must pass before publication in the Market
  • www.mql5.com
Before any product is published in the Market, it must undergo compulsory preliminary checks in order to ensure a uniform quality standard. This article considers the most frequent errors made by developers in their technical indicators and trading robots. An also shows how to self-test a product before sending it to the Market.
 
Garrett Barton #:

Thank you for the reply!

I went back to the source page and noticed what you were saying.  https://www.mql5.com/en/articles/2555


To me it still seems wrong, for a sell I feel like I should be checking the SL against the ask, but the TP against the current bid. I don't understand why I care to measure from the ask which is higher than the bid for the sell TP.  For now sell is all ask checks, and buy is all bid checks.

SL or TP is irrelevant. They are both related to the close price.

Are you thinking you have 2 different close prices depending if you are in profit or loss ?

 

Admittedly I just took the function (and butchered it) without really thinking about what it does.  Now that I'm thinking about it, I'm not 100% sure I get what its doing.  I thought its goal was to ensure you were not trying to place a Buy/Sell trade with a SL or TP that would violate the minimum distance required by the broker for a stop order.  Reading the description from the reference section though I'm not so sure.

SYMBOL_TRADE_STOPS_LEVEL

Minimal indention in points from the current close price to place Stop orders


Reading the above this seems to be a minimum distance from the Bid price (bid = close) for any stop loss to be set.  So if I'm about to place a Sell, my thinking is that I would want to make sure that the SL is at least above the trade_stops level (and honestly above the ASK otherwise its gonna instant close anyways!)  If I'm placing a buy I think the SL should be trade_stops level below the current bid price.


In either case, I'm not sure why the function cares about the TP's at all.
 
IMO, you need to review your understanding of the basics of trading, you have a confusion about bid, ask and which is used for entering or closing a buy/sell trade. Then, come back to the function and review what it should try to do.

Edit:
TP or SL both are called stop orders as mentioned in the description above.
Reason: