DEV Community

Omais Ali
Omais Ali

Posted on

OrderSend returned true, so where is my trade? Checking results properly in MQL5

A very common bug in MT5 Expert Advisors: the code calls trade.Buy(...), it returns true, and the EA assumes it is now in a position. Then the next tick opens another one, or the EA manages a trade that does not exist.

The reason is that true from CTrade.Buy or OrderSend only means the request passed the local checks and was sent. Whether the trade server accepted it is in the result code.

Check the retcode, not just the return value

#include <Trade\Trade.mqh>
CTrade trade;

bool OpenBuy(double lots, double sl, double tp)
{
   if(!trade.Buy(lots, _Symbol, 0.0, sl, tp, "entry"))
   {
      PrintFormat("Request not sent: error %d", GetLastError());
      return false;
   }
   uint code = trade.ResultRetcode();
   if(code == TRADE_RETCODE_DONE || code == TRADE_RETCODE_PLACED)
      return true;

   PrintFormat("Server rejected buy: %u %s", code, trade.ResultRetcodeDescription());
   return false;
}
Enter fullscreen mode Exit fullscreen mode

TRADE_RETCODE_DONE means a market order was filled. TRADE_RETCODE_PLACED means a pending order was accepted. Anything else is a rejection you should handle.

The rejections you will actually see

  1. TRADE_RETCODE_REQUOTE and TRADE_RETCODE_PRICE_OFF: price moved. Refresh prices and retry a limited number of times.
  2. TRADE_RETCODE_INVALID_STOPS: stop loss or take profit too close. Read SYMBOL_TRADE_STOPS_LEVEL and widen them.
  3. TRADE_RETCODE_NO_MONEY: not enough free margin. Recalculate the lot size, do not retry.
  4. TRADE_RETCODE_MARKET_CLOSED: session is closed. Wait, do not loop.
  5. TRADE_RETCODE_INVALID_FILL: the broker does not support the filling mode you sent. See below.

Set the filling mode the broker supports

Many EAs work on one broker and fail on another only because of the filling mode. Ask the symbol what it allows:

void SetFilling()
{
   long modes = SymbolInfoInteger(_Symbol, SYMBOL_FILLING_MODE);
   if((modes & SYMBOL_FILLING_FOK) == SYMBOL_FILLING_FOK)
      trade.SetTypeFilling(ORDER_FILLING_FOK);
   else if((modes & SYMBOL_FILLING_IOC) == SYMBOL_FILLING_IOC)
      trade.SetTypeFilling(ORDER_FILLING_IOC);
   else
      trade.SetTypeFilling(ORDER_FILLING_RETURN);
}
Enter fullscreen mode Exit fullscreen mode

Call it once in OnInit after you create the CTrade object.

Confirm the position before you manage it

Even after TRADE_RETCODE_DONE, base your management logic on what is really open: loop over PositionsTotal(), match your magic number and symbol, and only then trail or close. That way a rejected order, a manual close or a restart never leaves the EA believing something that is not true.

Small change, but it removes a whole class of "the EA went crazy" support tickets. If you have hit a retcode that is not on this list, drop it in the comments and I will explain what it means.

Top comments (1)

Collapse
 
stratcorealpha profile image
Arnold Holm •

I would keep TRADE_RETCODE_DONE_PARTIAL as a separate outcome rather than treating everything outside DONE and PLACED as a rejection. With IOC, a partial fill can leave real exposure. Retrying the full requested volume can then add more than intended. Would you add a partial-fill case and compare the filled volume with the requested volume before deciding whether anything remains to send?